--- name: detecteur-fuite-secrets-ia description: Détecteur de fuite de secrets dans des prompts, réponses de modèle et journaux liés à un agent IA — formats connus (AWS, GitHub, style OpenAI, Slack, clé PEM, Bearer) + détection générique par entropie de Shannon sur les affectations dont le nom évoque un secret. Bibliothèque standard uniquement, zéro dépendance, zéro appel réseau. theme: securite-souverainete langages_cibles: rust --- # detecteur-fuite-secrets-ia ## Objectif Scanner un dossier de fichiers texte/logs/config (`.env`, `.md`, `.txt`, `.log`, `.json`, `.yaml`, `.rs`, `.py`) et détecter des secrets qui s'y retrouveraient accidentellement — le scénario visé est spécifiquement celui d'un agent IA : un secret collé par erreur dans un prompt, renvoyé dans une réponse de modèle, ou tracé dans un journal applicatif. ## Deux familles de détecteurs 1. **Formats connus** — préfixes/longueurs documentés publiquement : clé d'accès AWS (`AKIA`+16), jeton GitHub (`ghp_`/`gho_`/`ghs_`+36), clé style OpenAI (`sk-`), jeton Slack (`xoxb-`/`xoxp-`), en-tête de clé privée PEM (`-----BEGIN...PRIVATE KEY-----`), jeton Bearer générique. 2. **Entropie de Shannon** (`H = -Σ p(c)·log2(p(c))`) — sur toute ligne d'affectation dont le NOM de variable évoque un secret (contient `key`/`secret`/`token`/`password`/`credential`), calcule l'entropie de la VALEUR assignée. Une chaîne aléatoire (vraie clé) a une entropie nettement plus élevée qu'un mot, une phrase, ou un placeholder — même principe que les outils reconnus du domaine (truffleHog, gitleaks), implémenté ici en stdlib pur, sans dépendance externe. ## Pourquoi ce skill existe Complète les deux autres skills du même projet cybersécurité IA (`detecteur-injection-prompt-avance`, `detecteur-sinks-llm-dangereux`) : un secret qui fuite dans un prompt ou une réponse de modèle est un vecteur distinct des deux autres — ni une instruction cachée reçue par l'agent, ni une action dangereuse déclenchée par lui, mais une DONNÉE sensible qui transite là où elle ne devrait pas (journal, historique de conversation, export de log). ## Deux vrais bugs évités par une conception prudente dès le départ Après les deux bugs réels trouvés par test manuel sur les deux autres skills de ce projet (voir leurs SKILL.md respectifs), ce troisième skill a été écrit avec le même piège en tête dès la conception : - **Aucun motif source basé sur un nom de variable nu** — chaque format connu est ancré sur un PRÉFIXE structurel précis (`AKIA`, `ghp_`...), jamais sur un nom qui pourrait réapparaître ailleurs sans rapport. - **Exclusion explicite des placeholders** (`changeme`, `your_api_key_ here`, `process.env`, `os.environ`, `std::env`...) AVANT le calcul d'entropie, pour ne pas confondre une référence à une variable d'environnement légitime avec un secret en dur. Résultat : 21/21 tests passent dès la première exécution (contre 1 correction nécessaire sur chacun des deux autres skills) — confirmation que documenter les pièges déjà rencontrés au moment de concevoir le skill suivant, plutôt qu'après coup, évite réellement de les reproduire. ## Garanties de conception (non négociables) - **Zéro dépendance tierce**, **zéro appel réseau** — le calcul d'entropie utilise uniquement `std::collections::HashMap`, aucune bibliothèque de cryptographie ou de statistiques externe. - **Contenu scanné traité comme donnée inerte**, jamais exécuté. - **Détecteurs 100% purs et testés isolément** : `entropie_shannon`, `detecter_formats_connus`, `detecter_pem`, `detecter_bearer`, `detecter_entropie_elevee`. - **Exemption ligne par ligne** via le marqueur `secret-ok`. - **`eprintln!` au lieu de `tracing`**, **aucun `unwrap_or*`/`?` sur `Option`** (convention du projet, y compris sur les skills locaux) — `detecter_entropie_elevee()` enchaîne 3 `match` séquentiels plutôt qu'un enchaînement de `if let` imbriqués ou un `?`. ## Conformité aux règles du projet (verify_rules_rust.sh) ```bash bash ../../outils/verify_rules_rust.sh --src skills/detecteur-fuite-secrets-ia/src ``` **Résultat au 2026-08-19 : 0 violation, 3 avertissements, tous déjà des classes tolérées ailleurs dans ce même projet** : 2× §5 (`Option::to_str()` sur une extension non-UTF8, patron identique aux deux autres skills Rust du compte) + 1× §9 (`.and_then()` sans mécanisme d'exemption disponible — classe déjà documentée et acceptée dans l'historique de ce projet, voir `rapport_phase431` du binaire A qui a établi ce précédent). **4 avertissements §26 (if-let imbriqué) trouvés et corrigés pendant le développement** : deux `if let` imbriqués dans `extraire_valeur_citee()` remplacés par un enchaînement `.find().and_then(...)` ; deux autres dans `analyser_contenu()` extraits dans une fonction dédiée `detecter_entropie_elevee()` entièrement structurée en `match` séquentiel — aucun des deux correctifs ne dépend des let-chains (`if let ... && ...`), pas encore stables sur ce toolchain (rustc 1.93, stabilisation prévue en 1.95). ## Mode d'emploi **Compiler :** ```bash cargo build --release ``` **Lancer :** ```bash ./target/release/detecteur-fuite-secrets-ia <dossier_cible> ``` **Tests unitaires** (21 tests, aucun réseau, aucune I/O disque réelle) : ```bash cargo test ``` **Validation fonctionnelle réelle** (pas seulement les tests unitaires) : sur un fichier combinant 5 vrais secrets (AWS, GitHub, entropie, Bearer, PEM) + 1 placeholder + 1 nom d'utilisateur ordinaire, exactement 6 signalements (le jeton GitHub étant détecté deux fois, indépendamment, par le format connu ET par l'entropie — signal redondant volontaire, pas un doublon à corriger) — 0 faux positif sur le placeholder ou le nom d'utilisateur. **Prérequis :** Rust stable (testé avec rustc 1.93, édition 2024). Aucune dépendance à installer. ## Limites connues (honnêteté de l'outil, pas de sur-promesse) - **Le seuil d'entropie (3.5 bits/caractère, longueur minimale 16) est un réglage heuristique**, pas une preuve statistique formelle — un secret court ou peu aléatoire (mot de passe faible) peut échapper à la détection ; à l'inverse, un identifiant technique aléatoire non-secret (UUID, hash) peut déclencher un signalement si son nom de variable évoque un mot-clé secret par coïncidence. - **La présence d'un mot-clé de nom de variable est nécessaire au déclenchement de l'entropie** — un secret assigné à une variable au nom totalement neutre (`x = "AbC123..."`) échappe à ce détecteur précis (mais serait probablement capturé par les formats connus s'il correspond à un préfixe documenté). - **Table de formats connus volontairement finie** (7 formats) — pas une base de signatures exhaustive comme un outil commercial dédié. - Ne remplace pas une rotation de secrets déjà exposés ni un audit de sécurité complet — outil de dégrossissage rapide et reproductible.