$BROWSER: OAuth URL wrapped in literal quotes; on Windows a .cmd target truncates it at `=` and executes `&` query fragments via cmd.exe

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 16, 2026

What happened

When Claude Code opens an OAuth/sign-in URL, it launches the $BROWSER executable with the URL pre-wrapped in literal double-quotes ("<url>") via a no-shell spawn (the URL is passed as a raw argument, not run through a shell). This causes two distinct failures depending on platform:

Windows (when $BROWSER points to a .cmd/.bat target):
A .cmd/.bat cannot be spawned directly the way a native .exe can, so Windows re-invokes it through cmd.exe. cmd.exe re-parses the MSVCRT-escaped command line with broken quote state, and two things go wrong with a typical OAuth URL such as https://claude.ai/oauth/authorize?code=abc&state=xyz:

  1. The URL is truncated at the first =. The child receives a leading-literal-quote fragment cut off at the first =. Observed live, the browser navigated to "https//claude.ai/oauth/authorize?code (note the missing : is a downstream artifact; the key point is truncation at =).
  2. &-split query fragments are executed as commands. Because the URL is no longer inside intact quoting after the re-parse, cmd.exe treats & as a command separator and executes the query-string fragments (e.g. state=xyz) as separate shell commands. This is the same vulnerability class as CVE-2024-27980 (Node.js .bat/.cmd argument handling on Windows).

Linux:
The URL arrives as a single intact argv element but still wrapped in a literal quote pair ("<url>"), so the receiving browser/target sees the surrounding quotes as part of the URL unless it strips them.

Expected

$BROWSER should be invoked with the URL as a clean, single argument — no surrounding literal quotes, and never in a way that lets a .cmd/.bat intermediary re-split the URL's &/= characters. On Windows specifically, either the target should be invoked without a cmd.exe re-parse, or the URL should be quoted/escaped so it survives the cmd.exe hop intact.

Impact

  • OAuth sign-in through a custom $BROWSER (.cmd/.bat wrapper) fails outright on Windows — the browser gets a truncated URL and the auth code never reaches the correct endpoint.
  • The &-fragment execution is a command-injection surface: any attacker-influenced or malformed query fragment following an & is handed to cmd.exe as a command.

Environment

  • Claude Code v2.1.x (observed on v2.1.207), Windows 10/11.
  • $BROWSER set to a .cmd wrapper (required because a .ps1 cannot be used as a CreateProcess exec target directly).

Workaround we're using

In our $BROWSER shim we (a) Trim('"') the incoming argument on both platforms, and (b) on Windows recover the un-mangled URL from the parent cmd.exe's raw command line (Win32_Process.CommandLine), taking the first https://… run and stripping cmd's ^ escapes. This mitigates the URL truncation, but the &-fragment command execution happens in the cmd.exe hop before our shim runs and is not fixable from outside Claude Code.

Suggested fix direction

Pass the URL to $BROWSER without wrapping it in literal quotes, and on Windows avoid (or properly escape for) the cmd.exe re-parse when the target is a .cmd/.bat — mirroring the guidance from the CVE-2024-27980 remediation.

View original on GitHub ↗