
Trouvez des identifiants dans des captures d'écran, enregistrez-les dans votre gestionnaire de secrets, et masquez-les de manière irréversible dans l'image — local, hors ligne, basé sur l'OCR.
Trouvez des identifiants dans les captures d'écran, extrayez-les vers un gestionnaire de secrets, et
masquez-les irréversiblement des images — pour que les secrets ne pourrissent pas dans vos
notes, captures d'écran et ~/Desktop.
Local. Hors ligne. Silencieux. Aucun réseau, aucune télémétrie par défaut.
screenscrub fait le pont entre deux catégories d'outils existants : les masqueurs d'interface graphique (Xnapper, Shhshot, macshot) floutent les données personnelles à la main mais ne peuvent pas distinguer un secret d'une adresse ni capturer ce qu'ils cachent ; les scanners de secrets (gitleaks, TruffleHog) trouvent de vrais identifiants mais uniquement dans du texte, jamais dans un PNG. screenscrub apporte une détection de qualité gitleaks — plus un pack d'équipe rouge et une analyse d'entropie — aux pixels, sans tête : il capture chaque secret dans votre gestionnaire, masque irréversiblement l'image, et ré-OCRel le résultat pour prouver que le secret a disparu.
Un filet de sécurité, pas une garantie — screenscrub ne prétend jamais qu'une image est "garantie propre" ; l'OCR et la détection peuvent tous deux manquer des choses, donc vérifiez toujours les résultats. Lisez ceci avant de vous y fier.
screenscrub scan ~/Screenshots # essai à blanc : détecte + rapporte, n'écrit rien
screenscrub clean ~/Screenshots --out ~/clean \ # extrait, masque, vérifie, met en quarantaine
--sink file://./secrets.age --yes
screenscrub verify ~/clean # prouve qu'un dossier est propre (exit≠0 si non)
screenscrub clean --keep-originals ./tmp
Nettoyage de 7 image(s) dans ./tmp (7 worker(s))…
[1/7] tmp/ssh.jpg — 6 région(s) → tmp/ssh.redacted.jpg ✓ vérifié
[2/7] tmp/key.jpg — 13 région(s) → tmp/key.redacted.jpg ✓ vérifié
[3/7] tmp/test2.jpg — 12 région(s) → tmp/test2.redacted.jpg ✓ vérifié
[4/7] tmp/test5.jpg — 13 région(s) → tmp/test5.redacted.jpg ✓ vérifié
[5/7] tmp/test3.jpg — erreur : échec de la vérification : 4 région(s) secrète(s) encore détectables dans tmp/test3.redacted.jpg ; original conservé
[6/7] tmp/test4.jpg — erreur : échec de la vérification : 9 région(s) secrète(s) encore détectables dans tmp/test4.redacted.jpg ; original conservé
[7/7] tmp/test6.jpg — erreur : échec de la vérification : 7 région(s) secrète(s) encore détectables dans tmp/test6.redacted.jpg ; original conservé
7/7 fichier(s) signalé(s), 382 région(s) au total, 3 fichier(s) en erreur/non vérifié(s).
Par type :
haute-entropie 343
mot-clé-secret 15
uri-connexion-bdd 10
github-pat 6
unix-crypt-hash 6
aws-access-key 1
stripe-key 1
Nécessite une attention manuelle (3) :
tmp/test3.jpg — erreur : échec de la vérification : 4 région(s) secrète(s) encore détectables dans tmp/test3.redacted.jpg ; original conservé
tmp/test4.jpg — erreur : échec de la vérification : 9 région(s) secrète(s) encore détectables dans tmp/test4.redacted.jpg ; original conservé
tmp/test6.jpg — erreur : échec de la vérification : 7 région(s) secrète(s) encore détectables dans tmp/test6.redacted.jpg ; original conservé
| Avant — valeurs secrètes visibles | Après clean — masqué irréversiblement |
![]() |
La même capture d'écran avant et après clean : chaque valeur secrète est décodée en pixels, encadrée, et ré-encodée dans une nouvelle image (irréversible) — les étiquettes et la structure restent lisibles.
Nécessite le binaire OCR tesseract sur le PATH :
brew install tesseractwinget install UB-Mannheim.TesseractOCR (ou l'installateur UB Mannheim)apt-get install tesseract-ocrPuis construisez :
go build -o screenscrub ./cmd/screenscrub
Se compile en croisé pour macOS (arm64/amd64) et Windows (amd64) — il appelle tesseract via un sous-processus plutôt que d'utiliser cgo. Testé uniquement sur macOS.
scan <dossier|fichier> — essai à blancDétecte et rapporte ; n'écrit rien.
screenscrub scan ~/Screenshots --ignore 'node_modules/**' --ignore '*.thumb.png'
screenscrub scan shot.png --json # rapport lisible par machine
screenscrub scan ~/Screenshots --preview ~/review # visualisez ce que clean masquerait
--preview <dossier> écrit une copie non destructive de chaque image signalée avec chaque
région détectée délimitée (pas remplie), afin que vous puissiez confirmer ce que clean
masquerait avant de vous engager. ⚠️ Ces copies contiennent encore les secrets — le dossier
est créé avec les droits 0700 et ne doit pas être partagé.
Le rapport se termine par un récapitulatif : une répartition par type et une liste explicite "Nécessite une attention manuelle" — les fichiers en erreur, ayant échoué à la vérification, ou n'ayant produit aucun texte OCR sur ce qui devrait être une capture d'écran (un échec silencieux de l'OCR se lit comme une image propre, donc il est signalé plutôt que pris pour acquis).
scan --fail-on-findings se termine avec un code non nul si quelque chose est détecté — une
porte de pré-commit/CI.
clean <dossier|fichier> — extraire, masquer, mettre en quarantaineSCREENSCRUB_PASSPHRASE=… screenscrub clean ~/Screenshots \
--out ~/Screenshots/clean \
--sink file://./secrets.age \
--yes
Pour chaque image avec des résultats, clean :
<nom>.redacted.png dans --out (ou à côté de la source),<out>/quarantine/ chiffré avec age.Les secrets vont dans le puits que vous passez à --sink : fichier age (par défaut), 1Password
(op://), KeePassXC (keepassxc://), Trousseau macOS (keychain://), ou
HashiCorp Vault (vault://) — ou des destinataires age matériels (YubiKey,
Touch ID). Voir Puits de secrets.
Drapeaux :
--exclude-kind et --min-confidence sont des réglages de précision locaux (aucun réseau)
pour un corpus bruyant — l'équivalent hors ligne de --llm. Ils échangent du rappel contre
un rapport plus silencieux, utilisez-les donc avec intention. Les deux
fonctionnent aussi sur scan/verify et peuvent être définis dans --config (exclude_kinds,
min_confidence).
Ordre de résolution de la phrase de passe : --passphrase, puis le fichier de configuration, puis
SCREENSCRUB_PASSPHRASE. Évitez --passphrase sur la ligne de commande — argv est
visible par les autres processus.
verify <dossier|fichier> — prouver qu'un dossier est propreRé-OCRel chaque image, relance la détection, et se termine avec un code non nul si une région secrète est encore trouvée — utilisez-le comme livrable/porte de CI avant d'envoyer des captures d'écran à un client. (Il prouve que l'OCR ne peut pas lire le secret, pas qu'un humain ne le pourrait jamais.)
screenscrub verify ~/deliverable/screenshots
echo $? # 0 = propre, non nul = un secret est encore détectable
clean exécute cette même vérification automatiquement sur chaque copie masquée et ne
mettra pas en quarantaine ni ne déchiquettera un original dont la copie masquée échoue ; --no-verify la
saute (non recommandé).
open <stockage> — récupérer les secrets ou un original mis en quarantaineRécupérez vos données : open déchiffre le puits age (les enregistrements de secrets stockés) ou un
original mis en quarantaine vers --out, jamais vers stdout. Voir
Déchiffrement des stockages.
--llm — supprimer les faux positifs bénins avec un LLMLes règles de qualité gitleaks sur du texte OCR (avec du bruit OCR) provoquent inévitablement des
faux positifs : le détecteur orienté rappel fait remonter des chaînes bénignes de haute entropie qu'un
opérateur souhaite conserver — noms d'hôtes, GUIDs, SHA git, hachages NTLM qui sont eux-mêmes la
preuve de compromission. --llm soumet chaque candidat à Claude (Opus 4.8), en gardant
les vrais secrets et en supprimant les identifiants bénins ; les régions confirmées par le LLM sont
remplies en rouge.
ANTHROPIC_API_KEY=… screenscrub clean ~/Screenshots --out ~/clean --llm --yes
Cela brise délibérément le principe local-et-silencieux : cela envoie les chaînes candidates (les
secrets détectés) à l'API Anthropic, donc c'est uniquement sur option, imprime un avertissement,
et nécessite une clé API. Il échoue en mode fermé : toute erreur API/réseau conserve tous les
résultats (masque tout) plutôt que de risquer de laisser passer un vrai secret. Il effectue un
appel API par image signalée (limité à 4 concurrents quel que soit --jobs) —
attention aux limites de débit et au coût sur les grands dossiers.
--watchscreenscrub clean ~/Screenshots --watch --sink file://./secrets.age --yes
Traite tout ce qui est déjà présent, puis surveille les nouvelles images modifiées et
masque chacune dès son apparition. Les sorties *.redacted.png sont ignorées pour éviter les
boucles.
Sur les tokens OCR, screenscrub combine :
AKIA…, GitHub ghp_…, GitLab,
Slack, Stripe, OpenAI, GCP API keys, JWT, et en-têtes PEM. (Copiées, pas importées, pour
rester petites et totalement hors ligne.)postgres://u:p@…), en-têtes d'authentification
HTTP Basic/Bearer, hachages NetNTLMv2 et NTLM (pwdump), hachages crypt /etc/shadow, clés
privées PuTTY et OpenSSH, jetons SendGrid/npm.password, token=, secret, api_key, …) qui signalent
la valeur adjacente même quand elle ne correspond à aucun motif et a une faible entropie.BEGIN/END et masque généreusement toute la région englobante
plutôt que de faire confiance à des correspondances mot par mot.Chaque résultat est lié aux boîtes de pixels des tokens qui le composent (deux reconstructions de ligne — espacée et serrée — récupèrent les secrets que l'OCR a séparés par des espaces), et les boîtes sont rembourrées avant d'être remplies.
Un puits est une méthode : Store(Secret) error. Adaptateurs livrés :
file:// — fichier local chiffré avec age (par défaut). Aucune dépendance externe ; un
tableau JSON d'enregistrements chiffré avec une phrase de passe scrypt age (ou à des
destinataires age — voir chiffrement matériel ci-dessous).op:// — CLI 1Password. Exécute op item create avec la valeur sur stdin
(jamais dans argv).keepassxc://./vault.kdbx — KeePassXC. Gestionnaire KeePass local multiplateforme
populaire. Exécute keepassxc-cli add ; le mot de passe de la base et le secret sont tous deux
passés sur stdin (jamais dans argv). Le mot de passe de la base est la phrase de passe
résolue (--config/SCREENSCRUB_PASSPHRASE).keychain://screenscrub — Trousseau macOS. Exécute security add-generic-password. Mise en garde : l'outil security n'accepte le secret
que comme argument de ligne de commande, donc il est brièvement visible par les ps du
pendant l'exécution (macOS cache argv aux autres utilisateurs). Pour une
hygiène plus stricte, préférez 1Password, KeePassXC, ou le destinataire Secure-Enclave
ci-dessous.Pour ajouter le vôtre, implémentez sink.SecretSink (internal/sink) et branchez-le dans
sink.FromURI. C'est tout le contrat.
Le puits fichier age par défaut et la quarantaine peuvent chiffrer vers des destinataires age au lieu d'une phrase de passe, y compris ceux basés sur du matériel via des plugins age :
# YubiKey (PIV) — installez age-plugin-yubikey, puis :
screenscrub clean ~/Screenshots --out ~/clean \
--sink file://./secrets.age \
--recipient age1yubikey1q... --yes
# Apple Secure Enclave / Touch ID — installez age-plugin-se, puis :
screenscrub clean ~/Screenshots --out ~/clean \
--recipient age1se1q... --yes
# Paire de clés age simple, ou plusieurs destinataires / un fichier de destinataires :
screenscrub clean ~/Screenshots --recipient age1abc... --recipients-file team.age.pub --yes
En mode destinataire, aucune phrase de passe n'est nécessaire ; le puits et la
quarantaine chiffrée vont tous deux aux destinataires. Récupérez avec le jeton matériel :
age -d -i <identité> secrets.age (le déchiffrement demande le contact YubiKey /
Touch ID). Le chiffrement vers un destinataire plugin nécessite le binaire age-plugin-<nom> ;
le déchiffrement nécessite en plus le périphérique.
openLe puits et la quarantaine utilisent des clés distinctes dérivées de votre unique phrase de passe
(HKDF), donc cracker un stockage ne peut pas ouvrir l'autre. Comme les clés sur disque sont
dérivées, déchiffrez-les avec screenscrub plutôt qu'avec age brut :
screenscrub open ./secrets.age --out secrets.json # les enregistrements de secrets
screenscrub open ~/clean/quarantine/shot.png.ab12cd34.age --out shot.png # un original
open essaie automatiquement chaque clé dérivée et écrit le texte clair dans --out
(jamais stdout). Pour les stockages en mode destinataire, passez un fichier de clé age :
screenscrub open secrets.<id>.age --identity key.txt --out secrets.json (pour
YubiKey/Touch ID, utilisez age -d -i directement, qui pilote le plugin matériel).
Le mode destinataire écrit un fichier par secret (secrets.<hash>-<rand>.age) donc
les exécutions répétées ne s'écrasent jamais.
go test ./... # les tests unitaires s'exécutent sans tesseract ; les tests OCR/ré-OCR sont sautés s'il est absent
La garantie principale est couverte par un test de bout en bout : générer une capture d'écran avec
une clé AWS, exécuter le pipeline réel OCR → détection → masquage, ré-OCRer la sortie, et
affirmer que le secret a disparu (internal/redact).
screenscrub n'est pas une permission d'arrêter de réfléchir.
password:) ; un secret sous une forme qu'elle
ne reconnaît pas passera à travers.Elle est réglée pour le rappel plutôt que la précision — elle signale délibérément trop, car un identifiant laissé dans une image est le cas catastrophique et un faux positif ne vous coûte qu'un bloc masqué.
Vous pouvez voir un cas d'utilisation réaliste dans le dossier testdata/, tiré de captures d'écran de RExpository :
https://jaimepolop.github.io/RExpository/
Ce n'est pas parfait, mais plutôt bon.
clean la ré-OCRel
et relance la détection ; si un secret est encore détectable, il refuse de mettre
en quarantaine ou de détruire l'original et signale le fichier. Les boîtes de masquage sont
rembourrées proportionnellement à la hauteur des glyphes afin que les bords anti-aliasés sur les grands
textes à haute DPI soient couverts.clean écrit la copie masquée, la vérifie, puis
déplace l'original non masqué dans une quarantaine chiffrée avec age et le supprime
de son emplacement d'origine — seulement après que la copie est vérifiée propre et que les
secrets extraits sont en sécurité sur le disque. Les étapes destructives nécessitent --yes.AK****…**LE). La seule
copie en texte clair vit en mémoire jusqu'à ce qu'elle atteigne votre puits chiffré.--purge et les limites de la suppression sécuriséePar défaut, les originaux sont conservés (chiffrés) en quarantaine afin qu'un faux masquage soit
récupérable. --purge déchiquète plutôt l'original : écrasement avec aléatoire,
écrasement avec des zéros, puis suppression du lien.
La suppression sécurisée n'est pas fiable. Sur les SSD, les systèmes de fichiers à copie sur écriture (APFS,
Btrfs, ZFS), les systèmes de fichiers journalisés et les volumes avec snapshots/sauvegardes,
l'écrasement suivi d'une suppression du lien ne garantit pas que les octets d'origine ont disparu —
l'usure et les snapshots peuvent les conserver. La seule véritable défense est de ne jamais persister le texte clair
en premier lieu. Privilégiez la quarantaine chiffrée et traitez
--purge comme un effort au mieux.
OCR — un futur ajout potentiel.Les motifs d'identifiants intégrés sont dérivés de gitleaks (MIT).
![]() |
| Drapeau | Signification |
|---|
--out <dossier> | où vont les copies masquées (défaut : à côté de la source) |
--sink <uri> | file://./secrets.age · op://Vault · keepassxc://./v.kdbx · keychain://service · vault://KV |
--recipient <age1…> | chiffre pour un destinataire age (répétable) : X25519, YubiKey ou Touch ID |
--recipients-file <f> | fichier de destinataires age, un par ligne |
--passphrase | phrase de passe pour le puits fichier / la quarantaine (préférez la variable d'env ou --config) |
--watch | reste actif ; traite les nouvelles captures d'écran au fur et à mesure (fsnotify) |
--purge | déchiquète les originaux au lieu de les mettre en quarantaine (voir mise en garde ci-dessous) |
--keep-originals | écrit uniquement les copies masquées ; laisse les originaux en place (pas de quarantaine, pas de phrase de passe, pas de --yes) — mutuellement exclusif avec --purge |
--yes | confirme la gestion destructive des originaux (obligatoire) |
--config <fichier> | configuration JSON ; permet de garder la phrase de passe hors de argv |
--no-verify | saute la vérification par ré-OCR de la sortie masquée (non recommandé) |
-i, --interactive | ouvre chaque image signalée et confirme le masquage/le saut avant d'y toucher |
--llm | utilise Claude pour supprimer les faux positifs bénins ; envoie les chaînes candidates à l'API Anthropic |
--llm-api-key | clé API Anthropic pour --llm (ou ANTHROPIC_API_KEY) |
--jobs N | traite N images en parallèle (défaut : nombre de CPU) |
--manifest <fichier> | écrit une carte JSON des originaux→masqués→régions |
--ocr-raw / --ocr-upscale N | désactive le prétraitement du terminal sombre / agrandit avant OCR |
--exclude-kind <type> | supprime les résultats d'un type (répétable), ex. high-entropy — réglage de précision hors ligne |
--min-confidence <0..1> | ignore les résultats en dessous de cette confiance — réglage de précision hors ligne |
--json | émet le rapport en JSON |
--ignore <glob> | glob à ignorer, répétable ; ** correspond à n'importe quelle profondeur |
--lang | langue de tesseract (défaut eng) |
vault:// — HashiCorp Vault KV v2 — présent mais reporté/moins courant ; le
chemin hache le secret pour qu'il n'apparaisse jamais dans l'URL.