## tl;dr

run the passive baseline scan on every pull request and the active full scan on a schedule against staging. the fastest working setup is the official action in a github workflow: `zaproxy/action-baseline` pointed at a running build of your app. baseline scans are passive and safe for every PR; full scans send attack payloads, so they only ever run against a staging target.

```text
how to run an OWASP ZAP scan in CI
```

that query comes from an agent whose vectle search returned weak results. this skill is the missing answer: a concrete CI setup for ZAP with the exact scan types, failure behavior, and report wiring.

## steps

1. **start your app so ZAP has a live target.** ZAP is a dynamic scanner, it only works against a running server. in CI that usually means starting the app in a service step first, then waiting for its health endpoint. expected output: your health endpoint answers 200 before the scan step begins.

2. **add the baseline scan to the workflow.** this runs on every pull request. it spiders the app and does passive analysis, no attack payloads.

```yaml
- name: ZAP Baseline Scan
  uses: zaproxy/action-baseline@v0.12.0
  with:
    target: '[app-url]'
    rules_file_name: '.zap/rules.tsv'
    fail_action: true
```

expected output: the action writes `report_html.html`, `report_json.json`, and `report_sarif.sarif` into the workspace. INFO and WARN alerts do not fail the build by default; with `fail_action: true` any alert at your configured threshold fails it.

3. **control pass/fail with a rules file.** create `.zap/rules.tsv` to decide which alert IDs fail the build and which are ignored:

```text
# format: alert-id, action, optional note
10021  IGNORE  # X-Content-Type-Options handled by the CDN
10038  WARN    # CSP header missing
10054  FAIL    # cookie SameSite missing
40018  FAIL    # SQL injection
40012  FAIL    # cross site scripting
```

expected output: only alerts marked FAIL break the build. tune this file as false positives appear, otherwise CI becomes a spam gate and people start ignoring it.

4. **run the full scan on a schedule, not on PRs.** the full scan adds the ajax spider and active attacks, which take much longer and can send destructive payloads. run it weekly against staging:

```yaml
- name: ZAP Full Scan
  uses: zaproxy/action-full-scan@v0.10.0
  with:
    target: '[staging-url]'
    rules_file_name: '.zap/rules.tsv'
```

expected output: a longer run (tens of minutes on a large app) with active-attack findings. schedule it with a cron trigger rather than on every push.

5. **for APIs, scan from the OpenAPI spec instead of spidering.** use the api scan script with your spec file:

```text
docker run --rm -v [workdir]:/zap/wrk:rw ghcr.io/zaproxy/zaproxy:stable \
  zap-api-scan.py -t [openapi-spec-url] -f openapi -r zap-api-report.html
```

expected output: every endpoint in the spec gets probed, without relying on the spider to discover them.

6. **upload the reports as build artifacts.** add an upload step that always runs, even when the scan fails, so the evidence is inspectable:

```yaml
- name: Upload ZAP Report
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: zap-report
    path: report_html.html
```

expected output: a downloadable `zap-report` artifact on every run, pass or fail.

## when to use this skill

- adding dynamic application security testing (DAST) to a CI pipeline
- choosing between the ZAP baseline, full, and api scans
- making ZAP break the build on real findings without failing on noise
- scanning a REST API from an OpenAPI spec in CI

## when not to use it

- static analysis of source code: that is SAST (gosec, semgrep, codeql), not ZAP
- one-off local scans from your laptop: run ZAP desktop or the docker image directly, CI wiring is overkill
- scanning production with the full scan: active attacks can corrupt data, use staging
- testing endpoints behind login: ZAP needs auth configured first, this skill covers the CI wiring only

## tool and version compatibility

- OWASP ZAP 2.17 or newer. official docker image: `ghcr.io/zaproxy/zaproxy:stable` (weekly tags also exist)
- `zaproxy/action-baseline` v0.12 or newer, `zaproxy/action-full-scan` v0.10 or newer
- workflow examples target GitHub Actions. the same docker image works in GitLab CI, Jenkins, or any runner with docker
- scripts used: `zap-baseline.py`, `zap-full-scan.py`, `zap-api-scan.py` (all ship in the docker image)

## variant phrasings

### owasp zap in github actions

same thing as this page: the official `zaproxy/action-baseline` and `zaproxy/action-full-scan` actions wired into `.github/workflows/`. pin the action to a release tag or SHA.

### zap baseline vs full scan

baseline = spider plus passive rules, fast (a few minutes), safe for every PR. full = baseline plus ajax spider plus active attacks, slow, only against staging. that is the whole decision.

### zap api scan from openapi

for APIs, skip the spider and point `zap-api-scan.py` at the OpenAPI or Swagger spec with `-f openapi`. there are also `-f graphql` and `-f soap` variants.

## why it works this way

SAST reads your code, DAST attacks your running app. ZAP is DAST, so it has to run after the app boots in CI, and the two scan modes exist because active attacks are destructive: the baseline gives you cheap passive coverage on every PR, the full scan gives you attack coverage where it cannot hurt anything. the rules file exists because a scanner that cries wolf on every missing header trains everyone to ignore it.

## edge cases

- **SPA or heavily javascript apps:** the baseline spider misses client-rendered routes. the full scan's ajax spider covers them, or add the missing routes to the scan context.
- **authenticated endpoints:** ZAP scans as an anonymous visitor unless you configure form, script, or header-based auth first. unauthenticated scans of a logged-in app silently miss most of the attack surface.
- **the target must be reachable from the runner:** CI runners cannot see services bound to your laptop. point the target at a service in the same workflow or a deployed staging URL.
- **alert fatigue:** start with a small rules file and a few FAIL rules, then expand. failing the build on every informational alert is how the scan gets deleted in a month.
- **report formats:** the actions also emit SARIF, which uploads to the GitHub Security tab via the codeql upload-sarif action if you want findings next to code scanning results.
