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)}`
|
||||
);
|
||||
}
|
||||
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) {
|
||||
return String(value).replace(/\/+$/, "");
|
||||
let s = String(value);
|
||||
while (s.endsWith("/")) s = s.slice(0, -1);
|
||||
return s;
|
||||
}
|
||||
|
||||
function resolveUrl(path, opts) {
|
||||
|
||||
@@ -8,7 +8,9 @@ export function generateClaudeConfig(options: {
|
||||
apiKey: string;
|
||||
model?: 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 config = {
|
||||
|
||||
@@ -8,7 +8,9 @@ export function generateClineConfig(options: {
|
||||
apiKey: string;
|
||||
model?: 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 = {
|
||||
openAiBaseUrl: `${base}/v1`,
|
||||
|
||||
@@ -17,7 +17,9 @@ export async function generateCodexConfig(options: {
|
||||
model?: string;
|
||||
}): Promise<string> {
|
||||
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 = {
|
||||
openai: {
|
||||
|
||||
@@ -17,7 +17,9 @@ export async function generateContinueConfig(options: {
|
||||
model?: string;
|
||||
}): Promise<string> {
|
||||
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 = {
|
||||
models: [
|
||||
|
||||
@@ -8,7 +8,9 @@ export function generateKilocodeConfig(options: {
|
||||
apiKey: string;
|
||||
model?: 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 = {
|
||||
apiKey: options.apiKey,
|
||||
|
||||
@@ -8,7 +8,9 @@ export function generateOpencodeConfig(options: {
|
||||
apiKey: string;
|
||||
model?: 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 = {
|
||||
provider: "omniroute",
|
||||
|
||||
@@ -25,7 +25,11 @@ if (!process.env.DEEPSEEK_WEB_SESSION_COOKIE) {
|
||||
});
|
||||
|
||||
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) {
|
||||
const ct = result.response.headers.get("content-type") || "";
|
||||
|
||||
Reference in New Issue
Block a user