Skip to main content

security-review

Auto-load

Attaches itself automatically when a prompt matches.

Identify exploitable security vulnerabilities in generated code. Use when reviewing for security issues, when building auth / file upload / external requests, or before deploy. Reports only HIGH confidence findings — clear vulnerable patterns with attacker-controlled input. OWASP-grounded.

Scaffolds

next
react
vanilla

Source

Adapted from getsentry/skills (security-review, adapted — uv/bash tool requirements stripped)

Instructions

The full instruction set the agent receives when this skill is active.

Security Review

Identify exploitable vulnerabilities. Report only HIGH CONFIDENCE findings — clear vulnerable patterns with attacker-controlled input. Theoretical issues, defense-in-depth nags, and missing-best-practice notes are noise; skip them.

The discipline

  • Research broadly. Report narrowly. Trace where the input came from before flagging. A pattern that looks dangerous in isolation may be fed only by server-controlled config.
  • Pattern matching alone is not a vulnerability. Investigate first.

Confidence levels

LevelCriteriaAction
HIGHVulnerable pattern + attacker-controlled input confirmedReport with severity
MEDIUMVulnerable pattern, input source unclearNote as "Needs verification"
LOWTheoretical, best practice, defense-in-depthDo not report

Do NOT flag

General

  • Test files (unless explicitly reviewing test security)
  • Dead code, commented code, doc strings
  • Patterns using constants or server-controlled config
  • Code paths that require prior authentication to reach (note the auth requirement instead)

Server-controlled values (NOT attacker-controlled)

SourceExampleWhy safe
Env varsprocess.env.DATABASE_URLDeployment config
Config filesconfig.api.url, import config from './config'Source-controlled
Hardcoded constantsconst BASE_URL = "https://api.internal"Compile-time
Framework settingsNext.js next.config.js exportsOperator-controlled

SSRF example — NOT a vulnerability:

ts
// SAFE: URL comes from env (server-controlled) const res = await fetch(`${process.env.API_URL}${path}`);

SSRF example — IS a vulnerability:

ts
// VULNERABLE: URL comes from request query (attacker-controlled) const res = await fetch(req.query.url);

Framework-mitigated patterns (usually safe)

PatternWhy usually safe
React {variable} in JSXAuto-escaped
vue {{ variable }}Auto-escaped
Next.js dynamic routes via paramsType-safe, framework-validated
Prisma where: { id: input }ORM parameterizes
Tagged template SQL libs (sql\SELECT ...``)Parameterized

Only flag when the escape is bypassed:

  • React: dangerouslySetInnerHTML={{__html: userInput}}
  • Vue: v-html="userInput"
  • ORM: .$queryRawUnsafe(...), raw SQL string interpolation

Always flag (Critical)

ts
eval(userInput) // Any language new Function(userInput) // JS arbitrary code child_process.exec(userInput) // Node command injection child_process.exec(`grep ${userInput} file`) // Shell injection fs.readFile(userInput) // Path traversal candidate fetch(userInput) // SSRF candidate (if URL from user)

Always flag (High)

ts
el.innerHTML = userInput // DOM XSS dangerouslySetInnerHTML={{ __html: userInput }} // React XSS `SELECT * FROM users WHERE id = ${userInput}` // SQL injection (raw) res.redirect(req.query.next) // Open redirect process.env.SECRET = req.body.secret // Trust boundary violation

Always flag (Secrets)

ts
const apiKey = "sk-real-key-here" // Hardcoded API key const password = "hunter2" // Hardcoded password // AWS_SECRET_ACCESS_KEY = "AKIA..." // Even in comments const privateKey = "-----BEGIN RSA PRIVATE KEY" // Inline private key

Check context first (investigate before flagging)

ts
// SSRF — only flag if URL is from user input fetch(req.query.url) // FLAG fetch(process.env.API_URL) // SAFE fetch(`${process.env.BASE}/${x}`) // CHECK: is x user input? // Path traversal — only flag if path is from user input fs.readFile(req.query.file) // FLAG fs.readFile(process.env.LOG_PATH) // SAFE fs.readFile(path.join(BASE_DIR, name)) // CHECK: is name user input + unfiltered? // Open redirect — only flag if URL is from user input res.redirect(req.query.next) // FLAG res.redirect("/login") // SAFE // Weak crypto — only flag if used for security crypto.createHash("md5").update(fileBytes) // SAFE (file checksums, caching) crypto.createHash("md5").update(password) // FLAG (use bcrypt/scrypt/argon2) Math.random() // SAFE (non-security uses) Math.random() // for auth token // FLAG (use crypto.randomBytes)

Authentication / authorization (real wins live here)

  • Missing authz on a "user can edit X" route — does the handler check session.userId === X.ownerId? If not, it's IDOR. HIGH.
  • JWT verified with alg=none or no alg check — bypass. CRITICAL.
  • Cookies without Secure; HttpOnly; SameSite for session — flag if production-facing.
  • CSRF on state-changing routes — Next.js Route Handlers + cookie auth need a CSRF defense (SameSite=lax/strict, or a token). Tag flag if the handler is POST/PUT/DELETE and accepts cookies but has no SameSite or token.
  • Rate limiting absent on /login, /signup, /reset — brute-force candidate. MEDIUM unless real users exist.

Severity guide

SeverityWhen
CriticalRCE, SQL injection to data, auth bypass, hardcoded prod secret
HighStored XSS, SSRF to metadata, IDOR to sensitive data, missing authz on edit
MediumReflected XSS, CSRF on state-changing actions, path traversal with constraints
LowMissing security headers, verbose errors — DO NOT REPORT unless asked

Output format

markdown
## Security Review: [target] ### Summary - Findings: X (Y Critical, Z High, …) - Confidence: High ### Findings #### [VULN-001] [Vulnerability Type] (Severity) - Location: `path/to/file.ts:LINE` - Issue: [what the vulnerability is] - Impact: [what an attacker could do] - Evidence: ```ts // vulnerable snippet
  • Fix: [concrete remediation]

Needs verification

[VERIFY-001] [Potential Issue]
  • Location: path/to/file.ts:LINE
  • Question: [what would change the verdict]
If clean: "No high-confidence vulnerabilities identified." ## When applied during a build (not after) You can prevent most of the above by reflex while writing code: - Never put raw user input into `innerHTML`, SQL strings, shell commands, file paths, or redirect URLs - Always pull secrets from `process.env`, never hard-code - Always check authz against the resource owner, not just authentication - Always parameterize SQL via the ORM or tagged template See also: [[evidence-before-claim]] (don't claim "secure" without verifying).
security-review skill — Codebase