๐ท๏ธ OWASP ZAP in the Real World: Tuning, Reports, and Quality Gates
Intro: ZAP is at its best when the team treats it like a practical DAST workbench, not a magic box. The tool can absolutely find useful issues in CI, but it needs sane scope, a clean auth model, tuned policies, and very deliberate expectations about what it will and will not catch.
What this page includes
- the handful of ZAP settings that matter most in practice
- where to click in the desktop UI and what the equivalent Docker or automation flow looks like
- report interpretation with seven representative findings
- GitLab quality gate patterns and DefectDojo-friendly outputs
Working assumptions
- the target is an application you own and are authorized to test
- the first objective is signal quality, not scanner drama
- DAST complements manual testing and code-side controls; it does not replace them
Figure: ZAP flow from discovery and context setup through reporting, gating, and findings ingestion.
Why teams struggle with ZAP
Most ZAP failures are not scanner-engine failures. They are setup failures:
- the wrong URLs are in scope;
- authentication is half-configured;
- the team uses a full active scan when a baseline or API scan was the right first move;
- default rules are left untouched, so noisy findings bury the real work;
- the report is generated before the passive scan queue drains;
- the release gate is defined as "zero alerts", which sounds strict but is usually immature and brittle.
That is why good ZAP usage starts with a simple rule: tune the scan to the app you actually have.
A quick mental model of the main scan modes
| Mode | When to use it | What it does well | Where teams misuse it |
|---|---|---|---|
| Baseline scan | Early CI checks on a running web app | Safe passive checks, quick smoke-level coverage | Treating it as a full penetration test |
| Full scan | Staging or pre-release environments | Spider + optional AJAX spider + active scan | Running it against unstable ephemeral preview apps |
| API scan | OpenAPI, SOAP, or GraphQL targets | Faster API-oriented coverage with less web UI noise | Pointing it at a browser-heavy site and expecting UI coverage |
| Desktop interactive use | Tuning, auth debugging, targeted verification | Visibility into requests, contexts, alerts, scripts | Replacing repeatable automation with manual clicking forever |
The settings that matter most
Authenticated contexts and session handling
A lot of practical ZAP failures happen because the scanner never reaches the real application state.
The scan โworked,โ but only against the anonymous login page.
When you need a context
Create an authenticated context when:
- the interesting flows exist only after login;
- role differences matter;
- CSRF tokens or custom headers are part of the normal app state;
- the app uses bearer tokens or header-based auth instead of a classic form login.
A practical sequence for authenticated ZAP automation
- define the in-scope hosts and paths;
- choose an authentication style:
- browser/form login
- script-based login
- static or dynamically refreshed bearer header
- define a test user;
- confirm the logged-in and logged-out indicators;
- run the spider or API scan in that context;
- only then run active checks that depend on authenticated state.
UI path that matters
In the desktop client, the quickest path is usually:
- Sites tree โ target โ Include in Context
- Contexts โ right click context โ Authentication
- Contexts โ Users
- Contexts โ Session Management
- Forced User Mode for debugging
Header-based session management
For APIs, a form-login flow is often the wrong abstraction.
If the app expects Authorization: Bearer ... or another custom header, use a header-based or script-assisted approach and make sure the token lifecycle is explicit.
Typical design choices:
- inject a fixed short-lived token only for test environments;
- mint a token in CI immediately before the scan;
- fail the scan fast when token acquisition breaks instead of silently scanning anonymously.
Automation note
If you already have a reliable OpenAPI-driven API target, prefer an API scan with explicit auth material over a browser-style scan that fakes a UI login it does not really need.
Practical warning signs
- the report contains mostly anonymous endpoints only;
- logout URLs keep getting hit during the spider phase;
- alert volume is tiny but coverage is also tiny;
- session cookies are present, but the app is still redirecting to login;
- the same scan behaves differently every run because token state is not deterministic.
1) Scope and Context
If you get scope wrong, nothing else matters.
Where to click
- Import โ Import an OpenAPI definition for API-driven scans
- Sites tree โ right click target โ Include in Context
- Edit โ Enable / Disable Forced User Mode once a context user is configured
Why it matters A clean context is what turns ZAP from a noisy crawler into a scanner that behaves like a test user with boundaries.
What to set
- include only the app, path, and hostnames you care about;
- exclude logout paths, destructive admin paths, third-party callbacks, and rate-limited endpoints you know will distort results;
- define a user when the app has real authentication and role-specific behavior.
2) Active Scan policy
Where to click
- Analyse โ Scan Policy Manager
- or Active Scan tab โ Scan Policy Manager
- then choose or clone a policy instead of blindly using the default
Why it matters A scan policy defines which rules run and how aggressively they run. In a real pipeline, one tuned policy is worth more than ten heroic post-scan meetings.
Good habit Create policies like:
- baseline-web-safe
- api-fast
- staging-active-authenticated
- deep-verification-manual
3) Active Scan concurrency
Where to click
- Tools โ Options โ Active Scan
Settings that usually matter
- Number of Hosts Scanned Concurrently
- Concurrent Scanning Threads per Host
- Persist Temporary HTTP Messages
Practical guidance
- keep concurrency modest in staging if the app is stateful or brittle;
- start conservative before increasing threads;
- only persist temporary messages if the team really needs request-by-request forensic review.
A lot of teams mistake "more threads" for "better coverage". In reality it often means more flaky sessions, more rate limiting, and worse reproducibility.
4) Input vectors
Where to click
- Tools โ Options โ Active Scan Input Vectors
- or New Scan โ Show Advanced Options โ Input Vectors
What this controls Which request elements ZAP will attack:
- query parameters
- POST data
- JSON or XML bodies
- path segments
- headers
- cookies
Practical guidance
- keep JSON and POST data enabled for APIs and modern web apps;
- be careful enabling aggressive header and cookie mutation on fragile apps;
- use custom input vectors only when the target has unusual data shapes.
5) Passive scanning
Where to click
- Tools โ Options โ Passive Scanner
- Tools โ Options โ Passive Scan Rules
Why it matters Passive scanning is usually the cheapest signal you can get. It is also where teams often ignore useful findings like missing security headers, cookie flags, cache behavior, and TLS-related issues.
Good tuning moves
- set Only scan messages in scope;
- cap Max alerts any rule can raise in CI so one noisy rule does not flood the report;
- set a sensible Max body size if the app returns large payloads.
6) AJAX spider
Where to click
- Attack โ AJAX Spider
- or the dialog from the relevant tab if the add-on is installed
When it helps Use it when the app is JavaScript-heavy and the normal spider misses routes.
When it hurts Do not turn it on by default for every pipeline. It is heavier, slower, and often unnecessary for API-centric or server-rendered applications.
7) OAST
Where to click
- Tools โ Options โ OAST
- choose the service used for out-of-band checks
When it matters This matters for vulnerabilities that show up only when the target makes a callback, DNS lookup, or other out-of-band interaction.
Practical note Only enable this when the team actually knows how to interpret the signal and where the callbacks go.
8) Reports
Where to click
- Report โ Generate Report
What to watch Do not generate the report too early. Wait for:
- passive scan queue to drain;
- active scan to finish;
- auth-related retries or failures to settle.
A sensible tuning pattern for a new target
- Baseline first. Confirm that the app is reachable, in scope, and not throwing auth or session errors.
- Import the API spec if one exists. This is usually faster and cleaner than trying to discover an API from traffic.
- Build a context and user. If the app requires login, stop pretending a public scan tells the whole story.
- Clone the policy. Disable the rules that are irrelevant, too destructive, or too noisy for the environment.
- Limit concurrency. Get a stable scan first. Speed comes later.
- Run one tuned active scan in staging and keep the artifacts.
- Use the findings to refine the gate, not the other way around.
Desktop clicks that are worth remembering
๐ Menu map: where to click in the desktop UI
- Tools โ Options
General scanner behavior, active scan settings, passive scanner, input vectors, OAST, and more. - Analyse โ Scan Policy Manager
Create, clone, import, export, and modify active scan policies. - Import โ Import an OpenAPI definition
Best first step for API-driven targets. - Edit โ Enable / Disable Forced User Mode
Useful once a context and test user are configured. - Report โ Generate Report
Generate HTML, Markdown, and other report formats through the report add-on. - Active Scan tab โ New Scan
Start a scan and optionally open advanced options for input vectors, policy selection, and user context.
Example: ZAP Automation Framework plan
env:
contexts:
- name: "staging-app"
urls:
- "https://staging.example.internal"
includePaths:
- "https://staging.example.internal/.*"
excludePaths:
- "https://staging.example.internal/logout.*"
- "https://staging.example.internal/admin/destructive/.*"
jobs:
- type: openapi
parameters:
apiFile: "openapi.yaml"
context: "staging-app"
targetUrl: "https://staging.example.internal"
- type: spider
parameters:
context: "staging-app"
maxDuration: 3
- type: activeScan-policy
parameters:
policyName: "staging-active-authenticated"
defaultStrength: "MEDIUM"
defaultThreshold: "MEDIUM"
- type: activeScan
parameters:
context: "staging-app"
policy: "staging-active-authenticated"
maxRuleDurationInMins: 3
maxScanDurationInMins: 20
- type: report
parameters:
template: "traditional-html-plus"
reportDir: "reports"
reportFile: "zap-report.html"
Why this plan is sane
- it imports the contract first;
- it spiders lightly before attacking;
- it uses an explicit named policy instead of whatever happened to be the desktop default;
- it produces a report artifact that can be archived or ingested elsewhere.
GitLab CI example
zap_api_scan:
stage: dast
image: ghcr.io/zaproxy/zaproxy:stable
script:
- mkdir -p zap
- cp snippets/zap/api-rules.conf zap/api-rules.conf
- zap-api-scan.py
-t openapi.yaml
-f openapi
-r zap/zap-report.html
-J zap/zap-report.json
-w zap/zap-report.md
-c zap/api-rules.conf
artifacts:
when: always
paths:
- zap/
Rule file example
10020 WARN (X-Frame-Options Header Not Set)
10021 WARN (X-Content-Type-Options Header Missing)
10038 IGNORE (Content Security Policy Header Not Set)
40018 FAIL (SQL Injection)
40012 FAIL (Cross Site Scripting)
That rule file is where quality gate maturity starts to show. Serious exploit-class findings fail the job. Lower-value or context-dependent hygiene issues stay visible without blocking the release.
Example report with seven representative issues
Below is a sample-style summary, not a claim that ZAP will always label your application exactly this way.
| Risk | Issue | What it usually means | Why it matters | Short fix |
|---|---|---|---|---|
| High | SQL Injection | User input reaches a query without safe parameterization | Data theft, auth bypass, integrity loss | Parameterized queries, ORM safe APIs, server-side validation |
| High | Cross-Site Scripting | Untrusted input is rendered into HTML or JS | Session theft, phishing, browser-side compromise | Output encoding, templating discipline, CSP as defense-in-depth |
| Medium | Missing Anti-CSRF Tokens | State-changing requests lack CSRF protection | Account-changing actions can be triggered cross-site | CSRF tokens, SameSite cookies, origin/referrer validation |
| Medium | Authentication Request Identified | Login flow is visible and may need deeper manual review | Not a vulnerability by itself, but a pivot for auth testing | Validate MFA, brute-force controls, lockout, logging |
| Medium | CSP Header Missing or Weak | Browser-side policy is absent or overly permissive | XSS blast radius grows when CSP is weak | Deploy a realistic CSP and tighten incrementally |
| Low | X-Content-Type-Options Missing | Browser MIME sniffing is still allowed | Browser confusion and file handling issues | Add X-Content-Type-Options: nosniff |
| Low | Cookie Without Secure / HttpOnly | Session cookies lack transport or script protections | Session theft risk rises, especially with other weaknesses | Set Secure, HttpOnly, and review SameSite |
How to read a ZAP report like an adult
The wrong way:
- sort by count;
- panic because the report says 41 alerts;
- fail the release;
- force engineers to argue with the scanner.
The right way:
- Group by exploitability and trust.
- Separate direct exploit findings from hygiene findings.
- Check for route concentration. Ten alerts on one endpoint is not ten independent problems.
- Confirm auth context. Half the report can be meaningless if the scanner was logged out.
- Decide what belongs in the gate and what belongs in the backlog.
๐งช Example triage notes for a ZAP report
- Fail the pipeline immediately
- confirmed SQL injection
- confirmed reflected or stored XSS on authenticated paths
- SSRF or unsafe server-side callback behavior
- Block the release only for internet-facing assets
- missing secure cookie flags on session cookies
- weak TLS or downgrade paths
- Track, but do not fail by default
- missing clickjacking header on an internal-only tool
- CSP absent on an API gateway that serves no browser content
Quality gate pattern for ZAP
A mature ZAP gate is usually phrased like this:
- fail on confirmed or high-confidence exploit-class issues;
- warn on hygiene findings unless the asset is customer-facing or subject to a hard standard;
- ignore findings that are provably irrelevant for that target class.
That is how DAST becomes a delivery control instead of a permanent argument.
Common tuning mistakes
- Scanning production first instead of proving the setup in staging.
- No context or auth model, which makes the scan look broad but shallow.
- Unlimited concurrency on stateful apps.
- Using the full scan everywhere when the API scan or baseline scan was enough.
- Blindly trusting default rules instead of managing policy.
- Generating reports too early before passive scanning drains.
- Failing on all alerts instead of failing on the findings that actually change release risk.
- No exclusions for logout, destructive admin routes, or third-party traffic.
- Treating DAST as coverage proof for business logic bugs.
- Running it once and never tuning it again.
DefectDojo-friendly outputs
If you are already standardizing on DefectDojo, keep one clean path:
- emit JSON or XML that your ingestion process supports;
- reimport into the same product/engagement when this is the same target and same scan class;
- avoid inventing a new product entry for every pipeline run.
Cross-links
- Security Quality Gates and Release Blocking
- DefectDojo Integration Patterns
- Mobile Testing Quality Gates and DefectDojo Integration
Official references worth keeping open
- ZAP desktop Tools โ Options and scanner dialogs
- ZAP Analyse โ Scan Policy Manager
- ZAP Report โ Generate Report
- ZAP Docker scripts: baseline, full scan, API scan
- ZAP Automation Framework docs
v3.2 modernization note
For a cleaner present-day testing model, pair this page with ๐ Web Application Security Testing and Gate Patterns. The practical shift is to split fast passive scans, preview-environment checks, and deep manual testing instead of treating all DAST as one gate.
Authenticated scanning deserves its own playbook
Most teams eventually learn this the hard way: a โsuccessfulโ anonymous ZAP scan can still miss the majority of the real application surface.
Use authenticated ZAP when:
- interesting routes exist only after login;
- roles matter;
- token, cookie, or CSRF behavior is part of the risk picture.
Continue with ๐ OWASP ZAP Authenticated Scanning and Session Management for:
- context design;
- session management choices;
- API and Automation Framework examples;
- token-lifecycle failure modes in CI.
Sample packaged artifact
2026 update: report generation and evidence handling
Older ZAP material often ends with โgenerate the HTML report.โ That is still useful, but in a mature delivery system the report is only one artifact.
Treat ZAP output as a package of evidence:
- a human-readable HTML or Markdown report;
- JSON for machine handling and downstream ingestion;
- the rule config or alert filter that explains what was intentionally failed, warned, or ignored;
- the AF plan or packaged-scan command proving how the scan was run;
- pipeline metadata that ties the scan to a release candidate, review app, or staging deployment.
This is where ZAP becomes compatible with release evidence, audit-friendly reporting, and platform-team triage rather than remaining a standalone analyst tool.
For the broader operating model, read: