Skip to content
API Documentation
BastionGuard™
ScanPage Class Reference

#include <ScanPage.hpp>

Inheritance diagram for ScanPage:

Public Member Functions

 ScanPage (Gtk::Window &parent)
 ~ScanPage () override

Private Member Functions

void enqueueLog (const std::string &msg)
void flushLog ()
void saveCloudConfig ()
void loadCloudConfig ()
void onApiKeySaveClicked ()
void showCloudPrivacyDisclaimer ()
void onToggleApiKeyVisibility ()
void onChooseFolder ()
void onChooseFile ()
void onStartScan ()
void readOutput (const Glib::RefPtr< Gio::AsyncResult > &result)
void addFileToList (const std::string &filepath)
void saveConfig ()
void loadConfig ()
std::vector< std::string > detectBrowserDownloadDirs ()
std::vector< std::string > getXdgUserDirs ()
bool isOutsideKnownDownloadDirs (const std::string &path)
void addInotifyRecursive (const std::string &path)
bool onInotifyEvent (Glib::IOCondition cond)
void handleInotifyFileEvent (const std::string &path, uint32_t mask)
void enableAutoScanDownloads ()
void enableGlobalDownloadMonitor ()
void disableAllMonitors ()
void onChooseFolderFinish (const Glib::RefPtr< Gio::AsyncResult > &result, Glib::RefPtr< Gtk::FileDialog > dialog)
void onChooseFileFinish (const Glib::RefPtr< Gio::AsyncResult > &result, Glib::RefPtr< Gtk::FileDialog > dialog)
void scanFileAutomatically (const std::string &filepath)
bool isInterestingDownload (const std::string &path)
std::string resolveFinalDownloadedFile (const std::string &path)
void onAurPkgbuildDetected (const std::string &path)
void scanAurAuto (const std::string &path)
void cloudCheckMalwareBazaar (const std::string &filepath)
void cloudCheckMalwareBazaarWorker (const std::string &filepath)
void handleInfectedFile (const std::string &filepath)
void handleInfectedFiles (const std::vector< std::string > &files)
void handleInfectedFileWithName (const std::string &filepath, const std::string &virusName)
void showAlertWindow (const std::string &file, const std::string &family)
void logMessage (const std::string &msg)
bool safeExists (const std::string &p) noexcept

Private Attributes

Gtk::Window & parentWindow
Gtk::Box buttonBox {Gtk::Orientation::HORIZONTAL, 5}
Gtk::Button btnChooseFolder {_("Seleziona cartella")}
Gtk::Button btnChooseFile {_("Seleziona file")}
Gtk::Button btnScan {_("Scansiona")}
Gtk::Box autoScanBox {Gtk::Orientation::HORIZONTAL, 6}
Gtk::Label autoScanLabel
Gtk::Switch autoScanSwitch
bool autoScanEnabled = true
Gtk::TextView textView
Glib::RefPtr< Gtk::TextBuffer > textBuffer
Gtk::ListBox fileListBox
Gtk::Box apiKeyActionsBox {Gtk::Orientation::HORIZONTAL, 6}
Glib::RefPtr< Gtk::SizeGroup > apiKeySizeGroup
Glib::RefPtr< Gio::Subprocess > subprocess
Glib::RefPtr< Gio::InputStream > stdoutStream
Glib::RefPtr< Gio::DataInputStream > dataStream
Gtk::Box apiKeyBox {Gtk::Orientation::HORIZONTAL, 6}
Gtk::Entry apiKeyEntry
Gtk::Button btnShowApiKey
Gtk::Button btnApiKeySave {"Salva"}
Gtk::Button btnPrivacy
bool apiKeyVisible = false
std::string malwareBazaarApiKey
std::mutex logMutex
std::vector< std::string > pendingLog
bool logFlushScheduled = false
std::string selectedPath
std::vector< std::string > infectedFilesBuffer
bool scanInProgress = false
std::vector< std::string > knownDownloadDirs
int inotifyFd = -1
Glib::RefPtr< Glib::IOSource > inotifySource
std::unordered_map< int, std::string > watchMap
std::unordered_set< std::string > alreadyWatched
BastionGuard::AliveFlag alive_ = BastionGuard::make_alive_flag()

Detailed Description

Definition at line 37 of file ScanPage.hpp.

Constructor & Destructor Documentation

◆ ScanPage()

ScanPage::ScanPage ( Gtk::Window & parent)
explicit

Definition at line 131 of file ScanPage.cpp.

132: Gtk::Box(Gtk::Orientation::VERTICAL, 5),
133parentWindow(parent)
134{
135 set_margin(8);
136 set_spacing(5);
137
138
139 buttonBox.set_spacing(8);
141 buttonBox.append(btnChooseFile);
142 buttonBox.append(btnScan);
143 append(buttonBox);
144
145 btnChooseFolder.signal_clicked().connect(sigc::mem_fun(*this, &ScanPage::onChooseFolder));
146 btnChooseFile.signal_clicked().connect(sigc::mem_fun(*this, &ScanPage::onChooseFile));
147 btnScan.signal_clicked().connect(sigc::mem_fun(*this, &ScanPage::onStartScan));
148
149 // ===== UI scansione automatica =====
150 autoScanLabel.set_text(_("Scansione automatica"));
151 autoScanBox.set_spacing(8);
154 append(autoScanBox);
155
156 autoScanSwitch.set_active(true);
157 autoScanEnabled = true;
158
159 autoScanSwitch.property_active().signal_changed().connect([this]() {
160 autoScanEnabled = autoScanSwitch.get_active();
161 enqueueLog(autoScanEnabled ? _("⚙️ Scansione automatica attiva")
162 : _("⛔ Scansione automatica disattivata"));
163 if (autoScanEnabled) {
166 } else {
168 }
169 saveConfig();
170 });
171
172 textBuffer = Gtk::TextBuffer::create();
173 textView.set_buffer(textBuffer);
174 textView.set_editable(false);
175 textView.set_wrap_mode(Gtk::WrapMode::WORD_CHAR);
176
177 auto scrollLog = Gtk::make_managed<Gtk::ScrolledWindow>();
178 scrollLog->set_child(textView);
179 scrollLog->set_min_content_height(180);
180 scrollLog->set_expand(true);
181 append(*scrollLog);
182
183
184 auto scrollFiles = Gtk::make_managed<Gtk::ScrolledWindow>();
185 scrollFiles->set_child(fileListBox);
186 scrollFiles->set_min_content_height(150);
187 scrollFiles->set_expand(true);
188 append(*scrollFiles);
189
190
191 apiKeyBox.set_spacing(8);
192 auto lbl = Gtk::make_managed<Gtk::Label>(_("API Key VirusTotal:"));
193 lbl->set_valign(Gtk::Align::CENTER);
194 apiKeyEntry.set_hexpand(true);
195 apiKeyEntry.set_visibility(false);
196 apiKeyBox.append(*lbl);
197 apiKeyBox.append(apiKeyEntry);
198
199 apiKeyBox.set_spacing(8);
200
201
202 lbl->set_valign(Gtk::Align::CENTER);
203
204
205 apiKeyEntry.set_hexpand(true);
206 apiKeyEntry.set_visibility(false);
207 apiKeyEntry.set_valign(Gtk::Align::CENTER);
208
209
210 btnShowApiKey.set_icon_name("view-reveal-symbolic");
211 btnShowApiKey.add_css_class("flat");
212 btnShowApiKey.set_valign(Gtk::Align::CENTER);
213
214
215 btnApiKeySave.set_valign(Gtk::Align::CENTER);
216
217
218 btnPrivacy.set_icon_name("dialog-information-symbolic");
219 btnPrivacy.set_tooltip_text(_("Privacy e dati cloud"));
220 btnPrivacy.add_css_class("flat");
221 btnPrivacy.set_valign(Gtk::Align::CENTER);
222
223 btnShowApiKey.signal_clicked().connect(
224 sigc::mem_fun(*this, &ScanPage::onToggleApiKeyVisibility)
225 );
226
227
228 apiKeyActionsBox.set_valign(Gtk::Align::CENTER);
232
233
234 apiKeySizeGroup = Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::VERTICAL);
235 apiKeySizeGroup->add_widget(apiKeyEntry);
236 apiKeySizeGroup->add_widget(btnShowApiKey);
237 apiKeySizeGroup->add_widget(btnApiKeySave);
238 apiKeySizeGroup->add_widget(btnPrivacy);
239
240
241 apiKeyBox.append(*lbl);
242 apiKeyBox.append(apiKeyEntry);
244
245 append(apiKeyBox);
246
247
248
249 btnPrivacy.set_label(_("Privacy / Dati cloud"));
250 btnPrivacy.set_icon_name("dialog-information");
251 btnPrivacy.set_halign(Gtk::Align::START);
252 btnPrivacy.add_css_class("flat");
253
254 btnPrivacy.signal_clicked().connect(
255 sigc::mem_fun(*this, &ScanPage::showCloudPrivacyDisclaimer)
256 );
257
258
259
260 btnApiKeySave.signal_clicked().connect(sigc::mem_fun(*this, &ScanPage::onApiKeySaveClicked));
261
262 loadConfig();
265
266
267 inotifyFd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
268 if (inotifyFd < 0) {
269 enqueueLog(_("❌ Errore: inotify non inizializzato"));
270 } else {
271 enqueueLog(_("📡 Inotify inizializzato"));
272
273 inotifySource = Glib::IOSource::create(inotifyFd, Glib::IOCondition::IO_IN);
274
275 inotifySource->connect(sigc::mem_fun(*this, &ScanPage::onInotifyEvent));
276 inotifySource->attach(Glib::MainContext::get_default());
277 }
278
279
283}
#define _(s)
void loadCloudApiKeys()
bool onInotifyEvent(Glib::IOCondition cond)
Definition ScanPage.cpp:399
Glib::RefPtr< Glib::IOSource > inotifySource
Definition ScanPage.hpp:117
void enableAutoScanDownloads()
Definition ScanPage.cpp:979
Gtk::Entry apiKeyEntry
Definition ScanPage.hpp:69
Gtk::TextView textView
Definition ScanPage.hpp:56
std::vector< std::string > detectBrowserDownloadDirs()
Definition ScanPage.cpp:895
Gtk::ListBox fileListBox
Definition ScanPage.hpp:59
bool autoScanEnabled
Definition ScanPage.hpp:54
void onStartScan()
Definition ScanPage.cpp:651
Gtk::Button btnScan
Definition ScanPage.hpp:49
Glib::RefPtr< Gtk::SizeGroup > apiKeySizeGroup
Definition ScanPage.hpp:61
void showCloudPrivacyDisclaimer()
Gtk::Switch autoScanSwitch
Definition ScanPage.hpp:53
void onToggleApiKeyVisibility()
Gtk::Box buttonBox
Definition ScanPage.hpp:46
Gtk::Label autoScanLabel
Definition ScanPage.hpp:52
Glib::RefPtr< Gtk::TextBuffer > textBuffer
Definition ScanPage.hpp:57
Gtk::Button btnChooseFolder
Definition ScanPage.hpp:47
void enqueueLog(const std::string &msg)
Definition ScanPage.cpp:286
Gtk::Button btnShowApiKey
Definition ScanPage.hpp:70
void saveConfig()
Definition ScanPage.cpp:314
void enableGlobalDownloadMonitor()
void onChooseFolder()
Definition ScanPage.cpp:431
void onApiKeySaveClicked()
Definition ScanPage.cpp:551
Gtk::Box autoScanBox
Definition ScanPage.hpp:51
Gtk::Button btnApiKeySave
Definition ScanPage.hpp:71
Gtk::Box apiKeyBox
Definition ScanPage.hpp:68
Gtk::Window & parentWindow
Definition ScanPage.hpp:43
int inotifyFd
Definition ScanPage.hpp:116
Gtk::Button btnChooseFile
Definition ScanPage.hpp:48
void disableAllMonitors()
Definition ScanPage.cpp:638
Gtk::Button btnPrivacy
Definition ScanPage.hpp:72
void onChooseFile()
Definition ScanPage.cpp:463
std::vector< std::string > knownDownloadDirs
Definition ScanPage.hpp:110
void loadConfig()
Definition ScanPage.cpp:324
Gtk::Box apiKeyActionsBox
Definition ScanPage.hpp:60
void loadCloudConfig()
Definition ScanPage.cpp:494

