Secret scanner
The scanner runs before every torii save. It is not a hook you install, not a CI job that tells you an hour later, and not something you can forget to enable on a new machine.
What it matches
| Family | Shapes | Example prefix |
|---|---|---|
| AWS | access keys, session keys, role ids | AKIA… ASIA… AROA… |
| GitHub | personal, OAuth, server, fine-grained | ghp_ gho_ ghs_ github_pat_ |
| GitLab | personal and trigger tokens | glpat- glptt- |
| Payments and mail | Stripe live keys, SendGrid, Twilio, Brevo | sk_live_ SG. |
| Bare bearers | a literal header value with no vendor prefix | Bearer … |
| Private keys | PEM blocks, SSH private keys | -----BEGIN … PRIVATE KEY----- |
| Connection strings | userinfo carrying a real user:password@ | postgres:// postgresql:// mysql:// mongodb:// mongodb+srv:// |
| Generic | high entropy next to a name that means secret | TOKEN SECRET API_KEY PASSWORD |
Files named *.example, *.sample or *.template are always skipped.
Detection matches the shape a key actually appears in, not only a token standing alone as a word. KEY=ghp_… with no spaces around it — the shape every .env file and every -e KEY=value container argument uses — is the case that slipped through before 0.13.0, and the case the current patterns were rewritten around.
Scanning what is already committed
torii scan # staged files
torii scan --history # every commit, from the rootIf --history finds something, the key is already public. Rotate it first, then clean the history:
torii history replace-text --redact-secretsFalse positives
Only syntactic placeholders are excluded — ${VAR}, $VAR, <placeholder>. Word-shaped ones like password or changeme are not, because a real weak password collides with them. So a documentation example containing user:pass@localhost will be flagged.
| Situation | What to do |
|---|---|
| A one-off, and the finding is not real | torii save -am "msg" --yes |
| A file that will keep tripping it | an allow: entry under [secrets] in .toriignore |
| One line, vetted | // torii:allow-secret on that line |
| CI, or any run without a terminal | --yes is required: the scanner cannot prompt and refuses to hang |
Your own patterns
torii ignore secret 'PROP_[a-z]{20}' --name internal # lands in .toriignore.local
torii ignore secret 'AKIA[0-9A-Z]{16}' --public # lands in .toriignore, with a warning
torii ignore list # the effective, merged rules.toriignore.local is machine-private and gitignored automatically. It is where a pattern belongs when the pattern itself is a hint: a proprietary key format, an internal path, an audit regex. Publishing the regex that matches your internal tokens tells an attacker what to look for.