Security

Security Review

Review a diff for OWASP-class vulnerabilities that this specific change could introduce -- scoped to what actually applies, with a verify pass before anything is reported.

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.

SKILL.md
---
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:

Claude Code
.claude/skills/security-review/SKILL.md

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.

Cursor
.cursor/rules/security-review.mdc

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 / Copilot
AGENTS.md

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.

Windsurf
.windsurfrules

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.

Where this goes wrong
  • 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).