TruffleHog misses Supabase's new secret keys, and gitleaks catches only some
If your app runs on Supabase, the key that opens all of its data now starts with sb_secret_. I tested two free tools that search code for leaked keys. TruffleHog found none of my 110 made-up keys. gitleaks found most of the ones sitting next to a word like "key", "secret" or "token", and missed the rest. GitHub's own scanning knows this key, but scans for it free only in public repositories (the places where code is stored on GitHub).
The short version
A clean scan doesn't prove your Supabase secret key is safe. Search for sb_secret_ in your code and its history yourself. Section 5 shows how, starting with one command.
Published
1 What the key is
Supabase gives every project two kinds of key. The publishable key (sb_publishable_...) is meant to sit in your app where anyone can see it. The secret key (sb_secret_...) is for your server only. Supabase's documentation says secret keys "provide full access to your project's data, bypassing Row Level Security", the rules that decide which user can see which rows measured (Supabase).
Supabase is deprecating its older keys, anon and service_role, by the end of 2026 measured (Supabase), so I expect more projects to carry the new format derived. Supabase rejects a secret key in a request that looks like it came from a web browser, which blocks one way a leaked key gets used. It doesn't stop anyone who copies the key into a script measured (Supabase).
2 What I tested, and what happened
On October 21, 2026, I made up 110 random keys in the layout Supabase documents and put 10 in each of 11 places a key might end up, in a throwaway project on my own computer that I never pushed anywhere. Then I ran both tools as they come, with no extra rules measured.
| Where the key sat | TruffleHog 3.97.9 | gitleaks 8.30.1 |
|---|---|---|
A settings file line, SUPABASE_SECRET_KEY=... | 0 of 10 found | 10 of 10 found |
A settings file line, SERVICE_KEY=... | 0 of 10 found | 10 of 10 found |
A settings file line, SUPABASE_TOKEN=... | 0 of 10 found | 10 of 10 found |
Code, const SUPABASE_SECRET = '...' | 0 of 10 found | 10 of 10 found |
Code, const apiKey = '...' | 0 of 10 found | 10 of 10 found |
A JSON file, "secret": "..." | 0 of 10 found | 10 of 10 found |
Code, headers: { apikey: '...' } | 0 of 10 found | 10 of 10 found |
A YAML file, password: ... | 0 of 10 found | 9 of 10 found |
Code, createClient(url, '...') | 0 of 10 found | 0 of 10 found |
Code, const admin = '...' | 0 of 10 found | 0 of 10 found |
| On its own line in a notes file | 0 of 10 found | 0 of 10 found |
So TruffleHog found 0 of 110 and gitleaks found 79. Both tools were working: in a first, smaller run, each caught a made-up Stripe live key, and TruffleHog caught a made-up Supabase personal access token, the other Supabase key type it knows measured. One of the ten YAML keys got past gitleaks. That miss depends on the key's random characters, not where it sits: across 300 more keys on a SUPABASE_SECRET_KEY= line, gitleaks found 294 and missed 6, about 2% measured. Its general rule skips strings that contain certain common words, and three of the six missed keys contained one, such as md5 measured (gitleaks's source). I didn't find the cause for the other three.
3 Why they miss it
Neither tool has a rule for this key. TruffleHog's only Supabase rule, in its newest release (3.99.3, October 19, 2026), looks for personal access tokens that start with sbp_ measured (TruffleHog's source), so I expect the newest release to miss these keys too derived. gitleaks's default rules don't mention Supabase. It caught keys only through a general rule that flags a long random string near one of ten words: access, api, auth, key, credential, creds, passwd, password, secret or token measured (gitleaks's source). That's why the same key passed into createClient or stored as admin got past it derived.
Both are open-source projects that take new rules from contributors. I asked TruffleHog to add one for these keys in September, and as of October 21, 2026 that request is still open measured (TruffleHog issue 5362).
4 What GitHub covers
GitHub's list of the secrets it scans for includes "Supabase Secret Key", and it can block a push (sending your code up to GitHub) that contains one. But the same page says the scanning runs free only on public repositories. A private repository owned by an organization needs GitHub's paid Secret Protection, and a private repository on a personal account is covered only on GitHub's enterprise plans (Enterprise Cloud with managed users, or the self-hosted Enterprise Server) measured (GitHub's documentation). I didn't test GitHub's scanning myself.
If your code is in a private repository on your own GitHub account, GitHub doesn't scan it for this key. The checks in section 5 do derived.
5 Check it yourself
You need git and a copy of your code on your computer. If your code only lives on GitHub, download it first with git clone and its address. Then open Terminal on a Mac, or PowerShell on Windows, move into your project folder, and run these. If one says "not a git repository", you're in the wrong folder.
git grep --untracked -n "sb_secret_"searches your current files, including new ones you haven't committed yet (a commit is a saved snapshot of your code). A settings file such as.envthat git is told to ignore won't show up, and that's where the key belongs.git log --all -G "sb_secret_" --onelinelists every commit, on the branches you have on your computer, that added, changed or removed a line with a secret key, including ones you deleted later measured (git's documentation). No output means no committed key on those branches.- If your project is older than the new keys, it may still hold the legacy
service_rolekey. It's a long string startingeyJhbGciOi, and the wordservice_roledoesn't appear in it as plain text, so run both commands again witheyJhbGciOiin place ofsb_secret_. Your public key starts the same way and is safe to show, so compare each hit with the public key in your Supabase dashboard. Anything else is the secret one. Don't paste a key into an online decoder. In my first run, gitleaks caught a made-up legacy key and TruffleHog didn't measured. Once you've switched to the new keys, Supabase lets you deactivate the old ones in its API Keys settings measured (Supabase). - If you find a key, follow Supabase's order: fix how it leaked, create a new secret key, switch every part of your app to it, check that it all works, then delete the old key measured (Supabase). Deleting the line from your code isn't enough, because your history still has it.
- If you use gitleaks, add a rule for these keys. With this rule in a
.gitleaks.tomlfile, it found all 110 test keys in all 11 places, and flagged none of 10 made-up publishable keys measured:[extend] useDefault = true [[rules]] id = "supabase-secret-key" description = "Supabase secret API key" regex = '''sb_secret_[A-Za-z0-9_-]{22}_[A-Za-z0-9_-]{8}''' keywords = ["sb_secret_"]
Keys also leak outside git, into the files your site sends to browsers. My security self-check covers that in check 8, and secrets anywhere in your git history in check 9.
How I got this
I ran the tests on October 21, 2026, on my own computer, with keys I made up at random in the layout Supabase documents (sb_secret_, 22 characters, an underscore, 8 more). None was ever a real key, and the test project was never pushed anywhere. I tested the public releases of both tools: TruffleHog 3.97.9, six releases behind the newest, whose rule I checked in the source, and gitleaks 8.30.1, the newest. What GitHub covers comes from its documentation, not a test. I tested 11 placements; I didn't test a key split across lines or encoded (rewritten in another format).
Two things you should know about me: I opened the TruffleHog request mentioned above, and I sell a security check for apps like these. I rechecked every source on October 21, 2026. The labels say how far each claim goes:
- Measured
- I counted it myself, or read it on the source that owns the fact (a product's own documentation, say).
- Reported
- A named source says it, and I can't check it myself.
- Derived
- My own reasoning or arithmetic from the two above.
Sources
- Supabase documentation, "API keys" (undated; read October 21, 2026). supabase.com
- GitHub documentation, "Supported secret scanning patterns" (undated; read October 21, 2026). docs.github.com
- TruffleHog source code, its Supabase detector at release 3.99.3, October 19, 2026. github.com
- gitleaks source code, its default rules at release 8.30.1, March 21, 2026. github.com
- GitHub, TruffleHog issue 5362, "Detector request: Supabase secret API keys (sb_secret_)", opened by me on September 28, 2026. github.com
- git documentation, "git-log", the
-Goption (read October 21, 2026). git-scm.com
Want me to look at your app?
The Launch Blocker Check is a private first look at your repo: I don't touch your live app or use a key I find without your written OK.
Launch Blocker Check, $149