References _, apiKeyActionsBox, apiKeyBox, apiKeyEntry, apiKeySizeGroup, autoScanBox, autoScanEnabled, autoScanLabel, autoScanSwitch, btnApiKeySave, btnChooseFile, btnChooseFolder, btnPrivacy, btnScan, btnShowApiKey, buttonBox, detectBrowserDownloadDirs(), disableAllMonitors(), enableAutoScanDownloads(), enableGlobalDownloadMonitor(), enqueueLog(), fileListBox, inotifyFd, inotifySource, knownDownloadDirs, loadCloudApiKeys(), loadCloudConfig(), loadConfig(), onApiKeySaveClicked(), onChooseFile(), onChooseFolder(), onInotifyEvent(), onStartScan(), onToggleApiKeyVisibility(), parentWindow, saveConfig(), showCloudPrivacyDisclaimer(), textBuffer, and textView.

◆ ~ScanPage()

ScanPage::~ScanPage ( )
override

Definition at line 125 of file ScanPage.cpp.

126{
127 if (alive_) alive_->store(false, std::memory_order_release);
128}
BastionGuard::AliveFlag alive_
Definition ScanPage.hpp:166

References alive_.

Member Function Documentation

◆ addFileToList()

void ScanPage::addFileToList ( const std::string & filepath)
private

Definition at line 878 of file ScanPage.cpp.

879{
880 auto row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
881
882 auto icon = Gtk::make_managed<Gtk::Image>();
883 icon->set_from_icon_name("text-x-generic");
884 icon->set_pixel_size(20);
885 row->append(*icon);
886
887 auto lbl = Gtk::make_managed<Gtk::Label>(filepath);
888 lbl->set_hexpand(true);
889 lbl->set_ellipsize(Pango::EllipsizeMode::END);
890 row->append(*lbl);
891
892 fileListBox.append(*row);
893}

References fileListBox.

Referenced by readOutput().

◆ addInotifyRecursive()

void ScanPage::addInotifyRecursive ( const std::string & path)
private

Definition at line 335 of file ScanPage.cpp.

336{
337 namespace fs = std::filesystem;
338
339 if (!fs::exists(root)) return;
340
341 std::string norm = normalizePath(root);
342
343 // Evita duplicati
344 if (alreadyWatched.count(norm)) return;
345
346 // ANTI-BOOTLOOP: se la cartella è una directory di build (CMake, Cargo,
347 // node_modules, .venv, target/, …), NON aggiungere watch. Durante una
348 // compilazione vengono creati migliaia di file in pochi secondi: senza
349 // questo filtro inotify scatena un cascade di eventi che a sua volta
350 // satura il main loop (handleInotifyFileEvent -> clamdscan async ->
351 // ulteriori scritture) → bootloop di ScanPage e degrado del sistema.
352 if (isInBuildDir(norm + "/")) {
353 return;
354 }
355
356 alreadyWatched.insert(norm);
357
358 int wd = inotify_add_watch(
359 inotifyFd,
360 norm.c_str(),
361 IN_CREATE | IN_MOVED_TO | IN_CLOSE_WRITE | IN_ATTRIB
362 );
363
364 if (wd < 0) {
365 enqueueLog(_("⚠️ Impossibile monitorare: ") + norm);
366 return;
367 }
368
369 watchMap[wd] = norm;
370 enqueueLog(_("📁 Watch attivo: ") + norm);
371
372
373 try {
374 for (auto& entry : fs::directory_iterator(norm)) {
375 if (entry.is_directory()) {
376 std::string sub = entry.path().string();
377 // ignora cartelle nascoste
378 std::string name = entry.path().filename().string();
379 if (!name.empty() && name[0] == '.')
380 continue;
381
382 // ANTI-BOOTLOOP: non scendere in directory di build note
383 // (build/, target/, node_modules/, CMakeFiles/, ecc.).
384 // Il check con "/" finale forza isInBuildDir a matchare il
385 // segmento completo.
386 if (isInBuildDir(sub + "/")) {
387 continue;
388 }
389
391 }
392 }
393 } catch (...) {
394
395 }
396}
static std::string normalizePath(std::string p)
Definition ScanPage.cpp:111
static bool isInBuildDir(const std::string &path)
Definition ScanPage.cpp:93
void addInotifyRecursive(const std::string &path)
Definition ScanPage.cpp:335
std::unordered_map< int, std::string > watchMap
Definition ScanPage.hpp:120
std::unordered_set< std::string > alreadyWatched
Definition ScanPage.hpp:123

References _, addInotifyRecursive(), alreadyWatched, enqueueLog(), inotifyFd, isInBuildDir(), normalizePath(), and watchMap.

Referenced by addInotifyRecursive(), enableAutoScanDownloads(), and enableGlobalDownloadMonitor().

◆ cloudCheckMalwareBazaar()

void ScanPage::cloudCheckMalwareBazaar ( const std::string & filepath)
private

Definition at line 1216 of file ScanPage.cpp.

