Repository navigation
Conversation
Fixes dotnet#45700. SpaProxyLaunchManager appends '.cmd' to a bare launch command on Windows to handle the npm/yarn shim convention. Tools installed via standalone script (notably pnpm at https://pnpm.io/installation#using-a-standalone-script) ship a '.exe' instead of a '.cmd', so the existing transform turns 'pnpm dev' into 'pnpm.cmd dev' and the launcher fails with 'Failed to launch the SPA development server'. Behavior change: when the bare command resolves to a '.cmd' on PATH, nothing changes (npm, yarn, anything currently working). When no '.cmd' shim is found but a '.exe' is, use the '.exe'. If neither resolves, fall back to '.cmd' so the existing diagnostic message is unchanged. The resolution logic is factored into an 'internal static' helper with an injectable 'Func<string, bool>' for PATH probing, so it can be exercised by tests if a test project is introduced for SpaProxy (none exists today).
|
Thanks for your PR, @bsallesp. Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
There was a problem hiding this comment.
Removed in 449b7fd. Also cleaning it from the fork's main so future branches don't inherit it.
| internal static string ResolveWindowsLaunchCommand(string command) | ||
| => ResolveWindowsLaunchCommand(command, ExistsOnPath); | ||
|
|
||
| internal static string ResolveWindowsLaunchCommand(string command, Func<string, bool> existsOnPath) |
There was a problem hiding this comment.
You're right — it was speculative for a test project that doesn't exist yet. Collapsed to a single private static method that calls ExistsOnPath directly in 449b7fd.
- FORK_SCOPE.md was leftover fork documentation that should not be in an upstream contribution. Removed. - Collapsed the two ResolveWindowsLaunchCommand overloads into a single private static method. The Func<string, bool> seam was speculative (no test project exists today); YAGNI.
|
This covers a second case: nvm-windows v2's default shim mode puts only Matrix row if useful: nvm-windows v2 shim mode | Details and repro: nvm-windows/nvm#1405. |
|
I have been carrying #66924 for the same issue, taking the narrower route of resolving Probing PATH for Two things from my branch that may be useful to you, take or leave:
Happy to send either as a PR against your branch if that is easier than lifting it. |
|
Creator of NVM for Windows here. As @AllainPL described, we put an npm.exe shim on the 1. npm has no ABI contract Currently, npm has 2. npm.cmd/npm.ps1 are exploitable These files cannot be code-signed. They're easily replaceable, i.e. nvm-windows ships a code-signed npm/npx/pnpm/yarn executable that directly invokes the underlying JS module from a DACL-protected directory. The nvm-windows node shim verifies the npm shim's digital signature, while the npm shim monitors JS file SHASUMs for unintentional changes. The result is an E2E guarantee that you're actually running the npm you intend to run, but it depends on entry via npm.exe. You can see an example of why we use this approach in nvm-windows/nvm#1403 (comment) In the coming months, there will be more announcements about security around Node.js. It's actually one of the main subjects for NodeConf EU in October 2026. npm continues to be an attack target, so it wouldn't surprise me to see the ABI remove some of these vulnerable files. My recommendation is to honor the guarantee that an npm executable will be on |
Fixes #45700.
Bug
SpaProxyLaunchManagerappends.cmdto any bare launch command on Windows to handle the npm/yarn shim convention:Tools installed via standalone script ship a
.exeinstead of a.cmd. The most common case in the wild is pnpm installed via the standalone script: it placespnpm.exeon PATH with no companion.cmd. The transform above turnspnpm devintopnpm.cmd devand the launcher fails:Same shape applies to other modern Node toolchains that ship a single
.exe(bun installed via PowerShell installer, etc.).Fix
When the bare command does not already carry an extension on Windows, probe PATH for a
.cmdshim first (preserves the existing behavior for npm/yarn). If no.cmdis on PATH but.exeis, use.exe. If neither resolves, fall back to.cmdso the existing diagnostic message is unchanged.Why not blind
.exefallback or removing the transform.cmdtransform entirely (suggested by@morganbpin the thread): some npm setups on Windows depend on the.cmdshim being launched explicitly viaProcessStartInforather than letting the shell resolve PATHEXT. Probing PATH for.cmdfirst preserves that path..exeblindly when.cmdexists: would change behavior for environments that have both (some scoop/winget npm packages do).Probing strategy
ExistsOnPathwalks thePATHenvironment variable and checksFile.Existsfor each directory. PATH parsing tolerates invalid entries (catches per-directory exceptions instead of failing the whole probe). The single iteration runs at SPA proxy launch time only, not on every request.Tests
No test project exists for
Microsoft.AspNetCore.SpaProxytoday, so I have not added unit tests in this PR. The resolution helper is intentionally:internal static(no instance state),Func<string, bool>for PATH probing,so a follow-up that introduces a
Microsoft.AspNetCore.SpaProxy.Testsproject can exercise it directly. Happy to do that as a follow-up if maintainers prefer the new project to ship in this PR.Behavior matrix
commandnpm install -g(only.cmdexists)npmnpm.cmdnpm.cmdnpm install -g yarnyarnyarn.cmdyarn.cmd.exe)pnpmpnpm.cmd❌pnpm.exe✅<SpaProxyLaunchCommand>pnpm.cmd dev</SpaProxyLaunchCommand>(explicit)pnpm.cmdpnpm.cmdpnpm.cmd.cmdnor.exeon PATHwhateverwhatever.cmdwhatever.cmd(same diagnostic)Draft because I have not been able to run the aspnetcore build locally (preview SDK + Linux). Will mark Ready for Review once CI is green; happy to address review feedback while in draft.