mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-16 08:26:52 +08:00
fix: prevent IOC scanner false positives on hook filenames and scan .cursor configs (#2245)
* fix: prevent IOC scanner false positives on hook filenames and scan .cursor configs The supply-chain IOC scanner matched CRITICAL_TEXT_INDICATORS with plain substring search, so legitimate hook filenames that merely end with a known payload name (e.g. the stock Cursor hook before-shell-execution.js vs the payload execution.js) were flagged as CRITICAL. Indicator matching now requires a non-filename character before the match. Also add .cursor/ to the special config paths so Cursor hooks.json files (a known persistence vector already listed in PERSISTENCE_FILENAMES) are actually inspected in normal checkouts - previously they were only scanned by accident when the repo path happened to contain /.claude/. * test: cover underscore-prefixed filenames in IOC boundary suppression Make explicit that '_' is treated as a filename word character, so snake_case hook names like post_execution.js are intentionally not flagged by the execution.js indicator (real payload references appear after '/', quotes, or whitespace).
This commit is contained in:
parent
d293941643
commit
e3f18d2376
@ -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(
|
||||
`(?<![a-z0-9_-])${escapeRegExp(normalizedIndicator)}`,
|
||||
'g',
|
||||
);
|
||||
let match;
|
||||
while ((match = indicatorPattern.exec(lowerText)) !== null) {
|
||||
if (!indexInRanges(match.index, defensiveClaudeDenyRanges)) {
|
||||
addFinding(
|
||||
findings,
|
||||
'critical',
|
||||
relativePath,
|
||||
lineForIndex(text, index),
|
||||
lineForIndex(text, match.index),
|
||||
indicator,
|
||||
'Known active supply-chain IOC is present',
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
index = lowerText.indexOf(normalizedIndicator, index + normalizedIndicator.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -310,6 +310,45 @@ function run() {
|
||||
});
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('does not flag legitimate hook filenames ending in an IOC filename', () => {
|
||||
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({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user