1217{
1218 if (activeCloudChecks.load() >= 4) {
1219 enqueueLog(_("⏳ Troppi controlli cloud in corso, salto temporaneamente."));
1220 return;
1221 }
1222
1223 enqueueLog(_("🌐 Avvio controllo cloud..."));
1225
1226 auto alive = alive_;
1227 std::thread([this, alive, filepath]() {
1228 try {
1230 }
1231 catch (const std::exception& ex) {
1232 if (!alive->load()) { activeCloudChecks--; return; }
1233 Glib::signal_idle().connect_once([this, alive, msg = std::string(ex.what())]() {
1234 BastionGuard::if_alive(alive, [this, &msg]() {
1235 enqueueLog(std::string(_("❌ Errore controllo cloud: ")) + msg);
1236 });
1237 });
1238 }
1239 catch (...) {
1240 if (!alive->load()) { activeCloudChecks--; return; }
1241 Glib::signal_idle().connect_once([this, alive]() {
1242 BastionGuard::if_alive(alive, [this]() {
1243 enqueueLog(_("❌ Errore controllo cloud sconosciuto."));
1244 });
1245 });
1246 }
1247
1249 }).detach();
1250}
static std::atomic< int > activeCloudChecks
void cloudCheckMalwareBazaarWorker(const std::string &filepath)
void if_alive(const AliveFlag &alive, Fn &&fn)

References _, activeCloudChecks, alive_, cloudCheckMalwareBazaarWorker(), enqueueLog(), and BastionGuard::if_alive().

Referenced by resolveFinalDownloadedFile(), and scanFileAutomatically().

◆ cloudCheckMalwareBazaarWorker()

void ScanPage::cloudCheckMalwareBazaarWorker ( const std::string & filepath)
private

Definition at line 1154 of file ScanPage.cpp.

1155{
1156 if (!fs::exists(filepath))
1157 return;
1158
1159 std::string sha = MalwareBazaar::sha256_file(filepath);
1160 if (sha.empty())
1161 return;
1162
1163 MBResult r = MalwareBazaar::lookupHash(sha);
1164
1165 if (!alive_->load()) return;
1166
1167 // Rientro nel main loop GTK
1168 auto alive = alive_;
1169 Glib::signal_idle().connect_once(
1170 [this, alive, filepath, r]() {
1171 BastionGuard::if_alive(alive, [this, &filepath, &r]() {
1172 if (!r.found) {
1173 enqueueLog(_("🔏 Nessun match cloud → File sicuro."));
1174 return;
1175 }
1176
1177 enqueueLog("🚨 Malware identificato:");
1178 enqueueLog("Nome: " + r.malwareName);
1179 enqueueLog("Tipo: " + r.fileType);
1180 enqueueLog("Tags: " + r.tags);
1181
1182 try {
1183 auto conn = Gio::DBus::Connection::get_sync(
1184 Gio::DBus::BusType::SESSION
1185 );
1186 auto proxy = Gio::DBus::Proxy::create_sync(
1187 conn,
1188 "org.BastionGuard.Ransomware.Alert",
1189 "/org/BastionGuard/ransomware/alert",
1190 "org.BastionGuard.Ransomware.Alert"
1191 );
1192
1193 auto params = Glib::VariantContainerBase::create_tuple({
1194 Glib::Variant<Glib::ustring>::create(filepath),
1195 Glib::Variant<Glib::ustring>::create(
1196 "VirusTotal: " + r.malwareName)
1197 });
1198
1199 proxy->call_sync("ShowAlert", params);
1200 }
1201 catch (...) {
1203 &parentWindow,
1204 (Glib::ustring("Malware cloud:\n") + filepath +
1205 "\nNome: " + r.malwareName).raw(),
1206 Gtk::MessageType::WARNING
1207 );
1208 }
1209 });
1210 }
1211 );
1212}
static MBResult lookupHash(const std::string &sha256)
static std::string sha256_file(const std::string &filepath)
static void show_message(Gtk::Window *parent, const std::string &message, Gtk::MessageType type=Gtk::MessageType::INFO, const std::string &secondary=std::string())
Definition Utils.cpp:33

References _, alive_, enqueueLog(), BastionGuard::if_alive(), MalwareBazaar::lookupHash(), parentWindow, MalwareBazaar::sha256_file(), and Utils::show_message().

Referenced by cloudCheckMalwareBazaar(), and resolveFinalDownloadedFile().

◆ detectBrowserDownloadDirs()

std::vector< std::string > ScanPage::detectBrowserDownloadDirs ( )
private

Definition at line 895 of file ScanPage.cpp.

896{
897 std::vector<std::string> dirs;
898 std::string home = Glib::get_home_dir();
899
900
901 std::string def = home + "/Downloads";
902 if (fs::exists(def)) dirs.push_back(def);
903
904
905 try {
906 std::string base = home + "/.mozilla/firefox";
907 if (fs::exists(base)) {
908 for (auto& entry : fs::directory_iterator(base)) {
909 if (!entry.is_directory()) continue;
910
911 fs::path prefs = entry.path() / "prefs.js";
912 if (!fs::exists(prefs)) continue;
913
914 std::ifstream f(prefs);
915 std::string line;
916
917 while (std::getline(f, line)) {
918 if (line.find("browser.download.dir") != std::string::npos) {
919 size_t s = line.find('"', line.find(',') + 1);
920 size_t e = line.find('"', s + 1);
921 if (s != std::string::npos && e != std::string::npos) {
922 std::string d = line.substr(s + 1, e - s - 1);
923 if (fs::exists(d)) dirs.push_back(d);
924 }
925 }
926 }
927 }
928 }
929 } catch (...) {}
930
931
932 std::vector<std::string> cfgs = {
933 home + "/.config/google-chrome/Default/Preferences",
934 home + "/.config/chromium/Default/Preferences",
935 home + "/.config/microsoft-edge/Default/Preferences"
936 };
937
938 for (auto& pref : cfgs) {
939 try {
940 if (!fs::exists(pref)) continue;
941
942 std::ifstream f(pref);
943 std::string content(
944 (std::istreambuf_iterator<char>(f)),
945 std::istreambuf_iterator<char>()
946 );
947
948 std::string key = "\"default_directory\"";
949 size_t pos = content.find(key);
950 if (pos == std::string::npos) continue;
951
952 size_t s = content.find('"', pos + key.size());
953 size_t e = content.find('"', s + 1);
954
955 if (s != std::string::npos && e != std::string::npos) {
956 std::string d = content.substr(s + 1, e - s - 1);
957 if (fs::exists(d)) dirs.push_back(d);
958 }
959 } catch (...) {}
960 }
961
962
963 std::sort(dirs.begin(), dirs.end());
964 dirs.erase(std::unique(dirs.begin(), dirs.end()), dirs.end());
965
966 return dirs;
967}

Referenced by enableAutoScanDownloads(), and ScanPage().

◆ disableAllMonitors()

void ScanPage::disableAllMonitors ( )
private

Definition at line 638 of file ScanPage.cpp.

639{
640 alreadyWatched.clear();
641 watchMap.clear();
642
643 if (inotifyFd >= 0) {
644 close(inotifyFd);
645 inotifyFd = -1;
646 }
647
648 enqueueLog(_("🛑 Tutti i monitor inotify sono stati disattivati"));
649}

References _, alreadyWatched, enqueueLog(), inotifyFd, and watchMap.

Referenced by ScanPage().

◆ enableAutoScanDownloads()

void ScanPage::enableAutoScanDownloads ( )
private

Definition at line 979 of file ScanPage.cpp.

980{
982
983 for (const auto& d : knownDownloadDirs) {
984 if (!fs::exists(d)) continue;
986 }
987
988 enqueueLog(_("📡 Monitoraggio Download attivo (inotify)"));
989}

References _, addInotifyRecursive(), detectBrowserDownloadDirs(), enqueueLog(), knownDownloadDirs, and normalizePath().

Referenced by ScanPage().

◆ enableGlobalDownloadMonitor()

void ScanPage::enableGlobalDownloadMonitor ( )
private

Definition at line 1023 of file ScanPage.cpp.

1024{
1025 // ANTI-BOOTLOOP: NON monitorare $HOME né /tmp ricorsivamente.
1026 // Un singolo `make -j` in qualsiasi cartella del home (incluso il
1027 // checkout di BastionGuard stesso) creerebbe migliaia di file
1028 // sotto build/CMakeFiles/, e l'inotify watcher genererebbe una
1029 // cascata di eventi → scansioni clamdscan in serie → main loop
1030 // bloccato → "bootloop" visivo della ScanPage.
1031 //
1032 // Limitiamo i watch alle XDG user dirs (Downloads, Desktop,
1033 // Documents, …) e a ~/.var/app, che è l'unico path Flatpak
1034 // dove i browser scrivono i download.
1035 std::vector<std::string> dirs;
1036
1037 auto xdg = getXdgUserDirs();
1038 dirs.insert(dirs.end(), xdg.begin(), xdg.end());
1039
1040 std::string varApp = Glib::get_home_dir() + "/.var/app";
1041 if (fs::exists(varApp)) dirs.push_back(varApp);
1042
1043 std::sort(dirs.begin(), dirs.end());
1044 dirs.erase(std::unique(dirs.begin(), dirs.end()), dirs.end());
1045
1046 for (auto& d : dirs) {
1047 if (!fs::exists(d)) continue;
1048 // addInotifyRecursive a sua volta filtra le build dirs
1049 // (per esempio se l'utente ha ~/Documents/progetti/foo/build/).
1051 }
1052
1053 enqueueLog(_("🌍 Monitor globale inotify attivo (XDG dirs)"));
1054}
std::vector< std::string > getXdgUserDirs()
Definition ScanPage.cpp:992

