From d5026ae77334956fad242b233a00c91670bad591 Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Thu, 4 Dec 2025 11:13:26 -0600 Subject: [PATCH] Fix malformed host values causing an unhandled exception --- .changeset/gentle-clouds-heal.md | 5 +++ .../src/tokens/__tests__/clerkRequest.test.ts | 33 +++++++++++++++++++ packages/backend/src/tokens/clerkRequest.ts | 7 +++- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .changeset/gentle-clouds-heal.md diff --git a/.changeset/gentle-clouds-heal.md b/.changeset/gentle-clouds-heal.md new file mode 100644 index 00000000000..4e68a4e9a0d --- /dev/null +++ b/.changeset/gentle-clouds-heal.md @@ -0,0 +1,5 @@ +--- +'@clerk/backend': patch +--- + +Fixes an issue with host header parsing that would cause Clerk to throw an exception when receiving malformed host values. diff --git a/packages/backend/src/tokens/__tests__/clerkRequest.test.ts b/packages/backend/src/tokens/__tests__/clerkRequest.test.ts index 9582d70a1fb..18c5c72ece0 100644 --- a/packages/backend/src/tokens/__tests__/clerkRequest.test.ts +++ b/packages/backend/src/tokens/__tests__/clerkRequest.test.ts @@ -158,6 +158,39 @@ describe('createClerkRequest', () => { const req2 = new Request('http://localhost:3000////path'); expect(createClerkRequest(req2).clerkUrl.toString()).toBe('http://localhost:3000////path'); }); + + it('handles malicious host header with script injection gracefully', () => { + const req = new Request('http://localhost:3000/path', { + headers: { + 'x-forwarded-host': 'z2cgvm.xfh">/', + 'x-forwarded-proto': 'https', + }, + }); + expect(() => createClerkRequest(req)).not.toThrow(); + expect(createClerkRequest(req).clerkUrl.toString()).toBe('http://localhost:3000/path'); + }); + + it('handles malicious host header with invalid characters gracefully', () => { + const req = new Request('http://localhost:3000/path?foo=bar', { + headers: { + 'x-forwarded-host': 'host', + 'x-forwarded-proto': 'https', + }, + }); + expect(() => createClerkRequest(req)).not.toThrow(); + expect(createClerkRequest(req).clerkUrl.toString()).toBe('http://localhost:3000/path?foo=bar'); + }); + + it('handles empty forwarded headers gracefully', () => { + const req = new Request('http://localhost:3000/path', { + headers: { + 'x-forwarded-host': '', + 'x-forwarded-proto': '', + }, + }); + expect(() => createClerkRequest(req)).not.toThrow(); + expect(createClerkRequest(req).clerkUrl.toString()).toBe('http://localhost:3000/path'); + }); }); describe('toJSON', () => { diff --git a/packages/backend/src/tokens/clerkRequest.ts b/packages/backend/src/tokens/clerkRequest.ts index 9eef0a6c117..f35b079d779 100644 --- a/packages/backend/src/tokens/clerkRequest.ts +++ b/packages/backend/src/tokens/clerkRequest.ts @@ -59,7 +59,12 @@ class ClerkRequest extends Request { if (origin === initialUrl.origin) { return createClerkUrl(initialUrl); } - return createClerkUrl(initialUrl.pathname + initialUrl.search, origin); + + try { + return createClerkUrl(initialUrl.pathname + initialUrl.search, origin); + } catch { + return createClerkUrl(initialUrl); + } } private getFirstValueFromHeader(value?: string | null) {