diff --git a/scripts/ci/scan-supply-chain-iocs.js b/scripts/ci/scan-supply-chain-iocs.js index ad8bd529..7b4c9569 100755 --- a/scripts/ci/scan-supply-chain-iocs.js +++ b/scripts/ci/scan-supply-chain-iocs.js @@ -414,6 +414,7 @@ function normalizeForMatch(value) { function isInSpecialConfigPath(filePath) { const normalized = normalizedPath(filePath); return /\/\.claude\//.test(normalized) + || /\/\.cursor\//.test(normalized) || /\/\.vscode\//.test(normalized) || /\/\.kiro\/settings\//.test(normalized) || /\/Library\/LaunchAgents\//.test(normalized) @@ -661,21 +662,26 @@ function scanFile(filePath, rootDir, findings) { for (const indicator of CRITICAL_TEXT_INDICATORS) { const normalizedIndicator = normalizeForMatch(indicator); - let index = lowerText.indexOf(normalizedIndicator); - while (index !== -1) { - if (!indexInRanges(index, defensiveClaudeDenyRanges)) { + // Require a non-filename character before the indicator so legitimate + // names that merely end with an IOC filename (e.g. the stock Cursor hook + // `before-shell-execution.js` vs the payload `execution.js`) do not match. + const indicatorPattern = new RegExp( + `(? { + withFixture({ + '.cursor/hooks.json': JSON.stringify({ + hooks: { + beforeShellExecution: [{ + command: 'node .cursor/hooks/before-shell-execution.js', + }], + afterShellExecution: [{ + command: 'node .cursor/hooks/after-shell-execution.js', + }], + beforeMCPExecution: [{ + command: 'node .cursor/hooks/before-mcp-execution.js', + }], + afterFileEdit: [{ + command: 'node .cursor/hooks/post_execution.js', + }], + }, + }, null, 2), + }, rootDir => { + const result = scanSupplyChainIocs({ rootDir }); + assert.deepStrictEqual(result.findings, []); + }); + })) passed++; else failed++; + + if (test('still flags the bare execution.js payload after a path separator', () => { + withFixture({ + '.cursor/hooks.json': JSON.stringify({ + hooks: { + sessionStart: [{ + command: 'node .cursor/hooks/execution.js', + }], + }, + }, null, 2), + }, rootDir => { + const result = scanSupplyChainIocs({ rootDir }); + assert.ok(result.findings.some(finding => finding.indicator === 'execution.js')); + }); + })) passed++; else failed++; + if (test('rejects user-level VS Code task persistence when home scan is enabled', () => { withFixture({ 'home/Library/Application Support/Code/User/tasks.json': JSON.stringify({