References _, addInotifyRecursive(), enqueueLog(), getXdgUserDirs(), and normalizePath().

Referenced by ScanPage().

◆ enqueueLog()

void ScanPage::enqueueLog ( const std::string & msg)
private

◆ flushLog()

void ScanPage::flushLog ( )
private

Definition at line 297 of file ScanPage.cpp.

297 {
298 std::vector<std::string> batch;
299 {
300 std::lock_guard<std::mutex> lock(logMutex);
301 batch.swap(pendingLog);
302 logFlushScheduled = false;
303 }
304 Glib::ustring s;
305 for (auto& m : batch) s += m + "\n";
306 textBuffer->insert(textBuffer->end(), s);
307}

References logFlushScheduled, logMutex, pendingLog, and textBuffer.

Referenced by enqueueLog().

◆ getXdgUserDirs()

std::vector< std::string > ScanPage::getXdgUserDirs ( )
private

Definition at line 992 of file ScanPage.cpp.

993{
994 std::vector<std::string> out;
995
996 std::string p = Glib::get_home_dir() + "/.config/user-dirs.dirs";
997 if (!fs::exists(p)) return out;
998
999 std::ifstream f(p);
1000 std::string line;
1001
1002 while (std::getline(f, line)) {
1003 if (line.find("XDG_") != 0 || line.find("_DIR=") == std::string::npos)
1004 continue;
1005
1006 size_t q1 = line.find('"');
1007 size_t q2 = line.find('"', q1 + 1);
1008
1009 if (q1 == std::string::npos || q2 == std::string::npos)
1010 continue;
1011
1012 std::string value = line.substr(q1 + 1, q2 - q1 - 1);
1013 if (value.find("$HOME") == 0)
1014 value.replace(0, 5, Glib::get_home_dir());
1015
1016 if (fs::exists(value))
1017 out.push_back(value);
1018 }
1019
1020 return out;
1021}

Referenced by enableGlobalDownloadMonitor().

◆ handleInfectedFile()

void ScanPage::handleInfectedFile ( const std::string & filepath)
private

Definition at line 1253 of file ScanPage.cpp.

1254{
1255 handleInfectedFileWithName(filepath, "ClamAV.Detected");
1256}
void handleInfectedFileWithName(const std::string &filepath, const std::string &virusName)

References handleInfectedFileWithName().

Referenced by resolveFinalDownloadedFile().

◆ handleInfectedFiles()

void ScanPage::handleInfectedFiles ( const std::vector< std::string > & files)
private

Definition at line 1259 of file ScanPage.cpp.

1260{
1261 if (files.empty()) return;
1262
1263 try {
1264 auto conn = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SESSION);
1265 auto proxy = Gio::DBus::Proxy::create_sync(
1266 conn,
1267 "org.BastionGuard.Ransomware.Alert",
1268 "/org/BastionGuard/ransomware/alert",
1269 "org.BastionGuard.Ransomware.Alert"
1270 );
1271
1272 for (const auto& f : files) {
1273 auto params = Glib::VariantContainerBase::create_tuple({
1274 Glib::Variant<Glib::ustring>::create(f),
1275 Glib::Variant<Glib::ustring>::create("ClamAV.Detected")
1276 });
1277 proxy->call_sync("ShowAlert", params);
1278
1279 enqueueLog(_("⚠️ Alert inviato per file infetto: ") + f);
1280 }
1281 }
1282 catch (...) {
1284 &parentWindow,
1285 _("File infetto rilevato:\n") + files.front(),
1286 Gtk::MessageType::WARNING
1287 );
1288 }
1289}

References _, enqueueLog(), parentWindow, and Utils::show_message().

Referenced by resolveFinalDownloadedFile().

◆ handleInfectedFileWithName()

void ScanPage::handleInfectedFileWithName ( const std::string & filepath,
const std::string & virusName )
private

Definition at line 1292 of file ScanPage.cpp.

1295{
1296 try {
1297 auto conn = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SESSION);
1298 auto proxy = Gio::DBus::Proxy::create_sync(
1299 conn,
1300 "org.BastionGuard.Ransomware.Alert",
1301 "/org/BastionGuard/ransomware/alert",
1302 "org.BastionGuard.Ransomware.Alert"
1303 );
1304
1305 auto params = Glib::VariantContainerBase::create_tuple({
1306 Glib::Variant<Glib::ustring>::create(filepath),
1307 Glib::Variant<Glib::ustring>::create(virusName)
1308 });
1309
1310 proxy->call_sync("ShowAlert", params);
1311
1312 enqueueLog(_("⚠️ File infetto: ") + filepath + " (" + virusName + ")");
1313 }
1314 catch (...) {
1316 &parentWindow,
1317 _("File infetto:\n") + filepath + "\nVirus: " + virusName,
1318 Gtk::MessageType::WARNING
1319 );
1320 }
1321}

References _, enqueueLog(), parentWindow, and Utils::show_message().

Referenced by handleInfectedFile(), onAurPkgbuildDetected(), readOutput(), resolveFinalDownloadedFile(), scanAurAuto(), and scanFileAutomatically().

◆ handleInotifyFileEvent()

void ScanPage::handleInotifyFileEvent ( const std::string & path,
uint32_t mask )
private

Definition at line 558 of file ScanPage.cpp.

559{
560 if (!autoScanEnabled) return;
561 if (path.empty()) return;
562
563 // Filtro directory rumorose / sensibili. Usiamo segmenti con slash su
564 // entrambi i lati per evitare falsi positivi come "/builder" o
565 // "/.localdomain". Il filtro per le build dir vere è isInBuildDir()
566 // poco sotto, qui filtriamo solo cache/git/local privati.
567 if (path.find("/.cache/") != std::string::npos ||
568 path.find("/.local/") != std::string::npos ||
569 path.find("/.git/") != std::string::npos)
570 return;
571
572
573 if (!(mask & (IN_CLOSE_WRITE | IN_MOVED_TO)))
574 return;
575
576
577 if (ends_with(path, ".part") || ends_with(path, ".crdownload"))
578 return;
579
580 // ---- Anti-compilazione: salta artefatti di build e directory di build.
581 // Sui progetti che si compilano si producono molti file in pochi secondi,
582 // se partisse una scansione per ognuno il sistema rallenterebbe troppo
583 // (bootloop di ScanPage). Questo check va fatto PRIMA del burst counter,
584 // così i file di build non lo gonfiano (altrimenti rischiamo di entrare
585 // in throttle anche quando l'utente sta solo scaricando legittimamente).
586 if (isBuildArtifactExt(path) || isInBuildDir(path))
587 return;
588
589 // ---- Anti-compilazione (parte 2): rilevamento "burst di scrittura".
590 // Se in una finestra breve arrivano molti eventi vicini, presumiamo che
591 // sia in corso una compilazione: entriamo in throttle e per qualche
592 // secondo non scansioniamo nulla automaticamente.
593 {
594 const int64_t now = nowMs();
595
596 // Se siamo già in throttle, salta direttamente.
597 if (now < g_throttleUntilMs.load(std::memory_order_relaxed)) {
598 return;
599 }
600
601 // Aggiorna/azzera la finestra di osservazione.
602 int64_t winStart = g_burstWindowStartMs.load(std::memory_order_relaxed);
603 if (winStart == 0 || now - winStart > kBurstWindowMs) {
604 g_burstWindowStartMs.store(now, std::memory_order_relaxed);
605 g_recentEventCount.store(1, std::memory_order_relaxed);
606 } else {
607 int n = g_recentEventCount.fetch_add(1, std::memory_order_relaxed) + 1;
608 if (n > kBurstThreshold) {
609 // Burst rilevato → throttle: niente scansione automatica.
611 std::memory_order_relaxed);
612 g_burstWindowStartMs.store(0, std::memory_order_relaxed);
613 g_recentEventCount.store(0, std::memory_order_relaxed);
614 enqueueLog(_("⏸️ Compilazione/burst rilevato: scansione automatica sospesa temporaneamente"));
615 return;
616 }
617 }
618 }
619
620 if (recentlyScanned.count(path)) return;
621 recentlyScanned.insert(path);
622 Glib::signal_timeout().connect_once([path](){
623 recentlyScanned.erase(path);
624 }, 1500);
625
626
627 Glib::signal_timeout().connect_once(
628 [this, path]() {
629 if (!fs::exists(path)) return;
631 },
632 100
633 );
634}
static std::atomic< int64_t > g_burstWindowStartMs
Definition ScanPage.cpp:58
static int64_t nowMs()
Definition ScanPage.cpp:66
static constexpr int64_t kThrottleHoldMs
Definition ScanPage.cpp:64
static bool isBuildArtifactExt(const std::string &path)
Definition ScanPage.cpp:79
static constexpr int kBurstThreshold
Definition ScanPage.cpp:63
static bool ends_with(const std::string &s, const std::string &suf)
Definition ScanPage.cpp:71
static std::atomic< int > g_recentEventCount
Definition ScanPage.cpp:57
static std::atomic< int64_t > g_throttleUntilMs
Definition ScanPage.cpp:59
static std::unordered_set< std::string > recentlyScanned
Definition ScanPage.cpp:46
static constexpr int kBurstWindowMs
Definition ScanPage.cpp:62
void scanFileAutomatically(const std::string &filepath)
Definition ScanPage.cpp:760

