skip to content
fizzgig
sign in
// suite
architectureknow what you builtoperationsknow it’s runningauditknow it’s safe to shipintegrationspricingdocschangelog
// community
sourdough startersproject kicks-off in 1 prompt
sign inrequest access →
// starters/security/security-posture

security posture

Security defaults that should travel with any vibe-coded project. Secret handling, RLS philosophy, auth check placement, webhook signatures, validation parity — the seven things that account for most of the failures we see.

● last fed 28 may 20261170 words · 6 min read
// when to use this starter

Fork this into a new project's CLAUDE.md or into a dedicated SECURITY.md sourdough starter. Use it whenever the AI is about to scaffold something that touches auth, secrets, database access, or external integrations.

securityrlsauthsecretswebhookssupabaseposture

Security posture

A sourdough starter for security defaults. The seven things that account for most of what bites vibe-coded apps in their first six months. Fork into your project's CLAUDE.md or keep as a separate SECURITY.md your AI re-reads on every session.


1. Secrets

Rule: No secret in any file that touches git. Ever.

  • .env.local is gitignored — use it for development.
  • Production secrets live in a secrets manager: Supabase Vault if you're on Supabase, Doppler / Infisical / AWS Secrets Manager / Vercel env vars otherwise. Not committed files.
  • Never paste a production secret into a chat with an AI. Treat the AI as you'd treat a public log: if it went in, assume it's been recorded somewhere.
  • NEXT_PUBLIC_ prefix exposes the variable to the browser. Don't put a service-role key, an admin token, or any server-only credential behind NEXT_PUBLIC_. The pattern misleads the AI; the AI follows the pattern; the key is now on the client.
  • Rotate when in doubt. If a secret might have leaked (chat, screenshot, log dump), rotate immediately. Faster than auditing whether it actually leaked.

Tool: secret_leak_finder scans 29 distinct credential patterns. Run before every deploy.


2. Row-Level Security (Supabase)

Rule: RLS enabled on every table. Every policy scoped to auth.uid().

  • Default-deny: turn RLS on, then write explicit allow policies.
  • USING (true) is not a policy. It's the absence of a policy with extra steps. Catches everyone, lets everyone through.
  • auth.role() = 'authenticated' is not the same as auth.uid() = user_id. The first lets any logged-in user through; only the second scopes to the owner. The AI confuses these.
  • WITH CHECK on INSERT / UPDATE. USING only covers SELECT-shaped reads. Without WITH CHECK, a user can update a row to reassign it to another user — the row-reassignment bug.
  • SECURITY DEFINER functions bypass RLS. Use sparingly; when you use one, guard inside the function (check auth.uid() explicitly).
  • System tables (spatial_ref_sys, pg_*) can't have RLS toggled — that's normal. Don't fight it.

Tool: rls_checker scans for the 8 canonical RLS misconfigurations. Run before every migration.


3. Auth check placement

