tutorials
11 min read·MOFU

DAST in GitHub Actions: A Practical Tutorial with Vuln0x

A complete walkthrough of running DAST scans in GitHub Actions using Vuln0x: API tokens, workflow YAML, blocking PRs on critical findings, posting results to GitHub Code Scanning via SARIF, and Slack alerts. Production-ready in under an hour.

April 29, 2026
By Vuln0x Security Research TeamOffensive Security & Vulnerability Research40+ scanner engines, 29+ Kali tools, 7-phase methodologyLast updated: April 29, 2026
DAST in GitHub Actions: A Practical Tutorial with Vuln0x

DAST in GitHub Actions: A Practical Tutorial with Vuln0x

Most teams agree DAST should run in CI. Most teams also do not actually run DAST in CI, because integrating a real scanner into a pipeline used to be a multi-week project. With the right tool, it should take an afternoon.

This tutorial walks through setting up a complete DAST pipeline in GitHub Actions using Vuln0x. By the end you will have:

  • A scan that runs on every PR against a deployed preview environment.
  • Critical findings that block the PR from merging.
  • Results posted to GitHub Code Scanning as SARIF, so they show up in the Security tab.
  • Slack alerts for high-severity findings.
We will use real, copy-pasteable YAML and explain what each piece does.

Prerequisites

You need three things before starting:

  • A Vuln0x account with at least one project. Sign up at vuln0x.com — the free tier with 20 credits is enough to test this tutorial end to end.
  • A deployment with a stable, scannable URL. PR preview environments (Vercel, Netlify, Cloudflare Pages) work great. A staging environment also works.
  • GitHub repository write access to add Actions and secrets.

Step 1 — Create a Vuln0x API token

In the Vuln0x dashboard:

  • Go to Settings → API Reference.
  • Create a new API token. Name it something like github-actions-prod.
  • Copy the token. You will not be able to see it again.
In your GitHub repository:
  • Settings → Secrets and variables → Actions → New repository secret.
  • Name: VULN0X_API_TOKEN. Value: the token you just copied.
  • While you are here, add VULN0X_PROJECT_ID (the project ID from the dashboard) as another secret.

Step 2 — The minimum viable workflow

Create a file at .github/workflows/dast.yml:

name: DAST Scan

on:
pull_request:
branches: [main]

jobs:
dast:
runs-on: ubuntu-latest
steps:
- name: Wait for preview deployment
run: |
# Replace with your deploy provider's check or a sleep
sleep 60

- name: Trigger Vuln0x scan
id: scan
env:
VULN0X_API_TOKEN: ${{ secrets.VULN0X_API_TOKEN }}
VULN0X_PROJECT_ID: ${{ secrets.VULN0X_PROJECT_ID }}
TARGET_URL: https://pr-${{ github.event.pull_request.number }}.preview.example.com
run: |
curl -X POST https://api.vuln0x.com/v1/scans \
-H "Authorization: Bearer $VULN0X_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$VULN0X_PROJECT_ID\",\"target\":\"$TARGET_URL\",\"profile\":\"standard\"}" \
-o scan.json
echo "scan_id=$(jq -r .id scan.json)" >> $GITHUB_OUTPUT

