Security is often the ‘forgotten’ step in the rapid cycle of development and deployment. I’ve seen too many teams treat security as a final hurdle—a manual audit performed right before a major release. This approach is not only stressful but dangerous. That’s why I’m putting together this owasp zap github actions tutorial to help you shift security left.
Dynamic Application Security Testing (DAST) allows us to find vulnerabilities while the application is running, simulating how a real attacker would probe your site. OWASP ZAP (Zaproxy) is the industry standard for open-source DAST. By integrating it into your GitHub Actions pipeline, you can ensure that every pull request is automatically scanned for common flaws like XSS, SQL injection, and insecure headers.
If you’re new to this, you might want to start by learning how to automate security testing in CI/CD pipelines generally before diving into the specific ZAP configuration.
Prerequisites
Before we dive into the YAML configuration, ensure you have the following ready:
- A web application hosted in a staging or development environment (ZAP needs a running URL to scan).
- A GitHub repository with your project’s source code.
- Basic familiarity with GitHub Actions workflows.
- An understanding of the difference between SAST (Static) and DAST (Dynamic) testing—if you’re debating tools, check out my OWASP ZAP vs StackHawk comparison.
Step-by-Step Integration
Step 1: Create the Workflow File
In your repository, create a directory path `.github/workflows/` and add a file named `zap-scan.yml`. I recommend triggering this on pull_request or push to your main branch to catch regressions early.
Step 2: Define the Workflow Configuration
We will use the official ZAP Docker images. In my experience, the ‘Baseline Scan’ is the best starting point for most teams because it’s fast and doesn’t attempt to ‘attack’ the site aggressively, making it safe for most staging environments.
name: Security Scan
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
zap_scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: OWASP ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: 'https://staging.your-app.com'
rules_file_name: '.zap/rules.tsv'
As shown in the image below, the ZAP action will spin up a Docker container, crawl your target URL, and generate a report based on the alerts it finds.
Step 3: Handling False Positives with rules.tsv
One of the biggest frustrations with DAST is false positives. If ZAP flags something that you’ve already mitigated or deemed an acceptable risk, you don’t want your build to fail every time. This is where the rules.tsv file comes in.
Create a file at `.zap/rules.tsv` in your repo. The format is a tab-separated list. For example, to ignore a specific alert, you can add it to this file to tell ZAP it’s an expected behavior.
Step 4: Moving to the Full Scan
Once you are comfortable with the Baseline scan, you can implement the Full Scan. Unlike the baseline, the full scan performs active attacks. Warning: Never run a full scan against a production database, as it can create dummy data or trigger thousands of emails.
- name: OWASP ZAP Full Scan
uses: zaproxy/action-full-scan@v0.12.0
with:
target: 'https://staging.your-app.com'
Pro Tips for Better Security Pipelines
- Use Secret Variables: Never hardcode your staging URLs or API keys in the YAML. Use
${{ secrets.STAGING_URL }}. - Scan After Deploy: Ensure your ZAP job depends on your deployment job. Use
needs: [deploy]in your workflow to ensure the app is actually live before ZAP tries to hit it. - Incremental Adoption: Start with the baseline scan. Only move to full scans once your app is stable and you have a dedicated test database.
- Review the HTML Report: The console output is great, but the HTML report provides the exact request/response pairs needed to reproduce the bug.
Troubleshooting Common Issues
During my own setup, I encountered a few common roadblocks. Here is how to fix them:
| Issue | Solution |
|---|---|
| Scan fails due to timeout | Increase the timeout settings in the Action or optimize your app’s response time. |
| ZAP can’t reach the target | Check your firewall/security groups. Ensure the GitHub runner IP can access your staging environment. |
| Too many false positives | Properly configure the rules.tsv file to ignore known non-issues. |
What’s Next?
Now that you’ve automated your DAST scanning, you should look into rounding out your security posture. I suggest implementing a SAST tool like CodeQL (which is built into GitHub) to find bugs in the source code before it even deploys. Combining SAST and DAST gives you a comprehensive safety net.
Ready to optimize your development flow further? Explore our guides on automation and productivity tools to shave hours off your weekly sprint.