Files
EnterpriseAppProtection/content.js
becarta ba83c95914 feat: Add Enterprise App Protection extension with settings and popup UI
- Implement manifest.json for Chrome extension with necessary permissions and background scripts.
- Create options.html for user settings including Google Safe Browsing API key, domains database URL, update interval, and warning message template.
- Develop options.js to handle loading and saving settings using Chrome storage.
- Design popup.html to display suspicious links and provide options to update the database and manage domains.
- Implement popup.js to manage interactions in the popup, including updating the database and resetting the suspicious links counter.
- Add testsite.html for dynamic testing of the extension with both official and fake links.
2025-05-10 03:25:49 +02:00

240 lines
9.0 KiB
JavaScript

// content.js
let SAFE_BROWSING_API_KEY = "";
let domainsDB = {};
let trustedDomains = [];
let blockedDomains = [];
let warningTemplate = "Warning: This link claims to be {app} but goes to an unofficial domain.";
let isDomainsLoaded = false;
// ✅ Get the top-level domain (TLD)
function getTopLevelDomain(hostname) {
const domainParts = hostname.split(".");
const knownTLDs = ["co.uk", "com.au", "gov.uk", "edu.au"];
if (domainParts.length > 2) {
const lastTwoParts = domainParts.slice(-2).join(".");
if (knownTLDs.includes(lastTwoParts)) {
return domainParts.slice(-3).join(".");
}
}
return domainParts.slice(-2).join(".");
}
// ✅ Check if a domain is valid
function isValidDomain(domain, validDomains, trustedDomains) {
const extractedTLD = getTopLevelDomain(domain);
if (trustedDomains.includes(domain) || trustedDomains.includes(extractedTLD)) {
return true; // User has explicitly marked this domain as safe
}
return validDomains.some(validDomain => {
const validTLD = getTopLevelDomain(validDomain);
return extractedTLD === validTLD;
});
}
// ✅ Check Google Safe Browsing API for dangerous links
async function checkGoogleSafeBrowsing(url) {
console.log("🔍 Checking Google Safe Browsing for:", url);
if (!SAFE_BROWSING_API_KEY) {
console.warn("⚠️ API key is not set. Skipping Safe Browsing check.");
return false;
}
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 }]
}
})
});
const data = await response.json();
console.log("🔹 Google Safe Browsing API Response:", data);
return data.matches ? true : false;
}
// ✅ Scan page content for suspicious links
function analyzePageContent() {
if (!isDomainsLoaded || !domainsDB || Object.keys(domainsDB).length === 0) {
console.warn("⚠️ domainsDB is not ready yet. Skipping analysis.");
return;
}
const links = document.querySelectorAll("a:not(.checked-by-extension)");
let newFlaggedLinks = new Set();
links.forEach(link => {
try {
// ✅ Validate the URL before using it
let url;
try {
url = new URL(link.href);
} catch (e) {
console.warn("⚠️ Skipping invalid URL:", link.href);
return; // Stop processing this link
}
const domain = url.hostname.toLowerCase();
const linkText = link.innerText.trim();
// ✅ Mark link as processed
link.classList.add("checked-by-extension");
// ✅ Skip trusted domains
if (trustedDomains.includes(domain) || trustedDomains.includes(getTopLevelDomain(domain))) {
console.log("Skipping trusted domain:", domain);
return;
}
let matchedApp = null;
// ✅ Find the most specific matching app name (ONLY full word matches)
for (const [appName, validDomains] of Object.entries(domainsDB)) {
const regex = new RegExp(`\\b${appName}\\b`, "i"); // Ensure full-word match
if (regex.test(linkText)) {
matchedApp = appName; // Keep the most specific match
}
}
if (matchedApp && domainsDB[matchedApp]) {
const validDomains = domainsDB[matchedApp];
const isValid = isValidDomain(domain, validDomains, trustedDomains);
if (!isValid) {
newFlaggedLinks.add(url.href);
// ✅ Add warning immediately
const warning = document.createElement("div");
warning.classList.add("warning-alert");
warning.style.cssText = `
background: #fff3cd;
color: #856404;
padding: 10px;
border: 1px solid #ffeeba;
border-radius: 4px;
margin: 5px 0;
font-size: 14px;
`;
warning.textContent = `⚠️ ${warningTemplate.replace("{app}", matchedApp)}`;
warning.setAttribute("title", `This link goes to ${domain} instead of an official ${matchedApp} domain.`);
link.parentElement.insertBefore(warning, link.nextSibling);
// ✅ Update warning if Google Safe Browsing confirms danger
checkGoogleSafeBrowsing(url.href).then(isUnsafe => {
if (isUnsafe) {
warning.textContent = `⚠️ This link is confirmed dangerous by Google Safe Browsing!`;
}
});
}
}
} catch (e) {
console.error("Unexpected error processing link:", e);
}
});
// ✅ Store flagged links without waiting for Safe Browsing API
chrome.storage.local.get(["flaggedLinks", "totalSuspiciousLinks"], function (result) {
let existingLinks = new Set(result.flaggedLinks || []);
let totalCount = existingLinks.size;
let newLinksToAdd = [...newFlaggedLinks].filter(link => !existingLinks.has(link));
if (newLinksToAdd.length > 0) {
totalCount += newLinksToAdd.length;
chrome.storage.local.set({
totalSuspiciousLinks: totalCount,
flaggedLinks: [...existingLinks, ...newLinksToAdd]
}, () => {
chrome.runtime.sendMessage({ action: "updatePopup", flaggedLinks: [...existingLinks, ...newLinksToAdd], totalCount });
});
}
});
}
// ✅ Load domains and start analysis once ready
function loadDomainsAndAnalyze() {
chrome.storage.local.get(["domainsDB", "trustedDomains", "blockedDomains", "safeBrowsingApiKey", "warningTemplate"], function (result) {
domainsDB = result.domainsDB || {};
trustedDomains = result.trustedDomains || [];
blockedDomains = result.blockedDomains || [];
SAFE_BROWSING_API_KEY = result.safeBrowsingApiKey || "";
warningTemplate = result.warningTemplate || "Warning: This link claims to be {app} but goes to an unofficial domain.";
console.log("✅ Loaded API Key:", SAFE_BROWSING_API_KEY);
isDomainsLoaded = true;
console.log("✅ Domains database loaded:", domainsDB);
analyzePageContent();
});
}
// Debounce function to limit how often a function can be called
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Enhanced error handling wrapper
function withErrorHandling(fn, errorMessage) {
return async (...args) => {
try {
return await fn(...args);
} catch (error) {
console.error(`${errorMessage}:`, error);
// You could add error reporting service here
return null;
}
};
}
// Debounced version of analyzePageContent
const debouncedAnalyzePageContent = debounce(analyzePageContent, 250);
// Enhanced version of analyzePageContent with better error handling
const enhancedAnalyzePageContent = withErrorHandling(debouncedAnalyzePageContent, "Error analyzing page content");
// Update the observer to use the enhanced version
function observePageForLinks() {
const observer = new MutationObserver(() => enhancedAnalyzePageContent());
observer.observe(document.body, { childList: true, subtree: true });
function observeIframes() {
document.querySelectorAll("iframe").forEach((iframe) => {
try {
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
if (iframeDoc) {
observer.observe(iframeDoc.body, { childList: true, subtree: true });
console.log("Observing links inside iframe:", iframe.src);
}
} catch (e) {
console.warn("Cannot access iframe due to cross-origin restrictions:", iframe.src);
}
});
}
// ✅ Run iframe observer every 3 seconds for Office 365 dynamic content
setInterval(observeIframes, 3000);
}
// ✅ Initialize extension scanning
document.addEventListener("DOMContentLoaded", loadDomainsAndAnalyze);
window.addEventListener("load", loadDomainsAndAnalyze);
observePageForLinks();