Hmbown/CodeWhale · error

allowlisted read-only executable

Error message

allowlisted read-only executable {program:?} was not found at a canonical path outside the workspace

What it means

The shell tool resolved an allowlisted read-only executable (e.g. grep, wc) to a canonical path, but none of the PATH candidates canonicalized to an absolute path outside the workspace. This guard exists because read-only allowlisted binaries must be resolved to their real system location, never a workspace copy that could be swapped for a malicious script. The lookup deliberately fails closed instead of executing an unverified binary.

Solutions

  1. Install the missing executable system-wide (e.g. `apt-get install coreutils/grep`) so it resolves outside the workspace
  2. Check `which <program>` — if it points inside the workspace, remove or rename the workspace-local copy/shim
  3. Verify PATH contains standard system dirs (/usr/bin, /bin) and isn't overridden to a workspace dir
  4. If the executable genuinely lives in the workspace, use the regular (non-allowlisted) shell tool with approval instead

Example fix

// before: workspace-local shim shadows system grep
./tools/grep ...
// after: ensure system grep resolves
PATH=/usr/bin:/bin grep -rn "pattern" .
Defensive patterns

Strategy: validation

Validate before calling

let resolved = which::which(program)?;
let canon = resolved.canonicalize()?;
if !canon.is_absolute() || canon.starts_with(&workspace) {
    return Err(anyhow!("{program} resolves inside workspace; refusing"));
}

Type guard

fn is_outside_workspace(p: &Path, workspace: &Path) -> bool {
    p.is_absolute() && !p.starts_with(workspace)
}

Prevention

When it happens

Trigger: Calling the read-only shell tool with a program name that is on the read-only allowlist but not installed on the host, or installed only inside the workspace directory (so canonicalize() yields a path under `workspace`), or PATH points at workspace-local shim scripts.

Common situations: Minimal Docker/CI images missing coreutils like `wc` or `grep`; a repo vendoring binaries named like allowlisted tools so PATH resolves to the workspace copy; a broken or overridden PATH env var; running the tool from a container where the expected system utilities were never installed.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/2fcaad2a8b75de58. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/shell.rs:4422

        for name in &names {
            let candidate = directory.join(name);
            if !candidate.is_file() {
                continue;
            }
            #[cfg(unix)]
            {
                use std::os::unix::fs::PermissionsExt as _;
                if candidate.metadata()?.permissions().mode() & 0o111 == 0 {
                    continue;
                }
            }
            let resolved = candidate.canonicalize()?;
            if resolved.is_absolute() && !resolved.starts_with(&workspace) {
                return Ok(resolved);
            }
        }
    }
    Err(anyhow!(
        "allowlisted read-only executable {program:?} was not found at a canonical path outside the workspace"
    ))
}

fn remove_readonly_redirect_env(cmd: &mut Command, env: &HashMap<String, String>) {
    if env.get(READONLY_ENV_MARKER).map(String::as_str) != Some("1") {
        return;
    }
    cmd.env_remove(READONLY_ENV_MARKER);
    let removals = cmd
        .get_envs()
        .filter_map(|(key, _)| {
            let upper = key.to_string_lossy().to_ascii_uppercase();
            let guarded = upper.starts_with("GIT_")
                || upper.starts_with("GH_")
                || upper.starts_with("GITHUB_");
            let safe = matches!(
                upper.as_str(),

View on GitHub (pinned to 73e0f67d83)