Phase 5: deterministic detection rules and time-window correlation #6

Merged
hannah-vernon merged 1 commit from feature/deterministic-analysis into dev 2026-08-20 15:05:15 -05:00

What does this PR do?

Implements the Phase 5 first increment: deterministic detection rules and time-window correlation, turning individual normalized events into scored, correlated incidents. No LLM involvement anywhere in this path.

  • Detection (src/Viegard.Application/Detection/): IDetectionRule port; HTTP rules (sensitive-path probing, path traversal incl. encoded variants, SQL-injection/command-injection/XSS indicators, suspicious User-Agents, unusual methods, error-status supporting evidence) and conservative mail rules (link count, from/link domain mismatch, reply-to mismatch, double/executable attachment extensions). All term lists and scores configurable via Viegard:Detection with working code defaults. Hostile input is matched with linear Contains/IndexOf scans only: no regex on untrusted data, so catastrophic backtracking is impossible by construction.
  • Correlation (TimeWindowCorrelator): folds scored events into incidents keyed by source IP (ip=<value>) or mail sender, with a configurable window (default 10 min), window extension on continued activity, context-event appending, and caps on member events/evidence with explicit truncation markers. Benign traffic never creates incidents.
  • CorrelationWorker: consumes the durable events queue (lease/complete/abandon; poison handling stays in the queue), gated on the correlation role (D-0011), audited at PipelineStage.Correlation.

Fixes #

How was this tested?

  • dotnet build Viegard.slnx - 0 errors, 0 warnings
  • dotnet test Viegard.slnx - 163/163 pass including the 17 live PostgreSQL integration tests (disposable postgres:17 container; schema reset before run)
  • 23 new tests: per-category fire/no-fire, encoded traversal variants, prompt-injection UA text scored as data without exceptions, 8KB hostile URI completes without hanging, correlator create/append/extend/expiry/caps/separate-IP behavior
  • Pipeline host smoke test: boots with Correlation worker started

Implementation was drafted by a coding agent against a detailed spec, then independently reviewed (diff + security spot-check on untrusted-input matching) and re-verified by a second full test run.

Checklist

  • I have read the Contributing Guide
  • Changes are focused - one logical change per PR
  • Documentation updated (AGENT-README.md, TODO.md)
  • New dependencies: none
  • No secrets, credentials, or real infrastructure identifiers in the diff
  • No commented-out code or debug leftovers
## What does this PR do? Implements the Phase 5 first increment: deterministic detection rules and time-window correlation, turning individual normalized events into scored, correlated incidents. No LLM involvement anywhere in this path. - **Detection** (`src/Viegard.Application/Detection/`): `IDetectionRule` port; HTTP rules (sensitive-path probing, path traversal incl. encoded variants, SQL-injection/command-injection/XSS indicators, suspicious User-Agents, unusual methods, error-status supporting evidence) and conservative mail rules (link count, from/link domain mismatch, reply-to mismatch, double/executable attachment extensions). All term lists and scores configurable via `Viegard:Detection` with working code defaults. Hostile input is matched with linear `Contains`/`IndexOf` scans only: no regex on untrusted data, so catastrophic backtracking is impossible by construction. - **Correlation** (`TimeWindowCorrelator`): folds scored events into incidents keyed by source IP (`ip=<value>`) or mail sender, with a configurable window (default 10 min), window extension on continued activity, context-event appending, and caps on member events/evidence with explicit truncation markers. Benign traffic never creates incidents. - **`CorrelationWorker`**: consumes the durable events queue (lease/complete/abandon; poison handling stays in the queue), gated on the `correlation` role (D-0011), audited at `PipelineStage.Correlation`. Fixes # ## How was this tested? - [x] `dotnet build Viegard.slnx` - 0 errors, 0 warnings - [x] `dotnet test Viegard.slnx` - **163/163 pass including the 17 live PostgreSQL integration tests** (disposable postgres:17 container; schema reset before run) - [x] 23 new tests: per-category fire/no-fire, encoded traversal variants, prompt-injection UA text scored as data without exceptions, 8KB hostile URI completes without hanging, correlator create/append/extend/expiry/caps/separate-IP behavior - [x] Pipeline host smoke test: boots with `Correlation worker started` Implementation was drafted by a coding agent against a detailed spec, then independently reviewed (diff + security spot-check on untrusted-input matching) and re-verified by a second full test run. ## Checklist - [x] I have read the [Contributing Guide](../CONTRIBUTING.md) - [x] Changes are focused - one logical change per PR - [x] Documentation updated (AGENT-README.md, TODO.md) - [x] New dependencies: none - [x] No secrets, credentials, or real infrastructure identifiers in the diff - [x] No commented-out code or debug leftovers
Add configurable deterministic HTTP and mail detection rules for sensitive paths, traversal, injection indicators, suspicious User-Agents, unusual methods, error statuses, mail link heuristics, reply-to mismatches, and attachment metadata.

Add the time-window correlator, incident caps, evidence truncation, latest-open in-memory incident lookup, and the role-gated correlation worker that consumes queued event IDs and writes correlation audit records.

Add xUnit coverage for detection safety and correlation behavior, and update Phase 5 documentation.

Tests: 156 passed, 7 skipped.
Verification:
- dotnet build Viegard.slnx: 0 warnings, 0 errors
- dotnet test Viegard.slnx: 156 passed, 7 skipped
- dotnet run --project src\Viegard.PipelineHost --no-build: host booted and logged Correlation worker started

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
hannah-vernon/viegard-sentinel!6
No description provided.