// lib/policy.js — Policy loader and rule engine const DEFAULT_POLICY = { version: 1, rules: [ { id: 'rule_allowed_domains', type: 'allowed_domains', enabled: true, params: { domains: ['github.com', 'claude.ai', 'api.openai.com', 'chatgpt.com', 'cursor.sh'] } }, { id: 'rule_allowed_operations', type: 'allowed_operations', enabled: true, params: { operations: ['read', 'chat', 'search', 'code_generation', 'page_visit'] } }, { id: 'rule_disallowed_tools', type: 'disallowed_tools', enabled: true, params: { tools: ['eval', 'exec', 'shell', 'system', 'rm', 'sudo', 'chmod'] } }, { id: 'rule_permission_change', type: 'permission_change', enabled: true, params: {} }, { id: 'rule_redundant_rate', type: 'redundant_rate', enabled: true, params: { max_per_minute: 10 } }, { id: 'rule_task_scope', type: 'task_scope', enabled: false, params: { task_keywords: [] } } ] }; export async function loadPolicy() { try { const result = await chrome.storage.local.get(['policy']); if (result.policy) return result.policy; await chrome.storage.local.set({ policy: DEFAULT_POLICY }); return DEFAULT_POLICY; } catch (err) { console.error('Policy load error:', err); return DEFAULT_POLICY; } } export async function savePolicy(policy) { policy.version = (policy.version || 0) + 1; await chrome.storage.local.set({ policy }); return policy; } export function checkPolicy(event, policy) { const rules = policy?.rules || []; let outcome = 'allowed'; let severity = 'info'; let ruleId = null; for (const rule of rules) { if (!rule.enabled) continue; const result = evaluateRule(event, rule); if (result.violation) { outcome = 'violation'; severity = result.severity; ruleId = rule.id; break; } if (result.warning) { if (outcome !== 'violation') { outcome = 'warning'; severity = result.severity; ruleId = rule.id; } } } return { outcome, severity, ruleId }; } function evaluateRule(event, rule) { switch (rule.type) { case 'allowed_domains': return evaluateAllowedDomains(event, rule); case 'allowed_operations': return evaluateAllowedOperations(event, rule); case 'disallowed_tools': return evaluateDisallowedTools(event, rule); case 'permission_change': return evaluatePermissionChange(event, rule); case 'redundant_rate': return { violation: false }; case 'task_scope': return evaluateTaskScope(event, rule); default: return { violation: false }; } } function evaluateAllowedDomains(event, rule) { const allowed = rule.params?.domains || []; if (event.type === 'api_call' || event.type === 'tool_use') { const url = event.target || event.url || ''; try { const hostname = new URL(url).hostname; const isAllowed = allowed.some(d => hostname.includes(d)); if (!isAllowed) { return { violation: true, severity: 'high' }; } } catch { /* invalid URL */ } } return { violation: false }; } function evaluateAllowedOperations(event, rule) { const allowed = rule.params?.operations || []; const opType = event.type || ''; const isAllowed = allowed.some(op => opType.includes(op)); if (!isAllowed && (event.type === 'api_call' || event.type === 'tool_use')) { return { violation: true, severity: 'medium' }; } return { violation: false }; } function evaluateDisallowedTools(event, rule) { const disallowed = rule.params?.tools || []; const toolName = event.tool || ''; if (disallowed.some(dt => toolName.toLowerCase().includes(dt.toLowerCase()))) { return { violation: true, severity: 'critical' }; } return { violation: false }; } function evaluatePermissionChange(event, rule) { const context = (event.context || '') + (event.target || '') + (event.url || ''); const keywords = ['permission', 'grant', 'access control', 'role', 'privilege', 'acl', 'rbac']; if (keywords.some(kw => context.toLowerCase().includes(kw))) { return { violation: true, severity: 'critical' }; } return { violation: false }; } function evaluateTaskScope(event, rule) { const keywords = rule.params?.task_keywords || []; if (keywords.length === 0) return { violation: false }; const context = (event.context || '') + (event.target || ''); const onTask = keywords.some(kw => context.toLowerCase().includes(kw.toLowerCase())); if (!onTask && event.type === 'tool_use') { return { violation: true, severity: 'low' }; } return { violation: false }; }