Fix 29 audit findings across all severity tiers
Critical: fix unsquashfs arg order, quote Exec paths with spaces, fix compare_versions antisymmetry, chunk-based signature detection, bounded ELF header reads. High: handle NULL CVE severity, prevent pipe deadlock in inspector, fix glob_match edge case, fix backup archive path collisions, async crash detection with stderr capture. Medium: gate scan on auto-scan setting, fix window size persistence, fix announce() for Stack containers, claim lightbox gesture, use serde_json for CLI output, remove dead CSS @media blocks, add detail-tab persistence, remove invalid metainfo categories, byte-level fuse signature search. Low: tighten Wayland env var detection, ELF magic validation, timeout for update info extraction, quoted arg parsing, stop watcher timer on window destroy, GSettings choices/range constraints, remove unused CSS classes, define status-ok/status-attention CSS.
This commit is contained in:
@@ -595,29 +595,30 @@ impl DriftwoodWindow {
|
||||
let Some(record_id) = param.and_then(|p| p.get::<i64>()) else { return };
|
||||
let toast_overlay = window.imp().toast_overlay.get().unwrap().clone();
|
||||
let window_ref = window.clone();
|
||||
let (path_str, app_name) = {
|
||||
let (path_str, app_name, launch_args_raw) = {
|
||||
let db = window.database();
|
||||
match db.get_appimage_by_id(record_id) {
|
||||
Ok(Some(r)) => {
|
||||
let name = r.app_name.clone().unwrap_or_else(|| r.filename.clone());
|
||||
(r.path.clone(), name)
|
||||
(r.path.clone(), name, r.launch_args.clone())
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
let launch_args = launcher::parse_launch_args(launch_args_raw.as_deref());
|
||||
glib::spawn_future_local(async move {
|
||||
let path_bg = path_str.clone();
|
||||
let result = gio::spawn_blocking(move || {
|
||||
let bg_db = crate::core::database::Database::open().expect("DB open");
|
||||
let appimage_path = std::path::Path::new(&path_bg);
|
||||
launcher::launch_appimage(&bg_db, record_id, appimage_path, "gui_context", &[], &[])
|
||||
launcher::launch_appimage(&bg_db, record_id, appimage_path, "gui_context", &launch_args, &[])
|
||||
}).await;
|
||||
match result {
|
||||
Ok(launcher::LaunchResult::Started { child, method }) => {
|
||||
log::info!("Launched: {} (PID: {}, method: {})", path_str, child.id(), method.as_str());
|
||||
}
|
||||
Ok(launcher::LaunchResult::Crashed { exit_code, stderr, .. }) => {
|
||||
log::error!("App crashed (exit {}): {}", exit_code.unwrap_or(-1), stderr);
|
||||
Ok(launcher::LaunchResult::Crashed { exit_code, stderr, method }) => {
|
||||
log::error!("App crashed (exit {}, method: {}): {}", exit_code.unwrap_or(-1), method.as_str(), stderr);
|
||||
widgets::show_crash_dialog(&window_ref, &app_name, exit_code, &stderr);
|
||||
}
|
||||
Ok(launcher::LaunchResult::Failed(msg)) => {
|
||||
@@ -830,12 +831,49 @@ impl DriftwoodWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// Always scan on startup to discover new AppImages and complete pending analyses
|
||||
self.trigger_scan();
|
||||
// Scan on startup if enabled in preferences
|
||||
if self.settings().boolean("auto-scan-on-startup") {
|
||||
self.trigger_scan();
|
||||
}
|
||||
|
||||
// Start watching scan directories for new AppImage files
|
||||
self.start_file_watcher();
|
||||
|
||||
// Auto-cleanup old backups based on retention setting
|
||||
let retention_days = self.settings().int("backup-retention-days") as u32;
|
||||
glib::spawn_future_local(async move {
|
||||
let _ = gio::spawn_blocking(move || {
|
||||
let bg_db = Database::open().expect("Failed to open database");
|
||||
match crate::core::backup::auto_cleanup_old_backups(&bg_db, retention_days) {
|
||||
Ok(removed) if removed > 0 => {
|
||||
log::info!("Auto-cleaned {} old backup(s) (retention: {} days)", removed, retention_days);
|
||||
}
|
||||
Err(e) => log::warn!("Backup auto-cleanup failed: {}", e),
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
|
||||
// Run background security scan and notify if auto-security-scan is enabled
|
||||
let settings_sec = self.settings().clone();
|
||||
if settings_sec.boolean("auto-security-scan") {
|
||||
let threshold = settings_sec.string("security-notification-threshold").to_string();
|
||||
glib::spawn_future_local(async move {
|
||||
let _ = gio::spawn_blocking(move || {
|
||||
let bg_db = Database::open().expect("Failed to open database");
|
||||
let notifications = notification::scan_and_notify(&bg_db, &threshold);
|
||||
for n in ¬ifications {
|
||||
log::info!(
|
||||
"CVE notification sent: app={} (id={}), severity={}, count={}",
|
||||
n.app_name, n.appimage_id, n.severity, n.cve_count,
|
||||
);
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
||||
// Check for orphaned desktop entries in the background
|
||||
let toast_overlay = self.imp().toast_overlay.get().unwrap().clone();
|
||||
glib::spawn_future_local(async move {
|
||||
@@ -989,6 +1027,10 @@ impl DriftwoodWindow {
|
||||
toast_overlay.add_toast(adw::Toast::new(&msg));
|
||||
|
||||
// Phase 2: Background analysis per file with debounced UI refresh
|
||||
let running = analysis::running_count();
|
||||
if running > 0 {
|
||||
log::info!("Analyzing {} AppImage(s) in background ({} already running)", needs_analysis.len(), running);
|
||||
}
|
||||
if !needs_analysis.is_empty() {
|
||||
let pending = Rc::new(std::cell::Cell::new(needs_analysis.len()));
|
||||
let refresh_timer: Rc<std::cell::Cell<Option<glib::SourceId>>> =
|
||||
@@ -1057,20 +1099,27 @@ impl DriftwoodWindow {
|
||||
let changed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
|
||||
let changed_watcher = changed.clone();
|
||||
let handle = watcher::start_watcher(dirs, move |_event| {
|
||||
let handle = watcher::start_watcher(dirs, move |event| {
|
||||
match &event {
|
||||
watcher::WatchEvent::Changed(paths) => {
|
||||
log::info!("File watcher: {} path(s) changed: {:?}", paths.len(), paths);
|
||||
}
|
||||
}
|
||||
changed_watcher.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
});
|
||||
|
||||
if let Some(h) = handle {
|
||||
self.imp().watcher_handle.replace(Some(h));
|
||||
|
||||
// Poll the flag every second from the main thread
|
||||
// Poll the flag every second from the main thread.
|
||||
// Returns Break when the window is gone to stop the timer.
|
||||
let window_weak = self.downgrade();
|
||||
glib::timeout_add_local(std::time::Duration::from_secs(1), move || {
|
||||
let Some(window) = window_weak.upgrade() else {
|
||||
return glib::ControlFlow::Break;
|
||||
};
|
||||
if changed.swap(false, std::sync::atomic::Ordering::Relaxed) {
|
||||
if let Some(window) = window_weak.upgrade() {
|
||||
window.trigger_scan();
|
||||
}
|
||||
window.trigger_scan();
|
||||
}
|
||||
glib::ControlFlow::Continue
|
||||
});
|
||||
@@ -1204,7 +1253,7 @@ impl DriftwoodWindow {
|
||||
|
||||
fn save_window_state(&self) {
|
||||
let settings = self.settings();
|
||||
let (width, height) = self.default_size();
|
||||
let (width, height) = (self.width(), self.height());
|
||||
settings
|
||||
.set_int("window-width", width)
|
||||
.expect("Failed to save window width");
|
||||
|
||||
Reference in New Issue
Block a user