CI/CD Integration
Run DAST on every push, every PR, or on a schedule. The scanner ships as a Docker image and returns a non-zero exit code when findings cross the --fail-on threshold โ perfect for blocking a pipeline.
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 jsonโ machine-readable findings on stdout.
Exit codesโ
| Code | Meaning |
|---|---|
| 0 | Scan completed; no findings above --fail-on threshold. |
| 1 | Scan failed, or findings at/above --fail-on. |
| 130 | Scan interrupted. |
GitHub Actionsโ
Basic workflowโ
- With levo-dast.yml
- With CLI flags
levo-dast.yml checked into the repo:
version: "1"
name: "acme-webapp"
target:
url: "${TARGET_URL}"
auth:
strategy: "none"
scan:
depth: "smart"
.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 --shm-size=1g \
-v "$PWD/levo-dast.yml:/work/levo-dast.yml:ro" \
-w /work \
-e TARGET_URL=${{ secrets.TARGET_URL }} \
-e LEVOAI_AUTH_KEY=${{ secrets.LEVOAI_AUTH_KEY }} \
-e LEVOAI_ORG_ID=${{ secrets.LEVOAI_ORG_ID }} \
levoai/levoai-shadownet:stable \
scan --config /work/levo-dast.yml --ci --fail-on high
name: DAST Security Test
on:
push:
branches: [main, develop]
schedule:
- cron: '0 2 * * *'
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- name: Run DAST
run: |
pip install shadownet
playwright install chromium
shadownet scan ${{ secrets.TARGET_URL }} \
--ci \
--auth none \
--fail-on high
Authenticated scanโ
- With levo-dast.yml
- With CLI flags
levo-dast.yml:
version: "1"
name: "acme-webapp-staging"
target:
url: "${STAGING_URL}"
auth:
strategy: "form"
login_url: "${LOGIN_URL}"
username: "${SCAN_USERNAME}"
scan:
depth: "smart"
Workflow:
- name: Run authenticated DAST
run: |
docker run --rm --shm-size=1g \
-v "$PWD/levo-dast.yml:/work/levo-dast.yml:ro" \
-w /work \
-e STAGING_URL=${{ secrets.STAGING_URL }} \
-e LOGIN_URL=${{ secrets.LOGIN_URL }} \
-e SCAN_USERNAME=${{ secrets.SCAN_USERNAME }} \
-e LEVOAI_AUTH_KEY=${{ secrets.LEVOAI_AUTH_KEY }} \
-e LEVOAI_ORG_ID=${{ secrets.LEVOAI_ORG_ID }} \
levoai/levoai-shadownet:stable \
scan --config /work/levo-dast.yml \
--password "${{ secrets.SCAN_PASSWORD }}" \
--ci --fail-on critical
- name: Run authenticated DAST
run: |
pip install shadownet
playwright install chromium
shadownet scan ${{ secrets.STAGING_URL }} \
--ci \
--auth form \
--login-url ${{ secrets.LOGIN_URL }} \
--username ${{ secrets.SCAN_USERNAME }} \
--password ${{ secrets.SCAN_PASSWORD }} \
--fail-on critical
GitHub secret setupโ
| Secret | Value |
|---|---|
TARGET_URL | https://example.com |
STAGING_URL | https://staging.example.com |
LOGIN_URL | https://example.com/login |
SCAN_USERNAME | Service account username |
SCAN_PASSWORD | Service account password |
LEVOAI_AUTH_KEY | Levo auth key |
LEVOAI_ORG_ID | Organization ID |
GitLab CIโ
dast:
image: docker:24
services: [docker:24-dind]
stage: test
script:
- |
docker run --rm --shm-size=1g \
-v "$PWD/levo-dast.yml:/work/levo-dast.yml:ro" \
-w /work \
-e TARGET_URL -e SCAN_USERNAME \
-e LEVOAI_AUTH_KEY -e LEVOAI_ORG_ID \
levoai/levoai-shadownet:stable \
scan --config /work/levo-dast.yml \
--password "$SCAN_PASSWORD" \
--ci --fail-on high
Best practicesโ
- Service accounts for auth โ don't scan with a developer's personal account.
- Scan staging, not production. If you must scan prod, follow Scanning production safely.
- Set
fail_ondeliberately.highblocks pipelines on high/critical;criticalonly blocks on critical. Pick the one your team will actually fix, not the one that looks strictest. - Schedule deep scans off-peak and keep PR scans fast (
depth: smart, lowmax_pages). - 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โ
- CLI reference โ every flag and exit code.
- YAML schema reference โ every YAML field.
- Levo dashboard โ findings triage and reporting.
- Configuration โ environment variables and advanced scan settings.
Was this page helpful?