fix: quote dropped and pasted paths for the pane's shell, not the host platform - #432
fix: quote dropped and pasted paths for the pane's shell, not the host platform#432dormouse-bot wants to merge 4 commits into
Conversation
…atform shellEscapePath picked cmd.exe quoting from IS_WINDOWS alone, so a PowerShell pane got a double-quoted path — and PowerShell double quotes are expandable. Dropping a file named `$(calc.exe).txt` staged `"$(calc.exe).txt"`, which ran the subexpression as soon as the user pressed Enter, which is the whole point of dropping a file in. Git Bash and WSL panes on Windows had the same mismatch, minus the execution. Dispatch on the selected shell instead, classified by dor's shellCommandKind (the app-global default stands in for the pane's shell, as dor's own command quoting already does), and reuse dor's quotePowerShellArg for the PowerShell branch. With no shell selected the host platform is still the fallback, so cmd panes and every posix host keep their current escaping. Closes #430
Deploying mouseterm with
|
| Latest commit: |
6a7e5ff
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://aac870f4.mouseterm.pages.dev |
| Branch Preview URL: | https://fix-issue-430.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
Self-review — the dispatch itself reads correctly (the cmd/no-shell fallback preserves today's behavior exactly, and the app-global stand-in is the only signal available: PersistedPane carries no shell and terminal-state's shellName is a display label, so there is no per-pane shell to thread). One narrow regression in the reused helper, flagged inline.
Also: paneShellKind's if (!shell) return !IS_MAC && IS_WINDOWS ? 'cmd' : 'posix' re-implements the fallback shellCommandKind already has (if (!normalizedShell && /win/i.test(platformString)) return 'cmd'), so the "Windows means cmd" rule now lives in two places and can drift. use-dor-control.ts's dorCommandString already relies on the built-in one — passing a possibly-undefined shell straight through.
I'm the author here, so rather than leave these as suggestions nobody will apply, I'll push both fixes to this branch.
…back Two self-review findings on the previous commit. `quotePowerShellArg`'s `WINDOWS_SAFE_ARG` allowed `,`, so a path built only from safe characters was returned bare — and PowerShell's argument mode reads a bare comma as the array operator, so `C:\a,b.png` reached the command as two arguments. Routing dropped paths through this helper would therefore have traded the `$(...)` execution bug for a mis-tokenization on comma-bearing filenames, which the old cmd-style double quoting handled by accident. cmd.exe treats `,` as an argument separator too, so `quoteCmdArg` shared the hole; dropping `,` from the shared regex closes both and only ever makes the output more conservative. `paneShellKind` re-implemented the platform fallback that `shellCommandKind` already has, putting "an unset shell means cmd on Windows" in two places. Call the classifier with the possibly-undefined shell instead, the way `use-dor-control.ts`'s `dorCommandString` already does.
dormouse-bot
left a comment
There was a problem hiding this comment.
Re-review after c7f3514, which replaced the app-global stand-in with per-Session shell capture. The plumbing holds end to end: the sidecar stores config.shell after resolveSpawnConfig, so a default-shell PTY reports the resolved ComSpec / $SHELL rather than undefined; the ptys.get(id) === p generation guard already covers ptyShells cleanup on a respawn under the same id; VS Code's pty-manager buffers the requested shell instead, but its PTY host is the same pty-core module (vscode-ext/src/pty-host.js requires lib/pty-core.cjs), so an absent value resolves to cmd.exe on Windows and classifies the same on both hosts — the claim the new PtyBufferEntry.shell comment makes. Cold restore already passed shellOpts?.shell into restoreTerminal, and Tauri's Rust bridge forwards the sidecar's list payload as raw JSON, so neither needed a change to carry the new field.
One test gap, inline: @ was dropped from WINDOWS_SAFE_ARG alongside ,, and the comment justifies both by cmd.exe behavior as much as by PowerShell's — but the only assertion added covers the PowerShell consumer. quoteCmdArg shares the constant and nothing pins its output for either character.
Separately, the PR description still describes the superseded design ("the app-global selected shell stands in for the pane's shell, which is not tracked per-session") and doesn't mention the pty:list shell field or the registry capture that replaced it. I'm updating it in place.
quoteCmdArg shares WINDOWS_SAFE_ARG with quotePowerShellArg, so dropping , and @ from the set changed cmd output too; only the PowerShell side was asserted.
Problem
shellEscapePathchose its quoting rule fromIS_WINDOWS, so every pane on a Windows host gotcmd.exequoting — double quotes with embedded"doubled. That is sound forcmd.exe, which does no expansion inside double quotes, but a PowerShell double-quoted string is expandable:$(...)runs a subexpression and$nameinterpolates. Dropping or pasting a file named$(calc.exe).txtinto a PowerShell pane staged the line"$(calc.exe).txt", and the subexpression ran the moment the user pressed Enter — which they have every reason to do, since they just dropped the file in to use its path. Git Bash and WSL panes on Windows had the same parser mismatch (minus the execution): they read posix quoting, not cmd quoting.Solution
Quote for the shell that will parse the line, not for the host platform.
shellEscapePathnow takes aShellCommandKindand dispatches three ways — posix backslash-escaping,cmddouble quotes, PowerShell single-quoted literals viador'squotePowerShellArg(exported for this).The kind comes from the Session, not from the app-global default, because a Windows user can keep PowerShell, Git Bash, WSL, and
cmd.exepanes live at the same time and change the default for future terminals at any point. EachTerminalEntrycaptures ashellKindwhen it is created, classified from the shell that Session actually launched with:shellCommandKindandspawnPty.session-restore.tsalready passed the current default intorestoreTerminal; that shell is now captured too.pty:listgains an optionalshellfield carrying each live PTY's launch shell, so a webview reload rebuilds the same kind. The standalone sidecar reports it post-resolveSpawnConfig(a default-shell PTY reports the resolvedComSpec/$SHELL, notundefined); the VS Code extension host buffers the requested shell, whose absence resolves through the samepty-coremodule and so classifies identically.Only a paste into an id with no registry entry falls back to the app-global shell, then to the platform — so
cmdpanes and every posix host keep exactly their current escaping.quotePowerShellArg's bare-argument set also had to lose,: PowerShell's argument mode reads a bare comma as the array operator, soC:\a,b.pngwould have reached the command as two arguments — a mis-tokenization the old cmd-style double quoting handled by accident.@went with it (splatting / array / hashtable syntax at the head of a token).cmd.exetreats,as an argument separator and@as echo suppression, so the sharedWINDOWS_SAFE_ARGfix coversquoteCmdArg(and thereforedor's owncmdcommand quoting) as well.greping for the same platform-infers-shell pattern elsewhere turned up nothing — the otherIS_WINDOWSreads (win32-input-mode, the updater's sidecar kill) are genuinely about the host, not about a shell parser.Testing
lib/src/lib/shell-escape.test.tscovers the dispatch directly: PowerShell subexpressions,$env:interpolation, embedded-'doubling, comma- and@-bearing paths, bare inert paths, and a posix (Git Bash / WSL) Session on Windows. Around it,clipboard.test.tspins that a PowerShell Session still gets PowerShell quoting after the app default changes,terminal-registry.alert.test.tspins capture on both spawn and resume,reconnect.test.tspins theshellfield survivingpty:list,pty-core.test.jspins the sidecar reporting it, andcli-output.test.mjspins the classifier's platform fallback. Fulllib,dor, and sidecar suites green;tsc -bclean.Specs
docs/specs/mouse-and-clipboard.md§8.6 gains the per-shell escaping rules and the Session-kind selection order it was missing.docs/specs/transport.mddocuments the newpty:listfield, anddocs/specs/dor-cli.mdcross-links to §8.6 since both now go through the same classifier.Closes #430 — automated triage