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
quoteCmdArg corrupts arguments containing a literal " on cmd surfaces #162
quoteCmdArg corrupts arguments containing a literal " on cmd surfaces
When dor forwards a command tail (dor split -- …, dor ensure …) to a pane whose shell is cmd.exe, the host renders the argv with quoteCmdArg. For any argument that contains a double-quote, the output is malformed.
Trace
functionquoteCmdArg(arg: string): string{if(arg==='')return'""';constescaped=arg.replace(/[%]/g,'%%').replace(/([&|<>()^"])/g,'^$1');// caret-escapes the "if(WINDOWS_SAFE_ARG.test(arg))returnescaped;return`"${escaped}"`;// ...then wraps the result in quotes}
For arg = 'say "hi"' the function:
caret-escapes each " → say ^"hi^", then
wraps in surrounding quotes → "say ^"hi^"".
Inside a cmd double-quoted region, ^ is not an escape character — it is literal. So the embedded ^" does not produce an escaped quote; the first ^" is read as a literal ^ followed by a quote that closes the surrounding quoted region early, and the rest of the argument is re-parsed outside quotes. The argument the launched program receives is not say "hi".
The same belt-and-suspenders pattern (caret-escape and wrap in quotes) is what the existing test pins for a&b → "a^&b" (cli-output.test.mjs:204-206); for &/(/) the caret inside quotes is merely redundant (those chars are already literal inside quotes), so it's harmless-but-odd. For " it is actively wrong, and that case is untested.
Why this needs maintainer input rather than a drive-by fix
Correct cmd-line quoting for a literal " depends on how the receiving program parses its command line (the msvcrt/CommandLineToArgvW convention uses \" and ""; a bare cmd builtin differs). Picking the right escaping is a design decision that should be validated on an actual Windows + cmd.exe host, which this CI environment can't do. Flagging rather than guessing.
Suggested direction (needs Windows verification)
For the cmd kind, escape an embedded " by doubling it ("") or backslash-escaping (\") inside the wrapped form, and don't caret-escape characters that already sit inside the surrounding quotes. Add a cli-output.test.mjs case with an embedded " to pin whatever behavior is chosen.
Confirmed the reproduction by running the built function directly (buildShellCommandForKind('cmd', [...])):
"say \"hi\"" => "\"say ^\"hi^\"\""
"\"" => "\"^\"\""
"x\"y" => "\"x^\"y\""
"a&b" => "\"a^&b\"" # the existing, pinned case — caret here is harmless-but-redundant
So say "hi" produces "say ^"hi^"", exactly as the trace describes. The ^ sits inside the surrounding cmd double-quoted region where it's literal, so the embedded ^" doesn't escape anything — the first " closes the quoted region early and the tail re-parses outside quotes. Every argument containing a " is affected, and that case is currently untested.
I'm deferring the fix rather than guessing at it: correct cmd escaping for an embedded " depends on the receiving program's command-line parser (the msvcrt / CommandLineToArgvW convention uses \" / "", and the cmd.exe layer toggles quote state on every bare " regardless of a preceding ^). The two layers interact in a way that should be validated on a real Windows + cmd.exe host before committing, which this CI can't do — consistent with why this was filed as an issue rather than a PR.
When the escaping is decided, the natural home for a pinning case is the cmd assertion in cli-output.test.mjs:204-206, alongside the existing a&b case — adding an argument with an embedded ".
Activity
dormouse-bot commentedon Jun 20, 2026
Confirmed the reproduction by running the built function directly (
buildShellCommandForKind('cmd', [...])):So
say "hi"produces"say ^"hi^"", exactly as the trace describes. The^sits inside the surrounding cmd double-quoted region where it's literal, so the embedded^"doesn't escape anything — the first"closes the quoted region early and the tail re-parses outside quotes. Every argument containing a"is affected, and that case is currently untested.I'm deferring the fix rather than guessing at it: correct cmd escaping for an embedded
"depends on the receiving program's command-line parser (the msvcrt /CommandLineToArgvWconvention uses\"/"", and the cmd.exe layer toggles quote state on every bare"regardless of a preceding^). The two layers interact in a way that should be validated on a real Windows + cmd.exe host before committing, which this CI can't do — consistent with why this was filed as an issue rather than a PR.When the escaping is decided, the natural home for a pinning case is the
cmdassertion incli-output.test.mjs:204-206, alongside the existinga&bcase — adding an argument with an embedded".