Added
- Timeline brushing + evidence-graph filters — drag a time-range on the swimlane to scope correlated events; evidence graph gains a severity floor + SVG export to declutter/share large graphs (#122).
- Dashboard view presets — a toolbar ⊞ view button (popover menu) with built-in Analyst / Lead / Executive (role) and Triage / Report / Deep-Dive / Hunt-Prep (phase) layouts that show/hide/reorder existing panels (reusing the section machinery), apply a per-view severity floor + top-N cap to findings and the timeline, and link each to a matching report template; remembered per-case. Views are fully editable — built-ins editable in place + create/delete custom ones in Settings → Dashboard Views (GLOBAL store beside
cases/, mirrors report templates) (closes #142). - Persistent case memory — synthesis now logs each run to the Investigation Log (durable, cross-session, never wiped); a known-unknowns block (timeline gaps, uncovered ATT&CK phases, lookalike actors' likely-next techniques) grounds synthesis + hunt suggestions in what's MISSING; opt-in
DFIR_SYNTH_ADVERSARY_HINTSfeeds candidate threat actors into the synthesis prompt as labelled hypotheses (closes #165). - Skip AI for disabled report sections — the Executive summary and narrative generators no longer spend tokens when their section is disabled in the case's report template (the route returns a 409 with the reason instead of generating); a hidden dashboard Executive Summary panel also won't generate. Already-saved content is preserved (closes #168).
- IOC filter by type — the dashboard IOC panel gets a "▾ Types" facet dropdown (ip/domain/url/hash/file/process/other) with per-type counts, composing with the existing flagged-only + search filters; client-side only, mirrors the timeline's source filter (closes #169).
- Geographic IP map — dashboard 🌍 panel plotting geo-located IP IOCs on a Leaflet world map (severity colors, victim→attacker flows, country stats, timeline sync, CSV export) + a §4.10 report section; coordinates from the GeoIP enrichment, no new external auto-calls (closes #133).
- Geo country-centroid fallback — IPs whose GeoIP enrichment has a country but no precise city coordinates (e.g. enriched before this feature) fall back to the country centroid and are shown with a dashed, faint marker flagged "country-level (approx)" in the popup and CSV; regenerate the offline table via
npm run data:update-geo(DFIR_COUNTRY_CENTROIDS_URL) (part of #133). - Hypothesis-driven investigation mode — the Hypotheses panel is the single home for status-tracked hypotheses (open / supported / refuted / unknown), auto-generated on synthesis and analyst-authored, with evidence/technique links and a report section; auto ones refresh on re-synthesis but freeze once edited, open analyst hypotheses steer the next synthesis, and any notebook note/question promotes into one via "→ Hypothesis" (the notebook's own
hypothesistype was folded in). Survive synthesis, travel in snapshots (closes #140). - Supporting events & IOCs per finding — each finding now lists the forensic events that back it (click to jump to the timeline row, even across pagination/filters) plus its supporting IOC values, derived client-side from the case state (part of #139).
- Hunting feedback loop — deployed hunts now record their outcome per case (found new evidence yes/no + counts, survives restart); hunt suggestions exclude a VQL that already ran and pivot on what a productive hunt surfaced, and a dashboard "Hunting Profile" panel shows what was hunted / hit / missed — each row reporting the rows the hunt RETURNED (a hunt that re-confirms known-bad is a hit, not a miss) and expandable to view those results on demand, with auto-collect + a per-hunt "Collect now"/"Re-collect" so pending outcomes fill (closes #157).
- Regenerate a suggested fleet hunt — each AI fleet-hunt card gets a per-card ↻ Regenerate (like playbook hunts) to get a different VQL when one is bad/won't compile (part of #57).
Fixed
-
Asset↔IoC over-linking on IP substrings — the asset graph's description scan now matches IP IOCs with a digit/dot boundary, so
1.1.1.1no longer links inside11.1.1.10(nor192.168.1.1inside192.168.1.10), preventing inflated asset IoC/severity associations (same class as the geo-map fix, #133). -
Hunt VQL
hash()signature — the fleet/playbook hunt prompts now teach the realhash(path=…).SHA256form (no inventedhashselect=arg, which fails to compile) and to avoid full-disk globs, cutting "Velociraptor did not launch the hunt" errors (part of #57/#70). -
spawn EPERMlaunching a hunt — the velociraptor binary launch now retries a transient Windows lock (AV / sync client / concurrent spawn), so deploying a second hunt no longer fails with "spawn EPERM" (DFIR_VELOCIRAPTOR_SPAWN_RETRIES, default 6); a persistent EPERM/EACCES (antivirus/EDR blocking the process for a hunt whose VQL command line carries credential-dump indicators likelsass.dmp) now reports an actionable message — add an AV exclusion for the velociraptor binary, or run that hunt's VQL from the Velociraptor GUI. -
Corrupted
investigation.jsonon concurrent state saves —atomicWriteused a single fixed.tmpfilename, so two concurrent saves of the same case (a manual event/IOC add landing while background enrichment or re-synthesis also saves) shared one temp file and left a malformedinvestigation.json(valid JSON followed by the tail of the other write). Every state-loading endpoint then 500'd (/report,/ask,/import/undo,/executive-summary,/narrative,/second-opinion,/suggest-hunts,/memory/next-steps,/timeline-gaps/hypothesize). The temp file is now unique per call (uuid suffix) so each write lands in its own file and the final rename replaces the target atomically — worst case is a lost update, never a malformed file. -
Lost-update on manual event/IOC add during synthesis — a manual event/IOC added during the seconds-long synthesis AI call was clobbered by the synthesis save (which was derived from the pre-call snapshot), so a just-added indicator vanished. Added a per-case
StateLockthat serializes the short load->save critical sections (manual adds, background enrichment, synthesis), and synthesis now re-reads the latest state before persisting and carries forward events/IOCs/threads that are new since its snapshot (mirroring the existing pinned-questions re-load).