Skip to content

Harden request ingress: body-size limit (413) and server timeouts #237

@vivek7405

Description

@vivek7405

Problem

Nothing caps request body size. toWebRequest builds an unbounded ReadableStream and the framework-owned action RPC endpoint plus route readBody buffer an arbitrarily large POST entirely into memory before the handler runs (invokeAction does await req.text() at actions.js:299/411, route readBody at json.js:58), with no content-length ceiling and no 413 path. An unbounded POST to /__webjs/action/* (reachable by any client holding a CSRF token) or any route.js handler is a trivial memory-exhaustion DoS. The same ingress is also unbounded in time: createHttp1Server + server.listen set no requestTimeout, headersTimeout, or keepAliveTimeout and no per-request deadline, so a slowloris slow-header client or a hung handler holds a connection indefinitely (the only timeouts in the codebase are vendor-fetch and SSE keepalive).

Design / approach

Count bytes off the native request ReadableStream and abort with 413 past a default cap (no full buffering), and set node:http's built-in requestTimeout/headersTimeout/keepAliveTimeout at listen time. Both are native knobs, not a new middleware layer.

Web-standards fit: Uses the native Streams API for the size check and node:http's own timeout properties; the framework adds only a counter and a few listen-time assignments.

Prior art: Express/body-parser 100kb default, Next.js api bodyParser.sizeLimit (1mb), Remix parseMultipartFormData maxPartSize, Rails ActionDispatch request-size protections.

Acceptance criteria

  • A sane default body-size limit applies to action POSTs and route.js handlers, returning 413 when exceeded
  • A config knob (webjs.* and/or per-route override) raises or lowers the limit
  • The limit is enforced while streaming, not by buffering the whole oversized body
  • Multipart/upload paths honor the same or a separate documented limit
  • Tests cover under-limit success and over-limit 413 on both the action path and a route handler
  • The production server sets requestTimeout, headersTimeout (> keepAliveTimeout), and keepAliveTimeout to sensible overridable defaults, with a documented manual check that a slow-header connection is terminated
  • Tests cover the new behaviour at the applicable layer(s)
  • Docs / AGENTS.md / CONVENTIONS.md updated if the public surface changed

Filed from the production-readiness audit (webjs vs Next.js / Remix / Rails / Turbo / Lit). Theme: runtime. Priority: P1. Kept to webjs identity: no-build, progressive enhancement, web-components-first, AI-first, batteries-included, close to web standards.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions