fix(#2128): bound the sibling bracket-prefix clause — complete the ReDoS fix
Review caught that the prior commit bounded only the paren tag clause and left
the SIBLING bracket-prefix `(?:\[[^\]]+\]\s*)?` (same host regexes, before Phase)
UNBOUNDED — the identical quadratic reachable via a `[...]` run (measured ~16s at
1.7MB). Bound `[^\]]+`/`[^\]]*` -> {1,200}/{0,200} across all 19 phase/milestone
heading prefixes. Comprehensive re-measurement now shows EVERY vector linear
(bracket/paren/id/name/milestone all ~2-44ms at 2.45MB; bracket scaling
2k->2ms, 4k->5ms, 8k->10ms). Also: update the #1729 literal-mirror parity test
off its stale unbounded constant, and add limit-1 (199) boundary coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1073,7 +1073,7 @@ function checkMilestonePrefixMismatches(
|
||||
): MilestoneMismatch[] {
|
||||
const mismatches: MilestoneMismatch[] = [];
|
||||
const sections: { version: string; start: number; end: number }[] = [];
|
||||
const sectionRx = /^#{1,3}\s+(?:\[[^\]]+\]\s*)?.*v(\d+\.\d+)/gim;
|
||||
const sectionRx = /^#{1,3}\s+(?:\[[^\]]{1,200}\]\s*)?.*v(\d+\.\d+)/gim;
|
||||
let m: RegExpExecArray | null;
|
||||
while ((m = sectionRx.exec(roadmapContent)) !== null) {
|
||||
if (sections.length > 0) sections[sections.length - 1].end = m.index;
|
||||
@@ -1082,7 +1082,7 @@ function checkMilestonePrefixMismatches(
|
||||
for (const section of sections) {
|
||||
const content = roadmapContent.slice(section.start, section.end);
|
||||
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
|
||||
const phaseRx = /#{2,4}\s*(?:\[[^\]]+\]\s*)?Phase\s+([\w][\w.-]*)(?:\s*\([^)\n]{0,200}\))?\s*:/gi;
|
||||
const phaseRx = /#{2,4}\s*(?:\[[^\]]{1,200}\]\s*)?Phase\s+([\w][\w.-]*)(?:\s*\([^)\n]{0,200}\))?\s*:/gi;
|
||||
let pm: RegExpExecArray | null;
|
||||
while ((pm = phaseRx.exec(content)) !== null) {
|
||||
const phaseId = pm[1];
|
||||
|
||||
Reference in New Issue
Block a user