Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 45 additions & 12 deletions content.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,32 +102,58 @@ class DIMAAnalyzer {

// ===== INITIALISATION ET STYLES =====

// CSS pour les animations
// CSS pour les animations (tous les @keyframes utilisés par l'extension
// sont définis ici une seule fois pour qu'ils soient disponibles avant
// l'apparition du bouton, de l'alerte de site suspect ou du modal).
// Tous les noms sont préfixés `dima*` car @keyframes partage un namespace
// global avec la page hôte; des noms génériques (fadeIn, slideIn) auraient
// pu écraser ou être écrasés par les animations existantes du site visité.
const style = document.createElement("style");
style.textContent = `
@keyframes dimaFadeIn {
@keyframes dimaFadeInScale {
from { opacity: 0; transform: scale(0.9); }
to { opacity: 1; transform: scale(1); }
}

@keyframes dimaFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes dimaSlideIn {
from { transform: translateY(30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes dimaSlideInRight {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}

#dima-btn {
animation: dimaFadeIn 0.5s ease-out;
animation: dimaFadeInScale 0.5s ease-out;
}
`;
document.head.appendChild(style);

// Initialisation sécurisée avec gestion d'erreurs améliorée
console.log("DIMA: Script chargé - Version complète avec mots-clés améliorés");
// Initialisation sécurisée. Le log d'init est derrière le flag debug pour
// rester silencieux sur les sites hôtes (l'extension tourne sur <all_urls>).
const _dimaDebug = () => !!window.DIMA_DEBUG;
if (_dimaDebug()) {
console.log("DIMA: Script chargé - Version complète avec mots-clés améliorés");
}

// Vérifier que toutes les dépendances sont chargées
// Vérifier que toutes les dépendances sont chargées. Ces dépendances sont
// censées être présentes dès l'exécution de content.js (dernier script
// déclaré dans le manifest, document_end, ordre garanti). Le retry sert
// uniquement de filet de sécurité pour les déploiements où l'ordre des
// scripts viendrait à changer.
function checkDependencies() {
return (
window.DIMA_TECHNIQUES &&
window.DIMA_ENHANCED_KEYWORDS &&
window.CONTEXT_PATTERNS &&
window.ContentExtractor &&
window.TechniqueAnalyzer &&
window.UIManager
window.UIManager &&
typeof window.checkSuspiciousSite === "function"
Comment on lines 152 to +156
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Touché, et c'est la cause racine plus que le symptôme. Le setTimeout(100) dans Suspicioussitesmanager.js était lui-même inutile : en MV3 avec run_at: document_end et un seul bloc content_scripts listé en ordre dans manifest.json, les fichiers s'exécutent en séquence — à l'exécution de ce module les bases (copycopDomains, …) sont déjà déclarées.

Dans 63a7651 :

  1. Suspicioussitesmanager.js : suppression du setTimeout(100). window.checkSuspiciousSite est publié de manière synchrone, donc content.js ne touche jamais la boucle de retry en fonctionnement normal.
  2. content.js : les trois console.log info-level (Script chargé, message de retry, Analyseur initialisé) passent derrière window.DIMA_DEBUG. Les console.error (échec de dépendances, exception d'init) restent inconditionnels.

Verrouillé par 2 tests dans tests/syncInit.test.js côté PR #24 : pas de setTimeout( dans le bootstrap, et window.checkSuspiciousSite est une fonction immédiatement après le chargement du module.

);
}

Expand All @@ -140,16 +166,23 @@ function initializeDIMA(retryCount = 0) {
console.error("DIMA: Échec du chargement des dépendances après 3 secondes.");
return;
}
console.log(`DIMA: Attente du chargement des dépendances... (${retryCount + 1}/${MAX_RETRIES})`);
// Log de retry derrière le flag debug. En fonctionnement normal le
// premier check passe et on n'imprime rien — pas de bruit sur la
// console de la page hôte.
if (_dimaDebug()) {
console.log(`DIMA: Attente du chargement des dépendances... (${retryCount + 1}/${MAX_RETRIES})`);
}
setTimeout(() => initializeDIMA(retryCount + 1), 100);
return;
}

try {
const analyzer = new DIMAAnalyzer();
console.log(
`DIMA: Analyseur initialisé pour page de type: ${analyzer.pageType}`
);
if (_dimaDebug()) {
console.log(
`DIMA: Analyseur initialisé pour page de type: ${analyzer.pageType}`
);
}
} catch (error) {
console.error("DIMA: Erreur d'initialisation critique:", error);
}
Expand Down
2 changes: 1 addition & 1 deletion data/keywords.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const DIMA_ENHANCED_KEYWORDS = {
patterns: [
/\d+\s+(?:choses|façons|méthodes|secrets|things|ways|methods)\s+(?:que|pour|de|to|that)/i,
/(?:voici|découvrez|here's|discover)\s+(?:comment|pourquoi|ce que|how|why|what)/i,
/(?:cette|cette|this)\s+\w+\s+va\s+vous\s+(?:\w+|will)/i,
/(?:cette|this)\s+\w+\s+va\s+vous\s+(?:\w+|will)/i,
/(?:shocking|amazing|incredible)\s+(?:secret|truth|fact)/i,
],
},
Expand Down
109 changes: 59 additions & 50 deletions modules/Suspicioussitesmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,35 @@ class SuspiciousSitesManager {
byTags: {},
bySocialPlatform: {}
};

// Logs verbeux désactivés par défaut: l'extension s'exécute sur
// <all_urls> et inondait la console de chaque page hôte.
this.debug = !!(typeof window !== 'undefined' && window.DIMA_DEBUG);

this.init();
}

log(...args) {
if (this.debug) console.log(...args);
}

/**
* Initialise le gestionnaire en chargeant toutes les sources disponibles
*/
init() {
console.log('🛡️ DIMA: Initialisation du gestionnaire de sites suspects...');
this.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.log(`✅ DIMA: ${this.allSites.length} entrées chargées depuis ${this.sources.size} source(s)`);
this.log(` - ${this.stats.totalDomains} domaines`);
this.log(` - ${this.stats.totalSocialAccounts} comptes de réseaux sociaux`);
this.logStats();
}

Expand All @@ -57,7 +64,7 @@ class SuspiciousSitesManager {
reportUrl: 'https://www.recordedfuture.com/research/cta-ru-2025-0917',
reportDate: '2025-09-17'
});
console.log(` ✓ Source CopyCop chargée: ${copycopDomains.length} domaines`);
this.log(` ✓ Source CopyCop chargée: ${copycopDomains.length} domaines`);
}

