mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(api): validate route payloads for release checks
This commit is contained in:
@@ -5,6 +5,9 @@
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(mitm):** Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
|
||||
- **fix(build):** Move the local `.tmp/wine32` Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot trigger `EACCES` scans during Node 24 builds.
|
||||
- **fix(api):** Validate the Codex Responses websocket bridge and `/v1/batches` JSON payloads with Zod before use, keeping `request.json()` route validation green and returning explicit 400 responses for invalid bodies.
|
||||
- **fix(ui):** Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
|
||||
- **fix(electron):** Harden the production desktop CSP by removing `unsafe-eval` outside development and adding object, base URI, form action, frame ancestor, and worker restrictions.
|
||||
- **fix(cli):** Replace shell-interpolated setup and privileged command execution paths with argument-based `spawn`/`execFile` helpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows.
|
||||
- **fix(ui):** Keep provider icons resilient by using direct `@lobehub/icons` components first, then local PNG/SVG fallbacks, avoiding the `@lobehub/ui` peer runtime in the dashboard.
|
||||
|
||||
@@ -23,6 +23,11 @@ export function getTransientBuildPaths(rootDir = projectRoot, env = process.env)
|
||||
sourcePath: path.join(rootDir, "app"),
|
||||
backupPath: path.join(backupRoot, "app"),
|
||||
},
|
||||
{
|
||||
label: "local Wine prefix",
|
||||
sourcePath: path.join(rootDir, ".tmp", "wine32"),
|
||||
backupPath: path.join(backupRoot, "wine32"),
|
||||
},
|
||||
];
|
||||
|
||||
if (env.OMNIROUTE_BUILD_MOVE_TASKS === "1") {
|
||||
|
||||
@@ -3042,8 +3042,20 @@ export default function ProviderDetailPage() {
|
||||
<Card>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">{t("upstreamProxyManagedTitle")}</h2>
|
||||
<p className="text-sm text-text-muted mt-1">{t("upstreamProxyManagedDescription")}</p>
|
||||
<h2 className="text-lg font-semibold">
|
||||
{providerText(
|
||||
t,
|
||||
"upstreamProxyManagedTitle",
|
||||
"Managed via Upstream Proxy Settings"
|
||||
)}
|
||||
</h2>
|
||||
<p className="text-sm text-text-muted mt-1">
|
||||
{providerText(
|
||||
t,
|
||||
"upstreamProxyManagedDescription",
|
||||
"CLIProxyAPI is configured as an upstream proxy layer, not as a direct provider connection. Manage the binary/runtime in CLI Tools and enable proxy routing on each provider via the provider proxy controls."
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Link
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { NextResponse } from "next/server";
|
||||
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";
|
||||
@@ -11,6 +12,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);
|
||||
}
|
||||
@@ -168,7 +178,11 @@ export async function POST(request: Request) {
|
||||
let body: JsonRecord;
|
||||
try {
|
||||
const parsed = await request.json();
|
||||
body = isRecord(parsed) ? parsed : {};
|
||||
const validation = bridgePayloadSchema.safeParse(parsed);
|
||||
if (!validation.success) {
|
||||
return jsonError(400, "invalid_json", "Request body must be a JSON object");
|
||||
}
|
||||
body = validation.data;
|
||||
} catch {
|
||||
return jsonError(400, "invalid_json", "Request body must be JSON");
|
||||
}
|
||||
|
||||
@@ -46,7 +46,19 @@ export async function POST(request: Request) {
|
||||
|
||||
try {
|
||||
const body = await request.json();
|
||||
const validated = v1BatchCreateSchema.parse(body);
|
||||
const validation = v1BatchCreateSchema.safeParse(body);
|
||||
if (!validation.success) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: {
|
||||
message: validation.error.message,
|
||||
type: "invalid_request_error",
|
||||
},
|
||||
},
|
||||
{ status: 400, headers: CORS_HEADERS }
|
||||
);
|
||||
}
|
||||
const validated = validation.data;
|
||||
|
||||
const inputFile = getFile(validated.input_file_id);
|
||||
if (!inputFile || (inputFile.apiKeyId !== null && inputFile.apiKeyId !== apiKeyId)) {
|
||||
|
||||
@@ -108,7 +108,7 @@ test("getTransientBuildPaths leaves _tasks in place by default", () => {
|
||||
|
||||
assert.deepEqual(
|
||||
paths.map((entry) => entry.label),
|
||||
["legacy app snapshot"]
|
||||
["legacy app snapshot", "local Wine prefix"]
|
||||
);
|
||||
assert.equal(
|
||||
paths.some((entry) => entry.sourcePath === "/repo/_tasks"),
|
||||
|
||||
Reference in New Issue
Block a user