{"record":{"id":"2e9502c36bfd8ace","repo":"Hmbown/CodeWhale","slug":"classifier-approved-git-read-did-not-keep-its-subc","errorCode":null,"errorMessage":"classifier-approved Git read did not keep its subcommand in argv[1]","messagePattern":"classifier-approved Git read did not keep its subcommand in argv\\[1\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":4236,"sourceCode":"                argv.splice(\n                    at..at,\n                    [\"--no-ext-diff\".to_string(), \"--no-textconv\".to_string()],\n                );\n            }\n            \"log\" | \"show\" => {\n                let at = subcommand_index + 1;\n                argv.splice(\n                    at..at,\n                    [\n                        \"--no-ext-diff\".to_string(),\n                        \"--no-textconv\".to_string(),\n                        \"--no-show-signature\".to_string(),\n                    ],\n                );\n            }\n            \"status\" | \"ls-files\" | \"blame\" | \"grep\" => {}\n            _ => {\n                return Err(anyhow!(\n                    \"classifier-approved Git read did not keep its subcommand in argv[1]\"\n                ));\n            }\n        }\n    }\n\n    let program = argv.remove(0);\n    Ok((program, argv))\n}\n\nfn enforce_readonly_workspace_operands(\n    command: &str,\n    workspace: &std::path::Path,\n    effective_cwd: &std::path::Path,\n) -> Result<(), ToolError> {\n    let argv = shell_words::split(&normalize_windows_command_paths(command)).map_err(|error| {\n        ToolError::invalid_input(format!(\n            \"Could not parse read-only command arguments: {error}\"","sourceCodeStart":4218,"sourceCodeEnd":4254,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/shell.rs#L4218-L4254","documentation":"This error is raised in `hardened_readonly_argv` when the subcommand found after the `--no-pager`/`-C` preamble is not one of the hardener-supported read-only subcommands (diff, log, show, status, ls-files, blame, grep). The guard enforces that only allowlisted Git reads, whose safety flags the hardener knows how to splice in, reach execution; anything else is rejected instead of run unhardened.","triggerScenarios":"Issuing a classifier-approved read-only `git` command whose subcommand is outside the allowlist, e.g. `git fetch`, `git remote -v`, `git add -n`, or `git worktree list`. The classifier may have passed the command as a read, but the argv hardener independently rejects unsupported subcommands.","commonSituations":"Developers assuming any read-looking Git command is allowed (`git remote -v`, `git branch -a`, `git config --get`); version drift where the classifier allowlist and the hardener's match arm diverge; typos in the subcommand token.","solutions":["Use one of the supported read-only subcommands: diff, log, show, status, ls-files, blame, grep","For other Git reads, fall back to the non-hardened (permission-prompted) shell path or another tool","If a new subcommand should be supported, add it to the match in hardened_readonly_argv together with the hardening flags it needs"],"exampleFix":"// before\nrun_readonly(\"git remote -v\")\n// after\nrun_readonly(\"git ls-files\") // or use the standard permissioned shell path","handlingStrategy":"validation","validationCode":"const GIT_READONLY_SUBCOMMANDS: &[&str] =\n    &[\"diff\", \"log\", \"show\", \"status\", \"ls-files\", \"blame\", \"grep\"];\nfn git_subcommand_allowed(cmd: &str) -> bool {\n    let argv = shell_words::split(cmd).unwrap_or_default();\n    let mut i = 1;\n    while matches!(argv.get(i).map(String::as_str), Some(\"--no-pager\") | Some(\"-C\")) {\n        i += if argv.get(i).map(String::as_str) == Some(\"-C\") { 2 } else { 1 };\n    }\n    matches!(argv.get(i).map(String::as_str), Some(s) if GIT_READONLY_SUBCOMMANDS.contains(&s))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Restrict read-only Git usage to diff/log/show/status/ls-files/blame/grep","Check the hardener's allowlist before adding new Git invocations to agent prompts","Route other Git reads through the permission-prompted shell path"],"tags":["git","shell","security","allowlist"],"backgroundTag":"unsupported-operation","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"}