wt doesn't remember the original shell type after "!" sftp command is used #10589

Closed
opened 2026-01-31 02:25:15 +00:00 by claunia · 6 comments
Owner

Originally created by @kitattyor on GitHub (Sep 11, 2020).

Environment

Windows build number: Microsoft Windows [Version 10.0.18363.1016]
Windows Terminal version (if applicable): 1.2.2381.0

OpenSSH_for_Windows_7.7p1, LibreSSL 2.6.5

Steps to reproduce

❯ sftp xx@xxxxxxxx
xx@xxxxxxxx's password:
Connected to xx@xxxxxxxx.
sftp> !

Expected behavior

! should bring me back to the original shell, which in my case was powershell.

Actual behavior

It went to cmd. I have to close that particular tab and open a new one to get powershell back.

Originally created by @kitattyor on GitHub (Sep 11, 2020). <!-- 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING: 1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement. 2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement. 3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number). 4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement. 5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement. All good? Then proceed! --> <!-- This bug tracker is monitored by Windows Terminal development team and other technical folks. **Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to Github issues**. Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue. If this is an application crash, please also provide a Feedback Hub submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal (Preview)" and choose "Share My Feedback" after submission to get the link. Please use this form and describe your issue, concisely but precisely, with as much detail as possible. --> # Environment ```none Windows build number: Microsoft Windows [Version 10.0.18363.1016] Windows Terminal version (if applicable): 1.2.2381.0 OpenSSH_for_Windows_7.7p1, LibreSSL 2.6.5 ``` # Steps to reproduce <!-- A description of how to trigger this bug. --> ``` ❯ sftp xx@xxxxxxxx xx@xxxxxxxx's password: Connected to xx@xxxxxxxx. sftp> ! ``` # Expected behavior <!-- A description of what you're expecting, possibly containing screenshots or reference material. --> `!` should bring me back to the original shell, which in my case was powershell. # Actual behavior <!-- What's actually happening? --> It went to cmd. I have to close that particular tab and open a new one to get powershell back.
claunia added the Resolution-ExternalNeeds-Tag-Fix labels 2026-01-31 02:25:15 +00:00
Author
Owner

@KalleOlaviNiemitalo commented on GitHub (Sep 11, 2020):

local_do_shell in sftp.c hardcodes cmd.exe for the case where ! is not followed by a command. It then calls the _wsystem function, which actually starts C:\WINDOWS\system32\cmd.exe /c C:\WINDOWS\system32\cmd.exe. That cmd.exe process then starts another cmd.exe process.

There is no reasonable way to make Windows Terminal override that behavior of sftp. Even if Windows Terminal changed the ComSpec environment variable to point to e.g. pwsh.exe instead, sftp would start pwsh.exe but still tell it to run cmd.exe.

(A very unreasonable way would be to make Windows Terminal detect when the user presses ! followed by Enter, and sneakily insert "pwsh" in between.)

It might be possible to make local_do_shell get the path of the shell from an environment variable that Windows Terminal could then set per profile. local_do_shell actually used to read the ComSpec variable for that purpose (in addition to the _wsystem function reading it) but that feature was removed in https://github.com/PowerShell/Win32-OpenSSH/issues/789 (diff), so perhaps the maintainers won't want to restore it.

Alternatively, it might be possible to make local_do_shell get the path of the shell from some per-user configuration file or Registry key. Then, it still would not match the Windows Terminal profile, but you'd at least be able to make it always use PowerShell.

