{"record":{"id":"6b3fe6f16e19d5ee","repo":"Hmbown/CodeWhale","slug":"pipeline-contains-a-command-outside-the-read-only-policy","errorCode":null,"errorMessage":"pipeline contains a command outside the read-only policy","messagePattern":"pipeline contains a command outside the read-only policy","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":4162,"sourceCode":"    use crate::shell_dispatcher::ShellKind;\n    // POSIX quoting must never be passed to a different command interpreter.\n    let supported = match crate::shell_dispatcher::global_dispatcher().kind() {\n        ShellKind::Bash => true,\n        ShellKind::Custom { binary, .. } => matches!(\n            std::path::Path::new(binary)\n                .file_name()\n                .and_then(|name| name.to_str()),\n            Some(\"bash\" | \"zsh\")\n        ),\n        _ => false,\n    };\n    if !supported {\n        return Err(anyhow!(\n            \"read-only pipelines require bash or zsh; run each read separately\"\n        ));\n    }\n    if !is_agent_readonly_shell_command(command) {\n        return Err(anyhow!(\n            \"pipeline contains a command outside the read-only policy\"\n        ));\n    }\n    let segments = command\n        .split('|')\n        .map(|segment| {\n            let (program, args) = hardened_readonly_argv(segment)?;\n            let program = resolve_readonly_program(&program, workspace)?;\n            let program = program\n                .to_str()\n                .ok_or_else(|| anyhow!(\"read-only executable path is not valid UTF-8\"))?;\n            Ok(std::iter::once(program)\n                .chain(args.iter().map(String::as_str))\n                .map(|arg| shell_words::quote(arg).into_owned())\n                .collect::<Vec<_>>()\n                .join(\" \"))\n        })\n        .collect::<Result<Vec<_>>>()?;","sourceCodeStart":4144,"sourceCodeEnd":4180,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/shell.rs#L4144-L4180","documentation":"Before executing a pipeline the shell tool re-validates the whole command string with is_agent_readonly_shell_command. If any part of the classifier-approved command is outside the read-only policy (a non-read program, redirection to a write, etc.), execution is refused. This is a defense-in-depth check run after the classifier so nothing non-read-only reaches the shell.","triggerScenarios":"Submitting a pipeline (via the agent shell tool with read-only mode active) whose command string fails is_agent_readonly_shell_command — e.g. it contains a write command (rm, mv, tee, redirect '>'), or an unknown program the policy does not whitelist.","commonSituations":"The agent model proposes 'git status | tee out.txt'; a user-configured git helper or alias expands to something non-read-only; a pipeline includes curl/wget or shell builtins the policy doesn't recognize.","solutions":["Remove or replace the non-read-only command in the pipeline so every stage is an allowed read-only program.","Replace output capture (tee, >, >>) with a plain read that prints to stdout.","If a legitimate read-only helper is being rejected, update the read-only policy/allowlist configuration to include it explicitly."],"exampleFix":"// before (rejected: tee writes)\nagent: cat config.toml | tee /tmp/backup.toml\n// after\nagent: cat config.toml","handlingStrategy":"validation","validationCode":"const WRITE_CMDS = new Set(['rm','mv','cp','tee','mkdir','touch','chmod','dd','shred',']]);\nfunction isReadOnlyPipeline(cmd: string): boolean {\n  return cmd.split('|').every(seg => {\n    const prog = seg.trim().split(/\\s+/)[0]?.replace(/^env\\s+\\S+\\s+/, '') ?? '';\n    return !prog.includes('>') && !WRITE_CMDS.has(prog);\n  });\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep agent prompts instructing output-only reads; forbid tee and redirect operators in read mode.","Review generated pipelines for write commands before approving them.","Keep the read-only allowlist explicit; don't rely on classifier approval alone."],"tags":["shell","read-only-policy","pipeline","security"],"backgroundTag":"permission-denied","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"}