Files
EnterpriseAppProtection/background.js
becarta 13d9821b8d feat: Enhance Enterprise App Protection extension with monitoring and UI improvements
- Introduce error reporting and performance monitoring in background.js to track API calls and processing times.
- Implement health check system to ensure the extension's operational status and log issues.
- Add caching and encryption utilities in content.js for improved link analysis and data validation.
- Refactor link analysis to process in batches, enhancing performance and user experience.
- Update UI in domains_management.html and options.html for better usability and aesthetics, including responsive design and improved layout.
- Enhance popup.html to display suspicious links with better styling and functionality.
- Modify manifest.json to include new permissions and host access for Safe Browsing API.
2025-05-10 03:31:18 +02:00

327 lines
9.8 KiB
JavaScript

// background.js
console.log("Background service worker loaded");
let domainsDB = {};
let SAFE_BROWSING_API_KEY = "";
const API_CALL_LIMIT = 100; // Maximum number of API calls per minute
let apiCallCount = 0;
let lastResetTime = Date.now();
// Error reporting service
const errorReporter = {
errors: [],
maxErrors: 100,
log(error, context = {}) {
const errorEntry = {
timestamp: Date.now(),
error: error.message || error,
stack: error.stack,
context
};
this.errors.push(errorEntry);
if (this.errors.length > this.maxErrors) {
this.errors.shift();
}
// Send to monitoring service if configured
if (this.monitoringEndpoint) {
this.sendToMonitoring(errorEntry);
}
console.error('Error logged:', errorEntry);
},
async sendToMonitoring(errorEntry) {
try {
await fetch(this.monitoringEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(errorEntry)
});
} catch (e) {
console.error('Failed to send error to monitoring:', e);
}
},
getErrors() {
return [...this.errors];
},
clearErrors() {
this.errors = [];
}
};
// Performance monitoring
const performanceMonitor = {
metrics: {
apiCalls: 0,
processingTime: 0,
errors: 0
},
startTimer() {
return performance.now();
},
endTimer(startTime) {
return performance.now() - startTime;
},
logMetric(name, value) {
this.metrics[name] = (this.metrics[name] || 0) + value;
},
getMetrics() {
return { ...this.metrics };
},
reset() {
this.metrics = {
apiCalls: 0,
processingTime: 0,
errors: 0
};
}
};
// Health check system
const healthCheck = {
lastCheck: Date.now(),
status: 'healthy',
issues: [],
async check() {
const startTime = performanceMonitor.startTimer();
try {
// Check storage access
await chrome.storage.local.get(['domainsDB']);
// Check API key
if (!SAFE_BROWSING_API_KEY) {
throw new Error('API key not configured');
}
// Check domains database
if (!domainsDB || Object.keys(domainsDB).length === 0) {
throw new Error('Domains database is empty');
}
this.status = 'healthy';
this.issues = [];
} catch (error) {
this.status = 'unhealthy';
this.issues.push({
timestamp: Date.now(),
error: error.message
});
errorReporter.log(error, { component: 'healthCheck' });
}
performanceMonitor.logMetric('processingTime', performanceMonitor.endTimer(startTime));
this.lastCheck = Date.now();
},
getStatus() {
return {
status: this.status,
lastCheck: this.lastCheck,
issues: [...this.issues]
};
}
};
async function updateDomainsDB() {
chrome.storage.local.get(["domainsDBURL"], async function (result) {
const domainsDBURL = result.domainsDBURL || "https://raw.githubusercontent.com/rrpbergsma/EnterpriseAppProtection/refs/heads/main/domains.json";
console.log("updateDomainsDB: Starting update from URL:", domainsDBURL);
try {
const response = await fetch(domainsDBURL);
if (!response.ok) {
console.error("updateDomainsDB: Fetch failed with status", response.status, response.statusText);
return;
}
domainsDB = await response.json();
chrome.storage.local.set({
domainsDB: domainsDB,
lastUpdate: Date.now()
}, () => {
console.log("updateDomainsDB: Database updated successfully at", new Date().toLocaleString());
});
} catch (error) {
console.error("updateDomainsDB: Failed to update domains database:", error);
}
});
}
// Run updates when the extension is installed or started
chrome.runtime.onInstalled.addListener(() => {
console.log("Extension installed");
updateDomainsDB();
});
chrome.runtime.onStartup.addListener(() => {
console.log("Service worker started");
updateDomainsDB();
});
// Periodic update based on UPDATE_INTERVAL (in hours)
setInterval(() => {
chrome.storage.local.get(["updateInterval"], function (result) {
const updateInterval = result.updateInterval || 24;
console.log("Periodic update triggered");
updateDomainsDB();
});
}, 3600000);
// Listen for manual update messages (e.g., from the popup)
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log("onMessage: Received message:", message);
if (message.action === "updateDB") {
console.log("onMessage: Manual update triggered via Update Now button");
updateDomainsDB().then(() => {
console.log("onMessage: Manual update completed");
sendResponse({ status: "updated" });
});
return true; // Keep message channel open for async response
}
if (message.action === "updatePopup") {
// Store flagged links and update the total suspicious count
chrome.storage.local.get(["totalSuspiciousLinks"], (result) => {
let totalCount = result.totalSuspiciousLinks || 0;
totalCount += message.flaggedLinks.length; // Add new links found in this session
chrome.storage.local.set({
flaggedLinks: message.flaggedLinks,
totalSuspiciousLinks: totalCount
}, () => {
console.log(`Updated totalSuspiciousLinks: ${totalCount}`);
});
});
}
if (message.action === "getSuspiciousLinks") {
// Retrieve both flagged links and total suspicious link count
chrome.storage.local.get(["flaggedLinks", "totalSuspiciousLinks"], (data) => {
sendResponse({
count: data.totalSuspiciousLinks || 0,
links: data.flaggedLinks || []
});
});
return true; // Keep message channel open for async response
}
if (message.action === "checkSafeBrowsing") {
checkSafeBrowsing(message.url)
.then(result => sendResponse(result))
.catch(error => {
console.error("Safe Browsing API error:", error);
sendResponse({ isUnsafe: false });
});
return true; // Required for async response
}
if (message.action === "getHealthStatus") {
sendResponse(healthCheck.getStatus());
return true;
}
if (message.action === "getMetrics") {
sendResponse({
performance: performanceMonitor.getMetrics(),
errors: errorReporter.getErrors()
});
return true;
}
});
// Debug Mode: Keep-alive mechanism for testing
const DEBUG_MODE = false; // Set to 'true' for testing
if (DEBUG_MODE) {
chrome.alarms.create("keepAlive", { delayInMinutes: 0.1, periodInMinutes: 0.1 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === "keepAlive") {
console.log("keepAlive alarm triggered, service worker is active");
}
});
}
// Reset API call count every minute
setInterval(() => {
apiCallCount = 0;
lastResetTime = Date.now();
}, 60000);
// Check URL against Google Safe Browsing API
async function checkSafeBrowsing(url) {
const startTime = performanceMonitor.startTimer();
try {
// Check rate limit
if (apiCallCount >= API_CALL_LIMIT) {
throw new Error('API call limit reached');
}
// Get API key from storage
if (!SAFE_BROWSING_API_KEY) {
const result = await chrome.storage.local.get(["safeBrowsingApiKey"]);
SAFE_BROWSING_API_KEY = result.safeBrowsingApiKey;
if (!SAFE_BROWSING_API_KEY) {
throw new Error('Safe Browsing API key not set');
}
}
apiCallCount++;
performanceMonitor.logMetric('apiCalls', 1);
const response = await fetch(
`https://safebrowsing.googleapis.com/v4/threatMatches:find?key=${SAFE_BROWSING_API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client: {
clientId: "enterprise-app-protection",
clientVersion: "1.0"
},
threatInfo: {
threatTypes: ["MALWARE", "SOCIAL_ENGINEERING"],
platformTypes: ["ANY_PLATFORM"],
threatEntryTypes: ["URL"],
threatEntries: [{ url }]
}
})
}
);
if (!response.ok) {
throw new Error(`API responded with status: ${response.status}`);
}
const data = await response.json();
performanceMonitor.logMetric('processingTime', performanceMonitor.endTimer(startTime));
return { isUnsafe: data.matches ? true : false };
} catch (error) {
performanceMonitor.logMetric('errors', 1);
errorReporter.log(error, { url, apiCallCount });
return { isUnsafe: false };
}
}
// Listen for storage changes to update API key
chrome.storage.onChanged.addListener((changes, namespace) => {
if (namespace === "local" && changes.safeBrowsingApiKey) {
SAFE_BROWSING_API_KEY = changes.safeBrowsingApiKey.newValue;
}
});
// Add periodic health checks
setInterval(() => {
healthCheck.check();
}, 300000); // Every 5 minutes