astrid-runtime/astrid · error
sandbox {label} contains forbidden characters (double-quote,
Error message
sandbox {label} contains forbidden characters (double-quote, backslash, or null): {} What it means
Paths are interpolated verbatim into sandbox profile text (SBPL/bwrap), where double-quote, backslash, and NUL can break out of or corrupt the profile syntax and weaken the sandbox. validate_sandbox_str rejects any path containing these characters with InvalidInput.
Source
Thrown at crates/astrid-workspace/src/sandbox/mod.rs:32
/// all of which can break or bypass sandbox profile syntax.
fn validate_sandbox_str<'a>(path: &'a Path, label: &str) -> io::Result<&'a str> {
if !path.is_absolute() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"sandbox {label} must be an absolute path, got: {}",
path.display()
),
));
}
let s = path.to_str().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("sandbox {label} is not valid UTF-8: {}", path.display()),
)
})?;
if s.contains(['"', '\\', '\0']) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"sandbox {label} contains forbidden characters (double-quote, backslash, or null): {}",
path.display()
),
));
}
Ok(s)
}
/// A host-verified, read-only file the sandbox materializes inside a spawned
/// child. `source` is the host-owned path the verified snapshot already lives
/// at (the in-sandbox bytes are bound/copied FROM here); `target` is the
/// absolute path inside the child's sandbox at which it reads those bytes.
///
/// The caller is responsible for ensuring `source` is a host-owned location
/// the child and the spawning principal's capsule fs surface cannot write —
/// the sandbox layer only wires the read-only exposure, it does not snapshot.View on GitHub (pinned to affd8760f4)
Solutions
- Remove or replace the forbidden characters in the path (e.g. normalize backslashes to forward slashes)
- Reject the path at your own input boundary: if p.as_os_str().to_string_lossy().contains(['"','\\','\0']) bail early
- Never build paths by string-concatenating untrusted input; use PathBuf::join
- If escaping is genuinely needed, do not escape yourself — the library intentionally refuses, so use a path without these characters
Example fix
// before
let p = PathBuf::from(user_supplied); // may contain " or \
wrap_with_process_paths(&ws, &[p], &[])?;
// after
let cleaned = user_supplied.replace('\\', "/");
if cleaned.contains(['"', '\\', '\0']) { return Err(...); }
wrap_with_process_paths(&ws, &[PathBuf::from(cleaned)], &[])?; Defensive patterns
Strategy: validation
Validate before calling
fn sandbox_safe(p: &Path) -> bool { p.to_str().map_or(false, |s| !s.contains(['"', '\\', '\0'])) } Type guard
fn has_forbidden_chars(p: &Path) -> bool { p.to_str().map_or(false, |s| s.contains(['"', '\\', '\0'])) } Try / catch
if let Err(e) = wrap_with_process_paths(&ws, &paths, &[]) { if e.to_string().contains("forbidden characters") { /* reject the path; do not attempt to escape it yourself */ } return Err(e); } Prevention
- Never interpolate untrusted strings into paths
- Normalize Windows-style backslash paths before use on Unix/macOS
- Treat this as a hard reject: escaping would defeat the library's security guarantee
When it happens
Trigger: Calling wrap_with_process_paths, validate_all_paths, or build_seatbelt_prefix with a path containing '"', '\\', or '\0' — e.g. a Windows-style path with backslashes used on the macOS Seatbelt path, or a crafted path from untrusted input.
Common situations: Untrusted user input used as a path; copy-pasted Windows paths with backslashes; generated paths from templates that accidentally include quotes; paths assembled from binary data retaining NULs.
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.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- git history path must be a relative in-repository path
- sandbox {label} must be an absolute path, got: {}
- sandbox {label} is not valid UTF-8: {}
- process read path does not exist: {}
- process write path does not exist: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/bf955586099db1b0.
Report an issue: GitHub.