From 996bf871cdcd9f7c37f261d84085f66ce4b5deaa Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Sat, 24 Oct 2026 04:36:46 -0400 Subject: [PATCH] fix(security): prevent symlink traversal in asset walker, safe cert tmpdir, and atomic icon copy (#1255) --- packaging/macos/import-cert.sh | 5 +++-- xtask/src/icon.rs | 8 ++++++-- xtask/src/main.rs | 5 +++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packaging/macos/import-cert.sh b/packaging/macos/import-cert.sh index 77611537..aeb58fd4 100755 --- a/packaging/macos/import-cert.sh +++ b/packaging/macos/import-cert.sh @@ -17,12 +17,13 @@ if [[ -z "${APPLE_CERTIFICATE:-}" ]]; then exit 0 fi -TMP="${RUNNER_TEMP:-${TMPDIR:-/tmp}}" +TMP_BASE="${RUNNER_TEMP:-${TMPDIR:-/tmp}}" +TMP="$(mktemp -d "${TMP_BASE}/figcraft-signing.XXXXXX")" +trap 'rm -rf "$TMP"' EXIT KEYCHAIN="$TMP/figcraft-signing.keychain-db" CERT="$TMP/figcraft-signing.p12" KC_PASS="${KEYCHAIN_PASSWORD:-$(openssl rand -hex 24)}" -trap 'rm -f "$CERT"' EXIT printf '%s' "$APPLE_CERTIFICATE" | base64 --decode >"$CERT" security create-keychain -p "$KC_PASS" "$KEYCHAIN" security set-keychain-settings -lut 21600 "$KEYCHAIN" diff --git a/xtask/src/icon.rs b/xtask/src/icon.rs index b91fccee..91b51aef 100644 --- a/xtask/src/icon.rs +++ b/xtask/src/icon.rs @@ -67,11 +67,15 @@ pub fn generate(root: &Path) -> bool { for png in &pngs { ico.extend_from_slice(png); } - ok &= std::fs::write(dir.join("figcraft.ico"), ico).is_ok(); + let ico_path = dir.join("figcraft.ico"); + let _ = std::fs::remove_file(&ico_path); + ok &= std::fs::write(&ico_path, ico).is_ok(); // The lighter trace keeps the scalable theme icon cheap to render. let sd = dir.join("hicolor/scalable/apps"); let _ = std::fs::create_dir_all(&sd); - ok &= std::fs::copy(dir.join("figcraft-small.svg"), sd.join("ai.storyteller.figcraft.svg")).is_ok(); + let target = sd.join("ai.storyteller.figcraft.svg"); + let _ = std::fs::remove_file(&target); + ok &= std::fs::copy(dir.join("figcraft-small.svg"), &target).is_ok(); eprintln!("icon: wrote {}", dir.display()); ok } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 313de3c6..e7f7b386 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -201,10 +201,11 @@ fn assets() -> bool { fn walk(dir: &std::path::Path, f: &mut dyn FnMut(&std::path::Path)) { let Ok(rd) = std::fs::read_dir(dir) else { return }; for e in rd.flatten() { + let Ok(ft) = e.file_type() else { continue }; let p = e.path(); - if p.is_dir() { + if ft.is_dir() && !ft.is_symlink() { walk(&p, f); - } else { + } else if !ft.is_dir() { f(&p); } }