{"record":{"id":"813b4af88f7f88b4","repo":"astrid-runtime/astrid","slug":"authenticated-astridfs-app-is-redirected-or-not-a","errorCode":null,"errorMessage":"authenticated AstridFS.app is redirected or not a directory","messagePattern":"authenticated AstridFS\\.app is redirected or not a directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/self_update/mod.rs","lineNumber":400,"sourceCode":"/// as temp files in `install_dir` (same filesystem) and `rename`d into place —\n/// atomic per file on Unix. Windows stages and flushes bytes on the live volume,\n/// then uses a recovery journal around handle-relative\n/// `SetFileInformationByHandle(FileRenameInfo)` transitions, each atomic at the\n/// individual name boundary; an interrupted mixed set is restored on the next\n/// attempt. The `.bak` copies are left in place for manual rollback after a\n/// successful update.\nfn backup_and_swap(install_dir: &Path, extract_dir: &Path, names: &[&str]) -> anyhow::Result<()> {\n    astrid_core::platform_fs::replace_executable_set(install_dir, extract_dir, names)\n        .context(\"failed to replace authenticated Astrid executables\")\n}\nfn prepare_macos_update_assets(extract_dir: &Path, target: &str) -> anyhow::Result<()> {\n    if !target.contains(\"-apple-darwin\") {\n        return Ok(());\n    }\n    let app = extract_dir.join(\"AstridFS.app\");\n    let app_metadata = std::fs::symlink_metadata(&app)\n        .with_context(|| format!(\"authenticated macOS release is missing {}\", app.display()))?;\n    anyhow::ensure!(\n        app_metadata.is_dir() && !app_metadata.file_type().is_symlink(),\n        \"authenticated AstridFS.app is redirected or not a directory\"\n    );\n    for name in [\"manage-macos-fskit.sh\", \"validate-macos-fskit.sh\"] {\n        let source = extract_dir.join(\"macos\").join(name);\n        let metadata = std::fs::symlink_metadata(&source).with_context(|| {\n            format!(\n                \"authenticated macOS release is missing {}\",\n                source.display()\n            )\n        })?;\n        anyhow::ensure!(\n            metadata.is_file() && !metadata.file_type().is_symlink(),\n            \"authenticated macOS lifecycle tool is redirected or not regular: {name}\"\n        );\n        std::fs::copy(&source, extract_dir.join(name))?;\n    }\n    Ok(())","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/self_update/mod.rs#L382-L418","documentation":"prepare_macos_update_assets validates the contents of an authenticated (signed, verified) macOS update archive before installing. It requires AstridFS.app inside the extract directory to be a real directory, not a symlink or other redirected entry. This guards against symlink-based path redirection attacks sneaking into the privileged update path.","triggerScenarios":"apply_authenticated_update processes a -apple-darwin release archive whose extracted AstridFS.app is missing its directory nature: it is a symlink, a regular file, or otherwise not a plain directory per symlink_metadata.","commonSituations":"A tampered or repackaged release archive; a build/packaging script that accidentally symlinks AstridFS.app; extracting the archive in a way that converted entries into symlinks.","solutions":["Re-download the release from the official channel so a verified, untampered archive is used","Inspect the archive (tar -tvf) and confirm AstridFS.app is a real directory","Fix the packaging script that produced a symlinked AstridFS.app","Re-run the update after removing any stale extraction directory"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn is_real_dir(p: &Path) -> bool {\n    std::fs::symlink_metadata(p).map(|m| m.is_dir() && !m.file_type().is_symlink()).unwrap_or(false)\n}\nassert!(is_real_dir(&extract_dir.join(\"AstridFS.app\")));","typeGuard":"fn is_real_dir(p: &Path) -> bool {\n    std::fs::symlink_metadata(p)\n        .map(|m| m.is_dir() && !m.file_type().is_symlink())\n        .unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Only install from official signed release archives","Inspect archive contents (tar -tvf) for symlinks before extracting","Reject any archive packaging that symlinks app bundles","Re-extract to a clean directory on failure"],"tags":["macos","security","symlink","archive"],"backgroundTag":"path-traversal-blocked","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}