Scan Module (ScanPage)

Overview

The Scan module is implemented by the ScanPage class and provides both manual scanning and automatic (on-download / on-change) scanning. It integrates the ClamAV engine via clamdscan, and optionally performs a cloud reputation check using MalwareBazaar based on the SHA-256 hash of the scanned file.

The module is designed to operate asynchronously and avoid blocking the GTK main loop, using:

  • Gio::Subprocess for ClamAV execution
  • Gio::DataInputStream::read_line_async() for non-blocking output parsing
  • inotify + Glib::IOSource for filesystem monitoring
  • Glib::signal_idle() and Glib::signal_timeout() for safe UI updates
  • A worker std::thread for cloud queries (no GTK calls outside the main thread)

User Interface Structure

Action Toolbar

The top of the page contains a button bar providing manual selection and scan controls:

  • btnChooseFolder → triggers ScanPage::onChooseFolder()
  • btnChooseFile → triggers ScanPage::onChooseFile()
  • btnScan → triggers ScanPage::onStartScan()

Automatic Scan Switch

An automatic scanning section is presented with a switch:

  • Label: autoScanLabel (text: “Scansione automatica”)
  • Control: autoScanSwitch (default: active)

When the switch state changes, the handler:

autoScanSwitch.property_active().signal_changed()

performs the following actions:

  • Updates the internal state flag autoScanEnabled
  • Logs a state message (“automatic scan enabled/disabled”)
  • If enabled: starts inotify monitoring via enableAutoScanDownloads() and enableGlobalDownloadMonitor()
  • If disabled: stops monitoring via disableAllMonitors()
  • Persists the configuration via saveConfig()

Log View

The scan page includes a read-only log area implemented with:

  • Gtk::TextBuffer (textBuffer)
  • Gtk::TextView (textView)
  • Gtk::ScrolledWindow

File List View

A dedicated list area displays scanned files during manual scans using a Gtk::ListBox (fileListBox). Items are added via ScanPage::addFileToList() when ClamAV output indicates a file is being scanned.


Logging Model (Thread-Safe UI Logging)

Logging is buffered and flushed to the GTK UI in batches to avoid excessive UI updates and to ensure thread safety.

  • ScanPage::enqueueLog() pushes messages to pendingLog protected by logMutex.
  • A delayed flush is scheduled once per burst using Glib::signal_timeout().connect_once(..., 50).
  • ScanPage::flushLog() drains the queue and appends messages to the Gtk::TextBuffer.

Configuration Persistence

Local Scan Configuration

Automatic scan configuration is stored in:

~/.local/share/BastionGuard/config.json

The following key is persisted:

"auto_scan_enabled": true|false

Functions involved:

  • ScanPage::saveConfig()
  • ScanPage::loadConfig()

Cloud Configuration (API Key)

Cloud reputation checks rely on a locally stored API key. The configuration file is:

~/.config/BastionGuard/cloud.conf

Key stored:

malware_bazaar_api_key=<value>

The module loads and applies the key using:

  • ScanPage::loadCloudConfig() (parses cloud.conf)
  • ScanPage::saveCloudConfig() (writes cloud.conf)
  • Updates the global variable globalMalwareBazaarApiKey

Filesystem Monitoring (inotify)

Initialization

The scan page initializes inotify in non-blocking mode:

inotify_init1(IN_NONBLOCK | IN_CLOEXEC)

The file descriptor is integrated into the GTK main loop using:

Glib::IOSource::create(inotifyFd, Glib::IOCondition::IO_IN)

Events are processed in:

ScanPage::onInotifyEvent(Glib::IOCondition cond)

Recursive Watch Registration

Directory monitoring is set recursively using ScanPage::addInotifyRecursive(root), which:

  • Normalizes paths (removes trailing slashes)
  • Prevents duplicates via alreadyWatched
  • Registers watches with:
IN_CREATE | IN_MOVED_TO | IN_CLOSE_WRITE | IN_ATTRIB

Subdirectories are iterated and hidden directories (name starting with .) are ignored.

Event Filtering and Debouncing

Events are filtered to reduce noise and avoid scanning incomplete downloads:

  • Ignores noisy paths containing: /build, /.cache, /.local, /.git
  • Accepts only: IN_CLOSE_WRITE and IN_MOVED_TO
  • Skips temporary download files: .part, .crdownload
  • Implements deduplication using a recentlyScanned cache
  • Introduces a short stabilization delay before scanning (100ms)

All automatic scans are triggered via:

ScanPage::scanFileAutomatically(path)

Disabling Monitors

When automatic scanning is disabled, all monitors are disabled using ScanPage::disableAllMonitors(), which clears watch state and closes the inotify file descriptor.


Download Directory Detection

The module attempts to detect browser download directories to monitor likely incoming files. Detection is implemented in ScanPage::detectBrowserDownloadDirs() and includes:

  • Default directory: $HOME/Downloads
  • Firefox: reads prefs.js under $HOME/.mozilla/firefox/<profile>/prefs.js and extracts browser.download.dir
  • Chromium-based browsers: reads JSON Preferences from:
$HOME/.config/google-chrome/Default/Preferences
$HOME/.config/chromium/Default/Preferences
$HOME/.config/microsoft-edge/Default/Preferences

Duplicate directories are removed by sorting and applying std::unique.


Global Monitoring Scope

