Ga naar inhoud

Drift

A scan tells you what posture a domain has now. A pair of scans tells you what changed. Wanderer surfaces those changes as drift Findings: they live in the same store, behind the same APIs, and are consumed by the assessor like any other Finding.

When drift runs

Drift runs automatically after every scheduled scan finishes (see scheduling.md). It also runs on demand via wanderer diff <scan-a> <scan-b> — that variant prints the would-be Findings to stdout without persisting anything.

The drift engine compares the new scan against the most recent previous scan of the same target. The first scan of a target has nothing to compare against; the engine emits a single drift.baseline_established Finding so an operator can distinguish "new target, no history" from "no changes since last scan" (which emits drift.no_changes).

Rules

A drift rule is a Go function func(prev, curr *models.Scan) []models.Finding. It looks at specific Findings in the two scans and decides whether a change is surface-worthy. The MVP rule set:

Rule Triggers on Severity Dimension
drift.tls.issuer_changed tls.issuer.issuer_cn differs finding juridisch
drift.tls.days_left_dropped tls.validity.days_left crosses the 30-day boundary down concern operationeel
drift.dns.mx_set_changed set of dns.mx.host values differs observation data_ai
drift.dns.ns_set_changed set of dns.ns.host values differs observation operationeel
drift.ip.country_changed ip.asn.country differs for the same host subject finding juridisch
drift.http.third_party_added new subject appears in http.third_party Findings observation technologie
drift.http.third_party_removed subject disappears from http.third_party Findings info technologie

Adding a rule: add a function to internal/drift/rules.go, append it to DefaultRules, write a test in rules_test.go with fabricated prev / curr scans. Rules MUST be total: missing or mistyped attributes return nil rather than panicking. The drift engine itself does not currently isolate panics — keep your rule code disciplined.

Attributes carried on every drift Finding

source_modus: "drift"
prev_scan_id: "<scan id of the comparison baseline>"
curr_scan_id: "<scan id of the new scan>"

Plus any rule-specific keys (added, removed, prev_issuer_cn, …). See findings.md for the full catalogue.

How the assessor reads drift

The assessor treats drift Findings exactly like probe Findings: a rule whose Match function inspects Finding.Attributes works the same on tls.issuer and on drift.tls.issuer_changed. There is no parallel "drift assessment" pipeline.

A future rule may explicitly act on drift, e.g. "a juridisch- relevant drift Finding in the last 30 days reduces the dimension's score regardless of current posture". That is a one-rule change when the demand is concrete.

wanderer diff

wanderer diff <scan-a> <scan-b>

Computes the same Findings the scheduler would persist, prints them as markdown, and does not touch the store. Use it to inspect historical scans without firing fresh ones.

The output is deterministic: same input scans → same markdown bytes, sorted by ProbeID then Subject. That makes it diff-friendly itself.