fix(login): avoid cmd.exe when opening the login URL on Windows (#1114)#1116
Open
maxmilian wants to merge 2 commits into
Open
fix(login): avoid cmd.exe when opening the login URL on Windows (#1114)#1116maxmilian wants to merge 2 commits into
maxmilian wants to merge 2 commits into
Conversation
…ymchenry#1114) openBrowser() launches the device-login verification URL returned by the login server. On Windows it ran the URL through `cmd /c start`, whose parser (and the `start` builtin) re-interprets shell metacharacters (& | ^ "). A malicious or compromised login server (or a CODEGRAPH_LOGIN_URL override) could embed such characters to inject commands. Launch via `rundll32 url.dll,FileProtocolHandler <url>` instead, which receives the URL as a single opaque argument with no shell re-parsing. macOS (open) and Linux (xdg-open) already pass the URL as a lone argv element and are unaffected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1114.
Problem
openBrowser()(src/reasoning/login.ts) launches the device-login verification URL that comes from the login server response (verification_uri_complete, where the server base isCODEGRAPH_LOGIN_URL/ defaultapp.getcodegraph.com). On Windows it ran:cmd.exe(and itsstartbuiltin) re-parse shell metacharacters (& | ^ "). A malicious or compromised login server — or a user tricked into pointingCODEGRAPH_LOGIN_URLat an attacker — could return averification_uri_completesuch ashttps://app.getcodegraph.com/device?...&calcand get arbitrary commands executed. (spawnwithoutshell: truedoes not help here: libuv only quotes args containing whitespace/quotes, so a space-free URL reaches cmd.exe unescaped.) Defense-in-depth (low): it needs a hostile server/env, but the browser-open path should never route a server-supplied string through a shell.Fix
Two layers:
rundll32 url.dll,FileProtocolHandler <url>instead ofcmd /c start.rundll32receives the URL as a single opaqueargvelement (Nodespawnwithoutshell), so there is no shell re-parsing. The macOS (open) and Linux (xdg-open) branches already pass the URL as a lone argument and are unchanged.https?://scheme allowlist before launching anything. All three OS handlers (open/rundll32,FileProtocolHandler/xdg-open) will happily execute afile:///...exe, bare, or UNC (\\host\share\payload.exe) path handed to them — swapping the launcher alone doesn't close that, the allowlist does, on every platform.Tests
__tests__/open-browser-security.test.ts: stubsprocess.platform = "win32", mockschild_process.spawn, and asserts (a) a URL containing&is not launched throughcmd//c, arrives at the launcher as one intact argument, and never withshell: true; (b)file:/UNC/bare-path/javascript:targets are refused (no spawn at all); (c) plainhttp://still launches. Written test-first (fails on the oldcmdpath, passes after the fix).vitest run+tsc --noEmitgreen.Reachability note (please weigh in)
As of current
main,openBrowser— and all ofsrc/reasoning/login.ts— has no callers: e5897d0 removed thecodegraph login/logout/usagecommand wiring and left the module orphaned. So onmainthis is hardening dead code (the vulnerable path does ship in already-released versions that had the command). If you'd rather deletelogin.tsoutright than patch it, happy to rework this PR into the removal instead.