- Introduce default configuration settings for API key, domains database URL, update interval, and warning message template in background.js and options.js. - Update domains database URL to a new source for improved reliability. - Refactor domain management UI in domains_management.js to support adding, editing, and removing trusted and blocked domains with enhanced user experience. - Implement search functionality for filtering domains in the management interface. - Improve status messaging for user feedback during domain operations.
359 lines
11 KiB
JavaScript
359 lines
11 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]
|
|
};
|
|
}
|
|
};
|
|
|
|
// Default configuration
|
|
const DEFAULT_CONFIG = {
|
|
apiKey: '',
|
|
domainsDBURL: 'https://git.365devnet.eu/365DevNet/EnterpriseAppProtection/raw/branch/main/domains.json',
|
|
updateInterval: 24, // hours
|
|
warningTemplate: 'Warning: This link claims to be {app} but goes to an unofficial domain.',
|
|
lastUpdate: null,
|
|
suspiciousCount: 0,
|
|
suspiciousLinks: []
|
|
};
|
|
|
|
// Function to get configuration
|
|
async function getConfig() {
|
|
return new Promise((resolve) => {
|
|
chrome.storage.local.get([
|
|
"safeBrowsingApiKey",
|
|
"domainsDBURL",
|
|
"updateInterval",
|
|
"warningTemplate"
|
|
], (result) => {
|
|
resolve({
|
|
apiKey: result.safeBrowsingApiKey || DEFAULT_CONFIG.apiKey,
|
|
domainsDBURL: result.domainsDBURL || DEFAULT_CONFIG.domainsDBURL,
|
|
updateInterval: result.updateInterval || DEFAULT_CONFIG.updateInterval,
|
|
warningTemplate: result.warningTemplate || DEFAULT_CONFIG.warningTemplate
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// Function to update last update timestamp
|
|
async function updateLastUpdate() {
|
|
await chrome.storage.local.set({ lastUpdate: Date.now() });
|
|
}
|
|
|
|
// Function to update domains database
|
|
async function updateDomainsDB() {
|
|
try {
|
|
const config = await getConfig();
|
|
console.log("Updating domains database from:", config.domainsDBURL);
|
|
const response = await fetch(config.domainsDBURL);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const data = await response.json();
|
|
await chrome.storage.local.set({ domainsDB: data });
|
|
await updateLastUpdate();
|
|
console.log('Domains database updated successfully');
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Failed to update domains database:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 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
|