fix(#663): resolve open CodeQL/Dependabot security alerts (ReDoS, prototype pollution, workflow perms, qs DoS) (#665)
* fix(#663): resolve open CodeQL/Dependabot security alerts - ReDoS: collapse ambiguous nested quantifiers in phase-heading regexes (verify/validate/commands) and the plan-filename lookahead (phase) to provably-equivalent non-backtracking forms - prototype pollution: guard __proto__/constructor/prototype in setConfigValue - remove dead no-op .replace(/-/g,'-') in phase.cts - escape all regex metachars in bug-2839 test - add contents:read permissions to security-scan + install-smoke workflows - pin qs >= 6.15.2 via overrides (DoS GHSA) - broaden prompt-injection allowlist to translated security-model docs Closes #663 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#663): regression tests for prototype-pollution guard and roadmap-phase ReDoS Behavioral test that config-set rejects __proto__/constructor/prototype keys without polluting Object.prototype, plus a ReDoS guard (timing-bound) and behavior-preservation assertions for the collapsed phase-heading regexes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#663): make ReDoS regression assert structured result, not elapsed time Replace elapsed-time assertions (which tripped local/no-elapsed-assertion ESLint rule and were unsound for synchronous ReDoS) with structured-result assertions on adversarial inputs: assert that malformed phase headings/ unchecked-item lines without a terminating colon/space yield an empty Set, which is both the correct behavior and an exercise of the fixed linear regex on the catastrophic-backtracking input shape. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#663): add Security changeset fragment for #665 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#663): fold prototype-pollution regression into config.test.cjs The standalone bug-663-config-prototype-pollution.test.cjs was a 9th config-module test file, tripping lint-test-file-count (the allowlist is ratcheted and must not grow). Consolidated into config.test.cjs instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -694,7 +694,7 @@ function cmdPhaseAdd(cwd: string, description: string, raw: boolean, customId?:
|
||||
let _dirName: string;
|
||||
|
||||
if (customId || config.phase_naming === 'custom') {
|
||||
_newPhaseId = customId || slug.toUpperCase().replace(/-/g, '-');
|
||||
_newPhaseId = customId || slug.toUpperCase();
|
||||
if (!_newPhaseId) error('--id required when phase_naming is "custom"');
|
||||
_dirName = `${prefix}${_newPhaseId}-${slug}`;
|
||||
} else {
|
||||
@@ -805,7 +805,7 @@ function cmdPhaseAddBatch(cwd: string, descriptions: string[], raw: boolean): vo
|
||||
let newPhaseId: number | string;
|
||||
let dirName: string;
|
||||
if (config.phase_naming === 'custom') {
|
||||
newPhaseId = slug.toUpperCase().replace(/-/g, '-');
|
||||
newPhaseId = slug.toUpperCase();
|
||||
dirName = `${prefix}${newPhaseId}-${slug}`;
|
||||
} else {
|
||||
maxPhase += 1;
|
||||
@@ -1176,7 +1176,7 @@ function updateRoadmapAfterPhaseRemoval(
|
||||
`${prefix}${decrementRoadmapPhaseNumber(num, removedInt)}${suffix}`,
|
||||
);
|
||||
content = content.replace(
|
||||
/(?<![0-9-])(\d{2})-(\d{2})(?=(?:(?:-[A-Za-z][A-Za-z0-9-]*)*-(?:PLAN|SUMMARY)\.md)|(?![0-9-]))/g,
|
||||
/(?<![0-9-])(\d{2})-(\d{2})(?=(?:(?:-[A-Za-z][A-Za-z0-9-]*)?-(?:PLAN|SUMMARY)\.md)|(?![0-9-]))/g,
|
||||
(_match, phaseNum: string, planNum: string) =>
|
||||
`${decrementRoadmapPaddedPhaseNumber(phaseNum, removedInt)}-${planNum}`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user