OWASP Top 10 Review skill for Codex
Find real OWASP bugs at file:line. With this skill Codex maps your app's attack surface, walks every OWASP Top 10 category against the real handlers, and reports each finding with the untrusted source, the sink, the missing control and a fix that compiles. It prioritizes access-control and injection bugs, where most real issues live, instead of listing generic advice.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- owasp-top-10-review Installs to ~/.agents/skills/owasp-top-10-review/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install owasp-top-10-review The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project owasp-top-10-review Installs to .agents/skills/owasp-top-10-review/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/owasp-top-10-review.zipunzip -o owasp-top-10-review.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) owasp-top-10-review PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $owasp-top-10-review, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Reviews a web application's source for the OWASP Top 10 (2021, with 2025 category shifts) — broken access control, injection, cryptographic failures, SSRF, insecure design and the rest — and reports findings with file:line evidence and a concrete fix. Use when asked to "check this app for OWASP issues", harden an endpoint, review auth or access-control code, or before a release that handles user data.
Use it when
- Reviewing an app or endpoint for web security issues
- Hardening auth, access control or data-handling code
- A pre-release security pass on code that handles user data
Use something else when
- You want a security-focused review of an arbitrary diff rather than full OWASP coverage: use secure-code-review
- You need live exploitation against an authorized target: use web-app-pentest
- The concern is only outdated dependencies with CVEs: use dependency-scanning
How to check the result
Each finding names a file:line, the untrusted input and the sink; re-grep the same pattern across sibling handlers to confirm the fix was applied everywhere.
The mistake it prevents
Reporting grep hits from tests, comments or framework-escaped sinks as findings without tracing the data flow from an untrusted source.
A good review names the exact category, points at the vulnerable line, proves why it is
exploitable in this code (not in general), and gives a fix that compiles. “Add input
validation” is not a finding; “the userId from req.params is used in the query without
an ownership check at routes/orders.js:42” is.
Before you start
- Read
AGENTS.mdand the project README for the stack, framework and auth model. - Map the attack surface first: list every route/handler, every place untrusted input enters (query, body, headers, cookies, path, file uploads, webhooks, message queues), and every trust boundary (auth middleware, tenant isolation, admin-only paths).
- Identify the authn/authz mechanism (session, JWT, OAuth) and the data store. Most of the real findings cluster around access control and injection.
This skill is a code review, not a live test. It needs no network and no running app.
Category checklist
Walk each category against the mapped surface. Record every hit with file:line.
| # | Category (2021) | Look for |
|---|---|---|
| A01 | Broken Access Control | Object IDs from the request used without an ownership/tenant check; missing role checks on admin routes; client-trusted isAdmin; path traversal in file access; CORS * with credentials |
| A02 | Cryptographic Failures | Secrets/PII sent or stored in plaintext; MD5/SHA1 for passwords; hardcoded keys; Math.random() for tokens; missing TLS; disabled cert verification |
| A03 | Injection | String-built SQL/NoSQL/LDAP/OS commands; eval/exec on input; unescaped HTML (XSS); template injection; ORM raw() with interpolation |
| A04 | Insecure Design | No rate limit on auth/reset; missing anti-automation; trust in client-side checks only; business-logic flaws (negative quantities, price from client) |
| A05 | Security Misconfiguration | Debug on in prod; default creds; verbose stack traces to users; permissive CORS; missing security headers; open cloud buckets |
| A06 | Vulnerable Components | Outdated deps with known CVEs; unpinned versions (hand off to dependency-scanning) |
| A07 | Identification & Auth Failures | Weak password policy; no lockout/MFA; session fixation; JWT alg:none or unverified signature; tokens in URLs |
| A08 | Software & Data Integrity | Unsigned/untrusted updates; insecure deserialization (pickle, Java readObject, PHP unserialize); CI pulling unpinned actions |
| A09 | Logging & Monitoring Failures | No audit log on auth/privilege change; secrets/PII in logs; no alerting path |
| A10 | SSRF | User-controlled URLs fetched server-side without allowlist/validation (hand off to full SSRF defenses) |
How to find the high-value bugs fast
Access control (A01) is where most real findings are. For every handler that reads a resource by ID, ask: who owns this row, and where is that enforced? Grep the pattern:
# Params/body used to look up records — check each for an ownership/tenant filter
grep -rnE "findById|findOne|WHERE id|params\.(id|userId)|\.get\(['\"]id" --include=*.js --include=*.ts src
# Admin or role gates — confirm they cannot be bypassed by another route
grep -rniE "isAdmin|role|authorize|requireAuth|@PreAuthorize" src
Injection (A03). Any query assembled with +, template literals, f-strings or
.format() around input is a finding unless the driver parameterizes it.
grep -rnE "(query|execute)\(.*(\+|\$\{|%s.*%|f['\"]).*" src # candidate string-built queries
grep -rnE "eval\(|exec\(|child_process|os\.system|subprocess" src
Secrets & crypto (A02). Scan for hardcoded material and weak primitives:
grep -rniE "api[_-]?key|secret|password|private[_-]?key|BEGIN RSA" src .env*
grep -rniE "md5|sha1|createHash\(['\"]md5|DES|ECB|Math\.random" src
For each hit, open the file and confirm the data flow from an untrusted source to the sink before you report it. A grep hit inside a test fixture or a comment is not a finding.
Confirming exploitability
Promote a candidate to a finding only when you can state the untrusted source, the sink,
and the missing control. For an access-control bug, write the one-line exploit in words:
“User A requests GET /orders/<B's id> and receives B’s order because no userId filter
is applied.” If you cannot articulate that, mark it “needs verification” instead of
asserting it.
Severity
Rate by impact × exploitability, aligned to CVSS bands:
- Critical — unauthenticated RCE, auth bypass, mass data exposure, SQLi on a prod path.
- High — authenticated privilege escalation, IDOR exposing other tenants’ data, stored XSS.
- Medium — reflected XSS needing user interaction, CSRF on a state change, missing rate limit on auth.
- Low — missing security header, verbose errors, info leak with no direct impact.
Output contract
## OWASP Top 10 review — <app/scope> (<date>)
Scope: <paths/modules reviewed>. Not covered: <what you did not read>.
### Findings
#### [Critical] A01 Broken Access Control — IDOR on order lookup
- Location: `routes/orders.js:42`
- Untrusted input: `req.params.id` (any authenticated user)
- Sink: `Order.findById(req.params.id)` returned without owner check
- Impact: any user reads any order (PII, totals)
- Fix:
```js
const order = await Order.findOne({ _id: req.params.id, userId: req.user.id });
if (!order) return res.sendStatus(404);
- Status: confirmed by code path
Summary
<counts by severity>. Top 3 to fix first: <list>.
## Hard rules
- Report only what the code shows. Do not assert exploitability you have not traced.
- Never run exploit payloads against a live or shared system from here — this is static
review. Live testing belongs to an authorized engagement (see web-app-pentest).
- Keep secrets you find out of the report body; reference the location and say "rotate".
- Re-grep after fixes land: the same pattern often repeats across sibling handlers. What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "OWASP Top 10 Review"
short_description: "Find real OWASP bugs at file:line"
default_prompt: "$owasp-top-10-review Review this Express app against the OWASP Top 10 before we ship" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.