Fix second audit findings and restore crash detection dialog

Address 29 issues found in comprehensive API/spec audit:
- Fix .desktop Exec key path escaping per Desktop Entry spec
- Fix update dialog double-dispatch with connect_response
- Fix version comparison total ordering with lexicographic fallback
- Use RETURNING id for reliable upsert in database
- Replace tilde-based path fallbacks with proper XDG helpers
- Fix backup create/restore path asymmetry for non-home paths
- HTML-escape severity class in security reports
- Use AppStream <custom> element instead of <metadata>
- Fix has_appimage_update_tool to check .is_ok() not .success()
- Use ListBoxRow instead of ActionRow::set_child in ExpanderRow
- Add ELF magic validation to architecture detection
- Add timeout to extract_update_info_runtime
- Skip symlinks in dir_size calculation
- Use Condvar instead of busy-wait in analysis thread pool
- Restore crash detection to single blocking call architecture
This commit is contained in:
lashman
2026-02-27 22:48:43 +02:00
parent e9343da249
commit 830c3cad9d
21 changed files with 228 additions and 181 deletions

View File

@@ -25,20 +25,34 @@ impl From<std::io::Error> for IntegrationError {
}
}
/// Escape a string for use inside a double-quoted Exec argument in a .desktop file.
/// Per the Desktop Entry spec, `\`, `"`, `` ` ``, and `$` must be escaped with `\`.
fn escape_exec_arg(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for c in s.chars() {
match c {
'\\' | '"' | '`' | '$' => {
out.push('\\');
out.push(c);
}
_ => out.push(c),
}
}
out
}
pub struct IntegrationResult {
pub desktop_file_path: PathBuf,
pub icon_install_path: Option<PathBuf>,
}
fn applications_dir() -> PathBuf {
dirs::data_dir()
.unwrap_or_else(|| PathBuf::from("~/.local/share"))
crate::config::data_dir_fallback()
.join("applications")
}
fn icons_dir() -> PathBuf {
dirs::data_dir()
.unwrap_or_else(|| PathBuf::from("~/.local/share"))
crate::config::data_dir_fallback()
.join("icons/hicolor")
}
@@ -90,21 +104,22 @@ pub fn integrate(record: &AppImageRecord) -> Result<IntegrationResult, Integrati
let icon_id = format!("driftwood-{}", app_id);
let desktop_content = format!(
"[Desktop Entry]\n\
Type=Application\n\
Name={name}\n\
Exec=\"{exec}\" %U\n\
Icon={icon}\n\
Categories={categories}\n\
Comment={comment}\n\
Terminal=false\n\
X-AppImage-Path={path}\n\
X-AppImage-Version={version}\n\
X-AppImage-Managed-By=Driftwood\n\
X-AppImage-Integrated-Date={date}\n",
let desktop_content = format!("\
[Desktop Entry]
Type=Application
Name={name}
Exec=\"{exec}\" %U
Icon={icon}
Categories={categories}
Comment={comment}
Terminal=false
X-AppImage-Path={path}
X-AppImage-Version={version}
X-AppImage-Managed-By=Driftwood
X-AppImage-Integrated-Date={date}
",
name = app_name,
exec = record.path,
exec = escape_exec_arg(&record.path),
icon = icon_id,
categories = categories,
comment = comment,