diff --git a/packages/agent/src/utils/gateway.test.ts b/packages/agent/src/utils/gateway.test.ts index 9a0b06e3e7..a7784ce067 100644 --- a/packages/agent/src/utils/gateway.test.ts +++ b/packages/agent/src/utils/gateway.test.ts @@ -124,15 +124,21 @@ describe("buildGatewayPropertyHeaders", () => { }, ); - it("strips characters an HTTP header value cannot carry", () => { - expect(buildGatewayPropertyHeaders({ task_title: "don’t🚀ship" })).toBe( - "x-posthog-property-task_title: dontship", - ); - }); - - it("keeps latin1 characters such as accents", () => { - expect(buildGatewayPropertyHeaders({ task_title: "café" })).toBe( - "x-posthog-property-task_title: café", + it.each([ + { + description: "smart quote and emoji", + title: "don’t🚀ship", + expected: "don%E2%80%99t%F0%9F%9A%80ship", + }, + { + description: "Danish accented, CJK, and emoji characters", + title: "har vi nogle logs på café 東京 🚀", + expected: + "har vi nogle logs p%C3%A5 caf%C3%A9 %E6%9D%B1%E4%BA%AC %F0%9F%9A%80", + }, + ])("encodes non-ASCII characters in $description", ({ title, expected }) => { + expect(buildGatewayPropertyHeaders({ task_title: title })).toBe( + `x-posthog-property-task_title: ${expected}`, ); }); }); diff --git a/packages/agent/src/utils/gateway.ts b/packages/agent/src/utils/gateway.ts index ef39ec2bb5..4d80c2b0b3 100644 --- a/packages/agent/src/utils/gateway.ts +++ b/packages/agent/src/utils/gateway.ts @@ -28,14 +28,14 @@ export function resolveGatewayProduct({ } /** - * Make a value safe to embed in an HTTP header value. Collapses newlines to - * spaces (the header block is newline-delimited) and drops characters outside - * the valid header-byte range — control chars and code points above latin1 - * (emoji, smart quotes) — which an HTTP client (e.g. undici) would otherwise - * reject before sending. ASCII is preserved. + * Make a value safe to embed in an HTTP header value. Collapses newlines to + * spaces and percent-encodes non-ASCII characters so header values remain + * ASCII-safe. ASCII is preserved. */ function sanitizeHeaderValue(value: string): string { - return value.replace(/[\r\n]+/g, " ").replace(/[^\x20-\x7e\x80-\xff]/g, ""); + return value + .replace(/[\r\n]+/g, " ") + .replace(/[^\x20-\x7e]/gu, (char) => encodeURIComponent(char)); } /**