зеркало из
https://github.com/M82-project/DIMA.git
synced 2025-10-29 05:04:18 +02:00
Merge pull request #23 from M82-project/Cybart-patch-3
ajout test sites sur rapport de désinfo
Этот коммит содержится в:
Коммит
98aa04e1e4
@ -1,8 +1,126 @@
|
||||
// Plugin DIMA - content.js - Version finale consolidée
|
||||
// Plugin DIMA - content.js - Version consolidée
|
||||
// Détection de manipulation cognitive - M82 Project
|
||||
// Version: 3.0 Refactored with ContentExtractor
|
||||
// Note: All dependencies are loaded via manifest.json in correct order
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// PARTIE 1: DÉTECTION DE SITES SUSPECTS (NOUVEAU)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Vérifie si le site actuel est dans la liste des sites suspects
|
||||
* Cette fonction est fournie par suspiciousSitesManager.js
|
||||
* et fonctionne automatiquement dès le chargement de la page
|
||||
*/
|
||||
function checkCurrentSiteInSuspiciousList() {
|
||||
const currentUrl = window.location.href;
|
||||
|
||||
// Utiliser la fonction fournie par suspiciousSitesManager.js
|
||||
const result = checkSuspiciousSite(currentUrl);
|
||||
|
||||
if (result.isSuspicious) {
|
||||
console.log('⚠️ DIMA: Site suspect détecté!');
|
||||
console.log('Source:', result.siteInfo.source);
|
||||
console.log('Raison:', result.siteInfo.reason);
|
||||
console.log('Niveau de risque:', result.siteInfo.riskLevel);
|
||||
|
||||
// Afficher une alerte visuelle
|
||||
showSuspiciousSiteAlert(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche une alerte pour un site suspect
|
||||
*/
|
||||
function showSuspiciousSiteAlert(result) {
|
||||
// Créer un bandeau d'alerte en haut de la page
|
||||
const alertBanner = document.createElement('div');
|
||||
alertBanner.id = 'dima-suspicious-site-alert';
|
||||
alertBanner.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(135deg, ${result.riskConfig.color}, ${result.riskConfig.color}dd);
|
||||
color: white;
|
||||
padding: 15px 20px;
|
||||
z-index: 999999;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
animation: slideDown 0.5s ease-out;
|
||||
`;
|
||||
|
||||
alertBanner.innerHTML = `
|
||||
<div style="display: flex; align-items: center; gap: 15px; flex: 1;">
|
||||
<span style="font-size: 24px;">${result.riskConfig.icon}</span>
|
||||
<div>
|
||||
<div style="font-weight: bold; font-size: 16px; margin-bottom: 5px;">
|
||||
${result.riskConfig.label} - ${result.siteInfo.source}
|
||||
</div>
|
||||
<div style="font-size: 14px; opacity: 0.95;">
|
||||
${result.siteInfo.reason}
|
||||
</div>
|
||||
<a href="${result.siteInfo.reportUrl}" target="_blank"
|
||||
style="color: white; text-decoration: underline; font-size: 13px; margin-top: 5px; display: inline-block;">
|
||||
→ Consulter le rapport source
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<button id="dima-close-alert" style="
|
||||
background: rgba(255,255,255,0.2);
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
color: white;
|
||||
padding: 8px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background 0.3s;
|
||||
">
|
||||
✕ Fermer
|
||||
</button>
|
||||
`;
|
||||
|
||||
// Animation CSS
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
#dima-close-alert:hover {
|
||||
background: rgba(255,255,255,0.3) !important;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Ajouter au body
|
||||
document.body.insertBefore(alertBanner, document.body.firstChild);
|
||||
|
||||
// Gérer la fermeture
|
||||
document.getElementById('dima-close-alert').addEventListener('click', () => {
|
||||
alertBanner.style.animation = 'slideDown 0.3s ease-out reverse';
|
||||
setTimeout(() => alertBanner.remove(), 300);
|
||||
});
|
||||
|
||||
// Ajuster le padding du body pour ne pas cacher le contenu
|
||||
document.body.style.paddingTop = `${alertBanner.offsetHeight}px`;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// PARTIE 2: ANALYSE DIMA du site visité
|
||||
// ============================================================================
|
||||
|
||||
// ===== CLASSE PRINCIPALE DIMA =====
|
||||
class DIMAAnalyzer {
|
||||
constructor() {
|
||||
|
||||
@ -0,0 +1,769 @@
|
||||
// DIMA - Base de données de l'infrastructure BAYBRIDGE
|
||||
// Opération d'influence chinoise ciblant des audiences étrangères via des sociétés de marketing digital
|
||||
// Sources: Tadaweb & Paul Charon, Focus Report 2025
|
||||
/**
|
||||
* OPÉRATION BAYBRIDGE
|
||||
* ===================
|
||||
*
|
||||
* Description: Vaste écosystème d'influence informationnelle chinoise opéré depuis la région
|
||||
* de Greater Bay Area (Guangdong). L'infrastructure technique combine des campagnes de
|
||||
* marketing digital avec de la manipulation informationnelle ciblant des dizaines de pays.
|
||||
*
|
||||
* Acteurs principaux:
|
||||
* - Shenzhen Haimai Yunxiang Media Co., Ltd. (深圳市海卖云享传媒有限公司)
|
||||
* - Shanghai Haixun Technology Co., Ltd (海讯社文化传播有限公司)
|
||||
*
|
||||
* Caractéristiques:
|
||||
* - Création de centaines de sites d'information inauthentiques
|
||||
* - Diffusion de contenu aligné avec Pékin et Moscou
|
||||
* - Narratives contradictoires: "positive energy" chinoise + propagande pro-Kremlin
|
||||
* - Traductions de mauvaise qualité, absence de supervision éditoriale
|
||||
* - Inefficacité remarquable malgré une infrastructure technique sophistiquée
|
||||
*
|
||||
* Date d'identification: 2025
|
||||
* Pays ciblés: USA, Europe, Asie, Amérique Latine, Afrique
|
||||
*/
|
||||
|
||||
const baybridgeDomains = [
|
||||
|
||||
// ===== ENTITÉS COMMERCIALES PRINCIPALES =====
|
||||
|
||||
{
|
||||
domain: "haipress.com",
|
||||
matchType: "exact",
|
||||
reason: "Site commercial principal de Shanghai Haixun Technology Co. Ltd., proposant des services de distribution de contenu à l'international",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Infrastructure",
|
||||
"Marketing-Digital",
|
||||
"Sites-Commerciaux",
|
||||
"Haixun"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "hmedium.com",
|
||||
matchType: "exact",
|
||||
reason: "Site commercial principal de Haimai, proposant 'increase the value of your brand'. Interface client pour services de marketing",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Infrastructure",
|
||||
"Marketing-Digital",
|
||||
"Sites-Commerciaux",
|
||||
"Haimai"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "hmedium.net",
|
||||
matchType: "exact",
|
||||
reason: "Site commercial de Haimai hébergeant les offres commerciales détaillées (packages ciblant audiences étrangères)",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Infrastructure",
|
||||
"Marketing-Digital",
|
||||
"Sites-Commerciaux",
|
||||
"Haimai"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "haixunpress.com",
|
||||
matchType: "exact",
|
||||
reason: "Site commercial principal de Haixun. Lien d'infrastructure avec sihaimai.com (IP 47.91.170.222, jan-oct 2024)",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Infrastructure",
|
||||
"Marketing-Digital",
|
||||
"Sites-Commerciaux",
|
||||
"Haixun"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "sihaimai.com",
|
||||
matchType: "exact",
|
||||
reason: "Infrastructure de services d'hébergement et de dissémination liée à Haimai. Lien technique avec haixunpress.com",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Infrastructure",
|
||||
"Hébergement",
|
||||
"Haimai"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "aisugao.com",
|
||||
matchType: "exact",
|
||||
reason: "Plateforme de marketing de marque pilotée par IA de Haixun, offrant traduction automatique d'articles via LLMs natifs",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2024-04-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"LLM",
|
||||
"IA-Générative",
|
||||
"Haixun",
|
||||
"Infrastructure"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "ebuypress.com",
|
||||
matchType: "exact",
|
||||
reason: "Site de distribution de contenu (content provider) lié à Haixun. API payante disponible: api.haipress.com/api/media/resources",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Infrastructure",
|
||||
"Distribution-Contenu",
|
||||
"Haixun"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "globerelease.com",
|
||||
matchType: "exact",
|
||||
reason: "Premier site enregistré par Haimai, affichant contenu générique distribué sur de nombreux autres sites de l'écosystème",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Infrastructure",
|
||||
"Distribution-Contenu",
|
||||
"Haimai"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "shiworld.cn",
|
||||
matchType: "contains",
|
||||
reason: "Site d'hébergement lié à Haimai. Contient news.shiworld.cn hébergeant packages commerciaux",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Infrastructure",
|
||||
"Hébergement",
|
||||
"Haimai"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "mlzgb.cn",
|
||||
matchType: "exact",
|
||||
reason: "Site d'hébergement des packages commerciaux de Haimai (fichiers Excel, liens directs). 92% des packages Haimai hébergés ici",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Infrastructure",
|
||||
"Hébergement",
|
||||
"Haimai"
|
||||
]
|
||||
},
|
||||
|
||||
// ===== FOURNISSEURS DE CONTENU (CONTENT PROVIDERS) =====
|
||||
|
||||
{
|
||||
domain: "timesnewswire.com",
|
||||
matchType: "exact",
|
||||
reason: "Principal fournisseur de contenu du réseau (13% des articles). Diffuse communiqués de presse + contenu propagande (CGTN, Global Times)",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Distribution-Contenu",
|
||||
"Propagande",
|
||||
"Positive-Energy"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "updatenews.info",
|
||||
matchType: "exact",
|
||||
reason: "PRINCIPAL fournisseur de contenu (77% des articles sur sites finaux). Diffuse massivement narratives pro-Kremlin + contenu chinois",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "critical",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Russie",
|
||||
"Chine",
|
||||
"Distribution-Contenu",
|
||||
"Propagande-Pro-Kremlin",
|
||||
"Ukraine",
|
||||
"LLM-Intoxication"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "meijiedaka.com",
|
||||
matchType: "exact",
|
||||
reason: "Fournisseur de contenu identifié dans l'écosystème, alimentant sites finaux de Haimai",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Distribution-Contenu"
|
||||
]
|
||||
},
|
||||
|
||||
// ===== SITES FINAUX - FRANCE (Package "Propagande Politique") =====
|
||||
|
||||
{
|
||||
domain: "alpsbiz.com",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Diffuse contenu pro-Kremlin + narratives chinoises contradictoires",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "rmtcityfr.com",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Traductions de mauvaise qualité, erreurs grammaticales multiples",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "provencedaily.com",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Amplifie narratives pro-Kremlin, cite TASS, RIA Novosti",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "louispress.org",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Usurpation d'identité (naming convention trompeur). Hébergé cluster FR",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Usurpation-Identité",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "friendlyparis.com",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France ('Paris Amical'). Fautes d'orthographe, images manquantes",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "eiffelpost.com",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Republie articles CGTN + contenu pro-Kremlin. Faible qualité éditoriale",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Positive-Energy",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "fr.wdpp.org",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Sous-domaine spécifique audience francophone",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "fr.euleader.org",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Version francophone de euleader.org",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Sites-UE",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "fftribune.com",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Naming convention mimant média légitime",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Usurpation-Identité",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "economyfr.com",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Focus thématique économie",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "froneplus.com",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Contenu synchronisé avec autres sites du réseau",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "frnewsfeed.com",
|
||||
matchType: "exact",
|
||||
reason: "Site final du package 'Propagande Politique' France. Publication synchronisée, architecture similaire Jeecg-Boot",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-France",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
// ===== SITES FINAUX - AUTRES PAYS EUROPÉENS =====
|
||||
|
||||
{
|
||||
domain: "euleader.org",
|
||||
matchType: "exact",
|
||||
reason: "Site ciblant audience UE. Diffuse contenu BTS, actualités Shenzhen, narratives pro-Kremlin. Exemple: article BTS 27/05/2025",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-UE",
|
||||
"Propagande",
|
||||
"Anti-Ukraine"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "londonclup.com",
|
||||
matchType: "exact",
|
||||
reason: "Site package UK. Contenu en vietnamien par erreur (exemple page d'accueil 08/07/2025), images manquantes",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-UK",
|
||||
"Propagande"
|
||||
]
|
||||
},
|
||||
|
||||
// ===== SITES FINAUX - RUSSIE =====
|
||||
|
||||
{
|
||||
domain: "findmoscow.com",
|
||||
matchType: "exact",
|
||||
reason: "Site ciblant audience russe ('Найти Москву'). Hébergé sur cluster serveur russe avec sites ciblant Russie",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-Russie",
|
||||
"Propagande"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "ekaterintech.com",
|
||||
matchType: "exact",
|
||||
reason: "Site ciblant audience russe. Hébergé IP 18.171.181.70 avec cluster sites RU. Lien avec louispress.org (même IP FR)",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Russie",
|
||||
"Sites-Russie",
|
||||
"Infrastructure"
|
||||
]
|
||||
},
|
||||
|
||||
// ===== SITES FINAUX - AUSTRALIE =====
|
||||
|
||||
{
|
||||
domain: "capitalsydney.com",
|
||||
matchType: "exact",
|
||||
reason: "Site ciblant audience australienne ('Sydney News'). Naming convention usurpation identité",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Sites-Australie",
|
||||
"Usurpation-Identité"
|
||||
]
|
||||
},
|
||||
|
||||
// ===== EXEMPLES TYPOSQUATTING =====
|
||||
|
||||
{
|
||||
domain: "dertagesspiegel.com",
|
||||
matchType: "exact",
|
||||
reason: "Typosquatting de 'Der Tagesspiegel' (journal allemand). Site identifié dans packages commerciaux",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Sites-Allemagne",
|
||||
"Usurpation-Identité",
|
||||
"Typosquatting"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "nrchandelsblad.com",
|
||||
matchType: "exact",
|
||||
reason: "Typosquatting de 'NRC Handelsblad' (journal néerlandais). Site identifié dans packages commerciaux",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Sites-Pays-Bas",
|
||||
"Usurpation-Identité",
|
||||
"Typosquatting"
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
domain: "kanagawa-ken.com",
|
||||
matchType: "exact",
|
||||
reason: "Typosquatting ciblant audience japonaise. Site identifié dans packages commerciaux",
|
||||
source: "Tadaweb & Paul Charon - Focus BAYBRIDGE 2025",
|
||||
reportUrl: "https://www.tadaweb.com/hub/68e3e66e4f7350000150899b",
|
||||
identifiedDate: "2025-03-01",
|
||||
riskLevel: "high",
|
||||
tags: [
|
||||
"BAYBRIDGE",
|
||||
"Chine",
|
||||
"Sites-Japon",
|
||||
"Usurpation-Identité",
|
||||
"Typosquatting"
|
||||
]
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
// =============================================================================
|
||||
// FONCTIONS UTILITAIRES
|
||||
// =============================================================================
|
||||
|
||||
// Filtrer par tag
|
||||
function filterBaybridgeByTag(tag) {
|
||||
return baybridgeDomains.filter(d => d.tags.includes(tag));
|
||||
}
|
||||
|
||||
// Filtrer par niveau de risque
|
||||
function filterBaybridgeByRiskLevel(level) {
|
||||
return baybridgeDomains.filter(d => d.riskLevel === level);
|
||||
}
|
||||
|
||||
// Obtenir tous les tags uniques
|
||||
function getBaybridgeTags() {
|
||||
const allTags = new Set();
|
||||
baybridgeDomains.forEach(d => {
|
||||
d.tags.forEach(tag => allTags.add(tag));
|
||||
});
|
||||
return Array.from(allTags).sort();
|
||||
}
|
||||
|
||||
// Obtenir les statistiques
|
||||
function getBaybridgeStats() {
|
||||
return {
|
||||
total: baybridgeDomains.length,
|
||||
critical: baybridgeDomains.filter(d => d.riskLevel === "critical").length,
|
||||
highRisk: baybridgeDomains.filter(d => d.riskLevel === "high").length,
|
||||
mediumRisk: baybridgeDomains.filter(d => d.riskLevel === "medium").length,
|
||||
lowRisk: baybridgeDomains.filter(d => d.riskLevel === "low").length,
|
||||
tags: getBaybridgeTags()
|
||||
};
|
||||
}
|
||||
|
||||
// Obtenir sites par catégorie
|
||||
function getBaybridgeByCategory() {
|
||||
return {
|
||||
infrastructure: filterBaybridgeByTag("Infrastructure"),
|
||||
contentProviders: filterBaybridgeByTag("Distribution-Contenu"),
|
||||
france: filterBaybridgeByTag("Sites-France"),
|
||||
russia: filterBaybridgeByTag("Sites-Russie"),
|
||||
uk: filterBaybridgeByTag("Sites-UK"),
|
||||
propaganda: filterBaybridgeByTag("Propagande"),
|
||||
typosquatting: filterBaybridgeByTag("Typosquatting")
|
||||
};
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// EXPORTS ET DISPONIBILITÉ GLOBALE
|
||||
// =============================================================================
|
||||
|
||||
// Export pour Node.js / modules
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = {
|
||||
baybridgeDomains,
|
||||
filterBaybridgeByTag,
|
||||
filterBaybridgeByRiskLevel,
|
||||
getBaybridgeTags,
|
||||
getBaybridgeStats,
|
||||
getBaybridgeByCategory
|
||||
};
|
||||
}
|
||||
|
||||
// Disponibilité globale pour le navigateur
|
||||
if (typeof window !== 'undefined') {
|
||||
window.baybridgeDomains = baybridgeDomains;
|
||||
window.baybridgeUtils = {
|
||||
filterByTag: filterBaybridgeByTag,
|
||||
filterByRiskLevel: filterBaybridgeByRiskLevel,
|
||||
getTags: getBaybridgeTags,
|
||||
getStats: getBaybridgeStats,
|
||||
getByCategory: getBaybridgeByCategory
|
||||
};
|
||||
}
|
||||
|
||||
// Log de chargement
|
||||
console.log(`Liste BAYBRIDGE chargée: ${baybridgeDomains.length} domaines identifiés`);
|
||||
if (baybridgeDomains.length > 0) {
|
||||
console.log("Statistiques BAYBRIDGE:", getBaybridgeStats());
|
||||
console.log("Catégories:", getBaybridgeByCategory());
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// NOTES IMPORTANTES SUR L'OPÉRATION
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* CONTEXTE GÉOPOLITIQUE:
|
||||
*
|
||||
* Acteurs identifiés:
|
||||
* - Wu Yanni (吴燕妮): Chercheur SZAS, membre du Comité de propagande municipale de Shenzhen,
|
||||
* directrice exécutive Haimai Yunxiang Media
|
||||
* - Zhu Haisong (朱海松): Expert en marketing, chercheur, PDG Haixun, liens avec le
|
||||
* Département de propagande du Guangdong
|
||||
*
|
||||
* Caractéristiques techniques:
|
||||
* - Infrastructure partagée entre Haimai et Haixun (IP 47.91.170.222)
|
||||
* - 24% d'overlap dans les offres commerciales internationales (104 packages communs)
|
||||
* - Utilisation de Jeecg-Boot pour génération automatique de sites web
|
||||
* - API Haixun: api.haipress.com/api/media/resources
|
||||
* - Traduction IA via aisugao.com (LLMs natifs)
|
||||
*
|
||||
* Pays principalement ciblés:
|
||||
* - USA (6% des packages, cible #1)
|
||||
* - Asie du Sud-Est (Corée du Sud, Inde, Vietnam, Thaïlande, Japon, Taiwan)
|
||||
* - Europe: UK (21 packages), Espagne (20), Italie (18), Portugal (17), France (15), Allemagne (15)
|
||||
*
|
||||
* Narratives diffusées:
|
||||
* 1. Contenu chinois "positive energy" (< 5% du volume):
|
||||
* - Republication CGTN, Global Times
|
||||
* - Focus: harmony, win-win cooperation, innovation, développement durable
|
||||
* - Vocabulaire récurrent: "innovation" (68%), "transformation" (54%), "leadership" (47%)
|
||||
*
|
||||
* 2. Contenu pro-Kremlin (volume dominant):
|
||||
* - Sources: TASS, RIA Novosti, RT, Tsargrad TV, Rambler.ru
|
||||
* - Focus: guerre Ukraine, anti-OTAN, amplification Florian Philippot
|
||||
* - Channels Telegram: Maria Zakharova, Alexey Pushkov
|
||||
* - Similarités avec réseau Portal Kombat / Pravda
|
||||
*
|
||||
* Inefficacité opérationnelle:
|
||||
* - Traductions automatiques de très mauvaise qualité
|
||||
* - Narratives contradictoires (Chine positive vs. Russie agressive)
|
||||
* - Aucune traction sur réseaux sociaux (SimilarWeb: pas de données)
|
||||
* - Système en boucle fermée (sites s'auto-citent)
|
||||
* - Erreurs techniques: encodage cyrillique raté (oct 2024 - fév 2025)
|
||||
* - Contenu vietnamien sur sites UK, images manquantes
|
||||
*
|
||||
* Risque LLM:
|
||||
* - Infrastructure potentiellement utilisée pour "intoxiquer" les LLMs
|
||||
* - Contournement sanctions médias russes (RT, Sputnik) via "laundering machine"
|
||||
* - Aucune preuve directe d'assimilation par ChatGPT/Copilot/Gemini/DeepSeek (avril 2025)
|
||||
*
|
||||
* Date pivot: 09 mars 2024
|
||||
* Apparition massive contenu pro-Kremlin sur updatenews.info (hors catégorie TimesNewsWire)
|
||||
* Hypothèse: appropriation infrastructure chinoise par acteurs russes
|
||||
*
|
||||
* Connexion Portal Kombat:
|
||||
* - Similarités narratives et sources avec réseau "Pravda"
|
||||
* - Pas de lien technique formel établi
|
||||
* - Exemple: même citation Philippot sur updatenews.info (05/01/25) et
|
||||
* france.news-pravda.com (06/01/25)
|
||||
*/
|
||||
2101
plugin/plugin_chrome/releases/Plugin-dima/data/databases/Copycop.js
Обычный файл
2101
plugin/plugin_chrome/releases/Plugin-dima/data/databases/Copycop.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
2122
plugin/plugin_chrome/releases/Plugin-dima/data/databases/PortalKombat.js
Обычный файл
2122
plugin/plugin_chrome/releases/Plugin-dima/data/databases/PortalKombat.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1217
plugin/plugin_chrome/releases/Plugin-dima/data/databases/RRN.js
Обычный файл
1217
plugin/plugin_chrome/releases/Plugin-dima/data/databases/RRN.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
6124
plugin/plugin_chrome/releases/Plugin-dima/data/databases/Storm1516.js
Обычный файл
6124
plugin/plugin_chrome/releases/Plugin-dima/data/databases/Storm1516.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@ -0,0 +1,169 @@
|
||||
// DIMA - Template pour nouvelle base de données d'opération
|
||||
// REMPLACEZ "OPERATION_NAME" par le nom de votre opération (ex: Doppelganger, Portal_Kombat, etc.)
|
||||
|
||||
/**
|
||||
* INSTRUCTIONS D'UTILISATION
|
||||
* ==========================
|
||||
*
|
||||
* 1. Copiez ce fichier et renommez-le (ex: Doppelganger.js)
|
||||
* 2. Remplacez tous les "OPERATION_NAME" par le nom de l'opération
|
||||
* 3. Remplissez les domaines dans le tableau
|
||||
* 4. Chargez ce fichier AVANT suspiciousSites.js dans votre HTML
|
||||
*
|
||||
* EXEMPLE:
|
||||
* <script src="data/Copycop.js"></script>
|
||||
* <script src="data/Doppelganger.js"></script>
|
||||
* <script src="data/suspiciousSites.js"></script>
|
||||
*/
|
||||
|
||||
// Nom de la variable globale (à adapter selon votre opération)
|
||||
// Exemples:
|
||||
// - copycopDomains (déjà existant)
|
||||
// - doppelgangerDomains
|
||||
// - portalKombatDomains
|
||||
// - yourOperationDomains
|
||||
|
||||
const OPERATION_NAMEDomains = [
|
||||
// ===== EXEMPLE D'ENTRÉE =====
|
||||
{
|
||||
domain: "example-fake-news.com",
|
||||
matchType: "exact", // "exact", "contains", ou "pattern"
|
||||
reason: "Site identifié dans l'opération [NOM], diffusant de la désinformation ciblée",
|
||||
source: "Nom de l'organisation source (ex: EU DisinfoLab, DFRLab, etc.)",
|
||||
reportUrl: "https://lien-vers-le-rapport-complet.com",
|
||||
identifiedDate: "2025-01-15", // Format: YYYY-MM-DD
|
||||
riskLevel: "high", // "high", "medium", ou "low"
|
||||
tags: [
|
||||
"OPERATION_NAME", // Tag obligatoire : nom de l'opération
|
||||
"Russie", // Origine géographique si connue
|
||||
"USA", // Pays ciblé
|
||||
"Anti-Ukraine", // Thématique
|
||||
"Élections" // Type de campagne
|
||||
]
|
||||
},
|
||||
|
||||
// ===== AJOUTEZ VOS DOMAINES ICI =====
|
||||
|
||||
/*
|
||||
// Template à copier pour chaque nouveau domaine:
|
||||
{
|
||||
domain: "votre-domaine.com",
|
||||
matchType: "exact",
|
||||
reason: "Description précise de la raison",
|
||||
source: "Organisation source",
|
||||
reportUrl: "https://...",
|
||||
identifiedDate: "YYYY-MM-DD",
|
||||
riskLevel: "high|medium|low",
|
||||
tags: ["OPERATION_NAME", "tag1", "tag2"]
|
||||
},
|
||||
*/
|
||||
|
||||
];
|
||||
|
||||
// =============================================================================
|
||||
// FONCTIONS UTILITAIRES (OPTIONNELLES)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Ces fonctions sont optionnelles mais recommandées pour faciliter
|
||||
* l'utilisation de votre base de données indépendamment du gestionnaire principal
|
||||
*/
|
||||
|
||||
// Filtrer par tag
|
||||
function filterOPERATION_NAMEByTag(tag) {
|
||||
return OPERATION_NAMEDomains.filter(d => d.tags.includes(tag));
|
||||
}
|
||||
|
||||
// Filtrer par niveau de risque
|
||||
function filterOPERATION_NAMEByRiskLevel(level) {
|
||||
return OPERATION_NAMEDomains.filter(d => d.riskLevel === level);
|
||||
}
|
||||
|
||||
// Obtenir tous les tags uniques
|
||||
function getOPERATION_NAMETags() {
|
||||
const allTags = new Set();
|
||||
OPERATION_NAMEDomains.forEach(d => {
|
||||
d.tags.forEach(tag => allTags.add(tag));
|
||||
});
|
||||
return Array.from(allTags).sort();
|
||||
}
|
||||
|
||||
// Obtenir les statistiques
|
||||
function getOPERATION_NAMEStats() {
|
||||
return {
|
||||
total: OPERATION_NAMEDomains.length,
|
||||
highRisk: OPERATION_NAMEDomains.filter(d => d.riskLevel === "high").length,
|
||||
mediumRisk: OPERATION_NAMEDomains.filter(d => d.riskLevel === "medium").length,
|
||||
lowRisk: OPERATION_NAMEDomains.filter(d => d.riskLevel === "low").length,
|
||||
tags: getOPERATION_NAMETags()
|
||||
};
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// EXPORTS ET DISPONIBILITÉ GLOBALE
|
||||
// =============================================================================
|
||||
|
||||
// Export pour Node.js / modules
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = {
|
||||
OPERATION_NAMEDomains,
|
||||
filterOPERATION_NAMEByTag,
|
||||
filterOPERATION_NAMEByRiskLevel,
|
||||
getOPERATION_NAMETags,
|
||||
getOPERATION_NAMEStats
|
||||
};
|
||||
}
|
||||
|
||||
// Disponibilité globale pour le navigateur
|
||||
if (typeof window !== 'undefined') {
|
||||
window.OPERATION_NAMEDomains = OPERATION_NAMEDomains;
|
||||
window.OPERATION_NAMEUtils = {
|
||||
filterByTag: filterOPERATION_NAMEByTag,
|
||||
filterByRiskLevel: filterOPERATION_NAMEByRiskLevel,
|
||||
getTags: getOPERATION_NAMETags,
|
||||
getStats: getOPERATION_NAMEStats
|
||||
};
|
||||
}
|
||||
|
||||
// Log de chargement
|
||||
console.log(`Liste OPERATION_NAME chargée: ${OPERATION_NAMEDomains.length} domaines identifiés`);
|
||||
if (OPERATION_NAMEDomains.length > 0) {
|
||||
console.log("Statistiques OPERATION_NAME:", getOPERATION_NAMEStats());
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GUIDE DES TAGS RECOMMANDÉS
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* TAGS OBLIGATOIRES:
|
||||
* - Le nom de votre opération (ex: "Doppelganger", "Portal_Kombat")
|
||||
*
|
||||
* TAGS GÉOGRAPHIQUES (origine):
|
||||
* - Russie, Chine, Iran, Corée_du_Nord, etc.
|
||||
*
|
||||
* TAGS GÉOGRAPHIQUES (cible):
|
||||
* - USA, France, Canada, UK, Allemagne, Ukraine, etc.
|
||||
* - Sites-US, Sites-France, Sites-Canada (pour collections de sites locaux)
|
||||
*
|
||||
* TAGS THÉMATIQUES:
|
||||
* - Anti-Ukraine
|
||||
* - Élections
|
||||
* - COVID-19
|
||||
* - Climat
|
||||
* - Immigration
|
||||
* - Santé
|
||||
*
|
||||
* TAGS TECHNIQUES:
|
||||
* - LLM (contenu généré par IA)
|
||||
* - Deepfake
|
||||
* - Usurpation-Identité
|
||||
* - Bot-Network
|
||||
* - Infrastructure
|
||||
*
|
||||
* TAGS DE MÉTHODE:
|
||||
* - Désinformation-Ciblée
|
||||
* - Amplification-Artificielle
|
||||
* - Multi-Langues
|
||||
* - Coordination-Cross-Platform
|
||||
*/
|
||||
@ -1,23 +1,39 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Analyseur DIMA - M82 Project",
|
||||
"version": "1.1",
|
||||
"description": "Plugin d'analyse de manipulation cognitive selon la matrice DIMA par M82 Project",
|
||||
"permissions": ["activeTab", "storage"],
|
||||
"name": "DIMA - Digital Influence Manipulation Analyzer",
|
||||
"version": "2.0.0",
|
||||
"description": "Plugin d'analyse de manipulation cognitive selon la matrice DIMA par M82 Project, détecte et analyse les sites suspects identifiés dans des rapports de désinformation",
|
||||
"permissions": [
|
||||
"activeTab",
|
||||
"storage"
|
||||
],
|
||||
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
],
|
||||
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": [
|
||||
"data/databases/Copycop.js",
|
||||
"data/databases/PortalKombat.js",
|
||||
"data/databases/RRN.js",
|
||||
"data/databases/Baybridge.js",
|
||||
"data/databases/Storm1516.js",
|
||||
"data/techniques.js",
|
||||
"data/keywords.js",
|
||||
"modules/contentExtractor.js",
|
||||
"modules/suspiciousSitesManager.js",
|
||||
"modules/techniqueAnalyzer.js",
|
||||
"modules/uiManager.js",
|
||||
"content.js"
|
||||
]
|
||||
],
|
||||
"run_at": "document_end"
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
|
||||
"action": {
|
||||
"default_title": "Analyse DIMA - M82 Project"
|
||||
},
|
||||
"icons": {
|
||||
|
||||
@ -0,0 +1,540 @@
|
||||
// DIMA - Gestionnaire Central de Sites Suspects
|
||||
// Version 2.2 - Support COMPLET des comptes sociaux (format Storm1516 natif)
|
||||
// Ce fichier charge et agrège toutes les bases de données de domaines suspects
|
||||
|
||||
/**
|
||||
* Gestionnaire centralisé des sites suspects
|
||||
* Compatible avec TOUS les formats de données existants
|
||||
*/
|
||||
class SuspiciousSitesManager {
|
||||
constructor() {
|
||||
this.sources = new Map();
|
||||
this.allSites = [];
|
||||
this.stats = {
|
||||
totalSites: 0,
|
||||
totalDomains: 0,
|
||||
totalSocialAccounts: 0,
|
||||
byRiskLevel: { high: 0, medium: 0, low: 0 },
|
||||
bySources: {},
|
||||
byTags: {},
|
||||
bySocialPlatform: {}
|
||||
};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise le gestionnaire en chargeant toutes les sources disponibles
|
||||
*/
|
||||
init() {
|
||||
console.log('🛡️ DIMA: Initialisation du gestionnaire de sites suspects...');
|
||||
|
||||
// Détecter et charger les sources disponibles
|
||||
this.detectAndLoadSources();
|
||||
|
||||
// Agréger tous les sites
|
||||
this.aggregateAllSites();
|
||||
|
||||
// Calculer les statistiques
|
||||
this.calculateStats();
|
||||
|
||||
console.log(`✅ DIMA: ${this.allSites.length} entrées chargées depuis ${this.sources.size} source(s)`);
|
||||
console.log(` - ${this.stats.totalDomains} domaines`);
|
||||
console.log(` - ${this.stats.totalSocialAccounts} comptes de réseaux sociaux`);
|
||||
this.logStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Détecte et charge automatiquement toutes les sources disponibles
|
||||
*/
|
||||
detectAndLoadSources() {
|
||||
// Source 1: CopyCop (Recorded Future)
|
||||
if (typeof copycopDomains !== 'undefined' && Array.isArray(copycopDomains)) {
|
||||
this.registerSource('CopyCop', copycopDomains, {
|
||||
name: 'Opération CopyCop',
|
||||
description: 'Réseau russe de sites fictifs et de désinformation',
|
||||
organization: 'Recorded Future - Insikt Group',
|
||||
reportUrl: 'https://www.recordedfuture.com/research/cta-ru-2025-0917',
|
||||
reportDate: '2025-09-17'
|
||||
});
|
||||
console.log(` ✓ Source CopyCop chargée: ${copycopDomains.length} domaines`);
|
||||
}
|
||||
|
||||
// Source 2: RRN (VIGINUM)
|
||||
if (typeof rrnDomains !== 'undefined' && Array.isArray(rrnDomains)) {
|
||||
this.registerSource('RRN', rrnDomains, {
|
||||
name: 'Réseau RRN',
|
||||
description: 'Réseau de faux médias et infrastructure de désinformation pro-russe',
|
||||
organization: 'VIGINUM',
|
||||
reportUrl: 'https://www.sgdsn.gouv.fr/files/files/20230619_NP_VIGINUM_RAPPORT-CAMPAGNE-RRN_VF_0.pdf',
|
||||
reportDate: '2023-06-19'
|
||||
});
|
||||
console.log(` ✓ Source RRN chargée: ${rrnDomains.length} domaines`);
|
||||
}
|
||||
|
||||
// Source 3: Portal Kombat (VIGINUM)
|
||||
if (typeof portalKombatDomains !== 'undefined' && Array.isArray(portalKombatDomains)) {
|
||||
this.registerSource('PortalKombat', portalKombatDomains, {
|
||||
name: 'Opération Portal Kombat',
|
||||
description: 'Réseau d\'influence',
|
||||
organization: 'Viginum',
|
||||
reportUrl: 'https://www.sgdsn.gouv.fr/files/files/20240212_NP_SGDSN_VIGINUM_RAPPORT-RESEAU-PORTAL-KOMBAT_VF.pdf',
|
||||
reportDate: '2024-02-01'
|
||||
});
|
||||
console.log(` ✓ Source Portal Kombat chargée: ${portalKombatDomains.length} domaines`);
|
||||
}
|
||||
|
||||
// Source 4: Baybridge (IRSEM)
|
||||
if (typeof baybridgeDomains !== 'undefined' && Array.isArray(baybridgeDomains)) {
|
||||
this.registerSource('Baybridge', baybridgeDomains, {
|
||||
name: 'Opération Baybridge',
|
||||
description: 'Vaste écosystème d\'influence informationnelle chinoise ',
|
||||
organization: 'IRSEM & TadaWeb',
|
||||
reportUrl: 'https://www.irsem.fr/focus',
|
||||
reportDate: '2025-10-17'
|
||||
});
|
||||
console.log(` ✓ Source Baybridge chargée: ${baybridgeDomains.length} domaines`);
|
||||
}
|
||||
|
||||
// Source 5: Storm 1516 - Domaines (VIGINUM)
|
||||
if (typeof storm1516Domains !== 'undefined' && Array.isArray(storm1516Domains)) {
|
||||
this.registerSource('Storm1516_Domains', storm1516Domains, {
|
||||
name: 'Opération Storm_1516 (Domaines)',
|
||||
description: 'Mode opératoire informationnel (MOI) russe actif depuis août 2023',
|
||||
organization: 'VIGINUM',
|
||||
reportUrl: 'https://www.defense.gouv.fr/sites/default/files/desinformation/Rapport%20Storm%201516%20-%20SGDSN.pdf',
|
||||
reportDate: '2025-05-02'
|
||||
});
|
||||
console.log(` ✓ Source Storm 1516 (domaines) chargée: ${storm1516Domains.length} domaines`);
|
||||
}
|
||||
|
||||
// Source 6: Storm 1516 - Comptes sociaux (VIGINUM) - FORMAT NATIF
|
||||
if (typeof storm1516SocialAccounts !== 'undefined' && Array.isArray(storm1516SocialAccounts)) {
|
||||
this.registerSource('Storm1516_Social', storm1516SocialAccounts, {
|
||||
name: 'Opération Storm_1516 (Comptes sociaux)',
|
||||
description: 'Comptes de réseaux sociaux relayant le MOI russe Storm 1516',
|
||||
organization: 'VIGINUM',
|
||||
reportUrl: 'https://www.defense.gouv.fr/sites/default/files/desinformation/Rapport%20Storm%201516%20-%20SGDSN.pdf',
|
||||
reportDate: '2025-05-02'
|
||||
});
|
||||
console.log(` ✓ Source Storm 1516 (comptes sociaux) chargée: ${storm1516SocialAccounts.length} comptes`);
|
||||
}
|
||||
|
||||
|
||||
// Avertissement si aucune source n'est chargée
|
||||
if (this.sources.size === 0) {
|
||||
console.warn('⚠️ DIMA: Aucune base de données de sites suspects n\'a été chargée');
|
||||
console.warn(' Vérifiez que les fichiers de bases de données sont correctement chargés avant ce gestionnaire');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enregistre une nouvelle source de données
|
||||
*/
|
||||
registerSource(sourceName, domains, metadata) {
|
||||
this.sources.set(sourceName, {
|
||||
domains: domains,
|
||||
metadata: metadata,
|
||||
count: domains.length
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Agrège tous les sites de toutes les sources
|
||||
*/
|
||||
aggregateAllSites() {
|
||||
this.allSites = [];
|
||||
|
||||
for (const [sourceName, sourceData] of this.sources) {
|
||||
this.allSites.push(...sourceData.domains);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule les statistiques globales
|
||||
*/
|
||||
calculateStats() {
|
||||
this.stats.totalSites = this.allSites.length;
|
||||
this.stats.totalDomains = 0;
|
||||
this.stats.totalSocialAccounts = 0;
|
||||
|
||||
// Reset stats
|
||||
this.stats.byRiskLevel = { high: 0, medium: 0, low: 0 };
|
||||
this.stats.bySources = {};
|
||||
this.stats.byTags = {};
|
||||
this.stats.bySocialPlatform = {};
|
||||
|
||||
// Compter par niveau de risque et tags
|
||||
this.allSites.forEach(site => {
|
||||
// Distinguer domaines et comptes sociaux
|
||||
// Format Storm1516: {platform: "X/Twitter", handle: "@..."}
|
||||
// Format standard: {domain: "...", accountType: "twitter"}
|
||||
if (site.platform || site.accountType) {
|
||||
this.stats.totalSocialAccounts++;
|
||||
const platform = site.platform || site.accountType;
|
||||
this.stats.bySocialPlatform[platform] = (this.stats.bySocialPlatform[platform] || 0) + 1;
|
||||
} else {
|
||||
this.stats.totalDomains++;
|
||||
}
|
||||
|
||||
// Par niveau de risque
|
||||
if (site.riskLevel) {
|
||||
this.stats.byRiskLevel[site.riskLevel] = (this.stats.byRiskLevel[site.riskLevel] || 0) + 1;
|
||||
}
|
||||
|
||||
// Par source
|
||||
if (site.source) {
|
||||
this.stats.bySources[site.source] = (this.stats.bySources[site.source] || 0) + 1;
|
||||
}
|
||||
|
||||
// Par tags
|
||||
if (site.tags && Array.isArray(site.tags)) {
|
||||
site.tags.forEach(tag => {
|
||||
this.stats.byTags[tag] = (this.stats.byTags[tag] || 0) + 1;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche les statistiques dans la console
|
||||
*/
|
||||
logStats() {
|
||||
console.log('📊 Statistiques:');
|
||||
console.log(` Total: ${this.stats.totalSites} entrées`);
|
||||
console.log(` - Domaines: ${this.stats.totalDomains}`);
|
||||
console.log(` - Comptes sociaux: ${this.stats.totalSocialAccounts}`);
|
||||
if (this.stats.totalSocialAccounts > 0) {
|
||||
console.log(' Répartition par plateforme:');
|
||||
for (const [platform, count] of Object.entries(this.stats.bySocialPlatform)) {
|
||||
console.log(` • ${platform}: ${count}`);
|
||||
}
|
||||
}
|
||||
console.log(` Risque élevé: ${this.stats.byRiskLevel.high || 0}`);
|
||||
console.log(` Risque moyen: ${this.stats.byRiskLevel.medium || 0}`);
|
||||
console.log(` Risque faible: ${this.stats.byRiskLevel.low || 0}`);
|
||||
console.log(` Sources: ${Object.keys(this.stats.bySources).length}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si une URL correspond à un site suspect OU un compte social suspect
|
||||
* @param {string} url - L'URL à vérifier
|
||||
* @returns {Object} Résultat de la vérification
|
||||
*/
|
||||
checkSite(url) {
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
const hostname = urlObj.hostname.toLowerCase();
|
||||
const pathname = urlObj.pathname.toLowerCase();
|
||||
|
||||
for (const site of this.allSites) {
|
||||
let isMatch = false;
|
||||
let matchType = 'domain';
|
||||
|
||||
// NOUVEAU: Support du format Storm1516 natif
|
||||
// Format: {platform: "X/Twitter", handle: "@JimFergusonUK", url: "..."}
|
||||
if (site.platform && site.handle) {
|
||||
isMatch = this.checkSocialAccountStorm1516Format(url, site, hostname, pathname);
|
||||
matchType = 'social_account';
|
||||
}
|
||||
// Support du format standard avec accountType
|
||||
else if (site.accountType) {
|
||||
const extractedHandle = this.extractSocialHandle(url, site.accountType);
|
||||
if (extractedHandle) {
|
||||
const dbHandle = site.domain.toLowerCase().replace(/^@/, '');
|
||||
isMatch = extractedHandle === dbHandle;
|
||||
matchType = 'social_account';
|
||||
}
|
||||
}
|
||||
// Vérification classique pour les domaines
|
||||
else {
|
||||
switch (site.matchType) {
|
||||
case "exact":
|
||||
isMatch = hostname === site.domain.toLowerCase() ||
|
||||
hostname === `www.${site.domain.toLowerCase()}`;
|
||||
break;
|
||||
|
||||
case "contains":
|
||||
isMatch = hostname.includes(site.domain.toLowerCase());
|
||||
break;
|
||||
|
||||
case "pattern":
|
||||
try {
|
||||
const regex = new RegExp(site.domain, "i");
|
||||
isMatch = regex.test(hostname);
|
||||
} catch (e) {
|
||||
console.error(`DIMA: Pattern regex invalide pour ${site.domain}:`, e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMatch) {
|
||||
console.log(`🎯 DIMA: Match trouvé!`, {
|
||||
type: matchType,
|
||||
site: site.handle || site.domain,
|
||||
url: url
|
||||
});
|
||||
|
||||
return {
|
||||
isSuspicious: true,
|
||||
siteInfo: site,
|
||||
riskConfig: this.getRiskConfig(site.riskLevel),
|
||||
matchedHostname: hostname,
|
||||
matchType: matchType,
|
||||
matchedIdentifier: matchType === 'social_account' ? (site.handle || site.domain) : hostname
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { isSuspicious: false };
|
||||
} catch (error) {
|
||||
console.error("DIMA: Erreur lors de la vérification du site suspect:", error);
|
||||
return { isSuspicious: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NOUVEAU: Vérifie un compte social au format Storm1516
|
||||
* Format: {platform: "X/Twitter", handle: "@JimFergusonUK"}
|
||||
*/
|
||||
checkSocialAccountStorm1516Format(url, site, hostname, pathname) {
|
||||
// Mapping des plateformes Storm1516 vers domaines
|
||||
const platformDomains = {
|
||||
'X/Twitter': ['twitter.com', 'x.com'],
|
||||
'Telegram': ['t.me', 'telegram.me'],
|
||||
'YouTube': ['youtube.com', 'youtu.be'],
|
||||
'Facebook': ['facebook.com', 'fb.com', 'm.facebook.com'],
|
||||
'Instagram': ['instagram.com'],
|
||||
'TikTok': ['tiktok.com'],
|
||||
'VK': ['vk.com'],
|
||||
'Rumble': ['rumble.com']
|
||||
};
|
||||
|
||||
const platform = site.platform;
|
||||
const handle = site.handle.toLowerCase().replace(/^@/, ''); // Enlever @ et lowercase
|
||||
|
||||
// Vérifier si on est sur la bonne plateforme
|
||||
const domains = platformDomains[platform];
|
||||
if (!domains) {
|
||||
console.warn(`DIMA: Plateforme inconnue: ${platform}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const isCorrectDomain = domains.some(domain => hostname.includes(domain));
|
||||
if (!isCorrectDomain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extraire le handle de l'URL actuelle
|
||||
let extractedHandle = null;
|
||||
|
||||
if (platform === 'X/Twitter') {
|
||||
// twitter.com/JimFergusonUK ou x.com/JimFergusonUK
|
||||
const match = pathname.match(/^\/([a-zA-Z0-9_]+)(?:\/|$|\?)/);
|
||||
if (match) extractedHandle = match[1].toLowerCase();
|
||||
} else if (platform === 'Telegram') {
|
||||
// t.me/username ou t.me/s/channelname
|
||||
const match = pathname.match(/^\/(?:s\/)?([a-zA-Z0-9_]+)(?:\/|$|\?)/);
|
||||
if (match) extractedHandle = match[1].toLowerCase();
|
||||
} else if (platform === 'YouTube') {
|
||||
// youtube.com/@username ou youtube.com/c/username
|
||||
const match = pathname.match(/^\/([@c]\/)?([a-zA-Z0-9_-]+)(?:\/|$|\?)/);
|
||||
if (match) extractedHandle = match[2].toLowerCase();
|
||||
} else if (platform === 'Facebook') {
|
||||
// facebook.com/username
|
||||
const match = pathname.match(/^\/([a-zA-Z0-9._-]+)(?:\/|$|\?)/);
|
||||
if (match) extractedHandle = match[1].toLowerCase();
|
||||
} else if (platform === 'Rumble') {
|
||||
// rumble.com/c/username
|
||||
const match = pathname.match(/^\/c\/([a-zA-Z0-9_-]+)(?:\/|$|\?)/);
|
||||
if (match) extractedHandle = match[1].toLowerCase();
|
||||
}
|
||||
|
||||
if (extractedHandle) {
|
||||
console.log(`🔍 DIMA: Comparaison - URL: "${extractedHandle}" vs DB: "${handle}"`);
|
||||
return extractedHandle === handle;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrait le handle/username d'une URL de réseau social
|
||||
* @param {string} url - L'URL complète
|
||||
* @param {string} accountType - Type de compte (twitter, facebook, youtube, etc.)
|
||||
* @returns {string|null} Le handle extrait ou null
|
||||
*/
|
||||
extractSocialHandle(url, accountType) {
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
const hostname = urlObj.hostname.toLowerCase();
|
||||
const pathname = urlObj.pathname;
|
||||
|
||||
// Patterns pour différents réseaux sociaux
|
||||
const patterns = {
|
||||
twitter: {
|
||||
domains: ['twitter.com', 'x.com'],
|
||||
regex: /^\/([a-zA-Z0-9_]+)(?:\/|$|\?)/
|
||||
},
|
||||
facebook: {
|
||||
domains: ['facebook.com', 'fb.com'],
|
||||
regex: /^\/([a-zA-Z0-9._]+)(?:\/|$|\?)/
|
||||
},
|
||||
instagram: {
|
||||
domains: ['instagram.com'],
|
||||
regex: /^\/([a-zA-Z0-9._]+)(?:\/|$|\?)/
|
||||
},
|
||||
youtube: {
|
||||
domains: ['youtube.com'],
|
||||
regex: /^\/([@c]\/)?([a-zA-Z0-9_-]+)(?:\/|$|\?)/
|
||||
},
|
||||
telegram: {
|
||||
domains: ['t.me', 'telegram.me'],
|
||||
regex: /^\/([a-zA-Z0-9_]+)(?:\/|$|\?)/
|
||||
},
|
||||
tiktok: {
|
||||
domains: ['tiktok.com'],
|
||||
regex: /^\/@?([a-zA-Z0-9._]+)(?:\/|$|\?)/
|
||||
},
|
||||
vk: {
|
||||
domains: ['vk.com'],
|
||||
regex: /^\/([a-zA-Z0-9._]+)(?:\/|$|\?)/
|
||||
}
|
||||
};
|
||||
|
||||
const pattern = patterns[accountType.toLowerCase()];
|
||||
if (!pattern) {
|
||||
console.warn(`DIMA: Type de compte non supporté: ${accountType}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Vérifier si on est sur le bon domaine
|
||||
const isCorrectDomain = pattern.domains.some(domain => hostname.includes(domain));
|
||||
if (!isCorrectDomain) return null;
|
||||
|
||||
// Extraire le handle
|
||||
const match = pathname.match(pattern.regex);
|
||||
if (match) {
|
||||
const handle = accountType.toLowerCase() === 'youtube' ? (match[2] || match[1]) : match[1];
|
||||
console.log(`DIMA: Handle extrait de ${accountType}: ${handle}`);
|
||||
return handle;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("DIMA: Erreur lors de l'extraction du handle social:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la configuration visuelle pour un niveau de risque
|
||||
*/
|
||||
getRiskConfig(riskLevel) {
|
||||
const RISK_LEVELS = {
|
||||
critical: {
|
||||
color: "#8b0000",
|
||||
icon: "🚨",
|
||||
label: "Risque Critique",
|
||||
message: "Ce site/compte a été identifié comme un acteur majeur de désinformation."
|
||||
},
|
||||
high: {
|
||||
color: "#c0392b",
|
||||
icon: "⚠️",
|
||||
label: "Risque Élevé",
|
||||
message: "Ce site/compte a été identifié comme diffusant de la désinformation de manière systématique."
|
||||
},
|
||||
medium: {
|
||||
color: "#e67e22",
|
||||
icon: "⚡",
|
||||
label: "Vigilance Requise",
|
||||
message: "Ce site/compte a été signalé pour des pratiques douteuses."
|
||||
},
|
||||
low: {
|
||||
color: "#f39c12",
|
||||
icon: "ℹ️",
|
||||
label: "À Surveiller",
|
||||
message: "Ce site/compte présente des caractéristiques suspectes."
|
||||
}
|
||||
};
|
||||
|
||||
return RISK_LEVELS[riskLevel] || RISK_LEVELS.low;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne les statistiques
|
||||
*/
|
||||
getStats() {
|
||||
return this.stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne les informations sur toutes les sources chargées
|
||||
*/
|
||||
getSourcesInfo() {
|
||||
const sourcesInfo = [];
|
||||
for (const [sourceName, sourceData] of this.sources) {
|
||||
sourcesInfo.push({
|
||||
name: sourceName,
|
||||
count: sourceData.count,
|
||||
...sourceData.metadata
|
||||
});
|
||||
}
|
||||
return sourcesInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche des sites par tag
|
||||
*/
|
||||
searchByTag(tag) {
|
||||
return this.allSites.filter(site =>
|
||||
site.tags && site.tags.includes(tag)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche des sites par source
|
||||
*/
|
||||
searchBySource(sourceName) {
|
||||
return this.allSites.filter(site =>
|
||||
site.source === sourceName
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche des comptes sociaux par plateforme
|
||||
*/
|
||||
searchBySocialPlatform(platform) {
|
||||
return this.allSites.filter(site =>
|
||||
site.platform === platform ||
|
||||
(site.accountType && site.accountType.toLowerCase() === platform.toLowerCase())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialisation automatique du gestionnaire
|
||||
let suspiciousSitesManager;
|
||||
|
||||
// Initialiser après le chargement de toutes les bases de données
|
||||
if (typeof window !== 'undefined') {
|
||||
// Dans le navigateur, initialiser après un court délai pour laisser les autres fichiers se charger
|
||||
setTimeout(() => {
|
||||
suspiciousSitesManager = new SuspiciousSitesManager();
|
||||
|
||||
// Rendre disponible globalement
|
||||
window.suspiciousSitesManager = suspiciousSitesManager;
|
||||
|
||||
// Pour compatibilité avec l'ancien code, exposer aussi checkSuspiciousSite
|
||||
window.checkSuspiciousSite = (url) => suspiciousSitesManager.checkSite(url);
|
||||
|
||||
// Exposer aussi les statistiques et infos
|
||||
window.getSuspiciousSitesStats = () => suspiciousSitesManager.getStats();
|
||||
window.getSuspiciousSitesSourcesInfo = () => suspiciousSitesManager.getSourcesInfo();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Export pour Node.js si nécessaire
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = SuspiciousSitesManager;
|
||||
}
|
||||
@ -9,6 +9,7 @@ class UIManager {
|
||||
this.buttonCreated = false;
|
||||
this.analysisResults = null;
|
||||
this.pageType = 'general';
|
||||
this.suspiciousSiteCheck = null;
|
||||
}
|
||||
|
||||
log(message, data = null) {
|
||||
@ -30,12 +31,20 @@ class UIManager {
|
||||
console.error('DIMA: Aucun résultat d\'analyse disponible pour créer le bouton');
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier si le site est suspect
|
||||
this.suspiciousSiteCheck = window.checkSuspiciousSite ?
|
||||
window.checkSuspiciousSite(window.location.href) :
|
||||
{ isSuspicious: false };
|
||||
|
||||
try {
|
||||
// Supprimer bouton existant
|
||||
document.getElementById('dima-btn')?.remove();
|
||||
document.getElementById('dima-suspicious-alert')?.remove();
|
||||
|
||||
if (this.buttonCreated) return;
|
||||
|
||||
// Créer le bouton principal
|
||||
const button = document.createElement('div');
|
||||
button.id = 'dima-btn';
|
||||
|
||||
@ -80,6 +89,12 @@ class UIManager {
|
||||
});
|
||||
|
||||
document.body?.appendChild(button);
|
||||
|
||||
// Créer l'alerte de site suspect si nécessaire
|
||||
if (this.suspiciousSiteCheck.isSuspicious) {
|
||||
this.createSuspiciousSiteAlert();
|
||||
}
|
||||
|
||||
this.buttonCreated = true;
|
||||
this.log('Bouton créé avec succès');
|
||||
|
||||
@ -88,6 +103,227 @@ class UIManager {
|
||||
}
|
||||
}
|
||||
|
||||
createSuspiciousSiteAlert() {
|
||||
const { siteInfo, riskConfig } = this.suspiciousSiteCheck;
|
||||
|
||||
const alert = document.createElement('div');
|
||||
alert.id = 'dima-suspicious-alert';
|
||||
|
||||
alert.innerHTML = `
|
||||
<div style="display: flex; align-items: start; gap: 12px;">
|
||||
<span style="font-size: 24px;">${riskConfig.icon}</span>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-weight: bold; margin-bottom: 4px; font-size: 14px;">
|
||||
${riskConfig.label}
|
||||
</div>
|
||||
<div style="font-size: 12px; line-height: 1.4; margin-bottom: 8px;">
|
||||
Vigilance : ce site appartient à un dispositif de manipulation de l'information identifié.
|
||||
</div>
|
||||
<button id="dima-suspicious-details" style="
|
||||
background: white;
|
||||
color: ${riskConfig.color};
|
||||
border: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s;
|
||||
">
|
||||
En savoir plus →
|
||||
</button>
|
||||
</div>
|
||||
<button id="dima-suspicious-close" style="
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
padding: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
alert.style.cssText = `
|
||||
position: fixed !important;
|
||||
top: 70px !important;
|
||||
right: 20px !important;
|
||||
z-index: 999998 !important;
|
||||
background: linear-gradient(135deg, ${riskConfig.color}, ${this.adjustColor(riskConfig.color, -15)}) !important;
|
||||
color: white !important;
|
||||
padding: 16px !important;
|
||||
border-radius: 12px !important;
|
||||
max-width: 350px !important;
|
||||
font-family: 'Segoe UI', Arial, sans-serif !important;
|
||||
box-shadow: 0 6px 20px rgba(0,0,0,0.3) !important;
|
||||
border: 2px solid rgba(255,255,255,0.2) !important;
|
||||
animation: slideInRight 0.4s ease-out !important;
|
||||
backdrop-filter: blur(10px) !important;
|
||||
`;
|
||||
|
||||
document.body?.appendChild(alert);
|
||||
|
||||
// Événements
|
||||
document.getElementById('dima-suspicious-details')?.addEventListener('click', () => {
|
||||
this.showSuspiciousSiteDetails();
|
||||
});
|
||||
|
||||
document.getElementById('dima-suspicious-close')?.addEventListener('click', () => {
|
||||
alert.remove();
|
||||
});
|
||||
|
||||
// Hover effects
|
||||
const detailsBtn = document.getElementById('dima-suspicious-details');
|
||||
if (detailsBtn) {
|
||||
detailsBtn.addEventListener('mouseenter', () => {
|
||||
detailsBtn.style.transform = 'translateY(-1px)';
|
||||
detailsBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)';
|
||||
});
|
||||
detailsBtn.addEventListener('mouseleave', () => {
|
||||
detailsBtn.style.transform = 'translateY(0)';
|
||||
detailsBtn.style.boxShadow = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
const closeBtn = document.getElementById('dima-suspicious-close');
|
||||
if (closeBtn) {
|
||||
closeBtn.addEventListener('mouseenter', () => {
|
||||
closeBtn.style.opacity = '1';
|
||||
});
|
||||
closeBtn.addEventListener('mouseleave', () => {
|
||||
closeBtn.style.opacity = '0.7';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
showSuspiciousSiteDetails() {
|
||||
const { siteInfo, riskConfig } = this.suspiciousSiteCheck;
|
||||
|
||||
// Créer modal avec détails
|
||||
const detailsModal = document.createElement('div');
|
||||
detailsModal.id = 'dima-suspicious-details-modal';
|
||||
|
||||
detailsModal.style.cssText = `
|
||||
position: fixed !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
background: rgba(0,0,0,0.75) !important;
|
||||
backdrop-filter: blur(5px) !important;
|
||||
z-index: 10000000 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
font-family: 'Segoe UI', Arial, sans-serif !important;
|
||||
animation: fadeIn 0.3s ease-out !important;
|
||||
`;
|
||||
|
||||
const logoUrl = chrome.runtime.getURL('M82-logo-16.png');
|
||||
|
||||
detailsModal.innerHTML = `
|
||||
<div style="background: white; padding: 30px; border-radius: 20px; max-width: 600px; max-height: 90vh; overflow-y: auto; margin: 20px; box-shadow: 0 25px 50px rgba(0,0,0,0.3); animation: slideIn 0.3s ease-out;">
|
||||
|
||||
<!-- En-tête -->
|
||||
<div style="text-align: center; margin-bottom: 25px; padding-bottom: 20px; border-bottom: 2px solid #f0f0f0;">
|
||||
<div style="display: flex; align-items: center; justify-content: center; gap: 12px; margin-bottom: 10px;">
|
||||
<img src="${logoUrl}"
|
||||
style="width: 24px; height: 24px;"
|
||||
alt="M82 Project"
|
||||
onerror="this.style.display='none'">
|
||||
<h2 style="color: #2c3e50; margin: 0; font-size: 1.8em;">Site Suspect Identifié</h2>
|
||||
</div>
|
||||
<div style="display: inline-block; background: ${riskConfig.color}; color: white; padding: 8px 16px; border-radius: 20px; font-size: 14px; font-weight: 600; margin-top: 10px;">
|
||||
${riskConfig.icon} ${riskConfig.label}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenu -->
|
||||
<div style="margin-bottom: 25px;">
|
||||
<div style="background: linear-gradient(135deg, #fff3cd, #ffeaa7); padding: 20px; border-radius: 12px; border-left: 4px solid ${riskConfig.color}; margin-bottom: 20px;">
|
||||
<h3 style="margin: 0 0 10px 0; color: #856404; font-size: 1.1em;">⚠️ Avertissement</h3>
|
||||
<p style="margin: 0; color: #856404; line-height: 1.6;">
|
||||
${riskConfig.message}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="background: #f8f9fa; padding: 20px; border-radius: 12px; margin-bottom: 15px;">
|
||||
<h4 style="margin: 0 0 12px 0; color: #2c3e50; font-size: 1em;">📋 Détails de l'identification</h4>
|
||||
<div style="display: grid; gap: 12px;">
|
||||
<div>
|
||||
<strong style="color: #7f8c8d; font-size: 0.9em;">Raison :</strong>
|
||||
<div style="color: #2c3e50; margin-top: 4px;">${siteInfo.reason}</div>
|
||||
</div>
|
||||
<div>
|
||||
<strong style="color: #7f8c8d; font-size: 0.9em;">Source du rapport :</strong>
|
||||
<div style="color: #2c3e50; margin-top: 4px;">${siteInfo.source}</div>
|
||||
</div>
|
||||
<div>
|
||||
<strong style="color: #7f8c8d; font-size: 0.9em;">Date d'identification :</strong>
|
||||
<div style="color: #2c3e50; margin-top: 4px;">${new Date(siteInfo.identifiedDate).toLocaleDateString('fr-FR')}</div>
|
||||
</div>
|
||||
${siteInfo.tags && siteInfo.tags.length > 0 ? `
|
||||
<div>
|
||||
<strong style="color: #7f8c8d; font-size: 0.9em;">Catégories :</strong>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px;">
|
||||
${siteInfo.tags.map(tag => `
|
||||
<span style="background: #e9ecef; color: #495057; padding: 4px 10px; border-radius: 12px; font-size: 0.8em;">
|
||||
${tag}
|
||||
</span>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="background: #e8f4f8; padding: 16px; border-radius: 10px; border-left: 4px solid #17a2b8;">
|
||||
<h4 style="margin: 0 0 8px 0; color: #0c5460; font-size: 0.95em;">💡 Recommandations</h4>
|
||||
<ul style="margin: 0; padding-left: 20px; color: #0c5460; line-height: 1.6;">
|
||||
<li>Vérifiez les informations auprès de sources fiables</li>
|
||||
<li>Consultez plusieurs sources avant de partager</li>
|
||||
<li>Soyez attentif aux techniques de manipulation détectées</li>
|
||||
<li>Signalez le contenu suspect si nécessaire</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div style="display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;">
|
||||
<button onclick="window.open('${siteInfo.reportUrl}', '_blank')"
|
||||
style="background: #3498db; color: white; border: none; padding: 12px 24px; border-radius: 8px; cursor: pointer; font-size: 15px; font-weight: 500; transition: all 0.3s; box-shadow: 0 2px 8px rgba(52, 152, 219, 0.3);">
|
||||
📄 Consulter le rapport complet
|
||||
</button>
|
||||
<button onclick="document.getElementById('dima-suspicious-details-modal').remove()"
|
||||
style="background: #95a5a6; color: white; border: none; padding: 12px 24px; border-radius: 8px; cursor: pointer; font-size: 15px; font-weight: 500; transition: all 0.3s;">
|
||||
Fermer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid #e9ecef; color: #7f8c8d; font-size: 0.85em;">
|
||||
Base de données maintenue par
|
||||
<a href="https://m82-project.org/" target="_blank"
|
||||
style="color: #3498db; text-decoration: none; font-weight: 500;">M82 Project</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
detailsModal.addEventListener('click', (e) => {
|
||||
if (e.target === detailsModal) detailsModal.remove();
|
||||
});
|
||||
|
||||
document.body.appendChild(detailsModal);
|
||||
}
|
||||
|
||||
adjustColor(color, amount) {
|
||||
const num = parseInt(color.replace("#", ""), 16);
|
||||
const amt = Math.round(2.55 * amount);
|
||||
@ -101,116 +337,205 @@ class UIManager {
|
||||
|
||||
generateTooltip() {
|
||||
const techniques = this.analysisResults.detectedTechniques.slice(0, 3);
|
||||
return `DIMA Score: ${this.analysisResults.globalScore} (${this.analysisResults.riskLevel})
|
||||
let tooltip = `DIMA Score: ${this.analysisResults.globalScore} (${this.analysisResults.riskLevel})
|
||||
${this.analysisResults.detectedTechniques.length} techniques détectées
|
||||
${techniques.map(t => `• ${t.nom}`).join('\n')}
|
||||
Contenu: ${this.analysisResults.contentLength} caractères`;
|
||||
}
|
||||
|
||||
getScoreIcon(score) {
|
||||
if (score >= 75) return '🚨'; // Critique
|
||||
if (score >= 50) return '⚠️'; // Élevé
|
||||
if (score >= 30) return '⚡'; // Modéré
|
||||
if (score >= 15) return '👀'; // Faible
|
||||
return '✅'; // Très faible
|
||||
}
|
||||
|
||||
generateExecutiveSummary() {
|
||||
const score = this.analysisResults.globalScore;
|
||||
const techniqueCount = this.analysisResults.detectedTechniques.length;
|
||||
const topTechniques = this.analysisResults.detectedTechniques.slice(0, 3);
|
||||
|
||||
let summary = "";
|
||||
|
||||
// Évaluation générale selon le score
|
||||
if (score >= 75) {
|
||||
summary = `🚨 <strong>Manipulation intensive détectée</strong> : Ce contenu présente un niveau critique de techniques manipulatoires (${techniqueCount} technique${techniqueCount > 1 ? 's' : ''}). `;
|
||||
} else if (score >= 50) {
|
||||
summary = `⚠️ <strong>Manipulation significative</strong> : Ce contenu utilise plusieurs techniques suspectes (${techniqueCount} technique${techniqueCount > 1 ? 's' : ''}). `;
|
||||
} else if (score >= 30) {
|
||||
summary = `⚡ <strong>Éléments manipulatoires présents</strong> : Quelques techniques détectées nécessitent votre attention (${techniqueCount} technique${techniqueCount > 1 ? 's' : ''}). `;
|
||||
} else {
|
||||
summary = `👀 <strong>Faible niveau de manipulation</strong> : Peu d'éléments manipulatoires détectés (${techniqueCount} technique${techniqueCount > 1 ? 's' : ''}). `;
|
||||
}
|
||||
|
||||
// Analyse des phases dominantes
|
||||
if (this.analysisResults.phaseScores && Object.keys(this.analysisResults.phaseScores).length > 0) {
|
||||
const sortedPhases = Object.entries(this.analysisResults.phaseScores)
|
||||
.sort(([,a], [,b]) => b - a)
|
||||
.slice(0, 2);
|
||||
${techniques.map(t => `• ${t.nom}`).join('\n')}`;
|
||||
|
||||
if (sortedPhases.length > 0) {
|
||||
const dominantPhase = sortedPhases[0][0];
|
||||
summary += `La manipulation se concentre principalement sur la phase "<strong>${dominantPhase}</strong>" (${this.getPhaseExplanation(dominantPhase)}). `;
|
||||
if (this.suspiciousSiteCheck.isSuspicious) {
|
||||
tooltip += `\n\n⚠️ SITE SUSPECT IDENTIFIÉ`;
|
||||
}
|
||||
}
|
||||
|
||||
// Technique principale
|
||||
if (topTechniques.length > 0) {
|
||||
const mainTechnique = topTechniques[0];
|
||||
summary += `La technique dominante est <strong>${mainTechnique.nom}</strong> avec ${mainTechnique.confidence}% de confiance. `;
|
||||
|
||||
// Conseil spécifique selon la technique
|
||||
summary += this.getTechniqueAdvice(mainTechnique.index);
|
||||
}
|
||||
|
||||
return summary;
|
||||
tooltip += `\nContenu: ${this.analysisResults.contentLength} caractères`;
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
getPhaseEmoji(phase) {
|
||||
const emojis = {
|
||||
'Detect': '👁️',
|
||||
'Informer': '📢',
|
||||
'Mémoriser': '🧠',
|
||||
'Act': '⚡'
|
||||
};
|
||||
return emojis[phase] || '📍';
|
||||
generatePhaseAnalysis() {
|
||||
if (!this.analysisResults || !this.analysisResults.detectedTechniques || this.analysisResults.detectedTechniques.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Analyser les techniques par phase
|
||||
const phaseStats = {
|
||||
'Detect': { count: 0, totalScore: 0, techniques: [], icon: '👁️', color: '#3498db' },
|
||||
'Informer': { count: 0, totalScore: 0, techniques: [], icon: '📢', color: '#e67e22' },
|
||||
'Mémoriser': { count: 0, totalScore: 0, techniques: [], icon: '🧠', color: '#9b59b6' },
|
||||
'Agir': { count: 0, totalScore: 0, techniques: [], icon: '⚡', color: '#e74c3c' }
|
||||
};
|
||||
|
||||
this.analysisResults.detectedTechniques.forEach(technique => {
|
||||
const phase = technique.phase || 'Detect';
|
||||
if (phaseStats[phase]) {
|
||||
phaseStats[phase].count++;
|
||||
phaseStats[phase].totalScore += technique.weightedScore || technique.score || 0;
|
||||
phaseStats[phase].techniques.push(technique);
|
||||
}
|
||||
});
|
||||
|
||||
// Calculer les pourcentages
|
||||
const totalTechniques = this.analysisResults.detectedTechniques.length;
|
||||
const totalScore = Object.values(phaseStats).reduce((sum, phase) => sum + phase.totalScore, 0);
|
||||
|
||||
// Trouver la phase dominante
|
||||
let dominantPhase = null;
|
||||
let maxCount = 0;
|
||||
Object.entries(phaseStats).forEach(([phase, stats]) => {
|
||||
if (stats.count > maxCount) {
|
||||
maxCount = stats.count;
|
||||
dominantPhase = phase;
|
||||
}
|
||||
});
|
||||
|
||||
// Générer l'explication contextuelle
|
||||
const explanation = this.generatePhaseExplanation(dominantPhase, phaseStats, totalTechniques);
|
||||
|
||||
// Générer le HTML
|
||||
return `
|
||||
<div style="background: #f8f9fa; padding: 25px; border-radius: 12px; margin-bottom: 25px; border: 1px solid #e9ecef;">
|
||||
<h3 style="margin: 0 0 15px 0; color: #2c3e50; font-size: 1.2em; display: flex; align-items: center; gap: 10px;">
|
||||
📊 Analyse par Phase DIMA
|
||||
<span style="font-size: 0.7em; color: #7f8c8d; font-weight: normal; font-style: italic;">
|
||||
(Detect, Informer, Mémoriser, Agir)
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
<!-- Explication contextuelle -->
|
||||
<div style="background: linear-gradient(135deg, #e8f4f8, #d4e8f0); padding: 16px; border-radius: 10px; margin-bottom: 20px; border-left: 4px solid ${phaseStats[dominantPhase]?.color || '#3498db'};">
|
||||
<div style="display: flex; align-items: start; gap: 12px;">
|
||||
<span style="font-size: 24px;">${phaseStats[dominantPhase]?.icon || '💡'}</span>
|
||||
<div>
|
||||
<h4 style="margin: 0 0 8px 0; color: #0c5460; font-size: 1em;">
|
||||
Analyse : Phase dominante "${dominantPhase}"
|
||||
</h4>
|
||||
<p style="margin: 0; color: #0c5460; font-size: 0.9em; line-height: 1.5;">
|
||||
${explanation}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Répartition visuelle des phases -->
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 20px;">
|
||||
${Object.entries(phaseStats).map(([phase, stats]) => {
|
||||
const percentage = totalTechniques > 0 ? Math.round((stats.count / totalTechniques) * 100) : 0;
|
||||
const scorePercentage = totalScore > 0 ? Math.round((stats.totalScore / totalScore) * 100) : 0;
|
||||
const isActive = stats.count > 0;
|
||||
|
||||
return `
|
||||
<div style="
|
||||
background: ${isActive ? 'white' : '#f8f9fa'};
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
border: ${isActive ? `2px solid ${stats.color}` : '1px solid #e9ecef'};
|
||||
opacity: ${isActive ? '1' : '0.5'};
|
||||
transition: all 0.3s;
|
||||
">
|
||||
<div style="font-size: 24px; margin-bottom: 8px;">${stats.icon}</div>
|
||||
<div style="font-weight: bold; color: ${stats.color}; font-size: 0.85em; margin-bottom: 4px;">
|
||||
${phase}
|
||||
</div>
|
||||
<div style="font-size: 1.8em; font-weight: bold; color: ${isActive ? stats.color : '#bdc3c7'}; margin-bottom: 4px;">
|
||||
${stats.count}
|
||||
</div>
|
||||
<div style="font-size: 0.75em; color: #7f8c8d; margin-bottom: 8px;">
|
||||
${percentage}% techniques
|
||||
</div>
|
||||
${isActive ? `
|
||||
<div style="background: ${stats.color}20; padding: 4px 8px; border-radius: 6px; font-size: 0.7em; color: ${stats.color}; font-weight: 600;">
|
||||
${scorePercentage}% du score
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
|
||||
<!-- Graphique à barres -->
|
||||
<div style="background: white; padding: 15px; border-radius: 10px; border: 1px solid #e9ecef;">
|
||||
<h4 style="margin: 0 0 15px 0; color: #2c3e50; font-size: 0.95em;">Distribution du score par phase</h4>
|
||||
${Object.entries(phaseStats).map(([phase, stats]) => {
|
||||
const percentage = totalScore > 0 ? (stats.totalScore / totalScore) * 100 : 0;
|
||||
const displayScore = stats.totalScore.toFixed(1);
|
||||
|
||||
return `
|
||||
<div style="margin-bottom: 12px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px;">
|
||||
<span style="font-size: 0.85em; font-weight: 600; color: #2c3e50;">
|
||||
${stats.icon} ${phase}
|
||||
</span>
|
||||
<span style="font-size: 0.8em; color: #7f8c8d;">
|
||||
${displayScore} pts (${Math.round(percentage)}%)
|
||||
</span>
|
||||
</div>
|
||||
<div style="background: #e9ecef; height: 8px; border-radius: 4px; overflow: hidden;">
|
||||
<div style="
|
||||
background: linear-gradient(90deg, ${stats.color}, ${this.adjustColor(stats.color, -15)});
|
||||
width: ${percentage}%;
|
||||
height: 100%;
|
||||
transition: width 0.6s ease-out;
|
||||
border-radius: 4px;
|
||||
"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
|
||||
<!-- Comprendre les phases -->
|
||||
<div style="margin-top: 20px; padding: 15px; background: white; border-radius: 10px; border: 1px solid #e9ecef;">
|
||||
<details style="cursor: pointer;">
|
||||
<summary style="font-weight: 600; color: #2c3e50; font-size: 0.9em; padding: 5px; outline: none;">
|
||||
ℹ️ Comprendre les phases DIMA
|
||||
</summary>
|
||||
<div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid #e9ecef; font-size: 0.85em; line-height: 1.6; color: #555;">
|
||||
<div style="margin-bottom: 10px;">
|
||||
<strong style="color: #3498db;">👁️ Detect (Détecter)</strong> :
|
||||
Techniques visant à capter l'attention et identifier les cibles sensibles aux messages.
|
||||
</div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<strong style="color: #e67e22;">📢 Informer</strong> :
|
||||
Techniques de transmission et cadrage de l'information pour influencer la perception.
|
||||
</div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<strong style="color: #9b59b6;">🧠 Mémoriser</strong> :
|
||||
Techniques d'ancrage mémoriel et de renforcement des messages dans la durée.
|
||||
</div>
|
||||
<div>
|
||||
<strong style="color: #e74c3c;">⚡ Agir</strong> :
|
||||
Techniques d'incitation à l'action et de mobilisation comportementale.
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
getPhaseColor(phase) {
|
||||
const colors = {
|
||||
'Detect': '#e3f2fd', // Bleu clair
|
||||
'Informer': '#f3e5f5', // Violet clair
|
||||
'Mémoriser': '#e8f5e8', // Vert clair
|
||||
'Act': '#fff3e0' // Orange clair
|
||||
};
|
||||
return colors[phase] || '#f5f5f5';
|
||||
generatePhaseExplanation(dominantPhase, phaseStats, totalTechniques) {
|
||||
const explanations = {
|
||||
'Detect': `Le contenu utilise principalement des techniques de <strong>détection et captation d'attention</strong> (${phaseStats['Detect'].count}/${totalTechniques} techniques). Cela suggère une stratégie axée sur l'identification des publics réceptifs et l'accroche initiale. Le contenu cherche à attirer et cibler des audiences spécifiques.`,
|
||||
|
||||
'Informer': `Le contenu se concentre sur des techniques de <strong>transmission et cadrage de l'information</strong> (${phaseStats['Informer'].count}/${totalTechniques} techniques). L'objectif est de contrôler la perception de l'information via le choix des faits présentés, leur contextualisation, et les biais introduits dans le message.`,
|
||||
|
||||
'Mémoriser': `Le contenu privilégie des techniques de <strong>mémorisation et ancrage</strong> (${phaseStats['Mémoriser'].count}/${totalTechniques} techniques). Ces méthodes visent à inscrire durablement les messages dans la mémoire du public, souvent par répétition, simplification ou associations émotionnelles fortes.`,
|
||||
|
||||
'Agir': `Le contenu met l'accent sur des techniques d'<strong>incitation à l'action</strong> (${phaseStats['Agir'].count}/${totalTechniques} techniques). L'objectif est de mobiliser le public vers des comportements spécifiques : partage, engagement, manifestation, ou modification d'opinions et de votes.`
|
||||
};
|
||||
|
||||
// Si plusieurs phases sont également représentées
|
||||
const topPhases = Object.entries(phaseStats)
|
||||
.filter(([_, stats]) => stats.count > 0)
|
||||
.sort((a, b) => b[1].count - a[1].count)
|
||||
.slice(0, 2);
|
||||
|
||||
if (topPhases.length > 1 && topPhases[0][1].count === topPhases[1][1].count) {
|
||||
return `Le contenu présente une <strong>stratégie équilibrée</strong> entre les phases "${topPhases[0][0]}" et "${topPhases[1][0]}" (${topPhases[0][1].count} techniques chacune). Cette combinaison indique une approche sophistiquée visant à la fois à attirer l'attention et à générer un impact durable.`;
|
||||
}
|
||||
|
||||
return explanations[dominantPhase] || 'Analyse de la répartition des techniques de manipulation cognitive détectées selon le modèle DIMA.';
|
||||
}
|
||||
|
||||
getPhaseDescription(phase) {
|
||||
const descriptions = {
|
||||
'Detect': 'Capter l\'attention',
|
||||
'Informer': 'Influencer ou orienter la compréhension',
|
||||
'Mémoriser': 'Ancrer l\'information',
|
||||
'Act': 'Provoquer l\'action'
|
||||
};
|
||||
return descriptions[phase] || phase;
|
||||
}
|
||||
|
||||
getPhaseExplanation(phase) {
|
||||
const explanations = {
|
||||
'Detect': 'techniques pour attirer et capter votre attention',
|
||||
'Informer': 'méthodes pour orienter votre interprétation des faits',
|
||||
'Mémoriser': 'stratégies pour ancrer certaines idées dans votre mémoire',
|
||||
'Act': 'pressions pour vous pousser à agir rapidement'
|
||||
};
|
||||
return explanations[phase] || 'manipulation cognitive';
|
||||
}
|
||||
|
||||
getTechniqueAdvice(techniqueIndex) {
|
||||
const advices = {
|
||||
'TE0500': 'Méfiez-vous des titres sensationnalistes et vérifiez les sources.',
|
||||
'TE0132': 'Prenez du recul face aux messages alarmistes excessifs.',
|
||||
'TE0501': 'Résistez à la pression de l\'urgence et prenez le temps de réfléchir.',
|
||||
'TE0422': 'Vérifiez les qualifications réelles des "experts" cités.',
|
||||
'TE0251': 'Questionnez les affirmations sur ce que "tout le monde" pense.',
|
||||
'TE0221': 'Attention aux généralisations excessives sur des groupes.',
|
||||
'TE0212': 'Ne tirez pas de conclusions générales à partir d\'anecdotes.',
|
||||
'TE0321': 'Cherchez des sources contradictoires pour éviter le biais de confirmation.'
|
||||
};
|
||||
return advices[techniqueIndex] || 'Restez critique et vérifiez les informations.';
|
||||
}
|
||||
|
||||
showModal() {
|
||||
try {
|
||||
this.log('Affichage du modal');
|
||||
@ -235,9 +560,33 @@ Contenu: ${this.analysisResults.contentLength} caractères`;
|
||||
font-family: 'Segoe UI', Arial, sans-serif !important;
|
||||
animation: fadeIn 0.3s ease-out !important;
|
||||
`;
|
||||
// Récupérer l'URL du logo
|
||||
|
||||
const logoUrl = chrome.runtime.getURL('M82-logo-16.png');
|
||||
|
||||
// Construire le contenu avec alerte site suspect si nécessaire
|
||||
let suspiciousAlert = '';
|
||||
if (this.suspiciousSiteCheck.isSuspicious) {
|
||||
const { riskConfig, siteInfo } = this.suspiciousSiteCheck;
|
||||
suspiciousAlert = `
|
||||
<div style="background: linear-gradient(135deg, ${riskConfig.color}, ${this.adjustColor(riskConfig.color, -15)}); color: white; padding: 20px; border-radius: 12px; margin-bottom: 25px; border: 2px solid rgba(255,255,255,0.2);">
|
||||
<div style="display: flex; align-items: start; gap: 12px;">
|
||||
<span style="font-size: 28px;">${riskConfig.icon}</span>
|
||||
<div style="flex: 1;">
|
||||
<h3 style="margin: 0 0 8px 0; font-size: 1.2em;">${riskConfig.label}</h3>
|
||||
<p style="margin: 0 0 12px 0; font-size: 0.95em; line-height: 1.5;">
|
||||
${riskConfig.message}
|
||||
</p>
|
||||
<button onclick="document.getElementById('dima-suspicious-details-modal')?.remove(); document.querySelector('#dima-modal .suspicious-details-btn').click()"
|
||||
class="suspicious-details-btn"
|
||||
style="background: white; color: ${riskConfig.color}; border: none; padding: 8px 16px; border-radius: 8px; cursor: pointer; font-size: 13px; font-weight: 600; transition: all 0.2s;">
|
||||
Voir les détails du rapport →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
modal.innerHTML = `
|
||||
<div style="background: white; padding: 30px; border-radius: 20px; max-width: 900px; max-height: 90vh; overflow-y: auto; margin: 20px; box-shadow: 0 25px 50px rgba(0,0,0,0.3); animation: slideIn 0.3s ease-out;">
|
||||
|
||||
@ -257,6 +606,8 @@ Contenu: ${this.analysisResults.contentLength} caractères`;
|
||||
</p>
|
||||
</div>
|
||||
|
||||
${suspiciousAlert}
|
||||
|
||||
<!-- Métriques principales -->
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 15px; margin-bottom: 25px;">
|
||||
<div style="background: linear-gradient(135deg, ${this.analysisResults.riskColor}, ${this.adjustColor(this.analysisResults.riskColor, -15)}); color: white; padding: 20px; border-radius: 12px; text-align: center; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
|
||||
@ -277,17 +628,7 @@ Contenu: ${this.analysisResults.contentLength} caractères`;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Résumé exécutif -->
|
||||
${this.analysisResults.globalScore > 15 ? `
|
||||
<div style="background: ${this.analysisResults.globalScore >= 50 ? '#ffebee' : this.analysisResults.globalScore >= 30 ? '#fff3e0' : '#e8f5e8'}; padding: 20px; border-radius: 12px; margin-bottom: 25px; border-left: 4px solid ${this.analysisResults.riskColor};">
|
||||
<h4 style="margin: 0 0 10px 0; color: ${this.analysisResults.riskColor}; font-size: 1.1em;">
|
||||
${this.getScoreIcon(this.analysisResults.globalScore)} Résumé de l'analyse
|
||||
</h4>
|
||||
<p style="margin: 0; color: #444; line-height: 1.5; font-size: 0.95em;">
|
||||
${this.generateExecutiveSummary()}
|
||||
</p>
|
||||
</div>
|
||||
` : ''}
|
||||
${this.generatePhaseAnalysis()}
|
||||
|
||||
<!-- Informations sur la page -->
|
||||
<div style="background: #f8f9fa; padding: 20px; border-radius: 12px; margin-bottom: 25px; border: 1px solid #e9ecef;">
|
||||
@ -300,38 +641,6 @@ Contenu: ${this.analysisResults.contentLength} caractères`;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Répartition par phase DIMA -->
|
||||
${this.analysisResults.phaseScores && Object.keys(this.analysisResults.phaseScores).length > 0 ? `
|
||||
<div style="background: #f8f9fa; padding: 20px; border-radius: 12px; margin-bottom: 25px; border: 1px solid #e9ecef;">
|
||||
<h4 style="margin: 0 0 15px 0; color: #2c3e50; font-size: 1.1em;">📊 Répartition par phase DIMA</h4>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 15px;">
|
||||
${Object.entries(this.analysisResults.phaseScores).map(([phase, score]) => {
|
||||
const maxScore = Math.max(...Object.values(this.analysisResults.phaseScores));
|
||||
const percentage = maxScore > 0 ? (score / maxScore) * 100 : 0;
|
||||
return `
|
||||
<div style="text-align: center;">
|
||||
<div style="font-size: 0.85em; color: #666; margin-bottom: 8px; font-weight: 500;">
|
||||
${this.getPhaseEmoji(phase)} ${phase}
|
||||
</div>
|
||||
<div style="background: ${this.getPhaseColor(phase)}; height: 12px; border-radius: 6px; margin-bottom: 8px; overflow: hidden;">
|
||||
<div style="background: ${this.analysisResults.riskColor}; height: 100%; width: ${Math.min(percentage, 100)}%; border-radius: 6px; transition: width 0.8s ease;"></div>
|
||||
</div>
|
||||
<div style="font-size: 0.8em; font-weight: bold; color: #333;">
|
||||
${score.toFixed(1)} pts
|
||||
</div>
|
||||
<div style="font-size: 0.7em; color: #888;">
|
||||
${this.getPhaseDescription(phase)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
<div style="margin-top: 15px; padding-top: 15px; border-top: 1px solid #e9ecef; font-size: 0.8em; color: #666; text-align: center;">
|
||||
💡 La matrice DIMA analyse comment l'information traverse les 4 phases cognitives
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<!-- Message si aucune technique -->
|
||||
${this.analysisResults.detectedTechniques.length === 0 ? `
|
||||
<div style="background: linear-gradient(135deg, #d4edda, #c3e6cb); color: #155724; padding: 25px; border-radius: 12px; text-align: center; border: 1px solid #c3e6cb;">
|
||||
@ -370,7 +679,7 @@ Contenu: ${this.analysisResults.contentLength} caractères`;
|
||||
${technique.matchedKeywords?.length > 0 ? `
|
||||
<div style="margin-top: 10px;">
|
||||
<div style="font-size: 0.85em; color: #666; margin-bottom: 6px; font-weight: 500;">
|
||||
🔍 Mots-clés détectés:
|
||||
🔎 Mots-clés détectés:
|
||||
</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 4px;">
|
||||
${technique.matchedKeywords.slice(0, 4).map(keyword =>
|
||||
@ -414,6 +723,10 @@ Contenu: ${this.analysisResults.contentLength} caractères`;
|
||||
from { transform: translateY(30px); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
@keyframes slideInRight {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
#dima-modal button:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
@ -425,6 +738,11 @@ Contenu: ${this.analysisResults.contentLength} caractères`;
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) modal.remove();
|
||||
});
|
||||
|
||||
// Ajouter l'événement pour le bouton des détails du site suspect
|
||||
modal.querySelector('.suspicious-details-btn')?.addEventListener('click', () => {
|
||||
this.showSuspiciousSiteDetails();
|
||||
});
|
||||
|
||||
document.body.appendChild(modal);
|
||||
this.log('Modal affiché');
|
||||
@ -434,5 +752,6 @@ Contenu: ${this.analysisResults.contentLength} caractères`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make UIManager available globally for Chrome extension
|
||||
window.UIManager = UIManager;
|
||||
window.UIManager = UIManager;
|
||||
Загрузка…
x
Ссылка в новой задаче
Block a user