Measured on haproxy 3.0.11 + coraza-spoa v0.7.1 + CRS 4.25.0 (local
haproxy-manager + coraza-spoa + echo backend):
- send-spoe-group fired at header time, so req.body held only what arrived
with the headers. A 933180 payload whose body came in a second send(),
2s late, or after Expect: 100-continue passed uninspected; 8 parallel
14 KB payload-first POSTs: 6/40 blocked before, 40/40 after.
- Add `option http-buffer-request` (Coraza block only). Uploads are not
held: one buffer is inspected, the rest streams (50 MB upload: sha-ok,
same timing before/after).
- Stamp X-WHP-WAF-Body: complete|partial for Coraza (client copies
stripped first, stripped again before the backend). Content-Length:
exact compare; chunked/no-CL: body+headers < 12 KB => complete. WHP's
generated rules use it to deny body-parse errors (200002/200003) only on
complete bodies.
- Pin tune.bufsize 16384 (= default) when Coraza is on. Coraza costs
~3.3 ms/KB of body and the 100 ms SPOE timeout fails open: at 32 KB a
payload at byte 0 of a 30 KB base64 body bypassed. SPOP frames > 65535
are refused by the agent library.
- coraza-spoa/config.yaml: SecRequestBodyLimit 131072 / InMemory 131072 /
ProcessPartial (Reject 413s even under DetectionOnly).
- Access log gains wafb= (complete|partial) and wafe= (SPOE error =
request passed uninspected).
- overrides.conf: document (comment only) that the 920440 promotion turns
the engine On for every URL with a file extension.
- scripts/test-waf-body-inspection.py (16 tests), wired into the build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>