Skip to content

Commit 1df7d21

Browse files
committed
v1.17.2 — Robustness sweep: 8 fixes across 10 modules
Process handle leak, CIM init fallback, TcpClient dispose, null Subject/Command/Description/cachePath guards, @() wrapper. 38968 lines | 63 modules | 1854 tests | PSSA 0
1 parent 418d68b commit 1df7d21

12 files changed

Lines changed: 49 additions & 15 deletions

Changelog.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## v1.17.2
4+
5+
- **Bug Fix:** Process handle leak in credential storage — `cmdkey.exe` process was never disposed after use; timeout path would also crash reading `ExitCode` on a still-running process. Now uses `try/finally` with `Dispose()` (35-Utilities).
6+
- **Bug Fix:** Script initialization falls back to registry when CIM service is unresponsive — unguarded `Get-CimInstance` at top level would crash the entire tool before any menu could display. Now falls back to `HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentBuildNumber` (00-Initialization).
7+
- **Bug Fix:** Port scan disposes `TcpClient` on error — if `BeginConnect` or `WaitOne` threw, the socket handle leaked. Now uses `try/finally` for cleanup (58-NetworkDiagnostics).
8+
- **Bug Fix:** Certificate display guards against null Subject — certificates with Subject Alternative Names only can have null `Subject`, causing blank output instead of "(no subject)" (37-HealthCheck, 35-Utilities).
9+
- **Bug Fix:** Command history display guards against null Command — corrupted or hand-edited `history.json` would crash `.PadRight()` on null (55-QoLFeatures).
10+
- **Bug Fix:** VHD download checks cache path before use — if host storage was never initialized, `.Substring()` on null path would crash (41-VHDManagement).
11+
- **Bug Fix:** Adapter table guards against null InterfaceDescription — virtual or transitional adapters can have null description, crashing `.PadRight()` (06-NetworkAdapters).
12+
- **Bug Fix:** Quick setup storage detection uses `@()` wrapper for PS 5.1 — single-item pipeline results lack `.Count` property without array wrapping (50-EntryPoint).
13+
- 63 modules, 1854 tests
14+
315
## v1.17.1
416

517
- **Bug Fix:** VM Checkpoint Management uses `*-VMSnapshot` cmdlets instead of `*-VMCheckpoint` — Server 2012 R2 only has the `VMSnapshot` variants; `VMCheckpoint` was introduced in Server 2016. Affects list, restore, and delete operations (52-VMCheckpoints).

Header.ps1

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,22 @@
3030
7h3 4b1d3r
3131
3232
.VERSION
33-
1.17.1
33+
1.17.2
3434
3535
.LAST UPDATED
3636
03/04/2026
3737
38+
.CHANGELOG v1.17.2
39+
ROBUSTNESS SWEEP — 8 FIXES:
40+
- FIX: Process handle leak in credential storage — cmdkey.exe process was never disposed; timeout path would also crash reading ExitCode on a still-running process (35-Utilities)
41+
- FIX: Script initialization falls back to registry when CIM service is unresponsive — unguarded Get-CimInstance at top level would crash the entire tool before any menu could display (00-Initialization)
42+
- FIX: Port scan disposes TcpClient on error — if BeginConnect or WaitOne threw, the socket handle leaked; now uses try/finally (58-NetworkDiagnostics)
43+
- FIX: Certificate display guards against null Subject — certs with Subject Alternative Names only have null Subject, causing blank output (37-HealthCheck, 35-Utilities)
44+
- FIX: Command history display guards against null Command — corrupted or hand-edited history JSON would crash PadRight on null (55-QoLFeatures)
45+
- FIX: VHD download checks cache path before use — if host storage was never initialized, Substring on null path would crash (41-VHDManagement)
46+
- FIX: Adapter table guards against null InterfaceDescription — virtual or transitional adapters can have null description, crashing PadRight (06-NetworkAdapters)
47+
- FIX: Quick setup storage detection uses @() wrapper for PS 5.1 — single-item pipeline results lack .Count property without array wrapping (50-EntryPoint)
48+
3849
.CHANGELOG v1.17.1
3950
PS 5.1 COMPATIBILITY FIX:
4051
- FIX: VM Checkpoint Management uses *-VMSnapshot cmdlets instead of *-VMCheckpoint — Server 2012 R2 only has the VMSnapshot variants; VMCheckpoint was added in Server 2016 (52-VMCheckpoints)

