๐ท๏ธ OWASP ZAP and DAST Modernization Patterns
Intro: Modern DAST is less about pointing a scanner at a URL and more about choosing the right scan mode, keeping scope tight, making authentication deterministic, and turning reports into repeatable evidence. ZAP remains one of the strongest flexible DAST workbenches for teams that need both desktop tuning and CI-friendly packaged scans.
What this page includes
- how to choose between baseline, API, full, and automation-framework flows
- what older ZAP habits should be retired
- practical GitLab CI examples for baseline and API-first DAST
- how to keep DAST useful instead of ceremonial
The modern starting point
In 2026, a practical ZAP strategy usually looks like this:
- Baseline scan for low-friction passive checks and early signal.
- API scan for OpenAPI, SOAP, or GraphQL-defined targets.
- Automation Framework when auth, context, or orchestration logic becomes non-trivial.
- Full active scan only where the environment can tolerate it and the scope is deliberate.
This is a better model than the old habit of treating a single heavy scan profile as the default answer for every application.
Legacy vs current DAST habits
| Older habit | Why it breaks down | Better current pattern |
|---|---|---|
| run one big full scan everywhere | slow, noisy, brittle, bad for unstable environments | separate baseline, API, and deeper active scans by purpose |
| treat anonymous crawling as โgood enoughโ | misses the real application surface behind auth | use context-aware authenticated scanning |
| define the quality gate as โzero findingsโ | creates scanner theatre and brittle pipelines | use config files, progress files, and rule-level fail criteria |
| scan browser-heavy apps only through generic crawling | misses intended flows and API specifics | combine API definitions, context scoping, and selective browser-heavy coverage |
| assume scan success equals coverage success | many successful scans cover only login pages or public shells | validate auth state, in-scope URLs, and discovered endpoints explicitly |
Choosing the right scan mode
Baseline scan
Use the baseline scan when you want:
- passive checks only;
- short CI runtime;
- low risk to the target;
- early signal on headers, cookies, cache behavior, and similar issues.
Good use cases
- merge request smoke scanning;
- recurring scheduled scans of non-destructive paths;
- public web entry points.
API scan
Use API scan when the target is defined by:
- OpenAPI,
- SOAP,
- GraphQL.
This is usually the most efficient DAST mode for modern APIs because it uses the API definition directly instead of pretending the scanner must rediscover everything via HTML routes.
Good use cases
- API-first products;
- internal services with a stable OpenAPI spec;
- GitLab pipelines where the definition can be produced or fetched reliably.
Full scan
Use a full active scan when:
- the environment is intended for deep testing;
- the team has accepted longer runtime and more scan aggressiveness;
- auth and context are already known to be stable.
Do not make this the default for every commit pipeline.
Automation Framework
Use the Automation Framework when:
- you need reusable context and auth orchestration;
- the app uses more than one auth mode or role;
- you need cleaner reproducibility than ad hoc command flags can provide.
The biggest practical reasons ZAP under-delivers
1) In-scope definition is weak
The scan wanders into logout paths, third-party callbacks, or unrelated hosts.
2) Authentication is not deterministic
The scanner starts logged in, becomes anonymous mid-run, and the team never notices.
3) The wrong scan type is selected
A UI-heavy full scan gets used on a pure API, or a baseline scan is expected to act like a penetration test.
4) Rules are not tuned
One noisy rule floods the report and the real findings get normalized away.
5) The environment is wrong
A scan meant for staging gets fired at a production-like path without proper rate-limiting expectations or legal clarity.
A practical GitLab baseline scan job
zap_baseline:
stage: security
image: ghcr.io/zaproxy/zaproxy:stable
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
script:
- mkdir -p reports/zap
- |
zap-baseline.py \
-t "$DAST_TARGET_URL" \
-c .zap/zap-baseline.conf \
-J reports/zap/baseline.json \
-r reports/zap/baseline.html \
-w reports/zap/baseline.md
artifacts:
when: always
paths:
- reports/zap/
expire_in: 14 days
Why this is a good default
- stable packaged scan;
- explicit config file;
- artifacts retained for review;
- low-friction enough for MR pipelines.
A practical GitLab API scan job
zap_api_scan:
stage: security
image: ghcr.io/zaproxy/zaproxy:stable
needs: [deploy_review]
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_PIPELINE_SOURCE == "schedule"'
script:
- mkdir -p reports/zap
- |
zap-api-scan.py \
-t "$OPENAPI_URL" \
-f openapi \
-c .zap/zap-api.conf \
-J reports/zap/api.json \
-r reports/zap/api.html
artifacts:
when: always
paths:
- reports/zap/
expire_in: 14 days
Why this works well
- it treats the API definition as a first-class test input;
- it keeps the DAST job tied to a known deployed review target;
- it is more reliable for API-first systems than generic crawling.
A minimal Automation Framework file pattern
env:
contexts:
- name: app
urls:
- ${TARGET_URL}
includePaths:
- ${TARGET_URL}.*
authentication:
method: manual
sessionManagement:
method: headers
users:
- name: ci-user
credentials:
headers:
Authorization: Bearer ${AUTH_TOKEN}
jobs:
- type: openapi
parameters:
apiFile: ${OPENAPI_FILE}
context: app
targetUrl: ${TARGET_URL}
- type: passiveScan-wait
- type: report
parameters:
template: traditional-html
reportFile: zap-report.html
Why this pattern matters
It makes three things explicit:
- scope,
- auth state,
- reporting.
That alone eliminates a large portion of โsuccessful but uselessโ DAST runs.
Baseline config and progress files
Config file
Use config files to control which rules are:
FAILWARNIGNORE
This is the difference between a usable release gate and scanner chaos.
Progress file
Use a progress file when a finding is:
- already known,
- tracked,
- in remediation,
- intentionally not release-blocking yet.
That keeps CI honest without pretending mature teams instantly remediate every class of issue.
Controller-driven ZAP automation safety notes
If ZAP is triggered from Jenkins, AWX, or another automation controller:
- do not disable the ZAP API key unless the environment is tightly isolated for a temporary lab;
- do not expose the ZAP API to broad networks with allow-all patterns;
- prefer reviewed job definitions, parameterized targets, and retained artifacts;
- keep authentication tokens in secret stores or controller credentials, not in shell history or copied URLs.
How to keep DAST reports useful
Good gate style
- fail on a small set of high-confidence conditions;
- warn on broader hygiene findings;
- keep a progress file for actively tracked issues;
- store artifacts for trend review and evidence.
Bad gate style
- fail on every alert;
- compare alert counts without considering scope;
- ignore whether auth or coverage succeeded;
- treat HTML output alone as evidence.
What to measure besides findings
A useful DAST program also tracks:
- number of authenticated endpoints reached;
- number of in-scope URLs discovered;
- percentage of scans that completed with valid auth;
- mean time to tune noisy rules;
- number of tracked accepted or in-progress findings in progress files.
These operational metrics often matter more than raw alert counts.
Recommended ZAP snippet pack for this KB
.zap/zap-baseline.conf.zap/zap-api.conf.zap/authenticated-api.yaml.gitlab/ci/zap-baseline.yml.gitlab/ci/zap-api.yml
Cross-links
- OWASP ZAP in the Real World: Tuning, Reports, and Quality Gates
- OWASP ZAP Authenticated Scanning and Session Management
- API Testing, Observability, and Release Gates
- Web Application Security Testing and Gate Patterns
Author attribution: Ivan Piskunov, 2026 - Educational and defensive-engineering use.
2026 update: what to carry forward from older ZAP material
Older ZAP books and articles are still helpful for learning:
- contexts and in-scope discipline;
- passive versus active scan separation;
- AJAX spider usage;
- report generation;
- Jenkins and API usage as practical starting points.
But treat the following as legacy context, not default modern practice:
- Java 8-era installation assumptions;
- older Docker image naming as the main automation reference;
- GUI-first scanning as the final operating model;
- generic crawl-first API coverage when an OpenAPI definition exists.
For the current Product Security model, use this page together with: