Design¶
Why draventis is shaped the way it is. This is the reasoning behind the code, not a how-to (see Deployment and Configuration for that).
Headline decisions¶
- ZAP is the primary engine, Nuclei the complement. ZAP (via the Automation
Framework) is the deep, stateful, authenticated crawler + active scanner.
Nuclei is the fast, low-false-positive templated layer (known CVEs, exposures,
misconfig) with optional
-dastfuzzing. Both are FOSS and, critically, both have first-class DefectDojo parsers (ZAP ScanXML,Nuclei ScanJSON). - DAST runs as a scheduled CronJob, not in CI on a PR, because DAST needs a running target. Your cluster already runs your apps; scan those.
- Baseline into DefectDojo, not diff-gated. DAST is non-deterministic and has no merge-base to diff against, so draventis does not try to be a PR gate. It reimports on a schedule and lets DefectDojo compute drift (see below).
- Non-destructive by default.
baseline(passive) is safe against production;full/api(active) must target staging only.
What DAST adds over static analysis¶
Static analysis reasons about source and artifacts at rest. It never sends an HTTP request to a running instance.
| Class | Why static analysis misses it | DAST catches it |
|---|---|---|
| AuthN/session | Emergent from config + runtime state (cookie flags, fixation, IDOR) | Exercises real auth flows, replays/swaps sessions |
| Injection reachable at runtime | SAST flags potential sinks; can't prove exploitability through the deployed stack | Actively fuzzes params → confirmed SQLi/XSS with a payload |
| Response headers / TLS / cookies | Set by ingress + app at runtime, not in the repo | Observes the actual CSP, HSTS, cookie flags, TLS config |
| CORS misconfig | Depends on deployed origin-reflection logic | Sends cross-origin preflights, detects ACAO: * + credentials |
| SSRF / OOB | Needs an actual outbound request from the running app | OOB interaction (callback server) proves it |
| Business-logic / API surface | No spec-awareness | Imports OpenAPI, drives operations, finds undocumented endpoints |
| Config drift | Repo ≠ what's actually deployed | Tests the live endpoint as an attacker sees it |
Tool evaluation¶
| Tool | License | Best at | DefectDojo parser | Verdict |
|---|---|---|---|---|
| OWASP ZAP | Apache-2.0 / $0 | Full active scan, auth, OpenAPI/GraphQL/SOAP import, Automation Framework | ZAP Scan (XML) |
PRIMARY |
| Nuclei | MIT / $0 | Fast known-CVE + exposure + misconfig; templated OOB fuzzing (-dast) |
Nuclei Scan (JSON) |
COMPLEMENT |
| Wapiti | FOSS / $0 | Lightweight black-box param fuzzing | Wapiti Scan |
Optional third opinion |
| Nikto | FOSS / $0 | Server/CGI misconfig | Generic/CSV only | Low value when a modern ingress fronts everything |
| Schemathesis | FOSS / $0 | Property-based API testing from OpenAPI/GraphQL | Generic / JUnit XML | Adopt as a CI test for spec'd services |
| StackHawk | Commercial | Developer-DX API DAST in CI (ZAP-derived) | StackHawk HawkScan |
Redundant when ZAP+Nuclei cover it |
| Burp Suite | Commercial | Manual pentest + enterprise DAST | Burp XML |
Keep as a manual bench tool, not the automation engine |
Why ZAP + Nuclei (not one, not a commercial tool): breadth (Nuclei) + depth (ZAP), two native DefectDojo parsers, zero license spend. Commercial DAST buys DX you can reproduce with the Automation Framework + your own DefectDojo hub.
ZAP scan modes¶
The Automation Framework (a single declarative YAML plan) is the standard for
every draventis job; it replaced the old zap-baseline/zap-full-scan shell
entry points. draventis ships one plan per policy in automation/:
| Plan | Jobs | draventis policy |
|---|---|---|
zap-baseline.yaml |
spider + passive | baseline: nightly, prod-safe |
full-active.yaml |
+ ajax-spider + active | full: weekly, staging only |
api-scan.yaml |
OpenAPI import → spider → active | api: services with a spec |
DefectDojo ingestion¶
draventis POSTs each report to POST /api/v2/reimport-scan/ (multipart form)
with:
scan_type="ZAP Scan"/"Nuclei Scan"product_name+engagement_name+auto_create_context=true→ DefectDojo auto-creates the product/engagement/test on first run (no pre-provisioning).test_title: a stable dedupe key across runs.close_old_findings=true→ fixed alerts auto-mitigate per engagement.tags, optionalproduct_type_name.
Why reimport, not import¶
Reimport compares the incoming report to the existing test and won't create
duplicates; it reactivates regressions and, with close_old_findings,
mitigates alerts that disappeared. That yields "what's new / fixed since last
scan" for free (the drift signal DAST needs), without a merge-base diff.
Engagement/test modeling¶
Product: example/app
Engagement: "DAST - app-staging" (auto-created)
Test: "ZAP Scan" ← reimport nightly/weekly
Test: "Nuclei Scan" ← reimport nightly
DefectDojo stays the durable triage system of record: mark false positives / risk-accept there once, and reimport dedupe preserves that state across runs.
Where DAST runs¶
DAST needs a running target. The default placement is a scheduled CronJob against already-deployed environments: the cluster already runs everything, it fits a secrets-from-a-store idiom, and it decouples slow/destructive active scans from PR latency.
- Nightly: ZAP baseline (passive) on prod-facing + staging hosts, plus Nuclei templated/known-CVE (non-destructive).
- Weekly: ZAP full active + Nuclei
-dastfuzzing against staging only.
An optional per-PR ephemeral-env scan (spin the app up in CI, scan, gate) is worth it only when a service ships an OpenAPI spec and you want API-contract DAST on every change, or you already build preview environments. For most repos the scheduled staging scan catches the same classes a day later at a fraction of the cost.
Authenticated scanning¶
Most apps behind an OIDC proxy need draventis to authenticate. Do both:
- Scan behind the proxy (app-layer bugs): ZAP Browser-Based Authentication drives the real OIDC login in a headless browser and uses a poll-based verification strategy to re-authenticate when the session drops. Give your identity provider a dedicated low-privilege scan user.
- Scan the origin directly (the app's own attack surface): in-cluster, hit the Service directly, bypassing the proxy. This tests the app as if the proxy were removed. That's valuable, since a misconfigured route would strip your only authn layer.
Status: the config model and credential plumbing exist, but the shipped AF
plans don't yet include an authentication block. Adding it to a plan is
currently a per-target step.
Safety: targets and credentials come only from config you control, never from a scanned response. Active scans hit staging, never production.
Net-new philosophy: baseline, not diff-gated¶
DAST is baseline-only, deliberately not a merge-base diff gate:
- No merge-base to diff against: DAST runs against a deployed environment,
not a PR's changed lines. There is no
git difffor "this response header regressed." - DAST is non-deterministic: crawler coverage, timing, and active-scan payload success vary run to run; a naive "new since last run" gate would flap.
- DefectDojo already gives the safe version of net-new: reimport dedupe +
close_old_findingsyields reactivated (regressed) and new finding states without a merge-base. Pair that with SLA tracking so new Highs get a clock.
The one place a gate belongs is the optional per-PR ephemeral packaging, and even there gate on ZAP's own new-alert delta vs a committed baseline, not a SARIF merge-base diff.
Container & hardening¶
The image builds on the official ZAP image (ZAP + JRE + headless browsers for
browser-based auth) plus the Nuclei binary and the draventis package. It runs
non-root with seccompProfile: RuntimeDefault and all capabilities dropped;
only /zap/wrk (an emptyDir) is writable. draventis needs no Kubernetes API
access (it only makes HTTP requests to targets and DefectDojo), so its
ServiceAccount has no RBAC and does not mount a token: a deliberately minimal
blast radius.
Rollout (crawl / walk / run)¶
A safe way to adopt draventis incrementally:
- Crawl: one nightly
baselinetarget against a safe host; prove the ZAP reimport pipe lands in DefectDojo withauto_create_context. - Walk: enable Nuclei; wire browser-based OIDC auth for one staging app; add the weekly full-active CronJob against staging only.
- Run: expand
targets.yamlto all apps (passive prod / active staging); addopenapi+ Nuclei-dastAPI scanning and Schemathesis for spec'd services.