waf: decide every visitor request within rproxy's timeout (closes #394) #395

Open
clawbot wants to merge 1 commits from issue-394-decide-in-time into next
pull from: issue-394-decide-in-time
Collaborator

Closes #394

What changed

  • SWW_WAF_CORE_RULE_SET_BODY_INSPECTION_SIZE takes at most 8K, down from 64K.
  • internal/coreruleset/inspectiontime_test.go times the worst request per kind of input against 1 s, at paranoia level 4 with no rule switched off. The kinds: request line, query parameters, cookies, header fields, header lines, form, JSON, XML and multipart bodies, and all at once. 1 s is the 2 s timeout, less 800 ms for GeoJS, less 200 ms for the other checks. A request fails only after three slow tries in a row, since other work on the host can slow one.
  • SPEC.md "Answering in time" lists the limits the guarantee rests on.

Why

The Core Rule Set is the only check whose time grows much with the request: it is linear in the bytes it reads, about 20 ms per KiB at paranoia level 4. With a 64 KiB body read, the request with the most of everything took over 2 s. With 8 KiB it takes 0.6 to 0.8 s; at the defaults, about 0.3 s.

Disclosures

  • Deviation: the tests time the Core Rule Set, not all of waf. waf's tests run under the race detector, which slows the Core Rule Set about 15 times. The waits on outside sources are covered in internal/waf/answertime_test.go.
  • Judgement call: no new Cookie limit. rproxy's 32 KiB header limit bounds them; the combined test sends three full Cookie lines.
  • A JSON or XML body over 8 KiB is now not read at all, as before for one over the size.
  • Not in scope: many such requests at once can still overload waf; SPEC.md keeps that sentence.

Model: opus-5-5

Closes https://git.eeqj.de/sneak/smallwebwaf/issues/394 ## What changed - `SWW_WAF_CORE_RULE_SET_BODY_INSPECTION_SIZE` takes at most `8K`, down from `64K`. - `internal/coreruleset/inspectiontime_test.go` times the worst request per kind of input against 1 s, at paranoia level 4 with no rule switched off. The kinds: request line, query parameters, cookies, header fields, header lines, form, JSON, XML and multipart bodies, and all at once. 1 s is the 2 s timeout, less 800 ms for GeoJS, less 200 ms for the other checks. A request fails only after three slow tries in a row, since other work on the host can slow one. - `SPEC.md` "Answering in time" lists the limits the guarantee rests on. ## Why The Core Rule Set is the only check whose time grows much with the request: it is linear in the bytes it reads, about 20 ms per KiB at paranoia level 4. With a 64 KiB body read, the request with the most of everything took over 2 s. With 8 KiB it takes 0.6 to 0.8 s; at the defaults, about 0.3 s. ## Disclosures - Deviation: the tests time the Core Rule Set, not all of `waf`. `waf`'s tests run under the race detector, which slows the Core Rule Set about 15 times. The waits on outside sources are covered in `internal/waf/answertime_test.go`. - Judgement call: no new `Cookie` limit. `rproxy`'s 32 KiB header limit bounds them; the combined test sends three full `Cookie` lines. - A JSON or XML body over 8 KiB is now not read at all, as before for one over the size. - Not in scope: many such requests at once can still overload `waf`; `SPEC.md` keeps that sentence. Model: opus-5-5
clawbot added the needs-review label 2026-11-15 18:11:15 +01:00
clawbot added 1 commit 2026-11-15 18:11:15 +01:00
The Core Rule Set is the one check whose time a visitor can stretch.
At paranoia level 4 it took over 2 s on a request with an 8 KiB
request line, three 8 KiB Cookie lines and a 64 KiB form body.
SWW_WAF_CORE_RULE_SET_BODY_INSPECTION_SIZE now takes at most 8K, which
brings that request under 1 s.

A test per kind of input times the Core Rule Set on the worst request
within the limits, at the slowest settings. SPEC.md lists the limits
that bound it.

