From ba168b0ff57caeb45a7b3185a97892ce745abec4 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Thu, 8 Sep 2022 23:08:47 -0700 Subject: [PATCH] [breaking] limit adapter-node request size (#6684) --- .changeset/tricky-ducks-kneel.md | 6 ++++++ packages/adapter-node/README.md | 4 ++++ packages/adapter-node/src/handler.js | 8 ++++++-- packages/kit/src/exports/node/index.js | 20 +++++++++++++++++--- packages/kit/types/ambient.d.ts | 1 + 5 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 .changeset/tricky-ducks-kneel.md diff --git a/.changeset/tricky-ducks-kneel.md b/.changeset/tricky-ducks-kneel.md new file mode 100644 index 000000000000..9616e9e54a7c --- /dev/null +++ b/.changeset/tricky-ducks-kneel.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-node': patch +'@sveltejs/kit': patch +--- + +[breaking] limit adapter-node request size diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md index 80dc3d2a98d7..2d5a93f210a8 100644 --- a/packages/adapter-node/README.md +++ b/packages/adapter-node/README.md @@ -69,6 +69,10 @@ Instead, we read from the _right_, accounting for the number of trusted proxies. > If you need to read the left-most address instead (and don't care about spoofing) — for example, to offer a geolocation service, where it's more important for the IP address to be _real_ than _trusted_, you can do so by inspecting the `x-forwarded-for` header within your app. +### `BODY_SIZE_LIMIT` + +The maximum request body size to accept in bytes. Defaults to 512kb. This option does not allow streaming. You can disable this option with a value of 0 and implement a custom check in [`handle`](https://kit.svelte.dev/docs/hooks#server-hooks-handle) if you need something more advanced. + ## Options The adapter can be configured with various options: diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index 4a29502e71f0..94f331f57ccb 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -14,10 +14,10 @@ const server = new Server(manifest); await server.init({ env: process.env }); const origin = env('ORIGIN', undefined); const xff_depth = parseInt(env('XFF_DEPTH', '1')); - const address_header = env('ADDRESS_HEADER', '').toLowerCase(); const protocol_header = env('PROTOCOL_HEADER', '').toLowerCase(); const host_header = env('HOST_HEADER', 'host').toLowerCase(); +const body_size_limit = parseInt(env('BODY_SIZE_LIMIT', '524288')); const dir = path.dirname(fileURLToPath(import.meta.url)); @@ -49,7 +49,11 @@ const ssr = async (req, res) => { let request; try { - request = await getRequest({ base: origin || get_origin(req.headers), request: req }); + request = await getRequest({ + base: origin || get_origin(req.headers), + request: req, + bodySizeLimit: body_size_limit + }); } catch (err) { res.statusCode = err.status || 400; res.end(err.reason || 'Invalid request body'); diff --git a/packages/kit/src/exports/node/index.js b/packages/kit/src/exports/node/index.js index d2fc32c2ce07..a04adee3f587 100644 --- a/packages/kit/src/exports/node/index.js +++ b/packages/kit/src/exports/node/index.js @@ -2,8 +2,9 @@ import * as set_cookie_parser from 'set-cookie-parser'; /** * @param {import('http').IncomingMessage} req + * @param {number} [body_size_limit] */ -function get_raw_body(req) { +function get_raw_body(req, body_size_limit) { const h = req.headers; if (!h['content-type']) { @@ -20,6 +21,19 @@ function get_raw_body(req) { return null; } + if (body_size_limit) { + if (!length) { + throw new Error( + `Received content-length of ${length}. content-length must be provided when body size limit is specified.` + ); + } + if (length > body_size_limit) { + throw new Error( + `Received content-length of ${length}, but only accept up to ${body_size_limit} bytes.` + ); + } + } + if (req.destroyed) { const readable = new ReadableStream(); readable.cancel(); @@ -69,7 +83,7 @@ function get_raw_body(req) { } /** @type {import('@sveltejs/kit/node').getRequest} */ -export async function getRequest({ request, base }) { +export async function getRequest({ request, base, bodySizeLimit }) { let headers = /** @type {Record} */ (request.headers); if (request.httpVersionMajor === 2) { // we need to strip out the HTTP/2 pseudo-headers because node-fetch's @@ -85,7 +99,7 @@ export async function getRequest({ request, base }) { return new Request(base + request.url, { method: request.method, headers, - body: get_raw_body(request) + body: get_raw_body(request, bodySizeLimit) }); } diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts index ea601187a62d..1c27752d3cb5 100644 --- a/packages/kit/types/ambient.d.ts +++ b/packages/kit/types/ambient.d.ts @@ -411,6 +411,7 @@ declare module '@sveltejs/kit/node' { export function getRequest(opts: { base: string; request: import('http').IncomingMessage; + bodySizeLimit?: number; }): Promise; export function setResponse(res: import('http').ServerResponse, response: Response): void; }