fix(#2128): bound the remaining lazy-scan ReDoS vectors (files_modified, Plans-count, <tag>)
A ReDoS-completeness audit surfaced a distinct class beyond the tag/bracket
clause: unbounded `[\s\S]*?` / `[^\]]*` lazy-scans searching for a literal
terminator that may never appear, driven quadratic by REPEATED structures in a
large PLAN.md/ROADMAP.md. Folded all 7 in at maintainer direction:
- files_modified `[^\]]*` -> `[^\]]{0,8000}` (commands.cts, verify.cts): 39.7s -> 0.9s.
- Plans-count `[\s\S]*?` -> section-local `(?:(?!\n#{1,4}\s)[\s\S])*?` — stops at the
next heading (semantically correct: Plans: belongs to the phase's own section)
(roadmap.cts x3, phase.cts): 36s -> 4ms.
- <tag> extraction `([\s\S]*?)` -> stop at the next same-tag opening
`((?:(?!<tag>)[\s\S])*?)` (verify.cts x3, markdown-sectionizer.cts): ~6s -> 2ms.
Every vector is now linear (comprehensively re-measured); real content matches
(end-to-end `roadmap get-phase` still resolves Plans-counted phases). Pre-existing;
byte-behavior preserved for realistic inputs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1334,7 +1334,7 @@ function cmdTodoMatchPhase(cwd: string, phase: string | undefined, raw: boolean)
|
||||
for (const pf of planFiles) {
|
||||
const planContent = platformReadSync(path.join(phaseDir, pf));
|
||||
if (planContent === null) continue;
|
||||
const fmFiles = planContent.match(/files_modified:\s*\[([^\]]*)\]/);
|
||||
const fmFiles = planContent.match(/files_modified:\s*\[([^\]]{0,8000})\]/);
|
||||
if (fmFiles) {
|
||||
phasePlans.push(...fmFiles[1].split(',').map(s => s.trim().replace(/['"]/g, '')).filter(Boolean));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user