diff --git a/src/js/background.js b/src/js/background.js index 661b6ac..29445e9 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -50,7 +50,9 @@ const notesBaseURL = // State Variables let unsafeSitesRegex = null; +let unsafeHostnamesRegex = null; // Domain-only regex for unsafe sites let potentiallyUnsafeSitesRegex = null; +let potentiallyUnsafeHostnamesRegex = null; // Domain-only regex for potentially unsafe sites let fmhySitesRegex = null; let fmhyHostnamesRegex = null; // Domain-only regex for FMHY sites let safeSites = []; @@ -108,7 +110,7 @@ const notesMapping = { // Crystal Disk Info "crystalmark.info": "crystaldiskinfo", // CS.RIN.RU - "cs.rin.ru": "csrin-search", + "cs.rin.ru": "csrin-search", "csrin.org": "csrin-search", // DODI Repacks "dodi-repacks.site": "dodi-warning", // FileBin @@ -237,6 +239,24 @@ const notesPatterns = [ { pattern: /^rgshows\./i, noteSlug: "rgshows-autoplay" }, ]; +// Hardcoded site passwords - Maps domains to their passwords +const sitePasswords = { + "cs.rin.ru": "cs.rin.ru", + "csrin.org": "csrin.org", + "online-fix.me": "online-fix.me", + "ovagames.com": "www.ovagames.com", + "g4u.to": "404", + "elenemigos.com": "elenemigos.com", + "triahgames.com": "www.triahgames.com", + "soft98.ir": "soft98.ir", +}; + +// Get password for a domain +function getPasswordForDomain(hostname) { + const domain = hostname.replace(/^www\./, "").toLowerCase(); + return sitePasswords[domain] || null; +} + // Get note slug for a domain function getNoteSlugForDomain(hostname) { const domain = hostname.replace(/^www\./, "").toLowerCase(); @@ -442,6 +462,9 @@ async function fetchFilterLists() { const unsafeText = await unsafeResponse.text(); unsafeSites = extractUrlsFromFilterList(unsafeText); unsafeSitesRegex = generateRegexFromList(unsafeSites); + // Also generate hostname-only regex for domain-level matching + const unsafeHostnames = extractHostnamesFromUrls(unsafeSites); + unsafeHostnamesRegex = generateRegexFromList(unsafeHostnames); } if (potentiallyUnsafeResponse.ok) { @@ -450,6 +473,9 @@ async function fetchFilterLists() { potentiallyUnsafeSitesRegex = generateRegexFromList( potentiallyUnsafeSites ); + // Also generate hostname-only regex for domain-level matching + const potentiallyUnsafeHostnames = extractHostnamesFromUrls(potentiallyUnsafeSites); + potentiallyUnsafeHostnamesRegex = generateRegexFromList(potentiallyUnsafeHostnames); } if (fmhyResponse.ok) { @@ -839,11 +865,11 @@ browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => { if (status === "no_data" && !isRepoSite) { console.log(`No match for full URL, trying domain: ${domain}`); - // Check domain against regex patterns - if (unsafeSitesRegex?.test(domain)) { + // Check domain against regex patterns (use hostname-only regex for domain matching) + if (unsafeHostnamesRegex?.test(domain)) { status = "unsafe"; matchedUrl = `https://${domain}`; - } else if (potentiallyUnsafeSitesRegex?.test(domain)) { + } else if (potentiallyUnsafeHostnamesRegex?.test(domain)) { status = "potentially_unsafe"; matchedUrl = `https://${domain}`; } else if (fmhyHostnamesRegex?.test(domain)) { @@ -889,10 +915,13 @@ browserAPI.runtime.onMessage.addListener((message, sender, sendResponse) => { reason = await getReasonForDomain(domain); } + // Get password if available + const password = getPasswordForDomain(domain); + console.log( `getSiteStatus result for ${url}: ${status}, matched: ${matchedUrl}` ); - sendResponse({ status: status, matchedUrl: matchedUrl, reason: reason }); + sendResponse({ status: status, matchedUrl: matchedUrl, reason: reason, password: password }); } catch (error) { console.error("Error in getSiteStatus handler:", error); sendResponse({ @@ -978,9 +1007,9 @@ function getStatusFromLists(url) { if (starredSites.includes(url)) return "starred"; if (safeSites.includes(url)) return "safe"; - // Then check domain-level - if (unsafeSitesRegex?.test(domain)) return "unsafe"; - if (potentiallyUnsafeSitesRegex?.test(domain)) return "potentially_unsafe"; + // Then check domain-level (use hostname-only regex for domain matching) + if (unsafeHostnamesRegex?.test(domain)) return "unsafe"; + if (potentiallyUnsafeHostnamesRegex?.test(domain)) return "potentially_unsafe"; if (fmhyHostnamesRegex?.test(domain)) return "fmhy"; // Try domain-level checks for starred and safe @@ -1148,6 +1177,9 @@ async function initializeExtension() { if (storedData.unsafeSites && storedData.unsafeSites.length > 0) { unsafeSitesRegex = generateRegexFromList(storedData.unsafeSites); + // Also generate hostname-only regex for domain-level matching + const unsafeHostnames = extractHostnamesFromUrls(storedData.unsafeSites); + unsafeHostnamesRegex = generateRegexFromList(unsafeHostnames); } if ( @@ -1157,6 +1189,9 @@ async function initializeExtension() { potentiallyUnsafeSitesRegex = generateRegexFromList( storedData.potentiallyUnsafeSites ); + // Also generate hostname-only regex for domain-level matching + const potentiallyUnsafeHostnames = extractHostnamesFromUrls(storedData.potentiallyUnsafeSites); + potentiallyUnsafeHostnamesRegex = generateRegexFromList(potentiallyUnsafeHostnames); } if (storedData.fmhySites && storedData.fmhySites.length > 0) { diff --git a/src/pub/index.html b/src/pub/index.html index 362a439..4d0c915 100644 --- a/src/pub/index.html +++ b/src/pub/index.html @@ -182,6 +182,67 @@ transform: scale(1.1); } + /* Password Section Styles */ + #password-container { + margin-top: 15px; + padding: 10px; + background: var(--hover-bg); + border-radius: 8px; + text-align: left; + display: none; + } + + #password-container.visible { + display: block; + } + + #password-title { + font-size: 13px; + font-weight: 600; + color: #4ecdc4; + margin-bottom: 8px; + display: flex; + align-items: center; + gap: 6px; + } + + #password-title svg { + stroke: #4ecdc4; + } + + #password-content { + font-size: 14px; + font-weight: 600; + font-family: monospace; + color: var(--text-color); + background: var(--bg-color); + padding: 6px 10px; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + } + + #password-content:hover { + background: var(--hover-bg); + } + + #password-content .copy-icon { + opacity: 0.5; + flex-shrink: 0; + } + + #password-content:hover .copy-icon { + opacity: 1; + } + + #password-content.copied { + background: #4ecdc4; + color: #000; + } + /* Reason Section Styles */ #reason-container { margin-top: 15px; @@ -286,6 +347,13 @@ border-radius: 3px; font-size: 11px; } + + #note-content img { + max-width: 100%; + height: auto; + border-radius: 4px; + margin: 8px 0; + } @@ -306,6 +374,22 @@

