-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Summary
FileExecutor::new does not expand ~ in path strings. When a user sets allowed_paths = ["~/Documents/git/zeph"] in config, the tilde is not resolved to the home directory by Rust's Path::canonicalize(). The unwrap_or(p) fallback retains the literal ~/ path, so starts_with never matches real absolute paths — silently blocking all file access.
Root Cause
crates/zeph-tools/src/file.rs:112:
.map(|p| p.canonicalize().unwrap_or(p))Path::canonicalize("~/Documents/git/zeph") returns Err (no such file — tilde is not a shell metacharacter in Rust filesystem APIs). unwrap_or(p) keeps the broken path.
Then validate_path (line 126):
if !self.allowed_paths.iter().any(|a| canonical.starts_with(a)) {
return Err(ToolError::SandboxViolation { ... });
}/Users/rabax/Documents/git/zeph/Cargo.toml does not start with ~/Documents/git/zeph → SandboxViolation on every file access.
Effect
When allowed_paths contains any ~-prefixed entry and no other valid absolute paths, all file access is silently blocked. The agent starts without any error, but every read, write, edit, grep, etc. call returns a sandbox violation.
This was observed in the user's ~/.config/zeph/default.toml config:
allowed_paths = ["~/Documents/git/zeph", ".claude/rules"]Only .claude/rules (relative, correctly canonicalized) was accessible. The entire project tree was blocked.
Fix
Expand ~ in FileExecutor::new before canonicalization:
.map(|p| {
let expanded = if let Some(stripped) = p.strip_prefix("~") {
dirs::home_dir().map(|h| h.join(stripped)).unwrap_or(p.clone())
} else {
p.clone()
};
expanded.canonicalize().unwrap_or(expanded)
})Or use the shellexpand crate for full tilde + env var expansion.
Workaround
Set allowed_paths = [] to use CWD as the sandbox root (the default behavior when the list is empty). Applied to user config as immediate fix.
Filed
CI-76, 2026-03-22