Skip to main content

CI/CD Integration

Run DAST on every push, every PR, or on a schedule. The scanner ships as a Docker image, returns non-zero on findings above a threshold, and emits SARIF for the GitHub Security tab.

Two ways to configure a CI scan
  • levo-dast.yml โ€” commit the scan config to the repo. Only secrets stay in CI. Recommended.
  • CLI flags โ€” one-off invocations with all options on the command line.

Key conceptsโ€‹

CI/CD flagsโ€‹

  • --ci / --non-interactive โ€” run without prompts.
  • --fail-on <severity> โ€” exit 1 if any finding at or above the threshold (critical ยท high ยท medium ยท low).
  • --output sarif โ€” SARIF report for the GitHub Security tab.
  • --output json โ€” machine-readable findings.

Exit codesโ€‹

CodeMeaning
0Scan completed; no findings above --fail-on threshold.
1Scan failed, or findings at/above --fail-on.
130Scan interrupted.

GitHub Actionsโ€‹

Basic workflowโ€‹

levo-dast.yml checked into the repo:

version: "1"
target:
url: "${TARGET_URL}"
auth:
strategy: "none"
scan:
depth: "smart"
reporting:
output: "sarif"
output_file: "/work/out/results.sarif"
fail_on: "high"

.github/workflows/dast.yml:

name: DAST Security Test

on:
push:
branches: [main, develop]
schedule:
- cron: '0 2 * * *'

jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run DAST
run: |
docker run --rm \
-v "$PWD/levo-dast.yml:/work/levo-dast.yml:ro" \
-v "$PWD:/work/out" \
-w /work \
-e TARGET_URL=${{ secrets.TARGET_URL }} \
-e LEVOAI_AUTH_KEY=${{ secrets.LEVOAI_AUTH_KEY }} \
-e LEVOAI_ORG_ID=${{ secrets.LEVOAI_ORG_ID }} \
ghcr.io/levoai/levoai-shadownet:latest scan
- name: Upload to GitHub Security
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif

Authenticated scanโ€‹

levo-dast.yml:

version: "1"
target:
url: "${STAGING_URL}"
auth:
strategy: "form"
login_url: "${LOGIN_URL}"
username: "${SCAN_USERNAME}"
scan:
depth: "smart"
reporting:
output: "sarif"
output_file: "/work/out/results.sarif"
fail_on: "critical"

Workflow:

- name: Run authenticated DAST
run: |
docker run --rm \
-v "$PWD/levo-dast.yml:/work/levo-dast.yml:ro" \
-v "$PWD:/work/out" -w /work \
-e STAGING_URL=${{ secrets.STAGING_URL }} \
-e LOGIN_URL=${{ secrets.LOGIN_URL }} \
-e SCAN_USERNAME=${{ secrets.SCAN_USERNAME }} \
-e SCAN_PASSWORD=${{ secrets.SCAN_PASSWORD }} \
-e LEVOAI_AUTH_KEY=${{ secrets.LEVOAI_AUTH_KEY }} \
-e LEVOAI_ORG_ID=${{ secrets.LEVOAI_ORG_ID }} \
ghcr.io/levoai/levoai-shadownet:latest scan --password "$SCAN_PASSWORD"

Reporting findings to the Levo dashboardโ€‹

Add these env vars to any job (both styles) to push findings to your Levo workspace:

env:
LEVOAI_AUTH_KEY: ${{ secrets.LEVOAI_AUTH_KEY }}
LEVOAI_ORG_ID: ${{ secrets.LEVOAI_ORG_ID }}
LEVOAI_ENV_ID: ${{ secrets.LEVOAI_ENV_ID }}

In YAML config, set reporting.send_issues: true. With flags, add --send-issues --env-id $LEVOAI_ENV_ID.

GitHub secret setupโ€‹

SecretValue
TARGET_URLhttps://example.com
STAGING_URLhttps://staging.example.com
LOGIN_URLhttps://example.com/login
SCAN_USERNAMEService account username
SCAN_PASSWORDService account password
LEVOAI_AUTH_KEYLevo auth key
LEVOAI_ORG_IDOrganization ID
LEVOAI_ENV_IDEnvironment ID

GitLab CIโ€‹

dast:
image: docker:24
services: [docker:24-dind]
stage: test
script:
- |
docker run --rm \
-v "$PWD/levo-dast.yml:/work/levo-dast.yml:ro" \
-v "$PWD:/work/out" -w /work \
-e TARGET_URL -e SCAN_USERNAME -e SCAN_PASSWORD \
-e LEVOAI_AUTH_KEY -e LEVOAI_ORG_ID -e LEVOAI_ENV_ID \
ghcr.io/levoai/levoai-shadownet:latest scan --password "$SCAN_PASSWORD"
artifacts:
when: always
paths: [results.sarif]

Best practicesโ€‹

  1. Service accounts for auth โ€” don't scan with a developer's personal account.
  2. Scan staging, not production. If you must scan prod, follow Scanning production safely.
  3. Set fail_on deliberately. high blocks pipelines on high/critical; critical only blocks on critical. Pick the one your team will actually fix, not the one that looks strictest.
  4. Schedule deep scans off-peak and keep PR scans fast (depth: smart, low max_pages).
  5. Version-control levo-dast.yml โ€” you'll thank yourself the next time someone tunes the scan.

Example: PR-fast, nightly-thoroughโ€‹

Two YAML files in the repo, picked by environment:

# PR job
shadownet scan --config levo-dast.pr.yml

# Nightly job
shadownet scan --config levo-dast.nightly.yml

Next stepsโ€‹

Was this page helpful?