fix(command-mode): flag pipe-to-shell, output-redirect, and whitespace-prefixed destructive commands in the confirm gate#434
Conversation
… the destructive-command confirm gate
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 88480e3fab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // or `cat y >> /etc/hosts`). Excluding a leading `-`/`=` skips arrows | ||
| // like `->`/`=>`, and requiring a non-`&` target skips file-descriptor | ||
| // duplications like `2>&1`. | ||
| if cmd.range(of: #"(?<![-=])>>?\s*[^&\s]"#, options: .regularExpression) != nil { |
There was a problem hiding this comment.
Catch >& file redirects before auto-execution
For commands that use the zsh/bash >& file form to redirect output to a file, this heuristic skips the redirect because it rejects any > whose target starts with &. That avoids >&2, but it also lets real file overwrites such as echo x >& ~/.zshrc or cmd >&log bypass the confirm gate and run without prompting, which is the same destructive class this change is trying to catch.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 88480e3fab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // or `cat y >> /etc/hosts`). Excluding a leading `-`/`=` skips arrows | ||
| // like `->`/`=>`, and requiring a non-`&` target skips file-descriptor | ||
| // duplications like `2>&1`. | ||
| if cmd.range(of: #"(?<![-=])>>?\s*[^&\s]"#, options: .regularExpression) != nil { |
There was a problem hiding this comment.
Cover zsh
>&file redirects in the confirm gate
This check treats any > followed by & as a file-descriptor duplication, but TerminalService runs commands through /bin/zsh (Sources/Fluid/Services/TerminalService.swift:79), and the zsh redirection docs document >& word/&> word as redirecting stdout and stderr to a file when it is not one of the duplication syntaxes. As a result, a command like echo hi >&/tmp/target can create or truncate a file without the new confirmation gate, even though the equivalent &> /tmp/target is caught; distinguish >&1/>&- from >&path instead of excluding every ampersand target.
Useful? React with 👍 / 👎.
(Reopens #428, which was closed by accident on my end after a force-push; same work, healthy branch.)
What
CommandModeService.isDestructiveCommandpowers the "confirm before running" safety gate in Command Mode. It missed several destructive command shapes that would auto-run without a confirmation prompt:curl https://example.com/install.sh | sh(and| bash/| zsh/| dash/| fish, including absolute or relative interpreter paths like| /bin/bash). Previously only| rm/| sudo/| ddwere caught.tee:echo ... | tee ~/.zshrc/tee -awrites (or appends) to files.| sudo teewas already caught via thesudopattern; the non-sudo case was not.echo x > ~/.zshrc,cat y >> /etc/hosts. The previous"> "check only matched commands that started with>, so real redirects slipped through.rm -rf xor a newline-prefixedsudo ...defeated thehasPrefixchecks because the command was not trimmed first.How
Added pattern checks for pipe-to-shell,
tee, and output redirects, and trimmed leading whitespace/newlines before classification. The function is nownonisolated staticso it can be unit-tested directly (its single call site is updated). The redirect regex is tuned to skip file-descriptor cases:2>&1and>&2and->/=>arrows do not trip it.Tests
CommandModeDestructiveCommandTestscovers the newly-caught classes, equivalent bypass forms (path-prefixed shells, spaceless /&>/2>>redirects), case-insensitivity, leading-whitespace prefixes, a regression guard over the existing detections, and safe-command non-triggers (2>&1,| ssh,| shuf,| /usr/bin/shasum, quoted arrows, input redirects).xcodebuild testis green andswiftlint --strictis clean.Accepted limitations
The redirect heuristic is intentionally token-blind, so it over-flags two harmless shapes (an extra confirmation prompt, never a missed destructive command): a quoted
>comparison likeawk '$1 > 10' data.csv, and a redirect to a discard target likecmd > /dev/null. That is the right bias for a confirm-before-execute gate, and shell-quote parsing was deliberately avoided to keep the heuristic simple and avoid introducing false negatives.