Pre-ship audit skill for SaaS that takes money. Finds the billing and data-isolation holes generic scanners miss on Next.js + Supabase + Stripe.
SaferSkills independently audited saas-preflight (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A pre-ship security and payment audit for SaaS built fast with AI, on the Next.js + Supabase + Stripe stack. It finds the failure modes that AI-generated code ships by default: a stranger reading another user's data, a stranger getting a paid plan for free, a webhook that silently fails so subscriptions never activate, a middleware that fails open.
This skill is defensive only. It detects weaknesses in the user's own codebase so they can be fixed. It never writes exploit code, never produces an attack payload, and never targets a system the user does not own.
Every finding maps to one of these lenses. They are the spine of the audit. Run through all seven; do not stop at the first scary thing.
actually checking who the caller is and whether they own the thing they touch?
not granted, or two writes that should be one?
the work twice (double-grant, double-charge, double-email)?
down, does the app fail safe or fail open?
hits the database, the filesystem, an outbound fetch, or the DOM?
between environments in a way that breaks prod or leaks keys to the client?
storage, compute) or exhaust quotas through races?
Plus one conditional lens, in scope only when the app is multi-tenant or white-label:
app, can one tenant reach another tenant's data or session? Covers cross-tenant data scoping, session cookie scope across subdomains, tenant context spoofing, subdomain and domain creation validation, and domain deprovisioning on downgrade. Skipped automatically for single-tenant apps.
Follow this order. The scanner is an optional accelerator, not a gate: if it cannot run on this machine, do the triage by reading the code yourself and continue. Never report a grep hit as a confirmed vulnerability without reading the actual code first.
Find the project root and confirm the stack. Look for package.json (Next.js), a supabase/ directory or @supabase/* imports, and stripe usage. Note whether the app uses the App Router (app/) or Pages Router (pages/), and whether there are server actions, route handlers, or both. If the stack is not Next.js + Supabase + Stripe, say so plainly and adapt: the 7 lenses still apply, but the specific patterns in the reference files may not match. The scanner also detects multi-tenant signals (a tenant or org table, Host or subdomain routing, a customer-domains table); Lens 8 (tenant isolation) is only in scope when at least one is present, and is skipped otherwise.
The scanner gives candidate flags in seconds. It is a convenience, not a requirement. The full audit comes from reading the code against the 7 lenses, so if the script does not run on this machine, do not stop: go to step 3 and do the triage yourself by reading the code.
Run it like this:
bash scripts/scan.sh <path-to-repo>On Windows, prefer the native PowerShell port, which needs no bash:
powershell -File scripts/scan.ps1 <path-to-repo>Both are read-only and make no network calls. Git Bash (bundled with Git for Windows) or WSL also run the .sh version, with a Windows-style path, for example bash scripts/scan.sh C:/Users/me/my-app. If neither runs or the script errors, say so in one line and proceed without it. The audit is never blocked by a missing scanner.
When it does run, it prints candidate findings grouped by lens. Treat every line as a lead, not a verdict. Grep cannot prove a vulnerability and will both miss real issues and flag safe code. Its job is to point your reading. When the app is multi-tenant, it also prints a Lens 8 section: session cookie scope, tenant header trust, subdomain reserved-name deny-list, and domain lifecycle on downgrade. On a single-tenant app it prints one line and emits no Lens 8 findings.
If you skip the scanner, your step 3 reading must cover all 7 lenses from scratch rather than starting from flags. Use the reference files as your checklist so nothing is missed.
For each lens with candidate flags, and for each lens regardless if the app is about to handle real money or real users, read the matching reference and verify by reading the actual code:
references/auth-and-isolation.md: Lens 1. Server-side auth on routes andactions, middleware fail-open, Supabase RLS, object ownership (IDOR), mass assignment, CSRF on route handlers, open redirect after auth.
references/payments.md: Lenses 2, 3, partly 4. Stripe webhook signature andidempotency, subscription state as source of truth, checkout and guest-checkout races, downgrades and refunds.
references/abuse-validation-config.md: Lenses 5, 6, 7, partly 4. Inputvalidation, SSRF, rate limiting, quota races, unbounded cost, secrets and env, CORS, Supabase Storage, degraded-mode behavior.
references/tenant-isolation.md: Lens 8, multi-tenant only. Read this onlywhen the app is multi-tenant.
Read a reference only when you reach its lens. This keeps context lean.
A finding is real only if you can point to the exact file and line and explain the concrete consequence ("an authenticated user can read row X belonging to another user because the query filters by nothing"). If you cannot, downgrade it to a note or drop it.
Produce the report using assets/REPORT_TEMPLATE.md exactly. Prioritize by severity. For every finding give: the lens, the file and line, what an attacker or unlucky user can do, and a concrete fix. Write the fix as remediation, never as a working exploit.
data that is not theirs, or obtain paid access without paying, or cause money loss. Fix before shipping, full stop.
id, a race window, a misconfigured env). Real risk, slightly higher bar.
P1, or missing defense in depth (no rate limit, no idempotency key yet).
route, TODOs near auth.
For Lens 8 (tenant isolation): a cross-tenant data read or write, or a session shared across tenants, is a P0 (a stranger reaching data that is not theirs). A missing domain deprovisioning on downgrade or a missing reserved-name deny-list is typically P2.
If you are unsure between two levels, state the assumption that decides it rather than guessing silently.
solid and stop.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.