mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(security): resolve CodeQL ReDoS + URL sanitization alerts
- Replace replace(/\/+$/, "") with explicit while-endsWith loop to avoid
polynomial backtracking on inputs with repeated trailing slashes
(CodeQL js/polynomial-redos #233-240, 8 alerts):
- @omniroute/opencode-provider/src/index.ts (normalizeBaseURL)
- bin/cli/api.mjs (stripTrailingSlash)
- src/lib/cli-helper/config-generator/{claude,cline,codex,continue,
kilocode,opencode}.ts (6 generators with identical pattern)
- tests/live/deepseek-web-live.test.ts: assert hostname via URL parsing
instead of String.includes() so the check is exact-match rather than
substring (CodeQL js/incomplete-url-substring-sanitization #241).
Alert #242 (Array.prototype.includes against fixed needle constant
OPENWEBUI_PARAGRAPH_ANCHORS) dismissed as CodeQL false-positive — not a
URL sanitization callsite.
This commit is contained in:
@@ -107,7 +107,10 @@ export function normalizeBaseURL(rawBaseURL: string): string {
|
|||||||
`@omniroute/opencode-provider: baseURL is not a valid URL: ${JSON.stringify(rawBaseURL)}`
|
`@omniroute/opencode-provider: baseURL is not a valid URL: ${JSON.stringify(rawBaseURL)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return trimmed.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1";
|
let base = trimmed;
|
||||||
|
while (base.endsWith("/")) base = base.slice(0, -1);
|
||||||
|
if (base.endsWith("/v1")) base = base.slice(0, -3);
|
||||||
|
return base + "/v1";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ export function getBaseUrl(opts = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stripTrailingSlash(value) {
|
function stripTrailingSlash(value) {
|
||||||
return String(value).replace(/\/+$/, "");
|
let s = String(value);
|
||||||
|
while (s.endsWith("/")) s = s.slice(0, -1);
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveUrl(path, opts) {
|
function resolveUrl(path, opts) {
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ export function generateClaudeConfig(options: {
|
|||||||
apiKey: string;
|
apiKey: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
}): string {
|
}): string {
|
||||||
const base = options.baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
|
let base = options.baseUrl;
|
||||||
|
while (base.endsWith("/")) base = base.slice(0, -1);
|
||||||
|
if (base.endsWith("/v1")) base = base.slice(0, -3);
|
||||||
const model = options.model || "claude-3-5-sonnet-20241022";
|
const model = options.model || "claude-3-5-sonnet-20241022";
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ export function generateClineConfig(options: {
|
|||||||
apiKey: string;
|
apiKey: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
}): string {
|
}): string {
|
||||||
const base = options.baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
|
let base = options.baseUrl;
|
||||||
|
while (base.endsWith("/")) base = base.slice(0, -1);
|
||||||
|
if (base.endsWith("/v1")) base = base.slice(0, -3);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
openAiBaseUrl: `${base}/v1`,
|
openAiBaseUrl: `${base}/v1`,
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ export async function generateCodexConfig(options: {
|
|||||||
model?: string;
|
model?: string;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
const y = await loadYaml();
|
const y = await loadYaml();
|
||||||
const base = options.baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
|
let base = options.baseUrl;
|
||||||
|
while (base.endsWith("/")) base = base.slice(0, -1);
|
||||||
|
if (base.endsWith("/v1")) base = base.slice(0, -3);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
openai: {
|
openai: {
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ export async function generateContinueConfig(options: {
|
|||||||
model?: string;
|
model?: string;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
const y = await loadYaml();
|
const y = await loadYaml();
|
||||||
const base = options.baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
|
let base = options.baseUrl;
|
||||||
|
while (base.endsWith("/")) base = base.slice(0, -1);
|
||||||
|
if (base.endsWith("/v1")) base = base.slice(0, -3);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
models: [
|
models: [
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ export function generateKilocodeConfig(options: {
|
|||||||
apiKey: string;
|
apiKey: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
}): string {
|
}): string {
|
||||||
const base = options.baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
|
let base = options.baseUrl;
|
||||||
|
while (base.endsWith("/")) base = base.slice(0, -1);
|
||||||
|
if (base.endsWith("/v1")) base = base.slice(0, -3);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
apiKey: options.apiKey,
|
apiKey: options.apiKey,
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ export function generateOpencodeConfig(options: {
|
|||||||
apiKey: string;
|
apiKey: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
}): string {
|
}): string {
|
||||||
const base = options.baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
|
let base = options.baseUrl;
|
||||||
|
while (base.endsWith("/")) base = base.slice(0, -1);
|
||||||
|
if (base.endsWith("/v1")) base = base.slice(0, -3);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
provider: "omniroute",
|
provider: "omniroute",
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ if (!process.env.DEEPSEEK_WEB_SESSION_COOKIE) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert.ok(result.response, "Should return a response");
|
assert.ok(result.response, "Should return a response");
|
||||||
assert.ok(result.url.includes("chat.deepseek.com"), "Should target chat.deepseek.com");
|
assert.equal(
|
||||||
|
new URL(result.url).hostname,
|
||||||
|
"chat.deepseek.com",
|
||||||
|
"Should target chat.deepseek.com"
|
||||||
|
);
|
||||||
|
|
||||||
if (result.response.ok) {
|
if (result.response.ok) {
|
||||||
const ct = result.response.headers.get("content-type") || "";
|
const ct = result.response.headers.get("content-type") || "";
|
||||||
|
|||||||
Reference in New Issue
Block a user