Compare commits

..

6 Commits

Author SHA1 Message Date
diegosouzapw
866f4df88b Merge remote-tracking branch 'origin/release/v3.8.51' into fix/v3851-streamhandler-public-errors 2026-09-02 16:05:45 -03:00
Diego Rodrigues de Sa e Souza
e243b04de2 fix(security): unbiased maxai X-Random nonce + stricter URL/regex assertions (#12502)
Drains 7 of the 13 open CodeQL alerts that put the `codeql-ratchet` gate into
regression (13 > baseline 11) on every open PR. The alerts arrived with the
recent provider/media merges (#11461 MaxAI, #11513 UC, #12365 prefix shadowing),
not with the work they are currently blocking.

Production fix (js/biased-cryptographic-random):
- open-sse/executors/maxai/signing.ts: the 6-digit `X-Random` wire slot was
  drawn as `randomBytes(4).readUInt32BE(0) % 900000`. 2^32 does not divide
  evenly by 900000, so the low ~4772 values of the range came out marginally
  more often. Extracted as `maxaiRandomSlot()` over `crypto.randomInt`, which
  rejection-samples internally. The emitted shape is unchanged (6 digits).

Test assertions strengthened (never weakened):
- tests/unit/helpers/ucClerkUrl.ts (new): `isUcClerkMintUrl()` matches the Clerk
  mint call by parsed origin (against `UC_CLERK_FAPI`) plus the
  `/v1/client/sessions/{sid}/tokens` path shape.
- tests/unit/uc-image.test.ts, tests/unit/uc-video.test.ts: the mock fetch
  routers dispatched on `url.includes("clerk.uncensored.com")`, so any host
  merely embedding the name was served the mint response — a malformed URL
  built by the executor could not fail the test
  (js/incomplete-url-substring-sanitization x4).
- tests/unit/maxai-image.test.ts: `new RegExp(PATH.replace(/\//g, "\\/"))`
  escaped only slashes (which need no escaping) and matched the path anywhere in
  a wrong URL; replaced by exact URL equality (js/incomplete-sanitization).
- tests/unit/custom-provider-prefix-shadowing-11943.test.ts: the expected node
  mention was a RegExp with only `()` hand-escaped; replaced by an exact
  substring check (js/incomplete-sanitization).
- tests/unit/maxai.test.ts: regression guard for the X-Random slot (6 digits,
  in range, spread across both halves of the range).

The remaining 6 alerts are not defects and are left for an operator dismissal
with justification (Hard Rule #14): the MaxAI HMAC-SHA1/SM3 signature and the
CryptoJS `EVP_BytesToKey(MD5)` derivation are wire-protocol requirements —
changing either breaks the provider — and `open-sse/utils/error.ts:749` already
routes through `sanitizeErrorMessage()` (documented CodeQL sanitizer blind spot,
docs/security/ERROR_SANITIZATION.md).

Co-authored-by: Markus Hartung <diegosouzapw@users.noreply.github.com>
2026-09-02 15:56:49 -03:00
diegosouzapw
5635565595 chore(release): preserve reconciled changelog 2026-09-02 08:26:27 -03:00
diegosouzapw
f89093def1 Merge remote-tracking branch 'origin/release/v3.8.51' into fix/v3851-streamhandler-public-errors 2026-09-02 08:20:29 -03:00
diegosouzapw
54e2cc7aa0 test(streaming): isolate public error boundary fixture 2026-09-02 08:20:20 -03:00
diegosouzapw
4c39274a96 fix(streaming): sanitize generic stream failures 2026-09-02 06:52:40 -03:00
17 changed files with 448 additions and 859 deletions

View File

@@ -97,6 +97,10 @@ _Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). B
### 🐛 Bug Fixes
- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI,
Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw
failures for internal classification and keeping client disconnects out of provider failure state.
### 📝 Maintenance
---

View File

@@ -1 +0,0 @@
- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop.

View File

@@ -27,11 +27,7 @@ import {
import { FETCH_TIMEOUT_MS } from "../config/constants.ts";
import { buildErrorBody, sanitizeErrorMessage } from "../utils/error.ts";
import { normalizeSessionCookieHeader } from "@/lib/providers/webCookieAuth";
import {
HuggingChatStreamError,
readJsonlResponse,
streamJsonlToOpenAi,
} from "./huggingchat/jsonlStream.ts";
import { streamJsonlToOpenAi, readJsonlResponse } from "./huggingchat/jsonlStream.ts";
const HUGGINGFACE_BASE = "https://huggingface.co";
const CONVERSATION_URL = `${HUGGINGFACE_BASE}/chat/conversation`;
@@ -42,7 +38,6 @@ const USER_AGENT =
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
const DEFAULT_MODEL = "baidu/ERNIE-4.5-VL-424B-A47B-Base-PT";
const HUGGINGCHAT_PUBLIC_STREAM_ERROR = "HuggingChat generation failed";
// -- Helpers -----------------------------------------------------------------
@@ -528,81 +523,26 @@ export class HuggingChatExecutor extends BaseExecutor {
if (stream) {
const encoder = new TextEncoder();
const streamCancellationController = new AbortController();
const jsonlStream = streamJsonlToOpenAi(
upstreamResponse.body,
resolvedModel,
id,
created,
signal,
streamCancellationController.signal
signal
);
const primedChunks: string[] = [];
try {
const first = await jsonlStream.next();
if (!first.done) {
primedChunks.push(first.value);
if (first.value.includes('"role":"assistant"')) {
const content = await jsonlStream.next();
if (!content.done) primedChunks.push(content.value);
}
}
} catch (err) {
if (!(err instanceof HuggingChatStreamError)) throw err;
const message = err instanceof Error ? err.message : String(err);
const safeMessage = sanitizeErrorMessage(message);
log?.error?.("HUGGINGCHAT", `Stream failed before content: ${safeMessage}`);
return {
response: new Response(
JSON.stringify(
buildErrorBody(502, message, undefined, {
type: "upstream_error",
code: "huggingchat_generation_error",
})
),
{ status: 502, headers: { "Content-Type": "application/json" } }
),
url: messageUrl,
headers: baseHeaders,
transformedBody: sendDataPayload,
};
}
let primedChunkIndex = 0;
let streamCancelled = false;
const sseStream = new ReadableStream<Uint8Array>({
async pull(controller) {
if (streamCancelled) return;
if (primedChunkIndex < primedChunks.length) {
controller.enqueue(encoder.encode(primedChunks[primedChunkIndex]));
primedChunkIndex += 1;
return;
}
const sseStream = new ReadableStream({
async start(controller) {
try {
const chunk = await jsonlStream.next();
if (streamCancelled) return;
if (chunk.done) {
controller.close();
return;
for await (const chunk of jsonlStream) {
controller.enqueue(encoder.encode(chunk));
}
controller.enqueue(encoder.encode(chunk.value));
} catch (err) {
if (streamCancelled) return;
const message = err instanceof Error ? err.message : String(err);
const safeMessage = sanitizeErrorMessage(message);
log?.error?.("HUGGINGCHAT", `Stream error: ${safeMessage}`);
controller.error(
Object.assign(new Error(HUGGINGCHAT_PUBLIC_STREAM_ERROR), { statusCode: 502 })
);
log?.error?.("HUGGINGCHAT", `Stream error: ${err}`);
} finally {
controller.close();
}
},
cancel() {
streamCancelled = true;
streamCancellationController.abort();
void jsonlStream.return(undefined).catch(() => undefined);
},
});
return {
@@ -620,29 +560,7 @@ export class HuggingChatExecutor extends BaseExecutor {
};
}
let fullText: string;
try {
fullText = await readJsonlResponse(upstreamResponse.body, signal);
} catch (err) {
if (!(err instanceof HuggingChatStreamError)) throw err;
const message = err instanceof Error ? err.message : String(err);
const safeMessage = sanitizeErrorMessage(message);
log?.error?.("HUGGINGCHAT", `Generation error: ${safeMessage}`);
return {
response: new Response(
JSON.stringify(
buildErrorBody(502, message, undefined, {
type: "upstream_error",
code: "huggingchat_generation_error",
})
),
{ status: 502, headers: { "Content-Type": "application/json" } }
),
url: messageUrl,
headers: baseHeaders,
transformedBody: sendDataPayload,
};
}
const fullText = await readJsonlResponse(upstreamResponse.body, signal);
const completionTokens = estimateTokens(fullText);
return {

View File

@@ -1,36 +1,5 @@
// Pure JSONL stream translation (HuggingChat NDJSON -> OpenAI SSE). Verbatim from huggingchat.ts.
export class HuggingChatStreamError extends Error {
constructor(message: string) {
super(message);
this.name = "HuggingChatStreamError";
}
}
function cancelReader(reader: ReadableStreamDefaultReader<Uint8Array>): void {
try {
void reader.cancel().catch(() => undefined);
} catch {
// The error event is authoritative; transport cleanup is best effort.
}
}
function bindReaderCancellation(
reader: ReadableStreamDefaultReader<Uint8Array>,
signal?: AbortSignal | null
): () => void {
if (!signal) return () => undefined;
const cancel = () => cancelReader(reader);
if (signal.aborted) {
cancel();
return () => undefined;
}
signal.addEventListener("abort", cancel, { once: true });
return () => signal.removeEventListener("abort", cancel);
}
export function sseChunk(data: unknown): string {
return `data: ${JSON.stringify(data)}\n\n`;
}
@@ -73,11 +42,9 @@ export async function* streamJsonlToOpenAi(
model: string,
id: string,
created: number,
signal?: AbortSignal | null,
cancellationSignal?: AbortSignal | null
signal?: AbortSignal | null
): AsyncGenerator<string> {
const reader = body.getReader();
const unbindReaderCancellation = bindReaderCancellation(reader, cancellationSignal);
const decoder = new TextDecoder();
let buffer = "";
let emittedRole = false;
@@ -103,8 +70,16 @@ export async function* streamJsonlToOpenAi(
const parsed = parseJsonlLine(trimmed);
if (parsed.error) {
cancelReader(reader);
throw new HuggingChatStreamError(parsed.error);
yield sseChunk({
id,
object: "chat.completion.chunk",
created,
model,
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
});
yield "data: [DONE]\n\n";
finished = true;
return;
}
if (parsed.token) {
@@ -165,9 +140,6 @@ export async function* streamJsonlToOpenAi(
if (!finished && buffer.trim()) {
const parsed = parseJsonlLine(buffer.trim());
if (parsed.error) {
throw new HuggingChatStreamError(parsed.error);
}
if (parsed.token && !signal?.aborted) {
if (!emittedRole) {
emittedRole = true;
@@ -189,11 +161,10 @@ export async function* streamJsonlToOpenAi(
}
}
} finally {
unbindReaderCancellation();
reader.releaseLock();
}
if (!signal?.aborted && !cancellationSignal?.aborted) {
if (!signal?.aborted) {
yield sseChunk({
id,
object: "chat.completion.chunk",
@@ -201,9 +172,7 @@ export async function* streamJsonlToOpenAi(
model,
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
});
if (!signal?.aborted && !cancellationSignal?.aborted) {
yield "data: [DONE]\n\n";
}
yield "data: [DONE]\n\n";
}
}
@@ -235,10 +204,7 @@ export async function readJsonlResponse(
const parsed = parseJsonlLine(trimmed);
if (parsed.token) fullText += parsed.token;
if (parsed.text) return parsed.text;
if (parsed.error) {
cancelReader(reader);
throw new HuggingChatStreamError(parsed.error);
}
if (parsed.error) throw new Error(parsed.error);
}
}
@@ -246,7 +212,6 @@ export async function readJsonlResponse(
const parsed = parseJsonlLine(buffer.trim());
if (parsed.text) return parsed.text;
if (parsed.token) fullText += parsed.token;
if (parsed.error) throw new HuggingChatStreamError(parsed.error);
}
} finally {
reader.releaseLock();

View File

@@ -23,7 +23,7 @@
* The non-secret STRUCTURAL fields (appVersion, ctxKey, header names) carry safe
* defaults so a transient parse miss can't break an otherwise-working signer.
*/
import { createHmac, createHash, createCipheriv, randomBytes } from "node:crypto";
import { createHmac, createHash, createCipheriv, randomBytes, randomInt } from "node:crypto";
import type { MaxaiSigningConstants, MaxaiHeaderNames } from "./constants.ts";
import { MAXAI_DEFAULT_HEADER_NAMES } from "./constants.ts";
@@ -39,8 +39,22 @@ const BLANK_USER_ROUTES = new Set([
const MAGIC = Buffer.from("Salted__", "ascii");
/**
* The wire `X-Random` slot: a 6-digit decimal string (100000-999999).
*
* Uses `crypto.randomInt`, which rejection-samples internally, instead of
* `randomBytes(4) % 900000` — a plain modulo over a 32-bit draw does not divide
* evenly by 900000, so the low ~4772 values of the range came out marginally
* more often. The emitted shape is unchanged (always exactly 6 digits).
*/
export function maxaiRandomSlot(): string {
return String(randomInt(100000, 1000000));
}
function hmacSha1Hex(message: string, key: string): string {
return createHmac("sha1", Buffer.from(key, "utf8")).update(Buffer.from(message, "utf8")).digest("hex");
return createHmac("sha1", Buffer.from(key, "utf8"))
.update(Buffer.from(message, "utf8"))
.digest("hex");
}
function sm3Hex(message: string): string {
@@ -58,7 +72,9 @@ function evpBytesToKey(
let block = Buffer.alloc(0);
const pass = Buffer.from(passphrase, "utf8");
while (derived.length < keyLen + ivLen) {
block = createHash("md5").update(Buffer.concat([block, pass, salt])).digest();
block = createHash("md5")
.update(Buffer.concat([block, pass, salt]))
.digest();
derived = Buffer.concat([derived, block]);
}
return { key: derived.subarray(0, keyLen), iv: derived.subarray(keyLen, keyLen + ivLen) };
@@ -124,8 +140,7 @@ export function buildMaxaiSignedHeaders(
constants: MaxaiSigningConstants
): Record<string, string> {
const reqTime = (input.now ?? (() => Date.now()))();
const random =
input.random?.() ?? String((randomBytes(4).readUInt32BE(0) % 900000) + 100000);
const random = input.random?.() ?? maxaiRandomSlot();
const h: MaxaiHeaderNames = { ...MAXAI_DEFAULT_HEADER_NAMES, ...constants.headerNames };
const ctxKey = constants.ctxKey;
const appVersion = constants.appVersion;

View File

@@ -1,6 +1,7 @@
import { trackPendingRequest } from "@/lib/usageDb";
import { STREAM_IDLE_TIMEOUT_MS } from "../config/constants.ts";
import { FORMATS } from "../translator/formats.ts";
import { buildErrorBody } from "./error.ts";
import { PENDING_REQUEST_CLEARED_MARKER } from "./stream.ts";
import { createCompletedResponsesToolHandoffWatcher } from "./responsesToolHandoff.ts";
import { createStreamContentWatcher, type StreamContentWatcher } from "./streamReadiness.ts";
@@ -187,6 +188,10 @@ function getErrorStatusCode(error: unknown): number {
return 502;
}
function getPublicErrorMessage(errorMsg: string, statusCode: number): string {
return buildErrorBody(statusCode, errorMsg).error.message;
}
function isDeadlineAbortReason(reason: unknown): reason is Error {
return (
reason instanceof Error &&
@@ -406,7 +411,7 @@ export function createStreamController({
}
if (error instanceof Error) {
logStream(`error: ${error.message}`);
logStream(`error: ${getPublicErrorMessage(error.message, getErrorStatusCode(error))}`);
return;
}
logStream("error: unknown");
@@ -452,6 +457,7 @@ export function buildStreamErrorChunks(
clientResponseFormat?: string | null
) {
const statusMapping = getStreamErrorStatusMapping(statusCode);
const publicErrorMessage = getPublicErrorMessage(errorMsg, statusCode);
if (isResponsesClientFormat(clientResponseFormat)) {
const errorEvent = {
@@ -460,7 +466,7 @@ export function buildStreamErrorChunks(
id: null,
status: "failed",
error: {
message: errorMsg,
message: publicErrorMessage,
type: statusMapping.responses.type,
code: statusMapping.responses.code,
},
@@ -475,7 +481,7 @@ export function buildStreamErrorChunks(
type: "error",
error: {
type: statusMapping.claude.type,
message: errorMsg,
message: publicErrorMessage,
},
};
@@ -498,7 +504,7 @@ export function buildStreamErrorChunks(
},
],
error: {
message: errorMsg,
message: publicErrorMessage,
type: statusMapping.responses.type,
code: statusMapping.responses.code,
},

View File

@@ -0,0 +1,211 @@
// This suite owns process-wide DATA_DIR, plugin, logger, and DB state. It must run only inside
// the subprocess launched by tests/unit/stream-handler-public-error-boundary.test.ts.
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
const originalDataDir = process.env.DATA_DIR;
const originalPluginsDir = process.env.OMNIROUTE_PLUGINS_DIR;
const testRoot = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-stream-public-error-"));
const TEST_DATA_DIR = path.join(testRoot, "data");
const TEST_PLUGINS_DIR = path.join(testRoot, "plugins");
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
fs.mkdirSync(TEST_PLUGINS_DIR, { recursive: true });
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.OMNIROUTE_PLUGINS_DIR = TEST_PLUGINS_DIR;
const [core, callLogs, artifactWriter, loggerResource, streamHandler, { FORMATS }] =
await Promise.all([
import("../../src/lib/db/core.ts"),
import("../../src/lib/usage/callLogs.ts"),
import("../../src/lib/usage/callLogArtifactWriter.ts"),
import("../../src/shared/utils/loggerResource.ts"),
import("../../open-sse/utils/streamHandler.ts"),
import("../../open-sse/translator/formats.ts"),
]);
const { createStreamController, pipeWithDisconnect } = streamHandler;
const SECRET = "sk-live-streamhandler-secret-123456";
const API_KEY = "provider-key-streamhandler-654321";
const PRIVATE_PATH = "/srv/omniroute/private/provider.ts:42:9";
const RAW_MESSAGE =
`Upstream failed at ${PRIVATE_PATH} Authorization: Bearer ${SECRET} api_key=${API_KEY}` +
`\n at dispatch (/srv/omniroute/private/dispatcher.ts:88:3)`;
test.after(async () => {
assert.equal(await callLogs.waitForCallLogSaves(3_000), true);
await artifactWriter.closeCallLogArtifactWriter();
core.resetDbInstance();
await loggerResource.closeSharedLoggerResource();
if (originalDataDir === undefined) delete process.env.DATA_DIR;
else process.env.DATA_DIR = originalDataDir;
if (originalPluginsDir === undefined) delete process.env.OMNIROUTE_PLUGINS_DIR;
else process.env.OMNIROUTE_PLUGINS_DIR = originalPluginsDir;
fs.rmSync(testRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("fixture binds all persistent state to its process-owned directories", () => {
assert.equal(core.DATA_DIR, TEST_DATA_DIR);
assert.equal(core.SQLITE_FILE, path.join(TEST_DATA_DIR, "storage.sqlite"));
assert.equal(process.env.DATA_DIR, TEST_DATA_DIR);
assert.equal(process.env.OMNIROUTE_PLUGINS_DIR, TEST_PLUGINS_DIR);
assert.equal(fs.existsSync(TEST_DATA_DIR), true);
assert.equal(fs.existsSync(TEST_PLUGINS_DIR), true);
});
test("OpenAI stream failures keep raw diagnostics internal and sanitize the public wire", async () => {
const upstreamError = Object.assign(new Error(RAW_MESSAGE), { statusCode: 502 });
const source = new ReadableStream<Uint8Array>({
start(controller) {
controller.error(upstreamError);
},
});
let internalMessage = "";
const stream = pipeWithDisconnect(
new Response(source),
new TransformStream<Uint8Array, Uint8Array>(),
createStreamController({
clientResponseFormat: FORMATS.OPENAI,
onError(event) {
internalMessage = event.message;
return true;
},
}),
{ stallTimeoutMs: 0 }
);
const publicWire = await new Response(stream).text();
assert.equal(internalMessage, RAW_MESSAGE, "failure classification must retain the raw message");
assert.match(publicWire, /"finish_reason":"error"/);
assert.match(publicWire, /"code":"server_error"/);
assert.match(publicWire, /\[DONE\]/);
assert.doesNotMatch(publicWire, new RegExp(SECRET));
assert.doesNotMatch(publicWire, new RegExp(API_KEY));
assert.doesNotMatch(publicWire, /\/srv\/omniroute\/private/);
assert.doesNotMatch(publicWire, /dispatcher\.ts/);
assert.match(publicWire, /Authorization: \[REDACTED\]/);
assert.match(publicWire, /<path>/);
});
test("Responses stream failures preserve the failure event shape without leaking diagnostics", async () => {
const upstreamError = Object.assign(new Error(RAW_MESSAGE), { statusCode: 429 });
const source = new ReadableStream<Uint8Array>({
start(controller) {
controller.error(upstreamError);
},
});
let internalError: unknown;
const stream = pipeWithDisconnect(
new Response(source),
new TransformStream<Uint8Array, Uint8Array>(),
createStreamController({
clientResponseFormat: FORMATS.OPENAI_RESPONSES,
onError(event) {
internalError = event.error;
return true;
},
}),
{ stallTimeoutMs: 0 }
);
const publicWire = await new Response(stream).text();
assert.equal(internalError, upstreamError, "the original error object must reach classification");
assert.match(publicWire, /event: response\.failed/);
assert.match(publicWire, /"type":"response\.failed"/);
assert.match(publicWire, /"type":"rate_limit_error"/);
assert.match(publicWire, /"code":"rate_limit_exceeded"/);
assert.doesNotMatch(publicWire, new RegExp(SECRET));
assert.doesNotMatch(publicWire, new RegExp(API_KEY));
assert.doesNotMatch(publicWire, /\/srv\/omniroute\/private/);
assert.doesNotMatch(publicWire, /dispatcher\.ts/);
assert.match(publicWire, /Authorization: \[REDACTED\]/);
assert.match(publicWire, /<path>/);
});
test("Claude stream failures preserve error and stop events without leaking diagnostics", async () => {
const upstreamError = Object.assign(new Error(RAW_MESSAGE), { statusCode: 403 });
const source = new ReadableStream<Uint8Array>({
start(controller) {
controller.error(upstreamError);
},
});
let internalStatusCode = 0;
const stream = pipeWithDisconnect(
new Response(source),
new TransformStream<Uint8Array, Uint8Array>(),
createStreamController({
clientResponseFormat: FORMATS.CLAUDE,
onError(event) {
internalStatusCode = event.statusCode;
return true;
},
}),
{ stallTimeoutMs: 0 }
);
const publicWire = await new Response(stream).text();
assert.equal(internalStatusCode, 403);
assert.match(publicWire, /event: error/);
assert.match(publicWire, /"type":"permission_error"/);
assert.match(publicWire, /event: message_stop/);
assert.doesNotMatch(publicWire, new RegExp(SECRET));
assert.doesNotMatch(publicWire, new RegExp(API_KEY));
assert.doesNotMatch(publicWire, /\/srv\/omniroute\/private/);
assert.doesNotMatch(publicWire, /dispatcher\.ts/);
assert.match(publicWire, /Authorization: \[REDACTED\]/);
assert.match(publicWire, /<path>/);
});
test("stream diagnostics sanitize logs while callbacks retain the original failure", () => {
const upstreamError = Object.assign(new Error(RAW_MESSAGE), { statusCode: 502 });
const originalLog = console.log;
const logLines: string[] = [];
let internalError: unknown;
console.log = (...args: unknown[]) => {
logLines.push(args.map(String).join(" "));
};
try {
createStreamController({
provider: "test-provider",
model: "test-model",
onError(event) {
internalError = event.error;
return true;
},
}).handleError(upstreamError);
} finally {
console.log = originalLog;
}
const logs = logLines.join("\n");
assert.equal(internalError, upstreamError);
assert.match(logs, /error: Upstream failed at <path>/);
assert.match(logs, /Authorization: \[REDACTED\]/);
assert.doesNotMatch(logs, new RegExp(SECRET));
assert.doesNotMatch(logs, new RegExp(API_KEY));
assert.doesNotMatch(logs, /\/srv\/omniroute\/private/);
assert.doesNotMatch(logs, /dispatcher\.ts/);
});
test("client disconnects stay outside the provider-failure callback", () => {
let providerFailureRecorded = false;
const controller = createStreamController({
onError() {
providerFailureRecorded = true;
return true;
},
});
controller.handleError(new DOMException("request_signal_aborted", "AbortError"));
assert.equal(providerFailureRecorded, false);
assert.equal(controller.signal.aborted, false);
});

View File

@@ -1,592 +0,0 @@
import assert from "node:assert/strict";
import { isAbsolute, relative } from "node:path";
import { after, test } from "node:test";
function requiredEnv(name: string): string {
const value = process.env[name];
assert.ok(value, `${name} must be supplied by the isolated parent wrapper`);
return value;
}
const testRoot = requiredEnv("OMNIROUTE_HUGGINGCHAT_TEST_ROOT");
const fixtureRunId = requiredEnv("OMNIROUTE_HUGGINGCHAT_TEST_RUN_ID");
const testDataDir = requiredEnv("DATA_DIR");
const testPluginsDir = requiredEnv("OMNIROUTE_PLUGINS_DIR");
const xdgConfigDir = requiredEnv("XDG_CONFIG_HOME");
for (const [name, candidate] of [
["DATA_DIR", testDataDir],
["OMNIROUTE_PLUGINS_DIR", testPluginsDir],
["XDG_CONFIG_HOME", xdgConfigDir],
] as const) {
const fromRoot = relative(testRoot, candidate);
assert.equal(
isAbsolute(fromRoot) || fromRoot.startsWith(".."),
false,
`${name} escaped test root`
);
}
assert.equal(
process.env.NODE_TEST_CONTEXT,
undefined,
"nested node:test state must not be inherited"
);
assert.equal(process.env.HOME, undefined, "the child must not inherit the operator HOME");
assert.equal(process.env.CODEX_HOME, undefined, "the child must not inherit CODEX_HOME");
assert.match(requiredEnv("API_KEY_SECRET"), /^[0-9a-f]{64}$/);
const [
{ HuggingChatExecutor },
{ HuggingChatStreamError, streamJsonlToOpenAi },
{ createPassthroughStreamWithLogger },
{ createStreamController, pipeWithDisconnect },
{ createStreamFailureFinalizers, finalizeStreamRequestLog },
{ ensureStreamReadiness },
{ FORMATS },
usageHistory,
coreDb,
callLogs,
callLogArtifactWriter,
loggerResource,
] = await Promise.all([
import("../../../open-sse/executors/huggingchat.ts"),
import("../../../open-sse/executors/huggingchat/jsonlStream.ts"),
import("../../../open-sse/utils/stream.ts"),
import("../../../open-sse/utils/streamHandler.ts"),
import("../../../open-sse/utils/streamFailureFinalization.ts"),
import("../../../open-sse/utils/streamReadiness.ts"),
import("../../../open-sse/translator/formats.ts"),
import("../../../src/lib/usage/usageHistory.ts"),
import("../../../src/lib/db/core.ts"),
import("../../../src/lib/usage/callLogs.ts"),
import("../../../src/lib/usage/callLogArtifactWriter.ts"),
import("../../../src/shared/utils/loggerResource.ts"),
]);
after(async () => {
assert.equal(
await callLogs.waitForCallLogSaves(10_000),
true,
"all asynchronous call-log writes must drain before DB teardown"
);
await callLogArtifactWriter.closeCallLogArtifactWriter();
usageHistory.clearPendingRequests();
await loggerResource.closeSharedLoggerResource();
coreDb.resetDbInstance();
});
function jsonlBody(lines: string[], trailingNewline = true): ReadableStream<Uint8Array> {
const encoded = new TextEncoder().encode(`${lines.join("\n")}${trailingNewline ? "\n" : ""}`);
return new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(encoded);
controller.close();
},
});
}
async function collectStream(body: ReadableStream<Uint8Array>): Promise<string> {
const chunks: string[] = [];
for await (const chunk of streamJsonlToOpenAi(
body,
"test/huggingchat-model",
"chatcmpl-huggingchat-test",
1_725_000_000
)) {
chunks.push(chunk);
}
return chunks.join("");
}
test("HuggingChat turns a pre-content JSONL generation error into a sanitized 502", async () => {
const rawError =
"generation failed at /srv/omniroute/providers/huggingchat.ts:44:9 api_key=super-secret\n" +
" at provider (/srv/omniroute/runtime.ts:1:1)";
const realFetch = globalThis.fetch;
let callCount = 0;
const errorLogs: string[] = [];
globalThis.fetch = (async () => {
callCount += 1;
if (callCount === 1) {
return Response.json({ conversationId: "conversation-test" });
}
if (callCount === 2) {
return Response.json({ rootMessageId: "root-message-test" });
}
if (callCount === 3) {
return new Response(
jsonlBody(
[
JSON.stringify({ type: "status", status: "started" }),
JSON.stringify({ type: "status", status: "error", message: rawError }),
],
false
),
{ status: 200, headers: { "Content-Type": "application/jsonl" } }
);
}
throw new Error(`Unexpected fetch call ${callCount}`);
}) as typeof globalThis.fetch;
try {
const result = await new HuggingChatExecutor().execute({
model: "test/huggingchat-model",
body: { messages: [{ role: "user", content: "hello" }] },
stream: true,
credentials: { apiKey: "hf-chat=fake-cookie" },
signal: null,
log: { error: (_tag, message) => errorLogs.push(message) },
});
assert.equal(callCount, 3, "the test must intercept every HuggingChat request");
assert.equal(result.response.status, 502);
assert.match(result.response.headers.get("content-type") || "", /application\/json/);
const payload = (await result.response.json()) as {
error: { message: string; type?: string; code?: string };
};
assert.equal(payload.error.type, "upstream_error");
assert.equal(payload.error.code, "huggingchat_generation_error");
assert.match(payload.error.message, /generation failed/);
assert.doesNotMatch(payload.error.message, /\/srv\/omniroute/);
assert.doesNotMatch(payload.error.message, /super-secret/);
assert.doesNotMatch(payload.error.message, /\n\s*at /);
assert.equal(errorLogs.length, 1);
assert.doesNotMatch(errorLogs[0], /\/srv\/omniroute/);
assert.doesNotMatch(errorLogs[0], /super-secret/);
assert.doesNotMatch(errorLogs[0], /\n\s*at /);
} finally {
globalThis.fetch = realFetch;
}
});
test("HuggingChat turns a terminal non-stream JSONL error into a sanitized 502", async () => {
const rawError =
"generation failed at /srv/omniroute/providers/huggingchat.ts:55:2 cookie=super-secret\n" +
" at provider (/srv/omniroute/runtime.ts:1:1)";
const realFetch = globalThis.fetch;
let callCount = 0;
globalThis.fetch = (async () => {
callCount += 1;
if (callCount === 1) return Response.json({ conversationId: "conversation-test" });
if (callCount === 2) return Response.json({ rootMessageId: "root-message-test" });
if (callCount === 3) {
return new Response(
jsonlBody([JSON.stringify({ type: "status", status: "error", message: rawError })], false),
{ status: 200, headers: { "Content-Type": "application/jsonl" } }
);
}
throw new Error(`Unexpected fetch call ${callCount}`);
}) as typeof globalThis.fetch;
try {
const result = await new HuggingChatExecutor().execute({
model: "test/huggingchat-model",
body: { messages: [{ role: "user", content: "hello" }] },
stream: false,
credentials: { apiKey: "hf-chat=fake-cookie" },
signal: null,
});
assert.equal(callCount, 3, "the test must intercept every HuggingChat request");
assert.equal(result.response.status, 502);
const payload = (await result.response.json()) as {
error: { message: string; type?: string; code?: string };
};
assert.equal(payload.error.type, "upstream_error");
assert.equal(payload.error.code, "huggingchat_generation_error");
assert.doesNotMatch(payload.error.message, /\/srv\/omniroute/);
assert.doesNotMatch(payload.error.message, /super-secret/);
assert.doesNotMatch(payload.error.message, /\n\s*at /);
} finally {
globalThis.fetch = realFetch;
}
});
test("HuggingChat rejects its JSONL generator after partial content instead of faking success", async () => {
const rawError =
"generation failed at /srv/omniroute/providers/huggingchat.ts:44:9 access_token=super-secret\n" +
" at provider (/srv/omniroute/runtime.ts:1:1)";
const stream = streamJsonlToOpenAi(
jsonlBody([
JSON.stringify({ type: "stream", token: "partial answer" }),
JSON.stringify({ type: "status", status: "error", message: rawError }),
]),
"test/huggingchat-model",
"chatcmpl-huggingchat-test",
1_725_000_000
);
const roleChunk = await stream.next();
const contentChunk = await stream.next();
assert.equal(roleChunk.done, false);
assert.match(roleChunk.value || "", /"role":"assistant"/);
assert.equal(contentChunk.done, false);
assert.match(contentChunk.value || "", /partial answer/);
await assert.rejects(() => stream.next(), HuggingChatStreamError);
});
test("HuggingChat partial failures reach stream finalization, persistence, and fallback", async () => {
const model = `test/huggingchat-model-${fixtureRunId}`;
const provider = "huggingchat";
const connectionId = `huggingchat-stream-error-boundary-${fixtureRunId}`;
const publicErrorMessage = "HuggingChat generation failed";
const rawError =
"generation failed at /srv/omniroute/providers/huggingchat.ts:44:9 access_token=super-secret\n" +
" at provider (/srv/omniroute/runtime.ts:1:1)";
const realFetch = globalThis.fetch;
let callCount = 0;
const errorLogs: string[] = [];
globalThis.fetch = (async () => {
callCount += 1;
if (callCount === 1) return Response.json({ conversationId: "conversation-test" });
if (callCount === 2) return Response.json({ rootMessageId: "root-message-test" });
if (callCount === 3) {
return new Response(
jsonlBody([
JSON.stringify({ type: "stream", token: "partial answer" }),
JSON.stringify({ type: "status", status: "error", message: rawError }),
]),
{ status: 200, headers: { "Content-Type": "application/jsonl" } }
);
}
throw new Error(`Unexpected fetch call ${callCount}`);
}) as typeof globalThis.fetch;
usageHistory.clearPendingRequests();
assert.equal(usageHistory.getPendingById().size, 0, "the child must start without pending state");
assert.equal(
usageHistory.getCompletedDetails().size,
0,
"the child must start without completed state"
);
const previousPersistence = coreDb
.getDbInstance()
.prepare("SELECT COUNT(*) AS count FROM call_logs WHERE connection_id = ? AND model = ?")
.get(connectionId, model) as { count: number };
assert.equal(previousPersistence.count, 0, "the child must not reuse a prior persisted identity");
const requestId = usageHistory.trackPendingRequest(model, provider, connectionId, true);
assert.ok(requestId, "the full-pipeline test must own a real pending request");
type CompletionPayload = {
status: number;
usage: unknown;
providerPayload?: unknown;
clientPayload?: unknown;
error?: string | null;
errorCode?: string | null;
};
type FailurePayload = {
status: number;
message: string;
code?: string;
type?: string;
};
let completionPayload: CompletionPayload | null = null;
let streamCompletionRecorded = false;
let streamFailureCompletionRecorded = false;
const persistedFailures: Array<{ status: number; errorCode?: string }> = [];
const fallbackFailures: FailurePayload[] = [];
const onStreamComplete = (payload: CompletionPayload) => {
const normalizedStatus = payload.status || 200;
if (streamCompletionRecorded) return;
streamCompletionRecorded = true;
if (normalizedStatus !== 200) {
if (streamFailureCompletionRecorded) return;
streamFailureCompletionRecorded = true;
}
completionPayload = payload;
finalizeStreamRequestLog({
pendingRequestId: requestId,
model,
provider,
connectionId,
providerResponse: payload.providerPayload,
clientResponse: payload.clientPayload,
status: normalizedStatus,
error: payload.error,
errorCode: payload.errorCode,
});
};
const { handleStreamFailure, onPipelineStreamError } = createStreamFailureFinalizers({
isFailureCompletionRecorded: () => streamFailureCompletionRecorded,
isStreamCompletionRecorded: () => streamCompletionRecorded,
onStreamComplete,
persistFailureUsage: (status, errorCode) => {
persistedFailures.push({ status, errorCode });
},
onStreamFailure: (failure) => {
fallbackFailures.push(failure);
},
});
try {
const result = await new HuggingChatExecutor().execute({
model,
body: { messages: [{ role: "user", content: "hello" }] },
stream: true,
credentials: { apiKey: "hf-chat=fake-cookie" },
signal: null,
log: { error: (_tag, message) => errorLogs.push(message) },
});
assert.equal(callCount, 3, "the test must intercept every HuggingChat request");
assert.equal(result.response.status, 200, "partial output has already committed HTTP 200");
const readiness = await ensureStreamReadiness(result.response, {
timeoutMs: 1_000,
provider,
model,
});
if (!readiness.ok) assert.fail(`unexpected readiness failure: ${readiness.reason}`);
const transform = createPassthroughStreamWithLogger(
provider,
null,
null,
model,
connectionId,
{ messages: [{ role: "user", content: "hello" }] },
onStreamComplete,
null,
handleStreamFailure,
FORMATS.OPENAI
);
const streamController = createStreamController({
onError: onPipelineStreamError,
provider,
model,
connectionId,
clientResponseFormat: FORMATS.OPENAI,
});
const clientStream = pipeWithDisconnect(readiness.response, transform, streamController, {
stallTimeoutMs: 0,
});
const wire = await new Response(clientStream).text();
assert.match(wire, /partial answer/);
assert.match(wire, /"finish_reason":"error"/);
assert.match(wire, new RegExp(publicErrorMessage));
assert.match(wire, /data: \[DONE\]/);
assert.doesNotMatch(wire, /"finish_reason":"stop"/);
assert.doesNotMatch(wire, /\/srv\/omniroute/);
assert.doesNotMatch(wire, /super-secret/);
assert.equal(errorLogs.length, 1);
assert.doesNotMatch(errorLogs[0], /\/srv\/omniroute/);
assert.doesNotMatch(errorLogs[0], /super-secret/);
assert.doesNotMatch(errorLogs[0], /\n\s*at /);
assert.ok(completionPayload, "the pipeline must record a terminal failure");
assert.equal(completionPayload.status, 502);
assert.equal(completionPayload.error, publicErrorMessage);
assert.equal(completionPayload.errorCode, "stream_pipeline_error");
assert.deepEqual(persistedFailures, [{ status: 502, errorCode: "stream_pipeline_error" }]);
assert.deepEqual(fallbackFailures, [
{
status: 502,
message: publicErrorMessage,
code: "stream_pipeline_error",
type: "stream_error",
},
]);
assert.equal(usageHistory.getPendingById().has(requestId), false);
const completedDetail = usageHistory.getCompletedDetails().get(requestId);
assert.ok(completedDetail, "failure finalization must persist the completed request detail");
assert.equal(completedDetail.status, 502);
assert.equal(completedDetail.error, publicErrorMessage);
assert.equal(completedDetail.errorCode, "stream_pipeline_error");
assert.doesNotMatch(JSON.stringify(completedDetail), /\/srv\/omniroute|super-secret/);
assert.deepEqual(
[...usageHistory.getCompletedDetails().keys()],
[requestId],
"only this child run may own completed usage state"
);
} finally {
globalThis.fetch = realFetch;
usageHistory.clearPendingRequests();
}
});
test("HuggingChat reports an authoritative error without waiting for transport cancellation", async () => {
let cancelCalled = false;
const encoded = new TextEncoder().encode(
`${JSON.stringify({ type: "status", status: "error", message: "provider failed" })}\n`
);
const body = new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(encoded);
},
cancel() {
cancelCalled = true;
return new Promise<void>(() => undefined);
},
});
const stream = streamJsonlToOpenAi(
body,
"test/huggingchat-model",
"chatcmpl-huggingchat-test",
1_725_000_000
);
const outcome = await Promise.race([
stream.next().then(
() => ({ kind: "resolved" as const }),
(error: unknown) => ({ kind: "rejected" as const, error })
),
new Promise<{ kind: "hung" }>((resolve) => {
setImmediate(() => resolve({ kind: "hung" }));
}),
]);
assert.equal(cancelCalled, true);
assert.equal(outcome.kind, "rejected", "transport cleanup must not delay error delivery");
assert.ok(
outcome.kind === "rejected" && outcome.error instanceof HuggingChatStreamError,
"the authoritative HuggingChat error must remain classifiable"
);
});
test("HuggingChat cancellation suppresses final chunks after a pending JSONL read", async () => {
let upstreamCancelCalled = false;
let upstreamPullCount = 0;
const cancellationController = new AbortController();
const token = new TextEncoder().encode(
`${JSON.stringify({ type: "stream", token: "partial answer" })}\n`
);
const body = new ReadableStream<Uint8Array>({
pull(controller) {
upstreamPullCount += 1;
if (upstreamPullCount === 1) {
controller.enqueue(token);
return;
}
return new Promise<void>(() => undefined);
},
cancel() {
upstreamCancelCalled = true;
return new Promise<void>(() => undefined);
},
});
const stream = streamJsonlToOpenAi(
body,
"test/huggingchat-model",
"chatcmpl-huggingchat-test",
1_725_000_000,
null,
cancellationController.signal
);
assert.match((await stream.next()).value || "", /"role":"assistant"/);
assert.match((await stream.next()).value || "", /partial answer/);
const pendingNext = stream.next();
await new Promise<void>((resolve) => setImmediate(resolve));
cancellationController.abort();
const outcome = await Promise.race([
pendingNext.then((result) => ({ kind: "settled" as const, result })),
new Promise<{ kind: "hung" }>((resolve) => setImmediate(() => resolve({ kind: "hung" }))),
]);
assert.equal(upstreamCancelCalled, true);
assert.equal(outcome.kind, "settled", "cancellation must settle the pending generator read");
assert.equal(
outcome.kind === "settled" ? outcome.result.done : false,
true,
"a cancelled generator must not emit stop or [DONE]"
);
void stream.return(undefined).catch(() => undefined);
});
test("HuggingChat client cancellation reaches a blocked upstream reader without waiting", async () => {
const realFetch = globalThis.fetch;
let callCount = 0;
let upstreamCancelCalled = false;
let upstreamPullCount = 0;
const errorLogs: string[] = [];
const token = new TextEncoder().encode(
`${JSON.stringify({ type: "stream", token: "partial answer" })}\n`
);
const blockedBody = new ReadableStream<Uint8Array>({
pull(controller) {
upstreamPullCount += 1;
if (upstreamPullCount === 1) {
controller.enqueue(token);
return;
}
return new Promise<void>(() => undefined);
},
cancel() {
upstreamCancelCalled = true;
return new Promise<void>(() => undefined);
},
});
globalThis.fetch = (async () => {
callCount += 1;
if (callCount === 1) return Response.json({ conversationId: "conversation-test" });
if (callCount === 2) return Response.json({ rootMessageId: "root-message-test" });
if (callCount === 3) {
return new Response(blockedBody, {
status: 200,
headers: { "Content-Type": "application/jsonl" },
});
}
throw new Error(`Unexpected fetch call ${callCount}`);
}) as typeof globalThis.fetch;
try {
const result = await new HuggingChatExecutor().execute({
model: "test/huggingchat-model",
body: { messages: [{ role: "user", content: "hello" }] },
stream: true,
credentials: { apiKey: "hf-chat=fake-cookie" },
signal: null,
log: { error: (_tag, message) => errorLogs.push(message) },
});
assert.equal(callCount, 3, "the test must intercept every HuggingChat request");
assert.ok(result.response.body);
const reader = result.response.body.getReader();
const roleChunk = await reader.read();
const contentChunk = await reader.read();
assert.match(new TextDecoder().decode(roleChunk.value), /"role":"assistant"/);
assert.match(new TextDecoder().decode(contentChunk.value), /partial answer/);
const blockedRead = reader.read();
await new Promise<void>((resolve) => setImmediate(resolve));
const cancelOutcome = await Promise.race([
reader.cancel("client disconnected").then(() => "resolved" as const),
new Promise<"hung">((resolve) => setImmediate(() => resolve("hung"))),
]);
void blockedRead.catch(() => undefined);
assert.equal(cancelOutcome, "resolved", "downstream cancellation must remain non-blocking");
assert.equal(upstreamCancelCalled, true, "cancellation must reach the locked upstream reader");
await new Promise<void>((resolve) => setImmediate(resolve));
assert.deepEqual(errorLogs, [], "client cancellation must not log a provider stream failure");
} finally {
globalThis.fetch = realFetch;
}
});
test("HuggingChat keeps the normal JSONL completion contract unchanged", async () => {
const output = await collectStream(
jsonlBody([
JSON.stringify({ type: "stream", token: "complete answer" }),
JSON.stringify({ type: "status", status: "finished" }),
])
);
assert.match(output, /"role":"assistant"/);
assert.match(output, /complete answer/);
assert.match(output, /"finish_reason":"stop"/);
assert.match(output, /data: \[DONE\]/);
assert.doesNotMatch(output, /"error":\{/);
});

View File

@@ -95,10 +95,14 @@ test("handleChat names the shadowed custom node when the built-in prefix has no
/prefix "of" is reserved by the built-in provider "openference"/,
`runtime error must explain that the prefix resolved to the built-in, got: ${message}`
);
assert.match(
message,
new RegExp(`"${SHADOWED_NODE_NAME.replace(/[()]/g, "\\$&")}" \\(${SHADOWED_NODE_ID}\\)`),
`runtime error must name the shadowed node and its id, got: ${message}`
// Exact substring, not a hand-escaped RegExp: the name carries regex
// metacharacters (parentheses) and the previous `.replace(/[()]/g, …)` escaped
// only those, so any other metachar in a future name would have been
// interpreted instead of matched literally (CodeQL js/incomplete-sanitization).
const expectedNodeMention = `"${SHADOWED_NODE_NAME}" (${SHADOWED_NODE_ID})`;
assert.ok(
message.includes(expectedNodeMention),
`runtime error must name the shadowed node and its id (${expectedNodeMention}), got: ${message}`
);
assert.match(message, /Rename that node's prefix/);
});

View File

@@ -0,0 +1,31 @@
/**
* Strict recognizer for the UC (uncensored.com) Clerk session-token mint call,
* shared by the uc-image / uc-video mock `fetch` routers.
*
* The mock routers used to dispatch on `url.includes("clerk.uncensored.com")`.
* That is a substring test over a whole URL, so ANY host answers as long as the
* name appears somewhere in it — `https://evil.example/?next=clerk.uncensored.com`
* would have been served the mint response. A test whose router accepts a
* malformed URL cannot fail when the executor builds one, which is exactly the
* regression such a test exists to catch (and CodeQL flags it as
* `js/incomplete-url-substring-sanitization`).
*
* This matches the real shape instead:
* POST https://clerk.uncensored.com/v1/client/sessions/{sid}/tokens?_clerk_js_version=…
* comparing the parsed origin against the production constant and pinning the
* path shape.
*/
import { UC_CLERK_FAPI } from "../../../open-sse/executors/uc/constants.ts";
const MINT_PATH = /^\/v1\/client\/sessions\/[^/]+\/tokens$/;
/** True only for the Clerk mint endpoint on the real Clerk FAPI origin. */
export function isUcClerkMintUrl(raw: unknown): boolean {
let parsed: URL;
try {
parsed = new URL(String(raw));
} catch {
return false;
}
return parsed.origin === UC_CLERK_FAPI && MINT_PATH.test(parsed.pathname);
}

View File

@@ -1,85 +0,0 @@
import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { basename, join } from "node:path";
import { fileURLToPath } from "node:url";
import test from "node:test";
const REPO_ROOT = fileURLToPath(new URL("../..", import.meta.url));
const FIXTURE = fileURLToPath(
new URL("./_fixtures/huggingchat-stream-error-boundary.fixture.ts", import.meta.url)
);
const SYNTHETIC_API_KEY_SECRET = "0".repeat(64);
type ChildResult = {
code: number | null;
signal: NodeJS.Signals | null;
stdout: string;
stderr: string;
};
function runFixture(testRoot: string): Promise<ChildResult> {
const dataDir = join(testRoot, "data");
const pluginsDir = join(testRoot, "plugins");
// Keep config fallbacks inside the fixture root without inheriting or repurposing HOME.
const xdgConfigDir = join(testRoot, "xdg-config");
for (const dir of [dataDir, pluginsDir, xdgConfigDir]) {
mkdirSync(dir, { recursive: true });
}
const childEnv: NodeJS.ProcessEnv = {
API_KEY_SECRET: SYNTHETIC_API_KEY_SECRET,
APP_LOG_TO_FILE: "false",
DATA_DIR: dataDir,
DISABLE_SQLITE_AUTO_BACKUP: "true",
FORCE_COLOR: "0",
LANG: "C.UTF-8",
NODE_ENV: "test",
OMNIROUTE_HUGGINGCHAT_TEST_ROOT: testRoot,
OMNIROUTE_HUGGINGCHAT_TEST_RUN_ID: basename(testRoot),
OMNIROUTE_PLUGINS_DIR: pluginsDir,
TZ: "UTC",
XDG_CONFIG_HOME: xdgConfigDir,
};
if (process.env.PATH) childEnv.PATH = process.env.PATH;
return new Promise((resolve, reject) => {
const child = spawn(process.execPath, ["--import", "tsx/esm", FIXTURE], {
cwd: REPO_ROOT,
env: childEnv,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.setEncoding("utf8").on("data", (chunk) => (stdout += chunk));
child.stderr.setEncoding("utf8").on("data", (chunk) => (stderr += chunk));
child.once("error", reject);
child.once("close", (code, signal) => resolve({ code, signal, stdout, stderr }));
});
}
function childDiagnostics(result: ChildResult): string {
return [
`exit=${String(result.code)} signal=${String(result.signal)}`,
"--- stdout ---",
result.stdout,
"--- stderr ---",
result.stderr,
].join("\n");
}
test("HuggingChat stream error boundaries stay isolated from shared DB and usage state", async () => {
const testRoot = mkdtempSync(join(tmpdir(), "omniroute-huggingchat-boundary-child-"));
try {
const result = await runFixture(testRoot);
assert.equal(result.signal, null, childDiagnostics(result));
assert.equal(result.code, 0, childDiagnostics(result));
assert.match(result.stdout, /(?:#|) pass 8\b/, childDiagnostics(result));
assert.match(result.stdout, /(?:#|) fail 0\b/, childDiagnostics(result));
assert.doesNotMatch(result.stdout + result.stderr, /super-secret/);
} finally {
rmSync(testRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});

View File

@@ -9,6 +9,7 @@ import {
} from "../../open-sse/handlers/imageGeneration/providers/maxaiImage.ts";
import { IMAGE_PROVIDERS } from "../../open-sse/config/imageRegistry.ts";
import { __setMaxaiConstantsForTest } from "../../open-sse/executors/maxai/constantsStore.ts";
import { MAXAI_BASE_URL } from "../../open-sse/executors/maxai/protocol.ts";
import { MOCK_CONSTANTS } from "./helpers/maxaiMockConstants.ts";
// Image generation signs like any request; seed the in-process constants memo
@@ -28,7 +29,9 @@ const CRED = {
// --- Registry ------------------------------------------------------------
test("maxai is registered in IMAGE_PROVIDERS with the maxai-image format + 6 models", () => {
const entry = (IMAGE_PROVIDERS as Record<string, { format?: string; baseUrl?: string; models?: unknown[] }>)["maxai"];
const entry = (
IMAGE_PROVIDERS as Record<string, { format?: string; baseUrl?: string; models?: unknown[] }>
)["maxai"];
assert.ok(entry, "maxai must exist in IMAGE_PROVIDERS");
assert.equal(entry.format, "maxai-image");
assert.match(String(entry.baseUrl), /api\.maxai\.me\/gpt\/get_image_generate_response/);
@@ -93,7 +96,10 @@ test("handleMaxaiImageGeneration returns OpenAI image data on success", async ()
ok: true,
status: 200,
async json() {
return { status: "OK", data: [{ png_url: "https://cdn/x.png", webp_url: "https://cdn/x.webp" }] };
return {
status: "OK",
data: [{ png_url: "https://cdn/x.png", webp_url: "https://cdn/x.webp" }],
};
},
async text() {
return "";
@@ -111,8 +117,12 @@ test("handleMaxaiImageGeneration returns OpenAI image data on success", async ()
assert.equal(result.success, true);
assert.deepEqual(result.data?.data, [{ url: "https://cdn/x.png" }]);
// Hit the image endpoint with the signed body.
assert.match(capturedUrl, new RegExp(MAXAI_IMAGE_PATH.replace(/\//g, "\\/")));
// Hit the image endpoint with the signed body. Exact URL equality instead of a
// hand-escaped RegExp over the path — the old `.replace(/\//g, "\\/")` escaped
// only slashes (which need no escaping in a RegExp anyway) and would have let
// any other metacharacter through (CodeQL js/incomplete-sanitization), while
// also accepting the path appearing anywhere in a wrong URL.
assert.equal(capturedUrl, MAXAI_BASE_URL + MAXAI_IMAGE_PATH);
assert.equal(capturedBody.model_name, "flux-1-schnell");
assert.equal(capturedBody.size, "512x512"); // flux passes size through
assert.equal(capturedBody.n, 2);

View File

@@ -12,6 +12,7 @@ import {
computeMaxaiProof,
maxaiAesEncrypt,
buildMaxaiSignedHeaders,
maxaiRandomSlot,
} from "../../open-sse/executors/maxai/signing.ts";
import {
assembleMaxaiContext,
@@ -103,7 +104,13 @@ test("computeMaxaiProof blanks the user id only on /oauth/* routes", () => {
// A blank-user route yields a different proof than the same route with a uid,
// proving the uid is dropped for /oauth/* (and only there).
const t = 1784594159681;
const oauthWithUid = computeMaxaiProof("/oauth/signin_with_email", t, USER_ID, HMAC_KEY, APP_VERSION);
const oauthWithUid = computeMaxaiProof(
"/oauth/signin_with_email",
t,
USER_ID,
HMAC_KEY,
APP_VERSION
);
const oauthNoUid = computeMaxaiProof("/oauth/signin_with_email", t, "", HMAC_KEY, APP_VERSION);
assert.equal(oauthWithUid, oauthNoUid); // uid ignored for /oauth/*
const chatWithUid = computeMaxaiProof("/gpt/cwc/chat", t, USER_ID, HMAC_KEY, APP_VERSION);
@@ -306,7 +313,28 @@ test("buildMaxaiSignedHeaders emits the X-App/X-Browser companions + X-Authoriza
assert.equal(h["X-App-Version"], MOCK_APP_VERSION);
assert.equal(h["X-App-Env"], "MaxAI-Browser-Extension");
assert.ok(h["X-Authorization"].length > 0);
assert.equal(Buffer.from(h["X-Authorization"], "base64").subarray(0, 8).toString("ascii"), "Salted__");
assert.equal(
Buffer.from(h["X-Authorization"], "base64").subarray(0, 8).toString("ascii"),
"Salted__"
);
});
test("maxaiRandomSlot emits an unbiased 6-digit X-Random slot", () => {
// The wire slot is always exactly 6 decimal digits, i.e. 100000-999999.
const samples = Array.from({ length: 4000 }, () => maxaiRandomSlot());
for (const s of samples) {
assert.match(s, /^\d{6}$/, `X-Random must be 6 digits, got: ${s}`);
const n = Number(s);
assert.ok(n >= 100000 && n <= 999999, `X-Random out of range: ${s}`);
}
// Regression guard for the modulo bias the previous
// `randomBytes(4).readUInt32BE(0) % 900000` draw introduced: the value must
// still spread across the whole range, not collapse onto its low end.
assert.ok(new Set(samples).size > samples.length * 0.9, "X-Random must not repeat heavily");
assert.ok(
samples.some((s) => Number(s) < 550000) && samples.some((s) => Number(s) >= 550000),
"X-Random must cover both halves of the 100000-999999 range"
);
});
// ── Context assembly ─────────────────────────────────────────────────────────
@@ -364,7 +392,12 @@ test("contentToText flattens multipart content, dropping non-text parts", () =>
});
test("buildMaxaiChatBody pins field order + constants", () => {
const body = buildMaxaiChatBody({ conversationId: "conv-1", text: "hi", modelName: "gpt-5.6", appVersion: APP_VERSION });
const body = buildMaxaiChatBody({
conversationId: "conv-1",
text: "hi",
modelName: "gpt-5.6",
appVersion: APP_VERSION,
});
const keys = Object.keys(body);
assert.equal(keys[0], "chat_mode");
assert.equal(keys[3], "message_content");
@@ -379,7 +412,12 @@ test("buildMaxaiChatBody pins field order + constants", () => {
// ── Vision input (image_url parts) ───────────────────────────────────────────
test("buildMaxaiChatBody text-only path is unchanged (no imageUrls)", () => {
const body = buildMaxaiChatBody({ conversationId: "c", text: "hi", modelName: "gpt-5.6", appVersion: APP_VERSION });
const body = buildMaxaiChatBody({
conversationId: "c",
text: "hi",
modelName: "gpt-5.6",
appVersion: APP_VERSION,
});
// Byte-identical to the pre-vision shape: a single text part.
assert.deepEqual(body.message_content, [{ type: "text", text: "hi" }]);
assert.deepEqual(body.doc_list, []);
@@ -563,8 +601,7 @@ test("maxaiRefreshAccessToken sends the exact web-app request + parses data.acce
test("maxaiRefreshAccessToken returns a structured error on non-200 (no throw)", async () => {
const nowSec = Math.floor(Date.now() / 1000);
const fakeFetch = (async () =>
new Response("nope", { status: 418 })) as unknown as typeof fetch;
const fakeFetch = (async () => new Response("nope", { status: 418 })) as unknown as typeof fetch;
const result = await maxaiRefreshAccessToken({
refreshToken: fakeJwt(nowSec + 1000, USER_ID),
deviceId: "dev",
@@ -687,7 +724,9 @@ test("verifyMaxaiEmailCode maps code 10119 to an expired-code message", async ()
test("verifyMaxaiEmailCode defaults to an invalid-code message otherwise", async () => {
const fakeFetch = (async () =>
new Response(JSON.stringify({ data: { status: "FAIL" } }), { status: 200 })) as unknown as typeof fetch;
new Response(JSON.stringify({ data: { status: "FAIL" } }), {
status: 200,
})) as unknown as typeof fetch;
const r = await verifyMaxaiEmailCode({
email: "x@y.z",
code: "999999",
@@ -1009,10 +1048,9 @@ test("discoverMaxaiModels drops deprecated, non-chat, and non-curated models", a
test("discoverMaxaiModels falls back to the catalog window when max_tokens is absent", async () => {
const fakeFetch = (async () =>
new Response(
modelsConfigBody([{ model_name: "claude-5-sonnet", type: "chat" }]),
{ status: 200 }
)) as unknown as typeof fetch;
new Response(modelsConfigBody([{ model_name: "claude-5-sonnet", type: "chat" }]), {
status: 200,
})) as unknown as typeof fetch;
const { models } = await discoverMaxaiModels({
providerSpecificData: DISCOVERY_CRED.providerSpecificData,
accessToken: DISCOVERY_CRED.accessToken,

View File

@@ -0,0 +1,62 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import test from "node:test";
const REPO_ROOT = fileURLToPath(new URL("../..", import.meta.url));
const FIXTURE = fileURLToPath(
new URL("../fixtures/stream-handler-public-error-boundary.fixture.ts", import.meta.url)
);
const CHILD_RUNTIME_ENV_KEYS = [
"PATH",
"TMPDIR",
"TMP",
"TEMP",
"SystemRoot",
"ComSpec",
"PATHEXT",
"LANG",
"LC_ALL",
"TZ",
] as const;
function buildFixtureEnv(): NodeJS.ProcessEnv {
const env: NodeJS.ProcessEnv = {
NODE_ENV: "test",
APP_LOG_TO_FILE: "false",
API_KEY_SECRET: "stream-handler-boundary-fixture-secret-20260902",
DISABLE_SQLITE_AUTO_BACKUP: "true",
NO_COLOR: "1",
};
for (const key of CHILD_RUNTIME_ENV_KEYS) {
const value = process.env[key];
if (value !== undefined) env[key] = value;
}
// Nested test runners must not inherit the parent runner's recursion marker.
delete env.NODE_TEST_CONTEXT;
return env;
}
test("generic stream public error boundaries pass in an isolated process", () => {
const result = spawnSync(
process.execPath,
["--import", "tsx/esm", "--import", "./open-sse/utils/setupPolyfill.ts", "--test", FIXTURE],
{
cwd: REPO_ROOT,
encoding: "utf8",
env: buildFixtureEnv(),
timeout: 120_000,
}
);
const output = `${result.stdout}\n${result.stderr}`;
assert.ifError(result.error);
assert.equal(result.signal, null, output.slice(-12_000));
assert.equal(result.status, 0, output.slice(-12_000));
assert.match(output, /(?:^|\s)tests\s+6(?:\s|$)/m);
assert.match(output, /(?:^|\s)pass\s+6(?:\s|$)/m);
assert.match(output, /(?:^|\s)fail\s+0(?:\s|$)/m);
});

View File

@@ -256,7 +256,8 @@ test("createDisconnectAwareStream emits Responses API failure events for Respons
assert.match(text, /event: response\.failed/);
assert.match(text, /"type":"response\.failed"/);
assert.match(text, /"message":"responses stream\\ndied"/);
assert.match(text, /"message":"responses stream"/);
assert.doesNotMatch(text, /died/);
assert.match(text, /"type":"server_error"/);
assert.match(text, /"code":"server_error"/);
assert.doesNotMatch(text, /chat\.completion\.chunk/);
@@ -264,7 +265,7 @@ test("createDisconnectAwareStream emits Responses API failure events for Respons
assert.doesNotMatch(text, /\[DONE\]/);
});
test("createDisconnectAwareStream keeps newlines escaped inside SSE data fields", async () => {
test("createDisconnectAwareStream strips multiline diagnostic tails from Responses errors", async () => {
const upstreamError = Object.assign(new Error("line one\nline two\rline three"), {
statusCode: 400,
});
@@ -290,9 +291,9 @@ test("createDisconnectAwareStream keeps newlines escaped inside SSE data fields"
const text = await readStreamText(stream);
assert.match(text, /^event: response\.failed\ndata: \{"type":"response\.failed"/);
assert.match(text, /"message":"line one\\nline two\\rline three"/);
assert.doesNotMatch(text, /^line two/m);
assert.doesNotMatch(text, /^line three/m);
assert.match(text, /"message":"line one"/);
assert.doesNotMatch(text, /line two/);
assert.doesNotMatch(text, /line three/);
});
test("createDisconnectAwareStream treats legacy OpenAI response format alias as Responses", async () => {
@@ -360,7 +361,7 @@ test("createDisconnectAwareStream emits Claude SSE errors for Claude clients", a
assert.doesNotMatch(text, /\[DONE\]/);
});
test("createDisconnectAwareStream keeps newlines escaped for Claude SSE errors", async () => {
test("createDisconnectAwareStream strips multiline diagnostic tails from Claude errors", async () => {
const upstreamError = Object.assign(new Error("claude line one\nclaude line two"), {
statusCode: 502,
});
@@ -386,8 +387,8 @@ test("createDisconnectAwareStream keeps newlines escaped for Claude SSE errors",
const text = await readStreamText(stream);
assert.match(text, /^event: error\ndata: \{"type":"error"/);
assert.match(text, /"message":"claude line one\\nclaude line two"/);
assert.doesNotMatch(text, /^claude line two/m);
assert.match(text, /"message":"claude line one"/);
assert.doesNotMatch(text, /claude line two/);
});
// #7699/#7816 — heuristic is scoped to FORMATS.CLAUDE (/v1/messages); a

View File

@@ -9,6 +9,7 @@ import {
UC_DIRECT_IMAGE_URL,
} from "../../open-sse/handlers/imageGeneration/providers/ucImage.ts";
import { IMAGE_PROVIDERS, parseImageModel } from "../../open-sse/config/imageRegistry.ts";
import { isUcClerkMintUrl } from "./helpers/ucClerkUrl.ts";
// A valid PERSONA credential (durable Clerk cookie + sid + uid in psd). No API
// key, so the handler takes the persona web path (mint -> POST -> poll).
@@ -144,7 +145,7 @@ function personaFetch(opts: {
let pollsSeen = 0;
return (async (url: string, init: RequestInit = {}) => {
// 1) Clerk mint
if (url.includes("clerk.uncensored.com")) {
if (isUcClerkMintUrl(url)) {
return {
ok: true,
status: 200,
@@ -265,7 +266,7 @@ test("handleUcImageGeneration (persona) times out with 504 when the result never
test("handleUcImageGeneration (persona) surfaces a Clerk mint failure", async () => {
const fetchImpl = (async (url: string) => {
if (url.includes("clerk.uncensored.com")) {
if (isUcClerkMintUrl(url)) {
return {
ok: false,
status: 401,

View File

@@ -13,6 +13,7 @@ import {
UC_DIRECT_VIDEO_URL,
} from "../../open-sse/handlers/videoGeneration/providers/ucVideo.ts";
import { VIDEO_PROVIDERS } from "../../open-sse/config/videoRegistry.ts";
import { isUcClerkMintUrl } from "./helpers/ucClerkUrl.ts";
// A valid PERSONA credential (durable Clerk cookie + sid + uid in psd). No API
// key, so the handler takes the persona web path (mint -> generate -> poll).
@@ -148,7 +149,7 @@ function personaFetch(opts: {
let pollsSeen = 0;
return (async (url: string, init: RequestInit = {}) => {
// Clerk mint
if (url.includes("clerk.uncensored.com")) {
if (isUcClerkMintUrl(url)) {
return {
ok: true,
status: 200,
@@ -339,7 +340,7 @@ test("handleUcVideoGeneration (persona) times out with 504 when never ready", as
test("handleUcVideoGeneration (persona) surfaces a Clerk mint failure", async () => {
const fetchImpl = (async (url: string) => {
if (url.includes("clerk.uncensored.com")) {
if (isUcClerkMintUrl(url)) {
return {
ok: false,
status: 401,