Where does the bug appear (feature/product)?
Cursor IDE
Describe the Bug
The command allowlist is matched as a prefix/glob against the raw text of a command. Cursor never inspects the right-hand side of a shell variable assignment, so an allowlist entry that merely names a variable grants unattended execution to any command placed after the =.
With $out on the allowlist and Get-Random not in the allowlist:
| Command | Result |
|---|---|
Get-Random |
Approval prompt |
$out = Get-Random |
Runs with no prompt |
The four characters $out are the entire difference. Nothing after the = is examined, so the same entry equally covers $out = Remove-Item -Recurse C:\Temp or other more destructive and potentially malicious comamands.
The mechanism is in resources/app/extensions/cursor-agent-exec/dist/main.js (3.14.7). matchesShell falls through to four tests, and the second one is a raw-text prefix:
matchesShell(e, t) {
const n = Wl(e);
if (!n) return !1;
const r = t.trim(),
o = this.extractBaseCommand(r);
if (null !== n.argsPattern) { /* ... */ }
return !!this.matchGlob(n.fullPattern, r)
|| !!r.startsWith(`${n.fullPattern} `) // <-- entry plus a space
|| !!this.matchGlob(n.fullPattern, o)
|| o === n.fullPattern;
}
$out = Get-Random starts with $out , so the second test returns true and the command is treated as fully approved. The rule appears to be intended for “entry is a command prefix and what follows is an argument list”, but it cannot tell an argument separator from an assignment operator.
The aggravating part is that Cursor creates these entries itself. Clicking “Always run” on a command containing a loop or a variable statement files the bare variable names as allowlist entries. Everyone should check their allowlist, as any entry begining with $ is a hole that allows the agent to run any command it wants as long as that variable is on the left-hand side.
Steps to Reproduce
Confirmed on 3.14.7 (Windows, PowerShell). No hooks or extensions are needed.
- Open Settings → Agents → Approvals & Execution. Set Run Mode to
Allowlist. - Under Command allowlist, add a single entry:
$out - Confirm
Get-Randomis not on the allowlist. - Ask the agent to run
Get-Random. → An approval prompt appears, as it should. - Ask the agent to run
$out = Get-Random. → It runs with no prompt, and the tool result reports that it matched the command allowlist and ran outside the sandbox. - Replace the right-hand side with anything you like.
$out = New-Item C:\Temp\proof.txtcreates the file unattended, which shows the bypass is not limited to read-only commands.
Step 2 can also be reached without typing anything: click Always run on a command that contains a loop or a variable statement (such as the command from step #5), and Cursor files the bare variable names for you.
Expected Behavior
- An assignment’s right-hand side is a command, and it should be matched against the allowlist on its own terms.
$out = Get-Randomshould require approval exactly asGet-Randomdoes. The left-hand side of the assignment (in this case$out) should not factor into the the allowlist’s decision to block the command. - The
startsWith(entry + " ")test should not fire when the character that follows is an assignment operator rather than the start of an argument list. - Cursor should never file a fragment that is not a command as an allowlist entry. A bare variable name, a literal, an option, a property name, or a quoted string should be refused rather than stored, because it can only ever match by accident.
Operating System
Windows 10/11
Version Information
Version: 3.14.7 (system setup)
VS Code Extension API: 1.128.0
Commit: a758f2241ca99fecf380180b6cbdbbce0f1f42c0
Date: 2026-07-30T06:41:34.009Z
Layout: IDE
Build Type: Stable
Release Track: Default
Electron: 40.10.3
Chromium: 144.0.7559.236
Node.js: 24.15.0
V8: 14.4.258.32-electron.0
xterm.js: 6.1.0-beta.291
OS: Windows_NT x64 10.0.26200
For AI issues: which model did you use?
Not model-specific — this is terminal approval logic rather than model behavior. Observed while running
For AI issues: add Request ID with privacy disabled
N/A
Additional Information
Prior threads that establish the mechanism but not this case:
Those posts reported the same class of gap through command chaining, and the second was closed as fixed. So && is now split correctly while assignment is not.
Workaround, for anyone who finds this thread:
For now I have added a single deliberately broad entry $* to the allowlist, and introduced a beforeShellExecution hook that parses the command and returns ask unless every statement touches only session-local variables and commands already on the allowlist. That is a lot of custom code to buy back a guarantee the allowlist should already be providing. It is only possible because a hook’s ask overrides an allowlist match, which previously had issues but may be resolved now:
Does this stop you from using Cursor
No - Cursor works, but with this issue