1. Overview
The USB Devices module provides detection and on-demand malware scanning for removable USB storage devices. It identifies removable block devices via libudev, presents them as UI cards, and allows the user to mount and scan the device contents using ClamAV (clamdscan).
The module supports:
- USB device discovery (removable block devices)
- UI cards with device metadata (name, node, size, serial if available)
- Manual refresh of device list and log view
- Optional auto-scan refresh every 3 minutes (persisted user setting)
- Automatic detection of newly connected devices and non-blocking scan prompt popup
- Mounting via
udisksctlwithout blocking the UI - Live scan output streaming to a dedicated dialog
- D-Bus alert emission for infected files detected during scan
- Local logging with UI-safe asynchronous updates
2. User Interface Structure
2.1 Header
The page uses a security-themed header consistent with other modules.
- Container:
Gtk::Boxwith CSS classsecure-headerbar - Centered title label with CSS class
secure-header-title
Displayed title:
🖴 USB Devices
2.2 Controls
The control bar contains:
- Manual refresh button (
btn_refresh_) that refreshes both device cards and log view - Auto-scan refresh toggle (
chk_auto_refresh_) labeled “Auto scan every 3 minutes”
When manual refresh is clicked, the module executes:
update_cards()update_log_view()write_log(...)
2.3 Devices Cards Area
Detected devices are rendered in a Gtk::FlowBox configured with:
- Maximum 3 cards per line
- No selection mode
- Row/column spacing for visual separation
The flowbox is wrapped in a scrolled window to support long lists of devices.
2.4 Daily Log Viewer
The module includes a scrollable log viewer area (txt_log_ inside scroller_log_) used to display the USB module log. The view is read-only and word-wrapped.
3. Settings Persistence (USB Auto-Scan Refresh)
The module persists the auto-scan refresh preference in a user configuration file:
~/.config/BastionGuard/usb.conf
Stored format:
auto_scan=1
auto_scan=0
Persistence helpers:
save_usb_autoscan(bool enabled)load_usb_autoscan()
If the setting is enabled, the module schedules a refresh timer every 180 seconds (3 minutes) using Glib::signal_timeout().connect_seconds().
4. USB Device Detection
4.1 Detection Source
USB devices are detected using libudev by enumerating block devices with:
- Subsystem:
block - Property:
DEVTYPE=disk
Filtering rules:
- Optical devices (
/dev/sr*) are ignored - The device must be removable (
removable == "1") - The device must have a valid size (non-zero sectors)
- The device must have a USB parent (
usb_device)
4.2 Device Metadata
For each valid device, the module builds a USBDeviceInfo record containing:
devnode(example:/dev/sdb)size_bytescalculated assectors * 512namederived from manufacturer + product (fallback: devnode)uidderived from serial (fallback: udev syspath)
Size is displayed using a human-readable formatter (human_readable_size()).
5. Device Cards Rendering
Each detected USB device is rendered as a card using build_usb_card(dev). The card includes:
- A USB icon loaded from project resources
- A title label with the device name
- Information label with devnode, optional serial number, and size
- A single action button: Mount and Scan
Pressing the action button triggers:
run_clamdscan_async(dev.devnode)
6. Device Mount Workflow
6.1 Determining a Mountable Partition
When scanning starts, the module attempts to mount a mountable target. If the devnode is a raw disk (example: /dev/sdX) and appears without an explicit partition suffix, the module attempts to mount sdX1 if it exists.
6.2 Detecting Existing Mounts
Before mounting, the module checks /proc/mounts to determine if the partition is already mounted.
If already mounted, it uses the existing mountpoint and proceeds to scanning.
6.3 Mounting via udisksctl
Mounting is performed via:
udisksctl mount -b <partition> --options umask=022
The module parses the command output in order to extract the mountpoint path from the “Mounted … at <PATH>” format. The mountpoint is validated by checking that it exists and is a directory.
7. Malware Scan Workflow (clamdscan)
7.1 Concurrency and Per-Device Scan State
The module prevents concurrent scans on the same USB device by maintaining a per-device state map (device_state_) protected by a mutex. The helper try_start_scan(uid) blocks scan start if the device is already in Mounting or Scanning state.
7.2 Live Scan Dialog
Once the device is mounted, the module creates a live output dialog (LiveScanDialog) to display scan progress and streamed output lines.
The scan is performed using:
clamdscan --verbose --fdpass --remove=no <mountpoint>
STDOUT is read asynchronously line-by-line. Each line is appended to the live dialog using GTK main-loop scheduling to ensure UI safety.
7.3 Completion Handling
When the subprocess output ends, the module:
- Waits for subprocess completion
- Clears the per-device scan state entry
- Marks the live dialog as finished
- Stores full output to the USB log
- Closes the live dialog
- Shows a scan result dialog indicating clean or infected outcome
Infection is detected by searching for FOUND in the scan output.
8. Scan Result Dialog
After scanning completes, the module shows a dedicated result dialog that provides a simplified outcome message:
- Clean outcome: scan completed without threats
- Infected outcome: threats detected and user is directed to quarantine
The dialog uses custom CSS styling consistent with other BastionGuard dialogs and selects an OK/Threat icon from system data paths.
9. Automatic Detection of Newly Connected USB Devices
9.1 Monitoring Thread
The module runs a background monitoring thread that polls detected USB devices every 2 seconds. It maintains a “previous” snapshot and compares it with the current list to detect:
- Newly connected devices
- Removed devices
On first run, the module records the initial set and suppresses notifications to avoid showing popups for already-connected devices.
9.2 Scan Prompt Popup
When a new device is detected, a non-modal popup (ScanPromptWindow) is displayed to ask the user whether to start a scan.
The popup emits:
signal_scan_requested()→ triggersrun_clamdscan_async(dev)signal_cancel_requested()→ logs cancellation only
The popup is explicitly non-blocking and destroyed on hide to avoid memory leaks.
10. D-Bus Alerts for Infected Files
If the scan output contains infected file lines, the module parses each matching line and sends a D-Bus alert via:
org.BastionGuard.Ransomware.Alert
/org/BastionGuard/ransomware/alert
org.BastionGuard.Ransomware.Alert.ShowAlert(file, family)
This is handled by:
show_infected_dialog(text)– parses output lines containingFOUNDsend_dbus_alert(file, family)– sends the alert as a tuple of two strings
11. Logging System
11.1 Log File Path
The module uses a dedicated log file:
~/.bastionguard_usb.log
11.2 Thread-Safe Logging + UI Updates
write_log(msg) performs:
- Thread-safe append to the log file with timestamp
- Queueing of log lines for GUI update
- A single scheduled idle callback that flushes queued lines into the UI text buffer
This design prevents frequent UI updates from multiple threads while keeping the UI log view responsive and ordered.
12. Threading and UI Safety
- USB monitoring runs in a dedicated thread (polling every 2 seconds).
- Mount and scan operations run asynchronously and do not block the UI.
- Subprocess output is streamed asynchronously with GTK-safe UI scheduling.
- Log UI updates are batched and flushed via a single idle callback.
- Per-device scan state is guarded by a mutex to prevent concurrent scanning of the same device.
13. Security Considerations
- Explicit user action: scanning requires user confirmation or explicit interaction with the card button.
- Controlled mounting: mounting is performed through
udisksctlwith standard options and validated mountpoints. - Alert propagation: infected detections are propagated via D-Bus to the BastionGuard alert subsystem.
- Auditability: all relevant actions (device insert/remove, scan start, scan output, user prompts) are logged to a dedicated log file.