Skip to main content

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.

  1. EASM discovers web apps (its httpx stage produces web-app assets).
  2. 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).
  3. 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.
  4. 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

StageScannerConsumesWhat it does
1dast.katanaDAST surfaceActive crawl of each discovered web app → endpoint/parameterized URLs. Bounded: depth 3, concurrency 10, rate-limit 100/s, root-domain scope, max 5000 URLs.
2dast.waybackurlsDAST surfacePassive historical URL discovery (Wayback / Common Crawl) for the surface hosts. No traffic to the target.
3dast.nucleiURLsActive fuzzing — Nuclei dast/ + http/fuzzing/ templates with -dast.
4dast.nuclei_cveURLsActive 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).