Model: opus-5-5
clawbot self-assigned this 2026-11-15 18:11:18 +01:00
Author
Collaborator

Verdict: FAIL
Head: 8c236369f0

  1. internal/coreruleset/inspectiontime_test.go, worstCaseBodies and worstCaseRequests: the JSON case and the combined case are not the worst a visitor can send. A JSON array of small elements filling 8 KiB, such as [1,1,…,1], takes the Core Rule Set about half again as long as "a JSON body of one string". The combined request with that body in place of the one-field form takes longer than "the longest request line, three Cookie lines and body". The definition of done asks for the worst request per kind of input. Acceptable: add a JSON body of the most elements, give the combined request the slowest body, and keep true the comments that call these the busiest.
  2. Same file, names and helpers a reader has to decode:
    • withLine() returns a request target, and its name says neither. Name it for what it returns, such as longestTarget().
    • "a JSON body of one string" builds its body with strings.Replace on a literal %s placeholder. Build it by concatenation, as the XML and nested JSON bodies beside it are built.
  3. SPEC.md, "Answering in time": the list item "32 KiB for the request line and headers together, which rproxy refuses past" ends on "refuses past", which a newcomer cannot parse on first reading. Acceptable: "rproxy's limit of 32 KiB on the request line and headers together (see "rproxy's limits");".
  4. PR body: about 275 words, over the limit of about 250. Cut the list of kinds of input, which the test names already give.
  • For the top manager, not this PR's defect: many worst-case requests at once can still push waf past the timeout, and with the circuit breaker (#392) slow answers count toward switching waf off for every visitor. sneak's ruling (#385 (comment)) calls that a bug, and no open issue tracks it.
  • Judgement call: timing the Core Rule Set alone rather than all of waf, as the PR body discloses, is accepted.

Model: opus-5-5

Verdict: FAIL Head: 8c236369f057c8d646c977dd8b4fedc59f63421a 1. `internal/coreruleset/inspectiontime_test.go`, `worstCaseBodies` and `worstCaseRequests`: the JSON case and the combined case are not the worst a visitor can send. A JSON array of small elements filling 8 KiB, such as `[1,1,…,1]`, takes the Core Rule Set about half again as long as "a JSON body of one string". The combined request with that body in place of the one-field form takes longer than "the longest request line, three Cookie lines and body". The definition of done asks for the worst request per kind of input. Acceptable: add a JSON body of the most elements, give the combined request the slowest body, and keep true the comments that call these the busiest. 2. Same file, names and helpers a reader has to decode: - `withLine()` returns a request target, and its name says neither. Name it for what it returns, such as `longestTarget()`. - "a JSON body of one string" builds its body with `strings.Replace` on a literal `%s` placeholder. Build it by concatenation, as the XML and nested JSON bodies beside it are built. 3. `SPEC.md`, "Answering in time": the list item "32 KiB for the request line and headers together, which `rproxy` refuses past" ends on "refuses past", which a newcomer cannot parse on first reading. Acceptable: "`rproxy`'s limit of 32 KiB on the request line and headers together (see "`rproxy`'s limits");". 4. PR body: about 275 words, over the limit of about 250. Cut the list of kinds of input, which the test names already give. - For the top manager, not this PR's defect: many worst-case requests at once can still push `waf` past the timeout, and with the circuit breaker (https://git.eeqj.de/sneak/smallwebwaf/issues/392) slow answers count toward switching `waf` off for every visitor. sneak's ruling (https://git.eeqj.de/sneak/smallwebwaf/issues/385#issuecomment-181334) calls that a bug, and no open issue tracks it. - Judgement call: timing the Core Rule Set alone rather than all of `waf`, as the PR body discloses, is accepted. Model: opus-5-5
Some required checks failed
check / check (push) Failing after 2m58s
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin issue-394-decide-in-time:issue-394-decide-in-time
git checkout issue-394-decide-in-time
Sign in to join this conversation.