fix: prevent webhook memory exhaustion via Content-Length header bypass#6230
Conversation
The webhook endpoint previously checked Content-Length (client-controlled) before calling req.text(), which reads the entire body into memory. An attacker could forge a small Content-Length while sending a multi-GB body, exhausting server memory before the actual size was validated. Replaces the two-step validation with a streaming readBodyWithLimit() function that enforces the 1MB size limit during chunked reading. If the limit is exceeded mid-read, the stream is cancelled immediately without buffering the full body. Also fixes the getClientIp usage to use the secure helper instead of reading x-forwarded-for directly. Fixes JhaSourav07#6135 Human Coded
|
@atul-upadhyay-7 is attempting to deploy a commit to the jhasourav07's projects Team on Vercel. A member of the Team first needs to authorize it. |
📦 Next.js Bundle Size Report (Gzipped Sizes)✨ No significant bundle size changes detected. 📊 Summary of Totals
|
Aamod-Dev
left a comment
There was a problem hiding this comment.
Excellent fix. Replacing req.text() with the streaming readBodyWithLimit() chunk reader in app/api/webhook/route.ts effectively neutralizes the out-of-memory vulnerability caused by forged Content-Length headers. Adding the regression test in app/api/webhook/route.test.ts to simulate the mismatch ensures this doesn't regress. Approved!
|
🎉 Congratulations @atul-upadhyay-7! Your PR has been successfully merged. 🚀 Thank you for contributing to CommitPulse. Your work helps us build a better tool for the community.
Keep building! 💻✨ |
Summary
Replaces the webhook endpoint's two-step payload size validation with a streaming
readBodyWithLimit()function that enforces the 1MB size limit during chunked reading, preventing memory exhaustion attacks.Problem
The previous implementation checked the client-controlled
Content-Lengthheader before callingreq.text(), which reads the entire body into memory as a JavaScript string. An attacker could:Content-Length: 100(passes the 1MB check)req.text()allocates ~1-2GB of memoryThis allows a single request to crash the Node.js process via OOM.
Solution
Added
readBodyWithLimit()that reads the request body as aReadableStreamin chunks, tracking total bytes consumed. If the limit is exceeded at any point during reading, the stream is cancelled immediately — the full body is never buffered in memory.Changes
app/api/webhook/route.ts— AddedreadBodyWithLimit()function, replacedContent-Lengthcheck +req.text()with streaming readapp/api/webhook/route.test.ts— Added regression test: "returns 413 when actual body exceeds 1MB despite small Content-Length"Testing
Fixes #6135
Human Coded