/* * BastionGuard™ * Copyright (C) 2025–2026 Calogero Scarnà * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * BastionGuard™ is a trademark of Calogero Scarnà. * The BastionGuard™ name and branding are not licensed under the GPL. */ #include "xdg-open.hpp" #include "SecureBrowser.hpp" #include "SettingsStore.hpp" #include #include #include #include #include #include #include #include namespace fs = std::filesystem; using json = nlohmann::json; bool BankOpener::open_if_trusted(const std::string& url) { if (url.empty()) { std::cerr << _("[BankOpener] ⚠️ URL vuoto.\n"); return false; } std::string domain = extract_domain(url); if (domain.empty()) { std::cerr << _("[BankOpener] ❌ Impossibile estrarre dominio da: ") << url << "\n"; return false; } const bool trusted_bank = is_trusted_domain(domain); const bool trusted_pay = is_payment_domain(domain); if (trusted_bank || trusted_pay) { std::cout << _("[BankOpener] 🧩 Dominio sicuro rilevato: ") << domain << (trusted_bank ? " (bank)" : " (payments)") << "\n"; std::cout << _("[BankOpener] 🔐 Avvio sandbox CEF (Blink)...\n"); if (!SecureBrowser::open(url, 0, nullptr)) { std::cerr << _("[BankOpener] ❌ Sandbox non avviata.\n"); return false; } return true; } std::cout << _("[BankOpener] 🌐 Dominio non in lista sicura: ") << domain << "\n"; std::cout << _("[BankOpener] → Delego l'apertura sicura al chiamante.\n"); return false; } std::string BankOpener::extract_domain(const std::string& url) { std::regex re(R"(^(?:https?:\/\/)?(?:www\.)?([^\/:]+))", std::regex::icase); std::smatch match; if (std::regex_search(url, match, re)) { std::string domain = match[1].str(); if (domain.rfind("www.", 0) == 0) domain = domain.substr(4); std::transform(domain.begin(), domain.end(), domain.begin(), ::tolower); return domain; } return ""; } bool BankOpener::ends_with(const std::string& s, const std::string& suffix) { if (suffix.size() > s.size()) return false; return std::equal( suffix.rbegin(), suffix.rend(), s.rbegin(), [](char a, char b) { return std::tolower(a) == std::tolower(b); } ); } bool BankOpener::is_trusted_domain(const std::string& domain) { namespace fs = std::filesystem; std::string path_user = Glib::get_home_dir() + "/.local/share/BastionGuard/banks.json"; std::string path_system = "/usr/share/BastionGuard/data/bank/banks.json"; std::string path_whitelist = Glib::get_home_dir() + "/.config/BastionGuard/whitelist.json"; std::vector domains; auto load_domains = [&](const std::string& path) { if (!fs::exists(path)) return; std::ifstream f(path); if (!f.is_open()) { std::cerr << _("[BankOpener] ⚠️ Impossibile aprire ") << path << "\n"; return; } json j; try { f >> j; } catch (...) { std::cerr << _("[BankOpener] ⚠️ Errore parsing JSON in ") << path << "\n"; return; } if (j.contains("list") && j["list"].is_array()) { for (auto& item : j["list"]) { if (item.is_string()) domains.push_back(clean_domain(item.get())); else if (item.contains("value")) domains.push_back(clean_domain(item["value"].get())); } } else if (j.is_array()) { for (auto& b : j) { if (b.is_object() && b.contains("domains") && b["domains"].is_array() && !b["domains"].empty()) domains.push_back(clean_domain(b["domains"][0].get())); else if (b.is_string()) domains.push_back(clean_domain(b.get())); } } }; load_domains(path_user); load_domains(path_system); load_domains(path_whitelist); if (domains.empty()) { std::cerr << _("[BankOpener] ⚠️ Nessun dominio caricato da liste.\n"); return false; } std::string dnorm = clean_domain(domain); for (auto& d : domains) { if (dnorm == d || ends_with(dnorm, "." + d) || ends_with(d, "." + dnorm)) return true; } return false; } bool BankOpener::is_payment_domain(const std::string& domain) { // Attivo solo se impostazione ON if (!SettingsStore::get_secure_payments_enabled()) return false; namespace fs = std::filesystem; std::string path_user = Glib::get_home_dir() + "/.config/BastionGuard/payments.json"; std::string path_system = "/usr/share/BastionGuard/data/payments/payments.json"; static bool initialized = false; static std::vector cached; if (!initialized) { auto load_domains = [&](const std::string& path) { if (!fs::exists(path)) return; std::ifstream f(path); if (!f.is_open()) { std::cerr << _("[BankOpener] ⚠️ Impossibile aprire ") << path << "\n"; return; } json j; try { f >> j; } catch (...) { std::cerr << _("[BankOpener] ⚠️ Errore parsing JSON in ") << path << "\n"; return; } auto push = [&](const std::string& raw) { auto cd = clean_domain(raw); if (!cd.empty()) cached.push_back(cd); }; if (j.is_object()) { if (j.contains("list") && j["list"].is_array()) { for (auto& item : j["list"]) { if (item.is_string()) push(item.get()); else if (item.is_object() && item.contains("value") && item["value"].is_string()) push(item["value"].get()); } } else if (j.contains("domains") && j["domains"].is_array()) { for (auto& item : j["domains"]) if (item.is_string()) push(item.get()); } else if (j.contains("whitelist") && j["whitelist"].is_array()) { for (auto& item : j["whitelist"]) if (item.is_string()) push(item.get()); } } else if (j.is_array()) { for (auto& v : j) { if (v.is_string()) push(v.get()); else if (v.is_object() && v.contains("domains") && v["domains"].is_array() && !v["domains"].empty()) { if (v["domains"][0].is_string()) push(v["domains"][0].get()); } } } }; load_domains(path_system); load_domains(path_user); std::cerr << _("[BankOpener] ✅ Domini pagamenti caricati: ") << cached.size() << "\n"; initialized = true; } if (cached.empty()) return false; std::string dnorm = clean_domain(domain); for (auto& d : cached) { if (dnorm == d || ends_with(dnorm, "." + d) || ends_with(d, "." + dnorm)) return true; } return false; } std::string BankOpener::clean_domain(const std::string& d) { std::string s = d; if (!s.empty() && s[0] == '.') s = s.substr(1); if (s.rfind("www.", 0) == 0) s = s.substr(4); std::transform(s.begin(), s.end(), s.begin(), ::tolower); return s; }