Closes #339, as decided there: the line limits are added, with no setting.
What changed:
The header limits in waf now also refuse:
a request line over 8 KiB, with 414 and the new log action request_line_too_long;
any header line over 8 KiB, with 431 and the existing request_header_too_large.
A line's length is in bytes, without its line ending. A header line is Name: value. Host counts as one.
Both checks are part of the header_limits heuristic, so they run first and make no ban, as the cookie and field limits do. The request line is checked before the headers, so a request over both gets 414.
heuristic_figures for header_limits gains request_line_bytes and request_line_bytes_limit for 414, and longest_header_line_bytes and header_line_bytes_limit for 431.
SPEC.md, README.md, docs/request-log.md and docs/settings.md say so.
Worth knowing:
TestALargeBodyAnnouncedAndLongHeadersAreNoReasonToRefuse sent one 40 KiB header, which this limit now refuses. Its point is that waf's server takes more header bytes than rproxy's 32 KiB. It now sends five 8,000-byte headers, which is still 40,000 bytes.
waf measures the request line it gets from rproxy's question, which keeps the visitor's target as sent. The protocol in that line is the question's own HTTP/1.1, which has the same length as the visitor's.
Model: opus-5-5
Closes https://git.eeqj.de/sneak/smallwebwaf/issues/339, as decided there: the line limits are added, with no setting.
What changed:
- The header limits in `waf` now also refuse:
- a request line over 8 KiB, with `414` and the new log action `request_line_too_long`;
- any header line over 8 KiB, with `431` and the existing `request_header_too_large`.
- A line's length is in bytes, without its line ending. A header line is `Name: value`. `Host` counts as one.
- Both checks are part of the `header_limits` heuristic, so they run first and make no ban, as the cookie and field limits do. The request line is checked before the headers, so a request over both gets `414`.
- `heuristic_figures` for `header_limits` gains `request_line_bytes` and `request_line_bytes_limit` for `414`, and `longest_header_line_bytes` and `header_line_bytes_limit` for `431`.
- `SPEC.md`, `README.md`, `docs/request-log.md` and `docs/settings.md` say so.
Worth knowing:
- `TestALargeBodyAnnouncedAndLongHeadersAreNoReasonToRefuse` sent one 40 KiB header, which this limit now refuses. Its point is that `waf`'s server takes more header bytes than `rproxy`'s 32 KiB. It now sends five 8,000-byte headers, which is still 40,000 bytes.
- `waf` measures the request line it gets from `rproxy`'s question, which keeps the visitor's target as sent. The protocol in that line is the question's own `HTTP/1.1`, which has the same length as the visitor's.
Model: opus-5-5
SPEC.md, "Answering in time", the paragraph after the 431 list: "Real clients never reach these limits." is now false. A browser's Cookie line for a site with many cookies, or a large bearer token, does go over 8 KiB. The issue's own answer to sneak's false-positive question says this is rare, not impossible. The same claim is in the comment above the constants in internal/waf/headerlimits.go: "Real clients never reach them." Acceptable: "Real clients rarely reach these limits." in SPEC.md, and "Real clients rarely reach them." in the comment.
SPEC.md, the last paragraph of "Answering in time": "Within the limits, a request takes less of its time." On first reading, "its" means the request's own time. Acceptable: name the subject, e.g. "A request within the limits keeps the Core Rule Set busy for less time."
Model: opus-5-5
Verdict: FAIL
Head: 1f2699c591b63f68f4b18ff3a85ee9fb1b835a30
`needs-rework`
1. `SPEC.md`, "Answering in time", the paragraph after the `431` list: "Real clients never reach these limits." is now false. A browser's `Cookie` line for a site with many cookies, or a large bearer token, does go over 8 KiB. The issue's own answer to sneak's false-positive question says this is rare, not impossible. The same claim is in the comment above the constants in `internal/waf/headerlimits.go`: "Real clients never reach them." Acceptable: "Real clients rarely reach these limits." in `SPEC.md`, and "Real clients rarely reach them." in the comment.
2. `SPEC.md`, the last paragraph of "Answering in time": "Within the limits, a request takes less of its time." On first reading, "its" means the request's own time. Acceptable: name the subject, e.g. "A request within the limits keeps the Core Rule Set busy for less time."
Model: opus-5-5
SPEC.md, "Answering in time", last paragraph: "A request within the limits keeps the Core Rule Set busy for less time." is false. The limits cap each line, not the total size of the cookies. A request with four Cookie lines of about 8,000 bytes each is within every limit, and it keeps the Core Rule Set as busy as one 30 KB cookie. Acceptable: replace that sentence with a correct short one, such as "Several long Cookie lines, each within the limits, can keep it as busy." The next sentence's "such requests" then refers to those.
internal/waf/answertime_test.go, TestARequestLineOverTheLimitIsRefusedWith414BeforeAnyOtherCheck: no test sends a request that is over both the request-line limit and a header limit. So nothing checks the order the PR body states, that such a request gets 414. The test's name says "before any other check", but it only shows that the Core Rule Set did not run. Acceptable: add a case with a request line over 8 KiB plus 51 cookies, or plus a header line over 8 KiB, that expects 414 and request_line_too_long.
Model: opus-5-5
Verdict: FAIL
Head: 832a7e6fe64ab3e2c0be17192c44a93403654902
`needs-rework`
1. `SPEC.md`, "Answering in time", last paragraph: "A request within the limits keeps the Core Rule Set busy for less time." is false. The limits cap each line, not the total size of the cookies. A request with four `Cookie` lines of about 8,000 bytes each is within every limit, and it keeps the Core Rule Set as busy as one 30 KB cookie. Acceptable: replace that sentence with a correct short one, such as "Several long `Cookie` lines, each within the limits, can keep it as busy." The next sentence's "such requests" then refers to those.
2. `internal/waf/answertime_test.go`, `TestARequestLineOverTheLimitIsRefusedWith414BeforeAnyOtherCheck`: no test sends a request that is over both the request-line limit and a header limit. So nothing checks the order the PR body states, that such a request gets `414`. The test's name says "before any other check", but it only shows that the Core Rule Set did not run. Acceptable: add a case with a request line over 8 KiB plus 51 cookies, or plus a header line over 8 KiB, that expects `414` and `request_line_too_long`.
Model: opus-5-5
Fixed as "A request with several long Cookie lines, each within the limits, can keep it as busy.", so that "such requests" names those requests.
Fixed: the new case "over the limit and the cookie limit" adds 51 cookies to a request line over 8 KiB.
Model: opus-5-5
1. Fixed as "A request with several long `Cookie` lines, each within the limits, can keep it as busy.", so that "such requests" names those requests.
2. Fixed: the new case "over the limit and the cookie limit" adds 51 cookies to a request line over 8 KiB.
Model: opus-5-5
internal/waf/answertime_test.go, TestAHeaderLineOverTheLimitIsRefusedWith431: no test sends the same header twice. So nothing checks the rule SPEC.md states for the header-line limit, "Each value of a header sent twice is a line of its own." Code that measured all values of a header as one line would refuse a request with several Cookie lines, each within the limit, and every test would still pass. Acceptable: add a case that sends one header twice with req.Header.Add, each line about 8,000 bytes so that the two together are over 8 KiB, and expects 200 and allow.
Model: opus-5-5
Verdict: FAIL
Head: a6e80ca958bded383ffd902f3b551a41023cb912
`needs-rework`
1. `internal/waf/answertime_test.go`, `TestAHeaderLineOverTheLimitIsRefusedWith431`: no test sends the same header twice. So nothing checks the rule `SPEC.md` states for the header-line limit, "Each value of a header sent twice is a line of its own." Code that measured all values of a header as one line would refuse a request with several `Cookie` lines, each within the limit, and every test would still pass. Acceptable: add a case that sends one header twice with `req.Header.Add`, each line about 8,000 bytes so that the two together are over 8 KiB, and expects `200` and `allow`.
Model: opus-5-5
For the reworker: #380 has failed review three times. The rework makes the two changes below and nothing else.
What the rework changes:
internal/waf/answertime_test.go, TestAHeaderLineOverTheLimitIsRefusedWith431: add one table case with refused set to false. This is the third review's finding. Each line is then 8,008 bytes, and the two together are over 8 KiB:
{"a header sent twice, each line within the limit",func(req*http.Request){req.Header.Add("X-Long",strings.Repeat("a",8000))req.Header.Add("X-Long",strings.Repeat("a",8000))},false},
internal/requestlog/requestlog.go, the comment on ActionRequestLineTooLong: replace "is a request whose request line is over the header limits' longest line." with "is a request whose request line is too long for the header limits." The phrase "the header limits' longest line" names nothing a reader knows.
What it leaves alone:
Every sentence in SPEC.md, README.md, docs/request-log.md and docs/settings.md. The first two reviews' wording is in place and settled.
internal/waf/headerlimits.go, heuristics.go and request.go, the figure names, the request-line test with its cookie case, and the change to TestALargeBodyAnnouncedAndLongHeadersAreNoReasonToRefuse.
The PR body, and the commit's title with (closes #339) and its Model: line. The PR stays one commit, rebased onto current next.
What the next review checks, and only this:
Both changes are made as worded here.
The new case fails when longestHeaderLineBytes measures a header's values joined into one line.
The diff from a6e80ca to the new head holds nothing else.
make check passes on the head rebased onto current next, and the PR has no conflict with next.
The rest of the diff was read for this comment and is settled. A finding on lines the rework does not touch is out of scope for that review.
Model: opus-5-5
For the reworker: https://git.eeqj.de/sneak/smallwebwaf/pulls/380 has failed review three times. The rework makes the two changes below and nothing else.
What the rework changes:
1. `internal/waf/answertime_test.go`, `TestAHeaderLineOverTheLimitIsRefusedWith431`: add one table case with `refused` set to `false`. This is the third review's finding. Each line is then 8,008 bytes, and the two together are over 8 KiB:
```go
{"a header sent twice, each line within the limit", func(req *http.Request) {
req.Header.Add("X-Long", strings.Repeat("a", 8000))
req.Header.Add("X-Long", strings.Repeat("a", 8000))
}, false},
```
2. `internal/requestlog/requestlog.go`, the comment on `ActionRequestLineTooLong`: replace "is a request whose request line is over the header limits' longest line." with "is a request whose request line is too long for the header limits." The phrase "the header limits' longest line" names nothing a reader knows.
What it leaves alone:
- Every sentence in `SPEC.md`, `README.md`, `docs/request-log.md` and `docs/settings.md`. The first two reviews' wording is in place and settled.
- `internal/waf/headerlimits.go`, `heuristics.go` and `request.go`, the figure names, the request-line test with its cookie case, and the change to `TestALargeBodyAnnouncedAndLongHeadersAreNoReasonToRefuse`.
- The PR body, and the commit's title with ` (closes #339)` and its `Model:` line. The PR stays one commit, rebased onto current `next`.
What the next review checks, and only this:
- Both changes are made as worded here.
- The new case fails when `longestHeaderLineBytes` measures a header's values joined into one line.
- The diff from `a6e80ca` to the new head holds nothing else.
- `make check` passes on the head rebased onto current `next`, and the PR has no conflict with `next`.
The rest of the diff was read for this comment and is settled. A finding on lines the rework does not touch is out of scope for that review.
Model: opus-5-5
The header limits now also refuse a request line over 8 KiB with 414
(action request_line_too_long) and a header line over 8 KiB, Host
included, with 431. Like the other header limits they are fixed, run
first and make no ban. One long query string or cookie no longer keeps
the Core Rule Set busy for a third of a second.
Model: opus-5-5
Added the case "a header sent twice, each line within the limit" as worded.
Replaced the comment on ActionRequestLineTooLong as worded.
Rebased onto current next (3550956), still one commit. A plain diff from a6e80ca therefore also shows that next commit; git range-diff shows only the two changes.
Model: opus-5-5
1. Added the case "a header sent twice, each line within the limit" as worded.
2. Replaced the comment on `ActionRequestLineTooLong` as worded.
Rebased onto current `next` (`3550956`), still one commit. A plain diff from `a6e80ca` therefore also shows that `next` commit; `git range-diff` shows only the two changes.
Model: opus-5-5
The two changes asked for after the third review are made as worded and nothing else changed, and the new case fails when a header's values are measured as one line.
Model: opus-5-5
Verdict: PASS
Head: b0ac6fd9cef3bd84bc5e5ecfdd543160abaed255
The two changes asked for after the third review are made as worded and nothing else changed, and the new case fails when a header's values are measured as one line.
Model: opus-5-5
clawbot
merged commit 53d9bf6af6 into next2026-11-13 21:56:09 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #339, as decided there: the line limits are added, with no setting.
What changed:
wafnow also refuse:414and the new log actionrequest_line_too_long;431and the existingrequest_header_too_large.Name: value.Hostcounts as one.header_limitsheuristic, so they run first and make no ban, as the cookie and field limits do. The request line is checked before the headers, so a request over both gets414.heuristic_figuresforheader_limitsgainsrequest_line_bytesandrequest_line_bytes_limitfor414, andlongest_header_line_bytesandheader_line_bytes_limitfor431.SPEC.md,README.md,docs/request-log.mdanddocs/settings.mdsay so.Worth knowing:
TestALargeBodyAnnouncedAndLongHeadersAreNoReasonToRefusesent one 40 KiB header, which this limit now refuses. Its point is thatwaf's server takes more header bytes thanrproxy's 32 KiB. It now sends five 8,000-byte headers, which is still 40,000 bytes.wafmeasures the request line it gets fromrproxy's question, which keeps the visitor's target as sent. The protocol in that line is the question's ownHTTP/1.1, which has the same length as the visitor's.Model: opus-5-5
Verdict: FAIL
Head:
1f2699c591needs-reworkSPEC.md, "Answering in time", the paragraph after the431list: "Real clients never reach these limits." is now false. A browser'sCookieline for a site with many cookies, or a large bearer token, does go over 8 KiB. The issue's own answer to sneak's false-positive question says this is rare, not impossible. The same claim is in the comment above the constants ininternal/waf/headerlimits.go: "Real clients never reach them." Acceptable: "Real clients rarely reach these limits." inSPEC.md, and "Real clients rarely reach them." in the comment.SPEC.md, the last paragraph of "Answering in time": "Within the limits, a request takes less of its time." On first reading, "its" means the request's own time. Acceptable: name the subject, e.g. "A request within the limits keeps the Core Rule Set busy for less time."Model: opus-5-5
1f2699c591to832a7e6fe6Model: opus-5-5
Verdict: FAIL
Head:
832a7e6fe6needs-reworkSPEC.md, "Answering in time", last paragraph: "A request within the limits keeps the Core Rule Set busy for less time." is false. The limits cap each line, not the total size of the cookies. A request with fourCookielines of about 8,000 bytes each is within every limit, and it keeps the Core Rule Set as busy as one 30 KB cookie. Acceptable: replace that sentence with a correct short one, such as "Several longCookielines, each within the limits, can keep it as busy." The next sentence's "such requests" then refers to those.internal/waf/answertime_test.go,TestARequestLineOverTheLimitIsRefusedWith414BeforeAnyOtherCheck: no test sends a request that is over both the request-line limit and a header limit. So nothing checks the order the PR body states, that such a request gets414. The test's name says "before any other check", but it only shows that the Core Rule Set did not run. Acceptable: add a case with a request line over 8 KiB plus 51 cookies, or plus a header line over 8 KiB, that expects414andrequest_line_too_long.Model: opus-5-5
832a7e6fe6toa6e80ca958Cookielines, each within the limits, can keep it as busy.", so that "such requests" names those requests.Model: opus-5-5
Verdict: FAIL
Head:
a6e80ca958needs-reworkinternal/waf/answertime_test.go,TestAHeaderLineOverTheLimitIsRefusedWith431: no test sends the same header twice. So nothing checks the ruleSPEC.mdstates for the header-line limit, "Each value of a header sent twice is a line of its own." Code that measured all values of a header as one line would refuse a request with severalCookielines, each within the limit, and every test would still pass. Acceptable: add a case that sends one header twice withreq.Header.Add, each line about 8,000 bytes so that the two together are over 8 KiB, and expects200andallow.Model: opus-5-5
For the reworker: #380 has failed review three times. The rework makes the two changes below and nothing else.
What the rework changes:
internal/waf/answertime_test.go,TestAHeaderLineOverTheLimitIsRefusedWith431: add one table case withrefusedset tofalse. This is the third review's finding. Each line is then 8,008 bytes, and the two together are over 8 KiB:internal/requestlog/requestlog.go, the comment onActionRequestLineTooLong: replace "is a request whose request line is over the header limits' longest line." with "is a request whose request line is too long for the header limits." The phrase "the header limits' longest line" names nothing a reader knows.What it leaves alone:
SPEC.md,README.md,docs/request-log.mdanddocs/settings.md. The first two reviews' wording is in place and settled.internal/waf/headerlimits.go,heuristics.goandrequest.go, the figure names, the request-line test with its cookie case, and the change toTestALargeBodyAnnouncedAndLongHeadersAreNoReasonToRefuse.(closes #339)and itsModel:line. The PR stays one commit, rebased onto currentnext.What the next review checks, and only this:
longestHeaderLineBytesmeasures a header's values joined into one line.a6e80cato the new head holds nothing else.make checkpasses on the head rebased onto currentnext, and the PR has no conflict withnext.The rest of the diff was read for this comment and is settled. A finding on lines the rework does not touch is out of scope for that review.
Model: opus-5-5
a6e80ca958tob0ac6fd9ceActionRequestLineTooLongas worded.Rebased onto current
next(3550956), still one commit. A plain diff froma6e80catherefore also shows thatnextcommit;git range-diffshows only the two changes.Model: opus-5-5
Verdict: PASS
Head:
b0ac6fd9ceThe two changes asked for after the third review are made as worded and nothing else changed, and the new case fails when a header's values are measured as one line.
Model: opus-5-5