astrid-runtime/astrid · error

authenticated AstridFS.app is redirected or not a directory

Error message

authenticated AstridFS.app is redirected or not a directory

What it means

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.

Source

Thrown at crates/astrid-cli/src/commands/self_update/mod.rs:400

/// as temp files in `install_dir` (same filesystem) and `rename`d into place —
/// atomic per file on Unix. Windows stages and flushes bytes on the live volume,
/// then uses a recovery journal around handle-relative
/// `SetFileInformationByHandle(FileRenameInfo)` transitions, each atomic at the
/// individual name boundary; an interrupted mixed set is restored on the next
/// attempt. The `.bak` copies are left in place for manual rollback after a
/// successful update.
fn backup_and_swap(install_dir: &Path, extract_dir: &Path, names: &[&str]) -> anyhow::Result<()> {
    astrid_core::platform_fs::replace_executable_set(install_dir, extract_dir, names)
        .context("failed to replace authenticated Astrid executables")
}
fn prepare_macos_update_assets(extract_dir: &Path, target: &str) -> anyhow::Result<()> {
    if !target.contains("-apple-darwin") {
        return Ok(());
    }
    let app = extract_dir.join("AstridFS.app");
    let app_metadata = std::fs::symlink_metadata(&app)
        .with_context(|| format!("authenticated macOS release is missing {}", app.display()))?;
    anyhow::ensure!(
        app_metadata.is_dir() && !app_metadata.file_type().is_symlink(),
        "authenticated AstridFS.app is redirected or not a directory"
    );
    for name in ["manage-macos-fskit.sh", "validate-macos-fskit.sh"] {
        let source = extract_dir.join("macos").join(name);
        let metadata = std::fs::symlink_metadata(&source).with_context(|| {
            format!(
                "authenticated macOS release is missing {}",
                source.display()
            )
        })?;
        anyhow::ensure!(
            metadata.is_file() && !metadata.file_type().is_symlink(),
            "authenticated macOS lifecycle tool is redirected or not regular: {name}"
        );
        std::fs::copy(&source, extract_dir.join(name))?;
    }
    Ok(())

View on GitHub (pinned to affd8760f4)

Solutions

  1. Re-download the release from the official channel so a verified, untampered archive is used
  2. Inspect the archive (tar -tvf) and confirm AstridFS.app is a real directory
  3. Fix the packaging script that produced a symlinked AstridFS.app
  4. Re-run the update after removing any stale extraction directory
Defensive patterns

Strategy: validation

Validate before calling

fn is_real_dir(p: &Path) -> bool {
    std::fs::symlink_metadata(p).map(|m| m.is_dir() && !m.file_type().is_symlink()).unwrap_or(false)
}
assert!(is_real_dir(&extract_dir.join("AstridFS.app")));

Type guard

fn is_real_dir(p: &Path) -> bool {
    std::fs::symlink_metadata(p)
        .map(|m| m.is_dir() && !m.file_type().is_symlink())
        .unwrap_or(false)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/813b4af88f7f88b4. Report an issue: GitHub.