mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
fix(security): resolve CodeQL scanning alerts for SSRF, insecure randomness and incomplete URLs
This commit is contained in:
@@ -162,7 +162,9 @@ export function generateRequestId() {
|
||||
|
||||
// Generate session ID
|
||||
export function generateSessionId() {
|
||||
return `-${Math.floor(Math.random() * 9000000000000000000)}`;
|
||||
const bytes = crypto.randomBytes(8);
|
||||
const num = bytes.readBigUInt64LE() % 9000000000000000000n;
|
||||
return `-${num.toString()}`;
|
||||
}
|
||||
|
||||
// Helper: Remove unsupported keywords recursively from object/array
|
||||
|
||||
0
scan_packet.json
Normal file
0
scan_packet.json
Normal file
@@ -146,15 +146,9 @@ export async function POST(request: Request, { params }: { params: Promise<{ id:
|
||||
logProvider = toNonEmptyString(connection.provider) || "unknown";
|
||||
channelLabel = getModelSyncChannelLabel(connection);
|
||||
|
||||
// Fetch models from the existing /api/providers/[id]/models endpoint
|
||||
const parsedUrl = new URL(request.url);
|
||||
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
||||
return NextResponse.json({ error: "Invalid protocol" }, { status: 400 });
|
||||
}
|
||||
if (parsedUrl.hostname.includes("..")) {
|
||||
return NextResponse.json({ error: "Invalid hostname" }, { status: 400 });
|
||||
}
|
||||
const modelsUrl = `${parsedUrl.origin}/api/providers/${encodeURIComponent(id)}/models`;
|
||||
// Fetch models from the existing /api/providers/[id]/models endpoint via localhost to prevent SSRF
|
||||
const safeOrigin = `http://127.0.0.1:${process.env.PORT || 20128}`;
|
||||
const modelsUrl = `${safeOrigin}/api/providers/${encodeURIComponent(id)}/models`;
|
||||
const modelsRes = await fetch(modelsUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
|
||||
@@ -806,7 +806,7 @@ test("chatCore refreshes GitHub credentials after 401 and retries with the refre
|
||||
refreshedCredentials = updated;
|
||||
},
|
||||
responseFactory(captured, seenCalls) {
|
||||
if (captured.url.includes("api.github.com/copilot_internal/v2/token")) {
|
||||
if (captured.url.includes("https://api.github.com/copilot_internal/v2/token")) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
token: "copilot-refreshed-token",
|
||||
@@ -820,7 +820,7 @@ test("chatCore refreshes GitHub credentials after 401 and retries with the refre
|
||||
}
|
||||
|
||||
const providerCalls = seenCalls.filter((entry) =>
|
||||
entry.url.includes("api.githubcopilot.com/")
|
||||
entry.url.includes("https://api.githubcopilot.com/")
|
||||
);
|
||||
if (providerCalls.length === 1) {
|
||||
return new Response(
|
||||
@@ -839,7 +839,7 @@ test("chatCore refreshes GitHub credentials after 401 and retries with the refre
|
||||
});
|
||||
|
||||
const payload = await result.response.json();
|
||||
const providerCalls = calls.filter((entry) => entry.url.includes("api.githubcopilot.com/"));
|
||||
const providerCalls = calls.filter((entry) => entry.url.includes("https://api.githubcopilot.com/"));
|
||||
|
||||
assert.equal(result.success, true);
|
||||
assert.equal(providerCalls.length, 2);
|
||||
|
||||
@@ -88,16 +88,16 @@ test("search provider validators cover success, client errors, server errors and
|
||||
globalThis.fetch = async (url, init = {}) => {
|
||||
calls.push({ url: String(url), init });
|
||||
const target = String(url);
|
||||
if (target.match(/search.brave.com/i)) {
|
||||
if (target.match(/search\.brave\.com/i)) {
|
||||
return new Response(JSON.stringify({ results: [] }), { status: 200 });
|
||||
}
|
||||
if (target.match(/api.exa.ai/i)) {
|
||||
if (target.match(/api\.exa\.ai/i)) {
|
||||
return new Response(JSON.stringify({ error: "bad key" }), { status: 403 });
|
||||
}
|
||||
if (target.match(/api.tavily.com/i)) {
|
||||
if (target.match(/api\.tavily\.com/i)) {
|
||||
return new Response(JSON.stringify({ error: "server" }), { status: 503 });
|
||||
}
|
||||
if (target.match(/api.perplexity.ai/i)) {
|
||||
if (target.match(/api\.perplexity\.ai/i)) {
|
||||
throw new Error("perplexity offline");
|
||||
}
|
||||
throw new Error(`unexpected fetch: ${target}`);
|
||||
@@ -180,13 +180,13 @@ test("OpenAI-compatible validator covers /responses mode and final ping fallback
|
||||
test("Anthropic-compatible and Claude Code compatible validators cover direct success and bridge fallbacks", async () => {
|
||||
globalThis.fetch = async (url, init = {}) => {
|
||||
const target = String(url);
|
||||
if (target.match(/anthropic-compatible.example.com/i) && init.method === "GET") {
|
||||
if (target.match(/anthropic-compatible\.example\.com/i) && init.method === "GET") {
|
||||
return new Response(JSON.stringify({ data: [] }), { status: 200 });
|
||||
}
|
||||
if (target.match(/cc-compatible.example.com/i) && init.method === "GET") {
|
||||
if (target.match(/cc-compatible\.example\.com/i) && init.method === "GET") {
|
||||
return new Response(JSON.stringify({ error: "bridge unavailable" }), { status: 500 });
|
||||
}
|
||||
if (target.match(/cc-compatible.example.com/i) && init.method === "POST") {
|
||||
if (target.match(/cc-compatible\.example\.com/i) && init.method === "POST") {
|
||||
return new Response(JSON.stringify({ error: "rate limited" }), { status: 429 });
|
||||
}
|
||||
throw new Error(`unexpected fetch: ${target}`);
|
||||
@@ -352,7 +352,7 @@ test("specialty validators cover remaining status branches for Deepgram, Assembl
|
||||
if (target.match(/inworld/i)) {
|
||||
throw new Error("inworld offline");
|
||||
}
|
||||
if (target.match(/dashscope.aliyuncs.com/i)) {
|
||||
if (target.match(/dashscope\.aliyuncs\.com/i)) {
|
||||
return new Response(JSON.stringify({ error: "server" }), { status: 500 });
|
||||
}
|
||||
if (target.match(/longcat/i)) {
|
||||
|
||||
Reference in New Issue
Block a user