From 9ee54077b675c49a70fb8a299675e9ff32ee6023 Mon Sep 17 00:00:00 2001 From: kitsuyui Date: Fri, 17 Jul 2026 18:55:56 +0900 Subject: [PATCH] fix: reject repeated x-rendering-proxy headers Align the server behavior with the documented single-header protocol so duplicate header inputs fail deterministically instead of being concatenated implicitly. --- README.md | 4 ++-- src/server/request_options.spec.ts | 11 ++++++++++- src/server/request_options.ts | 14 +++++++++++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 53dd92c5..4250cb85 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ x-rendering-proxy-version: 1 #### Protocol schema (v1) -**Request header** `X-Rendering-Proxy` — JSON object: +**Request header** `X-Rendering-Proxy` — a single JSON object header value: | field | type | default | description | | --- | --- | --- | --- | @@ -113,7 +113,7 @@ x-rendering-proxy-version: 1 | `evaluates` | `string[]` | `[]` | JavaScript snippets to evaluate before capturing the DOM. | | `timeout` | `number` (ms) | none | Navigation timeout in milliseconds. | -Requests with a non-object `X-Rendering-Proxy` JSON payload or with fields of the wrong type are rejected with `400 Bad Request`. +Requests with a repeated `X-Rendering-Proxy` header, a non-object JSON payload, or fields of the wrong type are rejected with `400 Bad Request`. **Response header** `X-Rendering-Proxy` — JSON array of `EvaluateResult`: diff --git a/src/server/request_options.spec.ts b/src/server/request_options.spec.ts index 97b8dae2..3cf63d4d 100644 --- a/src/server/request_options.spec.ts +++ b/src/server/request_options.spec.ts @@ -21,7 +21,16 @@ describe('parseRenderingProxyHeader', () => { }) }) - it('throws for syntactically invalid JSON in a non-empty header', async () => { + it('rejects repeated x-rendering-proxy header values', async () => { + expect(() => + parseRenderingProxyHeader([ + '{"evaluates":["1 + 1"]}', + '{"waitUntil":"load"}', + ]), + ).toThrow(TypeError) + }) + + it('throws SyntaxError for syntactically invalid JSON in a non-empty header', async () => { // Callers (e.g. the HTTP server) must catch and return 400 Bad Request expect(() => parseRenderingProxyHeader('{')).toThrow(SyntaxError) expect(() => parseRenderingProxyHeader('[unclosed')).toThrow(SyntaxError) diff --git a/src/server/request_options.ts b/src/server/request_options.ts index cca58aad..e409a04f 100644 --- a/src/server/request_options.ts +++ b/src/server/request_options.ts @@ -15,6 +15,18 @@ type RequestOptionInput = { timeout?: unknown } +function normalizeHeaderArray(headerValue: string[]): string { + if (headerValue.length === 0) { + return '' + } + if (headerValue.length === 1) { + return headerValue[0] ?? '' + } + throw new TypeError( + 'X-Rendering-Proxy must be provided as a single header value', + ) +} + export function parseRenderingProxyHeader( headerValue: undefined | string | string[], ): RequestOption { @@ -22,7 +34,7 @@ export function parseRenderingProxyHeader( return parseOptions(headerValue) } if (Array.isArray(headerValue)) { - return parseOptions(headerValue.join('')) + return parseOptions(normalizeHeaderArray(headerValue)) } return parseOptions('') }