References _, autoScanEnabled, ends_with(), enqueueLog(), g_burstWindowStartMs, g_recentEventCount, g_throttleUntilMs, isBuildArtifactExt(), isInBuildDir(), kBurstThreshold, kBurstWindowMs, kThrottleHoldMs, nowMs(), recentlyScanned, and scanFileAutomatically().

Referenced by onInotifyEvent().

◆ isInterestingDownload()

bool ScanPage::isInterestingDownload ( const std::string & path)
private

◆ isOutsideKnownDownloadDirs()

bool ScanPage::isOutsideKnownDownloadDirs ( const std::string & path)
private

Definition at line 970 of file ScanPage.cpp.

971{
972 for (const auto& d : knownDownloadDirs) {
973 if (path.rfind(d, 0) == 0) return false;
974 }
975 return true;
976}

References knownDownloadDirs.

◆ loadCloudConfig()

void ScanPage::loadCloudConfig ( )
private

Definition at line 494 of file ScanPage.cpp.

495{
496 std::string path = Glib::get_home_dir() + "/.config/BastionGuard/cloud.conf";
497
498 try {
499 if (!fs::exists(path)) {
501 return;
502 }
503
504 std::ifstream f(path);
505 std::string line;
506
507 while (std::getline(f, line)) {
508 line = trim(line);
509 if (line.empty() || line[0] == '#') continue;
510
511 size_t eq = line.find('=');
512 if (eq == std::string::npos) continue;
513
514 std::string key = trim(line.substr(0, eq));
515 std::string value = trim(line.substr(eq + 1));
516
517 if (key == "malware_bazaar_api_key") {
518 malwareBazaarApiKey = value;
519 }
520 }
521
523 enqueueLog(_("🔑 API Key VirusTotal caricata da cloud.conf"));
525 }
526 catch (...) {
527 enqueueLog(_("⚠️ Errore caricamento cloud.conf"));
528 }
529}
std::string globalMalwareBazaarApiKey
static std::string trim(const std::string &s)
Definition ScanPage.cpp:117
std::string malwareBazaarApiKey
Definition ScanPage.hpp:75

References _, apiKeyEntry, enqueueLog(), globalMalwareBazaarApiKey, malwareBazaarApiKey, and trim().

Referenced by ScanPage().

◆ loadConfig()

void ScanPage::loadConfig ( )
private

Definition at line 324 of file ScanPage.cpp.

324 {
325 try {
326 if (!fs::exists(getConfigPath())) return;
327 json cfg;
328 std::ifstream ifs(getConfigPath());
329 ifs >> cfg;
330 autoScanEnabled = cfg.value("auto_scan_enabled", true);
332 } catch (...) {}
333}
nlohmann::json json
Definition Backend.cpp:2908
std::string getConfigPath()
Definition ScanPage.cpp:310

References autoScanEnabled, autoScanSwitch, and getConfigPath().

Referenced by ScanPage().

◆ logMessage()

void ScanPage::logMessage ( const std::string & msg)
private

Definition at line 1057 of file ScanPage.cpp.

1058{
1059 textBuffer->insert(textBuffer->end(), msg + "\n");
1060}

References textBuffer.

Referenced by resolveFinalDownloadedFile().

◆ onApiKeySaveClicked()

void ScanPage::onApiKeySaveClicked ( )
private

Definition at line 551 of file ScanPage.cpp.

552{
553 malwareBazaarApiKey = apiKeyEntry.get_text();
555}
void saveCloudConfig()
Definition ScanPage.cpp:532

References apiKeyEntry, malwareBazaarApiKey, and saveCloudConfig().

Referenced by ScanPage().

◆ onAurPkgbuildDetected()

void ScanPage::onAurPkgbuildDetected ( const std::string & path)
private

Definition at line 1063 of file ScanPage.cpp.

1064{
1065 // Caso speciale: INFECTED:PATH
1066 if (path.rfind("INFECTED:", 0) == 0) {
1067 std::string real = path.substr(11);
1068 enqueueLog(_("⚠️ PKGBUILD sospetto: ") + real);
1069 handleInfectedFileWithName(real, "AUR.StaticHeuristic");
1070 return;
1071 }
1072
1073 enqueueLog(_("📦 PKGBUILD rilevato: ") + path);
1074
1075
1077
1078
1079 scanAurAuto(path);
1080}
void scanAurAuto(const std::string &path)
void scanPKGBUILD(const std::string &path)
Definition AurScan.cpp:258

References _, enqueueLog(), handleInfectedFileWithName(), scanAurAuto(), and AurScan::scanPKGBUILD().

Referenced by resolveFinalDownloadedFile().

◆ onChooseFile()

void ScanPage::onChooseFile ( )
private

Definition at line 463 of file ScanPage.cpp.

464{
465 auto dialog = Gtk::FileDialog::create();
466 dialog->set_title(_("Seleziona file"));
467 dialog->set_modal(true);
468
469 dialog->open(
471 sigc::bind(
472 sigc::mem_fun(*this, &ScanPage::onChooseFileFinish),
473 dialog
474 )
475 );
476}
void onChooseFileFinish(const Glib::RefPtr< Gio::AsyncResult > &result, Glib::RefPtr< Gtk::FileDialog > dialog)
Definition ScanPage.cpp:478

References _, onChooseFileFinish(), and parentWindow.

Referenced by ScanPage().

◆ onChooseFileFinish()

void ScanPage::onChooseFileFinish ( const Glib::RefPtr< Gio::AsyncResult > & result,
Glib::RefPtr< Gtk::FileDialog > dialog )
private

Definition at line 478 of file ScanPage.cpp.

480{
481 try {
482 auto file = dialog->open_finish(result);
483 if (!file)
484 return;
485
486 selectedPath = file->get_path();
487 enqueueLog(_("📄 File selezionato: ") + selectedPath);
488 }
489 catch (const Glib::Error&) {
490 }
491}
std::string selectedPath
Definition ScanPage.hpp:95

References _, enqueueLog(), and selectedPath.

Referenced by onChooseFile().

◆ onChooseFolder()

void ScanPage::onChooseFolder ( )
private

Definition at line 431 of file ScanPage.cpp.

432{
433 auto dialog = Gtk::FileDialog::create();
434 dialog->set_title(_("Seleziona cartella"));
435 dialog->set_modal(true);
436
437 dialog->select_folder(
439 sigc::bind(
440 sigc::mem_fun(*this, &ScanPage::onChooseFolderFinish),
441 dialog
442 )
443 );
444}
void onChooseFolderFinish(const Glib::RefPtr< Gio::AsyncResult > &result, Glib::RefPtr< Gtk::FileDialog > dialog)
Definition ScanPage.cpp:446

References _, onChooseFolderFinish(), and parentWindow.

Referenced by ScanPage().

◆ onChooseFolderFinish()

void ScanPage::onChooseFolderFinish ( const Glib::RefPtr< Gio::AsyncResult > & result,
Glib::RefPtr< Gtk::FileDialog > dialog )
private

Definition at line 446 of file ScanPage.cpp.

448{
449 try {
450 auto file = dialog->select_folder_finish(result);
451 if (!file)
452 return;
453
454 selectedPath = file->get_path();
455 enqueueLog(_("📁 Cartella selezionata: ") + selectedPath);
456 }
457 catch (const Glib::Error&) {
458 }
459}

References _, enqueueLog(), and selectedPath.

