You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[BUG] Destructive Command Guard parses commands as POSIX shell while execute_command runs cmd.exe on Windows #2082
On Windows, execute_command runs through cmd.exe, but the Destructive Command Guard parses the same string as POSIX shell. The two disagree, so the guard refuses commands cmd would run happily, and cannot recognise the
destructive forms that belong to the shell it is actually protecting.
What happened (Zoo Code 3.90.0, Windows 11, VS Code)
A command that is valid cmd — a python -c "…" one-liner with nested quotes, chained after a git command:
Destructive Command Guard blocked this command. Message from DCG: ambiguous command substitution could not be parsed
as POSIX shell syntax
Nothing in that string is a command substitution under cmd.exe; the quotes are ordinary.
The same session shows which shell is really in play: type, findstr and dir /b all return cmd output, and %ERRORLEVEL% and set X=Y behave as cmd. So the executor is cmd.exe, and the guard is reading a different language.
Why it matters
False positives. Any quoting a POSIX parser cannot resolve is refused, though cmd.exe would run it. Each refusal
costs a turn and gives the model an error it cannot act on — it does not know whether to rephrase for POSIX or cmd.
Blind spots. cmd-only destructive forms — rd /s, del /q, format, diskpart — sit outside the grammar the
guard reasons in, so they cannot be matched as destructive. The guard is strongest where it is least needed.
It is silent about the dialect. Nothing in the refusal says which shell it assumed, so the user cannot tell
whether the command is dangerous or merely unparseable.
Suggested direction
Parse in the dialect the command will actually run in — whatever that is, rather than a fixed one. Today it is COMSPEC
(cmd.exe) on Windows; if the shell selection raised in the executor issue is fixed, it becomes whatever the configured
profile names, bash or PowerShell or WSL. Where the dialect genuinely cannot be determined, a refusal is still the right
default — but say which shell was assumed, so the operator can tell "dangerous" from "unreadable".
Related
#82, #705, #1673 (closed) — earlier rounds of shell selection on Windows; #1947, #2068 (open) — the opposite direction,
nested substitutions bypassing the allowlist.
Companion issue: #2083 - on Windows the command shell is COMSPEC (cmd.exe), with no way to point it at the configured terminal profile. This issue is the guard side of the same assumption: whichever shell is settled on, the guard has to parse that one, and say which it assumed when it refuses.
Measured on Windows 11, Zoo Code 3.90.0, with cmd.exe as the executor - the same setup as the report above. The refusals
line up with a second shell inside the command rather than with the command itself, which may be useful for the fix
and for the refusal text.
Four shapes, one session:
powershell -NoProfile -ExecutionPolicy Bypass -File tools/read-window.ps1 -ProcessId <pid> - refused: PowerShell host option "-File" is unknown or ambiguous.
powershell -NoProfile -Command "<literal body>" at the head of the command - ran.
tools\read-window.cmd -ProcessId <pid>, a .cmd shim whose launcher carries %~dp0 and %* - ran three times, its
switches arriving each time.
A launcher embedded in a longer command - chained behind something else, or carrying $ or % in its body -
refused: embedded PowerShell launcher command contains runtime expansion that DCG cannot statically verify.
Two observations:
The -File refusal is the POSIX parse this issue describes: -File is a PowerShell host option, and POSIX grammar
has no notion of it.
The "runtime expansion" refusal is worded as a static-analysis limit rather than a parse error. If refusing what the
guard cannot verify is deliberate - and it seems a reasonable default - then that one is not a bug. What is hard for
the operator is that both arrive as the same kind of card: today one session was blocked for something genuinely
destructive (deleting a git branch, which needs approval) and blocked again for something merely unreadable, and
nothing in either message says which shell was assumed.
So the ask in this issue - name the dialect the guard assumed - would separate "dangerous" from "unparseable" for the
person deciding whether to approve.
Cause.runner.ts runs dcg test --format json --no-color <command>. It passes no shell or dialect option. dcg v0.7.7 has no such option and parses every command as POSIX. The refusal text is hardcoded: ambiguous command substitution could not be parsed as POSIX shell syntax.
Pinned version is old.DCG_VERSION in src/services/destructive-command-guard/constants.ts is v0.7.7. The latest upstream release is v0.15.3 (2026-10-08).
Blind spots today (v0.7.7, Linux build). The windows.filesystem and windows.misc packs are off by default, and Zoo Code does not enable them. With --with-packs windows.filesystem,windows.misc:
Activity
SteveALee commentedon Oct 17, 2026
Companion issue: #2083 - on Windows the command shell is COMSPEC (cmd.exe), with no way to point it at the configured terminal profile. This issue is the guard side of the same assumption: whichever shell is settled on, the guard has to parse that one, and say which it assumed when it refuses.
SteveALee commentedon Oct 18, 2026
One more symptom, and what it narrows to
Measured on Windows 11, Zoo Code 3.90.0, with cmd.exe as the executor - the same setup as the report above. The refusals
line up with a second shell inside the command rather than with the command itself, which may be useful for the fix
and for the refusal text.
Four shapes, one session:
powershell -NoProfile -ExecutionPolicy Bypass -File tools/read-window.ps1 -ProcessId <pid>- refused:PowerShell host option "-File" is unknown or ambiguous.powershell -NoProfile -Command "<literal body>"at the head of the command - ran.tools\read-window.cmd -ProcessId <pid>, a.cmdshim whose launcher carries%~dp0and%*- ran three times, itsswitches arriving each time.
$or%in its body -refused:
embedded PowerShell launcher command contains runtime expansion that DCG cannot statically verify.Two observations:
-Filerefusal is the POSIX parse this issue describes:-Fileis a PowerShell host option, and POSIX grammarhas no notion of it.
guard cannot verify is deliberate - and it seems a reasonable default - then that one is not a bug. What is hard for
the operator is that both arrive as the same kind of card: today one session was blocked for something genuinely
destructive (deleting a git branch, which needs approval) and blocked again for something merely unreadable, and
nothing in either message says which shell was assumed.
So the ask in this issue - name the dialect the guard assumed - would separate "dangerous" from "unparseable" for the
person deciding whether to approve.
edelauna commentedon Oct 19, 2026
Findings: cause and what upstream already offers
Cause.
runner.tsrunsdcg test --format json --no-color <command>. It passes no shell or dialect option.dcgv0.7.7 has no such option and parses every command as POSIX. The refusal text is hardcoded:ambiguous command substitution could not be parsed as POSIX shell syntax.Pinned version is old.
DCG_VERSIONinsrc/services/destructive-command-guard/constants.tsisv0.7.7. The latest upstream release isv0.15.3(2026-10-08).Upstream already handles dialects.
dcg testaccepts--dialectwithposix,cmdandunknown(unknownevaluates every dialect). Source: upstream issue fix(vertex): warn when 'Google Cloud Credentials' field receives a file path #294, reported on v0.9.4. I did not run it on v0.15.3, so the exact values need a check.-Filefalse positive: upstream [Bedrock] Expand explicit 1M context target variants #304 (closed).del,rd,rmdir: upstream [BUG] Input box text lost when clicking "Continue" #487 and [BUG] Semble download fails #491 (closed).windows.*packs did not activate for non-Windows hosts: upstream fix: remove unsupported --no-absolute-filenames tar arg and fix tests #494 and [BUG] Unable to create new provider profile with different model. #451 (closed).Blind spots today (v0.7.7, Linux build). The
windows.filesystemandwindows.miscpacks are off by default, and Zoo Code does not enable them. With--with-packs windows.filesystem,windows.misc:rd /s /q foo: denied.format c:: denied.del /q foo: still allowed.Without the packs, all three are allowed.
Still open upstream.
cmd //cdenied as an unknown host switch.-cprogram is analysed only up to the first embedded quote.Proposed fix in Zoo Code
DCG_VERSIONand the archive SHA-256 values. Related: chore(dcg): automate DCG_VERSION + checksum updates via Renovate #1067.--dialect cmdwhen the executor is cmd.exe. Pass--dialect unknownwhen the shell is not known. This depends on [BUG] On Windows, execute_command always runs cmd.exe via COMSPEC and ignores the configured terminal shell #2083.--with-packs windows.filesystem,windows.miscon Windows.Step 4 needs no
dcgchange. I tested only the Linux build of v0.7.7.PierrunoYT commentedon Oct 20, 2026
Working on it!