$npx -y skills add UnpaidAttention/fable5-methodology --skill security-reviewRun a structured vulnerability pass over code — injection, auth/authz gaps, secrets exposure, unsafe deserialization, unvalidated boundary input, SSRF, and stack-specific classes — reporting findings by severity with concrete fixes. Trigger this after writing or modifying code th
| 1 | # Security Review |
| 2 | |
| 3 | Attackers send the inputs you didn't imagine. This pass walks the code from the attacker's |
| 4 | side of every trust boundary. Work the checklist concretely — for each item either point to |
| 5 | the line that handles it or record its absence as a finding. |
| 6 | |
| 7 | ## Step 1: Map the trust boundaries and data flow |
| 8 | |
| 9 | List every point where external data enters: HTTP params/body/headers/cookies, CLI args, |
| 10 | file contents, env vars, message-queue payloads, third-party API responses, uploaded files. |
| 11 | For each, trace where the data GOES — into a query? a shell command? a file path? an HTML |
| 12 | response? a deserializer? The dangerous combination is always untrusted-source → |
| 13 | sensitive-sink. Find the sinks. |
| 14 | |
| 15 | ## Step 2: Run the vulnerability checklist |
| 16 | |
| 17 | For each, find the handling line or log a finding: |
| 18 | |
| 19 | 1. **Injection — the sink test.** Every sink gets checked: |
| 20 | - SQL/NoSQL: parameterized queries / bound params ONLY. Any string-built query with |
| 21 | external data = SEC-CRITICAL. ORMs help but raw-query escape hatches don't. |
| 22 | - OS command: no shelling out with interpolated input; use `execFile`/`subprocess` with an |
| 23 | arg array and `shell=False`; never `shell=True` with user data. |
| 24 | - Path traversal: user-influenced file paths are resolved and confirmed within an allowed |
| 25 | base dir; `../` is neutralized. Never `open(base + user_input)`. |
| 26 | - Template/HTML: output encoding on by default; find every raw/`dangerouslySetInnerHTML`/ |
| 27 | `|safe`/`v-html` sink and confirm the input is trusted or sanitized (XSS). |
| 28 | 2. **AuthN/AuthZ.** Every non-public endpoint checks authentication AND authorization. The |
| 29 | frequent hole is authz: authenticated user A can act on user B's resource (IDOR) — confirm |
| 30 | ownership/role is checked against the RESOURCE, not just that someone is logged in. Never |
| 31 | trust a client-supplied role/ID for access decisions. |
| 32 | 3. **Secrets exposure.** No hardcoded keys/tokens/passwords/connection strings. No secrets in |
| 33 | logs, error messages, or client responses. Stack traces and internal paths never reach the |
| 34 | client (generic message out, detail to server logs). |
| 35 | 4. **Unsafe deserialization.** No `pickle`/`yaml.load` (unsafe loader)/Java native |
| 36 | deserialization/`eval`/`Function()` on untrusted data. Use safe parsers (JSON, `yaml.safe_load`). |
| 37 | 5. **Input validation at the boundary.** Type, length, range, format validated where data |
| 38 | enters, allowlist over denylist. Unbounded input (huge body, deep JSON, giant upload) is |
| 39 | bounded — absence is a DoS vector. |
| 40 | 6. **SSRF and outbound requests.** URLs built from user input for server-side fetches are |
| 41 | validated against an allowlist; internal/metadata addresses (169.254.169.254, localhost, |
| 42 | RFC1918) are blocked. |
| 43 | 7. **Crypto and identifiers.** No MD5/SHA1 for passwords (use argon2/bcrypt/scrypt); tokens |
| 44 | and IDs from a crypto-secure RNG (`secrets`, `crypto.randomBytes`), never `Math.random`/ |
| 45 | `random.random`; TLS verification never disabled. |
| 46 | 8. **Transport and session.** State-changing endpoints protected against CSRF where |
| 47 | cookie-based; auth cookies `HttpOnly`+`Secure`+`SameSite`; tokens not in `localStorage` |
| 48 | if XSS is reachable. |
| 49 | |
| 50 | ## Step 3: Adapt to the stack |
| 51 | |
| 52 | Layer the ecosystem's specific classes on top: |
| 53 | - **Node/TS:** prototype pollution (`Object.assign`/merge from user JSON), `child_process` |
| 54 | misuse, regex catastrophic backtracking (ReDoS), missing `helmet`. |
| 55 | - **Python:** `yaml.load`, `subprocess(shell=True)`, `pickle`, Jinja2 SSTI, Flask `debug=True` |
| 56 | in prod, `assert` used for security checks (stripped under `-O`). |
| 57 | - **SQL/Postgres:** dynamic SQL in functions, over-broad role grants, RLS assumed but not |
| 58 | enabled. |
| 59 | - Run the stack's audit tool as corroboration, not substitute: `npm audit`, `pip-audit`, |
| 60 | `cargo audit`, and a SAST pass (`semgrep`) if available — read findings, don't just paste them. |
| 61 | |
| 62 | ## Step 4: Report by severity, with fixes |
| 63 | |
| 64 | Rank findings; each gets file:line, the concrete exploit ("attacker sends `?id=../../etc/passwd` |
| 65 | → reads arbitrary files"), and the specific fix — not "sanitize input" but "use |
| 66 | `Path(base).joinpath(name).resolve()` and assert it's relative to `base`". |
| 67 | |
| 68 | - **CRITICAL:** directly exploitable for data breach / RCE / auth bypass. Block delivery. |
| 69 | - **HIGH:** exploitable under real |