Skip to content

Commit d8ff489

Browse files
committed
v1.45.0 — DiskAudit + TLSAudit CLI actions
New CLI actions: - DiskAudit: physical disk health + volume utilization, flags low space and degraded disks - TLSAudit: TLS/SSL protocol config audit, flags insecure protocols, checks .NET strong crypto 65 modules, 56507 lines, 3285 tests, 0 PSSA errors
1 parent cd52833 commit d8ff489

8 files changed

Lines changed: 296 additions & 7 deletions

File tree

Changelog.md

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

3+
## v1.45.0
4+
5+
- **New Feature:** DiskAudit CLI action — `RackStack.exe -Action DiskAudit -OutputFormat JSON` audits physical disk health and volume utilization. Reports disk health status, operational status, media type, and size. Flags volumes with less than 10% free space as warnings and less than 5% as critical. Exits code 1 when disk issues detected (50-EntryPoint).
6+
- **New Feature:** TLSAudit CLI action — `RackStack.exe -Action TLSAudit -OutputFormat JSON` audits TLS/SSL protocol configuration via SCHANNEL registry. Checks SSL 2.0/3.0 and TLS 1.0/1.1/1.2/1.3 server and client status. Flags insecure protocols (SSL/TLS 1.0/1.1) enabled as warnings, disabled TLS 1.2/1.3 as critical. Also checks .NET Framework strong crypto settings. Exits code 1 when TLS issues detected (50-EntryPoint).
7+
- 65 modules, 3285 tests
8+
39
## v1.44.0
410

511
- **New Feature:** FirewallAudit CLI action — `RackStack.exe -Action FirewallAudit -OutputFormat JSON` audits Windows Firewall configuration: profile status (Domain/Private/Public), rule counts by direction and action, top inbound allow groups. Flags disabled profiles and permissive Public profile settings. Exits code 1 when firewall issues detected (50-EntryPoint).

