Add files via upload

This commit is contained in:
Kenneth Hendricks 2024-10-21 19:08:50 -04:00 committed by GitHub
parent a31417670b
commit 290d0ad57a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 507 additions and 0 deletions

View file

@ -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 = /<A HREF="(https?:\/\/[^\s"]+)"/g;
let matches;
const urls = [];
while ((matches = urlRegex.exec(html)) !== null) {
urls.push(matches[1]);
}
return urls;
}
// Helper function to normalize URLs (removes trailing slashes and "www.")
function normalizeUrl(url) {
return url.replace(/\/+$/, "").replace(/^https?:\/\/www\./, "https://"); // Remove trailing slash if exists and "www." if present
}
// Fetch the unsafe and potentially unsafe filter lists
async function fetchFilterLists() {
console.log("Fetching filter lists...");
try {
const [unsafeResponse, potentiallyUnsafeResponse] = await Promise.all([
fetch(filterListURLUnsafe),
fetch(filterListURLPotentiallyUnsafe),
]);
if (unsafeResponse.ok) {
const unsafeText = await unsafeResponse.text();
unsafeSites = unsafeText
.split("\n")
.filter((line) => 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();

View file

@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FMHY SafeGuard</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap" rel="stylesheet">
<style>
body {
text-align: center;
background-color: rgb(26, 26, 26);
font-family: "Inter", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
color: #848a94;
margin: 0;
padding: 10px;
max-width: 320px; /* Limit the width to make it more compact */
}
.title h1 {
font-size: 24px;
background: -webkit-linear-gradient(120deg, #c4b5fd 30%, #7bc5e4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin: 0;
padding: 10px;
}
#status-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 10px;
}
.status-image {
width: 60px; /* Reduced icon size */
height: 60px;
transition: transform 0.3s ease;
}
#status-message {
font-size: 14px; /* Smaller text for compact design */
margin-top: 10px;
color: #E8E8E8;
line-height: 1.3;
padding: 0 10px;
}
#status-message strong {
color: #c4b5fd;
}
#error-message {
color: red;
margin-top: 10px;
}
#footer {
font-size: 11px; /* Smaller footer text */
margin-top: 10px;
color: #848a94;
}
#footer a {
color: #78b3e2;
text-decoration: none;
}
#footer a:hover {
text-decoration: underline;
}
#feedback {
font-size: 12px;
margin-top: 10px;
color: #848a94;
}
#feedback a {
color: #78b3e2;
text-decoration: none;
}
#feedback a:hover {
text-decoration: underline;
}
/* Animation for status change */
.status-image.active {
transform: scale(1.1);
}
</style>
</head>
<body>
<div class="title">
<h1>FMHY SafeGuard</h1>
</div>
<div id="status-container">
<!-- The status icon will be dynamically updated by the JS -->
<img id="status-icon" class="status-image" src="safe.png" alt="Status Icon">
<!-- The status message will also be updated -->
<p id="status-message">Checking site status...</p>
</div>
<!-- An area for error messages, if necessary -->
<p id="error-message"></p>
<!-- Feedback link for users to report mistakes -->
<div id="feedback">
<p>Think this is a mistake? <a href="https://github.com/fmhy/FMHYFilterlist/issues" target="_blank">Let us know</a>.</p>
</div>
<div id="footer">
<p>Powered by <a href="https://github.com/fmhy/FMHYFilterlist" target="_blank">FMHY Filterlist</a></p>
</div>
<script src="index.js"></script>
</body>
</html>

View file

@ -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 <strong>unsafe</strong>. Be cautious when interacting with this site.`
);
break;
case "potentially_unsafe":
updateUI(
"potentially_unsafe",
`${displayUrl} is <strong>potentially unsafe</strong>. Proceed with caution.`
);
break;
case "safe":
updateUI("safe", `${displayUrl} is <strong>safe</strong> to browse.`);
break;
case "starred":
updateUI(
"starred",
`${displayUrl} is a <strong>starred</strong> site.`
);
break;
case "no_data":
updateUI(
"no_data",
`No data available for <strong>${displayUrl}</strong>.`
);
break;
default:
updateUI(
"unknown",
`An unknown status was received for <strong>${displayUrl}</strong>.`
);
}
}
/**
* 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}`);
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB