From e545d256db394e26ca8a5b5f8b3a4350af311491 Mon Sep 17 00:00:00 2001 From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Date: Wed, 2 Sep 2026 04:49:58 -0300 Subject: [PATCH] fix(adapta): redact streamed upstream errors --- open-sse/executors/adapta-web.ts | 6 +- .../executor-adapta-web-stream-error.test.ts | 70 +++++++++++++++++++ tests/unit/executor-adapta-web.test.ts | 31 ++++---- 3 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 tests/unit/executor-adapta-web-stream-error.test.ts diff --git a/open-sse/executors/adapta-web.ts b/open-sse/executors/adapta-web.ts index 8c9ae571ea..0b67c10db8 100644 --- a/open-sse/executors/adapta-web.ts +++ b/open-sse/executors/adapta-web.ts @@ -5,6 +5,7 @@ import { sanitizeErrorMessage } from "../utils/error.ts"; const ADAPTA_APP_URL = "https://agent.adapta.one"; const ADAPTA_CLERK_URL = "https://clerk.agent.adapta.one"; const ADAPTA_STREAM_URL = `${ADAPTA_APP_URL}/api/chat/stream/v1`; +const ADAPTA_PUBLIC_STREAM_ERROR = `\n\n[Erro: ${sanitizeErrorMessage("Adapta upstream error")}]`; // Default model ID in Adapta's internal system (corresponds to "ONE" / auto-select) const DEFAULT_AI_MODEL_ID = 14; @@ -308,10 +309,9 @@ function transformStream(adaptaStream: ReadableStream, model: string): ReadableS if (event.id === "quick-response") continue; // Real text ended — stream will send more events or close } else if (type === "error") { - const errText = String(event.errorText ?? "Adapta upstream error"); ensureRole(); - // Emit the error as content so the user sees it - chunk({ content: `\n\n[Erro: ${errText}]` }); + // Keep upstream diagnostics private: the transformed SSE is a public HTTP 200 body. + chunk({ content: ADAPTA_PUBLIC_STREAM_ERROR }); finalize(); return; } else if (type === "done" || type === "end") { diff --git a/tests/unit/executor-adapta-web-stream-error.test.ts b/tests/unit/executor-adapta-web-stream-error.test.ts new file mode 100644 index 0000000000..23cec6c610 --- /dev/null +++ b/tests/unit/executor-adapta-web-stream-error.test.ts @@ -0,0 +1,70 @@ +import assert from "node:assert/strict"; +import { mkdirSync, mkdtempSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import test from "node:test"; + +const testRoot = mkdtempSync(join(tmpdir(), "omniroute-adapta-stream-error-")); +process.env.DATA_DIR = join(testRoot, "data"); +process.env.OMNIROUTE_PLUGINS_DIR = join(testRoot, "plugins"); +mkdirSync(process.env.DATA_DIR, { recursive: true }); +mkdirSync(process.env.OMNIROUTE_PLUGINS_DIR, { recursive: true }); + +const { AdaptaWebExecutor } = await import("../../open-sse/executors/adapta-web.ts"); + +const originalFetch = globalThis.fetch; + +test.afterEach(() => { + globalThis.fetch = originalFetch; +}); + +test("terminates an upstream error event without exposing its text in the public SSE", async () => { + const hostileError = + "SQLSTATE 42P01 at /srv/omniroute/private.ts:91 — Authorization: Bearer secret-token"; + const requestedUrls: string[] = []; + const logMessages: string[] = []; + + globalThis.fetch = (async (input: RequestInfo | URL) => { + const url = String(input); + requestedUrls.push(url); + + if (url.endsWith("/v1/client")) { + return Response.json({ + response: { sessions: [{ id: "session-stream-error", status: "active" }] }, + }); + } + + if (url.includes("/tokens")) { + return Response.json({ jwt: "eyJ.test-session.jwt" }); + } + + return new Response(`data: ${JSON.stringify({ type: "error", errorText: hostileError })}\n\n`, { + status: 200, + headers: { "Content-Type": "text/event-stream" }, + }); + }) as typeof fetch; + + const executor = new AdaptaWebExecutor(); + const result = await executor.execute({ + model: "adapta-one", + body: { messages: [{ role: "user", content: "hello" }] }, + stream: true, + credentials: { apiKey: "__client=unique-stream-error-cookie" }, + signal: null, + log: { + info: (_tag, message) => logMessages.push(message), + warn: (_tag, message) => logMessages.push(message), + }, + }); + + assert.equal(result.response.status, 200); + assert.equal(result.response.headers.get("content-type"), "text/event-stream"); + + const publicSse = await result.response.text(); + assert.equal(requestedUrls.length, 3); + assert.match(publicSse, /"content":"\\n\\n\[Erro: Adapta upstream error\]"/); + assert.match(publicSse, /"finish_reason":"stop"/); + assert.match(publicSse, /data: \[DONE\]/); + assert.doesNotMatch(publicSse, /SQLSTATE|\/srv\/omniroute|secret-token/); + assert.doesNotMatch(logMessages.join("\n"), /SQLSTATE|\/srv\/omniroute|secret-token/); +}); diff --git a/tests/unit/executor-adapta-web.test.ts b/tests/unit/executor-adapta-web.test.ts index 46105774ee..fc6538f8cf 100644 --- a/tests/unit/executor-adapta-web.test.ts +++ b/tests/unit/executor-adapta-web.test.ts @@ -36,18 +36,25 @@ describe("AdaptaWebExecutor", () => { }); it("execute returns proper result shape on auth failure", async () => { - const executor = new mod.AdaptaWebExecutor(); - const result = await executor.execute({ - model: "adapta-one", - body: { messages: [{ role: "user", content: "hi" }] }, - stream: false, - credentials: { apiKey: "invalid-jwt" }, - signal: null, - }); - assert.ok(result.response instanceof Response); - assert.ok(typeof result.url === "string"); - assert.ok(typeof result.headers === "object"); - assert.ok(result.transformedBody !== undefined); + const originalFetch = globalThis.fetch; + globalThis.fetch = (async () => new Response(null, { status: 401 })) as typeof fetch; + + try { + const executor = new mod.AdaptaWebExecutor(); + const result = await executor.execute({ + model: "adapta-one", + body: { messages: [{ role: "user", content: "hi" }] }, + stream: false, + credentials: { apiKey: "invalid-jwt" }, + signal: null, + }); + assert.ok(result.response instanceof Response); + assert.ok(typeof result.url === "string"); + assert.ok(typeof result.headers === "object"); + assert.ok(result.transformedBody !== undefined); + } finally { + globalThis.fetch = originalFetch; + } }); it("testConnection returns false for invalid credentials", async () => {