Modules/00-Initialization.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,13 @@ if (-not $script:ModuleRoot) { $script:ModuleRoot = $PSScriptRoot }
135135
if (-not $script:ModuleRoot -and $script:ScriptPath) {
136136
$script:ModuleRoot = [System.IO.Path]::GetDirectoryName($script:ScriptPath)
137137
}
138-
$script:ScriptVersion = "1.17.1"
138+
$script:ScriptVersion = "1.17.2"
139139
$script:ScriptStartTime = Get-Date
140140

141141
# OS version detection (for feature compatibility)
142142
# 2012/2012 R2 lack SET, Storage Replica, Defender PowerShell module
143143
# 2008 R2 SP1 supported with WMF 5.1 installed (run Install-Prerequisites.ps1)
144-
$script:OSBuildNumber = [int](Get-CimInstance Win32_OperatingSystem).BuildNumber
144+
$script:OSBuildNumber = try { [int](Get-CimInstance Win32_OperatingSystem -ErrorAction Stop).BuildNumber } catch { [int](Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').CurrentBuildNumber }
145145
$script:IsServer2008R2 = $script:OSBuildNumber -eq 7601 # 6.1.7601 (SP1)
146146
$script:IsServer2012 = $script:OSBuildNumber -eq 9200 # 6.2.9200
147147
$script:IsServer2012R2 = $script:OSBuildNumber -eq 9600 # 6.3.9600

Modules/06-NetworkAdapters.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ function Show-AdaptersTable {
299299
Write-OutputColor $separator -color "Info"
300300

301301
foreach ($adapter in $adapters) {
302-
$desc = $adapter.InterfaceDescription
302+
$desc = if ($adapter.InterfaceDescription) { $adapter.InterfaceDescription } else { "" }
303303
if ($desc.Length -gt $columnWidths.Description) {
304304
$desc = $desc.Substring(0, $columnWidths.Description - 3) + "..."
305305
}

Modules/35-Utilities.ps1

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -714,9 +714,15 @@ function Save-StoredCredential {
714714
$startInfo.RedirectStandardOutput = $true
715715
$startInfo.RedirectStandardError = $true
716716
$proc = [System.Diagnostics.Process]::Start($startInfo)
717-
$proc.WaitForExit(10000)
718-
$password = $null
719-
return ($proc.ExitCode -eq 0)
717+
try {
718+
$exited = $proc.WaitForExit(10000)
719+
$password = $null
720+
if (-not $exited) { return $false }
721+
return ($proc.ExitCode -eq 0)
722+
}
723+
finally {
724+
if ($proc) { $proc.Dispose() }
725+
}
720726
}
721727
catch {
722728
Write-OutputColor "Failed to save credential: $_" -color "Error"
@@ -1178,7 +1184,7 @@ function Show-CertificateExpiryCheck {
11781184
})
11791185
foreach ($cert in $certs) {
11801186
$daysLeft = [math]::Round(($cert.NotAfter - $now).TotalDays, 0)
1181-
$subject = $cert.Subject
1187+
$subject = if ($cert.Subject) { $cert.Subject } else { "(no subject)" }
11821188
if ($subject.Length -gt 40) { $subject = $subject.Substring(0, 37) + "..." }
11831189
$allCerts += [PSCustomObject]@{
11841190
Store = $store.Name

Modules/37-HealthCheck.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ function Show-SystemHealthCheck {
134134
Write-OutputColor " No certificates in LocalMachine\My store." -color "Info"
135135
} else {
136136
foreach ($cert in ($allCerts | Sort-Object NotAfter)) {
137-
$subject = $cert.Subject
137+
$subject = if ($cert.Subject) { $cert.Subject } else { "(no subject)" }
138138
if ($subject.Length -gt 40) { $subject = $subject.Substring(0, 37) + "..." }
139139
$daysLeft = [math]::Floor(($cert.NotAfter - $now).TotalDays)
140140
$expiryStr = "$($cert.NotAfter.ToString('yyyy-MM-dd')) (${daysLeft}d)"

Modules/41-VHDManagement.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ function Get-SyspreppedVHD {
155155
}
156156

157157
# Pre-check: verify destination has enough free space
158+
if (-not $cachePath) {
159+
Write-OutputColor " VHD cache path not configured. Run Host Storage Setup first." -color "Error"
160+
return $null
161+
}
158162
$destDriveLetter = $cachePath.Substring(0, 1)
159163
$destVolume = Get-Volume -DriveLetter $destDriveLetter -ErrorAction SilentlyContinue
160164
if ($destVolume) {

Modules/50-EntryPoint.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,7 @@ function Start-BatchMode {
993993
$storageAlready = $false
994994
if ($checkDrive) {
995995
$checkPaths = @("$($checkDrive):\Virtual Machines", "$($checkDrive):\ISOs", "$($checkDrive):\Virtual Machines\_BaseImages")
996-
$storageAlready = ($checkPaths | Where-Object { Test-Path $_ }).Count -eq 3
996+
$storageAlready = @($checkPaths | Where-Object { Test-Path $_ }).Count -eq 3
997997
}
998998
if ($storageAlready) {
999999
Write-OutputColor " [$stepNum/$totalSteps] Host storage: already initialized on $($checkDrive):" -color "Debug"

Modules/55-QoLFeatures.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,8 @@ function Show-CommandHistory {
286286
$recentHistory = $script:CommandHistory | Select-Object -Last 20
287287
$index = 1
288288
foreach ($cmd in $recentHistory) {
289-
$cmdStr = if ($cmd.Command.Length -gt 40) { $cmd.Command.Substring(0,37) + "..." } else { $cmd.Command.PadRight(40) }
289+
$cmdName = if ($cmd.Command) { $cmd.Command } else { "(unknown)" }
290+
$cmdStr = if ($cmdName.Length -gt 40) { $cmdName.Substring(0,37) + "..." } else { $cmdName.PadRight(40) }
290291
$timeStr = if ($cmd.Timestamp -and $cmd.Timestamp.Length -ge 16) { $cmd.Timestamp.Substring(5,11) } else { " " }
291292
Write-OutputColor " │ [$($index.ToString().PadLeft(2))] $cmdStr $timeStr" -color "Info"
292293
$index++

Modules/58-NetworkDiagnostics.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,19 +496,19 @@ function Invoke-QuickPortScan {
496496
foreach ($p in $ports) {
497497
$jobs.Add((Start-Job -ScriptBlock {
498498
param($IP, $Port)
499+
$tcp = $null
499500
try {
500501
$tcp = New-Object System.Net.Sockets.TcpClient
501502
$connect = $tcp.BeginConnect($IP, $Port, $null, $null)
502503
$wait = $connect.AsyncWaitHandle.WaitOne(2000, $false)
503504
if ($wait -and $tcp.Connected) {
504505
$tcp.EndConnect($connect)
505-
$tcp.Close()
506506
return "OPEN"
507507
}
508-
$tcp.Close()
509508
return "CLOSED"
510509
}
511510
catch { return "CLOSED" }
511+
finally { if ($tcp) { $tcp.Close() } }
512512
} -ArgumentList $target, $p.Port))
513513
}
514514

0 commit comments

Comments
 (0)