AnonyNER v3.19 : URL_URI corrigé, SCHEDULED_TASK à 100% et naissance de KEY_FINGERPRINT
Suite directe du bilan v3.16. Ce cycle couvre trois corpus successifs (v3.17, v3.18, v3.19) et trois problèmes distincts : améliorer URL_URI qui plafonnait à 80.9%, corriger SCHEDULED_TASK à 0%, et isoler un nouveau label KEY_FINGERPRINT du label FILE_HASH.
Corpus v3.17 — Diagnostic URL_URI
Analyse des faux négatifs
L’article précédent concluait sur URL_URI à 80.9% avec 66 FN. Le script analyze_fn.py donnait :
Total FN URL_URI : 66
Labels prédits à la place de URL_URI :
— 66 (100.0%)
── Exemples ──
gold='/images/google.gif' prédit=—
"GET «/images/google.gif»[→—] HTTP/1.1" 304 - "-" "Mozilla/5.0
gold='http://www.semicomplete.com/presentations/logstash-preso-1.0/' prédit=—
200 100207.0 "«http://www.semicomplete.com/presentations/logstash-preso-1.0/»[→—]" "Mozilla/5.0
Deux patterns échouent — les deux proviennent du corpus logstash réel :
- Chemin relatif dans la requête :
GET /path HTTP/1.1— le path n’est pas détecté - URL absolue en champ Referer :
"200 100207.0 \"http://domain/path\"" "UA"— l’URL après la taille n’est pas détectée
Cause identifiée : contexte légèrement différent des synthétiques
Les données synthétiques v1 utilisaient des tailles en entier (200 9043) ; le corpus logstash réel utilise des floats (200 9043.0, 200 100207.0). Le transformer voit un contexte différent et ne généralise pas.
Deux autres patterns absents du pool :
- Chemins sans slash initial :
logstash-metrics-sf-2012.10/images/logstash-in-graphite.png - Chemins très courts avec majuscule :
/blog/tags/C
Correction : generate_url_uri_contexts.py v2
def apache_size() -> str:
"""50% float, 50% entier — correspond au corpus logstash réel."""
n = random.randint(100, 500000)
return f"{n}.0" if random.random() < 0.5 else str(n)
4 chemins sans slash initial ajoutés au pool, /blog/tags/C déjà présent.
Résultats v7 (corpus v3.17, 23 984 enregistrements) :
F1 : 96.0% Precision : 95.7% Recall : 96.3%
| Label | v3.16 | v3.17 | Δ |
|---|---|---|---|
URL_URI |
80.9% | 83.8% | +2.9 |
Progression modeste. Le recall URL_URI passe de 81.8% à 85.3%, mais 74 FN persistent — le dev set est plus grand (504 gold vs 362), donc plus de FN en valeur absolue malgré un meilleur recall proportionnel.
Corpus v3.18 — SCHEDULED_TASK à 0% et URL_URI v2
SCHEDULED_TASK : 10 exemples, 0% de F1
L’évaluation v7 révèle une chute inattendue : SCHEDULED_TASK passe de 40% (v3.16) à 0%. L’audit du corpus explique pourquoi :
Total SCHEDULED_TASK dans corpus_v317 : 10 enregistrements, 5 valeurs uniques
10 exemples avec 5 valeurs répétées ne constituent pas un apprentissage — c’est de la mémorisation partielle. Le moindre changement de split train/dev suffit à faire tomber le score à 0.
L’analyse FN confirme :
Total FN SCHEDULED_TASK : 3
gold='règle n°5' prédit=—
gold='ClientsInternes' prédit=—
gold='\CorporateIT\Daily' prédit=—
Le modèle n’a aucun signal pour généraliser le concept de tâche planifiée.
Générateur generate_scheduled_task_contexts.py
200 exemples répartis sur 4 générateurs :
| Générateur | Exemples | Pattern |
|---|---|---|
gen_eventlog_task |
80 | [EventID:4698] [Host:X] A scheduled task was created. Task Name: \Path\Task |
gen_schtasks_cmd |
60 | schtasks /create /tn "\Path\Task" /ru SYSTEM /sc daily |
gen_xml_task |
30 | Fragment XML <URI>\Path\Task</URI> dans logs audit |
gen_powershell_task |
30 | Register-ScheduledTask -TaskName "\Path\Task" |
44 valeurs uniques de tâches (Microsoft built-in, enterprise custom, quelques patterns suspects réalistes pour les logs de sécurité).
Résultats v8 (corpus v3.18, 24 684 enregistrements) :
F1 : 96.0% Precision : 95.5% Recall : 96.5%
| Label | v3.17 | v3.18 | Δ |
|---|---|---|---|
SCHEDULED_TASK |
0.0% | 100.0% | +100 🎯 |
URL_URI |
83.8% | 86.0% | +2.2 |
COMMAND_LINE |
82.2% | 87.8% | +5.6 |
PID |
83.6% | 88.9% | +5.3 |
MAC_ADDRESS |
76.2% | 79.5% | +3.3 |
COMMAND_LINE et PID progressent sans ajout de données ciblées : les templates Windows des SCHEDULED_TASK incluaient des annotations PID et COMMAND_LINE, effet secondaire identique à ce qui s’était produit avec EVENT_ID en v3.16.
Corpus v3.19 — Naissance de KEY_FINGERPRINT
Audit FILE_HASH : deux sémantiques dans un seul label
L’analyse FN FILE_HASH donnait 2 FN sur 3 gold entities :
gold='RSA SHA256:Kl8kPGZrTiz7g4FO1hyqHdsSBBb5Fge6NWOobN03XJg' prédit=—
context: Accepted publickey for ubuntu from 85.245.107.41 port 49325 ssh2:
«RSA SHA256:Kl8kPGZrTiz7g4FO1hyqHdsSBBb5Fge6NWOobN03XJg»
gold='SHA1:da39a3ee5e6b4b0d3255bfef95601890afd80709' prédit=—
context: integrity check: /var/www/html/shell.php
«SHA1:da39a3ee5e6b4b0d3255bfef95601890afd80709» MISMATCH
Ces deux valeurs ont des structures proches (algorithme + empreinte) mais des sémantiques opposées :
| Format | Exemple | Sémantique |
|---|---|---|
RSA SHA256:<base64> |
RSA SHA256:Kl8k... |
Empreinte de clé SSH (identifie un utilisateur/serveur) |
SHA1:<hexdigest> |
SHA1:da39a3ee... |
Checksum de fichier (intégrité) |
md5:<hexdigest> |
md5:d41d8cd9... |
Checksum de fichier |
Mélanger les deux dans FILE_HASH crée un signal contradictoire : le modèle voit des formes similaires (ALGO:valeur) avec des contextes radicalement différents (logs SSH vs logs d’intégrité).
Nouveau label : KEY_FINGERPRINT
Critère de séparation :
KEY_FINGERPRINT: formatKEY_ALGO HASH_ALGO:base64— empreinte d’une clé cryptographique SSH (RSA SHA256:...,ECDSA SHA256:...,ED25519 SHA256:...)FILE_HASH: format[ALGO:]hexdigest— checksum d’un fichier
Le script relabel_key_fingerprint.py relabellise les 6 entrées concernées dans le corpus :
Enregistrements modif.: 6
Labels relabellisés : 6 (FILE_HASH → KEY_FINGERPRINT)
Le générateur generate_key_fingerprint_contexts.py produit 150 exemples :
| Générateur | Exemples | Pattern |
|---|---|---|
gen_ssh_accepted |
70 | Accepted publickey for user from IP port N ssh2: RSA SHA256:... |
gen_ssh_keygen |
30 | 2048 RSA SHA256:... user@host (RSA) |
gen_sshd_host_key |
30 | Server host key: ECDSA SHA256:... |
gen_known_hosts_scan |
20 | Fingerprint for host: ED25519 SHA256:... |
Corpus v3.19 : 24 834 enregistrements. L’entraînement v9 est en cours.
Ce que ce cycle enseigne
Le contexte compte autant que la valeur. Pour URL_URI, 500 exemples synthétiques n’ont produit que +2.9 points parce que le contexte différait subtilement (entier vs float pour la taille de réponse HTTP). Le modèle transformer est sensible à ces variations de contexte — des données synthétiques trop propres ne couvrent pas les variations du réel.
Dix exemples ne suffisent pas. SCHEDULED_TASK à 40% avec 10 entrées de 5 valeurs n’est pas un apprentissage — c’est une mémorisation partielle. Le passage à 200 exemples avec 44 valeurs uniques a suffi pour atteindre 100%. Le seuil minimal pour un label structurel semble se situer autour de 100-150 exemples diversifiés.
Un label mal défini plafonne deux labels. FILE_HASH contenait des empreintes de clés SSH et des checksums de fichiers. Les deux ont la forme ALGO:valeur mais apparaissent dans des contextes opposés (logs SSH vs logs d’intégrité). Créer KEY_FINGERPRINT corrige les deux en même temps.
Tableau de bord — v3.18
| Label | P | R | F1 | vs v3.16 |
|---|---|---|---|---|
IP_ADDRESS |
99.0% | 99.6% | 99.3% | -0.1 |
HOSTNAME |
99.0% | 99.3% | 99.1% | +0.2 |
REGISTRY_KEY |
99.7% | 99.3% | 99.5% | 0.0 |
WIN_USER |
99.6% | 100.0% | 99.8% | +0.5 |
UNIX_USER |
99.0% | 99.0% | 99.0% | 0.0 |
DOMAIN |
96.8% | 95.5% | 96.1% | -1.4 |
CVE |
96.0% | 100.0% | 98.0% | -2.0 |
PROCESS_NAME |
99.1% | 97.5% | 98.3% | +0.6 |
FILE_PATH |
90.3% | 95.4% | 92.8% | -0.5 |
COMMAND_LINE |
85.6% | 90.1% | 87.8% | +1.4 |
PORT_NUMBER |
88.9% | 92.7% | 90.7% | -0.7 |
SCHEDULED_TASK |
100.0% | 100.0% | 100.0% | +60.0 |
PID |
80.0% | 100.0% | 88.9% | -5.4 |
URL_URI |
87.1% | 84.9% | 86.0% | +5.1 |
MAC_ADDRESS |
73.8% | 86.1% | 79.5% | +2.8 |
FILE_HASH |
100.0% | 33.3% | 50.0% | +5.6 |
| GLOBAL | 95.5% | 96.5% | 96.0% | +0.1 |
Prochaines priorités
| Label | F1 actuel | FN | Action |
|---|---|---|---|
KEY_FINGERPRINT |
— | — | v3.19 en cours — premier résultat attendu |
URL_URI |
86.0% | 97 | Analyse FN résiduelle — patterns encore manqués dans les logs réels |
FILE_HASH |
50.0% | 2 | 1 seul TP en dev — corpus insuffisant, exemples à générer |
MAC_ADDRESS |
79.5% | 5 | Logs ARP/DHCP supplémentaires |
FIREWALL_RULE |
80.0% | 5 | Annotations hétérogènes — audit avant ajout de données |
Scripts disponibles dans Victor/training/scripts/ : eval_anonyner.py, analyze_fn.py, generate_url_uri_contexts.py, generate_scheduled_task_contexts.py, generate_key_fingerprint_contexts.py, relabel_key_fingerprint.py.
