diff --git a/platform/firefox/js/background.js b/platform/firefox/js/background.js new file mode 100644 index 0000000..4e44564 --- /dev/null +++ b/platform/firefox/js/background.js @@ -0,0 +1,248 @@ +const filterListURLUnsafe = + "https://raw.githubusercontent.com/fmhy/FMHYFilterlist/refs/heads/main/sitelist.txt"; +const filterListURLPotentiallyUnsafe = + "https://raw.githubusercontent.com/fmhy/FMHYFilterlist/refs/heads/main/sitelist-plus.txt"; +const safeListURL = "https://api.fmhy.net/single-page"; +const starredListURL = + "https://raw.githubusercontent.com/fmhy/bookmarks/refs/heads/main/fmhy_in_bookmarks_starred_only.html"; + +let unsafeSites = []; +let potentiallyUnsafeSites = []; +let safeSites = []; +let starredSites = ["https://fmhy.net"]; + +// Helper function to extract URLs from markdown text +function extractUrlsFromMarkdown(markdown) { + const urlRegex = /https?:\/\/[^\s)]+/g; + return markdown.match(urlRegex) || []; +} + +// Helper function to extract URLs from HTML bookmarks +function extractUrlsFromBookmarks(html) { + const urlRegex = / line.trim() && !line.startsWith("#")); + } + + if (potentiallyUnsafeResponse.ok) { + const potentiallyUnsafeText = await potentiallyUnsafeResponse.text(); + potentiallyUnsafeSites = potentiallyUnsafeText + .split("\n") + .filter((line) => line.trim() && !line.startsWith("#")); + } + + console.log("Parsed Unsafe Sites:", unsafeSites); + console.log("Parsed Potentially Unsafe Sites:", potentiallyUnsafeSites); + } catch (error) { + console.error("Error fetching filter lists:", error); + } +} + +// Fetch the safe sites +async function fetchSafeSites() { + console.log("Fetching safe sites..."); + try { + const response = await fetch(safeListURL); + if (response.ok) { + const markdown = await response.text(); + const urls = extractUrlsFromMarkdown(markdown); + urls.forEach((siteUrl) => { + let fullUrl = normalizeUrl(siteUrl.trim()); + if (!safeSites.includes(fullUrl)) { + safeSites.push(fullUrl); + } + }); + } + console.log("Parsed Safe Sites:", safeSites); + } catch (error) { + console.error("Error fetching safe sites:", error); + } +} + +// Fetch the starred sites +async function fetchStarredSites() { + console.log("Fetching starred sites..."); + try { + const response = await fetch(starredListURL); + if (response.ok) { + const html = await response.text(); + const urls = extractUrlsFromBookmarks(html); + + // Normalize and add URLs to the starredSites array + starredSites = [...new Set(urls.map(normalizeUrl))]; + + // Ensure fmhy.net is always in the starred list + if (!starredSites.includes("https://fmhy.net")) { + starredSites.push("https://fmhy.net"); + } + + console.log("Parsed Starred Sites:", starredSites); + } + } catch (error) { + console.error("Error fetching starred sites:", error); + } +} + +// Update the Address Bar icon based on the site's status +function updatePageAction(status, tabId) { + let iconPath = "res/ext_icon_144.png"; // Default extension icon + + if (status === "safe") { + iconPath = "res/icons/safe.png"; + } else if (status === "unsafe") { + iconPath = "res/icons/unsafe.png"; + } else if (status === "potentially_unsafe") { + iconPath = "res/icons/potentially_unsafe.png"; + } else if (status === "starred") { + iconPath = "res/icons/starred.png"; + } + + // Show the page action (icon in the address bar) + browser.pageAction.setIcon({ + tabId: tabId, + path: iconPath, + }); + + // Make the page action icon visible in the address bar + browser.pageAction.show(tabId); +} + +// Check the site status and update the page action icon +function checkSiteAndUpdatePageAction(tabId, url) { + if (!url) return; + + const currentUrl = normalizeUrl(url.trim()); + console.log( + "Checking site status for address bar icon:", + currentUrl, + "TabId:", + tabId + ); + + // Check if the site is starred, safe, unsafe, or potentially unsafe + let isStarred = starredSites.some( + (site) => currentUrl.startsWith(normalizeUrl(site)) // Subdirectory check + ); + let isSafe = safeSites.some((site) => normalizeUrl(site) === currentUrl); + let isUnsafe = unsafeSites.some((site) => + currentUrl.includes(normalizeUrl(site)) + ); + let isPotentiallyUnsafe = potentiallyUnsafeSites.some((site) => + currentUrl.includes(normalizeUrl(site)) + ); + + // Prioritize starred sites first, then safe sites + if (isStarred) { + console.log("Updating address bar icon to starred for:", currentUrl); + updatePageAction("starred", tabId); + } else if (isSafe) { + console.log("Updating address bar icon to safe for:", currentUrl); + updatePageAction("safe", tabId); + } else if (isUnsafe) { + console.log("Updating address bar icon to unsafe for:", currentUrl); + updatePageAction("unsafe", tabId); + } else if (isPotentiallyUnsafe) { + console.log( + "Updating address bar icon to potentially unsafe for:", + currentUrl + ); + updatePageAction("potentially_unsafe", tabId); + } else { + console.log("No data for this site:", currentUrl); + updatePageAction("default", tabId); + } +} + +// Listen for messages from the popup to check site status +browser.runtime.onMessage.addListener((message, sender, sendResponse) => { + console.log("Received message in background for site:", message.url); + + if (message.action === "checkSiteStatus") { + const currentUrl = normalizeUrl(message.url.trim()); + console.log("Checking site status for:", currentUrl); + + let isStarred = starredSites.some( + (site) => currentUrl.startsWith(normalizeUrl(site)) // Subdirectory check + ); + let isSafe = safeSites.some((site) => normalizeUrl(site) === currentUrl); + let isUnsafe = unsafeSites.some((site) => + currentUrl.includes(normalizeUrl(site)) + ); + let isPotentiallyUnsafe = potentiallyUnsafeSites.some((site) => + currentUrl.includes(normalizeUrl(site)) + ); + + // Return appropriate status to the popup + if (isStarred) { + sendResponse({ status: "starred", url: currentUrl }); + } else if (isSafe) { + sendResponse({ status: "safe", url: currentUrl }); + } else if (isUnsafe) { + sendResponse({ status: "unsafe", url: currentUrl }); + } else if (isPotentiallyUnsafe) { + sendResponse({ status: "potentially_unsafe", url: currentUrl }); + } else { + sendResponse({ status: "no_data", url: currentUrl }); + } + } else { + console.error("Unknown action:", message.action); + sendResponse({ status: "error", url: message.url }); + } + + return true; +}); + +// Listen for tab updates +browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { + if (changeInfo.status === "complete" && tab.url) { + checkSiteAndUpdatePageAction(tabId, tab.url); + } +}); + +// Listen for tab activation +browser.tabs.onActivated.addListener((activeInfo) => { + browser.tabs.get(activeInfo.tabId, (tab) => { + if (tab.url) { + checkSiteAndUpdatePageAction(tab.id, tab.url); + } + }); +}); + +// Initialize the extension +async function initializeExtension() { + // Fetch all necessary lists + await fetchFilterLists(); + await fetchSafeSites(); + await fetchStarredSites(); + + console.log("Extension initialized successfully."); +} + +// Initialize everything once the extension is loaded +initializeExtension(); diff --git a/platform/firefox/pub/index.html b/platform/firefox/pub/index.html new file mode 100644 index 0000000..b65198e --- /dev/null +++ b/platform/firefox/pub/index.html @@ -0,0 +1,124 @@ + + + + + + FMHY SafeGuard + + + + + + + +
+

