// tests/detectors.test.js require('../__mocks__/chrome.js'); const { detectViolations, DETECTOR_NAMES } = require('../lib/detectors.js'); describe('Detectors', () => { beforeEach(() => { global.__chromeStore = {}; }); test('DETECTOR_NAMES has 6 detectors', () => { expect(DETECTOR_NAMES).toHaveLength(6); expect(DETECTOR_NAMES).toContain('permission_bypass'); expect(DETECTOR_NAMES).toContain('out_of_scope'); expect(DETECTOR_NAMES).toContain('self_permissioning'); expect(DETECTOR_NAMES).toContain('disallowed_tools'); expect(DETECTOR_NAMES).toContain('redundant_operations'); expect(DETECTOR_NAMES).toContain('off_task'); }); test('detects permission bypass for disallowed domain', async () => { const event = { id: 'evt-1', type: 'api_call', tool: 'fetch', target: 'https://malicious.com/api', url: 'https://claude.ai/chat', timestamp: new Date().toISOString() }; const policy = { rules: [ { id: 'rule_allowed_domains', type: 'allowed_domains', enabled: true, params: { domains: ['github.com', 'claude.ai'] } } ] }; const violations = await detectViolations(event, policy); const bypass = violations.find(v => v.detector === 'permission_bypass'); expect(bypass).toBeDefined(); expect(bypass.severity).toBe('high'); expect(bypass.message).toContain('malicious.com'); }); test('allows permitted domain', async () => { const event = { id: 'evt-2', type: 'api_call', tool: 'fetch', target: 'https://github.com/api', url: 'https://claude.ai/chat', timestamp: new Date().toISOString() }; const policy = { rules: [ { id: 'rule_allowed_domains', type: 'allowed_domains', enabled: true, params: { domains: ['github.com'] } } ] }; const violations = await detectViolations(event, policy); const bypass = violations.find(v => v.detector === 'permission_bypass'); expect(bypass).toBeUndefined(); }); test('detects disallowed tools', async () => { const event = { id: 'evt-3', type: 'tool_use', tool: 'eval', target: 'some-script.js', url: 'https://claude.ai/chat', timestamp: new Date().toISOString() }; const policy = { rules: [ { id: 'rule_disallowed_tools', type: 'disallowed_tools', enabled: true, params: { tools: ['eval', 'exec'] } } ] }; const violations = await detectViolations(event, policy); const disallowed = violations.find(v => v.detector === 'disallowed_tools'); expect(disallowed).toBeDefined(); expect(disallowed.severity).toBe('critical'); expect(disallowed.message).toContain('eval'); }); test('detects self-permissioning', async () => { const event = { id: 'evt-4', type: 'tool_use', tool: 'edit_file', target: 'permissions.json', context: 'grant admin access control role privilege', url: 'https://claude.ai/chat', timestamp: new Date().toISOString() }; const policy = { rules: [ { id: 'rule_permission_change', type: 'permission_change', enabled: true, params: {} } ] }; const violations = await detectViolations(event, policy); const perm = violations.find(v => v.detector === 'self_permissioning'); expect(perm).toBeDefined(); expect(perm.severity).toBe('critical'); }); test('detects out-of-scope operations', async () => { const event = { id: 'evt-5', type: 'api_call', tool: 'delete', target: 'https://api.example.com/resource', url: 'https://claude.ai/chat', timestamp: new Date().toISOString() }; const policy = { rules: [ { id: 'rule_allowed_operations', type: 'allowed_operations', enabled: true, params: { operations: ['read', 'chat'] } } ] }; const violations = await detectViolations(event, policy); const scope = violations.find(v => v.detector === 'out_of_scope'); expect(scope).toBeDefined(); expect(scope.severity).toBe('medium'); }); test('no violations for normal activity', async () => { const event = { id: 'evt-6', type: 'chat', tool: 'chat', target: 'conversation', url: 'https://claude.ai/chat', timestamp: new Date().toISOString() }; const policy = { rules: [ { id: 'rule_allowed_domains', type: 'allowed_domains', enabled: true, params: { domains: ['claude.ai'] } }, { id: 'rule_disallowed_tools', type: 'disallowed_tools', enabled: true, params: { tools: ['eval'] } } ] }; const violations = await detectViolations(event, policy); expect(violations).toHaveLength(0); }); });