SUPP-53: buffer url-encoded request bodies so the security filter cannot drain them #59
No reviewers
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
iterative-works/support!59
Loading…
Reference in a new issue
No description provided.
Delete branch "SUPP-53"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #53.
Summary
Pac4jHttpSecuritydrained url-encoded request bodies before the route could readthem, so an authenticated form POST could reach its handler with an empty form and
still answer 200. Nothing failed and nothing was logged — the loss was silent, and
intermittent, because it depended on how the server framed the body on the wire.
The security filter needs to read form parameters, and http4s request bodies are
single-read streams. The fix buffers the body once, before the security filter runs,
so both the filter and the route read the same bytes.
Defect fixed
Phase 1 — Security filter drains the request body before the route reads it
(merged via #54).
UrlEncodedBodyBuffermiddleware buffers a url-encoded request body into memoryand rebuilds the request around it, up to a configured cap.
Content-Lengthis set to the measured size andchunkedis dropped, so theheaders no longer describe the original stream.
Pac4jHttpSecurity.securewires the middleware in ahead of the security filter.Pac4jSecurityConfiggains a trailingmaxFormBodySize: Long, read fromsecurity.maxformbodysize, default 2 MiB.Only
application/x-www-form-urlencodedrequests are buffered; every other contenttype passes through untouched.
Two consequences worth reading before upgrading
Pac4jSecurityConfighas a new trailing field. Positional destructuring orunapplyover it will no longer compile. Named construction and config-fileloading are unaffected.
413, no body. htmx will not swapsuch a response, so the user sees nothing happen. Applications that accept large
forms should raise the cap or handle the 413 themselves.
Testing
19 regression tests added — 17 in
UrlEncodedBodyBufferSpec, 2 inPac4jSecurityConfigSpec. Theserver/httpmodule goes from 52 to 71 tests.There was no HTTP-level test fixture in
server/http/src/testbefore this work; theharness (a single-read
Ref-backed body stream, probe routes, a pac4jAnonymousClientconfig) was written from scratch and is reusable.Every test was proved fallible by mutation: removing the fix wiring fails the four
reproduction tests while the four guards keep passing, with the headline test failing
as
form = Map()at status 200 rather than as an exception.Verified locally:
./mill __.testgreen (pre-push hook, whole repo),./mill http.checkFormatclean, 0 compiler warnings.Also in this branch
54896faepoints.iw/config.confat the Forgejo tracker (it still saidgithub).Unrelated to the defect, but the workflow tooling needed it to read this issue. Say
the word and I will pull it out into its own change.
Follow-ups
Filed to the tracker rather than fixed here: #55, #56, #57, #58.
Release notes
project-management/issues/SUPP-53/release-notes.md(Czech), written for 0.1.24.Delivery
This PR targets
release/0.1.x— the consumer pins 0.1.23, so the fix ships as0.1.24 on the maintenance line. A forward-port to
mainfollows, as with #45/#46.🤖 Generated with Claude Code
https://claude.ai/code/session_01BDfb2ghkkKiwTKVrcufmaT