Skip to content

Commit 708a3aa

Browse files
committed
fix(body): enforce stream-based body size check regardless of content-length header
1 parent 43e1fa3 commit 708a3aa

File tree

2 files changed

+16
-1
lines changed

2 files changed

+16
-1
lines changed

src/utils/body.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,13 @@ async function isBodySizeWithin(event: HTTPEvent, limit: number): Promise<boolea
162162
// https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
163163
throw new HTTPError({ status: 400 });
164164
}
165-
return +contentLength <= limit;
165+
// Fail-fast: reject if declared size exceeds limit
166+
if (+contentLength > limit) {
167+
return false;
168+
}
166169
}
167170

171+
// Always verify actual body size via stream
168172
const reader = req.clone().body!.getReader();
169173
let chunk = await reader.read();
170174
let size = 0;

test/unit/body-limit.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@ describe("body limit (unit)", () => {
4949
await expect(assertBodySize(eventMock, 10)).rejects.toThrow(HTTPError);
5050
});
5151

52+
it("spoofed content-length smaller than actual body", async () => {
53+
const LARGE_BODY = "A".repeat(1024); // 1KB actual body
54+
const eventMock = mockEvent("/", {
55+
method: "POST",
56+
body: LARGE_BODY,
57+
headers: { "content-length": "10" }, // lie: claim 10 bytes
58+
});
59+
// Should reject based on actual body size, not Content-Length header
60+
await expect(assertBodySize(eventMock, 100)).rejects.toThrow(HTTPError);
61+
});
62+
5263
it("both content length and transfer encoding", async () => {
5364
const eventMock = mockEvent("/", {
5465
method: "POST",

0 commit comments

Comments
 (0)