astrid-runtime/astrid · error

process path {} overlaps a sensitive runtime path

Error message

process path {} overlaps a sensitive runtime path

What it means

wrap_with_process_paths checks every granted read/write path against the built-in sensitive runtime masks (resolved from AstridHome). Grants that overlap a built-in mask are rejected with PermissionDenied — except a grant that is strictly inside the user's home root, which is explicitly allowed.

Source

Thrown at crates/astrid-workspace/src/sandbox/mod.rs:356

                bwrap.arg("--ro-bind").arg(&inj.source).arg(&inj.target);
            }

            // #856 read-hole fix: the `--ro-bind / /` above mounts the entire
            // host filesystem read-only, which exposed Astrid's secret/key/state
            // dirs and the operator's home credential stores to the spawned
            // process. Shadow each masked path so the agent reads nothing (see
            // `push_mask_arg`). Placed AFTER the root ro-bind (so it overlays)
            // and the worktree/injection binds; `run/` (socket+token) and `etc/`
            // stay reachable for daemon access. Fail-secure: refuse the spawn if
            // the home is unresolvable.
            let built_in_masks = Self::masked_paths()?;
            let home_root = astrid_core::dirs::AstridHome::resolve()?.home_dir();
            for granted in extra_read_paths.iter().chain(extra_write_paths) {
                if built_in_masks.iter().any(|masked| {
                    paths_overlap(granted, masked)
                        && !(masked == &home_root && granted.starts_with(masked))
                }) {
                    return Err(io::Error::new(
                        io::ErrorKind::PermissionDenied,
                        format!(
                            "process path {} overlaps a sensitive runtime path",
                            granted.display()
                        ),
                    ));
                }
            }
            for masked in &built_in_masks {
                Self::push_mask_arg(&mut bwrap, masked);
            }

            // Caller-supplied masks (the CoW upper/work dirs). Same mechanism,
            // placed after the worktree/injection binds so they overlay; the
            // CoW dirs live OUTSIDE the worktree, so no writable bind needs to
            // punch back through. Existence is validated up front (a missing mask
            // already failed the spawn), so every entry is masked unconditionally.
            for masked in extra_masks {

View on GitHub (pinned to affd8760f4)

Solutions

  1. Move the grant to a subpath under home_root so the allowed exception applies
  2. Narrow the granted path to exclude the sensitive runtime directory
  3. Canonicalize the grant and re-check overlap; a trailing-slash or .. component can make it envelope the mask
  4. Relocate the workspace/process data outside the sensitive runtime tree

Example fix

// before
read_paths.push(home.join(".astrid")); // is the sensitive runtime root itself
// after
read_paths.push(home.join("projects")); // strictly beneath home_root
Defensive patterns

Strategy: validation

Validate before calling

let home = astrid_core::dirs::AstridHome::resolve()?.home_dir();
if !granted.starts_with(&home) {
    panic!("grant {} must live under AstridHome", granted.display());
}

Type guard

fn under_home(p: &Path, home: &Path) -> bool {
    p.starts_with(home)
}

Try / catch

match wrap_with_process_paths(...) {
    Err(e) if e.to_string().contains("sensitive runtime path") =>
        eprintln!("move grant under home or off the runtime path: {e}"),
    other => other?,
}

Prevention

When it happens

Trigger: Calling wrap_with_process_paths with extra_read_paths/extra_write_paths entries that overlap built-in masks such as the AstridHome runtime directories (and are not home_root-relative), or that equal the home root without starting beneath it as a proper subpath.

Common situations: Pointing a process grant at the AstridHome config/state directory directly; granting a parent directory (e.g. $HOME or /etc) that envelopes a sensitive runtime path; after relocating AstridHome so an old grant now collides with the new runtime location.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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