Beyond download directories, BastionGuard can enable broad monitoring via ScanPage::enableGlobalDownloadMonitor(). This includes:

  • $HOME
  • /tmp
  • $HOME/.var/app (if present)
  • XDG user directories parsed from:
$HOME/.config/user-dirs.dirs

XDG entries are resolved by expanding $HOME in paths and validating existence before monitoring.


Manual Scanning (clamdscan)

Scan Start Conditions

Manual scanning is initiated via ScanPage::onStartScan(). The function enforces:

  • Only one scan at a time (scanInProgress)
  • A selected target path must be set (selectedPath)

ClamAV Invocation

The scan is executed using:

clamdscan --fdpass --no-summary --infected <selectedPath>

Output is captured using Gio::Subprocess with stdout/stderr pipes, and parsed asynchronously line-by-line.

Output Parsing

In ScanPage::readOutput():

  • Lines beginning with Scanning cause the scanned file path to be added to the list via addFileToList()
  • Lines containing FOUND are parsed to extract:
<filepath>: <virusName> FOUND

Detected infections are recorded in infectedFilesBuffer and handled through handleInfectedFileWithName(filepath, virusName).


Automatic Scanning (File Integrity Checks)

Automatic scanning is triggered when filesystem events indicate a completed file write or file move. The scan routine includes defensive checks:

  • Rejects non-regular files (directories, symlinks, pipes, sockets) using std::filesystem::is_regular_file()
  • Rejects extremely small files (less than 64 bytes) to avoid scanning incomplete downloads
  • Deduplicates scanning using a composite key of:
<filepath>_<size>_<last_write_time>

Automatic ClamAV scans use the same invocation pattern:

clamdscan --fdpass --no-summary --infected <filepath>

Cloud Reputation Check (MalwareBazaar)

Trigger Conditions

After an automatic scan, if ClamAV does not report an infection, the module performs a cloud reputation lookup via cloudCheckMalwareBazaar(filepath).

Privacy-Preserving Model

The design explicitly avoids uploading files. Instead:

  • BastionGuard computes the SHA-256 hash of the file locally
  • Only the hash value is compared against public threat intelligence databases
  • No local file contents are transmitted

Worker Thread Execution

The cloud lookup is executed in a detached worker thread:

std::thread(&ScanPage::cloudCheckMalwareBazaarWorker, this, filepath).detach();

The worker computes:

MalwareBazaar::sha256_file(filepath)

and then performs:

MalwareBazaar::lookupHash(sha)

UI updates are marshaled back into the GTK main loop using:

Glib::signal_idle().connect_once(...)

Cloud Detection Handling

If a hash match is found, the UI logs details (name, file type, tags) and triggers an alert through D-Bus when available (see next section). If no match is found, the module logs a “no match / safe” status.


Threat Alerts (D-Bus + GUI Fallback)

When malware is detected (via ClamAV or cloud reputation), the module attempts to raise an alert using a session D-Bus interface:

org.BastionGuard.Ransomware.Alert
/org/BastionGuard/ransomware/alert
org.BastionGuard.Ransomware.Alert

Method invoked:

ShowAlert(filepath, familyOrName)

If D-Bus is unavailable or errors occur, the module falls back to a local Gtk::MessageDialog warning dialog.

Infected files are handled through:

  • handleInfectedFile(filepath) → uses family ClamAV.Detected
  • handleInfectedFiles(files) → bulk alerts
  • handleInfectedFileWithName(filepath, virusName) → exact detection name

AUR PKGBUILD Detection and Scanning

The Scan module includes a specific workflow for Arch Linux AUR build scripts (PKGBUILD) via:

ScanPage::onAurPkgbuildDetected(path)

Behavior includes:

  • Special marker handling: INFECTED:<path> triggers a direct infection workflow using family AUR.StaticHeuristic
  • Static heuristic analysis via:
AurScan::scanPKGBUILD(path)

and an additional ClamAV scan via:

ScanPage::scanAurAuto(path)

Cloud Privacy Disclaimer Window

The module provides a dedicated informational UI explaining cloud privacy rules. It is opened via:

ScanPage::showCloudPrivacyDisclaimer()

This window states that BastionGuard:

  • Does not upload files
  • Computes only the SHA-256 hash locally
  • Performs a reputation lookup using public databases
  • Does not transmit local paths or personal data
  • Stores the API key locally only

The disclaimer window uses custom CSS and a custom header bar. It is non-modal and can be closed via the custom close button or the “I understand” button.


Security and Robustness Controls

  • Non-blocking design: scanning and I/O are handled asynchronously to keep the UI responsive.
  • Inotify noise reduction: ignores cache/build directories and temporary download artifacts.
  • File safety gating: only regular files are scanned; extremely small files are skipped.
  • Deduplication: prevents repeated scanning bursts on the same file events.
  • Safe error handling: configuration and file operations use try/catch blocks to prevent crashes.
  • Thread safety: cloud worker threads do not access GTK directly; UI updates are marshaled to the main loop.

Key Files and Paths

  • Auto-scan config: ~/.local/share/BastionGuard/config.json
  • Cloud config: ~/.config/BastionGuard/cloud.conf
  • XDG dirs source: ~/.config/user-dirs.dirs
  • Manual/Auto scan engine: clamdscan
  • D-Bus alert interface: org.BastionGuard.Ransomware.Alert