fableaudit
Deep product security and authorization audit. Scope the target, gather proof-over-vibe evidence across lanes, report numbered findings — then wait for the human before any fix.
Install
curl -fsSL https://academica.sh/skills/fableaudit/install.sh | bash
Installs into .cursor/skills, .claude/skills and .codex/skills · sha256-verified · pin: ?v=2026-07-28
You own scoping, threat modeling, evidence, and the findings report. Subagents may fan out read-only lane work. You never fix while auditing unless the user explicitly asks to remediate after the report.
This skill is the audit twin of fableplan: same depth, opposite default (read → judge → report, not plan → edit → ship).
When to use
- “Audit files / documents for user visibility and share grants” - “Security audit of X” / find IDOR, injection, or authz holes - “Who can read this resource?” / “is sharing bounded?” - Any surface where private data must stay owner-scoped unless a grant says otherwise
Do not use this for ordinary PR review or for implementing fixes (hand fixes to fableplan after the user picks findings).
Phase 1 — Scope (you, alone)
1. Name the audit target in plain product language (host + user path), not just a folder. Example: “Workspace Files on the product host — list, open, share, and download.” 2. Inventory the asset classes that hold user data: - documents / files / folders / blobs / assets - reports / runs / articles - shares / invite tokens / grant rows - research session (SSE) state 3. Draw the trust boundary (who is Alice, Bob, anon, admin, other host). Assume two logged-in users on the same product unless the user narrows it. 4. Pick lanes from the lane set below. Drop lanes that cannot apply; do not invent empty lanes. 5. Present a short scope card and get go-ahead unless already approved:
AUDIT TARGET: …
ASSET CLASSES: …
ACTORS: owner | grantee | stranger | anon | admin
LANES: A Authz · B Injection · C Host/policy · …
OUTPUT: numbered findings (Where / What’s wrong / What right looks like)
NO FIXES until you say so.
No product edits in Phase 1.
Phase 2 — Evidence (read-only; parallel OK)
Work the chosen lanes. Prefer proof over vibe:
| Proof | How |
|---|---|
| Code path | Handler → store query → owner / grant check → response |
| Negative path | Same id, different user → expect 403/404, not 200 |
| SQL / store | Owner (or grant join) on every read/write/list |
| Host policy | Host allow-lists vs actual routes |
| UI only | Flag as UI-only; backend must independently enforce |
Rules:
- Absolute paths in private notes. User-facing findings stay plain English. - Trace every verb for each asset: list, get, create, update, delete, download, share, resolve-share, export. - Special focus for files / documents / assets: ownership and share-grant paths. Share must be an explicit second gate, never “id known ⇒ readable.” - Launch independent lanes as parallel read-only workers when useful; you synthesize. No edits, no deploys from lane workers. - If a path is unknown / unproven, say so — do not invent a clean bill.
Lane checklist
A — Authorization / visibility — For each asset × verb: is the session required? Does the store constrain by owner? If shared, is there an explicit grant check on the same request path? Do list endpoints leak other users’ rows? Can a grantee escalate?
B — Injection — SQL string-concat; shell/CLI with unsanitized paths; path traversal; XSS via unescaped HTML/markdown/SVG; SSRF on user URLs; prompt/tool text reaching shell or SQL tools without allow-lists.
C — Host & product policy — Does a route exist on a host that should not see it? Cookie domain / Secure / HttpOnly / SameSite vs shared parents. Cross-product redirects must not carry privileged tokens in the clear.
D — Session & auth — Dev access keys in production; session fixation; missing auth on session-stream endpoints; CSRF on cookie-auth state changes; admin routes requiring a real grant, not just “logged in.”
E — Data exposure — Error bodies echoing SQL / internal ids; logs printing tokens or file bodies; signed URLs guessable without owner; exports including unauthorized resources.
F — Share / grant machinery — Over-broad roles; token entropy / expiry / revoke; resolvers that load by id then check access — confirm the check cannot be skipped on any path.
G — Frontend trust — Client filtering is not authz. Hidden UI ≠ server deny. Any client call with a raw id from the URL is an IDOR candidate on the matching API.
Phase 3 — Synthesize (you, alone)
1. Deduplicate. One root cause → one finding (many symptoms under it). 2. Severity: P0 exploitable cross-user or anon data access; P1 privilege / grant bypass or reliable injection; P2 defense-in-depth / info leak; P3 hygiene / unproven-but-suspicious. 3. Stable numbering for the conversation: first report uses 1…N; later messages keep those numbers. 4. Every finding uses three headings: Where / What’s wrong / What right looks like — plain English, user-standing “Where”. 5. Add a compact severity table after the numbered findings (file:line allowed only in that table). 6. End with Remediation gate: list finding numbers that need a fix plan; do not start fixes.
Phase 4 — Close
- Write an optional durable audit note only if the user asked, or the audit is large (≥5 findings). Plans/docs only — no product deploy from the audit itself. - If the user says fix / remediate: hand off to fableplan (or execute) with briefs derived from findings — one brief per commit-sized fix; authz fixes prefer server-side fail-closed. - Never mark an audit “clean” because the UI hides a row. Clean means every mutating and reading API was checked or explicitly scoped out.
Judgment boundary
| Lane workers get | You keep |
|---|---|
| File discovery, route lists, “does this query filter owner?” | Threat model, severity, user report |
| Parallel lane digests | Deciding false positives |
| Diff-shaped evidence snippets | Whether to open a fix plan |
| Never during audit | After explicit fix approval |
|---|---|
| Product code edits | fableplan / normal ship |
| “Quick harden” drive-bys | Scoped remediation only |
| Deploy / secrets | Still never without user intent |
Anti-patterns
❌ BAD — Audit = skim handlers; skip list endpoints and share resolvers
❌ BAD — “UI only shows my files” ⇒ call authz done
❌ BAD — Fix P0s in the same turn as the first findings report
❌ BAD — Renumber findings between messages
❌ BAD — File paths in the Where / What’s wrong prose
❌ BAD — Claim clean on an untraced verb (“didn’t check delete”)
✅ GOOD — Every asset × verb has owner or grant proof, or a finding
✅ GOOD — Share path is a separate gate with evidence
✅ GOOD — Unproven called out as unproven
✅ GOOD — Fixes only after the user picks findings
Proof patterns (quick)
Owner-bound get: auth → user id → get(resource, owner=user) or get then deny if owner ≠ user → 404/403 on miss.
Share-bound get: auth or share token → resolve share → resource + role → load → enforce role for verb → never return body if expired/revoked.
IDOR probe: As A create R; as B GET/PATCH/DELETE R → expect deny; list for B must not include R; with A’s share token, only granted verbs work.
Released under the MIT license — copy, adapt, ship.
Published by Academica — academica.sh/skills/fableaudit