10 checks vibecheck runs that other scanners miss.

2026-05-10 · vibecheck team · 11 min read · Field notes

Quick answer Mozilla Observatory and SecurityHeaders.com grade response headers. SSL Labs grades TLS. None of them inspect the JavaScript your browser actually runs. Vibecheck reads the bundle: it pulls Supabase JWTs out of inline scripts, decodes them, probes the tables, finds OAuth authorize URLs and inspects their parameters, fingerprints Service Worker registrations, locates LLM prompt templates left in client code, and checks whether importScripts calls cross trust boundaries. Below: ten specific checks with worked examples — every one runs free against any URL you give us.

Header-grading scanners answer one question well: are your response headers configured the way the OWASP cheat sheet says they should be? That's a useful question. It's also a small fraction of what can go wrong in a deployed web app — especially one generated by an AI builder where the header story is often fine but the bundle is full of secrets.

Here are ten checks vibecheck runs that the header-grading tools don't. Each is a real category we see fire in scans of indie launches, enterprise deploys, and AI-generated apps. Each links to a fix guide with the why and the patch.

1. Subresource Integrity (SRI) gaps with severity escalation for historical-compromise CDNs

Cross-origin <script src="https://cdn.example.com/lib.js"> tags without an integrity= attribute are an open trust delegation: the browser executes whatever bytes the CDN returns, and you have no protection if the CDN changes hands or gets compromised.

This isn't theoretical. The polyfill.io supply-chain attack in June 2024 hit ~100,000 sites. Sites with SRI hashes were unaffected — the hash mismatch caused browsers to refuse execution. Sites without SRI ran the malicious payload. The fix is one HTML attribute. The cost of skipping it was full XSS-equivalent code execution on every page load.

Vibecheck flags missing SRI on every cross-origin script and stylesheet, and escalates severity from medium to high when the host matches a known historical-compromise CDN: polyfill.io, code.jquery.com, cdnjs.cloudflare.com, cdn.jsdelivr.net, unpkg.com, ajax.googleapis.com, plus a few others. /fix/missing_sri_external_script.

2. OAuth client_secret embedded in the JavaScript bundle

OAuth's client_secret is the credential a backend uses to exchange auth codes for access tokens. It belongs server-side. When it ends up in a browser bundle, the entire OAuth trust model collapses — anyone reading the bundle can impersonate your application against the OAuth provider, mint refresh tokens, and (for providers that allow client-credentials grants) get full app-level access without any user interaction.

The pattern we see most: a developer prefixes both CLIENT_ID and CLIENT_SECRET with NEXT_PUBLIC_ / VITE_ / PUBLIC_ because that's what made the build pipeline pick them up. The provider expects only CLIENT_ID to be public.

Vibecheck scans the bundle for client_secret = "..." assignments with secret-shaped values, redacts the value before reporting, and surfaces it as critical. Header scanners don't see this because it's inside JavaScript, not in a response header. /fix/oauth_client_secret_in_client.

3. OAuth redirect_uri built dynamically from window.location

OAuth security depends on the provider's redirect-URI allowlist being exact-match. When your client builds the URI dynamically — ${window.location.origin}/auth/callback — every preview deploy URL, every staging hostname, every reverse-proxy variation becomes a valid redirect destination. Operators respond by relaxing the allowlist with wildcards, and the allowlist stops being the airtight check it was designed to be.

Vibecheck looks at every OAuth authorize URL across nine providers (Google, GitHub, Microsoft, Auth0, Okta, Facebook, LinkedIn, Slack, Discord) and inspects the redirect_uri parameter. We flag template-literal markers (${...}), window.location references, and URL-encoded interpolation (%24%7B) — patterns that indicate the URI is not a fixed string. /fix/oauth_redirect_uri_dynamic.

4. JWT static analysis: alg=none, missing exp, admin claims in client-readable tokens

JWTs in client bundles are common — Supabase anon keys are JWTs, lots of API responses include short-lived JWTs the client uses for subsequent calls. The thing nobody else checks: what's inside those tokens.

Vibecheck decodes every JWT it finds and runs five distinct rules:

None of these require a server probe. They're all visible from decoding the base64url payload — but only if the scanner knows to look.

5. Supabase RLS table probes (we actually try the queries)

This is the most distinctive check vibecheck runs. When we find a Supabase URL + anon key in a bundle, we don't just log "anon key in client" — we use the anon key the way an attacker would. We hit the Supabase REST API at common table paths (/rest/v1/users, /rest/v1/posts, /rest/v1/messages, etc.) and check whether the anon role can actually read rows.