- name: Wait for scan to complete
env:
VULN0X_API_TOKEN: ${{ secrets.VULN0X_API_TOKEN }}
SCAN_ID: ${{ steps.scan.outputs.scan_id }}
run: |
for i in $(seq 1 60); do
STATUS=$(curl -s -H "Authorization: Bearer $VULN0X_API_TOKEN" \
https://api.vuln0x.com/v1/scans/$SCAN_ID | jq -r .status)
echo "Status: $STATUS"
if [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ]; then
break
fi
sleep 30
done

- name: Fail on critical findings
env:
VULN0X_API_TOKEN: ${{ secrets.VULN0X_API_TOKEN }}
SCAN_ID: ${{ steps.scan.outputs.scan_id }}
run: |
CRITICAL=$(curl -s -H "Authorization: Bearer $VULN0X_API_TOKEN" \
https://api.vuln0x.com/v1/scans/$SCAN_ID/findings?severity=critical | jq '. | length')
echo "Critical findings: $CRITICAL"
if [ "$CRITICAL" -gt 0 ]; then
echo "::error::Critical vulnerabilities detected. PR cannot merge."
exit 1
fi

That is the spine of the pipeline. It triggers a scan, waits for it to finish, and fails the job if any critical findings exist.

Note on the API: the exact endpoint paths and parameters in this tutorial follow the current Vuln0x REST API conventions. Always check the live API reference at vuln0x.com/dashboard/api for the most up-to-date schema.

Step 3 — Post results to GitHub Code Scanning (SARIF)

GitHub's Security tab can natively render any SARIF file. Vuln0x outputs SARIF, so this integration is essentially free:

      - name: Download SARIF report
        env:
          VULN0X_API_TOKEN: ${{ secrets.VULN0X_API_TOKEN }}
          SCAN_ID: ${{ steps.scan.outputs.scan_id }}
        run: |
          curl -s -H "Authorization: Bearer $VULN0X_API_TOKEN" \
            "https://api.vuln0x.com/v1/scans/$SCAN_ID/report?format=sarif" \
            -o vuln0x.sarif

- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: vuln0x.sarif
category: vuln0x-dast

After this runs, every PR shows DAST findings inline in the GitHub UI, with code-scanning-style annotations. The Security tab gets a permanent record. Reviewers can comment on findings the same way they comment on code.

This is the single highest-leverage piece of the integration. Every additional scan over time builds a real security audit trail directly inside GitHub.

Step 4 — Slack alerts for high-severity findings

For high (not just critical) findings, we want a Slack ping rather than a hard failure. Add another step:

      - name: Slack alert on high findings
        if: always()
        env:
          VULN0X_API_TOKEN: ${{ secrets.VULN0X_API_TOKEN }}
          SCAN_ID: ${{ steps.scan.outputs.scan_id }}
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
        run: |
          HIGH=$(curl -s -H "Authorization: Bearer $VULN0X_API_TOKEN" \
            https://api.vuln0x.com/v1/scans/$SCAN_ID/findings?severity=high | jq '. | length')
          if [ "$HIGH" -gt 0 ]; then
            curl -X POST -H 'Content-type: application/json' \
              --data "{\"text\":\":warning: $HIGH high-severity findings on PR #${{ github.event.pull_request.number }}\"}" \
              $SLACK_WEBHOOK
          fi

Add SLACK_WEBHOOK as a repository secret pointing to a channel-specific Slack incoming webhook.

Step 5 — Sentinel pentest on main branch merges

DAST is fast (minutes). Sentinel — the autonomous AI pentest agent — runs longer because it does multi-phase exploitation, privilege escalation, and lateral movement. We do not want that on every PR. We do want it on every merge to main:

on:
  push:
    branches: [main]

jobs:
sentinel:
runs-on: ubuntu-latest
steps:
- name: Trigger Sentinel pentest
env:
VULN0X_API_TOKEN: ${{ secrets.VULN0X_API_TOKEN }}
run: |
curl -X POST https://api.vuln0x.com/v1/sentinel/runs \
-H "Authorization: Bearer $VULN0X_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"${{ secrets.VULN0X_PROJECT_ID }}\",\"target\":\"https://staging.example.com\"}"

You can (and should) wire the Sentinel run to also produce a SARIF file and Slack notifications, with the same patterns as the DAST job.

Step 6 — Optimize for credits

The free tier gives you 20 credits, and team plans use a similar credit model. A few tips to keep your scan budget reasonable:

  • Run DAST only on PRs that touch app code — use paths: in the workflow to skip docs/typo PRs.
  • Use the quick profile for PR scans and the standard or deep profile only for main-branch merges.
  • Run Sentinel weekly, not on every merge, if your branch is high-traffic.
  • Block on critical only, not on high — high findings ping Slack but do not fail builds, so you maintain a fast PR loop.

Step 7 — Avoid the classic CI/CD security mistakes

This integration is only as good as the discipline around it. The classic mistakes:

  • Treating the scanner as the audit. It is not. It is one signal. Logic flaws, design issues, and authorization mistakes still need human (or Sentinel-agent) review.
  • Suppressing all noise. If everyone gets in the habit of suppressing findings, the pipeline becomes theater. Triage findings; do not blanket-mute them.
  • Pointing the scanner at production. Always scan staging or PR previews. A loud DAST run hammers your app — that is the point. Production should never see those payloads.
  • Storing the API token in plain text. Use secrets, not env blocks committed to the repo.

Putting it all together

The full workflow file ends up around 80 lines and gives you:

  • Per-PR DAST scans with SARIF output to GitHub Security.
  • Critical findings that block PR merges.
  • High findings that ping Slack but do not block.
  • Weekly or on-merge Sentinel pentests.
  • A real audit trail you can show to a SOC 2 or ISO 27001 auditor.
That is a meaningful AppSec posture for a small team, and it took an afternoon to set up.

Try it on your own repo

Sign up at vuln0x.com, grab your API token, copy the YAML above, and ship the workflow on a real PR today. The 20 free credits cover several full pipeline runs — enough to confirm the integration works end-to-end before you commit to a plan.

If you hit any rough edges in the API or the GitHub Action wiring, tell us. This is exactly the kind of integration we want to make boring and reliable for everyone.

Frequently Asked Questions

Why run DAST in CI/CD instead of weekly?

Because most security regressions are introduced by code changes, and most code changes ship through PRs. Catching a critical vulnerability before merge is dramatically cheaper than catching it after deploy. Weekly scans are still useful for production drift and dependency updates, but PR-time scanning is where most of the value lives.

Will scanning every PR slow down my deployment pipeline?

Quick-profile DAST scans typically finish in a few minutes for small to medium apps. You can run them in parallel with your other CI checks so they do not extend total wall time. For larger apps or deeper Sentinel pentests, run those on merge-to-main rather than on every PR.

Can I use this with GitLab CI or other platforms?

Yes. The same REST API calls work from any CI system. Vuln0x has documented integrations for GitHub Actions and GitLab CI specifically, plus a generic REST API and webhooks for everything else. If you are on CircleCI, Buildkite, Jenkins, or anything else that can run a curl command, the same workflow translates directly.

What happens if Vuln0x flags a false positive in a PR?

Use the dashboard to mark a finding as a false positive or accepted risk, with a reason. The next scan will respect that decision and the PR will not block on it. Avoid blanket suppressions — track each false positive with a justification so the audit trail stays meaningful.

github actions dast
dast in ci/cd
vuln0x github action
sarif github code scanning
block pr on vulnerability
automated security testing
devsecops tutorial

Ready to secure your application?