Referenced by onChooseFolder().

◆ onInotifyEvent()

bool ScanPage::onInotifyEvent ( Glib::IOCondition cond)
private

Definition at line 399 of file ScanPage.cpp.

400{
401 if (!static_cast<bool>(cond & Glib::IOCondition::IO_IN))
402 return true;
403
404 char buffer[4096]
405 __attribute__((aligned(__alignof__(struct inotify_event))));
406
407 ssize_t len = read(inotifyFd, buffer, sizeof(buffer));
408 if (len <= 0)
409 return true;
410
411 const char* ptr = buffer;
412
413 while (ptr < buffer + len) {
414 const inotify_event* ev =
415 reinterpret_cast<const inotify_event*>(ptr);
416
417 std::string parentDir =
418 watchMap.count(ev->wd) ? watchMap[ev->wd] : "";
419
420 std::string name = ev->len ? std::string(ev->name) : "";
421 std::string fullPath = parentDir + "/" + name;
422
423 handleInotifyFileEvent(fullPath, ev->mask);
424
425 ptr += sizeof(struct inotify_event) + ev->len;
426 }
427
428 return true;
429}
void handleInotifyFileEvent(const std::string &path, uint32_t mask)
Definition ScanPage.cpp:558

References handleInotifyFileEvent(), inotifyFd, and watchMap.

Referenced by ScanPage().

◆ onStartScan()

void ScanPage::onStartScan ( )
private

Definition at line 651 of file ScanPage.cpp.

652{
653 if (scanInProgress) {
654 enqueueLog(_("⏳ È già in corso un'altra scansione..."));
655 return;
656 }
657
658 if (selectedPath.empty()) {
659 textBuffer->set_text(_("⚠️ Nessun file o cartella selezionato.\n"));
660 return;
661 }
662
663 scanInProgress = true;
665
666 textBuffer->set_text(_("🔍 Avvio scansione con clamdscan...\n\n"));
667
668 infectedFilesBuffer.clear();
669 fileListBox.remove_all();
670
671 try {
672 std::vector<std::string> argv = {
673 "clamdscan",
674 "--fdpass",
675 "--no-summary",
676 "--infected",
678 };
679
680 subprocess = Gio::Subprocess::create(
681 argv,
682 Gio::Subprocess::Flags::STDOUT_PIPE |
683 Gio::Subprocess::Flags::STDERR_PIPE
684 );
685
686 stdoutStream = subprocess->get_stdout_pipe();
687 dataStream = Gio::DataInputStream::create(stdoutStream);
688
689 dataStream->read_line_async(
690 sigc::mem_fun(*this, &ScanPage::readOutput),
691 Glib::RefPtr<Gio::Cancellable>(),
692 Glib::PRIORITY_DEFAULT
693 );
694 }
695 catch (const Glib::Error& ex) {
696 textBuffer->insert(textBuffer->end(),
697 _("❌ Errore avvio clamdscan: ") + Glib::ustring(ex.what()) + "\n"
698 );
699 scanInProgress = false;
700 }
701}
Glib::RefPtr< Gio::InputStream > stdoutStream
Definition ScanPage.hpp:64
std::vector< std::string > infectedFilesBuffer
Definition ScanPage.hpp:96
void readOutput(const Glib::RefPtr< Gio::AsyncResult > &result)
Definition ScanPage.cpp:704
bool scanInProgress
Definition ScanPage.hpp:97
Glib::RefPtr< Gio::Subprocess > subprocess
Definition ScanPage.hpp:63
Glib::RefPtr< Gio::DataInputStream > dataStream
Definition ScanPage.hpp:65

References _, dataStream, enqueueLog(), fileListBox, infectedFilesBuffer, readOutput(), scanInProgress, selectedPath, stdoutStream, subprocess, textBuffer, and trim().

Referenced by ScanPage().

◆ onToggleApiKeyVisibility()

void ScanPage::onToggleApiKeyVisibility ( )
private

Definition at line 1435 of file ScanPage.cpp.

1436{
1438
1439 apiKeyEntry.set_visibility(apiKeyVisible);
1440
1441 btnShowApiKey.set_icon_name(
1443 ? "view-conceal-symbolic"
1444 : "view-reveal-symbolic"
1445 );
1446}
bool apiKeyVisible
Definition ScanPage.hpp:74

References apiKeyEntry, apiKeyVisible, and btnShowApiKey.

Referenced by ScanPage().

◆ readOutput()

void ScanPage::readOutput ( const Glib::RefPtr< Gio::AsyncResult > & result)
private

Definition at line 704 of file ScanPage.cpp.

705{
706 try {
707 std::string line;
708 bool ok = dataStream->read_line_finish(result, line);
709
710 if (!ok) {
711 scanInProgress = false;
712 textBuffer->insert(textBuffer->end(), _("✅ Scansione completata.\n"));
713 return;
714 }
715
716 auto alive = alive_;
717 Glib::signal_idle().connect_once([this, alive, line]() {
718 BastionGuard::if_alive(alive, [this, &line]() {
719 textBuffer->insert(textBuffer->end(), line + "\n");
720
721 // Esempio: "Scanning /path/file"
722 if (line.rfind("Scanning ", 0) == 0) {
723 std::string filepath = line.substr(9);
724 addFileToList(filepath);
725 }
726
727 // Esempio: "/file: Virus FOUND"
728 if (line.find("FOUND") != std::string::npos) {
729
730 size_t pos = line.find(": ");
731 size_t end = line.find(" FOUND");
732
733 if (pos != std::string::npos && end != std::string::npos) {
734 std::string infected = line.substr(0, pos);
735 std::string virus = line.substr(pos + 2, end - (pos + 2));
736
737 infectedFilesBuffer.push_back(infected);
738 handleInfectedFileWithName(infected, virus);
739 }
740 }
741 });
742 });
743
744 // Continua lettura
745 dataStream->read_line_async(
746 sigc::mem_fun(*this, &ScanPage::readOutput),
747 Glib::RefPtr<Gio::Cancellable>(),
748 Glib::PRIORITY_DEFAULT
749 );
750 }
751 catch (const Glib::Error& ex) {
752 textBuffer->insert(textBuffer->end(),
753 _("❌ Errore lettura output: ") + Glib::ustring(ex.what()) + "\n"
754 );
755 scanInProgress = false;
756 }
757}
void addFileToList(const std::string &filepath)
Definition ScanPage.cpp:878

References _, addFileToList(), alive_, dataStream, handleInfectedFileWithName(), BastionGuard::if_alive(), infectedFilesBuffer, readOutput(), scanInProgress, and textBuffer.

Referenced by onStartScan(), and readOutput().

◆ resolveFinalDownloadedFile()

◆ safeExists()

bool ScanPage::safeExists ( const std::string & p)
privatenoexcept

Definition at line 1334 of file ScanPage.cpp.

1335{
1336 try { return fs::exists(p); }
1337 catch (...) { return false; }
1338}

Referenced by resolveFinalDownloadedFile().

◆ saveCloudConfig()

void ScanPage::saveCloudConfig ( )
private

Definition at line 532 of file ScanPage.cpp.

533{
534 std::string dir = Glib::get_home_dir() + "/.config/BastionGuard";
535 std::string path = dir + "/cloud.conf";
536
537 try {
538 fs::create_directories(dir);
539
540 std::ofstream f(path);
541 f << "malware_bazaar_api_key=" << malwareBazaarApiKey << "\n";
542
543 enqueueLog(_("💾 API Key salvata in cloud.conf"));
545 }
546 catch (...) {
547 enqueueLog(_("❌ Errore salvataggio cloud.conf"));
548 }
549}

References _, enqueueLog(), globalMalwareBazaarApiKey, and malwareBazaarApiKey.

Referenced by onApiKeySaveClicked().

◆ saveConfig()

void ScanPage::saveConfig ( )
private

Definition at line 314 of file ScanPage.cpp.

314 {
315 try {
316 fs::create_directories(Glib::get_home_dir() + "/.local/share/BastionGuard");
317 json cfg;
318 cfg["auto_scan_enabled"] = autoScanEnabled;
319 std::ofstream ofs(getConfigPath());
320 ofs << cfg.dump(4);
321 } catch (...) {}
322}

References autoScanEnabled, and getConfigPath().

Referenced by ScanPage().

◆ scanAurAuto()

void ScanPage::scanAurAuto ( const std::string & path)
private

Definition at line 1082 of file ScanPage.cpp.

