bug(plan-mode): bash read-only classifier allows redirection writes
@Kaguya-19 is already working on this.
Since Jun 24, 2026.
- Dominant language
- TypeScript
- Stars
- 4k
- Forks
- 453
- Avg merge
- 12h 30m
- Merged PRs (30d)
- 46
Description
Summary
Plan mode treats some bash commands as read-only by prefix match only. Commands such as echo, printf, and node -e are classified as read-only even when they contain shell redirection or obvious file-writing JavaScript, so Plan mode can be bypassed to write arbitrary files.
Why it matters
Plan mode promises a constrained, read-only exploration phase, with writes limited to plan markdown files under .pilotdeck/plans/. The tool schema tells the model that bash is available for read-only commands. If echo SIDE_EFFECT > file, printf ... >> file, or node -e "fs.writeFileSync(...)" is allowed, Plan mode no longer enforces that boundary.
Evidence
src/tool/planModeConstraints.ts:11tosrc/tool/planModeConstraints.ts:26includesbashin the Plan mode allowed tool set.src/tool/planModeConstraints.ts:32tosrc/tool/planModeConstraints.ts:35describes Plan modebashas read-only.src/tool/execution/ToolRuntime.ts:48tosrc/tool/execution/ToolRuntime.ts:57blocks tools not in the Plan mode allowlist;bashpasses this gate.src/tool/builtin/bash/permissions.ts:35tosrc/tool/builtin/bash/permissions.ts:45marksprintf,echo, andnode -eas safe read patterns by prefix.src/tool/builtin/bash/permissions.ts:95tosrc/tool/builtin/bash/permissions.ts:97implementsisReadOnlyShellCommandas “any safe pattern matches.”src/tool/builtin/bash.ts:59tosrc/tool/builtin/bash.ts:60exposes that classifier as the bash tool’sisReadOnly.src/permission/decision/PermissionRuntime.ts:94tosrc/permission/decision/PermissionRuntime.ts:99allows read-only tools in Plan mode even when the tool permission would otherwise ask.src/permission/decision/PermissionRuntime.ts:184tosrc/permission/decision/PermissionRuntime.ts:185allows Plan mode whentool.isReadOnly(input)is true.src/permission/decision/PermissionRuntime.ts:373tosrc/permission/decision/PermissionRuntime.ts:377builds the Plan mode bash deny message only on the non-read-only path.src/tool/builtin/bash.ts:86tosrc/tool/builtin/bash.ts:93passes the command to the shell runner.src/tool/builtin/bash/commandRunner.ts:30tosrc/tool/builtin/bash/commandRunner.ts:34runs commands withshell: true, so shell redirection is effective.
Validation
Validation level: dynamic permission/runtime reproduction.
Repro approach: evaluate echo SIDE_EFFECT > plan-mode-write.txt through the bash read-only classifier and Plan mode permission runtime, then execute the bash command.
Key output: isReadOnlyShellCommand classified the command as read-only, PermissionRuntime returned allow in Plan mode, and shell execution created the file.
Boundary: this did not require a full agent loop. The permission runtime and bash tool are the same core path used by agent tool execution.
Expected behavior
Plan mode should reject bash commands that can write or mutate files, even if they start with a command that is sometimes read-only. Redirection, append redirection, shell command composition, and obvious file-writing node -e scripts should not be allowed under the read-only classifier.
Existing coverage checked
Coverage review checked existing Plan mode hardening work.
Coverage label: partially covered. Merged PR #216 and PR #220 strengthen Plan mode enforcement, tool filtering, error formatting, and UI presentation. They cover adjacent Plan mode hardening, but not this root cause: the bash read-only classifier still allows write-capable commands such as echo > file.
Suggested fix
Make the shell read-only classifier conservative. Reject commands containing redirection operators, write-oriented pipes, command separators that compose writes, and node -e snippets containing obvious filesystem write APIs. Alternatively, use a stricter Plan mode bash allowlist that only permits known read-only commands without shell metacharacters.
Suggested tests
echo SIDE_EFFECT > fileis rejected.printf SIDE_EFFECT >> fileis rejected.node -e "fs.writeFileSync('file','x')"is rejected.- Command chaining that writes after a read-looking prefix is rejected.
- Known read-only commands such as
pwd,ls, andgit statusstill pass.
Submitted with Codex.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.