VibeRaven

Findings reference

By Ohad Krispin, founder of VibeRaven · LinkedIn

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 --json

Each 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.

Severity
TerminalIn --jsonMeaning
🔴 BlockercriticalFix before launch. check exits 1.
🟡 WarningwarningWorth fixing. The exit code stays 0.
⚪ InfoinfoContext. 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/ and database/, 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'; shows true for 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_disabled writes 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 true with 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 definer function in public, not a trigger, whose execute right is not revoked.
Fix
revoke execute on function <fn> from public, anon, authenticated; or make it security invoker.
Test after the fix
select has_function_privilege('anon', 'public.<fn>(<argument types>)', 'execute'); returns false.

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 hardcoded service_role or sb_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, dist or build) for the key value or service_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 and vercel.json, under NEXT_PUBLIC_, VITE_, PUBLIC_, EXPO_PUBLIC_, REACT_APP_ and NUXT_PUBLIC_.
Fix
Rename it without the prefix, read it only in server code, and rotate the key.
Test after the fix
No NEXT_PUBLIC_ or VITE_ 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_KEY and 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_ or VITE_.
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.test and .npmrc
Reads
File names not covered by .gitignore. Env templates such as .env.example are skipped.
Fix
Ignore private overrides in .gitignore, check with git ls-files whether 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, .txt and .example files: service_role tokens and sb_secret_, sk_live_ or sk- 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_URL or POSTGRES_URL on port 5432, when a direct Postgres client such as pg, 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.example and 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 constructEvent over the raw body with the stripe-signature header.
Fix
Read the raw body with await req.text(), verify it with stripe.webhooks.constructEvent and STRIPE_WEBHOOK_SECRET, and answer 400 when it fails.
Test after the fix
stripe listen --forward-to localhost:3000/<route> and stripe trigger checkout.session.completed: the handler answers 200. A POST with a made-up stripe-signature header 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.completed and 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_SECRET to .env.example as a placeholder, generate a value with openssl rand -base64 32 and set it in the deployment env.
Test after the fix
AUTH_SECRET is set in the hosting provider's production env, and signing in works on a preview deployment.
Automatic fix
fix --gap auth_secret_missing writes 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.X and import.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.example and 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_limit adds a limiter to middleware.ts.

missing_health_route

No health check endpoint for an uptime monitor.

Severity
Info
Automatic fix
fix --gap missing_health_route creates app/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_header adds a default policy to next.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_boundary creates app/error.tsx.

missing_loading_state

No loading states. A slow fetch looks like a frozen app.

Severity
Info
Automatic fix
fix --gap missing_loading_state creates app/loading.tsx.

missing_404_page

Custom not-found page not detected.

Severity
Info
Automatic fix
fix --gap missing_404_page creates app/not-found.tsx.

missing_image_optimization

Raw <img> tags bypass image optimization.

Severity
Info
Fix
Use next/image, or set width, height and loading="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.

All VibeRaven docs