From 83789ebd22b4969f8b1bffad86a6c8c7e71f0b03 Mon Sep 17 00:00:00 2001 From: zyang-dev <267119621+zyang-dev@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:03:43 -0700 Subject: [PATCH 1/4] fix(windows): redact private data from Windows bootstrap WSL logs Signed-off-by: zyang-dev <267119621+zyang-dev@users.noreply.github.com> --- scripts/bootstrap-windows.ps1 | 165 ++++++++++++++++++++++++++++++--- test/bootstrap-windows.test.ts | 124 +++++++++++++++++++++++-- 2 files changed, 268 insertions(+), 21 deletions(-) diff --git a/scripts/bootstrap-windows.ps1 b/scripts/bootstrap-windows.ps1 index 2efa429c268..e3a74a42256 100644 --- a/scripts/bootstrap-windows.ps1 +++ b/scripts/bootstrap-windows.ps1 @@ -751,7 +751,7 @@ function Test-WslRepairOutputReportsForbidden { return $Output -match 'Forbidden\s*\(403\)' } -function Test-WslRepairOutputRequiresReboot { +function Test-WslOutputRequiresReboot { param([AllowNull()] [string]$Output) if (-not $Output) { @@ -760,6 +760,12 @@ function Test-WslRepairOutputRequiresReboot { return $Output -match 'Changes will not be effective until the system is rebooted' } +function Test-WslRepairOutputRequiresReboot { + param([AllowNull()] [string]$Output) + + return Test-WslOutputRequiresReboot -Output $Output +} + function Write-WslRepairInstructions { param( [Parameter(Mandatory)] [int]$ExitCode, @@ -1113,33 +1119,157 @@ function Wait-WslDistroRegistrationOrInstallExit { } } -function Write-WslInstallLog { - param([AllowNull()] [string]$LogPath) +function Get-WslInstallLog { + param( + [AllowNull()] [string]$LogPath, + [switch]$SuppressWarnings + ) if ([string]::IsNullOrWhiteSpace($LogPath)) { - return + return $null } if (-not (Test-Path -LiteralPath $LogPath)) { - return + return $null } try { - $log = Get-Content -LiteralPath $LogPath -Raw + return Get-Content -LiteralPath $LogPath -Raw } catch { - Write-Status -Level WARN "Could not read WSL install log: $($_.Exception.Message)" - return + if (-not $SuppressWarnings) { + Write-Status -Level WARN "Could not read WSL install log: $($_.Exception.Message)" + } + return $null + } +} + +function Convert-WslInstallLogForDisplay { + param( + [AllowNull()] [string]$Log, + [AllowNull()] [string[]]$SensitivePaths = @() + ) + + if ([string]::IsNullOrWhiteSpace($Log)) { + return $Log + } + + $lines = (($Log -replace "`r`n", "`n") -replace "`r", "`n") -split "`n" + $separatorPattern = '^\*{6,}\s*$' + $firstSeparator = -1 + + for ($i = 0; $i -lt $lines.Count; $i++) { + if ($lines[$i] -match $separatorPattern) { + $firstSeparator = $i + break + } } + if ($firstSeparator -lt 0) { + return $Log + } + + $headerEnd = -1 + for ($i = $firstSeparator + 1; $i -lt $lines.Count; $i++) { + if ($lines[$i] -match $separatorPattern) { + $headerEnd = $i + break + } + } + + if ($headerEnd -lt 0) { + return '[PowerShell transcript metadata redacted.]' + } + + if (($headerEnd + 1) -lt $lines.Count) { + $bodyLines = @($lines[($headerEnd + 1)..($lines.Count - 1)]) + } else { + $bodyLines = @() + } + + $bodySeparators = @() + for ($i = 0; $i -lt $bodyLines.Count; $i++) { + if ($bodyLines[$i] -match $separatorPattern) { + $bodySeparators += $i + } + } + + $footerStart = -1 + if ($bodySeparators.Count -ge 2) { + $footerStart = $bodySeparators[$bodySeparators.Count - 2] + } elseif ($bodySeparators.Count -eq 1) { + $footerStart = $bodySeparators[0] + } + + if ($footerStart -ge 0) { + if ($footerStart -eq 0) { + $bodyLines = @() + } else { + $bodyLines = @($bodyLines[0..($footerStart - 1)]) + } + } + + $filteredBodyLines = @() + foreach ($line in $bodyLines) { + $containsSensitivePath = $false + foreach ($path in $SensitivePaths) { + if ( + -not [string]::IsNullOrWhiteSpace($path) -and + $line.IndexOf($path, [System.StringComparison]::OrdinalIgnoreCase) -ge 0 + ) { + $containsSensitivePath = $true + break + } + } + if (-not $containsSensitivePath) { + $filteredBodyLines += $line + } + } + $bodyLines = $filteredBodyLines + + $bodyStart = 0 + $bodyEnd = $bodyLines.Count - 1 + while ($bodyStart -le $bodyEnd -and [string]::IsNullOrWhiteSpace($bodyLines[$bodyStart])) { + $bodyStart++ + } + while ($bodyEnd -ge $bodyStart -and [string]::IsNullOrWhiteSpace($bodyLines[$bodyEnd])) { + $bodyEnd-- + } + if ($bodyStart -le $bodyEnd) { + $bodyLines = @($bodyLines[($bodyStart)..($bodyEnd)]) + } else { + $bodyLines = @() + } + + return (@('[PowerShell transcript metadata redacted.]') + $bodyLines) -join "`n" +} + +function Write-WslInstallLog { + param( + [AllowNull()] [string]$LogPath, + [AllowNull()] [string]$StatusPath + ) + + $log = Get-WslInstallLog -LogPath $LogPath if ([string]::IsNullOrWhiteSpace($log)) { return } + $displayLog = Convert-WslInstallLogForDisplay -Log $log -SensitivePaths @($StatusPath, $LogPath) + if ([string]::IsNullOrWhiteSpace($displayLog)) { + return + } Write-Host '' Write-Host 'WSL install output:' -ForegroundColor Yellow - Write-NativeOutput -Value $log + Write-NativeOutput -Value $displayLog Write-Host '' } +function Test-WslInstallLogRequiresReboot { + param([AllowNull()] [string]$LogPath) + + $log = Get-WslInstallLog -LogPath $LogPath -SuppressWarnings + return Test-WslOutputRequiresReboot -Output $log +} + function Remove-WslInstallArtifacts { param( [AllowNull()] [string]$StatusPath, @@ -1228,7 +1358,7 @@ function Ensure-UbuntuWsl { $registrationResult = Wait-WslDistroRegistrationOrInstallExit -Name $DistroName -StatusPath $installResult.StatusPath if ($null -ne $registrationResult.ExitCode -and $registrationResult.ExitCode -ne 0) { - Write-WslInstallLog -LogPath $installResult.LogPath + Write-WslInstallLog -LogPath $installResult.LogPath -StatusPath $installResult.StatusPath Remove-WslInstallArtifacts -StatusPath $installResult.StatusPath -LogPath $installResult.LogPath $installArtifactsRemoved = $true Write-WslUbuntuRequiredNotice -Name $DistroName @@ -1237,13 +1367,20 @@ function Ensure-UbuntuWsl { if (-not $registrationResult.Registered) { if ($null -ne $registrationResult.ExitCode) { - Write-WslInstallLog -LogPath $installResult.LogPath + $installRequiresReboot = Test-WslInstallLogRequiresReboot -LogPath $installResult.LogPath + Write-WslInstallLog -LogPath $installResult.LogPath -StatusPath $installResult.StatusPath Remove-WslInstallArtifacts -StatusPath $installResult.StatusPath -LogPath $installResult.LogPath $installArtifactsRemoved = $true Write-Status -Level WARN "$DistroName install command completed, but the distro is not registered yet." - Write-Status -Level WARN 'A reboot is required before WSL can finish registering the distro.' - Request-Reboot - return + if ($installRequiresReboot) { + Write-Status -Level WARN 'A reboot is required before WSL can finish registering the distro.' + Request-Reboot + return + } + + Write-Status -Level WARN 'The install output did not report that a reboot is required.' + Write-WslUbuntuRequiredNotice -Name $DistroName + throw "WSL distro '$DistroName' is still not registered after install." } Remove-WslInstallArtifacts -StatusPath $installResult.StatusPath -LogPath $installResult.LogPath diff --git a/test/bootstrap-windows.test.ts b/test/bootstrap-windows.test.ts index 0478d4a0efc..0a4f5c65385 100644 --- a/test/bootstrap-windows.test.ts +++ b/test/bootstrap-windows.test.ts @@ -570,23 +570,34 @@ function Get-WslDistros { return @() } function Start-WslInstallInPowerShellWindow { param([string]$Name) Set-Content -LiteralPath $script:statusPath -Value '0' - Set-Content -LiteralPath $script:logPath -Value @' + $log = @' ********************** Windows PowerShell transcript start -Username: NVIDIA.COM\\zyang +Username: EXAMPLE\\bootstrap-user +Benutzername: EXAMPLE\\bootstrap-user Machine: TEST-HOST (Microsoft Windows NT 10.0.28000.0) +Computer: TEST-HOST (Microsoft Windows NT 10.0.28000.0) Host Application: powershell.exe -Command $ErrorActionPreference = 'Continue' +$transcriptStarted = $false +try { Start-Transcript -Path $logPath -Force | Out-Null; $transcriptStarted = $true } catch { } $statusPath = 'status-file' & 'C:\\WINDOWS\\System32\\wsl.exe' --install -d 'Ubuntu-24.04' +try { [System.IO.File]::WriteAllText($statusPath, [string]$wslExitCode) } catch { } Process ID: 1234 PSVersion: 5.1.28000.1830 ********************** +'@ + $log += ('Transcript started, output file is {0}{1}' -f $script:logPath, [Environment]::NewLine) + $log += ('Status file is {0}{1}' -f $script:statusPath, [Environment]::NewLine) + $log += @' The requested operation is successful. Changes will not be effective until the system is rebooted. Ubuntu installer command exited. This window will close automatically. ********************** Windows PowerShell transcript end +End time: 20260628121936 ********************** '@ + Set-Content -LiteralPath $script:logPath -Value $log return [pscustomobject]@{ StatusPath = $script:statusPath; LogPath = $script:logPath; ProcessId = 1234 } } function Request-Reboot { @@ -619,11 +630,24 @@ try { expect(result.stdout).toContain( "Ubuntu installer command exited. This window will close automatically.", ); - expect(result.stdout).toContain("Windows PowerShell transcript"); - expect(result.stdout).toContain("Username:"); - expect(result.stdout).toContain("Machine:"); - expect(result.stdout).toContain("Host Application:"); - expect(result.stdout).toContain("PSVersion:"); + expect(result.stdout).toContain("[PowerShell transcript metadata redacted.]"); + expect(result.stdout).not.toContain("Windows PowerShell transcript"); + expect(result.stdout).not.toContain("Username:"); + expect(result.stdout).not.toContain("Machine:"); + expect(result.stdout).not.toContain("Host Application:"); + expect(result.stdout).not.toContain("PSVersion:"); + expect(result.stdout).not.toContain("Benutzername:"); + expect(result.stdout).not.toContain("Computer:"); + expect(result.stdout).not.toContain("EXAMPLE\\\\bootstrap-user"); + expect(result.stdout).not.toContain("TEST-HOST"); + expect(result.stdout).not.toContain("$statusPath = 'status-file'"); + expect(result.stdout).not.toContain("$transcriptStarted = $false"); + expect(result.stdout).not.toContain("Start-Transcript"); + expect(result.stdout).not.toContain("WriteAllText"); + expect(result.stdout).not.toContain("& 'C:\\\\WINDOWS\\\\System32\\\\wsl.exe' --install -d 'Ubuntu-24.04'"); + expect(result.stdout).not.toContain("Transcript started, output file is"); + expect(result.stdout).not.toContain("Status file is"); + expect(result.stdout).not.toContain("End time:"); expect(parsed.statusMessages).toContain( "WARN:Ubuntu-24.04 install command completed, but the distro is not registered yet.", ); @@ -634,6 +658,92 @@ try { expect(parsed.outcome).toContain("REBOOT_REQUESTED"); }); + itPowerShell("fails closed when a PowerShell transcript header is incomplete", () => { + const result = runPowerShellHarness(` +$ErrorActionPreference = 'Stop' +. ${JSON.stringify(BOOTSTRAP_WINDOWS)} + +$log = @' +********************** +Windows PowerShell transcript start +Username: EXAMPLE\\bootstrap-user +Machine: TEST-HOST +'@ + +Convert-WslInstallLogForDisplay -Log $log +`); + + expect(result.status).toBe(0); + expect(result.stderr).toBe(""); + expect(result.stdout).toContain("[PowerShell transcript metadata redacted.]"); + expect(result.stdout).not.toContain("EXAMPLE\\\\bootstrap-user"); + expect(result.stdout).not.toContain("TEST-HOST"); + }); + + itPowerShell("does not request reboot when Ubuntu install exits without registering the distro", () => { + const result = runPowerShellHarness(` +$ErrorActionPreference = 'Stop' +. ${JSON.stringify(BOOTSTRAP_WINDOWS)} + +$script:statusMessages = @() +$script:requestReboot = $false +$script:outcome = 'success' +$script:statusPath = Join-Path $env:TEMP ('ubuntu-install-' + [guid]::NewGuid().ToString('N') + '.status') +$script:logPath = Join-Path $env:TEMP ('ubuntu-install-' + [guid]::NewGuid().ToString('N') + '.log') + +function Resolve-WslExe { return 'wsl.exe' } +function Get-WslDistros { return @() } +function Start-WslInstallInPowerShellWindow { + param([string]$Name) + Set-Content -LiteralPath $script:statusPath -Value '0' + Set-Content -LiteralPath $script:logPath -Value @' +The operation completed successfully. +Ubuntu installer command exited. This window will close automatically. +'@ + return [pscustomobject]@{ StatusPath = $script:statusPath; LogPath = $script:logPath; ProcessId = 1234 } +} +function Request-Reboot { + $script:requestReboot = $true + throw 'UNEXPECTED_REBOOT' +} +function Write-Status { param([string]$Message, [string]$Level = 'INFO') $script:statusMessages += ('{0}:{1}' -f $Level, $Message) } + +try { + Ensure-UbuntuWsl +} catch { + $script:outcome = $_.Exception.Message +} + +[pscustomobject]@{ + statusMessages = $script:statusMessages + requestReboot = $script:requestReboot + outcome = $script:outcome + statusPathExists = Test-Path -LiteralPath $script:statusPath + logPathExists = Test-Path -LiteralPath $script:logPath +} | ConvertTo-Json -Compress +`); + + expect(result.status).toBe(0); + expect(result.stderr).toBe(""); + expect(result.stdout).toContain("WSL install output:"); + expect(result.stdout).toContain("The operation completed successfully."); + expect(result.stdout).toContain("Please run: wsl --install -d Ubuntu-24.04"); + const parsed = JSON.parse(result.stdout.trim().split(/\r?\n/).at(-1) ?? "{}"); + expect(parsed.statusMessages).toContain( + "WARN:Ubuntu-24.04 install command completed, but the distro is not registered yet.", + ); + expect(parsed.statusMessages).toContain( + "WARN:The install output did not report that a reboot is required.", + ); + expect(parsed.statusMessages).not.toContain( + "WARN:A reboot is required before WSL can finish registering the distro.", + ); + expect(parsed.requestReboot).toBe(false); + expect(parsed.outcome).toContain("WSL distro 'Ubuntu-24.04' is still not registered after install."); + expect(parsed.statusPathExists).toBe(false); + expect(parsed.logPathExists).toBe(false); + }); + itPowerShell("reports failed Ubuntu install output in the main window", () => { const result = runPowerShellHarness(` $ErrorActionPreference = 'Stop' From 65eec1322b6dfe7249430f9685bde15332fedf10 Mon Sep 17 00:00:00 2001 From: zyang-dev <267119621+zyang-dev@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:50:50 -0700 Subject: [PATCH 2/4] test(windows): preserve transcript fixture header boundary Signed-off-by: zyang-dev <267119621+zyang-dev@users.noreply.github.com> --- test/bootstrap-windows.test.ts | 54 +++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/test/bootstrap-windows.test.ts b/test/bootstrap-windows.test.ts index 0a4f5c65385..82dbd2f9a67 100644 --- a/test/bootstrap-windows.test.ts +++ b/test/bootstrap-windows.test.ts @@ -587,6 +587,7 @@ Process ID: 1234 PSVersion: 5.1.28000.1830 ********************** '@ + $log += [Environment]::NewLine $log += ('Transcript started, output file is {0}{1}' -f $script:logPath, [Environment]::NewLine) $log += ('Status file is {0}{1}' -f $script:statusPath, [Environment]::NewLine) $log += @' @@ -644,7 +645,9 @@ try { expect(result.stdout).not.toContain("$transcriptStarted = $false"); expect(result.stdout).not.toContain("Start-Transcript"); expect(result.stdout).not.toContain("WriteAllText"); - expect(result.stdout).not.toContain("& 'C:\\\\WINDOWS\\\\System32\\\\wsl.exe' --install -d 'Ubuntu-24.04'"); + expect(result.stdout).not.toContain( + "& 'C:\\\\WINDOWS\\\\System32\\\\wsl.exe' --install -d 'Ubuntu-24.04'", + ); expect(result.stdout).not.toContain("Transcript started, output file is"); expect(result.stdout).not.toContain("Status file is"); expect(result.stdout).not.toContain("End time:"); @@ -680,8 +683,10 @@ Convert-WslInstallLogForDisplay -Log $log expect(result.stdout).not.toContain("TEST-HOST"); }); - itPowerShell("does not request reboot when Ubuntu install exits without registering the distro", () => { - const result = runPowerShellHarness(` + itPowerShell( + "does not request reboot when Ubuntu install exits without registering the distro", + () => { + const result = runPowerShellHarness(` $ErrorActionPreference = 'Stop' . ${JSON.stringify(BOOTSTRAP_WINDOWS)} @@ -723,26 +728,29 @@ try { } | ConvertTo-Json -Compress `); - expect(result.status).toBe(0); - expect(result.stderr).toBe(""); - expect(result.stdout).toContain("WSL install output:"); - expect(result.stdout).toContain("The operation completed successfully."); - expect(result.stdout).toContain("Please run: wsl --install -d Ubuntu-24.04"); - const parsed = JSON.parse(result.stdout.trim().split(/\r?\n/).at(-1) ?? "{}"); - expect(parsed.statusMessages).toContain( - "WARN:Ubuntu-24.04 install command completed, but the distro is not registered yet.", - ); - expect(parsed.statusMessages).toContain( - "WARN:The install output did not report that a reboot is required.", - ); - expect(parsed.statusMessages).not.toContain( - "WARN:A reboot is required before WSL can finish registering the distro.", - ); - expect(parsed.requestReboot).toBe(false); - expect(parsed.outcome).toContain("WSL distro 'Ubuntu-24.04' is still not registered after install."); - expect(parsed.statusPathExists).toBe(false); - expect(parsed.logPathExists).toBe(false); - }); + expect(result.status).toBe(0); + expect(result.stderr).toBe(""); + expect(result.stdout).toContain("WSL install output:"); + expect(result.stdout).toContain("The operation completed successfully."); + expect(result.stdout).toContain("Please run: wsl --install -d Ubuntu-24.04"); + const parsed = JSON.parse(result.stdout.trim().split(/\r?\n/).at(-1) ?? "{}"); + expect(parsed.statusMessages).toContain( + "WARN:Ubuntu-24.04 install command completed, but the distro is not registered yet.", + ); + expect(parsed.statusMessages).toContain( + "WARN:The install output did not report that a reboot is required.", + ); + expect(parsed.statusMessages).not.toContain( + "WARN:A reboot is required before WSL can finish registering the distro.", + ); + expect(parsed.requestReboot).toBe(false); + expect(parsed.outcome).toContain( + "WSL distro 'Ubuntu-24.04' is still not registered after install.", + ); + expect(parsed.statusPathExists).toBe(false); + expect(parsed.logPathExists).toBe(false); + }, + ); itPowerShell("reports failed Ubuntu install output in the main window", () => { const result = runPowerShellHarness(` From 4e3badf38bf3561cabcde0ea87996975da5bf757 Mon Sep 17 00:00:00 2001 From: zyang-dev <267119621+zyang-dev@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:27:13 -0700 Subject: [PATCH 3/4] fix(windows): fail closed for malformed WSL transcripts Signed-off-by: zyang-dev <267119621+zyang-dev@users.noreply.github.com> --- scripts/bootstrap-windows.ps1 | 25 +++++++++++-- test/bootstrap-windows.test.ts | 67 ++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/scripts/bootstrap-windows.ps1 b/scripts/bootstrap-windows.ps1 index e3a74a42256..66a2f53b90e 100644 --- a/scripts/bootstrap-windows.ps1 +++ b/scripts/bootstrap-windows.ps1 @@ -1152,8 +1152,9 @@ function Convert-WslInstallLogForDisplay { return $Log } + $redactedMarker = '[PowerShell transcript metadata redacted.]' $lines = (($Log -replace "`r`n", "`n") -replace "`r", "`n") -split "`n" - $separatorPattern = '^\*{6,}\s*$' + $separatorPattern = '^[\s\uFEFF]*\*{6,}\s*$' $firstSeparator = -1 for ($i = 0; $i -lt $lines.Count; $i++) { @@ -1164,6 +1165,24 @@ function Convert-WslInstallLogForDisplay { } if ($firstSeparator -lt 0) { + $transcriptEvidencePatterns = @( + '(?im)^\s*(?:Windows\s+)?PowerShell transcript (?:start|end)\s*$', + '(?i)\b(?:Start|Stop)-Transcript\b', + '(?i)\$transcriptStarted\b' + ) + foreach ($pattern in $transcriptEvidencePatterns) { + if ($Log -match $pattern) { + return $redactedMarker + } + } + foreach ($path in $SensitivePaths) { + if ( + -not [string]::IsNullOrWhiteSpace($path) -and + $Log.IndexOf($path, [System.StringComparison]::OrdinalIgnoreCase) -ge 0 + ) { + return $redactedMarker + } + } return $Log } @@ -1176,7 +1195,7 @@ function Convert-WslInstallLogForDisplay { } if ($headerEnd -lt 0) { - return '[PowerShell transcript metadata redacted.]' + return $redactedMarker } if (($headerEnd + 1) -lt $lines.Count) { @@ -1239,7 +1258,7 @@ function Convert-WslInstallLogForDisplay { $bodyLines = @() } - return (@('[PowerShell transcript metadata redacted.]') + $bodyLines) -join "`n" + return (@($redactedMarker) + $bodyLines) -join "`n" } function Write-WslInstallLog { diff --git a/test/bootstrap-windows.test.ts b/test/bootstrap-windows.test.ts index 82dbd2f9a67..ce428253a4e 100644 --- a/test/bootstrap-windows.test.ts +++ b/test/bootstrap-windows.test.ts @@ -673,14 +673,81 @@ Username: EXAMPLE\\bootstrap-user Machine: TEST-HOST '@ +Convert-WslInstallLogForDisplay -Log $log +`); + + expect(result.status).toBe(0); + expect(result.stderr).toBe(""); + expect(result.stdout.trim()).toBe("[PowerShell transcript metadata redacted.]"); + expect(result.stdout).not.toContain("EXAMPLE\\\\bootstrap-user"); + expect(result.stdout).not.toContain("TEST-HOST"); + }); + + itPowerShell("redacts transcript markers when separators are missing", () => { + const result = runPowerShellHarness(` +$ErrorActionPreference = 'Stop' +. ${JSON.stringify(BOOTSTRAP_WINDOWS)} + +$log = @' +Windows PowerShell transcript start +Username: EXAMPLE\\bootstrap-user +Machine: TEST-HOST +Host Application: powershell.exe -Command Get-Date +Log file: C:\\Users\\example\\install.log +'@ + +Convert-WslInstallLogForDisplay -Log $log +`); + + expect(result.status).toBe(0); + expect(result.stderr).toBe(""); + expect(result.stdout.trim()).toBe("[PowerShell transcript metadata redacted.]"); + expect(result.stdout).not.toContain("EXAMPLE\\\\bootstrap-user"); + expect(result.stdout).not.toContain("TEST-HOST"); + expect(result.stdout).not.toContain("C:\\Users\\example\\install.log"); + }); + + itPowerShell("recognizes transcript separators with a BOM and indentation", () => { + const result = runPowerShellHarness(` +$ErrorActionPreference = 'Stop' +. ${JSON.stringify(BOOTSTRAP_WINDOWS)} + +$separator = ([char]0xFEFF) + ' **********************' +$log = @( + $separator, + 'Windows PowerShell transcript start', + 'Username: EXAMPLE\\bootstrap-user', + 'Machine: TEST-HOST', + ' **********************', + 'Useful WSL output', + ' **********************', + 'Windows PowerShell transcript end', + ' **********************' +) -join [Environment]::NewLine + Convert-WslInstallLogForDisplay -Log $log `); expect(result.status).toBe(0); expect(result.stderr).toBe(""); expect(result.stdout).toContain("[PowerShell transcript metadata redacted.]"); + expect(result.stdout).toContain("Useful WSL output"); expect(result.stdout).not.toContain("EXAMPLE\\\\bootstrap-user"); expect(result.stdout).not.toContain("TEST-HOST"); + expect(result.stdout).not.toContain("Windows PowerShell transcript"); + }); + + itPowerShell("preserves plain WSL output without transcript evidence", () => { + const result = runPowerShellHarness(` +$ErrorActionPreference = 'Stop' +. ${JSON.stringify(BOOTSTRAP_WINDOWS)} + +Convert-WslInstallLogForDisplay -Log 'Invalid distribution name: NotARealDistro' +`); + + expect(result.status).toBe(0); + expect(result.stderr).toBe(""); + expect(result.stdout.trim()).toBe("Invalid distribution name: NotARealDistro"); }); itPowerShell( From 3524227e8dbc69202d2c82d8a96a60f745006b86 Mon Sep 17 00:00:00 2001 From: zyang-dev <267119621+zyang-dev@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:46:23 -0700 Subject: [PATCH 4/4] refactor(windows): reuse shared WSL reboot output check Signed-off-by: zyang-dev <267119621+zyang-dev@users.noreply.github.com> --- scripts/bootstrap-windows.ps1 | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/scripts/bootstrap-windows.ps1 b/scripts/bootstrap-windows.ps1 index 66a2f53b90e..d65d42eb3a6 100644 --- a/scripts/bootstrap-windows.ps1 +++ b/scripts/bootstrap-windows.ps1 @@ -760,12 +760,6 @@ function Test-WslOutputRequiresReboot { return $Output -match 'Changes will not be effective until the system is rebooted' } -function Test-WslRepairOutputRequiresReboot { - param([AllowNull()] [string]$Output) - - return Test-WslOutputRequiresReboot -Output $Output -} - function Write-WslRepairInstructions { param( [Parameter(Mandatory)] [int]$ExitCode, @@ -826,7 +820,7 @@ function Invoke-WslNoDistributionInstallRepair { if ($repairResult.ExitCode -eq 0) { Write-Status 'WSL repair command completed successfully.' - if (Test-WslRepairOutputRequiresReboot -Output $repairResult.Output) { + if (Test-WslOutputRequiresReboot -Output $repairResult.Output) { Request-Reboot return }