From db22f32e729565e40adf63c3220a9eaac04ec900 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 15 Feb 2026 04:27:03 +0000 Subject: [PATCH] Reject baseURL query and hash components Co-authored-by: Eric Allam --- docs/tasks/streams.mdx | 2 +- packages/ai/CHANGELOG.md | 1 + packages/ai/README.md | 1 + packages/ai/src/chatTransport.test.ts | 44 +++++++++++++++++++++++++++ packages/ai/src/chatTransport.ts | 4 +++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/tasks/streams.mdx b/docs/tasks/streams.mdx index 49798ca75..f0ac175de 100644 --- a/docs/tasks/streams.mdx +++ b/docs/tasks/streams.mdx @@ -656,7 +656,7 @@ If `onError` is omitted, reconnect still returns `null` and continues without ca `baseURL` supports optional path prefixes and trailing slashes; both trigger and stream URLs are normalized consistently, surrounding whitespace is trimmed before normalization, and the resulting value must not be empty. The value must also be a valid absolute URL using -the `http` or `https` protocol. +the `http` or `https` protocol, without query parameters or hash fragments. For richer TypeScript ergonomics in app code, `@trigger.dev/ai` also exports: diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 085ab4be4..7d61540b2 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -24,3 +24,4 @@ - Added explicit validation that `baseURL` is non-empty after normalization. - Added explicit validation that `baseURL` is a valid absolute URL. - Added explicit validation that `baseURL` uses `http` or `https`. +- Added explicit validation that `baseURL` excludes query parameters and hash fragments. diff --git a/packages/ai/README.md b/packages/ai/README.md index f20d57663..44c8ef0dc 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -163,6 +163,7 @@ both cleanup steps (`set` inactive state and `delete`) even if one of them fails - `baseURL` must not be empty after trimming/normalization. - `baseURL` must be a valid absolute URL. - `baseURL` must use the `http` or `https` protocol. +- `baseURL` must not include query parameters or hash fragments. ## `ai.tool(...)` example diff --git a/packages/ai/src/chatTransport.test.ts b/packages/ai/src/chatTransport.test.ts index ff6e51fd5..f3218df06 100644 --- a/packages/ai/src/chatTransport.test.ts +++ b/packages/ai/src/chatTransport.test.ts @@ -653,6 +653,28 @@ describe("TriggerChatTransport", function () { }).toThrowError("baseURL must use http or https protocol"); }); + it("throws when baseURL includes query parameters", function () { + expect(function () { + new TriggerChatTransport({ + task: "chat-task", + accessToken: "pk_trigger", + baseURL: "https://example.com/base?query=1", + stream: "chat-stream", + }); + }).toThrowError("baseURL must not include query parameters or hash fragments"); + }); + + it("throws when baseURL includes hash fragments", function () { + expect(function () { + new TriggerChatTransport({ + task: "chat-task", + accessToken: "pk_trigger", + baseURL: "https://example.com/base#fragment", + stream: "chat-stream", + }); + }).toThrowError("baseURL must not include query parameters or hash fragments"); + }); + it("accepts uppercase http protocol in baseURL", async function () { let observedTriggerPath: string | undefined; let observedStreamPath: string | undefined; @@ -2870,6 +2892,28 @@ describe("TriggerChatTransport", function () { }).toThrowError("baseURL must use http or https protocol"); }); + it("throws from factory when baseURL includes query parameters", function () { + expect(function () { + createTriggerChatTransport({ + task: "chat-task", + accessToken: "pk_trigger", + baseURL: "https://example.com/base?query=1", + stream: "chat-stream", + }); + }).toThrowError("baseURL must not include query parameters or hash fragments"); + }); + + it("throws from factory when baseURL includes hash fragments", function () { + expect(function () { + createTriggerChatTransport({ + task: "chat-task", + accessToken: "pk_trigger", + baseURL: "https://example.com/base#fragment", + stream: "chat-stream", + }); + }).toThrowError("baseURL must not include query parameters or hash fragments"); + }); + it("continues streaming when onTriggeredRun callback throws", async function () { let callbackCalled = false; const errors: TriggerChatTransportError[] = []; diff --git a/packages/ai/src/chatTransport.ts b/packages/ai/src/chatTransport.ts index ebc263b6c..aba58f5d2 100644 --- a/packages/ai/src/chatTransport.ts +++ b/packages/ai/src/chatTransport.ts @@ -480,6 +480,10 @@ function normalizeBaseUrl(baseURL: string) { throw new Error("baseURL must use http or https protocol"); } + if (parsedBaseUrl.search.length > 0 || parsedBaseUrl.hash.length > 0) { + throw new Error("baseURL must not include query parameters or hash fragments"); + } + return normalizedBaseUrl; }