PS Product SecurityKnowledge Base

๐Ÿ•ท๏ธ 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:

  1. Baseline scan for low-friction passive checks and early signal.
  2. API scan for OpenAPI, SOAP, or GraphQL-defined targets.
  3. Automation Framework when auth, context, or orchestration logic becomes non-trivial.
  4. 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:

  • FAIL
  • WARN
  • IGNORE

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.

  • .zap/zap-baseline.conf
  • .zap/zap-api.conf
  • .zap/authenticated-api.yaml
  • .gitlab/ci/zap-baseline.yml
  • .gitlab/ci/zap-api.yml

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: