fix(middleware): prevent BodyLimit bypass with contract-compliant readers - #3087
Open
istoolsfox wants to merge 1 commit into
Open
fix(middleware): prevent BodyLimit bypass with contract-compliant readers#3087istoolsfox wants to merge 1 commit into
istoolsfox wants to merge 1 commit into
Conversation
…ders limitedReader.Read forwarded the caller's buffer to the underlying reader unbounded and only checked the cumulative count afterwards. Per the io.Reader contract, callers process n>0 bytes before considering the error and then call Read again - which handed out more real data every time, so an oversized body could be drained in full despite the configured limit (also triggered by encoding/json.Decoder via c.Bind()). Align the reader with net/http's maxBytesReader: cap the buffer one byte past the limit, deliver only the allowed portion on the crossing read, and keep the error sticky so no further data is handed out.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
limitedReader.Readforwards the caller's buffer to the underlying reader unbounded and only checks the cumulative count afterwards. Any consumer following the documentedio.Readerpattern — process then>0bytes before considering the error, then callReadagain — can drain an oversized body in full, because the reader keeps handing out real data on every subsequent call after the limit has been crossed.encoding/json.Decoderfollows exactly that documented pattern, soBodyLimit+c.Bind()can silently accept oversized JSON bodies too (most visible on Go 1.27 wherejson/v2backsjson.Decoder).Reproduction from #3071: with a 5-byte limit and a 50-byte chunked body, a handler reading into a 64-byte buffer receives all 50 bytes.
Fix
Align
limitedReaderwithnet/http'smaxBytesReader:remaining + 1bytes, so a singleReadcan never pull more than the limit allows (the one extra byte only tells us whether the underlying reader holds more data than allowed),413 Request Entity Too Large,Readreturns0and the 413 error, never more data.A body of exactly the limit size keeps working: the crossing check is
n > remaining, so exact-size bodies still terminate with a clean EOF.Tests
New cases in
body_limit_test.go:TestBodyLimitReader_singleOversizedRead— a single read with a buffer much larger than the limit delivers at most the allowed bytes and reports 413.TestBodyLimitReader_noDataAfterLimitExceeded— a caller that keeps callingRead(processingn>0first, per theio.Readerdocs) receives no data after the limit is exceeded.TestBodyLimitReader_exactLimitBody— a body of exactly the limit size is still read completely (regression guard).TestBodyLimit_oversizedBodyWithContractCompliantReader— end-to-end middleware case from middleware.BodyLimit: a single oversized Read can silently bypass the limit #3071 (ContentLength = -1, chunked); on the previous code it reads 50 bytes under a 5-byte limit, with the fix it reads at most 5.All existing
BodyLimittests pass unchanged;gofmt,go vetand the full test suite are clean.Fixes #3071