Files
comi-audit-secu/runner.ps1
T

104 lines
5.3 KiB
PowerShell

# Copyright (c) 2026 Comitari. Read-only wrapper for HardeningKitty.
# Run ONLY Invoke-HardeningKitty -Mode Audit. Never invoke HailMary, GPO or Config.
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string]$InstallRoot,
[Parameter(Mandatory=$true)][string]$CsvFile,
[Parameter(Mandatory=$true)][string]$MetadataFile,
[Parameter(Mandatory=$true)][ValidateSet('GPO','Intune')][string]$Source,
[Parameter(Mandatory=$true)][string]$Profile
)
$ErrorActionPreference = 'Stop'
function Get-MachineFindingList {
param([string]$ProfileName, [string]$AuditSource)
if ($ProfileName -ne 'auto') {
if ($ProfileName -eq 'community') {
return 'finding_list_0x6d69636b_machine.csv'
}
if ($ProfileName -match '_user\.csv$') {
throw 'A machine audit running as SYSTEM cannot assess an interactive user baseline.'
}
if ($ProfileName -notmatch '^finding_list_[A-Za-z0-9_.-]+\.csv$' -or $ProfileName.Contains('..')) {
throw 'Invalid finding list override. Only a basename starting finding_list_ is allowed.'
}
return $ProfileName
}
# Get-CimInstance is supported, unlike the deprecated wmic.exe command.
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$cv = Get-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$build = [int]$cv.CurrentBuildNumber
$display = if ($cv.PSObject.Properties['DisplayVersion']) { [string]$cv.DisplayVersion } else { '' }
$version = $display.ToLowerInvariant()
if ([int]$os.ProductType -eq 1) {
if ($build -ge 22000) {
if ($version -notin @('21h2','22h2','23h2','24h2','25h2')) {
throw "Unsupported Windows 11 DisplayVersion '$display' (build $build); set profile explicitly."
}
return ('finding_list_msft_security_baseline_windows_11_' + $version + '_machine.csv')
}
if ($version -eq '20h2' -or $version -eq '21h1') {
return 'finding_list_msft_security_baseline_windows_10_20h2_21h1_machine.csv'
}
if ($version -notin @('21h2','22h2')) {
throw "Unsupported Windows 10 DisplayVersion '$display' (build $build); set profile explicitly."
}
return ('finding_list_msft_security_baseline_windows_10_' + $version + '_machine.csv')
}
# Domain controllers and member servers use distinct security baselines.
$role = if ([int]$os.ProductType -eq 2) { 'dc' } else { 'member' }
switch ($build) {
9600 { return 'finding_list_cis_microsoft_windows_server_2012_r2_3.0.0_machine.csv' }
14393 { return 'finding_list_cis_microsoft_windows_server_2016_1607_3.0.0_machine.csv' }
17763 { return 'finding_list_cis_microsoft_windows_server_2019_1809_3.0.0_machine.csv' }
20348 { return ('finding_list_msft_security_baseline_windows_server_2022_21h2_' + $role + '_machine.csv') }
26100 { return ('finding_list_msft_security_baseline_windows_server_2025_24h2_' + $role + '_machine.csv') }
default { throw "Unsupported Windows Server build $build; set profile explicitly." }
}
}
try {
if (-not [Environment]::Is64BitProcess) { throw 'A 64-bit PowerShell process is required.' }
if (-not (Test-Path -LiteralPath $InstallRoot -PathType Container)) { throw 'Installed package data missing.' }
$listName = Get-MachineFindingList -ProfileName $Profile -AuditSource $Source
$listPath = Join-Path (Join-Path $InstallRoot 'lists') $listName
if (-not (Test-Path -LiteralPath $listPath -PathType Leaf)) {
throw "Required finding list not bundled: $listName"
}
$module = Join-Path $InstallRoot 'HardeningKitty.psm1'
if (-not (Test-Path -LiteralPath $module -PathType Leaf)) { throw 'HardeningKitty module not bundled.' }
$osInfo = Get-CimInstance -ClassName Win32_OperatingSystem
$cvInfo = Get-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$metaDisplay = if ($cvInfo.PSObject.Properties['DisplayVersion']) { [string]$cvInfo.DisplayVersion } else { '' }
$metadata = [ordered]@{
hostname = [Environment]::MachineName
os_caption = [string]$osInfo.Caption
product_type = [int]$osInfo.ProductType
build = [string]$cvInfo.CurrentBuildNumber
display_version = $metaDisplay
benchmark = $listName
source = $Source
execution_identity = [Security.Principal.WindowsIdentity]::GetCurrent().Name
}
$json = $metadata | ConvertTo-Json -Depth 5
[System.IO.File]::WriteAllText($MetadataFile, $json, [System.Text.UTF8Encoding]::new($false))
# HardeningKitty supports write modes, but this wrapper never exposes them.
Import-Module -Name $module -Force -ErrorAction Stop
# Upstream may emit non-terminating errors for unsupported individual tests.
# We detect skipped checks by comparing CSV rows to the input finding list.
$ErrorActionPreference = 'Continue'
Invoke-HardeningKitty -Mode Audit -Source $Source -FileFindingList $listPath -Report -ReportFile $CsvFile -SkipUserInformation -SkipMachineInformation | Out-Null
if ((-not (Test-Path -LiteralPath $CsvFile -PathType Leaf)) -or ((Get-Item -LiteralPath $CsvFile).Length -lt 40)) {
throw 'Audit did not produce a valid CSV report.'
}
exit 0
} catch {
[Console]::Error.WriteLine('HardeningKitty audit failed: ' + $_.Exception.Message)
exit 1
}