244 lines
8.3 KiB
C++
244 lines
8.3 KiB
C++
/*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*
|
|
* 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 <fstream>
|
|
#include <iostream>
|
|
#include <nlohmann/json.hpp>
|
|
#include <filesystem>
|
|
#include <regex>
|
|
#include <cstdlib>
|
|
#include <glibmm.h>
|
|
#include <glibmm/i18n.h>
|
|
|
|
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] → Apertura nel browser predefinito.\n");
|
|
const char* wayland = std::getenv("WAYLAND_DISPLAY");
|
|
std::string cmd;
|
|
|
|
if (wayland && *wayland && std::system("command -v gio > /dev/null 2>&1") == 0)
|
|
cmd = "gio open \"" + url + "\"";
|
|
else
|
|
cmd = "xdg-open \"" + url + "\"";
|
|
|
|
std::system(cmd.c_str());
|
|
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<std::string> 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<std::string>()));
|
|
else if (item.contains("value"))
|
|
domains.push_back(clean_domain(item["value"].get<std::string>()));
|
|
}
|
|
} 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<std::string>()));
|
|
else if (b.is_string())
|
|
domains.push_back(clean_domain(b.get<std::string>()));
|
|
}
|
|
}
|
|
};
|
|
|
|
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<std::string> 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<std::string>());
|
|
else if (item.is_object() && item.contains("value") && item["value"].is_string())
|
|
push(item["value"].get<std::string>());
|
|
}
|
|
} else if (j.contains("domains") && j["domains"].is_array()) {
|
|
for (auto& item : j["domains"]) if (item.is_string()) push(item.get<std::string>());
|
|
} else if (j.contains("whitelist") && j["whitelist"].is_array()) {
|
|
for (auto& item : j["whitelist"]) if (item.is_string()) push(item.get<std::string>());
|
|
}
|
|
} else if (j.is_array()) {
|
|
for (auto& v : j) {
|
|
if (v.is_string()) push(v.get<std::string>());
|
|
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<std::string>());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
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;
|
|
}
|