Findings reference
Each finding has a stable id, such as `rls_disabled`, that appears in the terminal, in `--json` and in SARIF. This page lists every id the check reports, with its severity, what it found, the fix and a test to run once the fix is in.
npx -y viberaven@1.7.8 check --jsonEach entry in gaps has the id, severity, title and detail described below. Or ask your coding agent to "run npx -y viberaven@1.7.8 check and fix what it reports".
Read a finding
Every entry below has the title the terminal prints, what the check reads, the fix, and, where there is one, a test to run on the app once the fix is in. prompt --gap <id> prints the same fix and test as a prompt for your agent.
| Terminal | In --json | Meaning |
|---|---|---|
| 🔴 Blocker | critical | Fix before launch. check exits 1. |
| 🟡 Warning | warning | Worth fixing. The exit code stays 0. |
| ⚪ Info | info | Context. Often fine as it is. |
Supabase findings apply only if the app uses Supabase. A finding is evidence in the repository, not proof of what is live.
Row level security
rls_disabled
No RLS policy proof in migrations. A public table is created and no later migration enables row level security on it.
- Severity
- Blocker
- Reads
- SQL under
supabase/,migrations/,db/anddatabase/, replayed in order, public schema only. - Fix
- Add a migration that enables RLS and creates explicit policies for every user-facing table:
alter table <table> enable row level security; - Test after the fix
- After the migration is applied,
select relname, relrowsecurity from pg_class where relnamespace = 'public'::regnamespace and relkind = 'r';showstruefor each listed table. Then open a page that reads one of them while signed in: rows still appear only if a policy allows them. - Automatic fix
fix --gap rls_disabledwrites a new migration that enables RLS with no policies, so browser reads return no rows until policies exist. Apply it only when you mean that.
rls_no_policies
Table with RLS on and no policies (service role only). Only the service role can read or write it.
- Severity
- Info
- Reads
- Migration SQL: a public table with RLS enabled and zero policies.
- Fix
- Nothing, if only the server uses the table. If the browser reads it, add policies.
- Test after the fix
- Signed in as a normal user, the app's own query on
<table>returns that user's rows. An empty result means RLS is on and the policy the app needs is still missing.
rls_not_checked
RLS not checked: no table-level SQL evidence. The app uses Supabase, but no SQL in the repo creates a table, enables RLS or creates a policy.
- Severity
- Info
- Reads
- The same SQL folders as
rls_disabled. - Fix
- Commit your migrations, or check the policies in the Supabase dashboard. Do not enable RLS or invent policies because the SQL is missing.
rls_policy_allows_all_read
A policy lets anyone read every row: a select policy for anon, authenticated or public with using (true).
- Severity
- Warning
- Reads
- Migration SQL.
- Fix
- Keep it only if every row is meant to be public. Otherwise replace
truewith a condition on the signed-in user. - Test after the fix
- With the anon key and no session,
supabase.from('<table>').select('*').limit(1)returns no rows, unless the table is meant to be public.
rls_policy_allows_all_write
A policy lets anyone write every row: an insert, update, delete or all policy whose using or with check is a bare true.
- Severity
- Blocker
- Reads
- Migration SQL.
- Fix
- Tie the policy to the signed-in user, such as
auth.uid() = user_id, or drop it if only the server writes. - Test after the fix
- Signed in as user A,
supabase.from('<table>').update({...}).eq('id', <a row of user B>)changes 0 rows.
security_definer_function_executable
Security definer function callable by the Data API roles. It runs with its owner's rights, and anyone with the anon key can call it over /rest/v1/rpc/.
- Severity
- Warning
- Reads
- Migration SQL: a
security definerfunction in public, not a trigger, whose execute right is not revoked. - Fix
revoke execute on function <fn> from public, anon, authenticated;or make itsecurity invoker.- Test after the fix
select has_function_privilege('anon', 'public.<fn>(<argument types>)', 'execute');returnsfalse.
Data API grants
Since October 30, 2026, new Supabase tables are not exposed to the Data API until a migration grants them. See provider changes.
data_api_grant_missing
Client-queried table has no Data API grant. The browser queries a table that has RLS and a policy, but no grant, so the call fails with permission denied.
- Severity
- Warning
- Reads
- Migration SQL and
supabase/config.toml, plus.from('<table>')calls in browser code. - Fix
grant select on table <table> to authenticated;in a migration, with only the rights the app needs.- Test after the fix
- After the migration is applied, the app's query on
<table>through supabase-js returns data instead of a permission error (42501).
data_api_grant_without_rls
Table granted to the Data API with RLS off. The grant serves every row to that role.
- Severity
- Blocker
- Reads
- Migration SQL: a grant to anon or authenticated on a table without RLS, or on a view without
security_invoker. - Fix
- Enable RLS and add policies, or revoke the grant. For a view:
alter view <view> set (security_invoker = true); - Test after the fix
- With the anon key and no session,
supabase.from('<table>').select('*').limit(1)returns no rows, unless the table is meant to be public.
Keys and secrets
service_role_key_in_client_code
Service role key used in client code. The key skips RLS, and this file ships to the browser.
- Severity
- Blocker
- Reads
- Browser code, such as
'use client'files: a Supabase client built with the service role key, or a hardcodedservice_roleorsb_secret_key. - Fix
- Move every use behind a server route, server action or edge function, and rotate the key.
- Test after the fix
- Build the app (
npm run build), then search the client bundle (.next/static,distorbuild) for the key value orservice_role: no match. Rotate the key in Supabase, because it already shipped.
service_role_key_in_client_env
Service role key in a client-prefixed env variable, such as NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY. The framework exposes it to the browser.
- Severity
- Blocker; a warning when the framework does not expose that prefix
- Reads
.env*files andvercel.json, underNEXT_PUBLIC_,VITE_,PUBLIC_,EXPO_PUBLIC_,REACT_APP_andNUXT_PUBLIC_.- Fix
- Rename it without the prefix, read it only in server code, and rotate the key.
- Test after the fix
- No
NEXT_PUBLIC_orVITE_variable holds the service role key in.env*files or in the hosting provider's env settings. Rotate the key in Supabase, because it already shipped.
service_role_boundary
Service-role key may reach client code. The audit flagged the service role boundary without naming a single key.
- Severity
- Blocker
- Reads
- The same files as the two findings above.
- Fix
- Review every use of
SUPABASE_SERVICE_ROLE_KEYand keep it in server-only modules.
secret_key_in_client_env
Server credential under a public env prefix, such as NEXT_PUBLIC_STRIPE_SECRET_KEY.
- Severity
- Blocker; a warning when the framework does not make that prefix public
- Reads
- Env and config files: private provider credential names, references or key-shaped values under
NEXT_PUBLIC_orVITE_. - Fix
- Keep private credentials in unprefixed server-only variables. If a real one was exposed, rotate it with its provider.
openai_key_in_browser
OpenAI credential wired to browser code: read from a public env variable, passed to a browser SDK, or written as an sk- literal.
- Severity
- Blocker
- Reads
- Code files, not templates, tests or docs.
- Fix
- Keep the key in an unprefixed server-only variable and call OpenAI through an authenticated server endpoint. Rotate a real exposed key.
secrets_in_repo
Private-credential-like filenames need review, such as .env.local not covered by .gitignore. Only the file name is read, not its contents.
- Severity
- Blocker; a warning for
.env,.env.development,.env.production,.env.testand.npmrc - Reads
- File names not covered by
.gitignore. Env templates such as.env.exampleare skipped. - Fix
- Ignore private overrides in
.gitignore, check withgit ls-fileswhether they were committed, and rotate only real credentials that were.
secret_in_docs
Private credential-shaped value in a tracked document, such as a Supabase secret key pasted into a README.
- Severity
- Warning
- Reads
- Tracked
.md,.txtand.examplefiles:service_roletokens andsb_secret_,sk_live_orsk-values. Placeholders are skipped. - Fix
- Replace the value with a placeholder and rotate it if it was real. It stays in git history.
- Test after the fix
git grep -n "PRIVATE KEY"finds nothing in tracked files. Rotate the key, because it stays in git history.
scan_incomplete_service_role
Service role key scan did not finish: the walk hit a size or depth limit, or a path was unreadable.
- Severity
- Warning
- Fix
- Check the unscanned paths it lists by hand, remove what blocks the scan, such as large generated files, and run the check again.
Database connection
pooler_port_mismatch
Supabase connection is not using the pooler port. Serverless functions on Vercel open many short connections; the direct port 5432 runs out of them.
- Severity
- Warning
- Reads
- Env files and
vercel.json:DATABASE_URLorPOSTGRES_URLon port 5432, when a direct Postgres client such aspg, Prisma or Drizzle is installed. - Fix
- Point the database URL used on Vercel at the transaction pooler, port 6543.
- Test after the fix
- The database URL that serverless code reads uses port
6543(transaction pooler), in.env.exampleand in the hosting provider's env settings. Deploy a preview and call a route that queries the database: it answers without a connection error.
pooler_port_unchecked
Supabase pooler port could not be checked: a Postgres client is installed, but no connection string is in the repo.
- Severity
- Info
- Fix
- Find where the app builds its connection string, and use port 6543 for serverless deployments.
database_url_missing
DATABASE_URL missing from .env.example, in a repo with a Prisma schema or a Drizzle config.
- Severity
- Warning
- Fix
- Add a placeholder, such as
DATABASE_URL=postgres://user:password@host:6543/db, to.env.example.
unbounded_query
Database queries with no row limit: a Supabase select without .limit() or .range(), or a Prisma findMany without take.
- Severity
- Warning
- Fix
- Paginate, starting with the files it lists.
Payments and auth
unverified_stripe_webhook
Stripe webhook does not verify the signature. Anyone who finds the URL can post a fake event, such as a paid checkout.
- Severity
- Warning
- Reads
- Up to 40 payment-related code files: a handler that never calls
constructEventover the raw body with thestripe-signatureheader. - Fix
- Read the raw body with
await req.text(), verify it withstripe.webhooks.constructEventandSTRIPE_WEBHOOK_SECRET, and answer 400 when it fails. - Test after the fix
stripe listen --forward-to localhost:3000/<route>andstripe trigger checkout.session.completed: the handler answers 200. A POST with a made-upstripe-signatureheader answers 400.
missing_stripe_webhook
Stripe installed but no webhook handler, so payments that finish outside the browser never reach the app.
- Severity
- Warning
- Fix
- Add a verified webhook route for
checkout.session.completedand the subscription events, then set up the endpoint and its secret in the Stripe dashboard.
auth_secret_missing
Auth secret is not configured: Auth.js or NextAuth is installed and no AUTH_SECRET or NEXTAUTH_SECRET is referenced.
- Severity
- Blocker
- Fix
- Add
AUTH_SECRETto.env.exampleas a placeholder, generate a value withopenssl rand -base64 32and set it in the deployment env. - Test after the fix
AUTH_SECRETis set in the hosting provider's production env, and signing in works on a preview deployment.- Automatic fix
fix --gap auth_secret_missingwrites a generated value to.env.local. You still set it in the host.
Env vars and routes
env_var_drift
Env vars used in code but missing from .env.example. A fresh clone, CI or the production deploy can run without them.
- Severity
- Warning; info when the repo has no env template at all
- Reads
- Names read in code, such as
process.env.Xandimport.meta.env.X, compared with every env template. Names the host provides are ignored. - Fix
- Add placeholder entries to the template, never real values, and set each one in the deployment env.
- Test after the fix
- Every name the code reads is in
.env.exampleand set in the hosting provider's production env.
public_debug_endpoint
Review a diagnostic API endpoint: a debug, test or env route whose response may include environment data, with no auth check before it.
- Severity
- Warning
- Fix
- Remove the route or the env data, or require a verified session before every sensitive response.
- Test after the fix
curl -s <preview-url>/<route>answers 404 or 401, not env values or request internals.
missing_rate_limit
API routes have no rate limiting: no rate-limit package or code next to the API routes.
- Severity
- Warning
- Fix
- Rate-limit the auth, webhook and AI routes first.
- Automatic fix
fix --gap missing_rate_limitadds a limiter tomiddleware.ts.
missing_health_route
No health check endpoint for an uptime monitor.
- Severity
- Info
- Automatic fix
fix --gap missing_health_routecreatesapp/api/health/route.ts.
repo_security_scan_incomplete
Diagnostic-route or tracked-document checks are incomplete: the scan hit a limit or an unreadable file.
- Severity
- Warning
- Fix
- Review the files it lists, fix access or remove large generated inputs, then run the check again.
App basics
These read Next.js apps. Each one with an automatic fix writes one new file you can review.
missing_csp_header
No Content-Security-Policy header in next.config, proxy, middleware or vercel.json.
- Severity
- Warning
- Fix
- Set a CSP header, in report-only mode first if you need to, and confirm it on the deployed site.
- Automatic fix
fix --gap missing_csp_headeradds a default policy tonext.config. It allows'unsafe-inline'and'unsafe-eval', so tighten it.
missing_error_boundary
No error boundary. One render error blanks the whole app.
- Severity
- Warning
- Automatic fix
fix --gap missing_error_boundarycreatesapp/error.tsx.
missing_loading_state
No loading states. A slow fetch looks like a frozen app.
- Severity
- Info
- Automatic fix
fix --gap missing_loading_statecreatesapp/loading.tsx.
missing_404_page
Custom not-found page not detected.
- Severity
- Info
- Automatic fix
fix --gap missing_404_pagecreatesapp/not-found.tsx.
missing_image_optimization
Raw <img> tags bypass image optimization.
- Severity
- Info
- Fix
- Use
next/image, or set width, height andloading="lazy".
missing_monitoring
No error monitoring detected, such as Sentry or PostHog, in an app with auth or a database.
- Severity
- Info
- Fix
- Add error monitoring, so production errors reach you before users report them.
no_release_tags
No release tags for version context: at least 20 commits and no git tag.
- Severity
- Info
- Fix
- Tag the state you deploy, such as
git tag v0.1.0, so a later change can be compared with it.
Next
Checked
Checked on November 3, 2026, against the published viberaven 1.7.8, with a real run on a small Next.js + Supabase + Stripe fixture repo.