Files
2026-06-14 13:18:56 +00:00

118 lines
4.0 KiB
JavaScript

import { detectViolations } from './lib/detectors.js';
import { loadPolicy, checkPolicy } from './lib/policy.js';
import { addEvent, addViolation, getStats } from './lib/audit.js';
const AGENT_PATTERNS = {
'claude.ai': 'claude-code',
'chatgpt.com': 'chatgpt',
'chat.openai.com': 'chatgpt',
'github.com': 'github-copilot',
'cursor.sh': 'cursor'
};
function detectAgent(url) {
const hostname = new URL(url).hostname;
for (const [domain, agent] of Object.entries(AGENT_PATTERNS)) {
if (hostname.includes(domain)) return agent;
}
return 'unknown';
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'AGENT_EVENT') {
handleAgentEvent(message.payload, sender.tab?.url || 'unknown').then(() => {
sendResponse({ success: true });
}).catch(err => {
console.error('Agent Police Department: event error', err);
sendResponse({ success: false, error: err.message });
});
return true;
}
if (message.type === 'GET_STATS') {
getStats().then(stats => sendResponse({ success: true, stats })).catch(err => {
sendResponse({ success: false, error: err.message });
});
return true;
}
if (message.type === 'GET_AUDIT_LOG') {
import('./lib/audit.js').then(m => m.getAuditLog(message.limit || 100)).then(log => {
sendResponse({ success: true, log });
}).catch(err => sendResponse({ success: false, error: err.message }));
return true;
}
if (message.type === 'EXPORT_AUDIT') {
import('./lib/audit.js').then(m => m.exportAuditLog()).then(data => {
sendResponse({ success: true, data });
}).catch(err => sendResponse({ success: false, error: err.message }));
return true;
}
});
async function handleAgentEvent(event, tabUrl) {
event.agent = event.agent || detectAgent(tabUrl);
event.url = tabUrl;
event.timestamp = event.timestamp || new Date().toISOString();
event.id = event.id || crypto.randomUUID();
const policy = await loadPolicy();
const policyResult = checkPolicy(event, policy);
event.outcome = policyResult.outcome;
event.severity = policyResult.severity;
if (policyResult.ruleId) event.rule_triggered = policyResult.ruleId;
const violations = await detectViolations(event, policy);
await addEvent(event);
if (violations.length > 0) {
for (const v of violations) {
await addViolation(v);
}
await chrome.storage.local.set({ lastViolation: violations[0], lastViolationAt: Date.now() });
await chrome.action.setBadgeText({ text: String(violations.length) });
await chrome.action.setBadgeBackgroundColor({ color: '#DC2626' });
}
const stats = await getStats();
await chrome.storage.local.set({ stats });
}
chrome.runtime.onInstalled.addListener(async (details) => {
if (details.reason === 'install') {
await chrome.storage.local.set({
installDate: new Date().toISOString(),
version: chrome.runtime.getManifest().version
});
await loadPolicy();
}
});
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete' && tab.url) {
const agent = detectAgent(tab.url);
if (agent !== 'unknown') {
chrome.storage.local.get(['activeAgents']).then(result => {
const agents = result.activeAgents || {};
agents[agent] = { tabId, url: tab.url, lastSeen: Date.now() };
return chrome.storage.local.set({ activeAgents: agents });
});
}
}
});
chrome.alarms?.create?.('cleanup', { periodInMinutes: 60 });
chrome.alarms?.onAlarm?.addListener(async (alarm) => {
if (alarm.name === 'cleanup') {
const result = await chrome.storage.local.get(['activeAgents']);
const agents = result.activeAgents || {};
const now = Date.now();
const stale = 5 * 60 * 1000;
for (const [agent, data] of Object.entries(agents)) {
if (now - data.lastSeen > stale) delete agents[agent];
}
await chrome.storage.local.set({ activeAgents: agents });
}
});
console.log('Agent Police Department: background service worker loaded');