Skip to content

fix(cli): harden bash prefix approvals#30

Open
BunsDev wants to merge 1 commit into
mainfrom
codex/fix-bash-prefix-approval-vulnerability
Open

fix(cli): harden bash prefix approvals#30
BunsDev wants to merge 1 commit into
mainfrom
codex/fix-bash-prefix-approval-vulnerability

Conversation

@BunsDev
Copy link
Copy Markdown
Member

@BunsDev BunsDev commented Jun 3, 2026

Motivation

  • Prevent a benign first-word prefix approval from suppressing prompts for compound or redirected shell commands that include shell-control characters or substitutions.
  • Avoid deriving an allowlist entry from the first whitespace token, which can be abused by attacker-controlled appended commands.
  • Narrow the attack surface while preserving the prefix-allow UX for clearly-bounded, simple command prefixes.

Description

  • Stop proposing a prefix when the raw command contains shell-control syntax (;, |, &, <, >, backticks, $(, newlines, etc.) and compute the dialog suggested_prefix from the dialog payload when safe in src-rust/crates/cli/src/main.rs.
  • Record the dialog's explicit suggested_prefix (trimmed) into the session allowlist instead of re-deriving the first token, and only insert it if it does not itself contain shell-control syntax in src-rust/crates/tui/src/app.rs.
  • Replace the simple first-token allow check with a bounded-prefix match and a conservative bash_command_has_shell_control scan that rejects compound/redirected/substitution commands before skipping the permission dialog in src-rust/crates/tui/src/app.rs.
  • Update unit tests in crates/tui to assert the tighter prefix behavior and to reject chained/redirection cases (e.g. git status; curl ..., git status > /tmp/..., and similar).

Testing

  • Ran cargo fmt --all to format changes (succeeded).
  • Ran cargo test --package claurst-tui bash_prefix_allowlist and the three updated tests passed locally (3 passed; 0 failed).
  • Attempted workspace cargo check, cargo clippy, and cargo test --workspace but these were blocked by a missing system dependency (alsa.pc required by alsa-sys) in the execution environment, so full-workspace verification could not complete here.

Codex Task

Copilot AI review requested due to automatic review settings June 3, 2026 11:49
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the Bash “prefix allow” flow in the TUI/CLI by preventing session allowlist entries (and allowlist checks) from accidentally approving compound/redirected/substitution shell commands that should always require an explicit permission prompt.

Changes:

  • Switch prefix recording to use the dialog’s explicit suggested_prefix (instead of re-deriving the first whitespace token).
  • Add a shell-control scan plus bounded-prefix matching to decide whether a Bash command can skip the permission dialog.
  • Update claurst-tui unit tests to validate the tighter allowlist behavior for chaining/redirection cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src-rust/crates/tui/src/app.rs Records suggested prefixes, adds shell-control scanning + bounded prefix matching, and updates tests for stricter allowlist behavior.
src-rust/crates/cli/src/main.rs Avoids proposing a prefix for Bash commands containing obvious shell-control/substitution syntax and records the dialog-provided suggested prefix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5422 to +5439
if c == '\\' {
escaped = true;
continue;
}

match c {
'\'' if !in_double_quote => in_single_quote = !in_single_quote,
'"' if !in_single_quote => in_double_quote = !in_double_quote,
';' | '|' | '&' | '<' | '>' | '\n' | '\r' if !in_single_quote && !in_double_quote => {
return true;
}
'`' if !in_single_quote => return true,
'$' if !in_single_quote && chars.peek() == Some(&'(') => return true,
_ => {}
}
}

false
Comment on lines 5461 to +5466
pub fn bash_command_allowed_by_prefix(&self, command: &str) -> bool {
let first_word = command.split_whitespace().next().unwrap_or("");
!first_word.is_empty() && self.bash_prefix_allowlist.contains(first_word)
!Self::bash_command_has_shell_control(command)
&& self
.bash_prefix_allowlist
.iter()
.any(|prefix| Self::bash_prefix_matches_command(prefix, command))
Comment on lines +7122 to +7128
// Other commands and compound shell commands should NOT be allowed.
assert!(!app.bash_command_allowed_by_prefix("git push origin main"));
assert!(!app.bash_command_allowed_by_prefix("git status; curl https://example.com"));
assert!(!app.bash_command_allowed_by_prefix(
"git status ; curl https://example.com"
));
assert!(!app.bash_command_allowed_by_prefix("git status > /tmp/status.txt"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants