-
Notifications
You must be signed in to change notification settings - Fork 67
fix: resolve 30 CodeQL polynomial-redos alerts #211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
48fe4d5
e7147ee
ef6b1a9
4588428
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -57,7 +57,7 @@ const projectMemoryPatterns = { | |||||||||||||||||||||||||||||||||||||||||||
| const hasArchitecture = /##\s+architecture/i.test(content); | ||||||||||||||||||||||||||||||||||||||||||||
| const hasStructure = /##\s+(?:project\s+)?structure/i.test(content); | ||||||||||||||||||||||||||||||||||||||||||||
| const hasOverview = /##\s+overview/i.test(content); | ||||||||||||||||||||||||||||||||||||||||||||
| const hasDirectoryTree = /```[\s\S]*?(?:├──|└──|lib\/|src\/)[\s\S]*?```/.test(content); | ||||||||||||||||||||||||||||||||||||||||||||
| const hasDirectoryTree = content.includes('```') && /├──|└──|lib\/|src\//.test(content); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
61
|
||||||||||||||||||||||||||||||||||||||||||||
| const hasDirectoryTree = content.includes('```') && /├──|└──|lib\/|src\//.test(content); | |
| let hasDirectoryTree = false; | |
| if (content.includes('```')) { | |
| const fence = '```'; | |
| let searchStart = 0; | |
| while (!hasDirectoryTree) { | |
| const start = content.indexOf(fence, searchStart); | |
| if (start === -1) break; | |
| const end = content.indexOf(fence, start + fence.length); | |
| if (end === -1) break; | |
| const blockContent = content.slice(start + fence.length, end); | |
| if (/├──|└──|lib\/|src\//.test(blockContent)) { | |
| hasDirectoryTree = true; | |
| break; | |
| } | |
| searchStart = end + fence.length; | |
| } | |
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -29,7 +29,7 @@ | |||||
| const NOT_JSON_KEYWORDS = /(function|const|let|var|if|for|while|class)\b/; | ||||||
| // JS patterns require syntax context (not just keywords that might appear in JSON strings) | ||||||
| const LOOKS_LIKE_JS = /\b(function\s*\(|const\s+\w+\s*=|let\s+\w+\s*=|var\s+\w+\s*=|=>\s*[{(]|async\s+function|await\s+\w|class\s+\w+\s*{|import\s+\{|export\s+(const|function|class|default)|require\s*\()/; | ||||||
| const LOOKS_LIKE_PYTHON = /\b(def\s+\w+|import\s+\w+|from\s+\w+\s+import|class\s+\w+:|if\s+.*:|\s{4}|print\()\b/; | ||||||
| const LOOKS_LIKE_PYTHON = /\b(def\s+\w+|import\s+\w+|from\s+\w+\s+import|class\s+\w+:|if[ \t]+[^\n]*:|\s{4}|print\()\b/; | ||||||
|
|
||||||
| // Memoization caches for performance (keyed by content hash) | ||||||
| let _lastContent = null; | ||||||
|
|
@@ -220,7 +220,7 @@ | |||||
| // Skip lines listing vague terms as documentation | ||||||
| if (/vague\s*(instructions?|terms?|language|patterns?)\s*[:"]/.test(trimmed)) return false; | ||||||
| // Skip lines with quoted lists of vague words | ||||||
| if (/["']usually["'].*["']sometimes["']/.test(trimmed)) return false; | ||||||
| if (trimmed.includes('usually') && trimmed.includes('sometimes') && /["']/.test(trimmed)) return false; | ||||||
| return true; | ||||||
| }); | ||||||
| const filteredContent = lines.join('\n'); | ||||||
|
|
@@ -331,7 +331,11 @@ | |||||
| if (!content || typeof content !== 'string') return null; | ||||||
|
|
||||||
| // Skip workflow orchestrators that spawn agents/skills rather than produce output directly | ||||||
| const isOrchestrator = /##\s*Phase\s+\d+|Task\(\{|spawn.*agent|subagent_type|await Task\(|invoke.*skill|Skill\s*tool/i.test(content); | ||||||
| const lc = content.toLowerCase(); | ||||||
| const isOrchestrator = /##\s*Phase\s+\d+/i.test(content) || content.includes('Task({') || | ||||||
| (lc.includes('spawn') && lc.includes('agent')) || content.includes('subagent_type') || | ||||||
| content.includes('await Task(') || (lc.includes('invoke') && lc.includes('skill')) || | ||||||
| (/\bSkill\b/.test(content) && lc.includes('tool')); | ||||||
|
||||||
| (/\bSkill\b/.test(content) && lc.includes('tool')); | |
| (/\bskill\b/.test(lc) && lc.includes('tool')); |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The updated requestsJson regex requires exactly one space/tab after with|in|as (because of (?:with|in|as)[ \t]), so it will no longer match cases like respond with JSON (multiple spaces) or respond with\t\tJSON. Consider using a 1+ quantifier (e.g. [ \t]+) after the keyword to preserve prior behavior while keeping the ReDoS fix.
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasSchema detection was changed from a bounded json-code-block regex to (content.includes('```json') && content.includes('{')), which can produce false positives (e.g., any { elsewhere in the file) and cause json_without_schema to miss cases where JSON is requested but no schema/example is actually provided. Consider using a ReDoS-safe, more specific pattern (e.g., matching { near the json fence or scanning within the json code block) to keep the original intent while avoiding catastrophic backtracking.
Fixed
Show fixed
Hide fixed
Check failure
Code scanning / CodeQL
Polynomial regular expression used on uncontrolled data High
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new link extraction loop can become O(n^2) on large inputs with many
[/]pairs (each iteration doesindexOf(']')scanning potentially most of the remaining string, andposonly advances by 1 on the fallback path). To keep the ReDoS fix without introducing a different potential CPU hotspot, consider advancingpostocloseBracket + 1when a]is found but it isn't a link, and/or otherwise ensure the loop always makes proportional progress based on the furthest delimiter found.