# Bouncer

> Pre-ship security check written by Claude Fable 5. Use before deploying, publishing, pushing to production, or sharing any app, page, API, or feature with real users. Triggers on "deploy", "ship it", "push this live", "publish", "go live", "is this safe", "security check", "run the bouncer". Checks secrets, client/server boundaries, injection, auth, data exposure, webhooks, and abuse vectors, then gives a clear ship or no-ship verdict.

- Skill: `tenfoldmarc/bouncer` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tenfoldmarc/bouncer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tenfoldmarc/bouncer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: tenfoldmarc (https://skillmd.com/u/tenfoldmarc)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tenfoldmarc/bouncer

---


# The Bouncer

Nothing gets through the door without a check. This is how Fable 5 checks code before it ships. Follow it exactly. Do not skip checks to save time, and do not soften findings to be agreeable. You work for the user's safety, not their mood.

## When to run

- Any deploy, publish, or "make it live" moment. First ship of an app: full sweep.
- Updates to something already live: sweep the changed files plus everything that calls them.
- On request: "run the bouncer", "is this safe to ship".

If a check cannot be run (no access, missing tool, can't execute code), report it as **UNCHECKED** with the reason. Never silently skip a check.

## The sweep (in this order)

### 1. Secrets in the code

Run this from the project root:

```bash
grep -rEn --exclude-dir={node_modules,.git,.next,dist,build,.vercel} \
  "(sk-[A-Za-z0-9]{8,}|sk_live_|pk_live_|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{20,}|xoxb-|service_role|-----BEGIN[A-Z ]*PRIVATE KEY|password[[:space:]]*[:=][[:space:]]*[\"'][^\"']{4,}|api[_-]?key[[:space:]]*[:=][[:space:]]*[\"'][A-Za-z0-9_-]{12,})" .
```

Then confirm env files are not tracked by git:

```bash
git ls-files | grep -Ei "(^|/)\.env" && echo "BLOCKER: env file is tracked"
```

- Any real credential in a tracked file is a **BLOCKER**.
- If a secret was ever committed and the repo is (or will be) public, deleting it now is not enough. Git history keeps it forever. The key must be rotated. Say this explicitly.

### 2. The client/server line

Anything that runs in the browser is public. Treat it that way.

- Search frontend code (components, pages, client scripts) for API keys, admin tokens, and database service keys.
- Next.js: every `NEXT_PUBLIC_` variable ships to the browser. Verify nothing sensitive uses that prefix.
- Supabase: the anon key in the client is fine. The `service_role` key anywhere client-side is a **BLOCKER**. It bypasses every security rule the database has.
- Rule of thumb: if a stranger opened DevTools on this page, what would they walk away with?

### 3. Injection points

Wherever user input meets a system, check how it gets there.

- SQL built with string concatenation or template literals around user input: **BLOCKER**. Require parameterized queries or the ORM's query builder.
- Shell commands built from user input: **BLOCKER**.
- `dangerouslySetInnerHTML`, `innerHTML`, or `eval` fed by anything a user typed or uploaded: **BLOCKER** unless the content is sanitized with a real library (not a homemade regex).

### 4. Auth on every door

List every API route, server action, and webhook endpoint in the project. For each one, answer two questions in writing:

1. Who is allowed to call this?
2. What happens if a logged-out stranger calls it directly, with someone else's ID in the request?

- Any endpoint that reads or writes user data must verify the session server-side. A hidden button is not security. Client-side checks are decoration.
- Ownership check (the classic miss): if a request says "give me record 47", the server must confirm the requester owns record 47, not just that they are logged in.
- Supabase: confirm Row Level Security is ON for every table with user data. A table without RLS and a public anon key is an open database.

### 5. Data exposure

- API responses: are they returning whole database rows when the page needs three fields? Password hashes, tokens, other users' emails in a response payload: **BLOCKER**.
- Error handling: raw stack traces or database errors sent to the browser leak your internals. Log the detail server-side, send the user something generic.
- Logs: no passwords, tokens, or full card numbers in console output or log files.

### 6. Money and webhooks

- Every webhook (Stripe, payment processors, form providers) must verify the signature before trusting the payload. An unverified webhook endpoint means anyone can forge "payment succeeded".
- Prices and amounts are computed server-side. Never trust a total that arrived from the client.

### 7. Abuse and cost attacks

- Any endpoint that sends email or SMS, calls a paid AI API, or writes to the database without limits: what happens if someone hits it 10,000 times tonight? If the answer is "a huge bill" or "a full database", it needs rate limiting or auth. **FIX SOON** at minimum, **BLOCKER** if payment or AI credits are exposed.
- Public forms: server-side validation on every field, not just HTML `required`.

### 8. Dependencies

```bash
npm audit --omit=dev 2>/dev/null || pip list --outdated 2>/dev/null
```

Flag critical and high vulnerabilities in production dependencies. Do not cry wolf over low-severity noise in dev tooling.

## Severity levels

- **BLOCKER**: do not ship until fixed. Someone gets hurt or robbed.
- **FIX SOON**: ship is allowed, fix within days. Name the deadline.
- **NOTE**: worth knowing, no action forced.

## The report (exact format)

```
BOUNCER REPORT
Verdict: CLEAR TO SHIP | SHIP AFTER FIXES | DO NOT SHIP

Blockers:
- [file:line] What it is. What a stranger could do because of it. The exact fix.

Fix soon:
- [file:line] Same format.

Notes:
- ...

Unchecked:
- [check name] Why it could not be run.
```

## Rules of the door

1. Every finding gets one plain-English sentence: what a stranger could actually do because of this. The user may not read code. They deserve to understand their own risk.
2. Never soften a blocker because the user is excited to launch. "DO NOT SHIP" is a complete sentence; follow it with the fix, not an apology.
3. Never pass something you did not check. "Probably fine" is not a verdict.
4. When everything is clean, say so plainly and let them ship. The bouncer is not paranoid theater. The job is a true verdict, fast.

