mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 21:53:28 +08:00
Implements three roadmap features: - Agent description compression (#491): New `agent-compress` module with catalog/summary/full compression modes and lazy-loading. Reduces ~26k token agent descriptions to ~2-3k catalog entries for context efficiency. - Inspection logic (#485): New `inspection` module that detects recurring failure patterns in skill_runs. Groups by skill + normalized failure reason, generates structured reports with suggested remediation actions. Configurable threshold (default: 3 failures). - Governance event capture hook (#482): PreToolUse/PostToolUse hook that detects secrets, policy violations, approval-required commands, and elevated privilege usage. Gated behind ECC_GOVERNANCE_CAPTURE=1 flag. Writes to governance_events table via JSON-line stderr output. 59 new tests (16 + 16 + 27), all passing.
295 lines
11 KiB
JavaScript
295 lines
11 KiB
JavaScript
/**
|
|
* Tests for governance event capture hook.
|
|
*/
|
|
|
|
const assert = require('assert');
|
|
|
|
const {
|
|
detectSecrets,
|
|
detectApprovalRequired,
|
|
detectSensitivePath,
|
|
analyzeForGovernanceEvents,
|
|
run,
|
|
} = require('../../scripts/hooks/governance-capture');
|
|
|
|
async function test(name, fn) {
|
|
try {
|
|
await fn();
|
|
console.log(` \u2713 ${name}`);
|
|
return true;
|
|
} catch (error) {
|
|
console.log(` \u2717 ${name}`);
|
|
console.log(` Error: ${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function runTests() {
|
|
console.log('\n=== Testing governance-capture ===\n');
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
|
|
// ── detectSecrets ──────────────────────────────────────────
|
|
|
|
if (await test('detectSecrets finds AWS access keys', async () => {
|
|
const findings = detectSecrets('my key is AKIAIOSFODNN7EXAMPLE');
|
|
assert.ok(findings.length > 0);
|
|
assert.ok(findings.some(f => f.name === 'aws_key'));
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectSecrets finds generic secrets', async () => {
|
|
const findings = detectSecrets('api_key = "sk-proj-abcdefghij1234567890"');
|
|
assert.ok(findings.length > 0);
|
|
assert.ok(findings.some(f => f.name === 'generic_secret'));
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectSecrets finds private keys', async () => {
|
|
const findings = detectSecrets('-----BEGIN RSA PRIVATE KEY-----\nMIIE...');
|
|
assert.ok(findings.length > 0);
|
|
assert.ok(findings.some(f => f.name === 'private_key'));
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectSecrets finds GitHub tokens', async () => {
|
|
const findings = detectSecrets('token: ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij');
|
|
assert.ok(findings.length > 0);
|
|
assert.ok(findings.some(f => f.name === 'github_token'));
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectSecrets returns empty array for clean text', async () => {
|
|
const findings = detectSecrets('This is a normal log message with no secrets.');
|
|
assert.strictEqual(findings.length, 0);
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectSecrets handles null and undefined', async () => {
|
|
assert.deepStrictEqual(detectSecrets(null), []);
|
|
assert.deepStrictEqual(detectSecrets(undefined), []);
|
|
assert.deepStrictEqual(detectSecrets(''), []);
|
|
})) passed += 1; else failed += 1;
|
|
|
|
// ── detectApprovalRequired ─────────────────────────────────
|
|
|
|
if (await test('detectApprovalRequired flags force push', async () => {
|
|
const findings = detectApprovalRequired('git push origin main --force');
|
|
assert.ok(findings.length > 0);
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectApprovalRequired flags hard reset', async () => {
|
|
const findings = detectApprovalRequired('git reset --hard HEAD~3');
|
|
assert.ok(findings.length > 0);
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectApprovalRequired flags rm -rf', async () => {
|
|
const findings = detectApprovalRequired('rm -rf /tmp/important');
|
|
assert.ok(findings.length > 0);
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectApprovalRequired flags DROP TABLE', async () => {
|
|
const findings = detectApprovalRequired('DROP TABLE users');
|
|
assert.ok(findings.length > 0);
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectApprovalRequired allows safe commands', async () => {
|
|
const findings = detectApprovalRequired('git status');
|
|
assert.strictEqual(findings.length, 0);
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectApprovalRequired handles null', async () => {
|
|
assert.deepStrictEqual(detectApprovalRequired(null), []);
|
|
assert.deepStrictEqual(detectApprovalRequired(''), []);
|
|
})) passed += 1; else failed += 1;
|
|
|
|
// ── detectSensitivePath ────────────────────────────────────
|
|
|
|
if (await test('detectSensitivePath identifies .env files', async () => {
|
|
assert.ok(detectSensitivePath('.env'));
|
|
assert.ok(detectSensitivePath('.env.local'));
|
|
assert.ok(detectSensitivePath('/project/.env.production'));
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectSensitivePath identifies credential files', async () => {
|
|
assert.ok(detectSensitivePath('credentials.json'));
|
|
assert.ok(detectSensitivePath('/home/user/.ssh/id_rsa'));
|
|
assert.ok(detectSensitivePath('server.key'));
|
|
assert.ok(detectSensitivePath('cert.pem'));
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectSensitivePath returns false for normal files', async () => {
|
|
assert.ok(!detectSensitivePath('index.js'));
|
|
assert.ok(!detectSensitivePath('README.md'));
|
|
assert.ok(!detectSensitivePath('package.json'));
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('detectSensitivePath handles null', async () => {
|
|
assert.ok(!detectSensitivePath(null));
|
|
assert.ok(!detectSensitivePath(''));
|
|
})) passed += 1; else failed += 1;
|
|
|
|
// ── analyzeForGovernanceEvents ─────────────────────────────
|
|
|
|
if (await test('analyzeForGovernanceEvents detects secrets in tool input', async () => {
|
|
const events = analyzeForGovernanceEvents({
|
|
tool_name: 'Write',
|
|
tool_input: {
|
|
file_path: '/tmp/config.js',
|
|
content: 'const key = "AKIAIOSFODNN7EXAMPLE";',
|
|
},
|
|
});
|
|
|
|
assert.ok(events.length > 0);
|
|
const secretEvent = events.find(e => e.eventType === 'secret_detected');
|
|
assert.ok(secretEvent);
|
|
assert.strictEqual(secretEvent.payload.severity, 'critical');
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('analyzeForGovernanceEvents detects approval-required commands', async () => {
|
|
const events = analyzeForGovernanceEvents({
|
|
tool_name: 'Bash',
|
|
tool_input: {
|
|
command: 'git push origin main --force',
|
|
},
|
|
});
|
|
|
|
assert.ok(events.length > 0);
|
|
const approvalEvent = events.find(e => e.eventType === 'approval_requested');
|
|
assert.ok(approvalEvent);
|
|
assert.strictEqual(approvalEvent.payload.severity, 'high');
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('analyzeForGovernanceEvents detects sensitive file access', async () => {
|
|
const events = analyzeForGovernanceEvents({
|
|
tool_name: 'Edit',
|
|
tool_input: {
|
|
file_path: '/project/.env.production',
|
|
old_string: 'DB_URL=old',
|
|
new_string: 'DB_URL=new',
|
|
},
|
|
});
|
|
|
|
assert.ok(events.length > 0);
|
|
const policyEvent = events.find(e => e.eventType === 'policy_violation');
|
|
assert.ok(policyEvent);
|
|
assert.strictEqual(policyEvent.payload.reason, 'sensitive_file_access');
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('analyzeForGovernanceEvents detects elevated privilege commands', async () => {
|
|
const events = analyzeForGovernanceEvents({
|
|
tool_name: 'Bash',
|
|
tool_input: { command: 'sudo rm -rf /etc/something' },
|
|
}, {
|
|
hookPhase: 'post',
|
|
});
|
|
|
|
const securityEvent = events.find(e => e.eventType === 'security_finding');
|
|
assert.ok(securityEvent);
|
|
assert.strictEqual(securityEvent.payload.reason, 'elevated_privilege_command');
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('analyzeForGovernanceEvents returns empty for clean inputs', async () => {
|
|
const events = analyzeForGovernanceEvents({
|
|
tool_name: 'Read',
|
|
tool_input: { file_path: '/project/src/index.js' },
|
|
});
|
|
assert.strictEqual(events.length, 0);
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('analyzeForGovernanceEvents populates session ID from context', async () => {
|
|
const events = analyzeForGovernanceEvents({
|
|
tool_name: 'Write',
|
|
tool_input: {
|
|
file_path: '/project/.env',
|
|
content: 'DB_URL=test',
|
|
},
|
|
}, {
|
|
sessionId: 'test-session-123',
|
|
});
|
|
|
|
assert.ok(events.length > 0);
|
|
assert.strictEqual(events[0].sessionId, 'test-session-123');
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('analyzeForGovernanceEvents generates unique event IDs', async () => {
|
|
const events1 = analyzeForGovernanceEvents({
|
|
tool_name: 'Write',
|
|
tool_input: { file_path: '.env', content: '' },
|
|
});
|
|
const events2 = analyzeForGovernanceEvents({
|
|
tool_name: 'Write',
|
|
tool_input: { file_path: '.env.local', content: '' },
|
|
});
|
|
|
|
if (events1.length > 0 && events2.length > 0) {
|
|
assert.notStrictEqual(events1[0].id, events2[0].id);
|
|
}
|
|
})) passed += 1; else failed += 1;
|
|
|
|
// ── run() function ─────────────────────────────────────────
|
|
|
|
if (await test('run() passes through input when feature flag is off', async () => {
|
|
const original = process.env.ECC_GOVERNANCE_CAPTURE;
|
|
delete process.env.ECC_GOVERNANCE_CAPTURE;
|
|
|
|
try {
|
|
const input = JSON.stringify({ tool_name: 'Bash', tool_input: { command: 'git push --force' } });
|
|
const result = run(input);
|
|
assert.strictEqual(result, input);
|
|
} finally {
|
|
if (original !== undefined) {
|
|
process.env.ECC_GOVERNANCE_CAPTURE = original;
|
|
}
|
|
}
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('run() passes through input when feature flag is on', async () => {
|
|
const original = process.env.ECC_GOVERNANCE_CAPTURE;
|
|
process.env.ECC_GOVERNANCE_CAPTURE = '1';
|
|
|
|
try {
|
|
const input = JSON.stringify({ tool_name: 'Read', tool_input: { file_path: 'index.js' } });
|
|
const result = run(input);
|
|
assert.strictEqual(result, input);
|
|
} finally {
|
|
if (original !== undefined) {
|
|
process.env.ECC_GOVERNANCE_CAPTURE = original;
|
|
} else {
|
|
delete process.env.ECC_GOVERNANCE_CAPTURE;
|
|
}
|
|
}
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('run() handles invalid JSON gracefully', async () => {
|
|
const original = process.env.ECC_GOVERNANCE_CAPTURE;
|
|
process.env.ECC_GOVERNANCE_CAPTURE = '1';
|
|
|
|
try {
|
|
const result = run('not valid json');
|
|
assert.strictEqual(result, 'not valid json');
|
|
} finally {
|
|
if (original !== undefined) {
|
|
process.env.ECC_GOVERNANCE_CAPTURE = original;
|
|
} else {
|
|
delete process.env.ECC_GOVERNANCE_CAPTURE;
|
|
}
|
|
}
|
|
})) passed += 1; else failed += 1;
|
|
|
|
if (await test('run() can detect multiple event types in one input', async () => {
|
|
// Bash command with force push AND secret in command
|
|
const events = analyzeForGovernanceEvents({
|
|
tool_name: 'Bash',
|
|
tool_input: {
|
|
command: 'API_KEY="AKIAIOSFODNN7EXAMPLE" git push --force',
|
|
},
|
|
});
|
|
|
|
const eventTypes = events.map(e => e.eventType);
|
|
assert.ok(eventTypes.includes('secret_detected'));
|
|
assert.ok(eventTypes.includes('approval_requested'));
|
|
})) passed += 1; else failed += 1;
|
|
|
|
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
|
process.exit(failed > 0 ? 1 : 0);
|
|
}
|
|
|
|
runTests();
|