Light WAF v0.4.0 — Decision-log score breakdown (drill-down data)
A fast, modular Layer-7 Web Application Firewall written in Rust, operating as a
reverse proxy: it inspects every HTTP request, accumulates a CRS-style anomaly score,
and decides Allow / Block (403) / Reject (400 | 429) before forwarding to the backend.
This is a small, additive release focused on observability. There are no changes to
detection — the corpus verdicts are identical to v0.3.0. The whole workspace is bumped to
0.4.0 together (uniform versioning).
What's new
Per-rule score_contributions in the decision-log (the headline)
When a request is blocked or rejected, the structured decision-log line now carries a
score_contributions field: a JSON array of the per-rule breakdown that produced the
verdict — [{ "module", "rule_id", "severity", "points" }, …]. Previously the log recorded
only the final rule_id + cumulative score; the full breakdown existed in memory but was
never emitted.
This is the data source for verdict drill-down (reconstructing why a request was
blocked, module by module). The field:
- is emitted only on the already-logged denied path (never per benign request, never on the
hot path); - is a JSON-string field, so a log consumer parses the line, then parses the field;
- reconstructs losslessly into the typed model —
waf_core::ScoreContributionand
waf_core::Severitynow deriveSerialize/Deserialize(severity is lowercased:
"critical" | "error" | "warning" | "notice"), so a downstream consumer can reuse the exact
types.
The field names and format are a stable ingestion contract (locked by a shape test),
alongside the Prometheus metrics ABI.
Compatibility
- Additive. New log field + new
Serialize/Deserializederives on two existing types.
No API removed or changed; existing code compiles and behaves identically. - Detection unchanged — validation corpus 10/10, 0% false positives at PL3, same
recall as v0.3.0 (this is a logging enrichment, not a detection change). clippy -D warningsclean; full workspace test suite green, including a bite-test that drives
a real blocked request through the proxy and asserts the enriched field is emitted and
reconstructs into the typed breakdown.
Install / upgrade
# the binary (command: `waf`)
cargo install waf-proxy # picks up 0.4.0
# or use the core as a library (open-core, no fork — embed via Proxy::builder + seams)
cargo add waf-core@0.4Published crates
| Crate | Role |
|---|---|
waf-core |
Base types, WafModule/Decision, StateStore seam |
waf-normalizer |
Decode + NFKC + parsing + limits |
waf-pipeline |
Phased orchestrator + anomaly scoring |
waf-detection |
Detection modules + fast-path prefilter |
waf-wasm |
Proxy-Wasm runtime (wasmi) |
waf-proxy |
The binary (waf) + embeddable lib (ProxyBuilder) |
License
Open source under Apache-2.0. The open-source vs. enterprise boundary is documented
normatively in BOUNDARY.md.