{"record":{"id":"1f453ff41934b3d0","repo":"Hmbown/CodeWhale","slug":"no-trusted-executable-search-path-remains-outside","errorCode":null,"errorMessage":"no trusted executable search path remains outside the workspace","messagePattern":"no trusted executable search path remains outside the workspace","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":4396,"sourceCode":"fn resolve_readonly_program(program: &str, workspace: &std::path::Path) -> Result<PathBuf> {\n    let path = std::env::var_os(\"PATH\")\n        .ok_or_else(|| anyhow!(\"no executable search path is configured\"))?;\n    resolve_readonly_program_from_path(program, workspace, &path)\n}\n\nfn resolve_readonly_program_from_path(\n    program: &str,\n    workspace: &std::path::Path,\n    path: &std::ffi::OsStr,\n) -> Result<PathBuf> {\n    let workspace = workspace.canonicalize()?;\n    if std::path::Path::new(program).components().count() != 1 {\n        return Err(anyhow!(\n            \"read-only command must name a bare allowlisted executable\"\n        ));\n    }\n    let safe_path = readonly_sanitized_path_from(&workspace, path).ok_or_else(|| {\n        anyhow!(\"no trusted executable search path remains outside the workspace\")\n    })?;\n    let names = if cfg!(windows) {\n        vec![format!(\"{program}.exe\"), format!(\"{program}.com\")]\n    } else {\n        vec![program.to_string()]\n    };\n    for directory in std::env::split_paths(&safe_path) {\n        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                }","sourceCodeStart":4378,"sourceCodeEnd":4414,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/shell.rs#L4378-L4414","documentation":"After rejecting path-qualified programs, the resolver filters the process PATH down to entries that are absolute and canonicalize outside the workspace (`readonly_sanitized_path_from`). If no PATH entry survives — every entry is relative, unresolvable, or inside the workspace — this error is thrown because no trusted location exists to resolve the allowlisted executable from.","triggerScenarios":"Running with `PATH=.` or `PATH=$HOME/project` (the workspace itself); PATH containing only relative entries like `bin`; running with cwd-inside-workspace PATH entries so the filter removes them all; PATH entries that are dangling symlinks or nonexistent directories that fail canonicalize().","commonSituations":"Vendored-tool setups that prepend the project's `node_modules/.bin` or `target/debug` to PATH as the only entry; sandboxes that rewrite PATH to workspace-relative dirs; broken images where PATH points at removed directories.","solutions":["Add a system-wide directory outside the workspace to PATH, e.g. `PATH=/usr/local/bin:/usr/bin:$PATH`","Move the required binary to an installed location (`cargo install`, distro package, /usr/local/bin)","Ensure PATH entries are absolute and point at existing directories outside the workspace"],"exampleFix":"// before\nPATH=./node_modules/.bin app\n// after\nPATH=/usr/local/bin:/usr/bin app  # tool installed system-wide","handlingStrategy":"validation","validationCode":"fn has_trusted_path_entries(workspace: &std::path::Path) -> bool {\n    let Some(path) = std::env::var_os(\"PATH\") else { return false };\n    let Ok(workspace) = workspace.canonicalize() else { return false };\n    std::env::split_paths(&path).any(|entry| {\n        entry.is_absolute()\n            && entry.canonicalize().map(|r| !r.starts_with(&workspace)).unwrap_or(false)\n    })\n}\nassert!(has_trusted_path_entries(&workspace), \"PATH needs an absolute entry outside the workspace\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep at least one absolute system directory (/usr/bin, /usr/local/bin) on PATH","Do not prepend only workspace-relative or vendored directories to PATH","Remove dangling symlinks/dead directories from PATH; verify with `which -a <tool>` resolving outside the workspace"],"tags":["environment","path","security","shell"],"backgroundTag":"missing-env-var","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"}