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

Raw ↓ SKILL.md /skills/fableaudit/SKILL.md

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:

ProofHow
Code pathHandler → store query → owner / grant check → response
Negative pathSame id, different user → expect 403/404, not 200
SQL / storeOwner (or grant join) on every read/write/list
Host policyHost allow-lists vs actual routes
UI onlyFlag 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 getYou keep
File discovery, route lists, “does this query filter owner?”Threat model, severity, user report
Parallel lane digestsDeciding false positives
Diff-shaped evidence snippetsWhether to open a fix plan
Never during auditAfter explicit fix approval
Product code editsfableplan / normal ship
“Quick harden” drive-bysScoped remediation only
Deploy / secretsStill 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