/v1/secrets
Scans free text for leaked credentials — private keys, named-provider tokens, and generic high-entropy secrets — using gitleaks' default detection rules.
- METHOD
- POST
- BODY CAP
- 64 KiB
- CHECKS
- 7
- FINDING CODES
- 7
POST /v1/secrets scans a block of free text — a support ticket, a pasted .env, a log
line, anything you’re about to store or forward — for credentials that shouldn’t be there.
Request#
{ "text": "AWS_SECRET_ACCESS_KEY=AKIAZQ3XVGH4KJPBMNRT", "redact": "mask" }
(Not AWS’s own docs placeholder, AKIA...EXAMPLE — gitleaks’ aws-access-token rule
allowlists anything ending EXAMPLE by design, so that value never fires here either.)
| Field | Type | Required | Notes |
|---|---|---|---|
text |
string | yes | Bounded only by this gate’s 64 KB request-body cap (D-80) — no separate limit here. That’s roughly 60,000 characters of English text; fewer for text with accents or emoji. |
redact |
string | no | Only "mask" is accepted; anything else is a 400 invalid_request. See “Redaction” below. |
Checks this gate runs#
Every check here is pure rule-based text scanning — gitleaks’ own regexes, keyword
prefilter, entropy thresholds and allowlists, all reimplemented over Go’s own regexp, no
network calls, no model inference — so all of them are free on every plan (CLAUDE.md design
rule 4: “Never withhold a zero-marginal-cost check”).
| Check | What it does |
|---|---|
secret.private_key |
A -----BEGIN ... PRIVATE KEY----- PEM block — see secret.private_key. |
secret.provider_token |
A named provider’s own token format — AWS, GitHub, Stripe, Slack, and ~215 more — see secret.provider_token. |
secret.jwt |
A JSON Web Token, unless its claims mark it as a public anon key — see secret.jwt. |
secret.connection_string |
A database or broker URL with a password in it — see secret.connection_string. |
secret.url_credentials |
A password in an http(s) URL or a Basic Authorization header — see secret.url_credentials. |
secret.test_mode_token |
A Stripe sk_test_/rk_test_ key — see secret.test_mode_token. |
secret.generic_credential |
A keyword (api, key, secret, token, password, …) next to a high-entropy value, gitleaks’ catch-all for anything with no named pattern — see secret.generic_credential. |
Eight finding codes cover roughly 220 individual detection rules plus our own patterns — gitleaks’ own rule id
rides along in data.matches[].rule (e.g. "github-pat", "aws-access-token") for a
caller who wants to act on the specific pattern without parsing a finding’s message.
Source and licence#
The detection rules are gitleaks’ own default
ruleset (MIT licence), fetched at a pinned release (v8.30.1) and embedded like every
other StormDoor reference list — not re-derived, not hand-picked. meta.ruleset reflects
the pinned version. We reimplement gitleaks’ matching semantics (keyword prefilter,
secretGroup extraction, Shannon-entropy thresholds, allowlists) over Go’s own regexp
rather than shelling out to the gitleaks binary, so every rule runs unchanged but as one
in-process scan.
What this gate does not cover#
- It never tests a credential against the provider it looks like it belongs to.
Format-only verification is the whole product here — calling AWS, GitHub, or Stripe with
a value we found in someone’s request body would mean using a stranger’s credential to
find out if it’s real, which we won’t do. A
secret.provider_tokenfinding means “this looks like a live AWS/GitHub/Stripe/etc. key,” not “this key still works.” - No free-floating entropy scan. Every check requires a recognisable shape (a PEM header, a provider’s own prefix, or a credential-shaped keyword next to a high-entropy value) — a bare high-entropy string with no such context is not flagged. That would flag every hash, UUID, and base64 image alongside real secrets.
- We never echo the matched value. A finding’s
messagenames the pattern that matched (from the rule’s own description), never the secret itself — the same rule/v1/piifollows, and the reasonlocationis an offset, not a quote.
Overlapping matches#
A private-key match always wins over anything inside its PEM block. A named-provider match
always wins over a generic-credential match on the same span — gitleaks’ own
generic-vs-specific dedup, reimplemented as this gate’s overlap-resolution priority (same
mechanism /v1/pii uses for e.g. a payment card partially overlapping a phone number).
Redaction#
redact: "mask" adds data.redacted to the response: your input text with every match
replaced by *, one asterisk per character, so offsets still line up. It’s the only
redaction mode — see the response format docs for why hashing, tokenising
and removal are all cut rather than offered.
Matches, truncation and offsets#
Every match also appears in data.matches as { "code", "rule", "start", "end" }. rule
is the specific gitleaks rule id that fired. start/end are Unicode code-point offsets
of the secret itself, not the whole matched text — e.g. for AWS_SECRET_ACCESS_KEY=AKIA...,
the offsets bound AKIA..., not the AWS_SECRET_ACCESS_KEY= prefix that helped identify
it. A scan is capped at 100 matches; past that, data.truncated: true is set and only the
first 100 (by position) are returned.
Example#
curl -s -X POST https://api.stormdoor.dev/v1/secrets \
-H "Authorization: Bearer sk_live_..." \
-H "content-type: application/json" \
-d '{"text":"AWS_SECRET_ACCESS_KEY=AKIAZQ3XVGH4KJPBMNRT","redact":"mask"}'
{
"ok": true,
"verdict": "block",
"findings": [
{
"code": "secret.provider_token",
"severity": "high",
"message": "Identified a pattern that may indicate AWS credentials, risking unauthorized cloud resource access and data breaches on AWS platforms.",
"location": "23-43"
}
],
"data": {
"matches": [
{ "code": "secret.provider_token", "rule": "aws-access-token", "start": 23, "end": 43 }
],
"redacted": "AWS_SECRET_ACCESS_KEY=********************"
},
"checks_run": ["secret.generic_credential", "secret.private_key", "secret.provider_token"],
"checks_skipped": [],
"meta": { "gate": "secrets", "ruleset": "reference-..." }
}
See response format for how checks_skipped reasons work, and don’t
proxy that array — or the raw match offsets — to your own end users.
Finding codes#
Each code links to its own page with what it looks at, why it matters and what to do.