Rule: Every protected route has a detectable auth check. Centralised if possible.

  • Middleware is the cleanest place — one auth check covers a whole path prefix (/dashboard/*, /api/admin/*). Audit surface is one file.
  • Per-route checks are fine but get audited per-route, which means missed-on-new-route is a likely bug class.
  • Routes the AI tends to leave unprotected: /admin, /api/admin, /api/internal, anything under /dashboard that was added after the middleware was written.
  • Webhooks need signature verification. Stripe (stripe.webhooks.constructEvent), Svix, custom HMAC — pick one, use it on every webhook handler. Without verification, anyone can POST to your endpoint claiming to be Stripe.

Tool: auth_flow_trace flags protected-shaped routes without detectable auth gating, plus webhooks without signature verification.


4. Input validation parity

Rule: The server validates everything the client validates, plus everything the client doesn't.

  • Client validation is UX. Server validation is security. Never mistake one for the other.
  • <input required> is a client hint, not a check. The server still has to validate.
  • Schema library on both sides (Zod / Yup / Valibot etc.) — use the same library and ideally share the schema object so they can't drift.
  • The "client validates, server trusts" bug is the most common shape: form looks right, behaves right, then someone curls the endpoint with garbage and the database accepts it.

Tool: form_validation_audit flags client/server validation mismatches.


5. CORS

Rule: No wildcard origin in production. Ever.

  • Wildcard origin (Access-Control-Allow-Origin: *) lets any site's JavaScript hit your API.
  • Wildcard origin + credentials: true — the browser blocks the request, but the intent reveals an architectural flaw. Don't ship the combination even though it doesn't "work."
  • Reflective origin echoing (returning whatever the request's Origin header was) is wildcard with extra steps.
  • Allowlist specific domains. If you need many, build the allowlist explicitly; don't write a regex that drifts.

Tool: cors_audit covers the 6 CORS patterns.


6. Webhook signature verification

Rule: Every webhook handler verifies the signature before doing anything.

  • Stripe: stripe.webhooks.constructEvent(rawBody, sig, webhookSecret). Raw body, not parsed JSON — parsing changes the bytes and the signature fails.
  • Supabase Auth webhooks: HMAC SHA-256 of the body with the webhook secret.
  • Svix / Hookdeck / similar: provider-specific verification SDK.
  • Idempotency on every handler. Webhook providers can replay, can fire out of order, can deliver duplicates. Every handler needs to be safe to receive the same event twice.

The pattern: verify, then dispatch. If verification fails, return 401 silently — don't echo why.


7. Prompt injection (if you ship anything AI-shaped)

Rule: Treat fetched content as adversarial.

  • Anything your app fetches and forwards into a prompt (email body, scraped page, tool response, user-submitted document) can contain injection payloads — instructions to ignore the system prompt, role-breakout attempts, capability exploits.
  • Severity scales with where the content came from. A user-typed message is one thing; a scraped third-party page is much worse.
  • Don't put untrusted content into a system prompt position. If you need to summarise an email, put the email inside a <email_content> tag (or similar) and instruct the model that anything inside that tag is data, not instructions.
  • The model won't always follow that instruction. Defense-in-depth: don't give the model tools it shouldn't use on untrusted content.

Tool: prompt_injection_scan covers 6 injection categories with content-type-aware severity.


How to feed this starter

Add entries when:

  • A new failure mode shows up in your project. ("Our webhook handler trusted the body before verifying — fixed by reordering, lesson: verify-then-dispatch.")
  • A new tool / library introduces its own security pattern. ("Clerk webhooks use Svix signature verification, not the Stripe pattern.")
  • The AI suggests a workaround that would have shipped a hole. ("AI suggested disabling RLS to fix a 'permission denied' error — actually fix is the WITH CHECK policy. Recorded so it doesn't suggest it again.")

Remove entries when:

  • The underlying stack moves and the gotcha is no longer real.
  • A platform fixes the default (e.g. if Supabase ever defaults RLS to on, the "always enable RLS" rule becomes redundant).

Companion tools (Fizzgig)

The _audit tools that map to each section above:

Section Tool
1. Secrets fizzgig__secret_leak_finder (29 credential patterns) + fizzgig__env_auditor (.env file structure)
2. RLS fizzgig__rls_checker (8 misconfiguration patterns)
3. Auth + webhooks fizzgig__auth_flow_trace
4. Validation fizzgig__form_validation_audit
5. CORS fizzgig__cors_audit
7. Prompt injection fizzgig__prompt_injection_scan

All of these collapse into a single fizzgig__audit MCP tool at the June 2026 Audit Suite launch — one MCP tool, all checks, one verdict.


This is a sourdough starter — fed as the project evolves and as the security landscape moves. Last meaningful update: 2026-05-28.

// fork it

Copy the raw markdown and drop it into your project. Edit freely - the value comes from feeding it as you work.

MIT-licensed · attribution welcome but not required
// new to starters?

The canonical article on what a sourdough starter is, why CLAUDE.md isn't one, and how to feed yours.

read the article →
// contribute

Found a shape that works? Markdown PRs welcome on github.

github →
fizzgig

the fluffy guardian of vibe-coded products. growls at insecure code so you don't have to.

all systems operational

suite

architectureoperationsauditintegrationspricingdocschangelog

community

sourdough startersdiscord (soon)github (soon)x / twitter (soon)rss (soon)

company

aboutcontacttermsprivacy
© 2026 fizzgig.
v1.5.0 · 2026-07-26
AI usage: fizzgig uses AI internally (Anthropic Claude, Google Gemini, OpenAI embeddings) to power audit checks, summarise decisions, and rank findings. Content surfaced from these tools is labelled as such in the dashboard. No user data is used to train third-party AI models — see our privacy policy for the full sub-processor list.