Upgrade azureauth-cli to 0.9.4 with native Linux support#142
Conversation
Agent-Logs-Url: https://github.com/microsoft/ado-npm-auth/sessions/3f335052-804f-4397-a3e1-a5754e50896a Co-authored-by: jcreamer898 <472487+jcreamer898@users.noreply.github.com>
| `ado`, | ||
| `pat`, | ||
| `--prompt-hint ${isWsl() ? options.promptHint : `"${options.promptHint}"`}`, // We only use spawn for WSL. spawn does not does not require prompt hint to be wrapped in quotes. exec does. | ||
| `--prompt-hint ${isLinux() ? options.promptHint : `"${options.promptHint}"`}`, // We only use spawn for Linux (includes WSL). spawn does not require prompt hint to be wrapped in quotes. exec does. |
Check warning
Code scanning / CodeQL
Unsafe shell command constructed from library input Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 months ago
In general, the safest fix is to avoid going through the shell altogether and instead pass arguments as an array to a function like child_process.spawn/spawnSync or execFile. When that’s not possible, we should not manually build a single command string with interpolated values; instead, parse or quote arguments with a robust library such as shell-quote.
Here, the Linux path is already safe because it uses spawnSync(command[0], command.slice(1), ...) with a proper argument array. The unsafe part is the non‑Linux branch where we call exec(command.join(" "), { env }). The minimal, behavior‑preserving fix is:
- Import
spawnSyncdirectly from"node:child_process"is already done. - Stop joining
commandinto a single string in the non‑Linux branch. - Use
spawnSyncthere as well (like the Linux path), passing theenvfromazureAuthCommand()and encoding"utf-8". - Remove the special quoting logic that embeds quotes into the
--prompt-hintstring for non‑Linux; instead, always pass--prompt-hintas two separate arguments,[ "--prompt-hint", options.promptHint ], which makes quoting unnecessary. This preserves functional behavior while avoiding shell interpretation. - Similarly, split other
--flag valuepairs into distinct array elements instead of interpolated--flag ${value}strings, reducing the chance of accidental changes if someone later reintroduces a join.
These changes are all confined to packages/ado-npm-auth-lib/src/azureauth/ado.ts, specifically around the command array construction and the else branch that currently calls exec.
| @@ -51,28 +51,31 @@ | ||
|
|
||
| const command = [ | ||
| ...authCommand, | ||
| `ado`, | ||
| `pat`, | ||
| `--prompt-hint ${isLinux() ? options.promptHint : `"${options.promptHint}"`}`, // We only use spawn for Linux (includes WSL). spawn does not require prompt hint to be wrapped in quotes. exec does. | ||
| `--organization ${options.organization}`, | ||
| `--display-name ${options.displayName}`, | ||
| ...options.scope.map((scope) => `--scope ${scope}`), | ||
| "ado", | ||
| "pat", | ||
| "--prompt-hint", | ||
| options.promptHint, | ||
| "--organization", | ||
| options.organization, | ||
| "--display-name", | ||
| options.displayName, | ||
| ...options.scope.flatMap((scope) => ["--scope", scope]), | ||
| ]; | ||
|
|
||
| if (options.output) { | ||
| command.push(`--output ${options.output}`); | ||
| command.push("--output", options.output); | ||
| } | ||
|
|
||
| if (options.mode) { | ||
| command.push(`--mode ${options.mode}`); | ||
| command.push("--mode", options.mode); | ||
| } | ||
|
|
||
| if (options.domain) { | ||
| command.push(`--domain ${options.domain}`); | ||
| command.push("--domain", options.domain); | ||
| } | ||
|
|
||
| if (options.timeout) { | ||
| command.push(`--timeout ${options.timeout}`); | ||
| command.push("--timeout", options.timeout); | ||
| } | ||
|
|
||
| try { | ||
| @@ -93,10 +84,15 @@ | ||
| } | ||
| } else { | ||
| try { | ||
| result = await exec(command.join(" "), { env }); | ||
| result = spawnSync(command[0], command.slice(1), { | ||
| encoding: "utf-8", | ||
| env, | ||
| }); | ||
|
|
||
| if (result.stderr && !result.stdout) { | ||
| throw new Error(result.stderr); | ||
| if (result.status !== 0 || (result.stderr && !result.stdout)) { | ||
| throw new Error( | ||
| `Azure Auth failed with exit code ${result.status}: ${result.stderr}`, | ||
| ); | ||
| } | ||
| } catch (error: any) { | ||
| throw new Error( |
Upgrades
azureauth-clifrom0.8.4to0.9.4, which adds native Linux support outside of WSL. Updates all platform-detection logic to treat native Linux and WSL uniformly — callingazureauthdirectly rather than routing throughazureauth.exeornpm exec.Core Changes
node-azureauth/src/install.ts: Version bump to0.9.4; Windows artifact renamed fromwin10-x64.zip→win-x64.zip; removed stalelinux: "azureauth.exe"fromAZUREAUTH_NAME_MAPutils/is-wsl.ts: NewisLinux()export —platform() === "linux"— covers both native Linux and WSL;isWsl()retained for telemetryazureauth-command.ts:isWsl()+["azureauth.exe"]→isLinux()+["azureauth"]ado.ts: AllisWsl()guards replaced withisLinux()(spawn-vs-exec, prompt-hint quoting)is-supported-platform-and-architecture.ts: Addedlinux: ["x64", "arm64"]; removed redundantisWsl() ||OR checkBefore / After:
References and Relevant Issues
azureauth-cli 0.9.4 release: https://github.com/AzureAD/microsoft-authentication-cli/releases/tag/0.9.4
Detailed Description of the Pull Request / Additional comments
Tests updated to mock
isLinux(replacingisWsl), addclearMemotobeforeEachso each test gets a clean command memo, and assertspawnSync("azureauth", [...])rather than the old npm-exec invocation path.Validation Steps Performed
All 22 existing tests pass with no modifications to test counts or test logic — only mock targets and expected values updated to reflect new behavior.
PR Checklist
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
https://api.github.com/repos/AzureAD/microsoft-authentication-cli/releases/tags/0.9.4/home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js(http block)If you need me to access, download, or install something from one of these locations, you can either:
⚡ Quickly spin up Copilot coding agent tasks from anywhere on your macOS or Windows machine with Raycast.