If RLS is properly enabled, those reads return empty arrays or 401s. If RLS isn't enabled — the Moltbook breach shape — they return data. Vibecheck reports the table names, sample column lists, row counts, and any columns that look like PII (email, name, phone, address).

The output goes into the case file in plain English, with a generated SQL fix block ready to copy-paste. /fix/supabase_anon_only_no_rls for the full mechanic.

6. Firebase Firestore + RTDB rules probes

Same pattern, different backend. When vibecheck finds a Firebase project ID, it probes the Firestore REST API and the RTDB endpoints to see what the security rules actually allow. The most common finding: rules that say allow read: if true; — fine for a tutorial, catastrophic for production.

We surface the project ID, the kind of database (Firestore / RTDB / Storage), and the specific rules that resolved to "anyone can read." Header scanners can't see this because it's a Firebase-API behaviour, not a response-header property of your origin. /fix/firebase_anyone_can_read.

7. LLM prompt templates in client bundles

This one's specific to AI-built apps. When a vibe-coded product has a "talk to our AI" feature, the prompt template often gets baked into the client bundle: "You are a helpful assistant for ACME Corp. Here are the rules: never recommend competitors, always upsell the premium tier, …"

Two consequences:

  1. The prompt is your business logic. Competitors read it directly. Your prompt-engineering work is on display.
  2. Jailbreaks become trivial. An attacker who knows your exact prompt can craft a follow-up that sidesteps every rule you've written, because they have the full text to work backwards from.

Vibecheck looks for the regex shapes that indicate a prompt template: long string literals starting with "You are" / "Your role" / "System:" / "Instructions:", with role-shaped suffixes. We surface the rule, location, and a redacted excerpt. /fix/llm_prompt_template_in_client.

8. Service Worker cross-origin registration + scope width

Service Workers operate as a programmable proxy between page and network. A Service Worker registered from a third-party host (navigator.serviceWorker.register("https://other.example.net/sw.js")) can intercept every fetch your page makes, modify response bodies, and persist cached attacker-content for ~24 hours after you fix the registration.

Vibecheck flags three Service Worker patterns:

9. iframe Permissions Policy delegation to cross-origin frames

The allow= attribute on an iframe is the per-frame Permissions Policy. It explicitly grants the embedded origin permission to call powerful browser APIs — getUserMedia (camera/mic), Geolocation, PaymentRequest, navigator.usb, navigator.bluetooth — as if the parent had granted them.

The misleading bit: when the embedded frame triggers a permission prompt, the browser shows your domain as the requestor, not the third party's. Users approve based on the wrong identity.

Vibecheck flags every cross-origin iframe with dangerous allow= features. Severity tiers: camera/microphone/geolocation/payment/USB → high; midi/clipboard-read → medium; sensors → low. /fix/iframe_dangerous_allow.

10. Auth tokens and API keys in URL query strings

Credentials passed in URL query parameters end up in places nobody plans for: server access logs (every reverse proxy, every CDN edge, every backend logs the URL by default), browser history (synced to the user's other devices), Referer headers (sent to the next site the user visits), bookmarks, search-bar autocomplete, and front-end analytics tools that capture page URLs verbatim.

Vibecheck scans the bundle for URLs containing query parameters that look like authentication credentials. High-severity rules cover access_token, session_id, jwt, auth_token, id_token, refresh_token, bearer; medium-severity covers api_key, apikey, secret, client_secret. We skip placeholder values (YOUR_TOKEN, ${TOKEN}, xxxxxxx) and redact the captured token in the report. /fix/auth_token_in_url.

Bonus: 136 more

The list above is the headline set. The full catalogue runs to 173 rules across 22 categories. /checks has the complete tree, grouped by category and severity, every entry linking to a fix guide.

Why none of this is in the header-grading tools

Three reasons:

  1. Architecture. Mozilla Observatory and SecurityHeaders.com fetch the page once and inspect the response headers. They don't fetch the JavaScript bundles, parse them, decode JWTs, or probe Supabase tables. That's a different scanner shape.
  2. Scope. They were built for the era of server-rendered PHP and Rails apps. Most of vibecheck's checks live in concerns that didn't exist when those tools were designed: SPA bundles, OAuth in the browser, Service Workers, BaaS providers, AI prompts.
  3. Specificity. Generic security advice is cheap; specific actionable findings are expensive. Vibecheck only fires when it has high confidence — every rule has a fix-guide with concrete remediation, not "review your security posture."

The right answer is to run all of them. Mozilla Observatory and SecurityHeaders.com have a place; they catch what they catch well. Vibecheck catches the rest.

Inspect a deploy   Browse the full check catalogue