When to use this
- Before merging code that touches auth, user input, external calls, or data storage.
- When asked explicitly for a security review, not a general code review.
- Not a full penetration test or dependency audit -- pair with dependency-vuln-auditor and secrets-scanner for those.
Scope discipline is the whole skill
Security checklists are long, and running every item against every diff produces noise: a SQL-injection line item on a diff with no database code, an SSRF item on a static site. That noise is what makes real findings get ignored. This skill's checklist is applied selectively -- skip the items that don't apply to what's actually in the diff, and spend the saved effort tracing the input-to-sink path for the items that do.
The skill file
Copy this verbatim. It's written in the SKILL.md format (YAML frontmatter + markdown instructions) that Claude Code, and increasingly other agent tools, read directly.
--- name: security-review description: Review a diff for security vulnerabilities it could introduce -- injection, broken access control, unsafe deserialization, sensitive data exposure -- scoped to what actually applies to this diff's language/framework. Use for explicit security reviews, not general code review. --- # Security Review Only flag a vulnerability class that actually applies to what's in the diff. A diff with no database access can't have a SQL injection finding. ## Checklist, applied selectively - **Injection**: does user-controlled input reach a SQL query, shell command, template engine, or deserializer without parameterization/ escaping? - **Broken access control**: does a new endpoint/handler check authorization, or just authentication? Does an object-level check exist (can user A act on user B's resource by ID)? - **Sensitive data exposure**: secrets, tokens, or PII logged, returned in an error message, or stored unencrypted where the codebase's convention is to encrypt. - **SSRF**: does user input influence a URL the server fetches? - **Unsafe deserialization**: is untrusted input passed to a deserializer that can execute code (e.g. unrestricted pickle, unsafe YAML load)? - **Cryptography misuse**: a weak/deprecated algorithm, a hardcoded key/IV, a homegrown auth scheme where a standard one exists. ## Verify before reporting State the exact untrusted-input path: where the input originates, and the sink it reaches, with no sanitization in between. If the input is actually trusted (internal-only, validated upstream, constant), it's not a finding. ## Output Severity-ordered findings: vulnerability class, the input-to-sink path, concrete exploit scenario, and the standard fix (parameterized query, allowlist, existing auth middleware) -- not a from-scratch mitigation when the codebase already has the right primitive.
Installing it elsewhere
The frontmatter/body split above is Claude Code's convention. Here's how to carry the same instructions into other tools:
Save the file below verbatim (frontmatter included) at that path, project-local or in ~/.claude/skills/ for a user-level skill. Claude Code loads the name/description pair to decide when to pull it in, or you invoke it directly as /security-review.
Convert the YAML frontmatter to Cursor's rule format (description, globs, alwaysApply: false) and keep the markdown body as the rule content. Cursor surfaces it by description match, same idea as Claude Code's auto-load.
Codex CLI (and increasingly other agentic CLIs) read AGENTS.md at the repo root as always-on instructions. Paste the markdown body under a heading like ## {title}; for GitHub Copilot's coding agent, the equivalent file is .github/copilot-instructions.md.
Append the markdown body to .windsurfrules at the repo root. Windsurf treats the whole file as always-on context, so keep only the instructions you want applied on every request.
- Running the full OWASP Top 10 checklist against every diff regardless of relevance -- irrelevant findings train reviewers to skim past real ones.
- Flagging a theoretical vulnerability with no traceable untrusted-input path.
- Recommending a custom mitigation when the framework already has a standard, better-tested one (e.g. hand-rolled escaping instead of the ORM's parameterization).