// Source 2: RRN (VIGINUM)
Expand All @@ -69,7 +76,7 @@ class SuspiciousSitesManager {
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`);
this.log(` ✓ Source RRN chargée: ${rrnDomains.length} domaines`);
}

// Source 3: Portal Kombat (VIGINUM)
Expand All @@ -81,7 +88,7 @@ class SuspiciousSitesManager {
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`);
this.log(` ✓ Source Portal Kombat chargée: ${portalKombatDomains.length} domaines`);
}

// Source 4: Baybridge (IRSEM)
Expand All @@ -93,7 +100,7 @@ class SuspiciousSitesManager {
reportUrl: 'https://www.irsem.fr/focus',
reportDate: '2025-10-17'
});
console.log(` ✓ Source Baybridge chargée: ${baybridgeDomains.length} domaines`);
this.log(` ✓ Source Baybridge chargée: ${baybridgeDomains.length} domaines`);
}

// Source 5: Storm 1516 - Domaines (VIGINUM)
Expand All @@ -105,7 +112,7 @@ class SuspiciousSitesManager {
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`);
this.log(` ✓ Source Storm 1516 (domaines) chargée: ${storm1516Domains.length} domaines`);
}

// Source 6: Storm 1516 - Comptes sociaux (VIGINUM) - FORMAT NATIF
Expand All @@ -117,7 +124,7 @@ class SuspiciousSitesManager {
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`);
this.log(` ✓ Source Storm 1516 (comptes sociaux) chargée: ${storm1516SocialAccounts.length} comptes`);
}

// Source 7: Pravda
Expand All @@ -129,7 +136,7 @@ class SuspiciousSitesManager {
reportUrl: 'https://www.sgdsn.gouv.fr/files/files/20240212_NP_SGDSN_VIGINUM_PORTAL-KOMBAT-NETWORK_ENG_VF.pdf',
reportDate: '2024-12-02'
});
console.log(` ✓ Source Pravda chargée: ${pravdaDomains.length} domaines`);
this.log(` ✓ Source Pravda chargée: ${pravdaDomains.length} domaines`);
}

