Rule catalogue
What the Vibe Code Rescue Scan checks
Every check has a stable, versioned code (e.g. RSC-SEC-014) and its own page: what is checked, why it matters and what to do. The catalogue is the open reference behind the score.
Scan your own app for freeHow the score is calculated
The score is not an opinion, it is arithmetic. Here it is in full — so you can retrace any result and disagree with us. The numbers on this page come straight from the running code, not from documentation that can go stale.
Dimension weights
- Security
- 0.26
- Privacy (GDPR)
- 0.20
- Scaling & cost
- 0.14
- Accessibility
- 0.12
- Architecture & SEO
- 0.16
- Code quality
- 0.12
Deductions per finding
Every dimension starts at 100 points. Each finding deducts, depending on severity:
- High
- −34
- Medium
- −16
- Low
- −7
- Info
- −0
Deductions from “low” findings are capped at 21 points per dimension. Hygiene items should lower the score but never flip the verdict on their own — eight missing HTTP headers are not a broken foundation.
From score to verdict
- From 75 points, and with no evidenced security or privacy finding: go — the visible signals look sound. A single medium-severity finding in either area turns this into “iterate” even at a high score: a green light next to a finding the same report lists would be a contradiction.
- 45 to 74 points: iterate — solvable work, in a clear order.
- Stop is issued only when at least one finding has severity “high” — an evidenced high-risk result such as an open database or an exposed master key. A low score alone is not enough.
Whatever could not be assessed is marked “limited” and never counts as perfect. A dimension without findings is 100 only if the required scan tier actually ran.
Methodology version 2026.09. Every substantive rule change increments it, so older results stay interpretable.
Security
No end-to-end HTTPS
Checks whether the final response is served encrypted or ends up on plain http.
HSTS header missing
Checks whether Strict-Transport-Security is set so even the first request is protected.
No Content-Security-Policy
Checks header and meta tag for a Content-Security-Policy as the main XSS mitigation.
MIME sniffing not disabled
Checks whether `X-Content-Type-Options: nosniff` stops content-type guessing.
No clickjacking protection
Checks X-Frame-Options or CSP `frame-ancestors` against embedding in foreign pages.
CORS wildcard on the public document
Records `Access-Control-Allow-Origin: *` on HTML — not a flaw by itself, but a signal to check.
Server technology disclosed
Records version headers such as `X-Powered-By` that make targeted exploit search easier.
Cookies without Secure/HttpOnly
Checks issued cookies for the Secure and HttpOnly flags.
Supabase client configuration visible
Records project URL and anon key in the bundle — expected, but the starting point for access checks.
Supabase service_role key in the client
Decodes JWTs in the bundle and reports the `service_role` claim, which bypasses RLS entirely.
Secret key in shipped code
Searches for real secret patterns (Stripe, AWS, GitHub, PEM) in HTML and bundles.
Firebase backend detected
Records Firebase configuration in the client — allowed; what matters are the security rules.
.env file publicly retrievable
Requests known env paths and confirms hits by KEY=VALUE line signatures.
.git directory public
Confirms a shipped `.git` via the signatures of `config` and `HEAD`.
Database tables granted to the anon role
Queries the schema endpoint with the client key the app publishes itself and lists the tables granted to the anon role — without fetching a row or a row count. Granted is not the same as readable: whether rows come out depends on Row Level Security.
Database tables anonymously readable (proven)
Answers the actual question: do rows really come out with the publicly shipped key? Counting happens purely via the `Content-Range` header of a HEAD request — not a single row is fetched.
Google API key in the client
Records keys in `AIza…` format in shipped code — expected for Maps and Firebase, risky without a domain restriction.
Database credentials visible in the client
Finds connection strings with embedded username and password in HTML or bundles.
Firestore collections anonymously readable
Queries the collections the app names in its own bundle using the public web key — with `mask.fieldPaths=__name__`, so document names only, not a single field value.
Database functions callable by anonymous users
Lists the RPC functions granted to the anon role, read from the schema description — read, never called.
Signup open, accounts confirmed instantly
Reads the anonymously available auth configuration: self-signup open and email confirmation disabled.
AI API called directly from the browser (statically proven)
Finds AI provider endpoints in shipped code. `high` with a matching key pattern in the same bundle, `medium` without.
Browser writes directly to the database
Finds write calls (`insert`, `update`, `upsert`, `delete`, storage upload) in shipped code or in `use client` files.
Credentials compared in browser code
Finds comparisons such as `password === "…"` against a fixed literal in shipped code.
Storage buckets publicly listable
Checks whether file buckets hand out their object listing anonymously.
Firebase database anonymously readable
Queries the Realtime Database endpoint with `shallow=true`: if it returns key names instead of “permission denied”, the database is open.
Protected area guarded only in the browser
Detects admin/account routes the client router knows while the guard lives purely as a condition in the bundle — anyone with the URL gets in.
Role check inside the client bundle
Finds role comparisons such as `role === "admin"` in shipped JavaScript — trivially manipulated in the browser.
Access token in localStorage
Observes at runtime whether a JWT-like value ends up in web storage — readable via XSS.
AI API called directly from the browser
Observes calls to LLM providers from the client — the required key is exposed and any visitor can generate on your bill.
Backup or database dump public
Probes typical dump/backup paths and confirms hits via SQL or archive signatures.
No Permissions-Policy
Checks whether camera, microphone and geolocation are explicitly restricted by header.
No Referrer-Policy
Checks whether outgoing links leak internal URLs including query strings to third parties.
Unencrypted assets on an HTTPS page
Finds scripts, stylesheets or images embedded via `http://` on an https page.
Sign-in without a detectable limit
After ownership proof, sends a small series of failing sign-in attempts and checks whether anything throttles.
Admin area reachable anonymously
Requests discovered admin paths anonymously and reports content instead of a redirect to sign-in.
Tables without Row Level Security
Collects every `create table` and every `enable row level security` across all migrations and reports tables that are never protected.
RLS policy allows everyone
Finds policies with `using (true)` — RLS is on but has no effect.
RLS policy checks only that someone is signed in
Finds policies using `auth.role() = 'authenticated'` or `auth.uid() is not null` without binding to the row owner.
RLS policy allows arbitrary writes
Finds `with check (true)` — the read condition may be strict while writes stay wide open.
Edge function without JWT verification
Finds `verify_jwt = false` in `supabase/config.toml` — the server function is callable without signing in.
Firebase rules allow everyone
Finds `allow …: if true` in `.rules` files — Firestore or Storage is wide open.
service_role key in source code
Decodes JWTs in source files and reports the `service_role` claim.
Secret key in source code
Searches source files for real secret patterns (Stripe, AWS, OpenAI, Anthropic, Slack, SendGrid, PEM).
Database credentials in plaintext
Finds connection strings with embedded username and password.
.env file committed
Detects a `.env` with real values in the source tree (not `.env.example`).
Supabase in use, schema not versioned
Reports Supabase in the code without migrations containing tables and RLS — access control then cannot be verified from the code.
Secret exposed via a public env prefix
Finds variables with a public bundler prefix (`NEXT_PUBLIC_`, `VITE_`, `REACT_APP_`, `PUBLIC_`, `EXPO_PUBLIC_` …) whose name implies a secret — such values are compiled into the client bundle.
Env file inside the shipped bundle
Detects a `.env` with real values among the files reconstructed from source maps — the build shipped it.
Server Action without an access check
Inspects the body of every `"use server"` function that writes to the database and reports a missing identity check — server actions are public endpoints.
Dependencies with a known vulnerability
Matches the repository's package versions against the public OSV database and names the affected packages with advisory IDs.
Secret key in the Git history
Checks the added lines of recent commits against the same pattern list as the current tree — a deleted key stays readable.
Env file was in the repository and got removed
Reports environment files present in the checked history but missing from the current tree — the old values stay retrievable.
Signed-in strangers read other people's records
After domain verification, creates a throwaway account and counts how many rows that stranger sees in personal-data tables — without fetching a single row.
Table fully readable by any signed-in user
Reports tables hidden from anonymous callers but fully visible to a freshly created stranger account — intended for catalogue data, the same flaw otherwise.
Tenant isolation holds under test
Confirms that a freshly created account with no data of its own saw no rows in the tables checked.
Cross-tenant check could not run
Records that no account could be created (signup closed or confirmation required) — tenant isolation stays unverified.
Access check runs after the write
Compares the position of the auth call and the database write within the same function body and reports checks that happen after the write.
Auth result is never evaluated
Reports functions that look up the identity but neither check the result nor bind it to the write — the call has no effect.
API route without an access check
Finds route handlers with mutating methods that contain no authentication.
Input reaching the database unvalidated
Traces request bodies that flow into an insert or update without schema validation (mass assignment).
Webhook without signature verification
Detects payment webhooks that process the payload without verifying the signature — anyone could fake a payment.
Unsanitised HTML rendered
Finds `dangerouslySetInnerHTML` with a dynamic value — the classic XSS path.
Privacy (GDPR)
Google Fonts loaded at runtime
Finds references to fonts.googleapis.com/gstatic.com that send visitors' IP addresses to Google.
US tracking without visible consent
Detects analytics and pixel scripts with third-country transfer in the shipped markup.
Mandatory legal pages not visible
For German-language targets, checks whether imprint and privacy policy are linked.
Privacy signals unremarkable
Standardised classification when no third-country transfers are visible from outside.
Trackers fire before consent
Observes in the browser which third-party hosts are contacted on first load before anything was agreed to — the provable violation static checks can only suspect.
Data sent to third-country services
Lists foreign hosts contacted at runtime that typically process outside the EU.
Trackers without a consent layer
Reports consent-requiring scripts while no consent mechanism is detectable in the markup.
Tracking cookies set before consent
Observes in the browser which cookies are set before any consent has been given.
Third-party cookies set before consent
Reports cookies from foreign domains that were set before any consent was given.
Consent without an equal reject option
Checks the rendered state for a reject option on the same level as the accept button.
Scaling & cost
Queries without a limit
Finds database queries loading entire tables without `limit`/`range` — fine at 10 rows, fatal at 10,000.
Very large JavaScript bundle
Measures shipped script bytes against a budget — every megabyte costs load time and mobile drop-offs.
Images without optimisation
Counts images without dimensions or modern formats — drives traffic cost and layout shift.
No caching strategy
Checks whether responses carry cache directives — without them every request pays full price.
Responses uncompressed
Checks whether text responses are served with gzip/brotli.
Queries inside a loop (N+1)
Detects `await` inside a loop over records — one round trip per row.
Heavy first page load
Measures the bytes actually transferred on first load in the browser.
Scaling signals unremarkable
Standardised classification when bundle, caching and images stay within budget.
Accessibility
No language declaration
Checks the `lang` attribute on `<html>` — without it screen readers use the wrong language.
Images without alternative text
Counts `<img>` elements without an `alt` attribute in the shipped markup.
Form fields without labels
Finds inputs with no associated label or `aria-label` — unusable with a screen reader.
Accessibility violations in the rendered state
Runs axe-core against the fully rendered page and reports violations by severity — the check static analysis cannot perform.
Accessibility law likely applicable
Records signals of consumer sales or services — the German BFSG then applies to the digital offering.
Accessibility signals unremarkable
Standardised classification when language, alt texts and labels exist in the markup.
Architecture & SEO
Platform detected
Detects the no-code/AI platform in use and weights its typical gaps.
Framework stack detected
Detects the framework stack for framework-native builds (Cursor, Claude Code, classic).
Domain ownership proven
Confirms a completed proof (DNS TXT or file) and unlocks the active checks.
Throwaway account created for the check
Names the account the scan created via your self-service signup to prove tenant isolation — so it stays deletable.
Content rendered client-side only
Detects empty root containers with almost no text in the initial HTML.
Basic metadata missing
Checks page title and meta description in the document.
Unchanged template title
Detects generator default titles such as “Vite + React” — a sign of unreviewed tool defaults.
No canonical link
Checks whether the page declares a canonical URL (duplicate-content protection).
Non-existent paths answer with 200
Requests a random path: if the app serves content with status 200 there, neither browsers nor crawlers can detect errors.
No robots.txt and no sitemap
Checks whether the target serves crawl directives and a sitemap.
Very large document — partial assessment
Records that the HTML document exceeded the inspection limit and was only assessed up to that point. Not a defect, but a statement about coverage.
Code quality
Source maps public
Detects shipped source maps that allow reconstructing the original source code.
Debug leftovers in the shipped build
Finds `console.log`, `debugger` or TODO markers in the production bundle.
Code internals only partly visible
Transparency finding: without source maps or repo access, code quality cannot be judged seriously.
Not all bundles analysed
Records that bundle analysis stopped at the budget limit — findings stay valid, the absence of findings says nothing.
Source code reconstructed from source maps
Transparency finding: states how many original files were read from public source maps.
Deep-scan coverage
Transparency finding: states how many repository files were analysed read-only.
No files readable
Reports a deep scan with no source files read — the result carries no meaning.
Repository read only partially
Reports that file selection hit the budget limit — absence-based rules stay silent and a GO verdict is ruled out.