From d25fea82b1e798b22d12e3a08415fab5a89ffed2 Mon Sep 17 00:00:00 2001 From: PTah Date: Fri, 12 Jun 2026 20:32:38 +1000 Subject: [PATCH] =?UTF-8?q?fix:=20Export-RdpRepairDiagnosticReport=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=BC=D0=B0=D0=BB=D1=81=D1=8F=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BE=D0=B4=D0=BD=D0=BE=D0=BC=20cert=20(foreach=20PS=205.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit foreach по PSCustomObject перечисляет свойства, не cert — нужен @(). Рекурсивное flatten в Get-RdpRepairCertificateArray, самотест Export-паттерна. Co-authored-by: Cursor --- scripts/Invoke-RdpRepairSelfTest.ps1 | 16 ++++++++++++++++ scripts/lib/RdpRepair.Common.ps1 | 16 ++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/scripts/Invoke-RdpRepairSelfTest.ps1 b/scripts/Invoke-RdpRepairSelfTest.ps1 index 60961fd..5f3bda3 100644 --- a/scripts/Invoke-RdpRepairSelfTest.ps1 +++ b/scripts/Invoke-RdpRepairSelfTest.ps1 @@ -100,6 +100,22 @@ Assert-Test 'Get-RdpRepairCertificateArray flattens nested Object[]' { if ($first.Thumbprint -ne 'NESTED01') { throw "expected NESTED01 got $($first.Thumbprint)" } } +Assert-Test 'foreach must use @() wrapper on single cert (Export pattern)' { + $mock = [PSCustomObject]@{ + Thumbprint = 'LOOP01' + Subject = 'CN=loop' + NotAfter = (Get-Date).AddDays(1) + HasKey = $true + } + $lines = @() + foreach ($c in @(Get-RdpRepairCertificateArray @($mock))) { + $lines += $c.Subject + } + if ($lines.Count -ne 1 -or $lines[0] -ne 'CN=loop') { + throw "expected CN=loop got $($lines -join ',')" + } +} + Assert-Test 'Get-RdpRepairDiagnosticSnapshot object shape' { if (-not (Test-RdpRepairIsAdministrator)) { Write-Host ' (skip snapshot fields - not admin)' -ForegroundColor DarkYellow diff --git a/scripts/lib/RdpRepair.Common.ps1 b/scripts/lib/RdpRepair.Common.ps1 index 505e109..fc1899b 100644 --- a/scripts/lib/RdpRepair.Common.ps1 +++ b/scripts/lib/RdpRepair.Common.ps1 @@ -93,12 +93,16 @@ function Get-RdpRepairCertificateArray { param([object]$Certificates) if ($null -eq $Certificates) { return @() } $flat = New-Object System.Collections.Generic.List[object] - foreach ($item in @($Certificates)) { + $pending = New-Object System.Collections.Generic.Stack[object] + foreach ($entry in @($Certificates)) { $pending.Push($entry) } + while ($pending.Count -gt 0) { + $item = $pending.Pop() + if ($null -eq $item) { continue } if ($item -is [object[]]) { - foreach ($inner in @($item)) { - if ($null -ne $inner) { [void]$flat.Add($inner) } - } - } elseif ($null -ne $item) { + foreach ($inner in @($item)) { $pending.Push($inner) } + } elseif ($null -ne $item.PSObject.Properties['Thumbprint']) { + [void]$flat.Add($item) + } else { [void]$flat.Add($item) } } @@ -324,7 +328,7 @@ function Export-RdpRepairDiagnosticReport { $snap.PSObject.Properties | ForEach-Object { if ($_.Name -eq 'Certificates') { $lines += 'Certificates:' - foreach ($c in (Get-RdpRepairCertificateArray $_.Value)) { + foreach ($c in @(Get-RdpRepairCertificateArray $_.Value)) { $lines += (' - {0}; Thumb={1}; NotAfter={2}; Key={3}' -f $c.Subject, $c.Thumbprint, $c.NotAfter, $c.HasKey) } } else {