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 every verdict.
Scan your own app for freeHow the verdict is derived
Until August 2026 this section held arithmetic: weighted dimensions, penalty points per finding, thresholds. It has been removed without replacement. A number built from freely chosen weights looked like a measurement but was an opinion with a decimal place — and it could hide an evidenced finding behind a good average. Three readable rules on severity and confidence take its place. The values on this page come straight from the running code, not from documentation that can go stale.
Severity — how bad it would be
Four levels. They state what the finding causes if it holds. Pure notices (“Supabase detected”) count internally as info and enter neither the list nor the verdict.
- Criticalcritical
- Highhigh
- Mediummedium
- Lowlow
Confidence — how sure we are
Derived from the tier that produced the finding. A missing header is beyond doubt from the outside; a guess from a partially reconstructed source map is not. Without this second value, observation and suspicion would sit side by side as equals:
- confirmedconfirmed
- highhigh
- mediummedium
The three rules, per dimension
- Stop: at least one finding with severity “critical” AND confidence “confirmed”. Both together — “confirmed” only ever comes from a run with domain proof or repository access. This explicitly means: a purely anonymous external scan never says “stop”. It holds no finding it triggered itself or read in the source.
- Iterate: otherwise — at least one finding of “high” or above, or at least 3 findings of severity “medium” within the same dimension, or a single medium finding in security or privacy. A single medium finding is normal in an MVP; 3 in one area are a pattern. In the two areas that carry trust, however, one is enough: we cannot call a system “solid” next to an anonymously readable table or tracking without consent.
- Go: otherwise. Findings of severity “low” never flip the green light — eight missing HTTP headers are not a broken foundation. And two medium findings are exempt: “Content-Security-Policy missing” and “HSTS header missing” describe an absent extra layer of protection, not a defect. Not hardened is not the same as broken.
The overall verdict is the worst verdict across the six dimensions. Findings are not added up across areas: the threshold is meant to spot a pattern within ONE area. Findings a connected repository has dismissed no longer count.
Whatever could not be assessed is marked “limited” and shows “—”, never a zero and never a flattering top mark. Next to it stands the scan depth in numbers: how many checks applied in your case and how many of them actually ran. Checks that do not apply to you leave the denominator — checks we could not run stay in it.
Methodology version 2026.18. 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 (active / passive)
Finds resources embedded via `http://` on an https page and separates active ones (scripts, frames, stylesheets — blocked by the browser) from passive ones (images, audio, video — warning, no block).
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.
No SPF and no DMARC — email spoofing unprotected
Checks whether the domain publishes at least an SPF or a DMARC record so receivers can recognise forged mail.
No DMARC record
Checks whether an existing SPF record is paired with a DMARC record that says what should happen to forged mail.
SPF record with `+all`
Checks whether the SPF record ends in `+all` (or a bare `all`), which explicitly allows every server to send as the domain.
DMARC with `p=none`
Checks whether the DMARC record is still at `p=none`, telling receivers to deliver forged mail anyway.
No DKIM key found under common selectors
Queries a handful of common DKIM selectors and reports when none of them holds a key — a hint, not a finding of absence.
TLS certificate expired
Reads the host's certificate and checks whether its validity date has passed.
TLS certificate about to expire
Reads the host's certificate and reports when it expires in 14 days or fewer.
Self-signed TLS certificate
Checks whether the served certificate is vouched for by its own subject instead of a public certificate authority.
No CAA record
Checks whether the domain uses a CAA record to restrict which certificate authorities may issue certificates for it.
No security.txt — no contact for vulnerability reports
Checks whether `/.well-known/security.txt` serves an RFC 9116 file with a contact line.
security.txt without or with an expired Expires
Checks whether the existing security.txt carries the mandatory `Expires:` line and whether that date is still in the future.
package.json publicly accessible
Checks whether `/package.json` serves the project file with its name and dependency list.
API description (OpenAPI/Swagger) publicly accessible
Checks the usual paths (`/openapi.json`, `/swagger.json`, `/api-docs`, `/docs` …) for an OpenAPI/Swagger description or a Swagger UI / ReDoc page.
Third-party scripts without Subresource Integrity
Finds `<script src>` and `<link rel=stylesheet>` from public CDNs (cdnjs, jsDelivr, unpkg, jQuery, Bootstrap …) without an `integrity` attribute.
Database wide open (composite)
Combines what would be explainable in isolation: the access key in shipped code, missing or permissive Row Level Security and — for the critical tier — writes straight from the browser.
Multiple secret families in the same build (composite)
Fires when at least two independent secrets (service-role key, database connection, credentials, provider keys, git history) were found in the same run.
Phishing-ready: mail spoofing plus login (composite)
Combines a domain without SPF/DMARC with a login or contact form on the site — the ingredients of a credible phishing mail in the domain's name.
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; on the syntax tree it also reports client files that read the key from the environment (comments do not count).
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.
System prompt in the shipped code
Checks whether the instruction steering an AI assistant is shipped in the browser code.
Shipped library with a known vulnerability
Detects front-end libraries and their version in the served bundle and in recovered source maps (retire.js signatures) and matches the version against OSV or the bundled retire.js database. Reports only on a confidently detected version.
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.
A stranger's account can see your uploaded files
Uses a throwaway account to check whether non-public storage buckets can be listed.
File storage sealed off from stranger accounts
The positive counterpart: no non-public bucket gave a stranger account a file listing.
A stranger's account may write to your tables
Creates a throwaway account and checks per table whether it may write.
Write access for stranger accounts rejected
The positive counterpart: the checked tables rejected every write attempt.
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 targets with a German, Austrian or Swiss connection, 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.
Legal notice: sample values from the template
Checks whether the German legal notice still contains example values — sample address, placeholder email, unreplaced template fields.
Testimonial with an invented face
Finds portraits from random-face generators inside sections that show testimonials or references.
Generated media without a visible notice
For apps that generate images or audio, checks whether the visible text says anywhere that content comes from an AI.
Contact data in the shipped JavaScript
Finds e-mail addresses and phone numbers in clear text inside the shipped JavaScript bundles — not in the HTML, where the legal notice requires them.
List of e-mail addresses public in the markup
Counts distinct e-mail addresses in the shipped HTML of the start page and the discovered sub-pages and reports from ten upwards — that looks like a publicly rendered member or customer list.
CSP violations are reported to a third party
Reads `report-uri` and `report-to` from the Content Security Policy (header or meta tag) and reports when the reports go to a foreign host.
Reject has no effect — trackers still load
Clicks the detected “reject” control of the consent banner in the browser and observes whether tracking services are still contacted or tracking cookies still set afterwards — the differential scan that makes an existing banner actually mean something.
Reject works
Positive finding of the differential scan: after clicking “reject”, no tracking services were contacted and no tracking cookies were set.
Images without an origin note
For apps that generate images, checks whether the invisible “made by an AI” note is still attached to what they serve.
Service not named in the privacy policy
Cross-checks the services your app actually contacts in the browser against the text of your privacy policy.
AI interaction without a visible notice
Finds AI endpoints in the client without a visible notice that users are interacting with an AI.
Tracking cookie with a very long lifetime
Measures the lifetime of tracking cookies and reports anything beyond one year.
Consent with pre-ticked boxes
Finds already ticked checkboxes inside the consent interface.
Imprint: mandatory details not found
Fetches the imprint page and looks for a postal address, a contact option and the identifier of the jurisdiction (VAT ID/commercial register, Firmenbuch/UID, CHE-UID/commercial register).
Privacy policy: mandatory details not found
Fetches the privacy policy and looks for the controller, data subject rights, the right to complain and the retention period.
Form without a privacy notice
Finds forms with an email field that have no reference to the privacy policy nearby.
Consent theatre (composite)
Summarises the case where a banner with a reject option exists and consent-dependent services load anyway — before the decision or after clicking “reject”.
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.
Consent provider detected
Names the embedded consent management platform (Usercentrics, OneTrust, Cookiebot, Borlabs, …) — or that only Google's Consent Mode is present without a banner.
Services and routes of the app detected
Reads from the shipped JavaScript what the app talks to: own routes, backend services (Supabase, Firebase …), AI providers and other third-party services — as a list with method and location.
Test data could not be removed
Names records the write check created and could not delete again.
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.
Placeholder copy in the shipped state
Scans the visible text of the reached pages for dummy copy, sample details and unreplaced template fields.
Mixed forms of address on one page
Counts the informal and formal German forms of address in a page's visible text and reports when two appear side by side.
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.
Service worker detected
Detects a registered service worker (or one at `/sw.js` / `/service-worker.js`) and analyses its source like a bundle.
Progressive web app detected
Detects a web app manifest linked via `<link rel="manifest">` that carries a name or start URL.
Development address in shipped code
Finds calls to localhost, 127.0.0.1, private network addresses or tunnels (ngrok) in the shipped JavaScript — leftovers from development that go nowhere for users.
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.