zap
Expert agent for OWASP ZAP open-source DAST. Covers active/passive scanning, spidering (traditional+AJAX), authentication handling, API scanning (OpenAPI/GraphQL/SOAP), automation framework (YAML), ad
SKILL.mdplugins/security/skills/zap/SKILL.md
GitHubOWASP ZAP
This skill covers OWASP ZAP (Zed Attack Proxy), the world's most widely used open-source web security scanner. ZAP is maintained by the ZAP core team and supported by the open-source community. It is licensed under Apache 2.0.
How to Approach Tasks
- Identify the mode:
- Headless/automated -- Docker, CI/CD, GitHub Actions (most common for DevSecOps)
- Desktop GUI -- Interactive testing, exploration, manual intercepting proxy
- Daemon mode -- API-driven automation from external scripts
- Identify the scan type:
- Baseline scan -- Passive scan only (safe for production, no active attacks)
- Full scan -- Active + passive (for test environments only)
- API scan -- Schema-driven API testing (OpenAPI, GraphQL, SOAP)
- Identify authentication requirements -- Most meaningful scans require authentication.
ZAP Core Concepts
Scan Types
Baseline scan (zap-baseline.py):
- Passive scanning only — no attack payloads sent
- Analyzes responses for issues visible without modification
- Detects: missing security headers, cookie flags, information disclosure, TLS issues
- Safe to run against production (read-only behavior)
- Fast: 1-5 minutes depending on application size
Full scan (zap-full-scan.py):
- Active + passive scanning
- Sends attack payloads (SQL injection, XSS, etc.)
- Requires authorized test environment (NOT production)
- Duration: 15 min - 4 hours depending on application size and rules
API scan (zap-api-scan.py):
- Schema-driven: import OpenAPI/Swagger, GraphQL introspection, or WSDL
- Tests all API endpoints and parameters defined in the schema
- Combines active scan with schema-aware testing
- Best coverage for API-centric applications
Spider vs. AJAX Spider
Traditional Spider:
- Follows HTML links, form actions
- Fast and lightweight
- Limited coverage for JavaScript-heavy applications
AJAX Spider:
- Uses a real browser (Chromium or Firefox via Selenium/Playwright)
- Executes JavaScript, discovers dynamically rendered content
- Slower but necessary for SPAs (React, Angular, Vue)
- Requires a browser to be available
Use both for maximum coverage.
Docker Usage (Recommended for CI/CD)
ZAP Docker images are the standard way to use ZAP in CI/CD pipelines:
# Pull latest stable image
docker pull ghcr.io/zaproxy/zaproxy:stable
# Baseline scan (passive only)
docker run --rm -v $(pwd):/zap/wrk ghcr.io/zaproxy/zaproxy:stable \
zap-baseline.py \
-t https://app.example.com \
-r report.html \
-J report.json \
-I # Do not return failure status (informational mode)
# Full scan (active)
docker run --rm -v $(pwd):/zap/wrk ghcr.io/zaproxy/zaproxy:stable \
zap-full-scan.py \
-t https://test.example.com \
-r full-report.html \
-J full-report.json
# API scan (OpenAPI)
docker run --rm -v $(pwd):/zap/wrk ghcr.io/zaproxy/zaproxy:stable \
zap-api-scan.py \
-t https://api.example.com/openapi.json \
-f openapi \
-r api-report.html
Important flags:
-tTarget URL-rHTML report output file-JJSON report output file-xXML report output file-IInformational (don't fail on alerts)-lAlert level to fail on: PASS, IGNORE, WARN, FAIL (default: WARN)-cConfig file (rules configuration)-zAdditional ZAP command line options-dEnable debug logging
ZAP Automation Framework
The Automation Framework (AF) is the modern way to configure and run ZAP. It uses a YAML plan file to define the full scan workflow.
Plan File Structure
# zap-automation-plan.yaml
---
env:
contexts:
- name: "My Application"
urls:
- "https://app.example.com"
includePaths:
- "https://app.example.com.*"
excludePaths:
- "https://app.example.com/logout.*"
authentication:
method: "form"
parameters:
loginPageUrl: "https://app.example.com/login"
loginRequestData: "username={%username%}&password={%password%}"
loginPageWait: 2
verification:
method: "response"
loggedInRegex: "\\QWelcome\\E"
loggedOutRegex: "\\QPlease login\\E"
pollFrequency: 60
pollUnits: requests
users:
- name: "test-user"
credentials:
username: "dast@example.com"
password: "${DAST_PASSWORD}"
parameters:
failOnError: true
failOnWarning: false
progressToStdout: true
jobs:
- type: passiveScan-config
parameters:
maxAlertsPerRule: 10
scanOnlyInScope: true
- type: spider
parameters:
context: "My Application"
user: "test-user"
maxDuration: 5 # minutes
maxCrawlDepth: 10
- type: ajaxSpider
parameters:
context: "My Application"
user: "test-user"
maxDuration: 5 # minutes
- type: passiveScan-wait
parameters:
maxDuration: 5 # minutes
- type: activeScan
parameters:
context: "My Application"
user: "test-user"
maxRuleDurationInMins: 5
maxScanDurationInMins: 60
policy: "Default Policy"
- type: report
parameters:
template: traditional-html
reportDir: "/zap/wrk"
reportFile: "zap-report"
risks:
- high
- medium
- low
- informational
Running the Automation Framework
# Desktop GUI
zap.sh -cmd -autorun zap-automation-plan.yaml
# Docker
docker run --rm -v $(pwd):/zap/wrk ghcr.io/zaproxy/zaproxy:stable \
zap.sh -cmd -autorun /zap/wrk/zap-automation-plan.yaml
# With environment variable substitution
DAST_PASSWORD=secret docker run --rm \
-e DAST_PASSWORD \
-v $(pwd):/zap/wrk \
ghcr.io/zaproxy/zaproxy:stable \
zap.sh -cmd -autorun /zap/wrk/zap-automation-plan.yaml
Available Job Types
| Job Type | Purpose |
|---|---|
spider |
Traditional link-following spider |
ajaxSpider |
JavaScript-aware browser spider |
passiveScan-config |
Configure passive scan rules |
passiveScan-wait |
Wait for passive scanning to complete |
activeScan |
Run active vulnerability scan |
activeScan-config |
Configure active scan policies |
openapi |
Import OpenAPI/Swagger definition |
graphql |
Import GraphQL schema |
soap |
Import WSDL |
report |
Generate report |
outputSummary |
Print summary to stdout |
requestor |
Send specific HTTP requests |
script |
Run ZAP scripts (JavaScript, Python, etc.) |
delay |
Wait for specified duration |
exitStatus |
Control exit code behavior |
Authentication Configuration
ZAP supports multiple authentication methods. Configure under Context → Authentication.
Form-Based Authentication
authentication:
method: "form"
parameters:
loginPageUrl: "https://app.example.com/login"
loginRequestData: "email={%username%}&password={%password%}&_csrf={%csrf%}"
loginPageWait: 1
verification:
method: "response"
loggedInRegex: "\\QDashboard\\E"
loggedOutRegex: "\\QSign In\\E"
Bearer Token (Header Injection)
authentication:
method: "header"
parameters:
headerValue: "Authorization: Bearer ${API_TOKEN}"
For tokens that need to be obtained dynamically (OAuth), use a script-based authentication:
authentication:
method: "script"
parameters:
script: "/zap/wrk/oauth-auth.js"
scriptEngine: "Graal.js"
Session Management
ZAP needs to know how sessions are maintained (cookies, tokens, headers):
sessionManagement:
method: "cookie" # cookie | httpAuthSessionManagement | scriptBasedSessionManagement
For JWT bearer tokens:
sessionManagement:
method: "script"
parameters:
script: "/zap/wrk/jwt-session.js"
API Scanning
OpenAPI / Swagger
- type: openapi
parameters:
apiFile: "/zap/wrk/openapi.yaml" # Local file
# apiUrl: "https://api.example.com/openapi.json" # or remote URL
targetUrl: "https://api.example.com"
context: "My API"
# Docker command
docker run --rm -v $(pwd):/zap/wrk ghcr.io/zaproxy/zaproxy:stable \
zap-api-scan.py \
-t https://api.example.com/v1/openapi.json \
-f openapi \
-r api-report.html \
-z "-config replacer.full_list(0).description=auth \
-config replacer.full_list(0).enabled=true \
-config replacer.full_list(0).matchtype=REQ_HEADER \
-config replacer.full_list(0).matchstr=Authorization \
-config replacer.full_list(0).replacement=Bearer\ ${TOKEN}"
GraphQL
- type: graphql
parameters:
endpoint: "https://api.example.com/graphql"
schemaUrl: "https://api.example.com/graphql?sdl"
# or schemaFile: "/zap/wrk/schema.graphql"
maxArgsDepth: 5
maxQueryDepth: 5
queryGenEnabled: true
GraphQL ZAP testing covers:
- Introspection enabled (information disclosure)
- Mutation injection testing
- Deep query DoS probing
- Authorization on individual fields/types
Alert Rules Configuration
Control which rules fire and at what severity.
Rules Configuration File
# zap-rules.tsv (tab-separated)
# Rule ID Status Threshold Strength
10016 IGNORE MEDIUM DEFAULT # Web Browser XSS Protection
10017 IGNORE MEDIUM DEFAULT # Cross-Domain JavaScript Source File Inclusion
10021 WARN LOW DEFAULT # X-Content-Type-Options Header Missing
10038 FAIL HIGH DEFAULT # Content Security Policy (CSP) Header Not Set
40012 FAIL HIGH DEFAULT # Cross Site Scripting (Reflected)
40014 FAIL HIGH DEFAULT # Cross Site Scripting (Persistent)
40018 FAIL HIGH DEFAULT # SQL Injection
Reference with -c rules-config.tsv in the scan command.
Common Rules by ID
| ID | Rule Name | Type |
|---|---|---|
| 10016 | Web Browser XSS Protection | Passive |
| 10021 | X-Content-Type-Options | Passive |
| 10038 | Content Security Policy | Passive |
| 10039 | X-Frame-Options | Passive |
| 10054 | Cookie with SameSite Attribute | Passive |
| 10096 | Timestamp Disclosure | Passive |
| 40012 | Cross Site Scripting (Reflected) | Active |
| 40014 | Cross Site Scripting (Persistent) | Active |
| 40018 | SQL Injection | Active |
| 40019 | SQL Injection (MySQL) | Active |
| 90019 | Server Side Code Injection | Active |
| 90020 | Remote OS Command Injection | Active |
| 20019 | External Redirect | Active |
| 30001 | Buffer Overflow | Active |
GitHub Actions Integration
Official ZAP GitHub Actions exist for baseline, full, API, and authenticated (Automation Framework) scans, plus a pattern for uploading SARIF results to the GitHub Security tab. See references/github-actions.md for full workflow YAML for each.
Add-ons (Marketplace)
ZAP's functionality is extended via add-ons. Manage via Tools → Marketplace.
| Add-on | Purpose |
|---|---|
| Active Scan Rules | Core active scan rules (required) |
| Passive Scan Rules | Core passive scan rules (required) |
| AJAX Spider | JavaScript-aware spidering |
| OpenAPI Support | OpenAPI/Swagger import |
| GraphQL Support | GraphQL schema import and testing |
| DOM XSS Active Scan Rule | DOM-based XSS detection |
| Fuzzer | Advanced fuzzing with payload lists |
| Retire.js | Vulnerable JavaScript library detection |
| SOAP Support | WSDL import for SOAP services |
| Technology Detection | Fingerprint web technologies |
| Selenium Integration | Browser automation |
| HUD (Heads Up Display) | In-browser security testing overlay |
ZAP API (Daemon Mode)
ZAP can run as a daemon with an HTTP API for programmatic control:
# Start ZAP as daemon
zap.sh -daemon -host 127.0.0.1 -port 8090 -config api.addrs.addr.name=127.0.0.1 -config api.addrs.addr.enabled=true
# Use API via Python client
pip install python-owasp-zap-v2.4
python3 << 'EOF'
from zapv2 import ZAPv2
zap = ZAPv2(apikey='your-api-key', proxies={'http': 'http://127.0.0.1:8090', 'https': 'http://127.0.0.1:8090'})
# Spider
scan_id = zap.spider.scan('https://app.example.com')
while int(zap.spider.status(scan_id)) < 100:
time.sleep(5)
# Active scan
scan_id = zap.ascan.scan('https://app.example.com')
while int(zap.ascan.status(scan_id)) < 100:
time.sleep(10)
# Get alerts
alerts = zap.core.alerts(baseurl='https://app.example.com')
for alert in alerts:
print(f"{alert['risk']}: {alert['name']} - {alert['url']}")
EOF
Common Issues
Authentication failing in automated scan:
- Use ZAP GUI first to manually verify the login sequence works
- Check
loggedInRegexpattern — test with regex tester before using in config - Add
loginPageWaittime if the login page loads slowly - CSRF tokens: use script-based auth to extract token from login page before submitting
AJAX Spider not working in Docker:
- Headless browser (Chromium) must be available in container
- Use
ghcr.io/zaproxy/zaproxy:stable(includes browser) not bare Java image - Add
--no-sandboxChrome flag in Docker environments
Active scan taking too long:
- Set
maxScanDurationInMinsto limit total scan time - Set
maxRuleDurationInMinsto limit per-rule time - Use
activeScan-configto enable only critical-severity rules for PR scans - Reduce spider depth (
maxCrawlDepth)
Too many alerts from false positives:
- Create a
rules.tsvfile to IGNORE noisy rules - Tune per-rule threshold (WARN vs. FAIL vs. IGNORE)
- Check if alerts are from out-of-scope URLs — configure scope more tightly
Reference Files
Load these for deep knowledge on specific topics:
references/github-actions.md— Full GitHub Actions workflow YAML for baseline, full, API, and authenticated ZAP scans, plus SARIF upload to the GitHub Security tab.