"use strict"; const NATIVE_HOST_ID = "it.codelinsoft.bastionguard.mail"; let configCache = null; let configCacheTime = 0; const CONFIG_CACHE_TTL = 60000; function t(key, substitutions = []) { return browser.i18n.getMessage(key, substitutions) || key; } async function ensureDefaultConfig() { try { const resp = await browser.runtime.sendNativeMessage(NATIVE_HOST_ID, { type: "get-config" }); if (resp?.ok && resp.config) { await browser.storage.local.set({ mailConfig: resp.config }); configCache = resp.config; configCacheTime = Date.now(); console.log("[BastionGuard]", t("config_loaded_native"), resp.config.profiles?.length ?? 0); return; } console.warn( "[BastionGuard]", t("config_failed"), resp?.error || resp?.error_code || "native host returned no config" ); } catch (e) { console.warn("[BastionGuard]", t("native_error"), e); } } // ============================================================ // Carica configurazione da native host // fallback: storage.local come cache // ============================================================ async function loadConfig() { const now = Date.now(); if (configCache && (now - configCacheTime) < CONFIG_CACHE_TTL) { return configCache; } try { const resp = await browser.runtime.sendNativeMessage(NATIVE_HOST_ID, { type: "get-config" }); if (resp?.ok && resp.config) { configCache = resp.config; configCacheTime = Date.now(); try { await browser.storage.local.set({ mailConfig: resp.config }); } catch (storageSetErr) { console.warn("[BastionGuard]", t("storage_error"), storageSetErr); } console.log("[BastionGuard]", t("config_loaded_native"), resp.config.profiles?.length ?? 0); return configCache; } console.warn( "[BastionGuard]", t("config_failed"), resp?.error || resp?.error_code || "native host returned no config" ); } catch (e) { console.warn("[BastionGuard]", t("native_error"), e); } try { const result = await browser.storage.local.get("mailConfig"); const cfg = result?.mailConfig || null; if (cfg && typeof cfg === "object") { configCache = cfg; configCacheTime = Date.now(); console.warn("[BastionGuard]", t("config_loaded_fallback"), cfg.profiles?.length ?? 0); return configCache; } } catch (e) { console.warn("[BastionGuard]", t("storage_error"), e); } return null; } // ============================================================ // Scansione email via native host // ============================================================ async function scanEmail(body, links, attachments = []) { try { const resp = await browser.runtime.sendNativeMessage(NATIVE_HOST_ID, { type: "scan-email", body, links, attachments }); return resp; } catch (e) { console.warn("[BastionGuard]", t("scan_error"), e); return { ok: false, clean: true, threats: [], phishing_links: [] }; } } // ============================================================ // Legge allegati dalla finestra di composizione // ============================================================ async function getComposeAttachments(tabId) { const out = []; try { const attachments = await browser.compose.listAttachments(tabId); for (const att of attachments) { try { const file = await browser.compose.getAttachmentFile(att.id); const buffer = await file.arrayBuffer(); const bytes = new Uint8Array(buffer); let binary = ""; for (let i = 0; i < bytes.length; i++) { binary += String.fromCharCode(bytes[i]); } out.push({ name: file.name || att.name || "attachment", content_type: file.type || "application/octet-stream", size: file.size || bytes.length, content_b64: btoa(binary) }); } catch (e) { console.warn("[BastionGuard] attachment read failed:", att?.name, e); } } } catch (e) { console.warn("[BastionGuard] listAttachments failed:", e); } return out; } // ============================================================ // Trova profilo SMTP // ============================================================ function findProfile(config, fromAddress) { if (!config?.profiles?.length) return null; const from = (fromAddress || "").toLowerCase().trim(); const fromDomain = from.includes("@") ? from.split("@")[1] : ""; for (const p of config.profiles) { if (p.match_from?.some(a => a.toLowerCase().trim() === from)) return p; if (p.match_from_domain?.some(d => d.toLowerCase().trim() === fromDomain)) return p; } if (config.default_profile_id) { const def = config.profiles.find(p => p.id === config.default_profile_id); if (def) return def; } return config.profiles[0] || null; } // ============================================================ // Estrai link // ============================================================ function extractLinks(text) { const urls = []; const re = /https?:\/\/[^\s"'<>]+/g; let m; while ((m = re.exec(text || "")) !== null) { let url = m[0].replace(/[.,;)]+$/, ""); if (!urls.includes(url)) { urls.push(url); } if (urls.length >= 10) { break; } } return urls; } // ============================================================ // Normalizzazione minacce / phishing // ============================================================ function formatThreat(threat) { if (!threat) return t("unknown_threat"); if (typeof threat === "string") { return threat; } if (typeof threat === "object") { const engine = threat.engine ? String(threat.engine).toUpperCase() : "ENGINE"; const source = threat.source === "attachment" && threat.attachment_name ? ` [${threat.attachment_name}]` : ""; const name = threat.name || threat.code || t("unknown_threat"); return `${engine}${source}: ${name}`; } return t("unknown_threat"); } function formatPhishingLabel(link) { if (!link) return "UNKNOWN"; if (link.classification) { return String(link.classification); } if (link.classification_code) { return String(link.classification_code).toUpperCase(); } return "UNKNOWN"; } function formatPhishingSeverity(link) { if (!link?.severity && !link?.severity_code) return ""; const sev = String(link.severity || link.severity_code).toUpperCase(); return ` (${sev})`; } // ============================================================ // Rimozione banner precedente // ============================================================ function stripExistingWarningBannerHtml(body) { if (!body) return body; return body.replace( /
| `;
if (name) html += `${name} `; if (title) html += `${title} `; if (company) html += `${company} `; const contacts = []; if (phone) contacts.push(`📞 ${phone}`); if (email) contacts.push(`✉ ${email}`); if (website) contacts.push(`🌐 ${website}`); if (contacts.length) { html += `${contacts.join(" ")} `; } html += `${t("scanned_by")}`; html += ` |
${l.url} — ${formatPhishingLabel(l)}${formatPhishingSeverity(l)}