DAST — Dynamic Application Security Testing
The platform's second scanner module. Where EASM is non-intrusive discovery, DAST actively sends payloads (fuzzing + injection-class checks) to a web app to find SQLi / XSS / RCE / SSRF / SSTI and similar. DAST does not discover the attack surface itself — it consumes the web apps the EASM module discovered.
:::info Status
Built & validated (2026-06-14): the EASM-fed surface, gating, the crawl stage
(katana + waybackurls), and both active Nuclei tiers (fuzzing + injection-class
CVE), wired into a DAST pipeline — exercised end-to-end against live domains
(EASM discovery → surface → crawl → fuzz). The worker image bundles katana +
waybackurls; the shared CLI-tool harness lives in the toolbox (scanners/_proc.py),
so DAST borrows it from there, not from EASM.
:::
The EASM → DAST contract
DAST depends on EASM and cannot run without it, but it is never auto-run when EASM runs. EASM only feeds DAST the data; a user triggers the DAST scan.
- EASM discovers web apps (its httpx stage produces web-app assets).
- At the end of every EASM scan the engine promotes those web apps into the DAST surface — a dedicated per-org table. This is the explicit separation between discovery and active testing (populate only; no DAST scan starts).
- The API gates DAST on that surface: a DAST scan is rejected (
409) unless the org has a non-empty surface. The UI lists the discovered web apps and a "Run DAST" action once the surface exists. - When the user starts DAST, the pipeline crawls the surface into a wide URL set and runs the active scanners over it.
EASM scan ─(httpx)─> web-app assets ─promote─> DAST surface
│ (user triggers; gated)
▼
DAST scan: katana (active crawl) + waybackurls (passive) ─> URLs
▼
dast.nuclei (fuzzing) + dast.nuclei_cve (injection CVEs)
▼
Checks → Findings → Tasks (same normalizer)
The DAST pipeline
| Stage | Scanner | Consumes | What it does |
|---|---|---|---|
| 1 | dast.katana | DAST surface | Active crawl of each discovered web app → endpoint/parameterized URLs. Bounded: depth 3, concurrency 10, rate-limit 100/s, root-domain scope, max 5000 URLs. |
| 2 | dast.waybackurls | DAST surface | Passive historical URL discovery (Wayback / Common Crawl) for the surface hosts. No traffic to the target. |
| 3 | dast.nuclei | URLs | Active fuzzing — Nuclei dast/ + http/fuzzing/ templates with -dast. |
| 4 | dast.nuclei_cve | URLs | Active injection-class CVE templates (the intrusive sqli / rce / … set EASM excludes). |
Stages 1–2 expand the URL set; stages 3–4 test the full crawled set. Findings flow through the same normalizer / finalization pass as EASM (the injection-class knowledge-base entries already exist).
Safety
DAST is intrusive by nature, so:
- It only runs against the EASM-discovered surface for an org — never an arbitrary URL — and only when DAST is enabled for that org.
- The crawl is bounded (depth / concurrency / rate-limit / scope / max-URLs) so it never hammers or wanders off a customer's app.
- It is on-demand: the user explicitly starts it; EASM never triggers it.
How to use it (API)
# 1) Run EASM (discovers web apps, populates the DAST surface)
POST /api/v1/org/scans { "module": "easm" }
# 2) Inspect the surface / whether DAST can run
GET /api/v1/org/dast/surface -> [ { url, host, state, ... } ]
GET /api/v1/org/dast/status -> { enabled, surface_count, can_run }
# 3) Run DAST (gated on a non-empty surface; 409 if EASM hasn't run)
POST /api/v1/org/scans { "module": "dast" }
dast must be in the org's enabled modules. The per-(org, module) scan cooldown
applies independently to DAST. See also Monitoring & Alerting
and Logging & SIEM.
Roadmap
Built first cut. Next: an httpx liveness filter on crawled URLs (waybackurls can return dead endpoints), per-target auth/session input for authenticated DAST, and optional direct URL seeding (today DAST always starts from the EASM surface).