After seven sessions of static analysis — SCA, license compliance, SAST, container scanning, secret detection — Patrick Steger and I move into the dynamic side of the pipeline. In Part 8 we add Dynamic Application Security Testing to our GitHub Actions pipeline. DAST runs the application and then attacks it. GitHub does not ship this out of the box, so we wire in a community action built on OWASP ZAP — and we are honest about where that approach falls short for enterprise use.
What DAST Means for Us#
In Dynamic Application Security Testing we run our own application and then attack it. All of it automated. We need a tool that scans and attacks an application running either inside the pipeline or somewhere on the internet or on-premises. GitHub does not provide a first-party DAST capability, so we go to the marketplace and find a suitable action. The one we land on is the action-full-scan provided by the ZAP team — based on the well-known OWASP ZAP open source tool.
We use it out of the box, no custom ZAP configuration. For an enterprise application this is rarely good enough — proper ZAP configuration is its own project. Interestingly, in our case the standard configuration produced more and better findings than what we got on the other platform we covered.
Up Front: The Drawbacks#
Before showing the workflow, Patrick names the limitations honestly. First and worst: ZAP findings are not visible in the GitHub vulnerability management UI. The reason is missing SARIF support — SARIF is the format GitHub expects in order to render findings in its security dashboard. Second, we did not customise the ZAP configuration, so findings are somewhat random. Third, we run the test against a Docker container started inside the pipeline rather than against a real cloud deployment. ZAP can absolutely scan a real deployed URL — we just skipped the deploy-to-Azure step to keep the demo focused.
The Workflow#
The DAST workflow can be triggered two ways: manually via workflow_dispatch, or as part of the overall pipeline via workflow_call. Either way it requires one parameter — the image tag — plus the two familiar environment variables for the container registry and image name.
The job runs on ubuntu-latest and does the following: check out the repo, log into the registry, pull the image, docker run the container, wait for it to start, and probe it to make sure it actually responds in a meaningful way. With the application running, we kick off the ZAP scan using action-full-scan. We provide the target URL, and we pass a couple of CLI options — among them an extra ajax spider to find more endpoints, and a reduced log level so the console stays readable.
We also disable an option that would otherwise open a GitHub issue for every scan. That might be fine for a single-person side project; in an enterprise context it is unusable. After the scan we upload the HTML report as a workflow artifact and stop the container. We would have preferred to upload SARIF, but at the time of recording that was not supported by the action and would have meant building it ourselves.
Calling It from the Main Pipeline#
The DAST workflow is wired into the orchestrator as a job that depends on build and docker. It calls the workflow we just defined, passes the secrets, and consumes the image-tag output from the docker job so it tests exactly the artifact that was built upstream.
The Report#
When the pipeline runs, the new DAST job appears in the run view. The log shows the registry login, the docker run, the wait-and-probe step, then the full ZAP scan with the exact command line and the most important findings on WARN level. At the end the HTML report is attached as an artifact.
Opening the report shows a real finding immediately — a cross-site scripting issue on a URL parameter. We know from the source that the vulnerability is genuine, so the scanner did its job. The catch, again, is that the only delivery is an HTML file. There is no triage flow, no dismiss-as-false-positive, no integration with the rest of GitHub security.
The Issue Option Is Not the Answer#
There is a flag on the action to file the findings as a GitHub issue. We try it, and a ZAP Full Scan Report issue gets created listing every problem. It looks helpful for thirty seconds. Then you realise that every run files a new issue, with no memory of what you already fixed or marked as not relevant. In an enterprise setting that becomes noise immediately and gets ignored within days. What we actually want is an entry in the Security tab — like Code Scanning — where findings are first-class objects you can manage. SARIF support is what would unlock that.
Recap#
DAST in GitHub today: run the container with your application (in the pipeline for the demo, in the cloud for real systems), run a second container with OWASP ZAP that attacks it, collect the findings, upload an HTML report. It works, it finds real vulnerabilities, and it has two real drawbacks — DAST is hard to configure well, and the GitHub-side integration is limited to an HTML artifact or a noisy auto-issue. In the next video we will look at Vulnerability Management.
Key Takeaways#
GitHub has no first-party DAST. You go to the marketplace. The community OWASP ZAP
action-full-scanis the practical choice today, used out of the box for the demo and tuned heavily for any real product.DAST needs a running target. We run the container in the pipeline to keep the demo simple. In a real enterprise pipeline the container is deployed to a cloud environment first and ZAP scans the live URL.
No SARIF means no native dashboard. Without SARIF output the findings do not show up in GitHub vulnerability management. You get an HTML artifact, which is fine for inspection but not for triage at scale.
The auto-issue option is a trap. Filing a GitHub issue per scan means every run re-creates everything you already triaged. It is an option for tiny projects only — for enterprise pipelines, do not enable it.
Wait-and-probe before you scan. Start the container, wait for it, and make sure it actually answers. Otherwise ZAP scans nothing and you ship a green pipeline that proved nothing.
Default ZAP configuration is a starting point, not the goal. It found a real XSS in our demo, which is encouraging — but high-quality DAST in production needs deliberate ZAP tuning. Plan that as its own work item.