Header.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
7h3 4b1d3r
3131
3232
.VERSION
33-
1.44.0
33+
1.45.0
3434
3535
.LAST UPDATED
3636
03/13/2026
@@ -1390,7 +1390,7 @@
13901390
param(
13911391
# CLI headless mode: run a specific action without interactive menus
13921392
# Usage: RackStack.exe -Action Cleanup [-Tier Standard] [-Silent] [-OutputFormat JSON]
1393-
[ValidateSet('Cleanup', 'Debloat', 'HealthCheck', 'Batch', 'QuickScan', 'Inventory', 'DriftCheck', 'Snapshot', 'Compliance', 'Harden', 'Remediate', 'Aggregate', 'Compare', 'Export', 'Trend', 'CertCheck', 'ReportHTML', 'ListeningPorts', 'SoftwareList', 'Uptime', 'ServiceAudit', 'EventAudit', 'NetInfo', 'ScheduledExport', 'ValidateConfig', 'Watch', 'Query', 'Diff', 'Baseline', 'Alert', 'FleetScan', 'PatchStatus', 'UserAudit', 'FirewallAudit', 'TaskAudit')]
1393+
[ValidateSet('Cleanup', 'Debloat', 'HealthCheck', 'Batch', 'QuickScan', 'Inventory', 'DriftCheck', 'Snapshot', 'Compliance', 'Harden', 'Remediate', 'Aggregate', 'Compare', 'Export', 'Trend', 'CertCheck', 'ReportHTML', 'ListeningPorts', 'SoftwareList', 'Uptime', 'ServiceAudit', 'EventAudit', 'NetInfo', 'ScheduledExport', 'ValidateConfig', 'Watch', 'Query', 'Diff', 'Baseline', 'Alert', 'FleetScan', 'PatchStatus', 'UserAudit', 'FirewallAudit', 'TaskAudit', 'DiskAudit', 'TLSAudit')]
13941394
[string]$Action,
13951395

13961396
[ValidateSet('Light', 'Standard', 'Aggressive')]

Install-RackStack.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
#>
3737

3838
param(
39-
[ValidateSet('Cleanup', 'Debloat', 'HealthCheck', 'Batch', 'QuickScan', 'Inventory', 'DriftCheck', 'Snapshot', 'Compliance', 'Harden', 'Remediate', 'Aggregate', 'Compare', 'Export', 'Trend', 'CertCheck', 'ReportHTML', 'ListeningPorts', 'SoftwareList', 'Uptime', 'ServiceAudit', 'EventAudit', 'NetInfo', 'ScheduledExport', 'ValidateConfig', 'Watch', 'Query', 'Diff', 'Baseline', 'Alert', 'FleetScan', 'PatchStatus', 'UserAudit', 'FirewallAudit', 'TaskAudit')]
39+
[ValidateSet('Cleanup', 'Debloat', 'HealthCheck', 'Batch', 'QuickScan', 'Inventory', 'DriftCheck', 'Snapshot', 'Compliance', 'Harden', 'Remediate', 'Aggregate', 'Compare', 'Export', 'Trend', 'CertCheck', 'ReportHTML', 'ListeningPorts', 'SoftwareList', 'Uptime', 'ServiceAudit', 'EventAudit', 'NetInfo', 'ScheduledExport', 'ValidateConfig', 'Watch', 'Query', 'Diff', 'Baseline', 'Alert', 'FleetScan', 'PatchStatus', 'UserAudit', 'FirewallAudit', 'TaskAudit', 'DiskAudit', 'TLSAudit')]
4040
[string]$Action = 'QuickScan',
4141

4242
[ValidateSet('Light', 'Standard', 'Aggressive')]

Modules/00-Initialization.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ if (-not $PSCommandPath -and $script:ScriptPath) {
160160
if (-not $script:ModuleRoot -and $script:ScriptPath) {
161161
$script:ModuleRoot = [System.IO.Path]::GetDirectoryName($script:ScriptPath)
162162
}
163-
$script:ScriptVersion = "1.44.0"
163+
$script:ScriptVersion = "1.45.0"
164164
$script:ScriptStartTime = Get-Date
165165

166166
# CLI headless mode parameters (populated from param block in monolithic/exe)

Modules/50-EntryPoint.ps1

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2968,6 +2968,253 @@ function Invoke-CLIAction {
29682968

29692969
if ($failedCount -gt 0) { [Environment]::Exit(1) }
29702970
}
2971+
'DiskAudit' {
2972+
Write-OutputColor " Auditing disk health and utilization..." -color "Info"
2973+
Write-OutputColor "" -color "Info"
2974+
2975+
$diskIssues = 0
2976+
$volumes = @()
2977+
$physicalDisks = @()
2978+
2979+
# Physical disk health
2980+
try {
2981+
$pDisks = @(Get-PhysicalDisk -ErrorAction Stop)
2982+
foreach ($d in $pDisks) {
2983+
$health = 'OK'
2984+
if ($d.HealthStatus -ne 'Healthy') { $health = 'FAIL'; $diskIssues++ }
2985+
if ($d.OperationalStatus -ne 'OK') { $health = 'FAIL'; if ($d.HealthStatus -eq 'Healthy') { $diskIssues++ } }
2986+
$sizeGB = [math]::Round($d.Size / 1GB, 1)
2987+
$physicalDisks += @{
2988+
DeviceId = $d.DeviceId
2989+
FriendlyName = $d.FriendlyName
2990+
MediaType = "$($d.MediaType)"
2991+
BusType = "$($d.BusType)"
2992+
SizeGB = $sizeGB
2993+
HealthStatus = "$($d.HealthStatus)"
2994+
OperationalStatus = "$($d.OperationalStatus)"
2995+
Health = $health
2996+
}
2997+
}
2998+
} catch {
2999+
Write-OutputColor " WARNING: Could not query physical disks: $($_.Exception.Message)" -color "Warning"
3000+
}
3001+
3002+
# Volume utilization
3003+
try {
3004+
$vols = @(Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" -ErrorAction Stop)
3005+
foreach ($v in $vols) {
3006+
$totalGB = [math]::Round($v.Size / 1GB, 1)
3007+
$freeGB = [math]::Round($v.FreeSpace / 1GB, 1)
3008+
$usedGB = [math]::Round(($v.Size - $v.FreeSpace) / 1GB, 1)
3009+
$pctFree = if ($v.Size -gt 0) { [math]::Round(($v.FreeSpace / $v.Size) * 100, 1) } else { 0 }
3010+
$status = 'OK'
3011+
if ($pctFree -lt 10) { $status = 'WARN'; $diskIssues++ }
3012+
if ($pctFree -lt 5) { $status = 'CRITICAL' }
3013+
$volumes += @{
3014+
Drive = $v.DeviceID
3015+
Label = $v.VolumeName
3016+
TotalGB = $totalGB
3017+
UsedGB = $usedGB
3018+
FreeGB = $freeGB
3019+
PctFree = $pctFree
3020+
Status = $status
3021+
}
3022+
}
3023+
} catch {
3024+
Write-OutputColor " ERROR: Failed to query volumes: $($_.Exception.Message)" -color "Error"
3025+
[Environment]::Exit(1)
3026+
}
3027+
3028+
# Console output
3029+
Write-OutputColor " ┌────────────────────────────────────────────────────────────────────────┐" -color "Info"
3030+
Write-OutputColor "$(" DISK AUDIT - $env:COMPUTERNAME".PadRight(72))" -color "Info"
3031+
Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" -color "Info"
3032+
if (@($physicalDisks).Count -gt 0) {
3033+
Write-OutputColor "$(" PHYSICAL DISKS".PadRight(72))" -color "Info"
3034+
foreach ($d in $physicalDisks) {
3035+
$line = " $($d.FriendlyName)"
3036+
if ($line.Length -gt 35) { $line = $line.Substring(0, 32) + "..." }
3037+
$line = "$($line.PadRight(36)) $($d.SizeGB)GB $($d.MediaType) [$($d.Health)]"
3038+
if ($line.Length -gt 72) { $line = $line.Substring(0, 69) + "..." }
3039+
$color = if ($d.Health -eq 'OK') { 'Success' } else { 'Error' }
3040+
Write-OutputColor "$($line.PadRight(72))" -color $color
3041+
}
3042+
Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" -color "Info"
3043+
}
3044+
Write-OutputColor "$(" VOLUMES".PadRight(72))" -color "Info"
3045+
foreach ($v in $volumes) {
3046+
$label = if ($v.Label) { " ($($v.Label))" } else { "" }
3047+
$line = " $($v.Drive)$label"
3048+
if ($line.Length -gt 20) { $line = $line.Substring(0, 17) + "..." }
3049+
$line = "$($line.PadRight(22)) $($v.UsedGB)/$($v.TotalGB)GB Free: $($v.PctFree)% [$($v.Status)]"
3050+
if ($line.Length -gt 72) { $line = $line.Substring(0, 69) + "..." }
3051+
$color = switch ($v.Status) { 'OK' { 'Success' } 'WARN' { 'Warning' } 'CRITICAL' { 'Error' } default { 'Info' } }
3052+
Write-OutputColor "$($line.PadRight(72))" -color $color
3053+
}
3054+
Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" -color "Info"
3055+
$summaryLine = " Disks: $(@($physicalDisks).Count) Volumes: $(@($volumes).Count) Issues: $diskIssues"
3056+
Write-OutputColor "$($summaryLine.PadRight(72))" -color $(if ($diskIssues -gt 0) { "Warning" } else { "Success" })
3057+
Write-OutputColor " └────────────────────────────────────────────────────────────────────────┘" -color "Info"
3058+
3059+
if ($script:CLIOutputFormat -eq 'JSON') {
3060+
$jsonResult = @{
3061+
Tool = $script:ToolFullName
3062+
Version = $script:ScriptVersion
3063+
Action = 'DiskAudit'
3064+
Timestamp = (Get-Date -Format "yyyy-MM-ddTHH:mm:ss")
3065+
Hostname = $env:COMPUTERNAME
3066+
Summary = @{
3067+
PhysicalDisks = @($physicalDisks).Count
3068+
Volumes = @($volumes).Count
3069+
Issues = $diskIssues
3070+
}
3071+
PhysicalDisks = $physicalDisks
3072+
Volumes = $volumes
3073+
}
3074+
Write-Output ($jsonResult | ConvertTo-Json -Depth 10)
3075+
}
3076+
3077+
if ($diskIssues -gt 0) { [Environment]::Exit(1) }
3078+
}
3079+
'TLSAudit' {
3080+
Write-OutputColor " Auditing TLS/SSL configuration..." -color "Info"
3081+
Write-OutputColor "" -color "Info"
3082+
3083+
$tlsIssues = 0
3084+
$protocols = @()
3085+
$regBase = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols'
3086+
3087+
$protocolList = @(
3088+
@{ Name = 'SSL 2.0'; Secure = $false }
3089+
@{ Name = 'SSL 3.0'; Secure = $false }
3090+
@{ Name = 'TLS 1.0'; Secure = $false }
3091+
@{ Name = 'TLS 1.1'; Secure = $false }
3092+
@{ Name = 'TLS 1.2'; Secure = $true }
3093+
@{ Name = 'TLS 1.3'; Secure = $true }
3094+
)
3095+
3096+
foreach ($proto in $protocolList) {
3097+
$name = $proto.Name
3098+
$serverPath = "$regBase\$name\Server"
3099+
$clientPath = "$regBase\$name\Client"
3100+
3101+
$serverEnabled = $null
3102+
$clientEnabled = $null
3103+
3104+
# Check Server subkey
3105+
if (Test-Path $serverPath) {
3106+
$serverReg = Get-ItemProperty -Path $serverPath -ErrorAction SilentlyContinue
3107+
if ($null -ne $serverReg -and $null -ne $serverReg.Enabled) {
3108+
$serverEnabled = $serverReg.Enabled -ne 0
3109+
} elseif ($null -ne $serverReg -and $null -ne $serverReg.DisabledByDefault) {
3110+
$serverEnabled = $serverReg.DisabledByDefault -eq 0
3111+
}
3112+
}
3113+
3114+
# Check Client subkey
3115+
if (Test-Path $clientPath) {
3116+
$clientReg = Get-ItemProperty -Path $clientPath -ErrorAction SilentlyContinue
3117+
if ($null -ne $clientReg -and $null -ne $clientReg.Enabled) {
3118+
$clientEnabled = $clientReg.Enabled -ne 0
3119+
} elseif ($null -ne $clientReg -and $null -ne $clientReg.DisabledByDefault) {
3120+
$clientEnabled = $clientReg.DisabledByDefault -eq 0
3121+
}
3122+
}
3123+
3124+
# Determine effective status
3125+
# If registry keys don't exist, Windows uses defaults:
3126+
# SSL 2.0/3.0: disabled by default on modern Windows
3127+
# TLS 1.0/1.1: enabled by default (but deprecated)
3128+
# TLS 1.2/1.3: enabled by default
3129+
$effectiveServer = if ($null -ne $serverEnabled) { $serverEnabled } else {
3130+
# Default behavior for unset protocols
3131+
switch ($name) {
3132+
'SSL 2.0' { $false }
3133+
'SSL 3.0' { $false }
3134+
default { $true }
3135+
}
3136+
}
3137+
$effectiveClient = if ($null -ne $clientEnabled) { $clientEnabled } else {
3138+
switch ($name) {
3139+
'SSL 2.0' { $false }
3140+
'SSL 3.0' { $false }
3141+
default { $true }
3142+
}
3143+
}
3144+
3145+
$status = 'OK'
3146+
$configured = $null -ne $serverEnabled -or $null -ne $clientEnabled
3147+
3148+
if (-not $proto.Secure -and ($effectiveServer -or $effectiveClient)) {
3149+
$status = if ($name -match 'SSL') { 'CRITICAL' } else { 'WARN' }
3150+
$tlsIssues++
3151+
}
3152+
if ($proto.Secure -and -not $effectiveServer -and -not $effectiveClient) {
3153+
$status = 'CRITICAL'; $tlsIssues++
3154+
}
3155+
3156+
$protocols += @{
3157+
Protocol = $name
3158+
ServerEnabled = $effectiveServer
3159+
ClientEnabled = $effectiveClient
3160+
Configured = $configured
3161+
Secure = $proto.Secure
3162+
Status = $status
3163+
}
3164+
}
3165+
3166+
# Check .NET strong crypto settings
3167+
$netFx64 = 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319'
3168+
$netFx32 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319'
3169+
$strongCrypto64 = $false
3170+
$strongCrypto32 = $false
3171+
try {
3172+
$reg64 = Get-ItemProperty -Path $netFx64 -ErrorAction SilentlyContinue
3173+
if ($null -ne $reg64 -and $null -ne $reg64.SchUseStrongCrypto) { $strongCrypto64 = $reg64.SchUseStrongCrypto -eq 1 }
3174+
$reg32 = Get-ItemProperty -Path $netFx32 -ErrorAction SilentlyContinue
3175+
if ($null -ne $reg32 -and $null -ne $reg32.SchUseStrongCrypto) { $strongCrypto32 = $reg32.SchUseStrongCrypto -eq 1 }
3176+
} catch { }
3177+
3178+
# Console output
3179+
Write-OutputColor " ┌────────────────────────────────────────────────────────────────────────┐" -color "Info"
3180+
Write-OutputColor "$(" TLS AUDIT - $env:COMPUTERNAME".PadRight(72))" -color "Info"
3181+
Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" -color "Info"
3182+
foreach ($p in $protocols) {
3183+
$serverStr = if ($p.ServerEnabled) { "On" } else { "Off" }
3184+
$clientStr = if ($p.ClientEnabled) { "On" } else { "Off" }
3185+
$cfgStr = if ($p.Configured) { "Explicit" } else { "Default" }
3186+
$line = " $($p.Protocol.PadRight(10)) Server: $($serverStr.PadRight(5)) Client: $($clientStr.PadRight(5)) ($cfgStr) [$($p.Status)]"
3187+
if ($line.Length -gt 72) { $line = $line.Substring(0, 69) + "..." }
3188+
$color = switch ($p.Status) { 'OK' { 'Success' } 'WARN' { 'Warning' } 'CRITICAL' { 'Error' } default { 'Info' } }
3189+
Write-OutputColor "$($line.PadRight(72))" -color $color
3190+
}
3191+
Write-OutputColor " ├────────────────────────────────────────────────────────────────────────┤" -color "Info"
3192+
$scLine = " .NET Strong Crypto: x64=$(if ($strongCrypto64) { 'Yes' } else { 'No' }) x86=$(if ($strongCrypto32) { 'Yes' } else { 'No' })"
3193+
$scColor = if ($strongCrypto64 -and $strongCrypto32) { "Success" } else { "Warning" }
3194+
Write-OutputColor "$($scLine.PadRight(72))" -color $scColor
3195+
$summaryLine = " Issues: $tlsIssues"
3196+
Write-OutputColor "$($summaryLine.PadRight(72))" -color $(if ($tlsIssues -gt 0) { "Warning" } else { "Success" })
3197+
Write-OutputColor " └────────────────────────────────────────────────────────────────────────┘" -color "Info"
3198+
3199+
if ($script:CLIOutputFormat -eq 'JSON') {
3200+
$jsonResult = @{
3201+
Tool = $script:ToolFullName
3202+
Version = $script:ScriptVersion
3203+
Action = 'TLSAudit'
3204+
Timestamp = (Get-Date -Format "yyyy-MM-ddTHH:mm:ss")
3205+
Hostname = $env:COMPUTERNAME
3206+
Summary = @{
3207+
Issues = $tlsIssues
3208+
StrongCrypto64 = $strongCrypto64
3209+
StrongCrypto32 = $strongCrypto32
3210+
}
3211+
Protocols = $protocols
3212+
}
3213+
Write-Output ($jsonResult | ConvertTo-Json -Depth 10)
3214+
}
3215+
3216+
if ($tlsIssues -gt 0) { [Environment]::Exit(1) }
3217+
}
29713218
default {
29723219
Write-OutputColor " Unknown CLI action: $($script:CLIAction)" -color "Error"
29733220
[Environment]::Exit(1)

0 commit comments

Comments
 (0)