Everything we have done in the GitLab DevSecOps pipeline so far has been static — analysis of source code, dependencies, containers and configuration. In Part 8, Patrick Steger and I cross the line into Continuous Delivery and add Dynamic Application Security Testing. DAST means we deploy the application, start it, and then attack it from the outside with an automated penetration testing tool. GitLab ships this capability out of the box, powered by OWASP ZAP.
What DAST Actually Does#
Patrick puts it simply: DAST finds vulnerabilities in your running application — think of it as automated penetration testing. The GitLab default is the OWASP ZAP attack proxy, which probes the live system and looks for issues a static scanner cannot see, such as response headers, misconfigured TLS, or injection points exposed at runtime.
There is one important caveat up front. Without explicit, manual configuration, DAST is very basic. If you want findings that move the needle on a real product, you have to invest serious time in tuning OWASP ZAP. We do not do that in this video. The OWASP ZAP project itself has dedicated sessions for that. What we show here is how to wire DAST into the pipeline so it runs on every commit.
Why It Lives in Continuous Delivery#
DAST cannot run on source code. It needs a deployed, running system. That is why this is the first step that pulls us out of pure CI and into CD. The container we built and scanned in earlier sessions is now started inside the pipeline so OWASP ZAP can attack it.
Defining the Stages Explicitly#
The first surprise is that enabling DAST is not “one extra line.” The default DAST job from GitLab requires a stage called dast. The moment you define stages explicitly, you have to list all of them, otherwise the existing jobs lose their build, test, and deploy stages. So in .gitlab-ci.yml we add:
stages:
- build
- test
- deploy
- dastbuild, test, and deploy exist by default, but as soon as you start declaring stages they all need to be there.
Wiring Up the Running Container#
Next we set the variable CONTAINER_TEST_IMAGE to point to the container image we built earlier for container scanning. Then in the dast: job we extend services so the container is started for the job and reachable under the alias demoApp.
The DAST job expects a DOCKER_IMAGE variable telling it what to test, and we set DAST_WEBSITE to the demoApp service alias so OWASP ZAP knows where to send its requests. To get more useful findings, we also set DAST_FULL_SCAN_ENABLED: "true". Without that flag, ZAP only does passive observation. With it, ZAP performs active attacks — actually trying to break things, not just watching.
Finally we include the GitLab-provided DAST template so the job definition itself is pulled in.
Looking at the Results#
When the pipeline runs, the new stage shows up. In the job log you see the service container starting first — that is the application under test. Then the ZAP server starts, performs its scan, and reports the findings. In our small Java application it found two warnings.
The findings show up in two places. First, directly on the pipeline view under the Security tab — the summary shows two new vulnerabilities and you can filter by DAST. Second, in Security and Compliance under the Vulnerability Report, again filterable by tool. The two findings we got are very basic, for example that HTTPS is not enabled. As mentioned, real findings require real ZAP configuration — but the integration itself works exactly as it should, and that is the point of this session.
Recap#
We added the new dast stage and re-declared the default stages alongside it. We included the GitLab DAST template, configured the endpoint where DAST finds the application, enabled full scan so ZAP actively attacks, and extended the DAST job with a service so the container is reachable. Five small pieces, and DAST runs on every commit.
The full DevSecOps toolset for our pipeline is now in place. In the next session we will look at the integrated Vulnerability Management capabilities of GitLab and how all these findings come together.
Key Takeaways#
DAST tests the running system, not the code. This is the moment where the pipeline crosses from CI into CD. Without a deployed, running container there is nothing for OWASP ZAP to attack.
Out-of-the-box DAST is basic on purpose. Treat the default GitLab DAST job as the integration scaffolding. Real, high-value findings require dedicated time tuning OWASP ZAP — that is a project of its own.
Explicit stages mean all stages. As soon as you declare a
daststage, you also have to declarebuild,test, anddeploy, otherwise the existing jobs break. Easy to miss, easy to debug.Use a service to host the application under test. The container built earlier is started as a service under an alias and pointed at via
DAST_WEBSITE. Same image, same artifact — no separate deployment dance.Enable full scan or you are only watching. Without
DAST_FULL_SCAN_ENABLED: "true", OWASP ZAP only observes traffic passively. With it, ZAP actively attacks the application, which is what you want from a penetration test.Findings flow into the same security dashboard. DAST results land in the pipeline Security tab and the project-wide Vulnerability Report alongside SAST, SCA, secret detection and container scanning. One place to triage everything.
