From e26e74c3c211ed0a6d86d982cd878c8fe355f828 Mon Sep 17 00:00:00 2001 From: Kiro Agent <244629292+kiro-agent@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:15:36 +0000 Subject: [PATCH] fix: use case-insensitive header lookup in parseBody and corsHeader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HTTP header names are case-insensitive per RFC 9110 §5.1. API Gateway REST (v1) proxy integrations pass headers with the client's original casing. The parseBody function previously did a direct bracket access for 'content-type' (lowercase only), which missed canonically-cased 'Content-Type' headers — silently passing an unparsed string body to handlers that expect a parsed object. Added a headerValue helper that finds headers case-insensitively and applied it to both parseBody (content-type) and corsHeader (origin). Closes #377 Co-authored-by: Thorsten Hoeger --- src/lambda/handler.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/lambda/handler.ts b/src/lambda/handler.ts index 0f395345..4385c9e1 100644 --- a/src/lambda/handler.ts +++ b/src/lambda/handler.ts @@ -184,6 +184,16 @@ export const createHttpHandler = }; }; +/** + * Looks up a header value case-insensitively. + * HTTP header names are case-insensitive per RFC 9110 §5.1. + */ +function headerValue(headers: Record | null | undefined, name: string): string | undefined { + if (!headers) return undefined; + const key = Object.keys(headers).find(k => k.toLowerCase() === name.toLowerCase()); + return key ? headers[key] : undefined; +} + /** * This function takes an event object representing an API Gateway Proxy event * with a Cognito authorizer, and extracts and parses the request body, if present. @@ -201,7 +211,7 @@ function parseBody(event: AWSLambda.APIGatewayProxyWithCognitoAuthorizerEvent } // If the request body is in JSON format, parse it into a JavaScript object - if (event.headers && event.headers['content-type']?.includes('application/json')) { + if (headerValue(event.headers, 'content-type')?.includes('application/json')) { return JSON.parse(body ?? '{}'); } @@ -217,9 +227,10 @@ function parseBody(event: AWSLambda.APIGatewayProxyWithCognitoAuthorizerEvent * @returns An object containing the CORS headers. */ function corsHeader(event: AWSLambda.APIGatewayProxyWithCognitoAuthorizerEvent): { [name: string]: string } { + const origin = headerValue(event?.headers, 'origin'); return { - 'Access-Control-Allow-Origin': event?.headers?.origin ?? '*', // Allow requests from the origin header, or allow any origin if not present - 'Access-Control-Allow-Credentials': event?.headers?.origin ? 'true' : 'false', // Include cookies in cross-origin requests if the origin header is present + 'Access-Control-Allow-Origin': origin ?? '*', // Allow requests from the origin header, or allow any origin if not present + 'Access-Control-Allow-Credentials': origin ? 'true' : 'false', // Include cookies in cross-origin requests if the origin header is present 'Access-Control-Allow-Methods': '*', // Allow any HTTP method 'Access-Control-Allow-Headers': 'Authorization, *', // Allow the Authorization header and any other headers };