{"record":{"id":"bf955586099db1b0","repo":"astrid-runtime/astrid","slug":"sandbox-label-contains-forbidden-characters-dou","errorCode":null,"errorMessage":"sandbox {label} contains forbidden characters (double-quote, backslash, or null): {}","messagePattern":"sandbox (.+?) contains forbidden characters \\(double-quote, backslash, or null\\): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-workspace/src/sandbox/mod.rs","lineNumber":32,"sourceCode":"/// all of which can break or bypass sandbox profile syntax.\nfn validate_sandbox_str<'a>(path: &'a Path, label: &str) -> io::Result<&'a str> {\n    if !path.is_absolute() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            format!(\n                \"sandbox {label} must be an absolute path, got: {}\",\n                path.display()\n            ),\n        ));\n    }\n    let s = path.to_str().ok_or_else(|| {\n        io::Error::new(\n            io::ErrorKind::InvalidInput,\n            format!(\"sandbox {label} is not valid UTF-8: {}\", path.display()),\n        )\n    })?;\n    if s.contains(['\"', '\\\\', '\\0']) {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            format!(\n                \"sandbox {label} contains forbidden characters (double-quote, backslash, or null): {}\",\n                path.display()\n            ),\n        ));\n    }\n    Ok(s)\n}\n\n/// A host-verified, read-only file the sandbox materializes inside a spawned\n/// child. `source` is the host-owned path the verified snapshot already lives\n/// at (the in-sandbox bytes are bound/copied FROM here); `target` is the\n/// absolute path inside the child's sandbox at which it reads those bytes.\n///\n/// The caller is responsible for ensuring `source` is a host-owned location\n/// the child and the spawning principal's capsule fs surface cannot write —\n/// the sandbox layer only wires the read-only exposure, it does not snapshot.","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-workspace/src/sandbox/mod.rs#L14-L50","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet p = PathBuf::from(user_supplied); // may contain \" or \\\nwrap_with_process_paths(&ws, &[p], &[])?;\n// after\nlet cleaned = user_supplied.replace('\\\\', \"/\");\nif cleaned.contains(['\"', '\\\\', '\\0']) { return Err(...); }\nwrap_with_process_paths(&ws, &[PathBuf::from(cleaned)], &[])?;","handlingStrategy":"validation","validationCode":"fn sandbox_safe(p: &Path) -> bool { p.to_str().map_or(false, |s| !s.contains(['\"', '\\\\', '\\0'])) }","typeGuard":"fn has_forbidden_chars(p: &Path) -> bool { p.to_str().map_or(false, |s| s.contains(['\"', '\\\\', '\\0'])) }","tryCatchPattern":"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); }","preventionTips":["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"],"tags":["sandbox","security","path","validation"],"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-14T05:17:10.506Z"}