FMHY SafeGuard

+
+ +
+ + Status Icon + +

Checking site status...

+
+ + +

+ + +
+

Think this is a mistake? Let us know.

+
+ + + + + + diff --git a/platform/firefox/pub/index.js b/platform/firefox/pub/index.js new file mode 100644 index 0000000..42fd595 --- /dev/null +++ b/platform/firefox/pub/index.js @@ -0,0 +1,135 @@ +document.addEventListener("DOMContentLoaded", async () => { + console.log("Popup loaded, preparing to check site status..."); + + const statusIcon = document.getElementById("status-icon"); + const statusMessage = document.getElementById("status-message"); + const errorMessage = document.getElementById("error-message"); + + // Helper function to normalize URLs (removes trailing slashes) + const normalizeUrl = (url) => url.replace(/\/+$/, "").trim(); + + // Helper function to extract the root domain from a URL + function extractRootDomain(url) { + let urlObj = new URL(url); + return `${urlObj.protocol}//${urlObj.hostname}`; + } + + try { + // Get the active tab's URL + const [activeTab] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + + if (!activeTab || !activeTab.url) { + throw new Error("No active tab found or URL is unavailable."); + } + + const currentUrl = normalizeUrl(activeTab.url); + const rootDomain = extractRootDomain(currentUrl); + console.log(`Active tab URL: ${currentUrl}, Root domain: ${rootDomain}`); + + // Send a message to the background script to check the site's status + const response = await browser.runtime.sendMessage({ + action: "checkSiteStatus", + url: currentUrl, + }); + + if (!response || !response.status) { + throw new Error( + "Failed to retrieve site status from the background script." + ); + } + + // Determine if the root domain or the full URL is marked as safe/starred + const isRootDomainMarked = await browser.runtime.sendMessage({ + action: "checkSiteStatus", + url: rootDomain, + }); + + // Handle different site statuses and update the UI accordingly + if ( + isRootDomainMarked.status === response.status && + response.status !== "no_data" + ) { + // If both the root domain and the current URL share the same status, show the root domain in the message + handleStatusUpdate(response.status, rootDomain); + } else { + // Otherwise, show the current URL in the message + handleStatusUpdate(response.status, currentUrl); + } + } catch (error) { + console.error("Error while checking site status:", error); + errorMessage.textContent = `Error: ${error.message}`; + updateUI("error", "An error occurred while retrieving the site status."); + } + + /** + * Updates the UI based on the site status + * @param {string} status - The status of the site (e.g., "safe", "unsafe") + * @param {string} displayUrl - The URL or root domain to display in the message + */ + function handleStatusUpdate(status, displayUrl) { + switch (status) { + case "unsafe": + updateUI( + "unsafe", + `${displayUrl} is flagged as unsafe. Be cautious when interacting with this site.` + ); + break; + case "potentially_unsafe": + updateUI( + "potentially_unsafe", + `${displayUrl} is potentially unsafe. Proceed with caution.` + ); + break; + case "safe": + updateUI("safe", `${displayUrl} is safe to browse.`); + break; + case "starred": + updateUI( + "starred", + `${displayUrl} is a starred site.` + ); + break; + case "no_data": + updateUI( + "no_data", + `No data available for ${displayUrl}.` + ); + break; + default: + updateUI( + "unknown", + `An unknown status was received for ${displayUrl}.` + ); + } + } + + /** + * Updates the UI with the appropriate icon, message, and effects. + * @param {string} status - The status of the site (e.g., "safe", "unsafe"). + * @param {string} message - The message to display to the user. + */ + function updateUI(status, message) { + const icons = { + unsafe: "../res/icons/unsafe.png", + potentially_unsafe: "../res/icons/potentially_unsafe.png", + safe: "../res/icons/safe.png", + starred: "../res/icons/starred.png", + no_data: "../res/ext_icon_144.png", + error: "../res/icons/error.png", + unknown: "../res/ext_icon_144.png", + }; + + // Update the icon and message + statusIcon.src = icons[status] || icons["unknown"]; + statusMessage.innerHTML = message || "An unknown error occurred."; + + // Add a small animation when the status changes + statusIcon.classList.add("active"); + setTimeout(() => statusIcon.classList.remove("active"), 300); + + console.log(`UI updated: ${message}`); + } +}); diff --git a/platform/firefox/res/ext_icon_144.png b/platform/firefox/res/ext_icon_144.png new file mode 100644 index 0000000..099c350 Binary files /dev/null and b/platform/firefox/res/ext_icon_144.png differ diff --git a/platform/firefox/res/icons/error.png b/platform/firefox/res/icons/error.png new file mode 100644 index 0000000..38a27e8 Binary files /dev/null and b/platform/firefox/res/icons/error.png differ diff --git a/platform/firefox/res/icons/potentially_unsafe.png b/platform/firefox/res/icons/potentially_unsafe.png new file mode 100644 index 0000000..3c88816 Binary files /dev/null and b/platform/firefox/res/icons/potentially_unsafe.png differ diff --git a/platform/firefox/res/icons/safe.png b/platform/firefox/res/icons/safe.png new file mode 100644 index 0000000..2e91dbc Binary files /dev/null and b/platform/firefox/res/icons/safe.png differ diff --git a/platform/firefox/res/icons/starred.png b/platform/firefox/res/icons/starred.png new file mode 100644 index 0000000..fd68271 Binary files /dev/null and b/platform/firefox/res/icons/starred.png differ diff --git a/platform/firefox/res/icons/unsafe.png b/platform/firefox/res/icons/unsafe.png new file mode 100644 index 0000000..1abdf95 Binary files /dev/null and b/platform/firefox/res/icons/unsafe.png differ