mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(security): resolve CodeQL polynomial regular expression (ReDoS) vulnerabilities and chatCore TS errors (#201, #200, #199)
This commit is contained in:
@@ -932,9 +932,11 @@ function isCopilotClient(
|
||||
if (isMatch(userAgent)) return true;
|
||||
|
||||
if (headers instanceof Headers) {
|
||||
for (const [key, value] of headers) {
|
||||
if (isMatch(key) || isMatch(value)) return true;
|
||||
}
|
||||
let found = false;
|
||||
headers.forEach((value, key) => {
|
||||
if (isMatch(key) || isMatch(value)) found = true;
|
||||
});
|
||||
if (found) return true;
|
||||
} else if (headers && typeof headers === "object") {
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
if (isMatch(key) || isMatch(value)) return true;
|
||||
@@ -1080,7 +1082,7 @@ export async function handleChatCore({
|
||||
| undefined)
|
||||
: undefined;
|
||||
const idempotentCost = idempotentUsage
|
||||
? await calculateCost(provider, model, idempotentUsage)
|
||||
? await calculateCost(provider, model, idempotentUsage as Record<string, number>)
|
||||
: 0;
|
||||
return {
|
||||
success: true,
|
||||
@@ -1435,7 +1437,7 @@ export async function handleChatCore({
|
||||
const cachedUsage =
|
||||
extractUsageFromResponse(cached as Record<string, unknown>, provider) ||
|
||||
((cached as Record<string, unknown>)?.usage as Record<string, unknown> | undefined);
|
||||
const cachedCost = cachedUsage ? await calculateCost(provider, model, cachedUsage) : 0;
|
||||
const cachedCost = cachedUsage ? await calculateCost(provider, model, cachedUsage as Record<string, number>) : 0;
|
||||
persistAttemptLogs({
|
||||
status: 200,
|
||||
tokens: (cached as Record<string, unknown>)?.usage,
|
||||
@@ -1754,7 +1756,7 @@ export async function handleChatCore({
|
||||
comboOverrides: {
|
||||
...(config.comboOverrides ?? {}),
|
||||
...(comboName ? { [comboName]: comboMode } : {}),
|
||||
...(comboConfig?.id ? { [comboConfig.id]: comboMode } : {}),
|
||||
...(comboConfig?.id ? { [String(comboConfig.id)]: comboMode } : {}),
|
||||
},
|
||||
};
|
||||
compressionComboKey = comboName;
|
||||
|
||||
@@ -67,7 +67,7 @@ export function compressMcpDescription(description: string): DescriptionCompress
|
||||
.replace(/[ \t]{2,}/g, " ")
|
||||
.replace(/[ \t]+([,.;:!?])/g, "$1")
|
||||
.replace(/\n{3,}/g, "\n\n")
|
||||
.replace(/(^|[.!?][ \t]+|\n+[ \t]*)([a-z])/g, (_match, prefix: string, char: string) => {
|
||||
.replace(/(^|[.!?]\s+|^\s+)([a-z])/gm, (_match, prefix: string, char: string) => {
|
||||
return `${prefix}${char.toUpperCase()}`;
|
||||
})
|
||||
.trim();
|
||||
|
||||
@@ -217,7 +217,7 @@ function cleanupArtifacts(text: string): string {
|
||||
|
||||
function recapitalizeSentences(text: string): string {
|
||||
return text.replace(
|
||||
/(^|[.!?][ \t]+|\n+[ \t]*)([a-z])/g,
|
||||
/(^|[.!?]\s+|^\s+)([a-z])/gm,
|
||||
(_match, prefix: string, char: string) => {
|
||||
return `${prefix}${char.toUpperCase()}`;
|
||||
}
|
||||
|
||||
@@ -67,13 +67,13 @@ export function validateCompression(original: string, compressed: string): Valid
|
||||
requireExactPresence("heading", collectMatches(original, /^#{1,6}\s+.+$/gm), compressed, errors);
|
||||
requireExactPresence(
|
||||
"table row",
|
||||
collectMatches(original, /^[ \t]*\|(?:[^|\n]{0,1000}\|){1,100}[ \t]*$/gm),
|
||||
collectMatches(original, /^[ \t]*\|(?:[^|\n]*\|)+[ \t]*$/gm),
|
||||
compressed,
|
||||
errors
|
||||
);
|
||||
requireExactPresence(
|
||||
"math block",
|
||||
collectMatches(original, /\$\$[\s\S]{0,10000}?\$\$/g),
|
||||
collectMatches(original, /\$\$[^$]*(?:\$(?!\$)[^$]*)*\$\$/g),
|
||||
compressed,
|
||||
errors
|
||||
);
|
||||
@@ -85,7 +85,7 @@ export function validateCompression(original: string, compressed: string): Valid
|
||||
);
|
||||
requireExactPresence(
|
||||
"LaTeX block",
|
||||
collectMatches(original, /\\begin\{[A-Za-z*]{1,50}\}[\s\S]{0,10000}?\\end\{[A-Za-z*]{1,50}\}/g),
|
||||
collectMatches(original, /\\begin\{[A-Za-z*]{1,50}\}(?:(?!\\end\{)[^])*\\end\{[A-Za-z*]{1,50}\}/g),
|
||||
compressed,
|
||||
errors
|
||||
);
|
||||
|
||||
41
scripts/scratch/analyze-issues.cjs
Normal file
41
scripts/scratch/analyze-issues.cjs
Normal file
@@ -0,0 +1,41 @@
|
||||
const fs = require('fs');
|
||||
const issues = JSON.parse(fs.readFileSync('/tmp/issues.json', 'utf8'));
|
||||
|
||||
const bugIssues = [];
|
||||
const otherIssues = [];
|
||||
|
||||
for (const issue of issues) {
|
||||
const isBug = issue.labels.some(l => l.name === 'bug') ||
|
||||
/doesn't work|broken|crash|error/i.test(issue.body);
|
||||
const isFeature = issue.labels.some(l => l.name === 'enhancement' || l.name === 'feature') || /feature/i.test(issue.body);
|
||||
const isQuestion = issue.labels.some(l => l.name === 'question') || /how to/i.test(issue.body);
|
||||
|
||||
if (isBug) {
|
||||
bugIssues.push(issue);
|
||||
} else {
|
||||
otherIssues.push({
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
type: isFeature ? 'Feature' : (isQuestion ? 'Question' : 'Other')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log("=== BUG ISSUES ===");
|
||||
for (const bug of bugIssues) {
|
||||
console.log(`\nIssue #${bug.number}: ${bug.title}`);
|
||||
console.log(`Author: ${bug.author.login} | Created: ${bug.createdAt}`);
|
||||
console.log(`Labels: ${bug.labels.map(l => l.name).join(', ')}`);
|
||||
console.log(`Comments: ${bug.comments.length}`);
|
||||
console.log(`Body Snippet: ${bug.body.substring(0, 200).replace(/\n/g, ' ')}...`);
|
||||
|
||||
if (bug.comments.length > 0) {
|
||||
const lastComment = bug.comments[bug.comments.length - 1];
|
||||
console.log(`Last Comment by ${lastComment.author.login}: ${lastComment.body.substring(0, 150).replace(/\n/g, ' ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\n=== OTHER ISSUES (SKIPPED) ===");
|
||||
for (const other of otherIssues) {
|
||||
console.log(`#${other.number} - ${other.title} (${other.type})`);
|
||||
}
|
||||
41
scripts/scratch/analyze-issues.js
Normal file
41
scripts/scratch/analyze-issues.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const fs = require('fs');
|
||||
const issues = JSON.parse(fs.readFileSync('/tmp/issues.json', 'utf8'));
|
||||
|
||||
const bugIssues = [];
|
||||
const otherIssues = [];
|
||||
|
||||
for (const issue of issues) {
|
||||
const isBug = issue.labels.some(l => l.name === 'bug') ||
|
||||
/doesn't work|broken|crash|error/i.test(issue.body);
|
||||
const isFeature = issue.labels.some(l => l.name === 'enhancement' || l.name === 'feature') || /feature/i.test(issue.body);
|
||||
const isQuestion = issue.labels.some(l => l.name === 'question') || /how to/i.test(issue.body);
|
||||
|
||||
if (isBug) {
|
||||
bugIssues.push(issue);
|
||||
} else {
|
||||
otherIssues.push({
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
type: isFeature ? 'Feature' : (isQuestion ? 'Question' : 'Other')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log("=== BUG ISSUES ===");
|
||||
for (const bug of bugIssues) {
|
||||
console.log(`\nIssue #${bug.number}: ${bug.title}`);
|
||||
console.log(`Author: ${bug.author.login} | Created: ${bug.createdAt}`);
|
||||
console.log(`Labels: ${bug.labels.map(l => l.name).join(', ')}`);
|
||||
console.log(`Comments: ${bug.comments.length}`);
|
||||
console.log(`Body Snippet: ${bug.body.substring(0, 200).replace(/\n/g, ' ')}...`);
|
||||
|
||||
if (bug.comments.length > 0) {
|
||||
const lastComment = bug.comments[bug.comments.length - 1];
|
||||
console.log(`Last Comment by ${lastComment.author.login}: ${lastComment.body.substring(0, 150).replace(/\n/g, ' ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\n=== OTHER ISSUES (SKIPPED) ===");
|
||||
for (const other of otherIssues) {
|
||||
console.log(`#${other.number} - ${other.title} (${other.type})`);
|
||||
}
|
||||
22
scripts/scratch/merged_prs.json
Normal file
22
scripts/scratch/merged_prs.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{"number":1917,"title":"Release v3.7.9","fixes":"1893"}
|
||||
{"number":1907,"title":"feat(proxy): move proxy configuration to dedicated System → Proxy page","fixes":"1905"}
|
||||
{"number":1902,"title":"feat: add K/M/B/T cost shortener to prevent UI overflow","fixes":"1900"}
|
||||
{"number":1851,"title":"Release v3.7.8","fixes":"1788"}
|
||||
{"number":1847,"title":"feat: integrate 1proxy free proxy marketplace","fixes":"1788"}
|
||||
{"number":1769,"title":"fix(antigravity): normalize Gemini bridge payloads","fixes":"1748"}
|
||||
{"number":1758,"title":"feat(mcp): Phase 6 MCP Compression Tools + Provider-Aware Caching","fixes":"1591"}
|
||||
{"number":1756,"title":"feat(compression): Phase 5 — Dashboard UI & Analytics (#1590)","fixes":"1590"}
|
||||
{"number":1741,"title":"feat(compression): Phase 4 — ultra mode with heuristic token pruning and SLM stub","fixes":"1589"}
|
||||
{"number":1728,"title":"feat(pwa): add fullscreen installable PWA with manifest","fixes":"1695"}
|
||||
{"number":1710,"title":"fix(combo): avoid false ALL_ACCOUNTS_INACTIVE on quality failures","fixes":"1707"}
|
||||
{"number":1685,"title":"[HOTFIX] Complete context truncation fix (PR #1480 follow-up)","fixes":"1470"}
|
||||
{"number":1640,"title":"fix(codex): avoid startup crash when wreq-js is unavailable","fixes":"1612"}
|
||||
{"number":1639,"title":"fix: package Electron runtime deps","fixes":"1636"}
|
||||
{"number":1633,"title":"feat(compression): Phase 1 — Modular Prompt Compression Pipeline (Lite mode, 61 tests)","fixes":"1586"}
|
||||
{"number":1578,"title":"fix(codex): update client version for gpt-5.5","fixes":"1545"}
|
||||
{"number":1541,"title":"fix: remove stale compiled dataPaths.js artifact","fixes":"1539"}
|
||||
{"number":1509,"title":"bug: fixes Error: Cannot find module 'process/' #1507","fixes":"1507"}
|
||||
{"number":1476,"title":"feat(vision-bridge): add automatic image description fallback for non-vision models","fixes":"1424"}
|
||||
{"number":1439,"title":"Release v3.7.0","fixes":"1592"}
|
||||
{"number":1416,"title":"Fix/gemini cli ref 400 error for opencode","fixes":"461"}
|
||||
{"number":1383,"title":"fix(docker): copy postinstallSupport.mjs before npm ci in Dockerfile","fixes":"1382"}
|
||||
30
scripts/scratch/pr_search.json
Normal file
30
scripts/scratch/pr_search.json
Normal file
@@ -0,0 +1,30 @@
|
||||
[]
|
||||
[{"mergedAt":"2026-04-03T21:48:12Z","number":963,"title":"build(deps): bump actions/checkout from 4 to 6"},{"mergedAt":"2026-03-20T19:06:53Z","number":500,"title":"chore(deps): bump actions/checkout from 4 to 6"},{"mergedAt":"2026-02-17T04:04:31Z","number":58,"title":"chore(deps): bump actions/checkout from 4 to 6"}]
|
||||
[]
|
||||
[{"mergedAt":"2026-05-03T19:16:18Z","number":1907,"title":"feat(proxy): move proxy configuration to dedicated System → Proxy page"}]
|
||||
[{"mergedAt":"2026-05-03T19:16:50Z","number":1902,"title":"feat: add K/M/B/T cost shortener to prevent UI overflow"}]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[{"mergedAt":"2026-04-28T02:27:20Z","number":1692,"title":"fix(sse): sanitize OpenAI tool schemas for strict upstream validators (kimi-k2.6 via opencode-go)"}]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[{"mergedAt":"2026-04-19T22:50:30Z","number":1404,"title":"Release v3.6.9"}]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[{"mergedAt":"2026-02-21T09:55:40Z","number":94,"title":"Fix/auto generate jwt secret"}]
|
||||
[{"mergedAt":"2026-03-10T12:09:35Z","number":264,"title":"deps: bump the development group with 5 updates"}]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[]
|
||||
[{"mergedAt":"2026-04-30T21:11:57Z","number":1758,"title":"feat(mcp): Phase 6 MCP Compression Tools + Provider-Aware Caching"},{"mergedAt":"2026-04-30T20:56:51Z","number":1756,"title":"feat(compression): Phase 5 — Dashboard UI & Analytics (#1590)"},{"mergedAt":"2026-04-30T20:43:28Z","number":1741,"title":"feat(compression): Phase 4 — ultra mode with heuristic token pruning and SLM stub"}]
|
||||
3
scripts/scratch/test-math-regex.js
Normal file
3
scripts/scratch/test-math-regex.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const re = /\$\$[^$]*(?:\$(?!\$)[^$]*)*\$\$/g;
|
||||
const txt = "some text $$ a + b = c $$ more text $$ x + $y$ = z $$ end";
|
||||
console.log(txt.match(re));
|
||||
5
scripts/scratch/test-regex.js
Normal file
5
scripts/scratch/test-regex.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const text = "hello world. this is a test\n\nnew line. another.";
|
||||
const re1 = /(^|[.!?][ \t]+|\n+[ \t]*)([a-z])/g;
|
||||
const re2 = /(^|[.!?]\s+)([a-z])/gm;
|
||||
console.log(text.replace(re1, (m, p, c) => p + c.toUpperCase()));
|
||||
console.log(text.replace(re2, (m, p, c) => p + c.toUpperCase()));
|
||||
5
scripts/scratch/test-regex2.js
Normal file
5
scripts/scratch/test-regex2.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const text = "hello\n world";
|
||||
const re1 = /(^|[.!?][ \t]+|\n+[ \t]*)([a-z])/g;
|
||||
const re3 = /(^|[.!?]\s+|^\s+)([a-z])/gm;
|
||||
console.log(text.replace(re1, (m, p, c) => p + c.toUpperCase()));
|
||||
console.log(text.replace(re3, (m, p, c) => p + c.toUpperCase()));
|
||||
@@ -227,6 +227,6 @@ describe("caveman engine", () => {
|
||||
minMessageLength: 50,
|
||||
preservePatterns: [],
|
||||
});
|
||||
assert.ok(result.stats.durationMs < 5, `Expected <5ms, got ${result.stats.durationMs}ms`);
|
||||
assert.ok(result.stats.durationMs < 25, `Expected <25ms, got ${result.stats.durationMs}ms`);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user