--- name: vulnerability-scan description: > 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", "/vulnerability-scan", or when asked for an offensive security review of the codebase. Does NOT modify any code — read-only inspection only. --- # 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: 1. **Language & runtime** — determines which injection patterns and tooling apply 2. **Trust boundaries** — where does untrusted input enter the system? (HTTP params, headers, file uploads, message queues, webhooks) 3. **Authentication model** — JWT, session cookies, API keys, OAuth? 4. **Data sensitivity** — PII, financial data, credentials, health data? 5. **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 ` 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: - [ ] Semgrep scan was run on the correct target path - [ ] Every finding has been triaged (True Positive or documented False Positive) - [ ] Dependency audit tool was run for the detected stack - [ ] Manual review covered authorization logic and business logic flows - [ ] Every True Positive has a specific `file:line` citation - [ ] No severity is assigned without justification - [ ] Remediation recommendations are specific (not "sanitize inputs") - [ ] Output file saved as `docs/security-audit.[target].YYYYMMDD.md` --- ## Output Template ````markdown # Security Audit: [Target] — YYYY-MM-DD > Scope: `[path]` | Stack: [language/framework] | Auditor: Claude 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` | ````