
Trova le credenziali negli screenshot, salvale nel tuo gestore di segreti e oscurale in modo irreversibile dall'immagine — locale, offline, basato su OCR.
Trova credenziali negli screenshot, estraile in un gestore di segreti e cancellale irreversibilmente dalle immagini — così i segreti non marciscano nelle note, negli screenshot e in ~/Desktop.
Locale. Offline. Silenzioso. Nessuna rete, nessuna telemetria per impostazione predefinita.
screenscrub collega due categorie di strumenti esistenti: i redattori GUI (Xnapper, Shhshot, macshot) offuscano manualmente i dati personali ma non sanno distinguere un segreto da un indirizzo né catturare ciò che nascondono; gli scanner di segreti (gitleaks, TruffleHog) trovano vere credenziali ma solo nel testo, mai in un PNG. screenscrub porta il rilevamento di livello gitleaks — più un pacchetto red-team e analisi di entropia — ai pixel, in modalità headless: cattura ogni segreto nel tuo gestore, cancella irreversibilmente l'immagine e ri-OCRizza il risultato per dimostrare che il segreto è sparito.
Una rete di sicurezza, non una garanzia — screenscrub non afferma mai che un'immagine sia "garantita pulita"; sia l'OCR che il rilevamento possono sbagliare, quindi dai sempre un'occhiata ai risultati. Leggi questo prima di fare affidamento su di esso.
screenscrub scan ~/Screenshots # prova a secco: rileva + segnala, non scrive
screenscrub clean ~/Screenshots --out ~/clean \ # estrai, oscura, verifica, metti in quarantena
--sink file://./secrets.age --yes
screenscrub verify ~/clean # dimostra che una cartella è pulita (exit≠0 se non lo è)
screenscrub clean --keep-originals ./tmp
Cleaning 7 image(s) in ./tmp (7 worker(s))…
[1/7] tmp/ssh.jpg — 6 region(s) → tmp/ssh.redacted.jpg ✓ verified
[2/7] tmp/key.jpg — 13 region(s) → tmp/key.redacted.jpg ✓ verified
[3/7] tmp/test2.jpg — 12 region(s) → tmp/test2.redacted.jpg ✓ verified
[4/7] tmp/test5.jpg — 13 region(s) → tmp/test5.redacted.jpg ✓ verified
[5/7] tmp/test3.jpg — error: verification failed: 4 secret region(s) still detectable in tmp/test3.redacted.jpg; original preserved
[6/7] tmp/test4.jpg — error: verification failed: 9 secret region(s) still detectable in tmp/test4.redacted.jpg; original preserved
[7/7] tmp/test6.jpg — error: verification failed: 7 secret region(s) still detectable in tmp/test6.redacted.jpg; original preserved
7/7 file(s) flagged, 382 region(s) total, 3 file(s) errored/unverified.
By kind:
high-entropy 343
keyword-secret 15
db-connection-uri 10
github-pat 6
unix-crypt-hash 6
aws-access-key 1
stripe-key 1
Needs manual attention (3):
tmp/test3.jpg — error: verification failed: 4 secret region(s) still detectable in tmp/test3.redacted.jpg; original preserved
tmp/test4.jpg — error: verification failed: 9 secret region(s) still detectable in tmp/test4.redacted.jpg; original preserved
tmp/test6.jpg — error: verification failed: 7 secret region(s) still detectable in tmp/test6.redacted.jpg; original preserved
| Prima — valori segreti visibili | Dopo clean — cancellati irreversibilmente |
![]() |
Lo stesso screenshot prima e dopo clean: ogni valore segreto viene decodificato in pixel, incorniciato e ricodificato in una nuova immagine (irreversibile) — etichette e struttura rimangono leggibili.
Richiede il binario OCR tesseract disponibile su PATH:
brew install tesseractwinget install UB-Mannheim.TesseractOCR (o l'installer UB Mannheim)apt-get install tesseract-ocrPoi costruisci:
go build -o screenscrub ./cmd/screenscrub
Compilazione incrociata per macOS (arm64/amd64) e Windows (amd64) — si appoggia a tesseract tramite shell anziché tramite cgo. Testato solo su macOS.
scan <dir|file> — prova a seccoRileva e segnala; non scrive nulla.
screenscrub scan ~/Screenshots --ignore 'node_modules/**' --ignore '*.thumb.png'
screenscrub scan shot.png --json # rapporto leggibile da macchina
screenscrub scan ~/Screenshots --preview ~/review # guarda cosa clean oscurerebbe
--preview <dir> scrive una copia non distruttiva di ogni immagine segnalata con ogni regione rilevata delineata (non riempita), così puoi confermare cosa clean cancellerebbe prima di procedere. ⚠️ Queste copie contengono ancora i segreti — la directory viene creata con permessi 0700 e non deve essere condivisa.
Il rapporto termina con un riepilogo: una suddivisione per tipo e un elenco esplicito "Richiede attenzione manuale" — file che hanno generato errori, fallito la verifica o prodotto nessun testo OCR su ciò che dovrebbe essere uno screenshot (una mancata lettura OCR silenziosa è identica a un'immagine pulita, quindi viene segnalata piuttosto che accettata come tale).
scan --fail-on-findings esce con codice diverso da zero quando viene rilevato qualcosa — un gate pre-commit/CI.
clean <dir|file> — estrai, oscura, metti in quarantenaSCREENSCRUB_PASSPHRASE=… screenscrub clean ~/Screenshots \
--out ~/Screenshots/clean \
--sink file://./secrets.age \
--yes
Per ogni immagine con risultati, clean:
<nome>.redacted.png in --out (o vicino all'originale),<out>/quarantine/ crittografato con age.I segreti vengono inviati al sink passato con --sink: file age (predefinito), 1Password (op://), KeePassXC (keepassxc://), Keychain di macOS (keychain://) o HashiCorp Vault (vault://) — o destinatari age basati su hardware (YubiKey, Touch ID). Vedi Sink di segreti.
Flag:
--exclude-kind e --min-confidence sono manopole di precisione locali (senza rete) per un corpus rumoroso — l'equivalente offline di --llm. Esse scambiano richiamo per un rapporto più silenzioso, quindi usale con attenzione. Entrambe funzionano anche su scan/verify e possono essere impostate in --config (exclude_kinds, min_confidence).
Ordine di risoluzione della passphrase: --passphrase, poi il file di configurazione, poi SCREENSCRUB_PASSPHRASE. Evita --passphrase sulla riga di comando — argv è visibile ad altri processi.
verify <dir|file> — dimostra che una cartella è pulitaRi-OCRizza ogni immagine, riesegue il rilevamento e esce con codice diverso da zero se una qualsiasi regione segreta viene ancora trovata — usalo come gate di consegna/CI prima di inviare screenshot a un cliente. (Dimostra che l'OCR non può leggere il segreto, non che un umano non possa mai farlo.)
screenscrub verify ~/deliverable/screenshots
echo $? # 0 = pulito, non-zero = un segreto è ancora rilevabile
clean esegue automaticamente questo stesso controllo su ogni copia oscurata e non metterà in quarantena né distruggerà un originale la cui copia oscurata fallisce; --no-verify lo salta (non raccomandato).
open <store> — recupera segreti o un originale in quarantenaRecupera i tuoi dati: open decifra il sink age (i record di segreti memorizzati) o un originale in quarantena in --out, mai su stdout. Vedi Decifrare i depositi.
--llm — elimina falsi positivi benigni con un LLMLe regole di livello gitleaks applicate a testo OCR (con rumore OCR) inevitabilmente generano falsi positivi: il rivelatore orientato al richiamo segnala stringhe ad alta entropia benigne che un operatore vuole mantenere — hostname, GUID, SHA git, hash NTLM che sono essi stessi la prova di compromissione. --llm passa ogni candidato a Claude (Opus 4.8), mantenendo i veri segreti e scartando gli identificatori benigni; le regioni confermate dal LLM vengono riempite di rosso.
ANTHROPIC_API_KEY=… screenscrub clean ~/Screenshots --out ~/clean --llm --yes
Questo rompe intenzionalmente la modalità locale-e-silenziosa: invia le stringhe candidate (i segreti rilevati) all'API Anthropic, quindi è solo opt-in, stampa un avviso e necessita di una chiave API. Fallisce in modo chiuso: qualsiasi errore API/rete mantiene tutti i risultati (oscura tutto) piuttosto che rischiare di perdere un vero segreto. Effettua una chiamata API per immagine segnalata (massimo 4 concorrenti indipendentemente da --jobs) — tieni conto dei limiti di velocità e dei costi su directory grandi.
--watchscreenscrub clean ~/Screenshots --watch --sink file://./secrets.age --yes
Elabora tutto ciò che è già presente, poi monitora nuove immagini modificate e le oscura non appena appaiono. Gli output *.redacted.png vengono ignorati per evitare cicli.
Su token OCR, screenscrub combina:
AKIA…, GitHub ghp_…, GitLab, Slack, Stripe, OpenAI, chiavi API GCP, JWT e intestazioni PEM. (Copiate, non importate, per rimanere piccolo e completamente offline.)postgres://u:p@…), intestazioni HTTP Basic/Bearer, hash NetNTLMv2 e NTLM (pwdump), hash crypt di /etc/shadow, chiavi private PuTTY e OpenSSH, token SendGrid/npm.password, token=, secret, api_key, …) che segnalano il valore adiacente anche quando non corrisponde a nessun modello e ha bassa entropia.BEGIN/END e oscura generosamente l'intera regione circostante piuttosto che fidarsi di corrispondenze parola per parola.Ogni hit viene mappato alle scatole di pixel dei token che lo compongono (due ricostruzioni di riga — spaziata e compatta — recuperano i segreti che l'OCR ha suddiviso per spazi), e le scatole vengono imbottite prima di essere riempite.
Un sink è un metodo: Store(Secret) error. Adattatori forniti:
file:// — file locale crittografato con age (predefinito). Nessuna dipendenza esterna; un array JSON di record crittografati con una passphrase scrypt age (o con destinatari age — vedi crittografia basata su hardware sotto).op:// — CLI 1Password. Esegue op item create con il valore su stdin (mai in argv).keepassxc://./vault.kdbx — KeePassXC. Popolare gestore locale multipiattaforma KeePass. Esegue keepassxc-cli add; la password del database e il segreto vengono entrambi passati su stdin (mai in argv). La password del database è la passphrase risolta (--config/SCREENSCRUB_PASSPHRASE).keychain://screenscrub — Keychain macOS. Esegue security add-generic-password. Avvertenza: lo strumento security accetta il segreto solo come argomento della riga di comando, quindi è brevemente visibile a ps dello mentre è in esecuzione (macOS nasconde argv dagli altri utenti). Per una maggiore igiene preferisci 1Password, KeePassXC o il destinatario Secure Enclave sotto.Per aggiungerne uno tuo, implementa sink.SecretSink (internal/sink) e collegalo in sink.FromURI. Questo è l'intero contratto.
Il sink file predefinito (age) e la quarantena possono crittografare per destinatari age invece di una passphrase, inclusi quelli basati su hardware tramite plugin age:
# YubiKey (PIV) — installa age-plugin-yubikey, poi:
screenscrub clean ~/Screenshots --out ~/clean \
--sink file://./secrets.age \
--recipient age1yubikey1q... --yes
# Apple Secure Enclave / Touch ID — installa age-plugin-se, poi:
screenscrub clean ~/Screenshots --out ~/clean \
--recipient age1se1q... --yes
# Coppia di chiavi age normale, o più destinatari / un file di destinatari:
screenscrub clean ~/Screenshots --recipient age1abc... --recipients-file team.age.pub --yes
In modalità destinatario non è necessaria alcuna passphrase; sia il sink che la quarantena crittografata vanno ai destinatari. Recupera con il token hardware: age -d -i <identità> secrets.age (la decifratura richiede il tocco YubiKey / Touch ID). Crittografare per un destinatario plugin necessita del binario age-plugin-<nome>; la decifratura richiede inoltre il dispositivo.
openIl sink e la quarantena usano chiavi separate derivate dalla tua unica passphrase (HKDF), quindi decifrare un deposito non può aprire l'altro. Poiché le chiavi su disco sono derivate, decifrale con screenscrub piuttosto che con age grezzo:
screenscrub open ./secrets.age --out secrets.json # i record dei segreti
screenscrub open ~/clean/quarantine/shot.png.ab12cd34.age --out shot.png # un originale
open prova automaticamente ogni chiave derivata e scrive il testo in chiaro in --out (mai stdout). Per i depositi in modalità destinatario, passa un file di chiave age: screenscrub open secrets.<id>.age --identity key.txt --out secrets.json (per YubiKey/Touch ID usa age -d -i direttamente, che guida il plugin hardware). La modalità destinatario scrive un file per segreto (secrets.<hash>-<rand>.age) così esecuzioni ripetute non si sovrascrivono mai a vicenda.
go test ./... # i test unitari vengono eseguiti senza tesseract; i test OCR/ri-OCR vengono saltati se assente
La garanzia principale è coperta da un test end-to-end: renderizza uno screenshot con una chiave AWS, esegue la pipeline reale OCR → rilevamento → oscuramento, ri-OCRizza l'output e asserisce che il segreto è sparito (internal/redact).
screenscrub non è una licenza per smettere di pensare.
password:); un segreto in una forma che non riconosce passerà inosservato.È ottimizzato per richiamo (recall) piuttosto che per precisione — deliberatamente sovra-segnala, perché una credenziale lasciata in un'immagine è il caso catastrofico e un falso positivo costa solo un blob oscurato.
Puoi vedere un caso d'uso realistico nella cartella testdata/, tratta da screenshot di RExpository:
https://jaimepolop.github.io/RExpository/
Non è perfetto, ma piuttosto buono.
clean la ri-OCRizza e riesegue il rilevamento; se un qualsiasi segreto è ancora rilevabile, si rifiuta di mettere in quarantena o distruggere l'originale e segnala il file. Le scatole di oscuramento vengono imbottite proporzionalmente all'altezza dei glifi in modo che i bordi anti-alias su testo grande/ad alta risoluzione siano coperti.clean scrive la copia oscurata, la verifica, quindi sposta l'originale non oscurato in una quarantena crittografata con age e lo rimuove dalla sua posizione originale — solo dopo che la copia è stata verificata pulita e i segreti estratti sono stati messi al sicuro sul disco. I passaggi distruttivi richiedono --yes.AK****…**LE). L'unica copia in testo chiaro vive in memoria fino a raggiungere il tuo sink crittografato.--purge e i limiti della cancellazione sicuraPer impostazione predefinita, gli originali vengono conservati (crittografati) in quarantena in modo che una falsa oscuramento sia recuperabile. --purge invece distrugge l'originale: sovrascrive con casuale, sovrascrive con zeri, poi scollega.
La cancellazione sicura è inaffidabile. Su SSD, filesystem copy-on-write (APFS, Btrfs, ZFS), filesystem journaled e volumi con snapshot/backup, sovrascrivere e poi scollegare non garantisce che i byte originali siano spariti — il wear leveling e gli snapshot possono trattenerli. L'unica vera difesa è non persistere mai il testo chiaro in primo luogo. Preferisci la quarantena crittografata e tratta --purge come un tentativo di buona volontà.
OCR — una possibile aggiunta futura.I modelli di credenziali inclusi derivano da gitleaks (MIT).
![]() |
| Flag | Significato |
|---|
--out <dir> | dove vanno le copie oscurate (predefinito: accanto all'originale) |
--sink <uri> | file://./secrets.age · op://Vault · keepassxc://./v.kdbx · keychain://service · vault://KV |
--recipient <age1…> | crittografa per un destinatario age (ripetibile): X25519, YubiKey o Touch ID |
--recipients-file <f> | file di destinatari age, uno per riga |
--passphrase | passphrase per il sink file / quarantena (preferisci la variabile d'ambiente o --config) |
--watch | rimane in esecuzione; elabora nuovi screenshot non appena arrivano (fsnotify) |
--purge | distrugge gli originali invece di metterli in quarantena (vedi avvertenza sotto) |
--keep-originals | scrive solo le copie oscurate; lascia gli originali al loro posto (nessuna quarantena, nessuna passphrase, nessun --yes) — mutualmente esclusivo con --purge |
--yes | conferma la gestione distruttiva degli originali (richiesto) |
--config <file> | configurazione JSON; ti permette di tenere la passphrase fuori da argv |
--no-verify | salta la verifica di ri-OCR dell'output oscurato (non raccomandato) |
-i, --interactive | apre ogni immagine segnalata e chiede di confermare l'oscuramento/il salto prima di toccarla |
--llm | usa Claude per eliminare falsi positivi benigni; invia stringhe candidate all'API Anthropic |
--llm-api-key | chiave API Anthropic per --llm (o ANTHROPIC_API_KEY) |
--jobs N | elabora N immagini in parallelo (predefinito: numero di CPU) |
--manifest <file> | scrive una mappa JSON di originale→oscurato→regioni |
--ocr-raw / --ocr-upscale N | disabilita la pre-elaborazione del terminale scuro / ingrandisce prima dell'OCR |
--exclude-kind <kind> | sopprime i risultati di un tipo (ripetibile), es. high-entropy — manopola di precisione offline |
--min-confidence <0..1> | scarta i risultati al di sotto di questa confidenza — manopola di precisione offline |
--json | emette il rapporto come JSON |
--ignore <glob> | glob da ignorare, ripetibile; ** corrisponde a qualsiasi profondità |
--lang | lingua di tesseract (predefinito eng) |
vault:// — HashiCorp Vault KV v2 — presente ma rinviato/meno comune; il percorso fa l'hash del segreto in modo che non appaia mai nell'URL.