Skip to main content

ZAP

Overview​

ZAP is the most widely used open source dynamic application security testing (DAST) scanner. It tests a running application from the outside: it sends requests, some carrying attack payloads, and looks for signs of a vulnerability in the responses.

To do that, ZAP needs the change to be running. In a microservices system, the usual target is a shared staging environment after merge, where a finding could come from any of the changes deployed together. A Signadot sandbox gives ZAP a target for a single change before it merges, so every finding belongs to that change. The same scan runs while you're still writing the change and again as a pull request check.

What ZAP detects​

ZAP's rules come in two kinds. Passive rules only inspect the responses to ordinary requests, so they're safe to run against any sandbox. Active rules send attack payloads and watch how the application reacts.

CategoryExamplesRules
InjectionSQL injection, OS command injection, server-side template injection, XML external entities, CRLF injectionActive
Cross-site scriptingReflected and stored XSSActive
File accessPath traversal, remote file inclusionActive
Information disclosureDatabase errors, stack traces and debug messages in responses, server version numbers in headersPassive
Missing security headersContent Security Policy, anti-clickjacking, X-Content-Type-Options, Strict-Transport-SecurityPassive
Weak cookie settingsCookies without the HttpOnly, Secure or SameSite attributesPassive
Cross-origin misconfigurationOverly permissive CORS headersPassive

Each finding has a risk level (High, Medium, Low or Informational), the request that triggered it, and a suggested fix.

ZAP's Docker image ships three packaged scans built on these rules:

ScanWhat it doesUse it for
Baseline (zap-baseline.py)Crawls the target and runs passive rules onlyWeb UIs, and any sandbox whose data isn't isolated
API (zap-api-scan.py)Imports an OpenAPI, SOAP or GraphQL definition and runs active rules against each operationAPIs. It skips browser-only checks such as XSS
Full (zap-full-scan.py)Crawls the target and runs all active rulesWeb apps, when the sandbox's data is isolated

How Jobs and sandboxes run ZAP​

Four pieces fit together:

  1. A sandbox runs the change. It contains only the changed service. Requests that carry the sandbox's routing key reach the change, and everything the change calls comes from the cluster's baseline.

  2. A job runner group runs ZAP in your cluster. It keeps a pod with ZAP's official image (ghcr.io/zaproxy/zaproxy:stable) ready to run scans. A Signadot admin creates it once.

  3. A Job runs the scan against the sandbox. ZAP targets the baseline service's in-cluster address, and Signadot adds the sandbox's routing key to every request ZAP sends:

    routingContext:
    sandbox: "@{sandbox}" # ties the Job to the sandbox
    trafficManager:
    injectRoutingKey: auto # adds the routing key to the Job's plain HTTP requests

    ZAP needs no configuration to reach the change, and it never needs to know where the change runs. Its own header settings stay free for your app's credentials if the scan has to log in.

  4. The Job's result is the verdict. The scan script decides what fails the Job, for example any High-risk finding, while lower-risk findings such as missing headers stay in the report. ZAP's HTML and JSON reports are uploaded as Job artifacts, which you can download with the CLI or open from the Job's page in the Signadot dashboard.

For an API scan, give ZAP an OpenAPI definition that lists only the operations the change touches. ZAP attacks every operation in the definition, so this keeps the scan short and leaves out operations whose writes you don't want.

Because the sandbox decides where the change runs, the same Job works whether the change runs on a developer's machine or is deployed in the cluster. That's what lets one scan serve both local development and pull requests.

Scan during local development​

With local sandboxes, the changed service runs on your machine. signadot local connect links your machine to the cluster, and the sandbox routes requests that carry its routing key to your local process. Start the service with the baseline's environment (signadot sandbox get-env), so it talks to the cluster's databases and services, and the scan loop looks like this:

  1. Submit the scan Job against the sandbox with signadot job submit ... --attach.
  2. Read the findings in the Job's output and reports.
  3. Fix the code and restart the service.
  4. Submit the Job again.

You don't build or push an image between iterations, so each fix is one restart away from its next scan. A coding agent runs the same loop on its own changes: see Run it in the agent loop and in CI.

Scan as a pull request check​

In CI, the pipeline builds an image from the pull request and creates a sandbox that forks the service's Deployment with that image. Then it submits the same Job with --attach. If the scan finds something that fails the Job, the PR check fails, and both reports stay attached to the Job for the reviewer. See CI/CD integrations for running Signadot Jobs from GitHub Actions, GitLab, Bitbucket and Jenkins.

Keep active scans contained​

API and full scans send real attack payloads. The sandboxed service processes them, and its calls to shared services and databases are real.

  • List only the operations the change touches in the scan's OpenAPI definition.
  • Before scanning operations that write data, give the sandbox its own database with a resource plugin, so injected rows never land in shared data.
  • To check a web UI without sending attacks, use the passive baseline scan in the same Job.

See Keep active scans contained for more.

Walk through it with the tutorial​

The ZAP and Signadot sandboxes tutorial puts this together on HotROD, Signadot's demo app:

  • A change to HotROD's location service adds search by name and, with it, a SQL injection.
  • The change runs on your machine in a local sandbox, and ZAP runs in the cluster as a Job.
  • ZAP's API scan, scoped to the changed endpoint, finds the injection and fails the Job on the High-risk finding.
  • A fix that passes the search term as a query parameter makes the next scan pass.
  • A final section scans the same change deployed in the cluster, which is how the scan runs in CI.

The tutorial's runner group, sandbox and Job specs are in the same directory, ready to copy into your own repository.