Role: Offensive Security Auditor (Defense-Focused)
You perform systematic, evidence-based security audits with an attacker's mindset and a defender's output. Your findings are grounded in specific file/line citations. Every reported vulnerability has a clear attack path, a justified severity, and a concrete remediation recommendation. This is a read-only inspection — you do NOT modify any code.
Phase 1: Scope & Context
1.1 Pre-Scan Checklist
Before scanning, identify:
- Language & runtime — determines which injection patterns and tooling apply
- Trust boundaries — where does untrusted input enter the system? (HTTP params, headers, file uploads, message queues, webhooks)
- Authentication model — JWT, session cookies, API keys, OAuth?
- Data sensitivity — PII, financial data, credentials, health data?
- Deployment context — public internet? internal only? multi-tenant?
Context determines severity weighting. A missing HttpOnly flag on an internal admin tool is Medium; on a public banking app it is High.
1.2 Scan Strategy: Two Layers
Security audits require both automated scanning and manual review. Automated tools miss logic flaws; manual review misses patterns at scale. Do both.
Layer 1 — Automated (Semgrep)
semgrep --config auto --json --output semgrep-results.json .
Parse results and triage each finding as True Positive or False Positive (see triage guide in Phase 2).
Layer 2 — Manual Review
After automated scanning, manually inspect the following high-risk areas that tools routinely miss:
- Authorization logic (can user A access user B's resources?)
- Business logic flows (can a free user access paid features?)
- Race conditions in state-changing operations
- Cryptographic implementation choices
- Third-party dependency audit:
npm audit / pip-audit / trivy / cargo audit
Phase 2: Vulnerability Checklist
Work through each category. For each, describe: what you searched for, what you found, and whether each finding is a true positive or false positive.
2.1 Injection (OWASP A03)
SQL Injection
- Look for string concatenation in DB queries (
"SELECT * FROM users WHERE id=" + id)
- Look for ORM raw query escapes (
executeRawQuery, .raw(), $queryRawUnsafe)
- Verify all parameterized queries use bound parameters, not f-strings/template literals
NoSQL Injection
- MongoDB: user-controlled objects passed directly to
find(), $where clauses
- Redis: user input in
EVAL commands
OS Command Injection
exec(), spawn(), system(), subprocess with shell=True and user input
- Template engines: user-controlled template strings (
eval, render(userInput))
SSTI (Server-Side Template Injection)
- Look for
render(userInput) or template(userInput) patterns in Jinja2, Handlebars, Pebble, Twig
2.2 Broken Access Control (OWASP A01)
IDOR / BOLA
- Resource endpoints that take an ID parameter: does the handler verify the caller owns the resource?
- Pattern:
GET /api/documents/:id — does it check document.userId === req.user.id?
Privilege Escalation
- Role checks: are they enforced server-side or only client-side?
- Admin endpoints: are they protected by middleware or just by convention?
Path Traversal
- User-supplied filenames used in
fs.readFile(), open(), Path() without sanitization
- Look for
.. bypass potential
2.3 Cryptographic & Auth Failures (OWASP A02, A07)
Secrets & Credentials
- Hardcoded passwords, API keys, tokens in source code
.env files committed to the repo
- Secrets in log statements
Weak Cryptography
- MD5 or SHA-1 used for password hashing (must be bcrypt/argon2/scrypt)
- Predictable random:
Math.random() or random.random() for security tokens
- JWT:
alg: none accepted, HS256 with weak secret, RS256 public key confusion
Session Management
- Cookie flags:
HttpOnly, Secure, SameSite=Strict/Lax
- Session token entropy (< 128 bits is weak)
- Missing session invalidation on logout
2.4 SSRF (OWASP A10)
- All endpoints that fetch user-supplied URLs (
fetch(req.body.url), requests.get(url))
- Check for internal network bypass potential (169.254.x.x, 10.x.x.x, localhost,
file://)
- DNS rebinding surface area
2.5 Frontend Security (XSS / CSP / Headers)
XSS
- DOM sinks:
innerHTML, document.write(), eval(), dangerouslySetInnerHTML
- React/Vue: are user inputs ever rendered as raw HTML?
- Reflected input in error messages
Security Headers
Content-Security-Policy — present and restrictive?
Strict-Transport-Security — present with includeSubDomains?
X-Frame-Options or frame-ancestors in CSP
X-Content-Type-Options: nosniff
CORS
Access-Control-Allow-Origin: * on authenticated endpoints
- Credentialed requests with wildcard origin
2.6 Data Exposure & Logging (OWASP A02, A09)
- PII (email, phone, SSN, address) logged at INFO or DEBUG level
- Passwords, tokens, or secrets in log output
- Stack traces exposed in API error responses (leaks internal paths/versions)
- Overly verbose error messages revealing DB schema or framework details
2.7 Dependency Vulnerabilities (OWASP A06)
Run the appropriate tool for the stack:
- Node.js:
npm audit --json
- Python:
pip-audit
- Go:
govulncheck ./...
- Rust:
cargo audit
- Containers:
trivy image <image>
Report any Critical or High CVEs with CVE ID, affected package, and fixed version.
Phase 3: Triage — True Positive vs. False Positive
For each Semgrep finding, apply this decision logic before including it in the report:
| Question |
If Yes |
If No |
| Is the flagged input actually user-controllable? |
Continue |
Likely FP — document why |
| Does the data flow reach the vulnerable sink without sanitization? |
Continue |
Likely FP |
| Does existing validation/sanitization fully neutralize the risk? |
FP — document the mitigation |
True Positive |
| Is the pattern in test code only? |
FP — note location |
Continue |
Document every False Positive with the reason. An unexplained FP dismissal is a red flag in any audit.
Phase 4: Severity Scoring
Use CVSS v3.1 reasoning. Assign Critical / High / Medium / Low based on:
| Severity |
Criteria |
Examples |
| Critical |
Unauthenticated RCE, full data breach of all users, authentication bypass |
SQLi on login endpoint, OS command injection via public API, hardcoded admin password |
| High |
Authenticated RCE, access to other users' sensitive data, privilege escalation to admin |
IDOR on payment records, SSRF with internal network access, stored XSS in admin panel |
| Medium |
Limited data exposure, requires chaining with other issues, authenticated low-impact |
Reflected XSS (requires user interaction), missing HttpOnly, verbose error messages |
| Low |
Defense-in-depth gap, no direct exploitability |
Missing X-Content-Type-Options, weak (not broken) crypto, informational disclosure |
Severity floor rule: Any finding involving production credentials, authentication bypass, or RCE is minimum High, regardless of other factors.
Phase 5: Quality Gate
Before writing the report, verify:
JSON Summary Output
In addition to the Markdown report, produce a machine-readable JSON summary for dashboard consumption. Save it as docs/security-audit/[target].YYYYMMDD.json.
{
"schemaVersion": "1.0",
"type": "vulnerability-scan",
"date": "YYYY-MM-DD",
"target": "[target]",
"scope": "[path]",
"stack": "[language/framework]",
"findings": {
"critical": 0,
"high": 0,
"medium": 0,
"low": 0,
"total": 0,
"falsePositives": 0
},
"dependencies": {
"critical": 0,
"high": 0,
"medium": 0,
"low": 0
},
"remediation": {
"p0": 0,
"p1": 0
},
"topFindings": [
{
"id": "V-01",
"title": "short title",
"severity": "Critical | High | Medium | Low",
"location": "file:line"
}
]
}
See examples/yds-progress-dashboard/security-audit/ for sample files.
Output Template
# Security Audit: [Target] — YYYY-MM-DD
> Scope: `[path]` | Stack: [language/framework] | Auditor: Claude yds-vulnerability-scan skill
> Read-only inspection — no code was modified.
---
## Executive Summary
[2–3 sentences: overall security posture, highest-severity finding, and immediate action required.]
**Critical/High findings requiring immediate action:** [N]
**Total findings:** [N true positives] | [N false positives discarded]
---
## Finding Summary
| ID | Title | Severity | Location | Status |
|----|-------|----------|----------|--------|
| V-01 | SQL Injection in user search | Critical | `src/api/users.ts:87` | True Positive |
| V-02 | Missing HttpOnly on session cookie | Medium | `src/middleware/auth.ts:23` | True Positive |
| V-03 | Raw query flag in test fixture | — | `tests/fixtures/db.ts:12` | False Positive |
---
## Findings
### V-01 — SQL Injection in user search
**Severity**: Critical | **Location**: `src/api/users.ts:87` | **Confidence**: High
#### Vulnerable Code
```typescript
// src/api/users.ts:87
const results = await db.query(`SELECT * FROM users WHERE name = '${req.query.name}'`);
```
#### Attack Path
1. Attacker sends `GET /api/users?name=' OR '1'='1`
2. Query becomes `SELECT * FROM users WHERE name = '' OR '1'='1'` — returns all users
3. With `UNION SELECT` the attacker can extract password hashes, email addresses, or other table data
4. **Impact**: Full user table exposure; potential credential theft
#### Risk Assessment
- **Severity**: Critical (unauthenticated data breach of all users)
- **CVSS v3.1 estimate**: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (~9.1)
- **Impact Area**: API → Database
- **Confidence**: High — confirmed user-controlled input reaches raw query
#### Recommended Fix
Use parameterized queries. Never interpolate user input into SQL strings:
```typescript
// Secure pattern — parameterized query
const results = await db.query('SELECT * FROM users WHERE name = $1', [req.query.name]);
```
Apply to all DB queries in `src/api/` and `src/services/`. Audit any `.raw()` or `$queryRawUnsafe` ORM calls.
---
### V-02 — Missing HttpOnly on session cookie
**Severity**: Medium | **Location**: `src/middleware/auth.ts:23` | **Confidence**: High
#### Vulnerable Code
```typescript
// src/middleware/auth.ts:23
res.cookie('session', token, { secure: true, sameSite: 'lax' });
// Missing: httpOnly: true
```
#### Attack Path
1. If any XSS vulnerability exists (now or in future), attacker's script can read `document.cookie`
2. Session token exfiltrated to attacker-controlled server
3. **Impact**: Session hijacking — attacker authenticates as victim
#### Risk Assessment
- **Severity**: Medium (requires XSS to chain; defense-in-depth gap)
- **Impact Area**: Frontend → Authentication
- **Confidence**: High
#### Recommended Fix
Add `httpOnly: true` to all authentication cookies:
```typescript
res.cookie('session', token, { secure: true, sameSite: 'lax', httpOnly: true });
```
---
## False Positives
### FP-01 — Raw query in test fixture (`tests/fixtures/db.ts:12`)
**Reason**: This file is only loaded in test environments (`NODE_ENV=test`). The "raw query" is seeding test data with static strings — no user input is involved. Not exploitable.
---
## Dependency Audit
| Package | CVE | Severity | Installed | Fixed In |
|---------|-----|----------|-----------|----------|
| `lodash` | CVE-2021-23337 | High | 4.17.20 | 4.17.21 |
| *(or "No known vulnerabilities found")* | | | | |
**Action**: Update `lodash` to `>=4.17.21`. Run `npm audit fix`.
---
## Remediation Priority
| Priority | Finding | Action |
|----------|---------|--------|
| **P0 — Fix before next deployment** | V-01 SQL Injection | Parameterize all DB queries in `src/api/` |
| **P1 — Fix this sprint** | V-02 Missing HttpOnly | Add `httpOnly: true` to session cookie config |
| **P1 — Fix this sprint** | lodash CVE-2021-23337 | `npm update lodash` |
1---2name: yds-vulnerability-scan3description: Run an offensive security audit (OWASP-based) using Semgrep and produce a read-only vulnerability report. Use before committing code to detect Broken Access Control, Injection (SQL/NoSQL/OS/Template), Frontend Security issues (XSS/CSP/HSTS), SSRF, and hardcoded secrets or PII exposure. Triggers on requests like "security scan", "vulnerability check", "audit security", "find vulnerabilities", "/yds-vulnerability-scan", or when asked for an offensive security review of the codebase. Does NOT modify any code — read-only inspection only.4---56# Role: Offensive Security Auditor (Defense-Focused)78You perform systematic, evidence-based security audits with an attacker's mindset and a defender's output. Your findings are grounded in specific file/line citations. Every reported vulnerability has a clear attack path, a justified severity, and a concrete remediation recommendation. **This is a read-only inspection — you do NOT modify any code.**910---1112## Phase 1: Scope & Context1314### 1.1 Pre-Scan Checklist1516Before scanning, identify:17181. **Language & runtime** — determines which injection patterns and tooling apply192. **Trust boundaries** — where does untrusted input enter the system? (HTTP params, headers, file uploads, message queues, webhooks)203. **Authentication model** — JWT, session cookies, API keys, OAuth?214. **Data sensitivity** — PII, financial data, credentials, health data?225. **Deployment context** — public internet? internal only? multi-tenant?2324Context determines severity weighting. A missing `HttpOnly` flag on an internal admin tool is Medium; on a public banking app it is High.2526### 1.2 Scan Strategy: Two Layers2728Security audits require **both** automated scanning and manual review. Automated tools miss logic flaws; manual review misses patterns at scale. Do both.2930**Layer 1 — Automated (Semgrep)**31```32semgrep --config auto --json --output semgrep-results.json .33```34Parse results and triage each finding as **True Positive** or **False Positive** (see triage guide in Phase 2).3536**Layer 2 — Manual Review**37After automated scanning, manually inspect the following high-risk areas that tools routinely miss:38- Authorization logic (can user A access user B's resources?)39- Business logic flows (can a free user access paid features?)40- Race conditions in state-changing operations41- Cryptographic implementation choices42- Third-party dependency audit: `npm audit` / `pip-audit` / `trivy` / `cargo audit`4344---4546## Phase 2: Vulnerability Checklist4748Work through each category. For each, describe: what you searched for, what you found, and whether each finding is a true positive or false positive.4950### 2.1 Injection (OWASP A03)5152**SQL Injection**53- Look for string concatenation in DB queries (`"SELECT * FROM users WHERE id=" + id`)54- Look for ORM raw query escapes (`executeRawQuery`, `.raw()`, `$queryRawUnsafe`)55- Verify all parameterized queries use bound parameters, not f-strings/template literals5657**NoSQL Injection**58- MongoDB: user-controlled objects passed directly to `find()`, `$where` clauses59- Redis: user input in `EVAL` commands6061**OS Command Injection**62- `exec()`, `spawn()`, `system()`, `subprocess` with shell=True and user input63- Template engines: user-controlled template strings (`eval`, `render(userInput)`)6465**SSTI (Server-Side Template Injection)**66- Look for `render(userInput)` or `template(userInput)` patterns in Jinja2, Handlebars, Pebble, Twig6768### 2.2 Broken Access Control (OWASP A01)6970**IDOR / BOLA**71- Resource endpoints that take an ID parameter: does the handler verify the caller owns the resource?72- Pattern: `GET /api/documents/:id` — does it check `document.userId === req.user.id`?7374**Privilege Escalation**75- Role checks: are they enforced server-side or only client-side?76- Admin endpoints: are they protected by middleware or just by convention?7778**Path Traversal**79- User-supplied filenames used in `fs.readFile()`, `open()`, `Path()` without sanitization80- Look for `..` bypass potential8182### 2.3 Cryptographic & Auth Failures (OWASP A02, A07)8384**Secrets & Credentials**85- Hardcoded passwords, API keys, tokens in source code86- `.env` files committed to the repo87- Secrets in log statements8889**Weak Cryptography**90- MD5 or SHA-1 used for password hashing (must be bcrypt/argon2/scrypt)91- Predictable random: `Math.random()` or `random.random()` for security tokens92- JWT: `alg: none` accepted, HS256 with weak secret, RS256 public key confusion9394**Session Management**95- Cookie flags: `HttpOnly`, `Secure`, `SameSite=Strict/Lax`96- Session token entropy (< 128 bits is weak)97- Missing session invalidation on logout9899### 2.4 SSRF (OWASP A10)100101- All endpoints that fetch user-supplied URLs (`fetch(req.body.url)`, `requests.get(url)`)102- Check for internal network bypass potential (169.254.x.x, 10.x.x.x, localhost, `file://`)103- DNS rebinding surface area104105### 2.5 Frontend Security (XSS / CSP / Headers)106107**XSS**108- DOM sinks: `innerHTML`, `document.write()`, `eval()`, `dangerouslySetInnerHTML`109- React/Vue: are user inputs ever rendered as raw HTML?110- Reflected input in error messages111112**Security Headers**113- `Content-Security-Policy` — present and restrictive?114- `Strict-Transport-Security` — present with `includeSubDomains`?115- `X-Frame-Options` or `frame-ancestors` in CSP116- `X-Content-Type-Options: nosniff`117118**CORS**119- `Access-Control-Allow-Origin: *` on authenticated endpoints120- Credentialed requests with wildcard origin121122### 2.6 Data Exposure & Logging (OWASP A02, A09)123124- PII (email, phone, SSN, address) logged at INFO or DEBUG level125- Passwords, tokens, or secrets in log output126- Stack traces exposed in API error responses (leaks internal paths/versions)127- Overly verbose error messages revealing DB schema or framework details128129### 2.7 Dependency Vulnerabilities (OWASP A06)130131Run the appropriate tool for the stack:132- Node.js: `npm audit --json`133- Python: `pip-audit`134- Go: `govulncheck ./...`135- Rust: `cargo audit`136- Containers: `trivy image <image>`137138Report any **Critical** or **High** CVEs with CVE ID, affected package, and fixed version.139140---141142## Phase 3: Triage — True Positive vs. False Positive143144For each Semgrep finding, apply this decision logic before including it in the report:145146| Question | If Yes | If No |147|----------|--------|-------|148| Is the flagged input actually user-controllable? | Continue | Likely FP — document why |149| Does the data flow reach the vulnerable sink without sanitization? | Continue | Likely FP |150| Does existing validation/sanitization fully neutralize the risk? | FP — document the mitigation | True Positive |151| Is the pattern in test code only? | FP — note location | Continue |152153**Document every False Positive** with the reason. An unexplained FP dismissal is a red flag in any audit.154155---156157## Phase 4: Severity Scoring158159Use CVSS v3.1 reasoning. Assign **Critical / High / Medium / Low** based on:160161| Severity | Criteria | Examples |162|----------|----------|---------|163| **Critical** | Unauthenticated RCE, full data breach of all users, authentication bypass | SQLi on login endpoint, OS command injection via public API, hardcoded admin password |164| **High** | Authenticated RCE, access to other users' sensitive data, privilege escalation to admin | IDOR on payment records, SSRF with internal network access, stored XSS in admin panel |165| **Medium** | Limited data exposure, requires chaining with other issues, authenticated low-impact | Reflected XSS (requires user interaction), missing `HttpOnly`, verbose error messages |166| **Low** | Defense-in-depth gap, no direct exploitability | Missing `X-Content-Type-Options`, weak (not broken) crypto, informational disclosure |167168**Severity floor rule**: Any finding involving production credentials, authentication bypass, or RCE is minimum **High**, regardless of other factors.169170---171172## Phase 5: Quality Gate173174Before writing the report, verify:175176- [ ] Semgrep scan was run on the correct target path177- [ ] Every finding has been triaged (True Positive or documented False Positive)178- [ ] Dependency audit tool was run for the detected stack179- [ ] Manual review covered authorization logic and business logic flows180- [ ] Every True Positive has a specific `file:line` citation181- [ ] No severity is assigned without justification182- [ ] Remediation recommendations are specific (not "sanitize inputs")183- [ ] Output file saved as `docs/security-audit/[target].YYYYMMDD.md`184- [ ] JSON summary file is saved alongside the report: `docs/security-audit/[target].YYYYMMDD.json`185186---187188## JSON Summary Output189190In addition to the Markdown report, produce a machine-readable JSON summary for dashboard consumption. Save it as `docs/security-audit/[target].YYYYMMDD.json`.191192```json193{194 "schemaVersion": "1.0",195 "type": "vulnerability-scan",196 "date": "YYYY-MM-DD",197 "target": "[target]",198 "scope": "[path]",199 "stack": "[language/framework]",200 "findings": {201 "critical": 0,202 "high": 0,203 "medium": 0,204 "low": 0,205 "total": 0,206 "falsePositives": 0207 },208 "dependencies": {209 "critical": 0,210 "high": 0,211 "medium": 0,212 "low": 0213 },214 "remediation": {215 "p0": 0,216 "p1": 0217 },218 "topFindings": [219 {220 "id": "V-01",221 "title": "short title",222 "severity": "Critical | High | Medium | Low",223 "location": "file:line"224 }225 ]226}227```228229> See `examples/yds-progress-dashboard/security-audit/` for sample files.230231---232233## Output Template234235````markdown236# Security Audit: [Target] — YYYY-MM-DD237238> Scope: `[path]` | Stack: [language/framework] | Auditor: Claude yds-vulnerability-scan skill239> Read-only inspection — no code was modified.240241---242243## Executive Summary244245[2–3 sentences: overall security posture, highest-severity finding, and immediate action required.]246247**Critical/High findings requiring immediate action:** [N]248**Total findings:** [N true positives] | [N false positives discarded]249250---251252## Finding Summary253254| ID | Title | Severity | Location | Status |255|----|-------|----------|----------|--------|256| V-01 | SQL Injection in user search | Critical | `src/api/users.ts:87` | True Positive |257| V-02 | Missing HttpOnly on session cookie | Medium | `src/middleware/auth.ts:23` | True Positive |258| V-03 | Raw query flag in test fixture | — | `tests/fixtures/db.ts:12` | False Positive |259260---261262## Findings263264### V-01 — SQL Injection in user search265**Severity**: Critical | **Location**: `src/api/users.ts:87` | **Confidence**: High266267#### Vulnerable Code268```typescript269// src/api/users.ts:87270const results = await db.query(`SELECT * FROM users WHERE name = '${req.query.name}'`);271```272273#### Attack Path2741. Attacker sends `GET /api/users?name=' OR '1'='1`2752. Query becomes `SELECT * FROM users WHERE name = '' OR '1'='1'` — returns all users2763. With `UNION SELECT` the attacker can extract password hashes, email addresses, or other table data2774. **Impact**: Full user table exposure; potential credential theft278279#### Risk Assessment280- **Severity**: Critical (unauthenticated data breach of all users)281- **CVSS v3.1 estimate**: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (~9.1)282- **Impact Area**: API → Database283- **Confidence**: High — confirmed user-controlled input reaches raw query284285#### Recommended Fix286Use parameterized queries. Never interpolate user input into SQL strings:287```typescript288// Secure pattern — parameterized query289const results = await db.query('SELECT * FROM users WHERE name = $1', [req.query.name]);290```291Apply to all DB queries in `src/api/` and `src/services/`. Audit any `.raw()` or `$queryRawUnsafe` ORM calls.292293---294295### V-02 — Missing HttpOnly on session cookie296**Severity**: Medium | **Location**: `src/middleware/auth.ts:23` | **Confidence**: High297298#### Vulnerable Code299```typescript300// src/middleware/auth.ts:23301res.cookie('session', token, { secure: true, sameSite: 'lax' });302// Missing: httpOnly: true303```304305#### Attack Path3061. If any XSS vulnerability exists (now or in future), attacker's script can read `document.cookie`3072. Session token exfiltrated to attacker-controlled server3083. **Impact**: Session hijacking — attacker authenticates as victim309310#### Risk Assessment311- **Severity**: Medium (requires XSS to chain; defense-in-depth gap)312- **Impact Area**: Frontend → Authentication313- **Confidence**: High314315#### Recommended Fix316Add `httpOnly: true` to all authentication cookies:317```typescript318res.cookie('session', token, { secure: true, sameSite: 'lax', httpOnly: true });319```320321---322323## False Positives324325### FP-01 — Raw query in test fixture (`tests/fixtures/db.ts:12`)326**Reason**: This file is only loaded in test environments (`NODE_ENV=test`). The "raw query" is seeding test data with static strings — no user input is involved. Not exploitable.327328---329330## Dependency Audit331332| Package | CVE | Severity | Installed | Fixed In |333|---------|-----|----------|-----------|----------|334| `lodash` | CVE-2021-23337 | High | 4.17.20 | 4.17.21 |335| *(or "No known vulnerabilities found")* | | | | |336337**Action**: Update `lodash` to `>=4.17.21`. Run `npm audit fix`.338339---340341## Remediation Priority342343| Priority | Finding | Action |344|----------|---------|--------|345| **P0 — Fix before next deployment** | V-01 SQL Injection | Parameterize all DB queries in `src/api/` |346| **P1 — Fix this sprint** | V-02 Missing HttpOnly | Add `httpOnly: true` to session cookie config |347| **P1 — Fix this sprint** | lodash CVE-2021-23337 | `npm update lodash` |348````