Checking site status...

+
+
+ + + + + Password +
+
+ + + + + +
+
diff --git a/src/pub/index.js b/src/pub/index.js index a6393cd..cc6cf44 100644 --- a/src/pub/index.js +++ b/src/pub/index.js @@ -56,11 +56,28 @@ document.addEventListener("DOMContentLoaded", async () => { function parseMarkdown(md) { if (!md) return ""; + // Store images to protect from URL linking + const imgTags = []; + let result = md // Remove the main header (#### Title) since we show "FMHY Note" already .replace(/^#{1,4}\s+.*$/gm, '') // Trim leading/trailing whitespace - .trim() + .trim(); + + // Protect HTML img tags from URL linking + result = result.replace(/]*>/gi, (match) => { + imgTags.push(match); + return `[[HTMLIMG_${imgTags.length - 1}]]`; + }); + + // Convert markdown images ![alt](url) to placeholder + result = result.replace(/!\[(.*?)\]\((.*?)\)/g, (match, alt, url) => { + imgTags.push(`${alt}`); + return `[[HTMLIMG_${imgTags.length - 1}]]`; + }); + + result = result // Bold (must come before italic) .replace(/\*\*(.*?)\*\*/g, '$1') // Italic @@ -68,11 +85,14 @@ document.addEventListener("DOMContentLoaded", async () => { // Markdown links [text](url) - use placeholder to avoid double-linking .replace(/\[(.*?)\]\((.*?)\)/g, '[[LINK:$2:$1]]'); - // Raw URLs (convert before restoring markdown links) + // Raw URLs (convert before restoring markdown links and images) result = result.replace(/(https?:\/\/[^\s<>\)\]]+)/g, '$1'); // Restore markdown links from placeholders result = result.replace(/\[\[LINK:(.*?):(.*?)\]\]/g, '$2'); + + // Restore images from placeholders + result = result.replace(/\[\[HTMLIMG_(\d+)\]\]/g, (match, index) => imgTags[parseInt(index)]); result = result // Code @@ -287,7 +307,7 @@ document.addEventListener("DOMContentLoaded", async () => { } // Update the popup with the result - handleStatusUpdate(response.status, displayUrl, response.reason); + handleStatusUpdate(response.status, displayUrl, response.reason, response.password); } catch (error) { console.error("Error checking site status:", error); errorMessage.textContent = `Error: ${error.message}`; @@ -295,7 +315,7 @@ document.addEventListener("DOMContentLoaded", async () => { } } - function handleStatusUpdate(status, displayUrl, reason) { + function handleStatusUpdate(status, displayUrl, reason, password) { let message; // Handle reason display in dedicated container @@ -309,6 +329,27 @@ document.addEventListener("DOMContentLoaded", async () => { reasonContainer.classList.remove("visible"); } + // Handle password display + const passwordContainer = document.getElementById("password-container"); + const passwordText = document.getElementById("password-text"); + const passwordContent = document.getElementById("password-content"); + if (password && passwordContainer && passwordText) { + passwordText.textContent = password; + passwordContainer.classList.add("visible"); + // Add click-to-copy functionality + passwordContent.onclick = async () => { + try { + await navigator.clipboard.writeText(password); + passwordContent.classList.add("copied"); + setTimeout(() => passwordContent.classList.remove("copied"), 1000); + } catch (err) { + console.error("Failed to copy password:", err); + } + }; + } else if (passwordContainer) { + passwordContainer.classList.remove("visible"); + } + // Use i18n for status messages if available const getMessage = window.i18n ? window.i18n.getMessage : (key, sub) => null;