Skip to content

quoteCmdArg corrupts arguments containing a literal " on cmd surfaces #162

Description

@dormouse-bot

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

function quoteCmdArg(arg: string): string {
  if (arg === '') return '""';
  const escaped = arg
    .replace(/[%]/g, '%%')
    .replace(/([&|<>()^"])/g, '^$1');   // caret-escapes the "
  if (WINDOWS_SAFE_ARG.test(arg)) return escaped;
  return `"${escaped}"`;                 // ...then wraps the result in quotes
}

For arg = 'say "hi"' the function:

  1. caret-escapes each "say ^"hi^", then
  2. 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.

Surfaced by the nightly code-quality survey.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions