A local-machine secret sweep for macOS, written in Swift 6. gitleaks and
friends scan your repositories; histscrub scans the laptop itself - the
places secrets leak that no repo scanner ever looks: shell and REPL histories,
.netrc, .git-credentials, .npmrc, .env files, and the permissions on
the credential stores you actually rely on.
Findings are always redacted - histscrub shows you enough of a secret to find it, never enough to leak it again.
$ histscrub
histscrub · scanned 4 file(s), 11 lines
CRIT ~/.git-credentials:1 URL with embedded credentials: https:…9@
CRIT ~/.zsh_history:3 Stripe live key: sk_liv…Qr
CRIT ~/.zsh_history:4 Anthropic API key: sk-ant…sT
WARN ~/.netrc mode 644 - should be 600
WARN ~/.netrc:3 plaintext password in .netrc: pass…23
WARN ~/.zsh_history mode 644 - should be 600
3 critical, 3 warning(s), 0 info
Default home sweep (histscrub, no arguments)
- Shell histories: zsh, bash, fish, plus
.zsh_sessions/ - REPL histories: Python, Node, IRB, psql, mysql
- Ad-hoc credential files:
.netrc,.git-credentials,.npmrc,.pypirc,.docker/config.json - Permission checks on stores that are supposed to hold secrets
(
.aws/credentials,~/.sshprivate keys) - their contents are expected, but world-readable mode bits are not
Directory scan (histscrub ~/Developer)
Walks the tree for stray .env*, *.pem, *.key, and id_* files,
skipping node_modules, .git, build output, and caches.
High-confidence token shapes (AWS, GitHub, Slack, Stripe, Google, Anthropic,
OpenAI, JWTs, private-key blocks) are flagged everywhere. Looser heuristics
(SECRET=/PASSWORD= assignments, --password on the command line, curl -u user:pass) apply only to histories and .env files, where a match is far more
likely to be a real leak than a false positive.
A secret in ~/.aws/credentials is normal - that file exists to hold it.
The same key pasted into your shell history is a leak. histscrub treats
expected stores and ad-hoc exposure differently, so it stays quiet about the
former and loud about the latter. That is the difference between a scanner you
run once and one you keep.
Requires Swift 6 (Xcode 16+ or a Swift 6 toolchain).
git clone https://github.com/GlyphSH/histscrub
cd histscrub
swift build -c release
.build/release/histscrub --versionhistscrub [--json] [dir ...]
| Code | Meaning |
|---|---|
| 0 | clean, or informational findings only |
| 1 | warnings |
| 2 | critical findings |
histscrub --json | jq '.findings[] | select(.severity == "crit")'- Swift 6 with complete strict-concurrency checking; all model types
Sendable. - A testable
HistscrubKitlibrary core; the executable is a thin CLI over it. - Zero dependencies - native Swift
Regex, no ArgumentParser. - Binary files (NUL byte) and anything over 10 MB are skipped; metafied zsh history with invalid UTF-8 is tolerated, not fatal.
swift test