mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(api): validate codex websocket bridge payload
This commit is contained in:
@@ -45,6 +45,7 @@ export interface RegistryModel {
|
||||
supportsVision?: boolean;
|
||||
supportsXHighEffort?: boolean;
|
||||
targetFormat?: string;
|
||||
strip?: readonly string[];
|
||||
unsupportedParams?: readonly string[];
|
||||
/** Maximum context window in tokens */
|
||||
contextLength?: number;
|
||||
|
||||
@@ -323,7 +323,7 @@ function fixToolPairs(messages: Record<string, unknown>[]) {
|
||||
|
||||
if (Array.isArray(newMsg.tool_calls)) {
|
||||
const filteredToolCalls = newMsg.tool_calls.filter(
|
||||
(tc: any) => !tc.id || toolResultIds.has(tc.id)
|
||||
(tc: Record<string, unknown>) => !tc.id || toolResultIds.has(tc.id)
|
||||
);
|
||||
if (filteredToolCalls.length !== newMsg.tool_calls.length) {
|
||||
newMsg.tool_calls = filteredToolCalls;
|
||||
@@ -333,7 +333,8 @@ function fixToolPairs(messages: Record<string, unknown>[]) {
|
||||
|
||||
if (Array.isArray(newMsg.content)) {
|
||||
const filteredContent = newMsg.content.filter(
|
||||
(block: any) => block.type !== "tool_use" || !block.id || toolResultIds.has(block.id)
|
||||
(block: Record<string, unknown>) =>
|
||||
block.type !== "tool_use" || !block.id || toolResultIds.has(block.id)
|
||||
);
|
||||
if (filteredContent.length !== newMsg.content.length) {
|
||||
newMsg.content = filteredContent;
|
||||
@@ -374,7 +375,7 @@ function fixToolPairs(messages: Record<string, unknown>[]) {
|
||||
|
||||
if (msg.role === "user" && Array.isArray(msg.content)) {
|
||||
const filteredContent = msg.content.filter(
|
||||
(block: any) =>
|
||||
(block: Record<string, unknown>) =>
|
||||
block.type !== "tool_result" || !block.tool_use_id || toolCallIds.has(block.tool_use_id)
|
||||
);
|
||||
if (filteredContent.length !== msg.content.length) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { createHash, timingSafeEqual } from "node:crypto";
|
||||
import { z } from "zod";
|
||||
import { CodexExecutor } from "@omniroute/open-sse/executors/codex.ts";
|
||||
import { getApiKeyMetadata } from "@/lib/db/apiKeys";
|
||||
import { authorizeWebSocketHandshake, extractWsTokenFromRequest } from "@/lib/ws/handshake";
|
||||
@@ -12,6 +13,15 @@ const executor = new CodexExecutor();
|
||||
|
||||
type JsonRecord = Record<string, unknown>;
|
||||
|
||||
const bridgePayloadSchema = z
|
||||
.object({
|
||||
action: z.string().optional(),
|
||||
requestUrl: z.string().optional(),
|
||||
headers: z.record(z.string(), z.unknown()).optional(),
|
||||
response: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
function isRecord(value: unknown): value is JsonRecord {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
@@ -179,8 +189,11 @@ export async function POST(request: Request) {
|
||||
|
||||
let body: JsonRecord;
|
||||
try {
|
||||
const parsed = await request.json();
|
||||
body = isRecord(parsed) ? parsed : {};
|
||||
const parsed = bridgePayloadSchema.safeParse(await request.json());
|
||||
if (!parsed.success) {
|
||||
return jsonError(400, "invalid_json", "Request body must be a JSON object");
|
||||
}
|
||||
body = parsed.data as JsonRecord;
|
||||
} catch {
|
||||
return jsonError(400, "invalid_json", "Request body must be JSON");
|
||||
}
|
||||
|
||||
@@ -403,3 +403,25 @@ test("Codex internal websocket bridge secret comparison handles mismatched lengt
|
||||
assert.equal(bridgeSecretMatches("bridge-secret", "bridge-secret-extra"), false);
|
||||
assert.equal(bridgeSecretMatches("bridge-secret", ""), false);
|
||||
});
|
||||
|
||||
test("Codex internal websocket bridge rejects non-object JSON payloads", async () => {
|
||||
await withEnv({ OMNIROUTE_WS_BRIDGE_SECRET: "bridge-secret" }, async () => {
|
||||
const { POST } = await import("../../src/app/api/internal/codex-responses-ws/route.ts");
|
||||
|
||||
const response = await POST(
|
||||
new Request("http://omniroute.local/api/internal/codex-responses-ws", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-omniroute-ws-bridge-secret": "bridge-secret",
|
||||
},
|
||||
body: JSON.stringify(["invalid"]),
|
||||
})
|
||||
);
|
||||
const body = await response.json();
|
||||
|
||||
assert.equal(response.status, 400);
|
||||
assert.equal(body.error.code, "invalid_json");
|
||||
assert.match(body.error.message, /JSON object/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user