Hmbown/CodeWhale · error · anyhow::Error
persistent path allow rules must stay within the workspace
Error message
persistent path allow rules must stay within the workspace
What it means
append_allow_rules (crates/config/src/lib.rs:5278) rejects a path allow rule whose path fails codewhale_execpolicy::normalize_workspace_relative_path relative to the rule's workspace, or normalizes to empty. Paths must stay inside the consenting workspace: absolute paths, '..' escapes, and '.'/'/' style empties are all refused so an allow cannot leak beyond the workspace boundary.
Source
Thrown at crates/config/src/lib.rs:5278
else {
bail!("persistent allow rules must be scoped to a workspace");
};
if rule.command.is_some() && !rule.command_exact {
bail!("persistent command allow rules must use exact matching");
}
if rule.command.is_none() && rule.path.is_none() {
bail!("persistent allow rules must match an exact command or path");
}
if let Some(command) = rule.command.as_deref()
&& command.trim().is_empty()
{
bail!("persistent command allow rules must not be empty");
}
if let Some(path) = rule.path.as_deref()
&& codewhale_execpolicy::normalize_workspace_relative_path(path, &workspace)
.is_none_or(|path| path.is_empty())
{
bail!("persistent path allow rules must stay within the workspace");
}
}
self.append_permission_rules(rules, PermissionAction::Allow)
}
fn append_permission_rules(
&mut self,
rules: &[ToolAskRule],
expected_action: PermissionAction,
) -> Result<usize> {
if rules.is_empty() {
return Ok(0);
}
if rules.iter().any(|rule| rule.action != expected_action) {
bail!(
"permission rule action does not match requested {:?} persistence",
expected_action
);View on GitHub (pinned to 0c42157ee5)
Solutions
- Convert the approved path to a workspace-relative form (strip the workspace prefix, remove '..')
- If the target genuinely lives outside the workspace, it cannot be persistently allowed — keep it session-scoped
- Validate the path through normalize_workspace_relative_path in the UI before offering 'always allow'
Example fix
# before path = "/home/me/project/target/bin/prog" # absolute -> rejected # after workspace = "/home/me/project" path = "target/bin/prog"
Defensive patterns
Strategy: validation
Validate before calling
let ws = normalize_workspace_scope(rule.workspace.as_deref().unwrap()).unwrap();
assert!(codewhale_execpolicy::normalize_workspace_relative_path(rule.path.as_deref().unwrap(), &ws)
.is_some_and(|p| !p.is_empty())); // before append Type guard
fn is_in_workspace_path(rule: &ToolAskRule) -> bool {
let Some(ws) = rule.workspace.as_deref().and_then(normalize_workspace_scope) else { return false };
rule.path.as_deref()
.is_none_or(|p| normalize_workspace_relative_path(p, &ws).is_some_and(|p| !p.is_empty()))
} Prevention
- Strip the workspace prefix before persisting paths
- Never put absolute paths in persistent allow rules
- Reject '..' segments and free-text paths at the UI layer
When it happens
Trigger: A ToolAskRule with path = Some("/etc/passwd"), path = "../../secrets", or path = "."/"" combined with an Allow action passed to append_allow_rules.
Common situations: UI sending an absolute path it had displayed, rules copied between workspaces whose relative targets no longer exist inside the new workspace, path built from user free-text input.
Related errors
- persistent allow rules must be scoped to a workspace
- persistent command allow rules must use exact matching
- persistent allow rules must match an exact command or path
- persistent command allow rules must not be empty
- {kind} must be a single path component
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/41b19d93a3fb9d70.
Report an issue: GitHub.