From a6abcdb6d3b2c599736908ff765b28005414dbcf Mon Sep 17 00:00:00 2001 From: Peter Hauge Date: Fri, 5 Jun 2026 14:36:55 -0700 Subject: [PATCH] fix: resolve cmd.exe quoting and stdin issues in Invoke-MaskedProcess When Invoke-MaskedProcess runs inside Start-Job (a separate process), two issues caused failures on Windows: 1. RedirectStandardInput was false, so child processes inherited a broken stdin handle from the job's consoleless process, causing Python (az CLI) to hang on startup. Fixed by redirecting stdin and closing it immediately. 2. ArgumentList individually quotes each argument, but cmd.exe /c has its own quoting rules that conflict. Paths with spaces (C:\Program Files\...) broke, and metacharacters like parentheses in JMESPath queries were interpreted as cmd.exe grouping operators. Fixed by using the Arguments string property with proper quoting for the cmd.exe /c case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../modules/LogMasking.psm1 | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/integration/all-resource-types/modules/LogMasking.psm1 b/tests/integration/all-resource-types/modules/LogMasking.psm1 index 485e607..a93b491 100644 --- a/tests/integration/all-resource-types/modules/LogMasking.psm1 +++ b/tests/integration/all-resource-types/modules/LogMasking.psm1 @@ -261,20 +261,36 @@ function Invoke-MaskedProcess { ) $exe = Resolve-NativeExecutable -Name $FilePath - $finalArgs = @() + $exe.Prefix + $Arguments $psi = [System.Diagnostics.ProcessStartInfo]::new() $psi.FileName = $exe.FilePath $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true - $psi.RedirectStandardInput = $false + $psi.RedirectStandardInput = $true $psi.UseShellExecute = $false $psi.CreateNoWindow = $true $psi.StandardOutputEncoding = [System.Text.Encoding]::UTF8 $psi.StandardErrorEncoding = [System.Text.Encoding]::UTF8 - foreach ($a in $finalArgs) { - [void]$psi.ArgumentList.Add([string]$a) + if ($exe.Prefix.Count -ge 2 -and $exe.Prefix[0] -eq '/c') { + # cmd.exe /c has special quoting rules that conflict with ArgumentList. + # Use the Arguments string property with double-quote wrapping so cmd.exe + # preserves the inner quotes around paths that contain spaces. + $cmdTarget = $exe.Prefix[1] + $quotedParts = @("`"$cmdTarget`"") + foreach ($a in $Arguments) { + $s = [string]$a + # Quote every argument individually so cmd.exe treats all content + # (including metacharacters like parentheses) as literals. + $escaped = $s -replace '"', '\"' + $quotedParts += "`"$escaped`"" + } + $psi.Arguments = "/c `"$($quotedParts -join ' ')`"" + } else { + $finalArgs = @() + $exe.Prefix + $Arguments + foreach ($a in $finalArgs) { + [void]$psi.ArgumentList.Add([string]$a) + } } $stdoutQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]::new() @@ -305,6 +321,7 @@ function Invoke-MaskedProcess { try { [void]$proc.Start() + $proc.StandardInput.Close() $outJob = Start-ThreadJob -ScriptBlock $readerScript -ArgumentList $proc.StandardOutput, $stdoutQueue $errJob = Start-ThreadJob -ScriptBlock $readerScript -ArgumentList $proc.StandardError, $stderrQueue