docs
stormdoor.dev
FINDING

secret.jwt

A JSON Web Token whose claims do not mark it as a public key appears in the text.

SEVERITY
high
LEADS TO
block
GATE
/v1/secrets
CHECK
secret.jwt
Jump to what to do

What it looks at#

Gitleaks’ jwt and jwt-base64 rules find the token; this check then decodes the payload (base64url, bounded to 4 KiB) and reads the role claim. A token with "role":"anon" is what Supabase calls the anon key: it is designed to ship in every browser bundle, so it emits nothing. Every other token fires, including "role":"service_role" (a full-access key that bypasses row-level security), "role":"authenticated" (a user’s session), and a token with no role at all. A token whose payload cannot be decoded is never cleared.

An expired token (exp in the past) still fires. Severity is fixed per finding code, the claims can still identify a person and a project, and exp is a claim the issuer wrote, not something this gate can vouch for. A base64-wrapped JWT (the jwt-base64 rule) is flagged without reading its claims.

Why it matters#

A JWT is a bearer credential: whoever holds an unexpired one is that user, or that service, until it expires or the signing key is rotated. A service-role or admin-scoped token is the worst case, since it usually grants unrestricted access to the whole project.

What to do#

High severity. Revoke or rotate the signing secret if the token is long-lived or privileged, and keep service-role keys server-side only. If the token is a public anon key and you see this finding, its payload does not say "role":"anon", so it is not one.

Example#

curl -s -X POST https://api.stormdoor.dev/v1/secrets \
  -H "Authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{"text":"SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIn0.Zk3pQ8rT1vWx7YbN5cLd2HgJ9mKa4sEf6uOi0tRq"}'

The response carries one secret.jwt finding. The same text with "role":"anon" in the payload returns verdict: "clean".

OTHER FINDING CODES FROM /V1/SECRETS