mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-18 23:03:06 +08:00
fix: ignore defensive ioc deny rules
This commit is contained in:
@@ -580,12 +580,51 @@ function addFinding(findings, severity, filePath, line, indicator, message) {
|
|||||||
findings.push({ severity, filePath, line, indicator, message });
|
findings.push({ severity, filePath, line, indicator, message });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isClaudeSettingsFile(filePath) {
|
||||||
|
const normalized = normalizedPath(filePath);
|
||||||
|
return /\/\.claude\/settings(?:\.local)?\.json$/.test(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
function claudePermissionDenyRanges(filePath, text) {
|
||||||
|
if (!isClaudeSettingsFile(filePath)) return [];
|
||||||
|
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(text);
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const denyEntries = parsed?.permissions?.deny;
|
||||||
|
if (!Array.isArray(denyEntries)) return [];
|
||||||
|
|
||||||
|
const ranges = [];
|
||||||
|
for (const entry of denyEntries) {
|
||||||
|
if (typeof entry !== 'string' || entry.length === 0) continue;
|
||||||
|
|
||||||
|
for (const needle of [...new Set([JSON.stringify(entry), entry])]) {
|
||||||
|
let index = text.indexOf(needle);
|
||||||
|
while (index !== -1) {
|
||||||
|
ranges.push([index, index + needle.length]);
|
||||||
|
index = text.indexOf(needle, index + needle.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
function indexInRanges(index, ranges) {
|
||||||
|
return ranges.some(([start, end]) => index >= start && index < end);
|
||||||
|
}
|
||||||
|
|
||||||
function scanFile(filePath, rootDir, findings) {
|
function scanFile(filePath, rootDir, findings) {
|
||||||
const base = path.basename(filePath);
|
const base = path.basename(filePath);
|
||||||
const relativePath = path.relative(rootDir, filePath) || filePath;
|
const relativePath = path.relative(rootDir, filePath) || filePath;
|
||||||
const text = readText(filePath);
|
const text = readText(filePath);
|
||||||
const lowerText = normalizeForMatch(text);
|
const lowerText = normalizeForMatch(text);
|
||||||
const hashFinding = MALICIOUS_FILE_HASHES[sha256File(filePath)];
|
const hashFinding = MALICIOUS_FILE_HASHES[sha256File(filePath)];
|
||||||
|
const defensiveClaudeDenyRanges = claudePermissionDenyRanges(filePath, text);
|
||||||
|
|
||||||
if (hashFinding) {
|
if (hashFinding) {
|
||||||
addFinding(
|
addFinding(
|
||||||
@@ -621,16 +660,22 @@ function scanFile(filePath, rootDir, findings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const indicator of CRITICAL_TEXT_INDICATORS) {
|
for (const indicator of CRITICAL_TEXT_INDICATORS) {
|
||||||
const index = lowerText.indexOf(normalizeForMatch(indicator));
|
const normalizedIndicator = normalizeForMatch(indicator);
|
||||||
if (index !== -1) {
|
let index = lowerText.indexOf(normalizedIndicator);
|
||||||
addFinding(
|
while (index !== -1) {
|
||||||
findings,
|
if (!indexInRanges(index, defensiveClaudeDenyRanges)) {
|
||||||
'critical',
|
addFinding(
|
||||||
relativePath,
|
findings,
|
||||||
lineForIndex(text, index),
|
'critical',
|
||||||
indicator,
|
relativePath,
|
||||||
'Known active supply-chain IOC is present',
|
lineForIndex(text, index),
|
||||||
);
|
indicator,
|
||||||
|
'Known active supply-chain IOC is present',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = lowerText.indexOf(normalizedIndicator, index + normalizedIndicator.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,6 +251,45 @@ function run() {
|
|||||||
});
|
});
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('ignores explicit Claude Code deny-wall IOC entries', () => {
|
||||||
|
withFixture({
|
||||||
|
'home/.claude/settings.local.json': JSON.stringify({
|
||||||
|
permissions: {
|
||||||
|
deny: [
|
||||||
|
'Bash(*filev2.getsession.org*)',
|
||||||
|
'Bash(*router_runtime.js*)',
|
||||||
|
'Bash(*gh-token-monitor*)',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}, null, 2),
|
||||||
|
}, rootDir => {
|
||||||
|
const homeDir = path.join(rootDir, 'home');
|
||||||
|
const result = scanSupplyChainIocs({ rootDir, home: true, homeDir });
|
||||||
|
assert.deepStrictEqual(result.findings, []);
|
||||||
|
});
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('still rejects Claude Code hooks when matching IOCs also appear in deny entries', () => {
|
||||||
|
withFixture({
|
||||||
|
'home/.claude/settings.local.json': JSON.stringify({
|
||||||
|
permissions: {
|
||||||
|
deny: [
|
||||||
|
'Bash(*router_runtime.js*)',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hooks: {
|
||||||
|
PostToolUse: [{
|
||||||
|
hooks: [{ command: 'node ~/.claude/router_runtime.js' }],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
}, null, 2),
|
||||||
|
}, rootDir => {
|
||||||
|
const homeDir = path.join(rootDir, 'home');
|
||||||
|
const result = scanSupplyChainIocs({ rootDir, home: true, homeDir });
|
||||||
|
assert.ok(result.findings.some(finding => finding.indicator === 'router_runtime.js'));
|
||||||
|
});
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
if (test('rejects current dead-drop and import-time payload markers', () => {
|
if (test('rejects current dead-drop and import-time payload markers', () => {
|
||||||
withFixture({
|
withFixture({
|
||||||
'.vscode/tasks.json': JSON.stringify({
|
'.vscode/tasks.json': JSON.stringify({
|
||||||
|
|||||||
Reference in New Issue
Block a user