No service-role or secret keys exposed to the public bundle
Why it matters
A variable name like NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY is a direct contradiction in terms — the NEXT_PUBLIC_ prefix tells the bundler to inline the value into every client bundle, and a service-role key grants RLS-bypassing database access. Shipping one to the browser gives any visitor full admin privileges against the database, including reading other users' rows, writing arbitrary data, and deleting tables. This is a documented pattern in the Supabase + Lovable ecosystem where service-role keys prefixed with NEXT_PUBLIC_* get inlined into every JavaScript bundle sent to browsers, where anyone can extract them via devtools. It shows up constantly in AI-generated Supabase starters because the tool sees SUPABASE_ env vars in docs and reaches for the public prefix out of pattern-matching habit, not understanding the access-tier distinction between anon and service-role keys. The variable name alone is enough signal to flag — intent doesn't matter when the prefix dictates build-time behavior.
Severity rationale
Critical because the naming pattern guarantees client-bundle exposure on every deploy, and a leaked service-role key produces immediate full database compromise accessible from any visitor's devtools.
Remediation
Rename to drop the public prefix, treat the credential as compromised, rotate it. For Supabase: use the anon key client-side (already public-safe) and service-role only in server code.
Deeper remediation guidance and cross-reference coverage for this check lives in the security-hardening Pro audit — run that after applying this fix for a more exhaustive pass on the same topic.
Detection
- ID:
no-service-role-in-public-bundle - Severity:
critical - What to look for: Grep for env var names combining a public prefix with a sensitive substring:
NEXT_PUBLIC_*SECRET*,NEXT_PUBLIC_*SERVICE_ROLE*,NEXT_PUBLIC_*PRIVATE*,NEXT_PUBLIC_*ADMIN*,VITE_*SECRET*,VITE_*SERVICE_ROLE*,PUBLIC_*SECRET*. Count matches across.env.example,.env.sample, source, config. - Pass criteria: Zero matches.
- Fail criteria: Any match — public prefix + sensitive substring is a red flag regardless of intent. Common offender:
NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEYshipping to the client bundle. - Skip (N/A) when: No client-side framework detected.
- Report even on pass:
"Scanned env declarations + source: 0 public-prefixed sensitive matches". - Detail on fail:
"NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY in .env.example — service role key ships to the client bundle". - Remediation: Drop the public prefix, rotate the credential, treat as compromised. For Supabase: anon key client-side, service role only in server code.
Taxons
History
- 2026-04-22·v1.0.0·Initial import from project-snapshot via Phase 8.1 bundling·by phase-8-1-bundle-project-snapshot
- 2026-04-22·v2.0.0·Phase 9 consequence-first restructure — moved to new section slug, added news/incident references, severity reviewed.·by phase-9-stack-scan-v3
- 2026-04-25·v2.0.1·v3.1.0 pre-ship trim — prose compression for under-80K MCP cap; merged overlapping Fail-criteria / Do-NOT-pass-when sections; compressed enumeration prose; one remediation example per pattern. No semantic change; anti-sycophancy guards preserved.·by phase-9-1-stack-scan-v3-1