Every check the engine runs
33 issue types, grouped by what the probe reads or asks. 32 carry a written fix in the paid report.
| SEVERITY | CHECK | WHAT IT MEANS | READ FROM | FIX |
|---|---|---|---|---|
| CRITICAL | supabase-service-role-key | Supabase service_role key shipped to the browser | THE SHIPPED CODE | WRITTEN |
| CRITICAL | supabase-secret-key | Supabase secret key shipped to the browser | THE SHIPPED CODE | WRITTEN |
| CRITICAL | stripe-secret-key | Stripe secret key shipped to the browser | THE SHIPPED CODE | WRITTEN |
| CRITICAL | aws-access-key | AWS access key exposed | THE SHIPPED CODE | WRITTEN |
| CRITICAL | private-key-block | Private key block shipped to the browser | THE SHIPPED CODE | WRITTEN |
| CRITICAL | supabase-jwt-signing-secret | Supabase JWT signing secret shipped to the browser | THE SHIPPED CODE | NONE YET |
| CRITICAL | stripe-webhook-accepts-unsigned | Stripe webhook accepts requests with no signature | STRIPE ROUTES | WRITTEN |
| CRITICAL | stripe-webhook-accepts-bad-signature | Stripe webhook does not verify its signature | STRIPE ROUTES | WRITTEN |
| CRITICAL | rls-cross-tenant | Row-level security is broken — users can read each other’s data | 2 SIGNED-IN USERS | WRITTEN |
| HIGH | openai-key | OpenAI API key exposed | THE SHIPPED CODE | WRITTEN |
| HIGH | anthropic-key | Anthropic API key exposed | THE SHIPPED CODE | WRITTEN |
| HIGH | sendgrid-key | SendGrid API key exposed | THE SHIPPED CODE | WRITTEN |
| HIGH | github-token | GitHub token exposed | THE SHIPPED CODE | WRITTEN |
| HIGH | generic-secret-env | Server-side secret exposed via a public env var | THE SHIPPED CODE | WRITTEN |
| HIGH | client-side-admin-flag | Admin access decided in the browser | THE SHIPPED CODE | WRITTEN |
| HIGH | stripe-client-side-amount | Charge amount looks like it is built in the browser | THE SHIPPED CODE | WRITTEN |
| HIGH | stripe-test-key-in-production | A Stripe TEST key is live on your production site | THE SHIPPED CODE | WRITTEN |
| HIGH | open-rest-tables | Tables readable without logging in (critical when a table holds user or account data) | SUPABASE REST | WRITTEN |
| HIGH | stripe-success-trusts-query-string | Success page shows a confirmation with nothing verified | STRIPE ROUTES | WRITTEN |
| MEDIUM | google-api-key | Google API key exposed | THE SHIPPED CODE | WRITTEN |
| MEDIUM | jwt-in-localstorage | Auth token stored where XSS can steal it | THE SHIPPED CODE | WRITTEN |
| MEDIUM | sequential-id-fetch | Records fetched by guessable sequential id | THE SHIPPED CODE | WRITTEN |
| MEDIUM | stripe-deprecated-redirect-to-checkout | Using a retired Stripe.js checkout method | THE SHIPPED CODE | WRITTEN |
| MEDIUM | stripe-js-outdated-version | Loading a retired version of Stripe.js | THE SHIPPED CODE | WRITTEN |
| MEDIUM | missing-hsts | No HTTPS enforcement (HSTS) | RESPONSE HEADERS | WRITTEN |
| MEDIUM | missing-frame-options | App can be embedded in a hostile iframe (clickjacking) | RESPONSE HEADERS | WRITTEN |
| MEDIUM | missing-csp | No Content-Security-Policy | RESPONSE HEADERS | WRITTEN |
| MEDIUM | auto-confirm-signups | Anyone can create an account with any email (no confirmation) | 2 SIGNED-IN USERS | WRITTEN |
| LOW | exposed-admin-route | Unguarded admin route referenced in the bundle | THE SHIPPED CODE | WRITTEN |
| LOW | debug-mode-on | Debug / verbose mode shipped to production | THE SHIPPED CODE | WRITTEN |
| LOW | missing-content-type-options | MIME-type sniffing not disabled | RESPONSE HEADERS | WRITTEN |
| LOW | missing-referrer-policy | No Referrer-Policy | RESPONSE HEADERS | WRITTEN |
| LOW | rest-schema-enumerable | Database schema is publicly enumerable | SUPABASE REST | WRITTEN |
| CLEAN | jwt-signing-secret-absent | JWT signing secret is not in the bundle | THE SHIPPED CODE | |
| CLEAN | stripe-webhook-rejects-unsigned | Webhook rejects an unsigned request | STRIPE ROUTES | |
| CLEAN | stripe-webhook-rejects-bad-signature | Webhook rejects a forged signature | STRIPE ROUTES | |
| CLEAN | stripe-success-page-gated | Success page does not confirm on its own | STRIPE ROUTES | |
| CLEAN | rls-correct | Tenant isolation held on tested tables | 2 SIGNED-IN USERS |
How the score is worked out
- 01Start at 100.
- 02Each critical takes 40, each high 22, each medium 10 and each low 4.
- 03Each further finding in the same severity counts 70% of the one before it, so ten low findings do not outweigh one critical.
- 04The score stops at 0.
- 0590 or more is an A, 78 a B, 62 a C, 45 a D, anything lower an F.
- 06Any critical finding makes the grade F. Any high finding caps it at C.
scoreFindings() IN src/lib/scan/score.ts, THE FUNCTION THAT SCORES EVERY PAID REPORT
The sample, demo-app.lovable.app
critical: 40.0 = 40.0
high: 22.0 + 15.4 = 37.4
medium: 10.0 + 7.0 + 4.9 = 21.9
low: 4.0 + 2.8 = 6.8
deducted: 106.1
100 minus 106.1, stopped at 0, is 0
one critical, so the grade is F