{"record":{"id":"2fcaad2a8b75de58","repo":"Hmbown/CodeWhale","slug":"allowlisted-read-only-executable-program-was-n","errorCode":null,"errorMessage":"allowlisted read-only executable {program:?} was not found at a canonical path outside the workspace","messagePattern":"allowlisted read-only executable (.+?) was not found at a canonical path outside the workspace","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":4422,"sourceCode":"        for name in &names {\n            let candidate = directory.join(name);\n            if !candidate.is_file() {\n                continue;\n            }\n            #[cfg(unix)]\n            {\n                use std::os::unix::fs::PermissionsExt as _;\n                if candidate.metadata()?.permissions().mode() & 0o111 == 0 {\n                    continue;\n                }\n            }\n            let resolved = candidate.canonicalize()?;\n            if resolved.is_absolute() && !resolved.starts_with(&workspace) {\n                return Ok(resolved);\n            }\n        }\n    }\n    Err(anyhow!(\n        \"allowlisted read-only executable {program:?} was not found at a canonical path outside the workspace\"\n    ))\n}\n\nfn remove_readonly_redirect_env(cmd: &mut Command, env: &HashMap<String, String>) {\n    if env.get(READONLY_ENV_MARKER).map(String::as_str) != Some(\"1\") {\n        return;\n    }\n    cmd.env_remove(READONLY_ENV_MARKER);\n    let removals = cmd\n        .get_envs()\n        .filter_map(|(key, _)| {\n            let upper = key.to_string_lossy().to_ascii_uppercase();\n            let guarded = upper.starts_with(\"GIT_\")\n                || upper.starts_with(\"GH_\")\n                || upper.starts_with(\"GITHUB_\");\n            let safe = matches!(\n                upper.as_str(),","sourceCodeStart":4404,"sourceCodeEnd":4440,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/shell.rs#L4404-L4440","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Install the missing executable system-wide (e.g. `apt-get install coreutils/grep`) so it resolves outside the workspace","Check `which <program>` — if it points inside the workspace, remove or rename the workspace-local copy/shim","Verify PATH contains standard system dirs (/usr/bin, /bin) and isn't overridden to a workspace dir","If the executable genuinely lives in the workspace, use the regular (non-allowlisted) shell tool with approval instead"],"exampleFix":"// before: workspace-local shim shadows system grep\n./tools/grep ...\n// after: ensure system grep resolves\nPATH=/usr/bin:/bin grep -rn \"pattern\" .","handlingStrategy":"validation","validationCode":"let resolved = which::which(program)?;\nlet canon = resolved.canonicalize()?;\nif !canon.is_absolute() || canon.starts_with(&workspace) {\n    return Err(anyhow!(\"{program} resolves inside workspace; refusing\"));\n}","typeGuard":"fn is_outside_workspace(p: &Path, workspace: &Path) -> bool {\n    p.is_absolute() && !p.starts_with(workspace)\n}","tryCatchPattern":null,"preventionTips":["Install allowlisted tools in standard system locations","Never place executables named like allowlisted tools inside the workspace","Keep PATH free of workspace-relative entries in agent environments","Run `which <tool>` in CI to confirm resolution before enabling read-only shell tools"],"tags":["shell","security","path-resolution"],"backgroundTag":"command-not-found","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}