Commit Graph

2 Commits

Author SHA1 Message Date
Oliver Kieselbach 98fd173fb8 rules+backend+mcp: rule-verify sweep fixes (dead factor, {{token}} evidence, stats cap, list stringify)
Full verify pass over all 54 built-in rules (validate_rule sweep, 30d/7d
get_rule_stats, test_analyze_rule spot checks). Fixes:

- ANALYZE-APP-007 v1.1.0: declare enrollment_failed as a non-required
  condition — the exists-factor referenced an undeclared signal and could
  never apply.
- ANALYZE-APP-014 v1.1.0: add non-required event_data conditions for
  espCategory/espErrorCode/topCandidatePackage/candidateCount/confidence
  so all explanation {{tokens}} resolve instead of rendering literally.
- ANALYZE-ENRL-002 v1.2.0: capture session_timeout.timeoutHours so
  {{timeoutHours}} interpolates (verified via dry-run: renders "5").
- RuleEngine TryFlatLookup/ResolveDotPath: flatten IEnumerable data
  values to a comma-joined string — List fields (e.g. app_tracking_summary
  .pendingNames) stringified to "System.Collections.Generic.List`1[…]",
  breaking contains-matching and {{pendingNames}} interpolation.
- GetRuleStatsAsync: raise the 500-row cap to 10000 and log when hit.
  The scan walks dates ascending, so the old cap silently dropped the
  NEWEST dates of a 30d window — newly added rules (e.g. ANALYZE-SEC-008)
  were entirely missing from global stats.
- MCP validate_rule lint: accept event_data_array itemField as a {{token}}
  resolver — {{identity}} in ANALYZE-SEC-005/006 was a false positive.

Backend 4157 tests green, MCP 548 green + tsc clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:50:29 +02:00
Oliver Kieselbach aa748cd212 feat(backend): RuleEngine dot-path resolver + synth vulnerability_report
Two related gaps that blocked ANALYZE-ID-003:

(1) RuleEngine.GetDataFieldValue did flat dictionary lookup only — rules
    targeting nested payloads (scan_summary.critical_cves) had no way to
    descend. Extended with a dot-path resolver that walks both
    Dictionary<string,object> (in-process typed sidecar) and JsonElement
    (rehydrated-from-storage) trees, case-insensitive at each level, and
    bails cleanly when a path lands in a scalar. Flat lookup runs first
    so legacy events that wrote literal-dot keys still match.

(2) Vulnerability findings only lived in the VulnerabilityReports table
    — never as an event — so analyze rules had no surface to fire on.
    EventIngestProcessor now synthesises a vulnerability_report event
    after a successful StoreVulnerabilityReportAsync, carrying the
    scan_summary slice + reportAvailable + phase. Severity is mapped
    from overall_risk (critical/high/medium/-> Critical/Error/Warning).
    The full per-CVE list stays in the table.

Rerun-after-emit: the end-of-session rule analysis at line 205 is
triggered by classification.CompletionEvent and may finish before the
async vulnerability task does. ReanalyzeAfterVulnerabilityEmitAsync
runs AnalyzeSessionAsync(reanalyze=false) right after the synth-emit;
existing results are skipped (no duplicates), un-evaluated rules pick up
the new event, and SignalR fires NotifyRuleResultsAvailable so the UI
updates without manual refresh.

Tests: RuleEngineDataPathResolverTests (7 cases — flat/Dict-descent/
JsonElement-descent/case-insensitive/missing-path/scalar-descent) +
existing 752 backend tests still green.

Legacy IngestEventsFunction path intentionally not mirrored — the V1
agent ingest is on its way out, no need to backport the synth-emit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:55:50 +02:00