1083{
1084 if (!fs::exists(path)) {
1085 enqueueLog(_("❌ PKGBUILD non trovato: ") + path);
1086 return;
1087 }
1088
1089 enqueueLog(_("🦠 Scansione AUR: ") + path);
1090
1091 std::vector<std::string> argv = {
1092 "clamdscan",
1093 "--fdpass",
1094 "--no-summary",
1095 "--infected",
1096 path
1097 };
1098
1099 try {
1100 auto proc = Gio::Subprocess::create(
1101 argv,
1102 Gio::Subprocess::Flags::STDOUT_PIPE
1103 );
1104
1105 auto out = proc->get_stdout_pipe();
1106 auto ds = Gio::DataInputStream::create(out);
1107
1108 ds->read_line_async(
1109 [this, ds, path](const Glib::RefPtr<Gio::AsyncResult>& res) {
1110 try {
1111 std::string line;
1112 bool ok = ds->read_line_finish(res, line);
1113
1114 if (ok && line.find("FOUND") != std::string::npos) {
1115
1116 size_t pos = line.find(": ");
1117 size_t end = line.find(" FOUND");
1118
1119 std::string virus = "Sconosciuto";
1120 if (pos != std::string::npos && end != std::string::npos)
1121 virus = line.substr(pos + 2, end - (pos + 2));
1122
1123 auto alive = alive_;
1124 Glib::signal_idle().connect_once([this, alive, path, virus]() {
1125 BastionGuard::if_alive(alive, [this, &path, &virus]() {
1126 handleInfectedFileWithName(path, virus);
1127 });
1128 });
1129
1130 return;
1131 }
1132
1133 auto alive = alive_;
1134 Glib::signal_idle().connect_once([this, alive, path]() {
1135 BastionGuard::if_alive(alive, [this, &path]() {
1136 enqueueLog(_("✅ PKGBUILD pulito: ") + path);
1137 });
1138 });
1139 }
1140 catch (...) {
1141 enqueueLog(_("❌ Errore lettura output AUR"));
1142 }
1143 },
1144 Glib::RefPtr<Gio::Cancellable>(),
1145 Glib::PRIORITY_DEFAULT
1146 );
1147 }
1148 catch (...) {
1149 enqueueLog(_("❌ Errore avvio clamdscan per PKGBUILD"));
1150 }
1151}

References _, alive_, enqueueLog(), handleInfectedFileWithName(), and BastionGuard::if_alive().

Referenced by onAurPkgbuildDetected(), and resolveFinalDownloadedFile().

◆ scanFileAutomatically()

void ScanPage::scanFileAutomatically ( const std::string & filepath)
private

Definition at line 760 of file ScanPage.cpp.

761{
762 if (!autoScanEnabled) return;
763 if (!fs::exists(filepath)) return;
764
765 if (!fs::is_regular_file(filepath)) {
766 enqueueLog(_("📁 Ignorato (non è un file regolare): ") + filepath);
767 return;
768 }
769
770 try {
771 std::uintmax_t size = fs::file_size(filepath);
772 if (size < 64) {
773 enqueueLog(_("⏳ File troppo piccolo, potrebbe essere ancora in scrittura: ") + filepath);
774 return;
775 }
776 }
777 catch (...) {
778 enqueueLog(_("❌ Impossibile determinare dimensione file: ") + filepath);
779 return;
780 }
781
782 try {
783 auto size = fs::file_size(filepath);
784 auto timep = fs::last_write_time(filepath).time_since_epoch().count();
785
786 std::string key = filepath + "_" + std::to_string(size) + "_" + std::to_string(timep);
787
788 if (recentlyScanned.count(key)) return;
789 recentlyScanned.insert(key);
790
791 Glib::signal_timeout().connect_once([key]() {
792 recentlyScanned.erase(key);
793 }, 5000);
794
795 enqueueLog(_("🔍 Scansione automatica per: ") + filepath);
796
797 std::vector<std::string> argv = {
798 "clamdscan",
799 "--fdpass",
800 "--no-summary",
801 "--infected",
802 filepath
803 };
804
805 auto proc = Gio::Subprocess::create(
806 argv,
807 Gio::Subprocess::Flags::STDOUT_PIPE
808 );
809
810 auto out = proc->get_stdout_pipe();
811 auto ds = Gio::DataInputStream::create(out);
812
813 auto reader = std::make_shared<std::function<void()>>();
814 *reader = [this, ds, filepath, reader]() {
815 ds->read_line_async(
816 [this, ds, filepath, reader](const Glib::RefPtr<Gio::AsyncResult>& res) {
817 try {
818 std::string line;
819 bool ok = ds->read_line_finish(res, line);
820
821 // EOF: ClamAV non ha trovato nulla
822 if (!ok) {
823 auto alive = alive_;
824 Glib::signal_idle().connect_once([this, alive, filepath]() {
825 BastionGuard::if_alive(alive, [this, &filepath]() {
826 enqueueLog(_("⬜ ClamAV pulito, avvio controllo cloud..."));
827 cloudCheckMalwareBazaar(filepath);
828 });
829 });
830 return;
831 }
832
833 // Trovato malware
834 if (line.find("FOUND") != std::string::npos) {
835 size_t pos = line.find(": ");
836 size_t end = line.find(" FOUND");
837
838 std::string virus = "Sconosciuto";
839 if (pos != std::string::npos && end != std::string::npos)
840 virus = line.substr(pos + 2, end - (pos + 2));
841
842 auto alive = alive_;
843 Glib::signal_idle().connect_once([this, alive, filepath, virus]() {
844 BastionGuard::if_alive(alive, [this, &filepath, &virus]() {
845 handleInfectedFileWithName(filepath, virus);
846 });
847 });
848 return;
849 }
850
851 // Continua a leggere fino a EOF
852 (*reader)();
853 }
854 catch (const Glib::Error& ex) {
855 enqueueLog(std::string(_("❌ Errore scansione automatica: ")) + ex.what());
856 }
857 catch (const std::exception& ex) {
858 enqueueLog(std::string(_("❌ Eccezione scansione automatica: ")) + ex.what());
859 }
860 catch (...) {
861 enqueueLog(_("❌ Errore scansione automatica"));
862 }
863 },
864 Glib::RefPtr<Gio::Cancellable>(),
865 Glib::PRIORITY_DEFAULT
866 );
867 };
868
869 (*reader)();
870 }
871 catch (const std::exception& ex) {
872 enqueueLog(std::string(_("❌ Errore accesso file: ")) + ex.what());
873 }
874}
void cloudCheckMalwareBazaar(const std::string &filepath)

References _, alive_, autoScanEnabled, cloudCheckMalwareBazaar(), enqueueLog(), handleInfectedFileWithName(), BastionGuard::if_alive(), and recentlyScanned.

Referenced by handleInotifyFileEvent().

◆ showAlertWindow()

void ScanPage::showAlertWindow ( const std::string & file,
const std::string & family )
private

Definition at line 1324 of file ScanPage.cpp.

1325{
1327 &parentWindow,
1328 _("Minaccia rilevata:\n") + file + _("\nTipo: ") + family,
1329 Gtk::MessageType::WARNING
1330 );
1331}

References _, parentWindow, and Utils::show_message().

Referenced by resolveFinalDownloadedFile().

◆ showCloudPrivacyDisclaimer()

void ScanPage::showCloudPrivacyDisclaimer ( )
private

Definition at line 1346 of file ScanPage.cpp.

