Module 4 Release Notes — Vulnerability Engine
Full decision record: docs/adr/0004-vulnerability-engine-module.md.
Features added
Backend — the Vulnerability Engine: Recon Assets → Scan Jobs → external scanners → normalized, deduplicated Findings → auto-attached Evidence → Activity Timeline → events reserved for a future AI hook:
- 5 scanners at launch — Nuclei, ffuf, dirsearch, feroxbuster, Nikto —
behind one
ScannerRunner/Normalizerabstraction (direct analog of Module 3'sToolRunner/Normalizer); extension points reserved (enum values + compatibility map, nothing implemented) for SQLMap, XSStrike, Dalfox, Wapiti, and OWASP ZAP. - Job System: Scan Queue, Retry, Cancel, Pause/Resume (new — the currently running scanner always finishes before a pause takes effect), Priority (0–100, higher claimed first), Worker Heartbeat, Dead Worker Recovery — all sharing the existing worker OS process with Module 3's Recon poller rather than a second binary.
- Unified
VulnFindingmodel: Title, Description, Severity (Info → Low → Medium → High → Critical), CVSS score + vector, CWE, OWASP Category, Scanner + Scanner Version, Confidence, Status, Target/Project/Workspace, Evidence (6 named FKs into the existing Evidence model), References, Tags, Created By/At, Updated At. - Finding Status lifecycle: New → Confirmed / Duplicate / Accepted Risk / Fixed / False Positive, single and bulk update endpoints.
- Deduplication: Target + Path + Scanner + Vulnerability Type + Signature, enforced by a DB unique constraint (same pure-function + constraint philosophy as Module 3's dedup).
- Evidence: HTTP Request, HTTP Response, Payload, Command, and Raw Output auto-attach as plain-text Evidence rows (Screenshot is reserved for a future visual-capture scanner; no v1 scanner produces one) — no new blob storage integration needed for this module.
- Events:
VulnScanJob{Created,Started,Completed,Failed,Cancelled,Paused, Resumed}Event,VulnFinding{Created,Updated,Confirmed,Resolved}Event— all recorded on the existing Activity Timeline via the same generic handler every other module already uses. - REST API: Start/Stop/Pause/Resume/Retry Scan, Findings (filter + search +
bulk status update), Finding Details, Evidence, Scanner History, Scan
Templates (CRUD) — fully documented in Swagger (
@ApiTags('vuln')).
Frontend — complete Vulnerability Dashboard:
- Pages:
/vuln(dashboard: severity chart, scanner status, recent findings),/vuln/jobs+/vuln/jobs/[jobId](job list/detail with Pause/Resume/Stop/Retry, per-scanner progress, live SSE log panel),/vuln/findings+/vuln/findings/[findingId](server-filtered findings table with search/severity/status/scanner filters, row-selection bulk actions, classification/evidence/timeline detail view),/vuln/templates(Scan Templates CRUD),/vuln/scanners(Scanner History). - Live Updates (React Query polling + the same manual SSE read pattern as
Recon), Search, Filters, Bulk Actions, a
/keyboard shortcut to focus search plus Escape/Ctrl+A on the findings table, Dark Mode and responsive layout (both inherited from the existing shell — no new theme work). - Integrated into the existing Target detail page (new Vulnerabilities card, parallel to the Recon card) and Project detail page (new Vulnerabilities tab, alongside Scan History).
Architectural improvements
- Worker Heartbeat / Dead Worker Recovery is a genuinely new pattern versus Module 3: orphan sweeping now keys off heartbeat staleness rather than claim-age alone, more accurate for scans that legitimately run much longer than Recon's tools typically do.
- Pause proven as a second, gentler primitive alongside Cancel — same
AbortController-based hard-kill for Cancel/Timeout, a purely DB-polled "finish the current scanner, then stop" mechanism for Pause. Not needed by Module 3, now available as a pattern for any future job-based module. - Evidence model reuse validated end-to-end without new storage
infrastructure — six nullable named FKs into Module 2's existing
Evidencemodel, two additiveEvidenceTypeenum values (PAYLOAD/COMMAND), zeroAttachment/StorageServiceinvolvement. - One shared worker process now runs two independent poll loops — proof that future job-based modules can add a poller to the existing worker rather than each needing its own deployment unit.
Breaking changes
None. Every addition is additive: new enums, new tables, two new
(non-breaking) EvidenceType values, a new module wired into the existing
AppModule/WorkerModule. No Module 1–3 endpoint, contract, or schema
column changed.
Known limitations
- No blob-storage-backed Evidence path exists for Vuln yet — contingent on every v1 scanner producing only plain text. The first visual-capture scanner (most likely OWASP ZAP) will need to revisit this.
- The Finding Details page's "Timeline" is built from the finding's own
timestamps (first/last seen, created/updated), not a proper "Activity
filtered by this entity" view — the Activity module doesn't expose an
entityIdfilter yet. - Scanner History / Dashboard summary are computed on read (aggregation
queries over
VulnScanJobToolRun/VulnFinding), not maintained as running counters — acceptable at current data volumes, revisit if this becomes a hot path. - SQLMap, XSStrike, Dalfox, Wapiti, and OWASP ZAP are enum-and-compatibility- map placeholders only — no adapter code exists for any of them.
- Full
pnpm build/lint/check-types/testverification for this module was run by the project owner outside this environment (the sandbox this work was authored in cannot fetch Prisma engine binaries or reliably run a full Next.js typecheck) — see the ADR's Consequences section and the commit history for the verification pass.
Recommended next module
AI Assistant (Module 5), per the existing rollout order in
PROJECT_SPEC.md — the first module with real events to summarize
(VulnFindingCreatedEvent, VulnScanJobCompletedEvent, and Module 3's
equivalents) rather than a cold start. Per explicit instruction, work stops
here pending approval to proceed.