import { configDotenv } from "dotenv"; import crypto from "crypto"; import { parse } from "tldts"; configDotenv(); const hashKey = Buffer.from(process.env.HASH_KEY || "", "utf-8"); const algorithm = "aes-256-ecb"; export function encryptAES(plaintext: string, key: Buffer): string { const cipher = crypto.createCipheriv(algorithm, key, null); const encrypted = Buffer.concat([ cipher.update(plaintext, "utf-8"), cipher.final(), ]); return encrypted.toString("base64"); } export function decryptAES(ciphertext: string, key: Buffer): string { const decipher = crypto.createDecipheriv(algorithm, key, null); const decrypted = Buffer.concat([ decipher.update(Buffer.from(ciphertext, "base64")), decipher.final(), ]); return decrypted.toString("utf-8"); } const urlBlocklist = [ "h8ngAFXUNLO3ZqQufJjGVA==", "fEGiDm/TWDBkXUXejFVICg==", "l6Mei7IGbEmTTFoSudUnqQ==", "4OjallJzXRiZUAWDiC2Xww==", "ReSvkSfx34TNEdecmmSDdQ==", "X1E4WtdmXAv3SAX9xN925Q==", "VTzBQfMtXZzM05mnNkWkjA==", "m/q4Lb2Z8cxwU7/CoztOFg==", "UbVnmRaeG+gKcyVDLAm0vg==", "xNQhczYG22tTVc6lYE3qwg==", "CQfGDydbg4l1swRCru6O6Q==", "l86LQxm2NonTWMauXwEsPw==", "6v4QDUcwjnID80G+uU+tgw==", "pCF/6nrKZAxaYntzEGluZQ==", "r0CRhAmQqSe7V2s3073T00sAh4WcS5779jwuGJ26ows==", "aBOVqRFBM4UVg33usY10NdiF0HCnFH/ImtD0n+zIpc8==", "QV436UZuQ6D0Dqrx9MwaGw==", "OYVvrwILYbzA2mSSqOPPpw==", "xW2i4C0Dzcnp+qu12u0SAw==", "OLHba209l0dfl0MI4EnQonBITK9z8Qwgd/NsuaTkXmA=", "X0VynmNjpL3PrYxpUIG7sFMBt8OlrmQWtxj8oXVu2QM=", "ObdlM5NEkvBJ/sojRW5K/Q==", "C8Th38X0SjsE1vL/OsD8bA==", "PTbGg8PK/h0Seyw4HEpK4Q==", "lZdQMknjHb7+4+sjF3qNTw==", "LsgSq54q5oDysbva29JxnQ==", "KZfBtpwjOpdSoqacRbz7og==", "Indtl4yxJMHCKBGF4KABCQ==", "e3HFXLVgxhaVoadYpwb2BA==", "b+asgLayXQ5Jq+se+q56jA==", "86ZDUI7vmp4MvNq3fvZrGQ==", "sEGFoYZ6GEg4Zocd+TiyfQ==", "6OOL72eXthgnJ1Hj4PfOQQ==", "g/ME+Sh1CAFboKrwkVb+5Q==", "Pw+xawUoX8xBYbX2yqqGWQ==", "k6vBalxYFhAvkPsF19t9gQ==", "b+asgLayXQ5Jq+se+q56jA==", "KKttwRz4w+AMJrZcB828WQ==", "vMdzZ33BXoyWVZnAPOBcrg==", "l8GDVI8w/ueHnNzdN1ODuQ==", "+yz9bnYYMnC0trJZGJwf6Q==", "oTdhIjEjqdT2pEvyxD1Ssg==", ] const allowedKeywords = [ "pulse", "privacy", "terms", "policy", "user-agreement", "legal", "help", "policies", "support", "contact", "about", "careers", "blog", "press", "conditions", "tos", "://library.tiktok.com", "://ads.tiktok.com", "://tiktok.com/business", "://developers.facebook.com", "://facebook.com/ads/library", "://www.facebook.com/ads/library", ]; export function decryptedBlocklist(list: string[]): string[] { return hashKey.length > 0 ? list.map((ciphertext) => decryptAES(ciphertext, hashKey)) : []; } export function isUrlBlocked(url: string): boolean { const lowerCaseUrl = url.trim().toLowerCase(); const blockedlist = decryptedBlocklist(urlBlocklist); const decryptedUrl = blockedlist.find((decrypted) => lowerCaseUrl === decrypted) || lowerCaseUrl; // If the URL is empty or invalid, return false let parsedUrl: any; try { parsedUrl = parse(decryptedUrl); } catch { console.log("Error parsing URL:", url); return false; } const domain = parsedUrl.domain; const publicSuffix = parsedUrl.publicSuffix; if (!domain) { return false; } // Check if URL contains any allowed keyword if ( allowedKeywords.some((keyword) => lowerCaseUrl.includes(keyword.toLowerCase()), ) ) { return false; } // Block exact matches if (blockedlist.includes(domain)) { return true; } // Block subdomains if (blockedlist.some((blocked) => domain.endsWith(`.${blocked}`))) { return true; } // Block different TLDs of the same base domain const baseDomain = domain.split(".")[0]; // Extract the base domain (e.g., "facebook" from "facebook.com") if ( publicSuffix && blockedlist.some( (blocked) => blocked.startsWith(baseDomain + ".") && blocked !== domain, ) ) { return true; } return false; }