зеркало из
https://github.com/M82-project/DIMA.git
synced 2025-10-29 13:06:08 +02:00
Сравнить коммиты
6 Коммитов
e2382bbb29
...
98aa04e1e4
| Автор | SHA1 | Дата | |
|---|---|---|---|
|
|
98aa04e1e4 | ||
|
|
82bf96445a | ||
|
|
a715246f0b | ||
|
|
07f11cc250 | ||
|
|
e93c24ae8f | ||
|
|
0ae313ccfe |
599
plugin/plugin_chrome/Calcul_score_doc_technique.md
Обычный файл
599
plugin/plugin_chrome/Calcul_score_doc_technique.md
Обычный файл
@ -0,0 +1,599 @@
|
||||
# Calcul du Score DIMA - Documentation Technique
|
||||
|
||||
## Table des matières
|
||||
|
||||
1. [Vue d'ensemble](#vue-densemble)
|
||||
2. [Architecture du système](#architecture-du-système)
|
||||
3. [Processus de calcul détaillé](#processus-de-calcul-détaillé)
|
||||
4. [Pondérations et coefficients](#pondérations-et-coefficients)
|
||||
5. [Contextes Regex](#contextes-regex)
|
||||
6. [Niveaux de risque](#niveaux-de-risque)
|
||||
7. [Exemples de calcul](#exemples-de-calcul)
|
||||
|
||||
---
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le **score DIMA** (Détection et Identification des Manipulations en ligne et Analytiques) est un indicateur quantitatif qui mesure le niveau de techniques de manipulation détectées dans un contenu web. Le score final est compris entre **0 et 100**, où :
|
||||
|
||||
- **0** = Aucune manipulation détectée
|
||||
- **100** = Niveau critique de manipulation
|
||||
|
||||
### Principes fondamentaux
|
||||
|
||||
Le système analyse uniquement les **techniques de manipulation (TE)**, pas les tactiques (TA). Chaque technique détectée contribue au score global selon :
|
||||
|
||||
1. Le nombre d'occurrences
|
||||
2. Les pondérations multiples appliquées
|
||||
3. Le contexte de la page
|
||||
4. Les patterns linguistiques détectés
|
||||
|
||||
---
|
||||
|
||||
## Architecture du système
|
||||
|
||||
### Composants principaux
|
||||
|
||||
```
|
||||
Texte à analyser
|
||||
↓
|
||||
Détection de mots-clés/patterns
|
||||
↓
|
||||
Application des pondérations
|
||||
↓
|
||||
Agrégation des scores
|
||||
↓
|
||||
Score DIMA final (0-100)
|
||||
```
|
||||
|
||||
### Types d'analyse
|
||||
|
||||
Le système propose deux modes d'analyse :
|
||||
|
||||
1. **Analyse basique** : Recherche simple de mots-clés
|
||||
2. **Analyse améliorée** : Inclut variantes, patterns regex et contextes
|
||||
|
||||
---
|
||||
|
||||
## Processus de calcul détaillé
|
||||
|
||||
### Étape 1 : Détection des correspondances
|
||||
|
||||
Pour chaque technique, le système recherche :
|
||||
|
||||
#### A. Mots-clés de base (Core Keywords)
|
||||
- **Poids** : `1.0`
|
||||
- Recherche exacte avec frontières de mots
|
||||
- Exemple : "urgent", "gratuit", "exclusif"
|
||||
|
||||
#### B. Variantes par catégorie
|
||||
|
||||
| Catégorie | Poids | Description |
|
||||
|-----------|-------|-------------|
|
||||
| `formal` | `0.9` | Variantes formelles (registre soutenu) |
|
||||
| `informal` | `1.1` | Variantes informelles (registre familier) |
|
||||
| `clickbait_formulas` | `1.6` | Formules clickbait typiques |
|
||||
| `emotional_hooks` | `1.4` | Accroches émotionnelles |
|
||||
| `curiosity_gaps` | `1.5` | Trous de curiosité |
|
||||
| `urgency` | `1.3` | Marqueurs d'urgence |
|
||||
| `scarcity` | `1.4` | Marqueurs de rareté |
|
||||
| `temporal` | `1.2` | Marqueurs temporels |
|
||||
|
||||
#### C. Variantes par intensité
|
||||
|
||||
| Intensité | Poids | Exemple |
|
||||
|-----------|-------|---------|
|
||||
| `weak` | `0.7` | "peut-être", "possiblement" |
|
||||
| `strong` | `1.5` | "ABSOLUMENT", "JAMAIS" |
|
||||
|
||||
#### D. Patterns Regex
|
||||
- **Poids** : `1.5`
|
||||
- Détection de structures linguistiques complexes
|
||||
- Exemples : questions rhétoriques, formules répétitives, etc.
|
||||
|
||||
### Étape 2 : Calcul du score brut
|
||||
|
||||
```javascript
|
||||
score_brut = Σ(poids de chaque correspondance)
|
||||
```
|
||||
|
||||
**Exemple** :
|
||||
- 3 mots-clés de base (×1.0) = 3 points
|
||||
- 2 variantes clickbait (×1.6) = 3.2 points
|
||||
- 1 pattern regex (×1.5) = 1.5 points
|
||||
- **Total** : 7.7 points
|
||||
|
||||
### Étape 3 : Application des boosts contextuels
|
||||
|
||||
Les **contextes regex** sont des patterns qui amplifient le score lorsqu'ils sont détectés.
|
||||
|
||||
```javascript
|
||||
score_avec_contexte = score_brut × boost_1 × boost_2 × ... × boost_n
|
||||
```
|
||||
|
||||
**Exemple** :
|
||||
- Score brut : 7.7
|
||||
- Contexte d'urgence détecté (×1.3)
|
||||
- Contexte émotionnel détecté (×1.4)
|
||||
- **Score avec contexte** : 7.7 × 1.3 × 1.4 = **14.0**
|
||||
|
||||
### Étape 4 : Pondérations multiples
|
||||
|
||||
Chaque technique reçoit trois types de pondérations :
|
||||
|
||||
#### A. Poids de base (`technique.weight`)
|
||||
- Défini par la gravité intrinsèque de la technique
|
||||
- Valeur par défaut : `1.0`
|
||||
- Peut varier selon l'importance de la technique
|
||||
|
||||
#### B. Poids contextuel (`contextualWeight`)
|
||||
|
||||
Adapté selon le type de page :
|
||||
|
||||
**Page NEWS**
|
||||
| Technique | Code | Poids |
|
||||
|-----------|------|-------|
|
||||
| Désinformation | TE0500 | `1.4` |
|
||||
| Appel à l'émotion | TE0132 | `1.3` |
|
||||
| Polarisation | TE0221 | `1.5` |
|
||||
| Titre sensationnaliste | TE0212 | `1.3` |
|
||||
| Citation hors contexte | TE0261 | `0.8` |
|
||||
|
||||
**Page SOCIAL**
|
||||
| Technique | Code | Poids |
|
||||
|-----------|------|-------|
|
||||
| Appel à l'émotion | TE0132 | `0.9` |
|
||||
| Témoignage fabriqué | TE0131 | `0.8` |
|
||||
| Preuve sociale | TE0501 | `1.3` |
|
||||
| Polarisation | TE0221 | `1.6` |
|
||||
| Bandwagon effect | TE0251 | `1.2` |
|
||||
|
||||
**Page COMMERCE**
|
||||
| Technique | Code | Poids |
|
||||
|-----------|------|-------|
|
||||
| Preuve sociale | TE0501 | `0.9` |
|
||||
| Scarcité artificielle | TE0141 | `0.8` |
|
||||
| Urgence artificielle | TE0143 | `0.7` |
|
||||
| Ancrage de prix | TE0422 | `1.2` |
|
||||
| Promotion trompeuse | TE0411 | `1.1` |
|
||||
|
||||
**Page BLOG**
|
||||
| Technique | Code | Poids |
|
||||
|-----------|------|-------|
|
||||
| Titre sensationnaliste | TE0212 | `0.8` |
|
||||
| Simplification excessive | TE0314 | `0.9` |
|
||||
| Citation hors contexte | TE0261 | `0.7` |
|
||||
| Liste numérotée | TE0321 | `1.1` |
|
||||
|
||||
#### C. Poids dynamique (`dynamicWeight`)
|
||||
|
||||
Basé sur le nombre d'occurrences :
|
||||
|
||||
| Occurrences | Poids de base |
|
||||
|-------------|---------------|
|
||||
| 1-2 | `1.0` |
|
||||
| 3-4 | `1.1` |
|
||||
| 5-6 | `1.2` |
|
||||
| 7-9 | `1.3` |
|
||||
| 10+ | `1.4` |
|
||||
|
||||
**Techniques critiques** (bonus ×1.1 dès 2 occurrences) :
|
||||
- TE0221 (Polarisation)
|
||||
- TE0500 (Désinformation)
|
||||
- TE0132 (Appel à l'émotion)
|
||||
- TE0501 (Preuve sociale)
|
||||
|
||||
**Techniques bénignes** (malus ×0.9 à partir de 5 occurrences) :
|
||||
- TE0143 (Urgence artificielle)
|
||||
- TE0232 (Répétition)
|
||||
- TE0333 (Analogie simpliste)
|
||||
|
||||
### Étape 5 : Calcul du score pondéré
|
||||
|
||||
```javascript
|
||||
poids_total = technique.weight × contextualWeight × dynamicWeight
|
||||
|
||||
score_pondéré = score_avec_contexte × poids_total
|
||||
```
|
||||
|
||||
**Exemple complet** :
|
||||
- Score avec contexte : 14.0
|
||||
- Poids de base : 1.0
|
||||
- Poids contextuel (news) : 1.5
|
||||
- Poids dynamique (5 occurrences) : 1.2
|
||||
- **Poids total** : 1.0 × 1.5 × 1.2 = 1.8
|
||||
- **Score pondéré** : 14.0 × 1.8 = **25.2**
|
||||
|
||||
### Étape 6 : Score global
|
||||
|
||||
```javascript
|
||||
total_score = Σ(tous les scores pondérés)
|
||||
|
||||
score_global = min(round(total_score × 3), 100)
|
||||
```
|
||||
|
||||
Le **facteur d'amplification ×3** permet d'étaler les scores sur la plage 0-100.
|
||||
|
||||
**Exemple final** :
|
||||
- Technique 1 : 25.2 points
|
||||
- Technique 2 : 18.5 points
|
||||
- Technique 3 : 12.0 points
|
||||
- **Total** : 55.7 points
|
||||
- **Score global** : min(round(55.7 × 3), 100) = min(167, 100) = **100**
|
||||
|
||||
---
|
||||
|
||||
## Contextes Regex
|
||||
|
||||
### Qu'est-ce qu'un contexte regex ?
|
||||
|
||||
Les **contextes regex** sont des expressions régulières qui détectent des structures linguistiques spécifiques dans le texte. Lorsqu'un contexte est détecté, il **amplifie le score** des techniques associées.
|
||||
|
||||
### Architecture des contextes
|
||||
|
||||
```javascript
|
||||
contextPatterns = {
|
||||
"nom_du_contexte": {
|
||||
techniques: ["TE0xxx", "TE0yyy"], // Techniques concernées
|
||||
boost: 1.3, // Multiplicateur
|
||||
patterns: [ // Expressions régulières
|
||||
/pattern1/gi,
|
||||
/pattern2/gi
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Exemples de contextes courants
|
||||
|
||||
#### 1. Contexte d'urgence
|
||||
|
||||
```javascript
|
||||
{
|
||||
techniques: ["TE0143", "TE0221", "TE0141"],
|
||||
boost: 1.3,
|
||||
patterns: [
|
||||
/urgent|maintenant|immédiatement|vite|rapidement/gi,
|
||||
/dernière chance|derniers jours|plus que \d+ heures/gi,
|
||||
/ne tardez (pas|plus)|dépêchez-vous/gi
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Effet** : Si le texte contient "URGENT : Plus que 24 heures !", les techniques d'urgence voient leur score multiplié par **×1.3**.
|
||||
|
||||
#### 2. Contexte émotionnel
|
||||
|
||||
```javascript
|
||||
{
|
||||
techniques: ["TE0132", "TE0212"],
|
||||
boost: 1.4,
|
||||
patterns: [
|
||||
/choquant|scandaleux|incroyable|inacceptable/gi,
|
||||
/vous ne croirez (jamais|pas)|hallucinant/gi,
|
||||
/indignation|colère|rage|honte/gi
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Effet** : "Ce scandale CHOQUANT va vous mettre en colère" → multiplicateur **×1.4**.
|
||||
|
||||
#### 3. Contexte de curiosité
|
||||
|
||||
```javascript
|
||||
{
|
||||
techniques: ["TE0212", "TE0321"],
|
||||
boost: 1.5,
|
||||
patterns: [
|
||||
/vous ne devinerez jamais|vous allez être surpris/gi,
|
||||
/ce qui s'est passé ensuite|la suite va vous étonner/gi,
|
||||
/\d+ (raisons|choses|secrets) que .* ne veut pas/gi
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Effet** : "10 secrets que les médias ne veulent pas révéler" → multiplicateur **×1.5**.
|
||||
|
||||
#### 4. Contexte de rareté
|
||||
|
||||
```javascript
|
||||
{
|
||||
techniques: ["TE0141", "TE0143"],
|
||||
boost: 1.4,
|
||||
patterns: [
|
||||
/stock limité|quantité limitée|édition limitée/gi,
|
||||
/plus que \d+ (disponibles?|restants?|en stock)/gi,
|
||||
/dernier exemplaire|derniers articles/gi
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Effet** : "Stock limité : plus que 5 disponibles" → multiplicateur **×1.4**.
|
||||
|
||||
#### 5. Contexte de preuve sociale
|
||||
|
||||
```javascript
|
||||
{
|
||||
techniques: ["TE0501", "TE0251"],
|
||||
boost: 1.3,
|
||||
patterns: [
|
||||
/\d+(\s*\d+)* (personnes?|clients?|utilisateurs?) (ont|achètent)/gi,
|
||||
/tout le monde|la plupart des gens|millions de/gi,
|
||||
/rejoignez les \d+|déjà \d+ membres/gi
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Effet** : "Déjà 50 000 clients satisfaits !" → multiplicateur **×1.3**.
|
||||
|
||||
#### 6. Contexte de question rhétorique
|
||||
|
||||
```javascript
|
||||
{
|
||||
techniques: ["TE0212", "TE0221"],
|
||||
boost: 1.2,
|
||||
patterns: [
|
||||
/(pourquoi|comment|qui|que|quoi) .{10,80}\?/gi,
|
||||
/n'est-ce pas|vous ne pensez pas|vous aussi/gi
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Effet** : "Pourquoi les médias cachent-ils cette vérité ?" → multiplicateur **×1.2**.
|
||||
|
||||
### Cumul des contextes
|
||||
|
||||
Les boosts contextuels se **multiplient entre eux** :
|
||||
|
||||
```javascript
|
||||
score_final = score_brut × boost_1 × boost_2 × boost_3
|
||||
```
|
||||
|
||||
**Exemple concret** :
|
||||
|
||||
Texte analysé :
|
||||
> "URGENT : Ce scandale CHOQUANT va vous mettre en colère ! Plus que 48 heures pour découvrir la vérité que 10 000 personnes connaissent déjà !"
|
||||
|
||||
Contextes détectés :
|
||||
1. Urgence (×1.3) : "URGENT", "Plus que 48 heures"
|
||||
2. Émotion (×1.4) : "CHOQUANT", "colère"
|
||||
3. Preuve sociale (×1.3) : "10 000 personnes"
|
||||
|
||||
**Calcul** :
|
||||
- Score brut : 10 points
|
||||
- Avec contextes : 10 × 1.3 × 1.4 × 1.3 = **23.66 points**
|
||||
|
||||
---
|
||||
|
||||
## Niveaux de risque
|
||||
|
||||
Le score global est converti en un niveau de risque qualitatif :
|
||||
|
||||
| Score | Niveau | Couleur | Code couleur | Description |
|
||||
|-------|--------|---------|--------------|-------------|
|
||||
| 0-14 | **Faible** | Vert | `#27ae60` | Peu ou pas de manipulation détectée |
|
||||
| 15-29 | **Modéré** | Orange clair | `#f39c12` | Techniques légères présentes |
|
||||
| 30-49 | **Élevé** | Orange | `#e67e22` | Manipulation significative |
|
||||
| 50-74 | **Très Élevé** | Rouge-orange | `#d35400` | Manipulation importante |
|
||||
| 75-100 | **Critique** | Rouge foncé | `#c0392b` | Manipulation massive |
|
||||
|
||||
### Interprétation des niveaux
|
||||
|
||||
**Faible (0-14)** : Le contenu peut contenir quelques formulations marketing standard, mais rien d'alarmant.
|
||||
|
||||
**Modéré (15-29)** : Présence de techniques de persuasion courantes. Vigilance recommandée.
|
||||
|
||||
**Élevé (30-49)** : Utilisation significative de techniques manipulatoires. Analyse critique nécessaire.
|
||||
|
||||
**Très Élevé (50-74)** : Manipulation intentionnelle évidente. Forte méfiance recommandée.
|
||||
|
||||
**Critique (75-100)** : Combinaison de multiples techniques de manipulation intensive. Risque élevé de désinformation ou d'escroquerie.
|
||||
|
||||
---
|
||||
|
||||
## Exemples de calcul
|
||||
|
||||
### Exemple 1 : Article de blog standard
|
||||
|
||||
**Texte** :
|
||||
> "Découvrez 5 astuces pour améliorer votre productivité. Ces conseils ont aidé de nombreuses personnes."
|
||||
|
||||
**Analyse** :
|
||||
- Technique TE0321 (Liste numérotée) : 1 occurrence
|
||||
- Score brut : 1
|
||||
- Poids contextuel (blog) : 1.1
|
||||
- Poids dynamique : 1.0
|
||||
- Score pondéré : 1 × 1.0 × 1.1 × 1.0 = **1.1**
|
||||
|
||||
- Technique TE0501 (Preuve sociale) : 1 occurrence
|
||||
- Score brut : 1
|
||||
- Poids contextuel (blog) : 1.0
|
||||
- Poids dynamique : 1.0
|
||||
- Score pondéré : 1 × 1.0 × 1.0 × 1.0 = **1.0**
|
||||
|
||||
**Score total** : 1.1 + 1.0 = 2.1
|
||||
**Score global** : min(round(2.1 × 3), 100) = **6**
|
||||
**Niveau** : Faible ✅
|
||||
|
||||
---
|
||||
|
||||
### Exemple 2 : Publicité e-commerce agressive
|
||||
|
||||
**Texte** :
|
||||
> "URGENT ! Stock limité : plus que 3 articles ! 50% de réduction, mais seulement pendant les 24 prochaines heures ! Déjà 5000 clients satisfaits. Ne ratez pas cette DERNIÈRE CHANCE !"
|
||||
|
||||
**Analyse** :
|
||||
|
||||
**Technique TE0143 (Urgence artificielle)** : 4 occurrences
|
||||
- Mots-clés : "URGENT", "pendant les 24 prochaines heures"
|
||||
- Patterns : "DERNIÈRE CHANCE"
|
||||
- Score brut : 6
|
||||
- Contextes détectés :
|
||||
- Urgence (×1.3) : "URGENT", "24 prochaines heures"
|
||||
- Rareté (×1.4) : "Stock limité", "plus que 3"
|
||||
- Score avec contexte : 6 × 1.3 × 1.4 = **10.92**
|
||||
- Poids contextuel (commerce) : 0.7
|
||||
- Poids dynamique (4 occ.) : 1.1
|
||||
- Score pondéré : 10.92 × 1.0 × 0.7 × 1.1 = **8.4**
|
||||
|
||||
**Technique TE0141 (Rareté artificielle)** : 2 occurrences
|
||||
- Mots-clés : "Stock limité", "plus que 3 articles"
|
||||
- Score brut : 3
|
||||
- Contexte rareté (×1.4) : 3 × 1.4 = **4.2**
|
||||
- Poids contextuel (commerce) : 0.8
|
||||
- Poids dynamique : 1.0
|
||||
- Score pondéré : 4.2 × 1.0 × 0.8 × 1.0 = **3.36**
|
||||
|
||||
**Technique TE0501 (Preuve sociale)** : 1 occurrence
|
||||
- Mots-clés : "Déjà 5000 clients"
|
||||
- Score brut : 2
|
||||
- Contexte preuve sociale (×1.3) : 2 × 1.3 = **2.6**
|
||||
- Poids contextuel (commerce) : 0.9
|
||||
- Poids dynamique : 1.0
|
||||
- Score pondéré : 2.6 × 1.0 × 0.9 × 1.0 = **2.34**
|
||||
|
||||
**Score total** : 8.4 + 3.36 + 2.34 = **14.1**
|
||||
**Score global** : min(round(14.1 × 3), 100) = **42**
|
||||
**Niveau** : Élevé ⚠️
|
||||
|
||||
---
|
||||
|
||||
### Exemple 3 : Article de désinformation
|
||||
|
||||
**Texte** :
|
||||
> "CHOQUANT : Ce que le gouvernement vous cache ! La VÉRITÉ sur ce scandale que les médias refusent de révéler. Vous ne croirez JAMAIS ce qui s'est vraiment passé. URGENT : partagez avant la censure ! Pourquoi veulent-ils nous empêcher de savoir ? Rejoignez les 50 000 personnes qui connaissent déjà la vérité !"
|
||||
|
||||
**Analyse** :
|
||||
|
||||
**Technique TE0500 (Désinformation)** : 5 occurrences
|
||||
- Mots-clés : "vérité", "vraiment passé"
|
||||
- Variantes clickbait : "ce que le gouvernement cache", "médias refusent"
|
||||
- Score brut : 12
|
||||
- Contextes :
|
||||
- Émotion (×1.4) : "CHOQUANT", "scandale"
|
||||
- Curiosité (×1.5) : "Vous ne croirez JAMAIS"
|
||||
- Question rhétorique (×1.2) : "Pourquoi veulent-ils"
|
||||
- Score avec contexte : 12 × 1.4 × 1.5 × 1.2 = **30.24**
|
||||
- Poids contextuel (news) : 1.4
|
||||
- Poids dynamique (5 occ.) : 1.2
|
||||
- **Technique critique** → bonus ×1.1
|
||||
- Score pondéré : 30.24 × 1.0 × 1.4 × 1.2 × 1.1 = **55.72**
|
||||
|
||||
**Technique TE0132 (Appel à l'émotion)** : 3 occurrences
|
||||
- Mots-clés : "CHOQUANT", "scandale"
|
||||
- Score brut : 5
|
||||
- Contexte émotion (×1.4) : 5 × 1.4 = **7**
|
||||
- Poids contextuel (news) : 1.3
|
||||
- Poids dynamique (3 occ.) : 1.1
|
||||
- Score pondéré : 7 × 1.0 × 1.3 × 1.1 = **10.01**
|
||||
|
||||
**Technique TE0221 (Polarisation)** : 4 occurrences
|
||||
- Mots-clés : "nous vs eux", "empêcher de savoir"
|
||||
- Score brut : 8
|
||||
- Contextes : urgence (×1.3) + question (×1.2) = 8 × 1.3 × 1.2 = **12.48**
|
||||
- Poids contextuel (news) : 1.5
|
||||
- Poids dynamique (4 occ.) : 1.1
|
||||
- **Technique critique** → bonus ×1.1
|
||||
- Score pondéré : 12.48 × 1.0 × 1.5 × 1.1 × 1.1 = **22.66**
|
||||
|
||||
**Technique TE0501 (Preuve sociale)** : 1 occurrence
|
||||
- Mots-clés : "50 000 personnes"
|
||||
- Score brut : 2
|
||||
- Contexte preuve sociale (×1.3) : 2 × 1.3 = **2.6**
|
||||
- Poids contextuel (news) : 1.0
|
||||
- Poids dynamique : 1.0
|
||||
- Score pondéré : 2.6 × 1.0 × 1.0 × 1.0 = **2.6**
|
||||
|
||||
**Score total** : 55.72 + 10.01 + 22.66 + 2.6 = **90.99**
|
||||
**Score global** : min(round(90.99 × 3), 100) = **100**
|
||||
**Niveau** : Critique 🚨
|
||||
|
||||
---
|
||||
|
||||
## Calcul du niveau de confiance
|
||||
|
||||
En plus du score, chaque technique reçoit un **niveau de confiance** (0-100) :
|
||||
|
||||
```javascript
|
||||
confidence = min(round(score × 15 + nb_matches × 10), 100)
|
||||
```
|
||||
|
||||
Ce niveau indique la fiabilité de la détection :
|
||||
- **0-30** : Faible confiance (peu d'éléments détectés)
|
||||
- **31-60** : Confiance moyenne
|
||||
- **61-100** : Haute confiance (nombreux éléments convergents)
|
||||
|
||||
**Exemple** :
|
||||
- Score : 5
|
||||
- Nombre de matches : 8
|
||||
- Confiance : min(round(5 × 15 + 8 × 10), 100) = min(155, 100) = **100**
|
||||
|
||||
---
|
||||
|
||||
## Résumé des formules
|
||||
|
||||
### Score d'une technique
|
||||
|
||||
```
|
||||
score_technique = Σ(poids_des_matches)
|
||||
|
||||
score_avec_contexte = score_technique × Π(boosts_contextuels)
|
||||
|
||||
poids_total = technique.weight × contextualWeight × dynamicWeight
|
||||
|
||||
score_pondéré = score_avec_contexte × poids_total
|
||||
```
|
||||
|
||||
### Score global
|
||||
|
||||
```
|
||||
score_global = min(round(Σ(scores_pondérés) × 3), 100)
|
||||
```
|
||||
|
||||
### Niveau de confiance
|
||||
|
||||
```
|
||||
confidence = min(round(score × 15 + nb_matches × 10), 100)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Limitations et considérations
|
||||
|
||||
### Points importants à noter
|
||||
|
||||
1. **Longueur minimale des mots-clés** : Par défaut, les mots de moins de 3 caractères sont ignorés.
|
||||
|
||||
2. **Sensibilité au contexte** : Le même texte peut recevoir des scores différents selon le type de page (news, social, commerce, blog).
|
||||
|
||||
3. **Cumul non linéaire** : Les contextes et pondérations se multiplient, ce qui peut créer des amplifications importantes.
|
||||
|
||||
4. **Plafonnement** : Le score est toujours plafonné à 100, même si les calculs donnent des valeurs supérieures.
|
||||
|
||||
5. **Analyse en minuscules** : Tout le texte est converti en minuscules avant analyse (sauf pour les détections de majuscules intentionnelles).
|
||||
|
||||
### Cas particuliers
|
||||
|
||||
- **Techniques critiques** : Certaines techniques (TE0221, TE0500, TE0132, TE0501) reçoivent des bonus même avec peu d'occurrences.
|
||||
|
||||
- **Techniques bénignes** : À l'inverse, certaines techniques courantes (TE0143, TE0232, TE0333) reçoivent des malus si elles sont trop fréquentes.
|
||||
|
||||
- **Patterns regex** : Ont un poids de base de 1.5 (plus élevé que les mots-clés simples).
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
Le score DIMA est un système sophistiqué qui combine :
|
||||
- **Détection lexicale** (mots-clés et variantes)
|
||||
- **Analyse structurelle** (patterns regex)
|
||||
- **Évaluation contextuelle** (type de page, boosts)
|
||||
- **Pondérations dynamiques** (fréquence, gravité)
|
||||
|
||||
Cette approche multi-niveaux permet une évaluation nuancée et adaptative du niveau de manipulation dans un contenu, tout en tenant compte du contexte d'utilisation.
|
||||
|
||||
---
|
||||
|
||||
**Document généré le** : {{ date }}
|
||||
**Version** : 1.0
|
||||
**Système** : DIMA (Détection et Identification des Manipulations en ligne et Analytiques)
|
||||
@ -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