levo-dast.yml schema reference
Every top-level key in levo-dast.yml, grouped by concern. Unknown keys cause a load error (extra = "forbid"), so typos surface immediately.
Top-level keys
| Key | Purpose |
|---|---|
version | Schema version. Always "1" for now. |
name | Logical scan / app name used in reports. |
env | Environment label (staging, prod, dev, …). |
target | URL and scope (capture / exclude domains). |
crawl | Crawler type, bounds, and crawl timeout. |
auth | Strategy + non-secret auth fields. |
scan | Phase toggles, injection controls, timeouts, and per-category active-test flags. |
cve | JS, DOM, and nuclei CVE checks. |
chatbot | Inline chatbot testing. |
ai | LLM provider (key stays in env). |
satellite | On-prem traffic export. |
reporting | Output format, severity, fail_on, SaaS push. |
app.*, vulnerabilities.*, timeouts.*, and levo.* are not top-level blocks. name/env are bare top-level keys; active-test categories nest inside scan.active_testing_categories; timeouts nest inside scan and crawl; Levo IDs are secrets and come from CLI/env.
version, name, env
version: "1"
name: "acme-webapp"
env: "${DEPLOY_ENV:-staging}"
| Field | Type | Default | Notes |
|---|---|---|---|
version | string | required | Schema version. |
name | string | required | Logical name used in reports. |
env | string | "default" | Environment label, ${VAR} interpolation supported. |
target
target:
url: "https://app.example.com"
start_page: "/login"
ignore_third_party: true
capture_domains:
- "*.example.com"
exclude_domains:
- "analytics.example.com"
| Field | Type | Default | Notes |
|---|---|---|---|
url | string | required | Base URL. Domain must be an owned domain. |
start_page | string | / | Path to start crawling from. |
ignore_third_party | bool | true | Skip hosts outside your org. |
capture_domains | string[] | [] | Extra in-scope hosts (glob patterns). |
exclude_domains | string[] | [] | Hosts to skip. |
crawl
crawl:
type: "hybrid"
max_depth: 5
max_pages: 200
max_clicks_per_page: 30
max_global_clicks: 1000
ai_form_filling: true
ai_form_policy: "smart"
timeout: 900
| Field | Type | Default | Notes |
|---|---|---|---|
type | enum | standard | standard · ai · hybrid · legacy. |
max_depth | int | 3 | Link-depth cap. |
max_pages | int | 100 | Upper bound on unique pages. |
max_clicks_per_page | int | 20 | AI-assisted click budget per page. |
max_global_clicks | int | 500 | Click budget across the scan. |
ai_form_filling | bool | false | Let an LLM fill unknown forms. |
ai_form_policy | enum | smart | smart · aggressive · off. |
timeout | int | 900 | Crawl phase timeout, seconds. |
auth
auth:
strategy: "form"
username: "${SCAN_USERNAME}"
login_url: "https://app.example.com/login"
wait_for_mfa: false
headers:
- "X-Scan-Source: shadownet"
| Field | Type | Default | Notes |
|---|---|---|---|
strategy | enum | none | none · form · token · oauth · ai. |
username | string | — | Non-secret half of form auth. |
login_url | string | — | Required for form and most ai flows. |
wait_for_mfa | bool | false | Pause for MFA in interactive mode. |
headers | string[] | [] | Extra request headers. Non-secret only. |
password, token, raw cookie values, and local_storage_b64 are rejected. Use --password, --token, or env vars (SCAN_PASSWORD, SCAN_TOKEN, …).
scan
scan:
enable_passive: true
enable_active: true
enable_websocket: true
enable_graphql: true
enable_ai: false
attack_strength: "medium"
max_payloads: 100
active_delay_ms: 50
http_methods: ["GET", "POST", "PUT", "DELETE"]
inject_locations: ["query", "body", "header", "path"]
depth: "thorough"
tech_aware: true
timeout: 3600
test_timeout: 30
probe_timeout: 15
active_testing_categories:
xss: true
sqli: true
path_traversal: true
ssrf: true
xxe: true
command_injection: true
open_redirect: true
csrf: true
idor: true
| Field | Type | Default | Notes |
|---|---|---|---|
enable_passive | bool | true | Response analysis only (prod-safe). |
enable_active | bool | true | Sends injection payloads. |
enable_websocket | bool | true | Test WS/WSS endpoints. |
enable_graphql | bool | true | Introspect and fuzz GraphQL. |
enable_ai | bool | false | LLM triage of findings. |
attack_strength | enum | medium | low · medium · high · insane. |
max_payloads | int | 50 | Active payloads per endpoint. |
active_delay_ms | int | 0 | Throttle between payloads. |
http_methods | string[] | ["GET","POST","PUT"] | Verbs to exercise. |
inject_locations | string[] | ["query","body","header","cookie","path"] | Injection points. |
depth | enum | smart | smart (default) · thorough. |
tech_aware | bool | true | Skip tests irrelevant to detected stack. |
timeout | int | 3600 | Overall scan budget, seconds. |
test_timeout | int | 30 | Per-test timeout, seconds. |
probe_timeout | int | 15 | Per-probe timeout, seconds. |
active_testing_categories | map | all true | Per-category toggles; only applied when enable_active: true. Omit categories to keep them on. |
scan.active_testing_categories
Nested map controlling individual active-test categories:
| Key | Default | Tests |
|---|---|---|
xss | true | Cross-site scripting (reflected, stored). |
sqli | true | SQL injection (error-based, time-based, union). |
path_traversal | true | ../ / LFI / directory traversal. |
ssrf | true | Server-side request forgery. |
xxe | true | XML external entity. |
command_injection | true | OS command injection. |
open_redirect | true | Unvalidated redirects. |
csrf | true | CSRF token absence / weakness. |
idor | true | Insecure direct object reference. |
cve
cve:
js: true
dom: true
nuclei_templates: "cves/,exposures/"
| Field | Type | Default | Notes |
|---|---|---|---|
js | bool | false | JavaScript library CVE scanning. |
dom | bool | false | DOM-based XSS and client injection. |
nuclei_templates | string | — | Nuclei template paths (comma-separated). |
chatbot
chatbot:
inline: true
test_timeout: 120
max_count: 5
| Field | Type | Default | Notes |
|---|---|---|---|
inline | bool | false | Test embedded chatbot widgets. |
test_timeout | int | 60 | Per-turn timeout (seconds). |
max_count | int | 3 | Cap on detected chatbots to test. |
ai
ai:
provider: "anthropic"
| Field | Type | Default | Notes |
|---|---|---|---|
provider | enum | anthropic | anthropic · openai. API key in env (ANTHROPIC_API_KEY / OPENAI_API_KEY). |
satellite
See Install Satellite for how to stand one up.
satellite:
enabled: true
url: "https://satellite.levo.ai"
span_buffer_size: 1000
flush_interval: 10
| Field | Type | Default | Notes |
|---|---|---|---|
enabled | bool | false | Route scan traffic through a Levo Satellite. |
url | string | — | Satellite HAProxy URL. |
span_buffer_size | int | 1000 | In-memory span buffer. |
flush_interval | int | 10 | Flush interval, seconds. |
reporting
reporting:
output: "sarif"
output_file: "shadownet-report.sarif"
severity: "low"
fail_on: "high"
send_issues: true
send_issues_interval: 60
| Field | Type | Default | Notes |
|---|---|---|---|
output | enum | table | table · json · sarif · quiet. |
output_file | string | — | Destination path (required for sarif/json). |
severity | enum | low | Minimum severity to report. |
fail_on | enum | — | CI exit-code threshold (critical · high · medium · low). |
send_issues | bool | false | Push findings to Levo SaaS (needs --env-id). |
send_issues_interval | int | 60 | Push interval in seconds. |
Secrets — never in YAML
These must come from CLI flags or env vars, not the file:
| Category | Keys / env vars |
|---|---|
| Levo identity | --org-id / LEVOAI_ORG_ID, --workspace-id / LEVOAI_WORKSPACE_ID, --env-id / LEVOAI_ENV_ID, --app-id / LEVOAI_APP_ID, LEVOAI_AUTH_KEY |
| Auth creds | --password / SCAN_PASSWORD, --token / SCAN_TOKEN, raw cookies, local_storage_b64 |
| LLM / third-party | ANTHROPIC_API_KEY, OPENAI_API_KEY, CAPSOLVER_API_KEY, INTERACTSH_SERVER_URL, INTERACTSH_SERVER_AUTH_TOKEN |
Validation errors
The loader reports errors with the YAML path of the offending field:
levo-dast.yml:scan.attack_strength: value 'extreme' is not a valid enum
(expected: low, medium, high, insane)
Typos surface the same way (thanks to extra = "forbid"):
levo-dast.yml: unknown field 'scann' at top level
(did you mean 'scan'?)
Secret keys are rejected with an actionable hint:
levo-dast.yml:auth.password: secrets not allowed in YAML
→ use --password or SCAN_PASSWORD env var
Next
- Examples & recipes.
- CLI reference for the matching flags.