fix(security): resolve CodeQL scanning alerts for SSRF, insecure randomness and incomplete URLs

This commit is contained in:
diegosouzapw
2026-04-07 23:51:33 -03:00
parent eec2db0590
commit 6fada51fe8
5 changed files with 17 additions and 21 deletions

View File

@@ -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
View File

View 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: {

View File

@@ -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);

View File

@@ -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)) {