continuedev / continuedev/continue

Incomplete destructive-command blocklist allows auto-execution of filesystem-destroying commands in headless/auto mode

Open
#13,001 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Before submitting your bug report
Relevant environment info
commit eaa23c5
Description

reported on 13 June 2026 via email - no response, trying here:

Summary

In the Continue CLI's headless (cn -p) and auto modes, the default policy sets Bash to allow, so the only backstop against a malicious LLM-emitted terminal command is the terminal-security evaluator returning a "disabled" verdict. That denylist is incomplete: it fails to flag rm -rf of /home, /root, /var, /opt, /srv, fails to flag rm -rf $HOME (shell-quote drops the $HOME token, which the shell re-expands at execution), and never disables find -delete, shred, wipefs, truncate, or pkexec. Because the agent autonomously ingests untrusted content (web pages via the Fetch tool, repo files, issue/PR text, MCP output) that is allow-by-default, indirect prompt injection can drive an unattended, irreversible wipe of the user's home directory or data partitions. Confirmed against the compiled evaluator and a shell.

Details

In headless/auto mode the default permission policy sets Bash -> allow (extensions/cli/src/permissions/defaultPolicies.ts:31-33, AUTO_MODE_POLICIES ~69-71), and the Fetch tool is allow-by-default (~21). Per extensions/cli/src/permissions/permissionChecker.ts:161-174, a disabled verdict forces exclude (hard block), but any other verdict lets the user's allow preference win and the command executes with no prompt. So isCriticalCommand is the sole guard.

packages/terminal-security/src/evaluateTerminalCommandSecurity.ts (evaluateSingleCommand -> isCriticalCommand, ~402-560):

  • The dangerous-rm check (~411-439) requires both hasRf and hasDangerousPath, where hasDangerousPath only matches /, /*, ~, ~/*, /usr, /etc, /bin, /sbin and those prefixes. /home, /root, /var, /opt, /srv are absent.
  • shell-quote (~70/93) parses $HOME to an empty-string token, so rm -rf $HOME becomes ["rm","-rf",""] -> hasDangerousPath=false -> not disabled. The shell re-expands $HOME at execution (spawn, extensions/cli/src/tools/runTerminalCommand.ts:144-152) and wipes the home directory.
  • find ... -delete is only "high-risk" (-> allowedWithPermission), never disabled. shred, wipefs, truncate, and pkexec are unhandled.

The untrusted input is the allow-by-default content the agent ingests (web/Fetch, repo files, issue/PR text, MCP tool output); indirect prompt injection in any of it steers the model to emit a command that survives the denylist.

To reproduce
PoC

scripts/poc_continue_terminal_blocklist.md.

A malicious web page / repo file / issue body the agent reads contains instructions that lead the model to emit, in cn -p/auto mode, rm -rf $HOME (or rm -rf /home, find / -delete, shred -u ...).

Validated by building the package and calling the actual exported evaluateTerminalCommandSecurity, plus a faithful reproduction of checkToolPermission under AUTO_MODE_POLICIES:

MODE: auto/headless (AUTO_MODE_POLICIES = [{tool:'*',permission:'allow'}])
EXECUTES WITHOUT PROMPT  <- rm -rf $HOME
EXECUTES WITHOUT PROMPT  <- rm -rf /home
EXECUTES WITHOUT PROMPT  <- rm -rf /root
EXECUTES WITHOUT PROMPT  <- rm -rf /var
EXECUTES WITHOUT PROMPT  <- find / -delete
EXECUTES WITHOUT PROMPT  <- shred -u /dev/sda
BLOCKED (exclude)        <- rm -rf /            (evaluator DOES block these - the blocklist is the intended control)
BLOCKED (exclude)        <- sudo rm -rf /etc

shell proof that $HOME re-expands at execution despite the policy pass:

BEFORE: .../fakehome/Documents/thesis.txt exists
$ bash -c 'rm -rf $HOME'
AFTER:  ls: cannot access '.../fakehome': No such file or directory   (thesis -> NO_DELETED)
Impact

In unattended Continue CLI runs (CI/scripts via cn -p, or --auto), a single prompt-injection payload in content the agent reads triggers irreversible deletion of the user's home directory or /var//root//opt//srv, or device wipe via shred/wipefs, with no prompt. The blocklist gap is also a defense-in-depth failure in interactive mode.

Remediation

Make the rm critical check trigger on -rf regardless of path (or treat any recursive-force rm of an absolute/$VAR/~-derived path as disabled), and expand hasDangerousPath to include /home, /root, /var, /opt, /srv and bare home/$HOME. Resolve env-var/~ tokens (or treat an empty-string token produced from a $-expansion as a wildcard dangerous path) before the dangerous-path test rather than letting shell-quote drop them. Add find with -delete/-exec rm, shred, wipefs, truncate -s 0, and pkexec to the disabled set. Strategically, for unattended execution prefer an explicit per-command allowlist over a denylist, or never let auto mode downgrade Bash below allowedWithPermission.

Log output

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 with packages/terminal-security/src/evaluateTerminalCommandSecurity.ts, especially evaluateSingleCommand and isCriticalCommand, then trace permission handling in extensions/cli/src/permissions/permissionChecker.ts and the Bash policies in defaultPolicies.ts. Reproduce the listed commands using scripts/poc_continue_terminal_blocklist.md; done means dangerous filesystem and device-destruction commands receive a disabled verdict and cannot execute under auto/headless policy.

Written by the indexing model from the issue text.

Assessment

Tech stack
shell, 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.