1347{
1348 auto *dialog = new Gtk::Window();
1349 dialog->set_title("");
1350 dialog->set_default_size(560, 300);
1351 dialog->set_resizable(false);
1352 dialog->set_modal(false);
1353 dialog->add_css_class("app-dialog");
1354 dialog->add_css_class("main-window");
1355 dialog->add_css_class("app-window");
1356
1357 /* CSS globale */
1358 try {
1359 auto css = Gtk::CssProvider::create();
1360 css->load_from_path(resource("BastionGuard.css"));
1361 auto display = Gdk::Display::get_default();
1362 Gtk::StyleContext::add_provider_for_display(
1363 display, css,
1364 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1365 } catch (...) {}
1366
1367 /* Header custom */
1368 auto *header = Gtk::make_managed<Gtk::HeaderBar>();
1369 header->set_show_title_buttons(false);
1370 header->set_decoration_layout("");
1371 header->add_css_class("custom-headerbar");
1372
1373 auto *close_box = Gtk::make_managed<Gtk::Box>(
1374 Gtk::Orientation::HORIZONTAL);
1375 close_box->set_margin_end(6);
1376
1377 try {
1378 auto icon = Gdk::Pixbuf::create_from_file(
1379 resource("icon-close.png"), 28, 28, true);
1380 auto *btn = Gtk::make_managed<Gtk::Button>();
1381 btn->set_child(
1382 *Gtk::make_managed<Gtk::Image>(icon));
1383 btn->add_css_class("header-button");
1384 btn->signal_clicked().connect(
1385 [dialog]() { dialog->close(); delete dialog; });
1386 close_box->append(*btn);
1387 } catch (...) {}
1388
1389 header->pack_end(*close_box);
1390
1391 auto *wrap =
1392 Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
1393 wrap->append(*header);
1394 dialog->set_titlebar(*wrap);
1395
1396 /* Contenuto */
1397 auto *vbox = Gtk::make_managed<Gtk::Box>(
1398 Gtk::Orientation::VERTICAL, 14);
1399 vbox->set_margin(24);
1400
1401 auto title = Gtk::make_managed<Gtk::Label>(
1402 _("🔐 Privacy – Controllo Cloud"));
1403 title->add_css_class("title");
1404 title->set_halign(Gtk::Align::CENTER);
1405
1406 auto msg = Gtk::make_managed<Gtk::Label>(_(
1407 "BastionGuard NON carica alcun file online.\n\n"
1408 "• Nessun contenuto locale viene trasmesso\n"
1409 "• Viene calcolato solo l’hash SHA256 del file\n"
1410 "• L’hash è confrontato con database pubblici\n"
1411 "• Nessun percorso o dato personale viene inviato\n"
1412 "• La API Key resta salvata esclusivamente in locale\n\n"
1413 "Il controllo cloud è una verifica di reputazione, "
1414 "non un upload del file.")
1415 );
1416 msg->set_wrap(true);
1417 msg->set_justify(Gtk::Justification::CENTER);
1418 msg->set_halign(Gtk::Align::CENTER);
1419
1420 auto closeBtn =
1421 Gtk::make_managed<Gtk::Button>("Ho capito");
1422 closeBtn->add_css_class("btn-success");
1423 closeBtn->set_halign(Gtk::Align::CENTER);
1424 closeBtn->signal_clicked().connect(
1425 [dialog]() { dialog->close(); delete dialog; });
1426
1427 vbox->append(*title);
1428 vbox->append(*msg);
1429 vbox->append(*closeBtn);
1430
1431 dialog->set_child(*vbox);
1432 dialog->present();
1433}
std::string resource(const std::string &rel)
Definition Resource.hpp:31

References _, and resource().

Referenced by ScanPage().

Member Data Documentation

◆ alive_

◆ alreadyWatched

std::unordered_set<std::string> ScanPage::alreadyWatched
private

Definition at line 123 of file ScanPage.hpp.

Referenced by addInotifyRecursive(), and disableAllMonitors().

◆ apiKeyActionsBox

Gtk::Box ScanPage::apiKeyActionsBox {Gtk::Orientation::HORIZONTAL, 6}
private

Definition at line 60 of file ScanPage.hpp.

Referenced by ScanPage().

◆ apiKeyBox

Gtk::Box ScanPage::apiKeyBox {Gtk::Orientation::HORIZONTAL, 6}
private

Definition at line 68 of file ScanPage.hpp.

Referenced by ScanPage().

◆ apiKeyEntry

Gtk::Entry ScanPage::apiKeyEntry
private

◆ apiKeySizeGroup

Glib::RefPtr<Gtk::SizeGroup> ScanPage::apiKeySizeGroup
private

Definition at line 61 of file ScanPage.hpp.

Referenced by ScanPage().

◆ apiKeyVisible

bool ScanPage::apiKeyVisible = false
private

Definition at line 74 of file ScanPage.hpp.

Referenced by onToggleApiKeyVisibility().

◆ autoScanBox

Gtk::Box ScanPage::autoScanBox {Gtk::Orientation::HORIZONTAL, 6}
private

Definition at line 51 of file ScanPage.hpp.

Referenced by ScanPage().

◆ autoScanEnabled

bool ScanPage::autoScanEnabled = true
private

◆ autoScanLabel

Gtk::Label ScanPage::autoScanLabel
private

Definition at line 52 of file ScanPage.hpp.

Referenced by ScanPage().

◆ autoScanSwitch

Gtk::Switch ScanPage::autoScanSwitch
private

Definition at line 53 of file ScanPage.hpp.

Referenced by loadConfig(), and ScanPage().

◆ btnApiKeySave

Gtk::Button ScanPage::btnApiKeySave {"Salva"}
private

Definition at line 71 of file ScanPage.hpp.

Referenced by ScanPage().

◆ btnChooseFile

Gtk::Button ScanPage::btnChooseFile {_("Seleziona file")}
private

Definition at line 48 of file ScanPage.hpp.

Referenced by ScanPage().

◆ btnChooseFolder

Gtk::Button ScanPage::btnChooseFolder {_("Seleziona cartella")}
private

Definition at line 47 of file ScanPage.hpp.

Referenced by ScanPage().

◆ btnPrivacy

Gtk::Button ScanPage::btnPrivacy
private

Definition at line 72 of file ScanPage.hpp.

Referenced by ScanPage().

◆ btnScan

Gtk::Button ScanPage::btnScan {_("Scansiona")}
private

Definition at line 49 of file ScanPage.hpp.

Referenced by ScanPage().

◆ btnShowApiKey

Gtk::Button ScanPage::btnShowApiKey
private

Definition at line 70 of file ScanPage.hpp.

Referenced by onToggleApiKeyVisibility(), and ScanPage().

◆ buttonBox

Gtk::Box ScanPage::buttonBox {Gtk::Orientation::HORIZONTAL, 5}
private

Definition at line 46 of file ScanPage.hpp.

Referenced by ScanPage().

◆ dataStream

Glib::RefPtr<Gio::DataInputStream> ScanPage::dataStream
private

Definition at line 65 of file ScanPage.hpp.

Referenced by onStartScan(), and readOutput().

◆ fileListBox

Gtk::ListBox ScanPage::fileListBox
private

Definition at line 59 of file ScanPage.hpp.

Referenced by addFileToList(), onStartScan(), and ScanPage().

◆ infectedFilesBuffer

std::vector<std::string> ScanPage::infectedFilesBuffer
private

Definition at line 96 of file ScanPage.hpp.

Referenced by onStartScan(), and readOutput().

◆ inotifyFd

int ScanPage::inotifyFd = -1
private

Definition at line 116 of file ScanPage.hpp.

Referenced by addInotifyRecursive(), disableAllMonitors(), onInotifyEvent(), and ScanPage().

◆ inotifySource

Glib::RefPtr<Glib::IOSource> ScanPage::inotifySource
private

Definition at line 117 of file ScanPage.hpp.

Referenced by ScanPage().

◆ knownDownloadDirs

std::vector<std::string> ScanPage::knownDownloadDirs
private

Definition at line 110 of file ScanPage.hpp.

Referenced by enableAutoScanDownloads(), isOutsideKnownDownloadDirs(), and ScanPage().

◆ logFlushScheduled

bool ScanPage::logFlushScheduled = false
private

Definition at line 81 of file ScanPage.hpp.

Referenced by enqueueLog(), and flushLog().

◆ logMutex

std::mutex ScanPage::logMutex
private

Definition at line 79 of file ScanPage.hpp.

Referenced by enqueueLog(), and flushLog().

◆ malwareBazaarApiKey

std::string ScanPage::malwareBazaarApiKey
private

Definition at line 75 of file ScanPage.hpp.

Referenced by loadCloudConfig(), onApiKeySaveClicked(), and saveCloudConfig().

◆ parentWindow

Gtk::Window& ScanPage::parentWindow
private

◆ pendingLog

std::vector<std::string> ScanPage::pendingLog
private

Definition at line 80 of file ScanPage.hpp.

Referenced by enqueueLog(), and flushLog().

◆ scanInProgress

bool ScanPage::scanInProgress = false
private

Definition at line 97 of file ScanPage.hpp.

Referenced by onStartScan(), and readOutput().

◆ selectedPath

std::string ScanPage::selectedPath
private

Definition at line 95 of file ScanPage.hpp.

Referenced by onChooseFileFinish(), onChooseFolderFinish(), and onStartScan().

◆ stdoutStream

Glib::RefPtr<Gio::InputStream> ScanPage::stdoutStream
private

Definition at line 64 of file ScanPage.hpp.

Referenced by onStartScan().

◆ subprocess

Glib::RefPtr<Gio::Subprocess> ScanPage::subprocess
private

Definition at line 63 of file ScanPage.hpp.

Referenced by onStartScan().

◆ textBuffer

Glib::RefPtr<Gtk::TextBuffer> ScanPage::textBuffer
private

Definition at line 57 of file ScanPage.hpp.

Referenced by flushLog(), logMessage(), onStartScan(), readOutput(), and ScanPage().

◆ textView

Gtk::TextView ScanPage::textView
private

Definition at line 56 of file ScanPage.hpp.

Referenced by ScanPage().

◆ watchMap

std::unordered_map<int, std::string> ScanPage::watchMap
private

Definition at line 120 of file ScanPage.hpp.

Referenced by addInotifyRecursive(), disableAllMonitors(), and onInotifyEvent().


The documentation for this class was generated from the following files: