test: add 3 edge-case tests for regex boundary, sticky flag, and type bypass (Round 105)

- parseSessionMetadata: blank line within Completed section truncates items
  due to regex lookahead (?=###|\n\n|$) stopping at \n\n boundary
- grepFile: sticky (y) flag not stripped like g flag, causing stateful
  .test() behavior that misses matching lines
- getExecCommand: object args bypass SAFE_ARGS_REGEX (typeof !== 'string')
  but coerce to "[object Object]" in command string
This commit is contained in:
Affaan Mustafa
2026-02-13 16:59:56 -08:00
parent 332d0f444b
commit b27c21732f
3 changed files with 71 additions and 0 deletions

View File

@@ -1469,6 +1469,26 @@ function runTests() {
);
})) passed++; else failed++;
// ── Round 105: getExecCommand with object args (bypasses SAFE_ARGS_REGEX, coerced to [object Object]) ──
console.log('\nRound 105: getExecCommand (object args — typeof bypass coerces to [object Object]):');
if (test('getExecCommand with args={} bypasses SAFE_ARGS validation and coerces to "[object Object]"', () => {
// package-manager.js line 334: `if (args && typeof args === 'string' && !SAFE_ARGS_REGEX.test(args))`
// When args is an object: typeof {} === 'object' (not 'string'), so the
// SAFE_ARGS_REGEX check is entirely SKIPPED.
// Line 339: `args ? ' ' + args : ''` — object is truthy, so it reaches
// string concatenation which calls {}.toString() → "[object Object]"
// Final command: "npx prettier [object Object]" — brackets bypass validation.
const cmd = pm.getExecCommand('prettier', {});
assert.ok(cmd.includes('[object Object]'),
'Object args should be coerced to "[object Object]" via implicit toString()');
// Verify the SAFE_ARGS regex WOULD reject this string if it were a string arg
assert.throws(
() => pm.getExecCommand('prettier', '[object Object]'),
/unsafe characters/,
'Same string as explicit string arg is correctly rejected by SAFE_ARGS_REGEX');
})) passed++; else failed++;
// Summary
console.log('\n=== Test Results ===');
console.log(`Passed: ${passed}`);

View File

@@ -1717,6 +1717,28 @@ file.ts
'hasContext should be true (context section has actual content)');
})) passed++; else failed++;
// ── Round 105: parseSessionMetadata blank-line boundary truncates section items ──
console.log('\nRound 105: parseSessionMetadata (blank line inside section — regex stops at \\n\\n):');
if (test('parseSessionMetadata drops completed items after a blank line within the section', () => {
// session-manager.js line 119: regex `(?=###|\n\n|$)` uses lazy [\s\S]*? with
// a lookahead that stops at the first \n\n. If completed items are separated
// by a blank line, items below the blank line are silently lost.
const content = '# Session\n\n### Completed\n- [x] Task A\n\n- [x] Task B\n\n### In Progress\n- [ ] Task C\n';
const meta = sessionManager.parseSessionMetadata(content);
// The regex captures "- [x] Task A\n" then hits \n\n and stops.
// "- [x] Task B" is between the two sections but outside both regex captures.
assert.strictEqual(meta.completed.length, 1,
'Only Task A captured — blank line terminates the section regex before Task B');
assert.strictEqual(meta.completed[0], 'Task A',
'First completed item should be Task A');
// Task B is lost — it appears after the blank line, outside the captured range
assert.strictEqual(meta.inProgress.length, 1,
'In Progress should still capture Task C');
assert.strictEqual(meta.inProgress[0], 'Task C',
'In-progress item should be Task C');
})) passed++; else failed++;
// Summary
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);

View File

@@ -1519,6 +1519,35 @@ function runTests() {
}
})) passed++; else failed++;
// ── Round 105: grepFile with sticky (y) flag — not stripped, causes stateful .test() ──
console.log('\nRound 105: grepFile (sticky y flag — not stripped like g, stateful .test() bug):');
if (test('grepFile with /pattern/y sticky flag misses lines due to lastIndex state', () => {
const tmpDir = fs.mkdtempSync(path.join(utils.getTempDir(), 'r105-grep-sticky-'));
const testFile = path.join(tmpDir, 'test.txt');
try {
fs.writeFileSync(testFile, 'hello world\nhello again\nhello third');
// grepFile line 466: `pattern.flags.replace('g', '')` strips g but not y.
// With /hello/y (sticky), .test() advances lastIndex after each successful
// match. On the next line, .test() starts at lastIndex (not 0), so it fails
// unless the match happens at that exact position.
const stickyResults = utils.grepFile(testFile, /hello/y);
// Without the bug, all 3 lines should match. With sticky flag preserved,
// line 1 matches (lastIndex advances to 5), line 2 fails (no 'hello' at
// position 5 of "hello again"), line 3 also likely fails.
// The g-flag version (properly stripped) should find all 3:
const globalResults = utils.grepFile(testFile, /hello/g);
assert.strictEqual(globalResults.length, 3,
'g-flag regex should find all 3 lines (g is stripped, stateless)');
// Sticky flag causes fewer matches — demonstrating the bug
assert.ok(stickyResults.length < 3,
`Sticky y flag causes stateful .test() — found ${stickyResults.length}/3 lines ` +
'(y flag not stripped like g, so lastIndex advances between lines)');
} finally {
fs.rmSync(tmpDir, { recursive: true, force: true });
}
})) passed++; else failed++;
// Summary
console.log('\n=== Test Results ===');
console.log(`Passed: ${passed}`);