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

  1. Convert the approved path to a workspace-relative form (strip the workspace prefix, remove '..')
  2. If the target genuinely lives outside the workspace, it cannot be persistently allowed — keep it session-scoped
  3. 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

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


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/41b19d93a3fb9d70. Report an issue: GitHub.