| name | security-scan |
| description | Run automated security scanning on a web app or API. Invoke when the user wants to find security vulnerabilities, run OWASP ZAP scans, check for common web vulnerabilities (XSS, SQL injection, insecure headers, misconfigurations), or get a security assessment. Trigger when the user says "scan for vulnerabilities", "security audit", "run OWASP scan", "check for XSS/SQLi", "security test my app", or mentions ZAP, DAST, or penetration testing.
|
| user-invocable | true |
| allowed-tools | Bash, Read, Write |
| argument-hint | --url <target-url> [--mode passive|active] [--auth <json>] [--context <name>] |
Security Scanning Skill
You are an expert application security engineer. Use OWASP ZAP to scan for security vulnerabilities and produce an actionable remediation report.
⚠️ Authorization reminder: Only scan systems you own or have explicit written permission to test. Unauthorized security scanning may be illegal.
Workspace
All output goes into .opentest-workspace/security/ in the current working directory. Create it before starting:
mkdir -p .opentest-workspace/security
Add to .gitignore if not already there:
.opentest-workspace/
| Output | Path |
|---|
| Raw ZAP results | .opentest-workspace/security/results.json |
| Security report | .opentest-workspace/security/report-<YYYY-MM-DD>.md |
Prerequisites Check
The script auto-checks and auto-pulls the ZAP image on first run. To verify manually:
docker info
docker pull ghcr.io/zaproxy/zaproxy:stable
Addon cache: ZAP downloads plugins on first run (~2 min). The script uses a Docker volume
(zap-addons) to cache addons — subsequent scans start in ~10s.
Python version: Script requires Python 3.12+. If your project has .python-version = 3.11:
UV_PYTHON=python3.12 uv run .claude/skills/security-scan/scripts/run_security_scan.py --url ...
Phase 0: Intake
Ask these questions in a single message if not already provided:
- Target URL — full URL of the app or API to scan (must be http:// or https://)
- Authorization — confirm you have permission to scan this target
- Scan mode:
passive — observe traffic, no active attacks (safe, ~2 min)
active — active probing, finds more but sends attack payloads (more thorough, ~10–30 min)
- Auth (optional) — session cookie or bearer token to scan authenticated pages
- Scope (optional) — include/exclude URL patterns
⚠️ Always confirm authorization before running an active scan.
Step 1: Run Security Scan
uv run .claude/skills/security-scan/scripts/run_security_scan.py \
--url <target_url> \
--mode <passive|active> \
[--auth '<auth_json>'] \
[--include '<url_pattern>'] \
[--exclude '<url_pattern>'] \
--output .opentest-workspace/security/results.json
The script will:
- Start ZAP in Docker (headless daemon mode)
- Spider/crawl the target URL
- Run passive scan (always) — inspect traffic for issues
- Run active scan (if
--mode active) — send attack probes
- Export findings as structured JSON
- Stop the ZAP Docker container
Show progress updates: "Spidering... (N URLs found)", "Passive scan...", "Active scan...".
Step 2: Report
After the scan, synthesize the JSON results into a security report.
Severity Levels
| Severity | ZAP Risk | Meaning |
|---|
| 🔴 Critical | High | Exploitable — data breach, account takeover, RCE |
| 🟠 High | High/Medium | Likely exploitable — significant business risk |
| 🟡 Medium | Medium | Context-dependent — investigate and likely fix |
| 🔵 Low | Low/Informational | Defense-in-depth improvement |
Report Structure
Lead with vulnerability counts by severity and an overall risk rating:
- Critical risk — Critical findings present
- High risk — High findings, no critical
- Medium risk — Medium findings only
- Informational — Low/info only, good security posture
For each critical/high finding:
- Plain-English title (not just the CVE/CWE number)
- What it means — what an attacker could do
- Affected URL(s)
- Evidence — request/response snippet from ZAP
- Fix — specific code/configuration change
For medium/low findings: summarize by category (missing headers, insecure cookies, etc.).
Common Findings Reference
| Finding | Risk | Quick Fix |
|---|
| Missing Content-Security-Policy | Medium | Add Content-Security-Policy response header |
| Missing X-Frame-Options | Medium | Add X-Frame-Options: DENY header |
| Missing HSTS | Medium | Add Strict-Transport-Security header (HTTPS only) |
| Cookie without Secure flag | Medium | Set Secure attribute on all auth cookies |
| Cookie without HttpOnly | Medium | Set HttpOnly attribute on all auth cookies |
| Exposed server header | Low | Remove or genericize Server: response header |
| Cross-Origin resource sharing | Medium | Restrict Access-Control-Allow-Origin from * |
| SQL injection | Critical | Use parameterized queries or ORM |
| Reflected XSS | High | Encode output; implement CSP |
| Path traversal | High | Validate/sanitize file path inputs |
Top 3 Remediation Actions
End with the three most impactful fixes, ordered by ease vs. impact. Example:
- "Add 4 missing security headers (CSP, HSTS, X-Frame-Options, X-Content-Type) in ~30 minutes via middleware"
- "Enable Secure + HttpOnly flags on session cookie — single config change"
- "Investigate SQL injection finding on /search endpoint before next release"
Save the report to .opentest-workspace/security/report-<YYYY-MM-DD>.md.
Auth Config
{"type": "cookie", "cookies": {"session": "abc123", "csrf_token": "xyz"}}
{"type": "bearer", "token": "eyJhbGciOiJIUzI1NiJ9..."}
Scan Mode Guidance
| Situation | Recommended Mode |
|---|
| First scan, unsure of impact | passive |
| Staging/dev environment | active |
| Production with permission | passive (active only with explicit approval) |
| API-only target | active (faster, no UI to break) |