Skip to content

Refactor PS login to use a static param()-bound script - #600

Draft
MaddyMicrosoft wants to merge 2 commits into
Azure:masterfrom
MaddyMicrosoft:fix/pwsh-static-login-script
Draft

Refactor PS login to use a static param()-bound script#600
MaddyMicrosoft wants to merge 2 commits into
Azure:masterfrom
MaddyMicrosoft:fix/pwsh-static-login-script

Conversation

@MaddyMicrosoft

@MaddyMicrosoft MaddyMicrosoft commented Jul 30, 2026

Copy link
Copy Markdown
Member

What

Move the Azure PowerShell login step from an interpolated PS script string to a static AzPSLogin.ps1 file invoked via pwsh -File, with user values passed as bound parameters and secret-shaped values passed via env vars.

Why

The current AzPSScriptBuilder concatenates action inputs into a PowerShell script string. Every interpolation is a potential injection surface if escaping is missed for even one field. This refactor gives the PS path the same structural safety the CLI path (AzureCliLogin.ts) has always had: data and code travel through different channels.

  • User values (tenant-id, subscription-id, client-id, resourceManagerEndpointUrl) ride as argv → PowerShell parameter binding treats them as inert strings.
  • Secret-shaped values (clientSecret, federatedToken) ride as env vars → they never appear in argv, ps aux, or the core.debug log.

Changes

File Purpose
src/PowerShell/AzPSLogin.ps1 New. Static PS script with param() binding and splatted Connect-AzAccount.
src/PowerShell/AzPSScriptBuilder.ts Rewritten. Returns { methodName, args, env } instead of a script string.
src/PowerShell/AzPSLogin.ts Calls new invocation API. Debug log now shows argv, not script text.
src/PowerShell/AzPSUtils.ts Shared runPwsh helper; new runPSFile(args, env).
scripts/copy-ps-assets.js New. Copies AzPSLogin.ps1 into lib/main/ at build time.
package.json build:main runs the copy script.
README.md Two new [!WARNING] blocks: don't pipe ${{ github.event.* }} into these inputs; keep enable-AzPSSession off unless needed.

Verification

  • 30/30 unit tests pass. New adversarial tests cover single-quote, double-quote, newline, $, and switch-shaped values in tenant, subscription, client-id, and resourceManagerEndpointUrl.
  • End-to-end tested with real pwsh: adversarial payloads arrive at Connect-AzAccount / Add-AzEnvironment as literal string data, not code.
  • npm run build produces lib/main/index.js + lib/main/AzPSLogin.ps1.

Scope notes

On LoginConfig format-validation suggestion: deliberately not included. Azure's ARM API spec (Microsoft.Subscription/subscriptions/rename, SubscriptionName schema) allows any Unicode string for subscription display names, and Az.Accounts' -Subscription accepts both GUIDs and display names via [Alias("SubscriptionName", "SubscriptionId")]. Any character-class check we impose has legitimate false positives against real subscription names like Prod (US East). The refactor closes the exploitation surface structurally; format validation is defense-in-depth we couldn't cleanly bound.
Happy to add per-field checks for tenant-id and client-id (which really are always GUID/domain) if reviewers prefer.

Behaviour change worth noting

core.debug log format: previously printed the fully interpolated PS script (including a ConvertTo-SecureString 'value' line subject to core.setSecret masking). Now prints the argv sent to pwsh and contains no secret-shaped values at all. Only visible when ACTIONS_STEP_DEBUG is enabled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant