---
name: detecteur-secrets
description: Recherche locale de secrets/credentials oubliés dans du code source (clés API, tokens, mots de passe en clair) avant tout commit ou publication — stdlib pur, zéro appel réseau.
theme: securite-souverainete
langages_cibles: python
---
# detecteur-secrets
## Objectif
Repérer, dans un dossier ou un fichier de code source, des secrets probables
oubliés en clair : clés d'API cloud (AWS/Google), jetons (GitHub/Slack),
clés de paiement (Stripe), blocs de clé privée PEM, jetons JWT, chaînes de
connexion base de données avec identifiants embarqués, et assignations
génériques à haute entropie (`password = "..."`, `secret = "..."`).
Complète `audit-souverainete` (qui cible les *dépendances* non-souveraines)
sous un angle différent : la *fuite de secrets* dans le code lui-même.
## Pourquoi Python (et pas un autre langage) ?
- Même famille de raisons que les 2 skills Python précédents du projet
(`audit-souverainete`, `connecteur-solivram-souverain`) : portabilité sans
compilation, `re`/`math`/`json` de la stdlib suffisent entièrement à la
tâche (recherche de motifs + calcul d'entropie de Shannon).
- Le calcul d'entropie (`math.log2`) et la manipulation de texte ligne par
ligne sont des opérations pour lesquelles Python est directement adapté
sans bibliothèque supplémentaire — un langage compilé type Rust n'apporterait
ici ni gain de robustesse (pas de calcul cryptographique réel, juste de la
détection de motifs) ni gain de performance perceptible à l'échelle d'un
dépôt de code (quelques centaines à quelques milliers de fichiers texte).
## Garanties de sécurité
- **Contenu scanné = donnée inerte, jamais exécutée ni interprétée comme
instruction.** Le script ne fait que chercher des motifs texte (regex) et
calculer une entropie — même si un fichier scanné contenait du texte
ressemblant à une consigne adressée à un assistant IA, ce script ne "lit"
jamais ce texte pour décider d'une action : chaque comportement (quel
fichier ouvrir, quelle règle appliquer) est déterminé AVANT le scan, jamais
par le contenu rencontré.
- **Catalogue de règles externalisé et vérifié** (`regles_secrets.json`) —
chaque motif regex est compilé et validé au chargement (`charger_catalogue`) ;
un motif invalide lève une erreur nommée (`ErreurReglesInvalides`) plutôt
que d'échouer silencieusement ou de planter sans explication.
- **Filtrage anti-faux-positifs à deux niveaux** sur l'assignation générique
(la règle la plus susceptible de faux positifs) : liste de placeholders
connus (`changeme`, `example`, `test`, ...) ET seuil d'entropie de Shannon
(`SEUIL_ENTROPIE_BITS_PAR_CAR = 3.0`, longueur minimale 12 caractères) —
une valeur basse-entropie ou explicitement un placeholder n'est jamais
signalée, réduisant le bruit sur les vrais dépôts de code (voir résultat du
test réel ci-dessous).
- **Aucune donnée n'est jamais transmise à l'extérieur** — 100% local,
0 appel réseau, aucune dépendance tierce.
## Gestion d'erreur exhaustive
`erreurs.py` définit une exception par cas réel identifié dans le pipeline :
`ErreurCibleIntrouvable` (chemin absent), `ErreurCibleNonAccessible`
(permission refusée), `ErreurReglesInvalides` (catalogue JSON malformé,
champ obligatoire absent, ou motif regex invalide — vérifié à la compilation,
jamais supposé correct), `ErreurEcritureRapport` (écriture du fichier de
sortie impossible). `main()` traite chaque catégorie dans une branche
`except` dédiée avec un code de sortie distinct, plus un dernier recours
explicitement nommé (`ErreurInterne`, code 99) pour tout cas non prévu —
jamais un `except Exception: pass` silencieux.
## Mode d'emploi
**Scanner un dossier (rapport Markdown, sortie standard) :**
```bash
python3 detecteur_secrets.py /chemin/vers/mon-projet
```
**Scanner un seul fichier, rapport JSON écrit sur disque :**
```bash
python3 detecteur_secrets.py /chemin/vers/mon-projet/config.py \
--format json --out rapports/mon-rapport.json
```
**Résultat réel obtenu lors de la validation de ce skill** (dossier `src/`
d'un projet Rust d'environ 650 fichiers, ~2.5 secondes) : 17 signalement(s) —
tous analysés manuellement et confirmés **faux positifs explicables**, 0 vrai
secret : 9 correspondent au marqueur littéral `-----BEGIN PRIVATE KEY-----`
utilisé dans des `assert!`/tests pour vérifier qu'un PEM généré a la bonne
structure (pas une clé embarquée), et 8 correspondent à des chaînes de test
explicitement nommées (ex. valeurs littérales utilisées uniquement dans des
fonctions `#[test]` pour vérifier un HMAC ou un hachage Argon2id) — jamais un
secret de production. Ce résultat illustre le compromis assumé du filtrage
par entropie/placeholder : reste volontairement sensible (mieux vaut un faux
positif explicable qu'un vrai secret manqué), la vérification manuelle finale
reste indispensable.
**Options communes :**
- `--format` : `md` (défaut, lisible) | `json` (structuré, pour automatisation)
- `--out` : chemin de sortie (défaut : stdout)
**Prérequis :** aucune installation — Python 3 standard suffit (testé en 3.12).
## Limites connues (honnêteté de l'outil, pas de sur-promesse)
- Détection par motifs connus (formats de clés publiés par les fournisseurs)
— un format de secret propriétaire ou inédit ne sera pas détecté par les
règles spécifiques ; seule la règle générique (entropie) peut, dans certains
cas, le repérer si le nom de variable évoque un secret.
- Le calcul d'entropie de Shannon est un indicateur statistique, pas une
preuve — une valeur non-secrète mais aléatoire (UUID, hash public) peut en
théorie déclencher la règle générique si son nom de variable évoque un
secret ; à l'inverse un secret réel mais très court (<12 caractères) ou
répétitif échappe au filtre entropie (d'où les règles spécifiques par
format, qui elles ne dépendent pas de l'entropie).
- Ne remplace pas un outil dédié de type pre-commit hook avec base de
signatures tierces continuellement mise à jour — ce skill est un
complément ponctuel et souverain, pas un service de renseignement de
menaces en temps réel.
- Comme `audit-souverainete`, l'extraction est par motif texte (regex),
pas une analyse sémantique du langage — un secret construit dynamiquement
(concaténation, encodage) au moment de l'exécution n'est pas détectable
par une analyse statique de ce type.
- **`regles_secrets.json` n'est pas hébergé sur cette instance** — vérifié
précisément par bissection réelle (une requête d'upload par règle prise
isolément) : 2 des 11 motifs (jeton de messagerie tierce et chaîne de
connexion base de données avec identifiants embarqués — voir les règles
correspondantes dans la copie source locale pour le détail exact)
déclenchent eux-mêmes le scanner de sécurité anti-fuite de solivram.com
("Fichier refusé par le scanner de sécurité"), qui reconnaît visiblement
ces deux formats comme des signatures de secret à part entière — même sans
aucune vraie valeur secrète, la simple présence du motif de détection
suffit (constaté aussi sur cette page elle-même : toute formulation trop
explicite du motif re-déclenche le même refus, d'où cette description
volontairement non littérale). Cohérence amusante mais réelle : un outil
de détection de secrets se fait bloquer par un autre outil de détection de
secrets. Le fichier reste disponible dans le dépôt source local ; la copie
de ce skill publiée sur solivram.com n'est donc pas 100% autonome sans ce
fichier fourni séparément (le code et les tests, eux, sont complets et
fonctionnels).