@KalleOlaviNiemitalo commented on GitHub (Sep 11, 2020): [`local_do_shell` in `sftp.c`](https://github.com/PowerShell/openssh-portable/blob/9369d870ade51a44f4c2b7b7f1202fe1fdf7e7bb/sftp.c#L295) hardcodes `cmd.exe` for the case where `!` is not followed by a command. It then calls [the `_wsystem` function](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/system-wsystem?view=vs-2019), which actually starts `C:\WINDOWS\system32\cmd.exe /c C:\WINDOWS\system32\cmd.exe`. That `cmd.exe` process then starts another `cmd.exe` process. There is no reasonable way to make Windows Terminal override that behavior of `sftp`. Even if Windows Terminal changed the `ComSpec` environment variable to point to e.g. `pwsh.exe` instead, `sftp` would start `pwsh.exe` but still tell it to run `cmd.exe`. (A very unreasonable way would be to make Windows Terminal detect when the user presses <kbd>!</kbd> followed by <kbd>Enter</kbd>, and sneakily insert "pwsh" in between.) It might be possible to make `local_do_shell` get the path of the shell from an environment variable that Windows Terminal could then set per profile. `local_do_shell` actually used to read the `ComSpec` variable for that purpose (in addition to the `_wsystem` function reading it) but that feature was removed in <https://github.com/PowerShell/Win32-OpenSSH/issues/789> ([diff](<https://github.com/PowerShell/openssh-portable/pull/172/files#diff-bfa9ee6de996100a65619653d189809fL301>)), so perhaps the maintainers won't want to restore it. Alternatively, it might be possible to make `local_do_shell` get the path of the shell from some per-user configuration file or Registry key. Then, it still would not match the Windows Terminal profile, but you'd at least be able to make it always use PowerShell.
Author
Owner

@kitattyor commented on GitHub (Sep 11, 2020):

Hmm, honestly this feels like something that should be fixed at the pwsh sftp side rather than here. Any change in wt will probably just paper over this particular crack.

It might be possible to make local_do_shell get the path of the shell from an environment variable that Windows Terminal could then set per profile. local_do_shell actually used to read the ComSpec variable for that purpose (in addition to the _wsystem function reading it) but that feature was removed in PowerShell/Win32-OpenSSH#789 (diff), so perhaps the maintainers won't want to restore it.

This honestly seems like the best idea. The team mentioned security being the reason why they removed it; but I don't really understand why; maybe they do not want some rogue program to change ComSpec to some potentially compromised shell? In that case, maybe they would be ok with using the ComSpec value for "known good" shells (cmd, pwsh, pscore etc.) and if it's something else, then default to cmd with an error message.

Alternatively, it might be possible to make local_do_shell get the path of the shell from some per-user configuration file or Registry key. Then, it still would not match the Windows Terminal profile, but you'd at least be able to make it always use PowerShell.

Also possible, but I guess this has the potential to cause more code fragmentation. ComSpec has been around for a long time, so probably "less surprising" and easier for developers (for other apps that spawn child shells) to follow. 😄

@kitattyor commented on GitHub (Sep 11, 2020): Hmm, honestly this feels like something that should be fixed at the `pwsh` sftp side rather than here. Any change in `wt` will probably just paper over this particular crack. > It might be possible to make local_do_shell get the path of the shell from an environment variable that Windows Terminal could then set per profile. local_do_shell actually used to read the ComSpec variable for that purpose (in addition to the _wsystem function reading it) but that feature was removed in PowerShell/Win32-OpenSSH#789 (diff), so perhaps the maintainers won't want to restore it. This honestly seems like the best idea. The team mentioned security being the reason why they removed it; but I don't really understand why; maybe they do not want some rogue program to change `ComSpec` to some potentially compromised shell? In that case, maybe they would be ok with using the `ComSpec` value for "known good" shells (`cmd`, `pwsh`, `pscore` etc.) and if it's something else, then default to `cmd` with an error message. > Alternatively, it might be possible to make local_do_shell get the path of the shell from some per-user configuration file or Registry key. Then, it still would not match the Windows Terminal profile, but you'd at least be able to make it always use PowerShell. Also possible, but I guess this has the potential to cause more code fragmentation. `ComSpec` has been around for a long time, so probably "less surprising" and easier for developers (for other apps that spawn child shells) to follow. 😄
Author
Owner

@KalleOlaviNiemitalo commented on GitHub (Sep 11, 2020):

maybe they do not want some rogue program to change ComSpec to some potentially compromised shell?

If so, the change does not protect against that because the _wsystem function runs the program referenced by ComSpec anyway.

https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/start mentions a surprising feature:

When you run a command that contains the string CMD as the first token without an extension or path qualifier, CMD is replaced with the value of the COMSPEC variable. This prevents users from picking up cmd from the current directory.

So, it assumes ComSpec points to some version of cmd.exe. I think it would be risky to violate that assumption by making ComSpec instead point to the user's preferred interactive command-line shell, which may be incompatible with arguments intended for cmd.exe. The SHELL environment variable seems less likely to interfere with Windows features.

Windows 10 nowadays has a "Replace Command Prompt with Windows PowerShell in the menu when I right-click the start button or press Windows key+X" toggle in the taskbar settings. Perhaps the value of this setting correlates with the user's preferred shell. Does Windows offer a public API for reading this setting?

@KalleOlaviNiemitalo commented on GitHub (Sep 11, 2020): > maybe they do not want some rogue program to change `ComSpec` to some potentially compromised shell? If so, the change does not protect against that because the `_wsystem` function runs the program referenced by `ComSpec` anyway. <https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/start> mentions a surprising feature: > When you run a command that contains the string CMD as the first token without an extension or path qualifier, CMD is replaced with the value of the COMSPEC variable. This prevents users from picking up **cmd** from the current directory. So, it assumes `ComSpec` points to some version of `cmd.exe`. I think it would be risky to violate that assumption by making `ComSpec` instead point to the user's preferred interactive command-line shell, which may be incompatible with arguments intended for `cmd.exe`. The `SHELL` environment variable seems less likely to interfere with Windows features. Windows 10 nowadays has a "Replace Command Prompt with Windows PowerShell in the menu when I right-click the start button or press Windows key+X" toggle in the taskbar settings. Perhaps the value of this setting correlates with the user's preferred shell. Does Windows offer a public API for reading this setting?
Author
Owner

@kitattyor commented on GitHub (Sep 11, 2020):

Windows 10 nowadays has a "Replace Command Prompt with Windows PowerShell in the menu when I right-click the start button or press Windows key+X" toggle in the taskbar settings. Perhaps the value of this setting correlates with the user's preferred shell. Does Windows offer a public API for reading this setting?

According to this article, currently there's this registry key : HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\DontUsePowerShellOnWinX

It seems to have 2 values (I tested) :
0 : use powershell
1 : use cmd

It's not a proper API, so I guess changes are possible.

@kitattyor commented on GitHub (Sep 11, 2020): >Windows 10 nowadays has a "Replace Command Prompt with Windows PowerShell in the menu when I right-click the start button or press Windows key+X" toggle in the taskbar settings. Perhaps the value of this setting correlates with the user's preferred shell. Does Windows offer a public API for reading this setting? According to [this article,](https://www.itprotoday.com/windows-10/replacing-command-prompt-powershell-windows-10-start-menu) currently there's this registry key : `HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\DontUsePowerShellOnWinX ` It seems to have 2 values (I tested) : 0 : use powershell 1 : use cmd It's not a proper API, so I guess changes are possible.
Author
Owner

@KalleOlaviNiemitalo commented on GitHub (Sep 11, 2020):

If it ever becomes possible to upgrade to PowerShell 7 or later in the Win+X menu (https://github.com/PowerShell/PowerShell/issues/12747), then it would be nice to have an API that returns the path of the shell executable, rather than a number that the caller has to recognize. Anyway, it seems there is no such thing yet.

A new setting (e.g. DefaultLocalShell) in the user's ssh_config file seems the least risky way to proceed. It would be easy to support on POSIX operating systems, too. And if the user really wants the shell to depend on the Windows Terminal profile, then they can make DefaultLocalShell point to a wrapper that figures out which shell to use.

@KalleOlaviNiemitalo commented on GitHub (Sep 11, 2020): If it ever becomes possible to upgrade to PowerShell 7 or later in the <kbd>Win</kbd>+<kbd>X</kbd> menu (<https://github.com/PowerShell/PowerShell/issues/12747>), then it would be nice to have an API that returns the path of the shell executable, rather than a number that the caller has to recognize. Anyway, it seems there is no such thing yet. A new setting (e.g. `DefaultLocalShell`) in the user's [ssh_config](https://man.openbsd.org/ssh_config) file seems the least risky way to proceed. It would be easy to support on POSIX operating systems, too. And if the user really wants the shell to depend on the Windows Terminal profile, then they can make `DefaultLocalShell` point to a wrapper that figures out which shell to use.
Author
Owner

@DHowett commented on GitHub (Sep 11, 2020):

It looks like the issue reported in this thread isn't actionable for us, so I'm gonna close this out. However! Couple things:

@KalleOlaviNiemitalo is right: ComSpec has unfortunately taken on a second life as "path to thing that acts like COMMAND.COM"--applications will launch it and explicitly pass /K /C and /S and expect it to do the thing Command would do. It's not equivalent to /etc/passwd by any means . . . unfortunately.

That leaves us with the shell "API" for changing the preferred ... shell. Unfortunately, my team doesn't own that, and we can't make any guarantees about how it's going to change or whether it's going to stick around forever or anything like that.

We're definitely having these discussions internally, though, as to how we can move the "defaults" experience forward on Windows. Internal discussions are usually tied to OS versions, and those have too long of release cycles to ask anybody to rely on 😄

@DHowett commented on GitHub (Sep 11, 2020): It looks like the issue reported in this thread isn't actionable for us, so I'm gonna close this out. However! Couple things: @KalleOlaviNiemitalo is right: `ComSpec` has unfortunately taken on a second life as "path to thing that acts like COMMAND.COM"--applications will launch it and explicitly pass `/K` `/C` and `/S` and expect it to do the thing Command would do. It's not equivalent to `/etc/passwd` by any means . . . unfortunately. That leaves us with the shell "API" for changing the preferred ... shell. Unfortunately, my team doesn't own that, and we can't make any guarantees about how it's going to change or whether it's going to stick around forever or anything like that. We're definitely having these discussions internally, though, as to how we can move the "defaults" experience forward on Windows. Internal discussions are usually tied to OS versions, and those have too long of release cycles to ask anybody to rely on :smile:
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#10589