fix(mxc): harden provider credential test runner

- preserve native JSON arguments under Windows PowerShell
- tolerate existing gateway registrations
- reject network executable paths with actionable launch diagnostics
- record redacted failure context in result bundles

Signed-off-by: Akber Raza <akberr@nvidia.com>
This commit is contained in:
Akber Raza
2026-09-14 18:16:05 -07:00
committed by Drew Newberry
parent 1db16e0fa0
commit 550b81afff
@@ -12,9 +12,12 @@
# $env:GITHUB_TOKEN = "github_pat_..." # $env:GITHUB_TOKEN = "github_pat_..."
# mise run --skip-tools windows:build:x64 # mise run --skip-tools windows:build:x64
# #
# Run from a demo-package folder containing openshell-gateway.exe, openshell.exe, # Run from a local demo-package folder containing openshell-gateway.exe,
# the PowerShell probe, and the three configuration fixtures beside this script, # openshell.exe, the PowerShell probe, and the three configuration fixtures
# or pass explicit gateway and CLI paths: # beside this script, or pass explicit local gateway and CLI paths. When the
# script is copied to a network share, keep the executables on a local volume;
# Windows Application Control commonly rejects unsigned development binaries
# launched from UNC or mapped network paths.
# #
# powershell -NoProfile -ExecutionPolicy Bypass ` # powershell -NoProfile -ExecutionPolicy Bypass `
# -File .\run-provider-credential-test.ps1 ` # -File .\run-provider-credential-test.ps1 `
@@ -45,7 +48,10 @@ $stamp = Get-Date -Format "yyyyMMdd-HHmmss"
$resultDir = Join-Path $here "results-provider-credential-$stamp" $resultDir = Join-Path $here "results-provider-credential-$stamp"
New-Item -ItemType Directory -Force $resultDir | Out-Null New-Item -ItemType Directory -Force $resultDir | Out-Null
function Step([string]$message) { Write-Host "`n=== $message ===" -ForegroundColor Cyan } function Step([string]$message) {
$script:failureStage = $message
Write-Host "`n=== $message ===" -ForegroundColor Cyan
}
function Info([string]$message) { Write-Host " $message" } function Info([string]$message) { Write-Host " $message" }
function Ok([string]$message) { Write-Host "[OK] $message" -ForegroundColor Green } function Ok([string]$message) { Write-Host "[OK] $message" -ForegroundColor Green }
function Bad([string]$message) { Write-Host "[FAIL] $message" -ForegroundColor Red } function Bad([string]$message) { Write-Host "[FAIL] $message" -ForegroundColor Red }
@@ -59,25 +65,95 @@ function Resolve-Artifact([string]$explicit, [string]$leaf) {
function Escape-Toml([string]$value) { return $value.Replace('\', '\\') } function Escape-Toml([string]$value) { return $value.Replace('\', '\\') }
function Invoke-Cli([string[]]$CommandArgs, [switch]$AllowFailure) { function Test-NetworkPath([string]$value) {
$previous = $ErrorActionPreference if ($value.StartsWith('\\')) { return $true }
$ErrorActionPreference = "Continue" if ($value -notmatch '^([A-Za-z]):[\\/]') { return $false }
try {
$lines = & $cli @CommandArgs 2>&1 $drive = Get-PSDrive -Name $Matches[1] -PSProvider FileSystem -ErrorAction SilentlyContinue
$exitCode = $LASTEXITCODE return $null -ne $drive -and
} finally { -not [string]::IsNullOrWhiteSpace($drive.DisplayRoot) -and
$ErrorActionPreference = $previous $drive.DisplayRoot.StartsWith('\\')
} }
# Windows PowerShell wraps native stderr lines in ErrorRecord objects and
# Out-String adds a misleading "At ... NativeCommandError" block even when function Assert-LocalExecutable([string]$label, [string]$path) {
# the command succeeds. Preserve the message without that decoration. if (-not (Test-NetworkPath $path)) { return }
$text = (($lines | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) { throw "$label executable resolves to network path '$path'. Windows Application Control can block unsigned development binaries launched from network locations. Pass -GatewayPath and -CliPath pointing to local build outputs (for example, the repository's target\x86_64-pc-windows-msvc\release directory); the script and result artifacts may remain on the network share."
$_.Exception.Message }
} else {
$_.ToString() function Get-LaunchFailureMessage([string]$label, [string]$path, [System.Exception]$exception) {
$messages = New-Object System.Collections.Generic.List[string]
$currentException = $exception
while ($null -ne $currentException) {
if (-not [string]::IsNullOrWhiteSpace($currentException.Message)) {
[void]$messages.Add($currentException.Message)
} }
}) -join [Environment]::NewLine).Trim() $currentException = $currentException.InnerException
}
$message = ($messages -join ' | ')
if ($message -match '(?i)Application Control policy has blocked this file') {
$sha256 = try { (Get-FileHash -LiteralPath $path -Algorithm SHA256 -ErrorAction Stop).Hash } catch { "unavailable" }
$signature = try { (Get-AuthenticodeSignature -LiteralPath $path -ErrorAction Stop).Status } catch { "unavailable" }
return "Application Control blocked $label launch '$path' (SHA256=$sha256; Authenticode=$signature). Use a local, policy-approved binary and review the applicable App Control event log if the local launch is also blocked. Original error: $message"
}
return "failed to launch $label '$path': $message"
}
# Build one CreateProcess-compatible command-line argument. Windows PowerShell
# 5.1 removes embedded quotes from JSON passed to native commands through the
# call operator, which corrupts --driver-config-json before the CLI parses it.
function Quote-NativeArgument([string]$value) {
if ($value.Length -gt 0 -and $value -notmatch '[\s"]') { return $value }
$quoted = New-Object System.Text.StringBuilder
[void]$quoted.Append('"')
$backslashes = 0
foreach ($ch in $value.ToCharArray()) {
if ($ch -eq '\') {
$backslashes++
continue
}
if ($ch -eq '"') {
[void]$quoted.Append(('\' * (2 * $backslashes + 1)))
[void]$quoted.Append('"')
} else {
if ($backslashes -gt 0) { [void]$quoted.Append(('\' * $backslashes)) }
[void]$quoted.Append($ch)
}
$backslashes = 0
}
if ($backslashes -gt 0) { [void]$quoted.Append(('\' * (2 * $backslashes))) }
[void]$quoted.Append('"')
return $quoted.ToString()
}
function Invoke-Cli([string[]]$CommandArgs, [switch]$AllowFailure) {
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = $cli
$startInfo.Arguments = (($CommandArgs | ForEach-Object { Quote-NativeArgument $_ }) -join ' ')
$startInfo.UseShellExecute = $false
$startInfo.CreateNoWindow = $true
$startInfo.RedirectStandardOutput = $true
$startInfo.RedirectStandardError = $true
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $startInfo
try {
if (-not $process.Start()) { throw "failed to start $cli" }
} catch {
throw (Get-LaunchFailureMessage "CLI" $cli $_.Exception)
}
$stdout = $process.StandardOutput.ReadToEndAsync()
$stderr = $process.StandardError.ReadToEndAsync()
$process.WaitForExit()
$exitCode = $process.ExitCode
$text = (@($stdout.Result, $stderr.Result) | Where-Object {
-not [string]::IsNullOrWhiteSpace($_)
}) -join [Environment]::NewLine
$text = $text.Trim()
if (-not $AllowFailure -and $exitCode -ne 0) { if (-not $AllowFailure -and $exitCode -ne 0) {
throw "openshell $($CommandArgs -join ' ') failed (exit $exitCode): $text" throw "openshell $($CommandArgs -join ' ') failed (exit $exitCode): $text"
} }
@@ -133,6 +209,8 @@ $providerName = "mxc-github-e2e"
$passed = $false $passed = $false
$rawTokenLeak = $false $rawTokenLeak = $false
$artifactScanFailed = $false $artifactScanFailed = $false
$failureReason = ""
$failureStage = "initialization"
$githubToken = $env:GITHUB_TOKEN $githubToken = $env:GITHUB_TOKEN
try { try {
@@ -144,6 +222,8 @@ try {
if (-not (Test-Path $file)) { throw "missing artifact: $file" } if (-not (Test-Path $file)) { throw "missing artifact: $file" }
Info "found $file" Info "found $file"
} }
Assert-LocalExecutable "gateway" $gateway
Assert-LocalExecutable "CLI" $cli
if (Get-NetTCPConnection -State Listen -LocalPort $Port -ErrorAction SilentlyContinue) { if (Get-NetTCPConnection -State Listen -LocalPort $Port -ErrorAction SilentlyContinue) {
throw "gateway port $Port is already in use" throw "gateway port $Port is already in use"
} }
@@ -222,10 +302,14 @@ try {
# a successful test cannot be attributed to gateway environment inheritance. # a successful test cannot be attributed to gateway environment inheritance.
Remove-Item Env:GITHUB_TOKEN -ErrorAction SilentlyContinue Remove-Item Env:GITHUB_TOKEN -ErrorAction SilentlyContinue
try { try {
$gw = Start-Process -FilePath $gateway ` try {
-ArgumentList @("--disable-tls", "--db-url", "sqlite::memory:", "--log-level", "info") ` $gw = Start-Process -FilePath $gateway `
-WorkingDirectory $here -PassThru -NoNewWindow ` -ArgumentList @("--disable-tls", "--db-url", "sqlite::memory:", "--log-level", "info") `
-RedirectStandardOutput $gwLog -RedirectStandardError $gwErrLog -WorkingDirectory $here -PassThru -NoNewWindow `
-RedirectStandardOutput $gwLog -RedirectStandardError $gwErrLog
} catch {
throw (Get-LaunchFailureMessage "gateway" $gateway $_.Exception)
}
} finally { } finally {
$env:GITHUB_TOKEN = $githubToken $env:GITHUB_TOKEN = $githubToken
} }
@@ -242,7 +326,12 @@ try {
Step "Configure provider and effective policy" Step "Configure provider and effective policy"
$env:OPENSHELL_GATEWAY = "" $env:OPENSHELL_GATEWAY = ""
Invoke-Cli @("gateway", "add", "http://127.0.0.1:$Port", "--local", "--name", $GatewayName) | Out-Null $gatewayAdd = Invoke-Cli @(
"gateway", "add", "http://127.0.0.1:$Port", "--local", "--name", $GatewayName
) -AllowFailure
if ($gatewayAdd.ExitCode -ne 0 -and $gatewayAdd.Text -notmatch '(?i)already exists') {
throw "gateway registration failed (exit $($gatewayAdd.ExitCode)): $($gatewayAdd.Text)"
}
Invoke-Cli @("gateway", "select", $GatewayName) | Out-Null Invoke-Cli @("gateway", "select", $GatewayName) | Out-Null
Invoke-Cli @("provider", "profile", "lint", "--file", $profileUsed) | Out-Null Invoke-Cli @("provider", "profile", "lint", "--file", $profileUsed) | Out-Null
Invoke-Cli @("provider", "profile", "import", "--file", $profileUsed) | Out-Null Invoke-Cli @("provider", "profile", "import", "--file", $profileUsed) | Out-Null
@@ -294,7 +383,11 @@ try {
Ok "placeholder isolation, authorized rewrite, and endpoint mismatch all passed" Ok "placeholder isolation, authorized rewrite, and endpoint mismatch all passed"
} }
catch { catch {
Bad $_.Exception.Message $failureReason = ($_.Exception.Message -replace '\r?\n', ' | ').Trim()
if (-not [string]::IsNullOrWhiteSpace($githubToken)) {
$failureReason = $failureReason.Replace($githubToken, "***REDACTED***")
}
Bad $failureReason
} }
finally { finally {
if ($cli -and $sandboxName -and $gw -and -not $gw.HasExited) { if ($cli -and $sandboxName -and $gw -and -not $gw.HasExited) {
@@ -342,6 +435,8 @@ sandbox : $sandboxName
backend : process_container backend : process_container
provider : $providerName provider : $providerName
share_path : $ShareDir share_path : $ShareDir
stage : $failureStage
failure : $(if ([string]::IsNullOrWhiteSpace($failureReason)) { "none" } else { $failureReason })
PASS proves: PASS proves:
- MXC received a revision-scoped GITHUB_TOKEN placeholder, not the token. - MXC received a revision-scoped GITHUB_TOKEN placeholder, not the token.