Summary
In the Azure WAF severity table (firewatch_azure_waf/severity.py), cap individual rule-match
events at medium (attack-class categories LFI/RFI/PHP/XSS/SQLi/RCE/Log4j currently map to
high/critical per match); the CRS anomaly-threshold refinement is unchanged (anomaly score
≥ 5 → high, ≥ 30 → critical) and becomes the sole carrier of the queue assertion — which is
CRS's own aggregate verdict event.
Why
CRS's own architecture (quoted verbatim in
ADR-0069
D4c from coreruleset.org) decouples per-rule detection from the verdict: "If a rule matches, no
immediate disruptive action is taken… the matched rule contributes to a transactional anomaly
score." Commodity bot-sprayed SQLi/XSS/Log4j probe strings are ambient on any public site; at
high/critical per match, each probe actor gets a Tier-2 ticket
(ADR-0067
D1(b)). Nothing real is lost: a single CRITICAL CRS rule contributes 5 points = the default
inbound threshold, so any genuine attack matching even one attack rule also produces the
threshold event, which queues.
Context
- ADR-0069 D4(c) — including its honest caveat: there is no in-tree ambient WAF capture;
the table is derived from CRS structure. The live juiceshop (Terraform) run calibrates it
before the announcement gate — that is why this is M3, not M1. If observation contradicts
the derivation, this issue reopens with data before implementation.
- The recon floor (scanner/protocol/bot/rate/geo → low) is already correct and unchanged.
Acceptance criteria
Out of scope
Summary
In the Azure WAF severity table (
firewatch_azure_waf/severity.py), cap individual rule-matchevents at
medium(attack-class categories LFI/RFI/PHP/XSS/SQLi/RCE/Log4j currently map tohigh/critical per match); the CRS anomaly-threshold refinement is unchanged (anomaly score
≥ 5 → high, ≥ 30 → critical) and becomes the sole carrier of the queue assertion — which is
CRS's own aggregate verdict event.
Why
CRS's own architecture (quoted verbatim in
ADR-0069
D4c from coreruleset.org) decouples per-rule detection from the verdict: "If a rule matches, no
immediate disruptive action is taken… the matched rule contributes to a transactional anomaly
score." Commodity bot-sprayed SQLi/XSS/Log4j probe strings are ambient on any public site; at
high/critical per match, each probe actor gets a Tier-2 ticket
(ADR-0067
D1(b)). Nothing real is lost: a single CRITICAL CRS rule contributes 5 points = the default
inbound threshold, so any genuine attack matching even one attack rule also produces the
threshold event, which queues.
Context
the table is derived from CRS structure. The live juiceshop (Terraform) run calibrates it
before the announcement gate — that is why this is M3, not M1. If observation contradicts
the derivation, this issue reopens with data before implementation.
Acceptance criteria
Matched/Detected/AnomalyScoringevent is normalized, its severitySHALL be at most
medium(attack-class categories high|critical → medium; existing lowtiers stay low).
≥ 30 → critical.
via D1(b) — routing test through the real qualify gate.
Inbound Anomaly Score Exceeded(threshold) actor SHALL still queue.explicitly record the Maintainer's decision to proceed without it).
tests/golden/fixtures/expected_scores.jsonSHALL be byte-identical.Out of scope
_ACTION_MAP/ action semantics (Detection vs Prevention mode handling stands).normalize()/severity-table change only.