security-review
Focused security review of a diff, a file, or a small module. Not a
comprehensive audit — that's an engagement. This finds the classes of bug
that regularly ship because nobody looked, and it's specific about what it
did and did not check.
When to use this
- "Review this for security"
- "Check for vulns"
- "Did I leak a secret in this diff"
- Before pushing / opening a PR that touches: auth, session, crypto, user
input, SQL, shell exec, file paths from users, deserialization,
eval,
regex on untrusted input, or third-party API calls with secrets
- After bumping a dependency to a version with a known CVE (
npm audit,
pip-audit, cargo audit)
Not for: comprehensive audits of an entire codebase (too big for a skill;
scope carefully), threat modeling, cryptographic protocol design, or
regulated-compliance certification. Say so if asked.
Procedure
1. Establish scope
Ask for or infer the exact scope. Options in priority order:
- A named file or files.
- A branch diff:
git diff <base>...HEAD.
- Staged changes:
git diff --cached.
- A directory or module.
Do NOT try to review a whole repo in one pass. If the scope is too large
(more than ~500 changed lines or ~10 files touching sensitive areas), say
so and offer to review the most sensitive slice first.
2. Run the fast, cheap checks first
Before deep reading, do the mechanical scans. These are cheap and catch
the highest-severity, dumbest bugs.
Secrets in diff:
git diff <range> | grep -iE '(api[_-]?key|secret|token|password|bearer|BEGIN [A-Z ]+PRIVATE KEY|xox[baprs]-|ghp_|sk-|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35})'
- Also check for
.env-shaped strings: [A-Z_]+=[^ ]+ in non-config files
- If anything hits, halt and report the exact lines. Do not continue
until the user confirms whether it's real.
Dependency CVEs:
- Detect package manager from lockfile
- Suggest running:
npm audit, pip-audit, cargo audit, bundle audit,
or equivalent — don't run automatically; the user does it
- If the diff bumps a dependency, check the version bump for a corresponding
advisory
3. Read for the OWASP-shaped classic bugs
Look at each changed file for these patterns. Note anything that matches;
don't guess when unsure.
Injection:
- SQL — string concatenation of user input into SQL, template literals
with query fragments, missing parameterization.
execute("SELECT * FROM u WHERE id = " + id) is a hit. Check for prepared statements / parameter
binding / ORM usage.
- Command / shell —
subprocess.run(..., shell=True) or backticks or
os.system with user input. shell=False + list args is safe.
- NoSQL —
find({field: userInput}) without validation, especially
if the field allows $where or operators.
- XSS —
innerHTML = userInput, unescaped template output, missing
htmlspecialchars / escape. React auto-escapes; dangerouslySetInnerHTML
reverses that.
- XXE / SSRF — user-controlled URLs passed to
fetch / requests.get,
XML parsers without external-entity restriction.
Auth / access control:
- Missing authorization checks on state-changing endpoints
- IDOR:
GET /users/:id returning any user's data without checking whether
the caller can view that ID
- Session fixation, missing CSRF tokens on state-changing routes
- Weak password hashing (MD5, SHA1, plain SHA256 without salt)
- JWT with
alg: none, JWT verification skipped, JWT secret hardcoded
Crypto / data:
- Home-rolled crypto instead of stdlib primitives
- ECB mode block cipher, static IV, missing MAC
- Insecure randomness for security purposes (
Math.random(), random.random())
- Timing-attack-vulnerable comparisons on secrets (
== on tokens; should
be constant-time compare)
Deserialization:
pickle.load on untrusted input, yaml.load (not safe_load),
unserialize, Java readObject on external data — all remote-code-exec
waiting to happen
File & path:
- Path joining with user input without normalization → path traversal
(
../../etc/passwd)
- File upload endpoints that don't check extension AND content type AND
filename length
- Symlink attacks if the code follows symlinks in user-controlled dirs
Race conditions on security-relevant state:
- TOCTOU: check-then-use with a gap (e.g. check file permissions, then read)
- Non-atomic "check if user exists, then create user" without unique constraint
4. Write the findings
For each real finding, structure it:
### <severity> · <one-line title>
**File / line:** path/to/file.py:42
**What:** <one-sentence description of the bug>
**Why it matters:** <what an attacker could do with this>
**Fix:** <concrete change — code or approach>
**Confidence:** <high | medium | low — how sure you are this is real>
Severity:
- Critical — direct compromise of secrets, data, or system integrity
- High — meaningful data exposure or bypassable auth
- Medium — exploitable under some conditions; defense-in-depth failure
- Low — style / hygiene / hardening opportunity
Order the findings most-severe first. If there are none, say so — do not
manufacture findings. "No security issues found in the reviewed scope
(). Not reviewed: <what you didn't cover>." is a valid
and honest report.
5. Say what you did and did NOT check
End with an explicit scope reminder. Reviewers of your review need to know
what you covered:
Reviewed: <files, diff range, or module>
Did not review: <what you deliberately skipped and why>
Not in scope: <things a security review can't verify without running
the code / a specialist audit — e.g. cryptographic proof,
side-channel analysis, business logic soundness>
Anti-patterns
- Do not run any exploit or PoC yourself. Describe how the bug would be
exploited in the write-up; do not demonstrate.
- Do not report "you should use HTTPS" or other generic hygiene as a finding
unless it's actually violated in the code you reviewed.
- Do not upgrade the confidence of a finding to make it sound scarier. "Low
confidence, might be a bug" beats a false positive that erodes trust.
- Do not review your own code with less rigor. If you wrote the change
earlier in the conversation, apply the same procedure.
1---2name: security-review3description: Review a code change or a file for security issues — secret leaks, injection, auth bypass, deserialization, common OWASP hits, and dependency risks. Triggers on "security review", "review this for security", "check for vulns", "did I leak a secret", or before pushing anything that touches auth / crypto / user input / SQL / shell / eval.4---56# security-review78Focused security review of a diff, a file, or a small module. Not a9comprehensive audit — that's an engagement. This finds the classes of bug10that regularly ship because nobody looked, and it's specific about what it11did and did not check.1213## When to use this1415- "Review this for security"16- "Check for vulns"17- "Did I leak a secret in this diff"18- Before pushing / opening a PR that touches: auth, session, crypto, user19 input, SQL, shell exec, file paths from users, deserialization, `eval`,20 regex on untrusted input, or third-party API calls with secrets21- After bumping a dependency to a version with a known CVE (`npm audit`,22 `pip-audit`, `cargo audit`)2324Not for: comprehensive audits of an entire codebase (too big for a skill;25scope carefully), threat modeling, cryptographic protocol design, or26regulated-compliance certification. Say so if asked.2728## Procedure2930### 1. Establish scope3132Ask for or infer the exact scope. Options in priority order:33341. A named file or files.352. A branch diff: `git diff <base>...HEAD`.363. Staged changes: `git diff --cached`.374. A directory or module.3839Do NOT try to review a whole repo in one pass. If the scope is too large40(more than ~500 changed lines or ~10 files touching sensitive areas), say41so and offer to review the most sensitive slice first.4243### 2. Run the fast, cheap checks first4445Before deep reading, do the mechanical scans. These are cheap and catch46the highest-severity, dumbest bugs.4748**Secrets in diff:**4950- `git diff <range> | grep -iE '(api[_-]?key|secret|token|password|bearer|BEGIN [A-Z ]+PRIVATE KEY|xox[baprs]-|ghp_|sk-|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35})'`51- Also check for `.env`-shaped strings: `[A-Z_]+=[^ ]+` in non-config files52- If anything hits, **halt** and report the exact lines. Do not continue53 until the user confirms whether it's real.5455**Dependency CVEs:**5657- Detect package manager from lockfile58- Suggest running: `npm audit`, `pip-audit`, `cargo audit`, `bundle audit`,59 or equivalent — don't run automatically; the user does it60- If the diff bumps a dependency, check the version bump for a corresponding61 advisory6263### 3. Read for the OWASP-shaped classic bugs6465Look at each changed file for these patterns. Note anything that matches;66don't guess when unsure.6768**Injection:**6970- **SQL** — string concatenation of user input into SQL, template literals71 with query fragments, missing parameterization. `execute("SELECT * FROM u72 WHERE id = " + id)` is a hit. Check for prepared statements / parameter73 binding / ORM usage.74- **Command / shell** — `subprocess.run(..., shell=True)` or backticks or75 `os.system` with user input. `shell=False` + list args is safe.76- **NoSQL** — `find({field: userInput})` without validation, especially77 if the field allows `$where` or operators.78- **XSS** — `innerHTML = userInput`, unescaped template output, missing79 `htmlspecialchars` / `escape`. React auto-escapes; `dangerouslySetInnerHTML`80 reverses that.81- **XXE / SSRF** — user-controlled URLs passed to `fetch` / `requests.get`,82 XML parsers without external-entity restriction.8384**Auth / access control:**8586- Missing authorization checks on state-changing endpoints87- IDOR: `GET /users/:id` returning any user's data without checking whether88 the caller can view that ID89- Session fixation, missing CSRF tokens on state-changing routes90- Weak password hashing (MD5, SHA1, plain SHA256 without salt)91- JWT with `alg: none`, JWT verification skipped, JWT secret hardcoded9293**Crypto / data:**9495- Home-rolled crypto instead of stdlib primitives96- ECB mode block cipher, static IV, missing MAC97- Insecure randomness for security purposes (`Math.random()`, `random.random()`)98- Timing-attack-vulnerable comparisons on secrets (`==` on tokens; should99 be constant-time compare)100101**Deserialization:**102103- `pickle.load` on untrusted input, `yaml.load` (not `safe_load`),104 `unserialize`, Java `readObject` on external data — all remote-code-exec105 waiting to happen106107**File & path:**108109- Path joining with user input without normalization → path traversal110 (`../../etc/passwd`)111- File upload endpoints that don't check extension AND content type AND112 filename length113- Symlink attacks if the code follows symlinks in user-controlled dirs114115**Race conditions on security-relevant state:**116117- TOCTOU: check-then-use with a gap (e.g. check file permissions, then read)118- Non-atomic "check if user exists, then create user" without unique constraint119120### 4. Write the findings121122For each real finding, structure it:123124```125### <severity> · <one-line title>126127**File / line:** path/to/file.py:42128129**What:** <one-sentence description of the bug>130131**Why it matters:** <what an attacker could do with this>132133**Fix:** <concrete change — code or approach>134135**Confidence:** <high | medium | low — how sure you are this is real>136```137138Severity:139- **Critical** — direct compromise of secrets, data, or system integrity140- **High** — meaningful data exposure or bypassable auth141- **Medium** — exploitable under some conditions; defense-in-depth failure142- **Low** — style / hygiene / hardening opportunity143144Order the findings most-severe first. If there are none, say so — do not145manufacture findings. "No security issues found in the reviewed scope146(<what you looked at>). Not reviewed: <what you didn't cover>." is a valid147and honest report.148149### 5. Say what you did and did NOT check150151End with an explicit scope reminder. Reviewers of your review need to know152what you covered:153154```155Reviewed: <files, diff range, or module>156Did not review: <what you deliberately skipped and why>157Not in scope: <things a security review can't verify without running158 the code / a specialist audit — e.g. cryptographic proof,159 side-channel analysis, business logic soundness>160```161162## Anti-patterns163164- Do not run any exploit or PoC yourself. Describe how the bug would be165 exploited in the write-up; do not demonstrate.166- Do not report "you should use HTTPS" or other generic hygiene as a finding167 unless it's actually violated in the code you reviewed.168- Do not upgrade the confidence of a finding to make it sound scarier. "Low169 confidence, might be a bug" beats a false positive that erodes trust.170- Do not review your own code with less rigor. If you wrote the change171 earlier in the conversation, apply the same procedure.