Skip to content

fix: prevent webhook memory exhaustion via Content-Length header bypass#6230

Merged
JhaSourav07 merged 1 commit into
JhaSourav07:mainfrom
atul-upadhyay-7:fix/webhook-memory-exhaustion-content-length
Jun 21, 2026
Merged

fix: prevent webhook memory exhaustion via Content-Length header bypass#6230
JhaSourav07 merged 1 commit into
JhaSourav07:mainfrom
atul-upadhyay-7:fix/webhook-memory-exhaustion-content-length

Conversation

@atul-upadhyay-7

Copy link
Copy Markdown
Contributor

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-Length header before calling req.text(), which reads the entire body into memory as a JavaScript string. An attacker could:

  1. Send Content-Length: 100 (passes the 1MB check)
  2. Send a 1GB actual body
  3. req.text() allocates ~1-2GB of memory
  4. The post-read size check detects the overflow, but the memory is already consumed

This allows a single request to crash the Node.js process via OOM.

Solution

Added readBodyWithLimit() that reads the request body as a ReadableStream in 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.

async function readBodyWithLimit(
  body: ReadableStream<Uint8Array> | null,
  maxBytes: number
): Promise<{ ok: true; body: string } | { ok: false; status: number; error: string }>

Changes

  • app/api/webhook/route.ts — Added readBodyWithLimit() function, replaced Content-Length check + req.text() with streaming read
  • app/api/webhook/route.test.ts — Added regression test: "returns 413 when actual body exceeds 1MB despite small Content-Length"

Testing

  • All 9 webhook tests pass (including the new vulnerability regression test)
  • Lint: 0 errors
  • Typecheck: no new errors

Fixes #6135

Human Coded

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
@vercel

vercel Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

@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.

@github-actions

Copy link
Copy Markdown
Contributor

📦 Next.js Bundle Size Report (Gzipped Sizes)

✨ No significant bundle size changes detected.

📊 Summary of Totals

Category PR Size Base Size Difference
Total JS 3697.00 KB 3697.00 KB 0 B
Total CSS 296.58 KB 296.58 KB 0 B

@Aamod-Dev Aamod-Dev added GSSoC 2026 mentor:Aamod007 level:advanced Complex contributions involving architecture, optimization, or significant feature work quality:clean PR follows clean coding practices, proper formatting, documentation, and maintainability standards. security bug Something isn't working labels Jun 21, 2026

@Aamod-Dev Aamod-Dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

@JhaSourav07 JhaSourav07 added the gssoc:approved PR has been reviewed and accepted for valid contribution points label Jun 21, 2026
@JhaSourav07 JhaSourav07 merged commit 9edbbc7 into JhaSourav07:main Jun 21, 2026
11 of 12 checks passed
@github-actions github-actions Bot added this to the GSSoC 2026 milestone Jun 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🎉 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.

⚠️ Important for GSSoC Contributors:
You are strictly advised to join our Discord Server as it is mandatory for all GSSoC participants. All important announcements, point claims, and community discussions happen there.

Keep building! 💻✨

@github-actions github-actions Bot added the type:bug Something isn't working as expected label Jun 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working gssoc:approved PR has been reviewed and accepted for valid contribution points GSSoC 2026 level:advanced Complex contributions involving architecture, optimization, or significant feature work mentor:Aamod007 quality:clean PR follows clean coding practices, proper formatting, documentation, and maintainability standards. security type:bug Something isn't working as expected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Webhook endpoint allows memory exhaustion through Content-Length header bypass

3 participants