// Source 8: Doppelganger - noms de domaines
Expand All @@ -141,7 +148,7 @@ class SuspiciousSitesManager {
reportUrl: 'https://en.wikipedia.org/wiki/List_of_political_disinformation_website_campaigns_in_Russia',
reportDate: '2023-11-23'
});
console.log(` ✓ Source Doppelganger chargée: ${doppelgangerDomains.length} domaines`);
this.log(` ✓ Source Doppelganger chargée: ${doppelgangerDomains.length} domaines`);
}

// Source 9: InfoRos - noms de domaines
Expand All @@ -153,7 +160,7 @@ class SuspiciousSitesManager {
reportUrl: 'https://openfacto.fr/2022/01/27/the-grus-galaxy-of-russian-speaking-websites/',
reportDate: '2022-01-27'
});
console.log(` ✓ Source InfoRos chargée: ${infoRosDomains.length} domaines`);
this.log(` ✓ Source InfoRos chargée: ${infoRosDomains.length} domaines`);
}

// Source 10: Laundromat - noms de domaines
Expand All @@ -165,7 +172,7 @@ class SuspiciousSitesManager {
reportUrl: 'https://securingdemocracy.gmfus.org/wp-content/uploads/2024/05/Laundromat-Paper.pdf',
reportDate: '2024-05-01'
});
console.log(` ✓ Source laundromat chargée: ${laundromatDomains.length} domaines`);
this.log(` ✓ Source laundromat chargée: ${laundromatDomains.length} domaines`);
}

// Avertissement si aucune source n'est chargée
Expand Down Expand Up @@ -247,20 +254,20 @@ class SuspiciousSitesManager {
* 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}`);
this.log('📊 Statistiques:');
this.log(` Total: ${this.stats.totalSites} entrées`);
this.log(` - Domaines: ${this.stats.totalDomains}`);
this.log(` - Comptes sociaux: ${this.stats.totalSocialAccounts}`);
if (this.stats.totalSocialAccounts > 0) {
console.log(' Répartition par plateforme:');
this.log(' Répartition par plateforme:');
for (const [platform, count] of Object.entries(this.stats.bySocialPlatform)) {
console.log(` • ${platform}: ${count}`);
this.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}`);
this.log(` Risque élevé: ${this.stats.byRiskLevel.high || 0}`);
this.log(` Risque moyen: ${this.stats.byRiskLevel.medium || 0}`);
this.log(` Risque faible: ${this.stats.byRiskLevel.low || 0}`);
this.log(` Sources: ${Object.keys(this.stats.bySources).length}`);
}

/**
Expand Down Expand Up @@ -317,7 +324,7 @@ class SuspiciousSitesManager {
}

if (isMatch) {
console.log(`🎯 DIMA: Match trouvé!`, {
this.log(`🎯 DIMA: Match trouvé!`, {
type: matchType,
site: site.handle || site.domain,
url: url
Expand Down Expand Up @@ -399,7 +406,7 @@ class SuspiciousSitesManager {
}

if (extractedHandle) {
console.log(`🔍 DIMA: Comparaison - URL: "${extractedHandle}" vs DB: "${handle}"`);
this.log(`🔍 DIMA: Comparaison - URL: "${extractedHandle}" vs DB: "${handle}"`);
return extractedHandle === handle;
}

Expand Down Expand Up @@ -464,7 +471,7 @@ class SuspiciousSitesManager {
const match = pathname.match(pattern.regex);
if (match) {
const handle = match[1];
console.log(`DIMA: Handle extrait de ${accountType}: ${handle}`);
this.log(`DIMA: Handle extrait de ${accountType}: ${handle}`);
return handle;
}

Expand Down Expand Up @@ -560,25 +567,27 @@ class SuspiciousSitesManager {
}
}

// Initialisation automatique du gestionnaire
let suspiciousSitesManager;

// Initialiser après le chargement de toutes les bases de données
// Initialisation automatique du gestionnaire.
//
// Historique: un setTimeout(100ms) entourait cette init pour "laisser les
// autres fichiers se charger". Mais en MV3 avec `run_at: document_end`, les
// scripts du content_scripts sont chargés et exécutés dans l'ordre déclaré
// dans manifest.json — à ce point toutes les bases (copycopDomains, ...)
// sont déjà définies. Le délai était donc inutile et créait une fenêtre
// pendant laquelle `window.checkSuspiciousSite` n'existait pas, forçant
// content.js à boucler sur ses retries pendant >=100ms à chaque page.
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);
const 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();
}

// Export pour Node.js si nécessaire
Expand Down
Loading