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.
This commit is contained in:
229
background.js
229
background.js
@@ -3,6 +3,136 @@
|
||||
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) {
|
||||
@@ -85,6 +215,29 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
});
|
||||
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
|
||||
@@ -97,4 +250,78 @@ if (DEBUG_MODE) {
|
||||
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
|
Reference in New Issue
Block a user