security-audit
Mission
Find real security vulnerabilities in code before they are exploited. This skill is
proactive — it audits code for security weaknesses, not just responds to incidents.
For writing secure code patterns (policies, auth, CSRF), use the security skill instead.
When to use
Use this skill when:
- Auditing a codebase or module for security risks
analysis-autonomous-mode routes here after detecting risky patterns
- Reviewing code that handles user input, authentication, or authorization
- Checking for vulnerabilities before a release or deployment
Do NOT use when:
- Writing new auth/policy code — route to
security
- Hunting for functional bugs — route to
bug-analyzer (proactive mode)
- Investigating performance — route to
performance-analysis
- You need a pre-implementation threat model for a new feature — route to
threat-modeling
- You need end-to-end authorization analysis for one route/action — route to
authz-review
Procedure: Security audit
0. False-positive gate — restate the claim before reporting
Before any finding enters the report, restate it as one falsifiable sentence
naming all three of:
- Privilege level — what access the attacker already has (anonymous,
authenticated user, tenant admin, CI runner).
- Execution context — where the vulnerable code runs (request handler,
queue worker, sandboxed template, build step).
- Attacker precondition — the concrete state or input the attacker must
control to trigger it.
If any of the three cannot be named concretely, the item is not a finding
yet — trace further or drop it with a one-line reason.
Rationalizations to Reject:
| Rationalization |
Reality |
| "It looks dangerous" |
Pattern-recognition is not analysis — trace the full data flow from entry to sink first |
| "This is clearly critical" |
Complete a devil's-advocate pass — models systematically overrate severity |
| "Report it just in case" |
Over-reporting erodes trust; an unverifiable finding is noise, not diligence |
| "Same pattern as a known CVE" |
Same pattern ≠ same preconditions — verify the preconditions hold in THIS codebase |
Standard vs. Deep verification routing:
- Standard — traced data flow + all three claim elements named → report
with the normal field list.
- Deep — severity would be High/Critical, OR the precondition chain
crosses a trust boundary you did not personally trace → run a
devil's-advocate pass first: actively try to refute the finding (existing
middleware? framework default? type system? config?). Report only what
survives; findings the pass killed are listed one-line under
Rejected candidates so the triage is auditable.
1. Map attack surface
Identify all entry points where untrusted data enters:
- HTTP request parameters, headers, cookies
- File uploads
- API payloads (JSON, XML, form data)
- Webhook callbacks
- Queue job payloads from external sources
- Import files (CSV, Excel, XML)
- URL path segments and query strings
2. Trace trust boundaries
For each entry point, trace where user input flows:
User Input → Controller → Validation → Service → DB/File/External
↓ ↓ ↓
Is it sanitized? Complete? Used safely?
3. Check vulnerability categories
| Category |
What to look for |
| SQL Injection |
Raw queries with concatenation, missing parameter binding |
| XSS |
Unescaped template output (Blade {!! !!}, JSX dangerouslySetInnerHTML, Jinja ` |
| CSRF |
Missing middleware, API endpoints without token verification |
| Auth bypass |
Missing policy checks, broken gate logic, withoutMiddleware() |
| IDOR |
Direct object access without ownership verification |
| Mass assignment |
Missing $fillable/$guarded, request()->all() in create/update |
| File upload |
Missing type validation, path traversal, executable uploads |
| SSRF |
User-controlled URLs passed to HTTP client |
| Deserialization |
Unserializing user input, unsafe queue payloads |
| Secret exposure |
Hardcoded credentials, secrets in logs, .env in public dir |
| Rate limiting |
Missing throttle on auth endpoints, password reset, API |
| Header injection |
User input in response headers, email headers |
| Insecure defaults / fail-open |
Guards that allow on error (catch { return true } in an authz check), default-allow matchers, debug mode defaulting on, permissive CORS/verify=false fallbacks, feature flags whose missing value grants access |
Worked example (fail-open): if (!$gate->check($user)) { … } wrapped in a
try/catch that logs and continues fails open — an exception in the gate
grants access. Finding shape: Category Insecure defaults, Evidence the
catch block file:line, Fix fail closed — rethrow or deny on gate error.
3b. Out of scope — route, do not guess
The table above names vulnerability classes, which are stable. This package
carries no cryptographic parameter, key size, work factor, cipher suite, or TLS
version floor: a value copied here reads authoritative long after it stops being
true. Report the finding, route the fix to
https://cheatsheetseries.owasp.org/ — Cryptographic Storage, Transport Layer
Security, Password Storage, XML External Entity Prevention — and never name a
value from memory. Rationale and reopening condition:
ADR-238.
4. Framework-specific checks
→ Laravel-specific checks: see laravel § Security audit checks.
5. Dependency audit
- Check
composer.lock for known vulnerable packages
- Check
package-lock.json for frontend vulnerabilities
- Identify outdated packages with known CVEs
- Check if security patches are available
Output format
- Emit one entry per vulnerability using the field list below; one finding = one block, never merge.
- Category must map to an OWASP Top 10 (or LLM Top 10) bucket; Severity must use Low / Medium / High / Critical with a single Exploitability tag.
- Close with a Recommended Fix Order ranked by exploitability × blast radius and tag each line with Confidence.
For each vulnerability:
- Vulnerability: concise title
- Category: OWASP category (Injection, Broken Auth, etc.)
- Location: file and line
- Severity: Low / Medium / High / Critical
- Exploitability: How easy to exploit (trivial / requires auth / complex)
- Impact: What an attacker could achieve
- Evidence: code reference showing the weakness
- Fix: concrete mitigation
- Confidence: Low / Medium / High
After the findings, add a Rejected candidates section: one line per
look-dangerous-but-benign pattern the Step-0 gate killed, with the traced
reason ("raw SQL string is a static migration constant — no user input
reaches it"). An audit that rejects nothing has usually skipped the gate.
Integration with other skills
- analysis-autonomous-mode — routes here when security concerns are detected
- security — complementary: security is about writing secure code, this is about finding holes
- universal-project-analysis — provides context about packages and framework usage
- bug-analyzer — some bugs have security implications (chain when found)
- untrusted-input-defense / lethal-trifecta-guard (rules) — prompt-injection / agent-config defense; consult when the audited code ingests untrusted content or wires an autonomous egress path
Gotcha
- Don't report theoretical vulnerabilities without a concrete attack vector — false positives erode trust.
- The model tends to flag framework-handled security as issues (e.g., Laravel's CSRF or Rails'
protect_from_forgery is already handled).
- Always check if a finding is already mitigated by middleware or configuration before reporting it.
Do NOT
- Do NOT report theoretical risks that require impossible preconditions
- Do NOT ignore user input flows — always trace from entry to usage
- Do NOT assume frameworks handle everything — verify middleware and config
- Do NOT confuse code quality issues with security vulnerabilities
- Do NOT skip dependency checking — known CVEs are real risks
See also
1---2name: security-audit3description: Security audit — vulnerability scan, pentest review, attack-surface sweep; explicit request only, not regular feature work. Pre-implementation threat pass → threat-modeling.4---56# security-audit78## Mission910Find real security vulnerabilities in code before they are exploited. This skill is11**proactive** — it audits code for security weaknesses, not just responds to incidents.1213For writing secure code patterns (policies, auth, CSRF), use the `security` skill instead.1415## When to use1617Use this skill when:1819- Auditing a codebase or module for security risks20- `analysis-autonomous-mode` routes here after detecting risky patterns21- Reviewing code that handles user input, authentication, or authorization22- Checking for vulnerabilities before a release or deployment2324Do NOT use when:2526* Writing new auth/policy code — route to [`security`](../security/SKILL.md)27* Hunting for functional bugs — route to [`bug-analyzer`](../bug-analyzer/SKILL.md) (proactive mode)28* Investigating performance — route to [`performance-analysis`](../performance-analysis/SKILL.md)29* You need a pre-implementation threat model for a new feature — route to30 [`threat-modeling`](../threat-modeling/SKILL.md)31* You need end-to-end authorization analysis for one route/action — route to32 [`authz-review`](../authz-review/SKILL.md)3334## Procedure: Security audit3536### 0. False-positive gate — restate the claim before reporting3738Before any finding enters the report, restate it as one falsifiable sentence39naming all three of:40411. **Privilege level** — what access the attacker already has (anonymous,42 authenticated user, tenant admin, CI runner).432. **Execution context** — where the vulnerable code runs (request handler,44 queue worker, sandboxed template, build step).453. **Attacker precondition** — the concrete state or input the attacker must46 control to trigger it.4748If any of the three cannot be named concretely, the item is **not a finding49yet** — trace further or drop it with a one-line reason.5051**Rationalizations to Reject:**5253| Rationalization | Reality |54|---|---|55| "It looks dangerous" | Pattern-recognition is not analysis — trace the full data flow from entry to sink first |56| "This is clearly critical" | Complete a devil's-advocate pass — models systematically overrate severity |57| "Report it just in case" | Over-reporting erodes trust; an unverifiable finding is noise, not diligence |58| "Same pattern as a known CVE" | Same pattern ≠ same preconditions — verify the preconditions hold in THIS codebase |5960**Standard vs. Deep verification routing:**6162- **Standard** — traced data flow + all three claim elements named → report63 with the normal field list.64- **Deep** — severity would be High/Critical, OR the precondition chain65 crosses a trust boundary you did not personally trace → run a66 devil's-advocate pass first: actively try to refute the finding (existing67 middleware? framework default? type system? config?). Report only what68 survives; findings the pass killed are listed one-line under69 *Rejected candidates* so the triage is auditable.7071### 1. Map attack surface7273Identify all entry points where untrusted data enters:7475- HTTP request parameters, headers, cookies76- File uploads77- API payloads (JSON, XML, form data)78- Webhook callbacks79- Queue job payloads from external sources80- Import files (CSV, Excel, XML)81- URL path segments and query strings8283### 2. Trace trust boundaries8485For each entry point, trace where user input flows:8687```88User Input → Controller → Validation → Service → DB/File/External89 ↓ ↓ ↓90 Is it sanitized? Complete? Used safely?91```9293### 3. Check vulnerability categories9495| Category | What to look for |96|---|---|97| **SQL Injection** | Raw queries with concatenation, missing parameter binding |98| **XSS** | Unescaped template output (Blade `{!! !!}`, JSX `dangerouslySetInnerHTML`, Jinja `|safe`), JSON responses with HTML |99| **CSRF** | Missing middleware, API endpoints without token verification |100| **Auth bypass** | Missing policy checks, broken gate logic, `withoutMiddleware()` |101| **IDOR** | Direct object access without ownership verification |102| **Mass assignment** | Missing `$fillable`/`$guarded`, `request()->all()` in create/update |103| **File upload** | Missing type validation, path traversal, executable uploads |104| **SSRF** | User-controlled URLs passed to HTTP client |105| **Deserialization** | Unserializing user input, unsafe queue payloads |106| **Secret exposure** | Hardcoded credentials, secrets in logs, `.env` in public dir |107| **Rate limiting** | Missing throttle on auth endpoints, password reset, API |108| **Header injection** | User input in response headers, email headers |109| **Insecure defaults / fail-open** | Guards that allow on error (`catch { return true }` in an authz check), default-allow matchers, debug mode defaulting on, permissive CORS/`verify=false` fallbacks, feature flags whose missing value grants access |110111Worked example (fail-open): `if (!$gate->check($user)) { … }` wrapped in a112`try/catch` that logs and **continues** fails open — an exception in the gate113grants access. Finding shape: Category *Insecure defaults*, Evidence the114catch block `file:line`, Fix *fail closed — rethrow or deny on gate error*.115116### 3b. Out of scope — route, do not guess117118The table above names vulnerability **classes**, which are stable. This package119carries no cryptographic parameter, key size, work factor, cipher suite, or TLS120version floor: a value copied here reads authoritative long after it stops being121true. Report the finding, route the fix to122<https://cheatsheetseries.owasp.org/> — Cryptographic Storage, Transport Layer123Security, Password Storage, XML External Entity Prevention — and never name a124value from memory. Rationale and reopening condition:125[ADR-238](../../../docs/decisions/ADR-238-security-content-routes-to-external-authority.md).126127### 4. Framework-specific checks128129→ Laravel-specific checks: see [`laravel`](../laravel/SKILL.md) § Security audit checks.130131### 5. Dependency audit132133- Check `composer.lock` for known vulnerable packages134- Check `package-lock.json` for frontend vulnerabilities135- Identify outdated packages with known CVEs136- Check if security patches are available137138## Output format1391401. Emit one entry per vulnerability using the field list below; one finding = one block, never merge.1412. Category must map to an OWASP Top 10 (or LLM Top 10) bucket; Severity must use Low / Medium / High / Critical with a single Exploitability tag.1423. Close with a *Recommended Fix Order* ranked by exploitability × blast radius and tag each line with Confidence.143144For each vulnerability:145146- **Vulnerability:** concise title147- **Category:** OWASP category (Injection, Broken Auth, etc.)148- **Location:** file and line149- **Severity:** Low / Medium / High / Critical150- **Exploitability:** How easy to exploit (trivial / requires auth / complex)151- **Impact:** What an attacker could achieve152- **Evidence:** code reference showing the weakness153- **Fix:** concrete mitigation154- **Confidence:** Low / Medium / High155156After the findings, add a **Rejected candidates** section: one line per157look-dangerous-but-benign pattern the Step-0 gate killed, with the traced158reason ("raw SQL string is a static migration constant — no user input159reaches it"). An audit that rejects nothing has usually skipped the gate.160161## Integration with other skills162163- **analysis-autonomous-mode** — routes here when security concerns are detected164- **security** — complementary: security is about writing secure code, this is about finding holes165- **universal-project-analysis** — provides context about packages and framework usage166- **bug-analyzer** — some bugs have security implications (chain when found)167- **untrusted-input-defense** / **lethal-trifecta-guard** (rules) — prompt-injection / agent-config defense; consult when the audited code ingests untrusted content or wires an autonomous egress path168169## Gotcha170171- Don't report theoretical vulnerabilities without a concrete attack vector — false positives erode trust.172- The model tends to flag framework-handled security as issues (e.g., Laravel's CSRF or Rails' `protect_from_forgery` is already handled).173- Always check if a finding is already mitigated by middleware or configuration before reporting it.174175## Do NOT176177- Do NOT report theoretical risks that require impossible preconditions178- Do NOT ignore user input flows — always trace from entry to usage179- Do NOT assume frameworks handle everything — verify middleware and config180- Do NOT confuse code quality issues with security vulnerabilities181- Do NOT skip dependency checking — known CVEs are real risks182183## See also184185- [`docs/threat-model.md`](../../../docs/threat-model.md) — package attack surface and trust boundary documentation.