Private/Collection.ps1
|
function New-HLCollectionContext { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Creates only an in-memory per-scan context and does not alter external state.')] [CmdletBinding()] param() return [pscustomobject][ordered]@{ StartedAt = (Get-Date).ToUniversalTime().ToString('o') Stopwatch = [Diagnostics.Stopwatch]::StartNew() ProviderCache = @{} ProviderMetrics = New-Object System.Collections.Generic.List[object] } } function Get-HLProviderSnapshot { [CmdletBinding()] param( [AllowNull()] [object]$CollectionContext, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(Mandatory)] [scriptblock]$Factory ) if ($null -eq $CollectionContext) { return & $Factory } if ($CollectionContext.ProviderCache.ContainsKey($Name)) { $metric = @($CollectionContext.ProviderMetrics | Where-Object { $_.Name -ceq $Name } | Select-Object -First 1) if ($metric.Count -gt 0) { $metric[0].CacheHits = [int]$metric[0].CacheHits + 1 } return $CollectionContext.ProviderCache[$Name] } $stopwatch = [Diagnostics.Stopwatch]::StartNew() try { $value = & $Factory $CollectionContext.ProviderCache[$Name] = $value $CollectionContext.ProviderMetrics.Add([pscustomobject][ordered]@{ Name = $Name DurationMs = [int][math]::Round($stopwatch.Elapsed.TotalMilliseconds) CacheHits = 0 Succeeded = $true }) return $value } catch { $CollectionContext.ProviderMetrics.Add([pscustomobject][ordered]@{ Name = $Name DurationMs = [int][math]::Round($stopwatch.Elapsed.TotalMilliseconds) CacheHits = 0 Succeeded = $false }) throw } finally { $stopwatch.Stop() } } function Get-HLContentDigest { [CmdletBinding()] param( [AllowNull()] [object]$InputObject ) $canonical = ConvertTo-HLCanonicalJson -Value $InputObject $algorithm = [Security.Cryptography.SHA256]::Create() try { $bytes = [Text.Encoding]::UTF8.GetBytes($canonical) return (($algorithm.ComputeHash($bytes) | ForEach-Object { $_.ToString('x2') }) -join '') } finally { $algorithm.Dispose() } } function Get-HLLogicalBaseline { [CmdletBinding()] param( [Parameter(Mandatory)] [object]$Baseline ) $properties = [ordered]@{} foreach ($name in @('schemaVersion', 'name', 'displayName', 'version', 'description', 'sourceBasis', 'supportedRoles', 'controls', 'notes')) { if (Test-HLProperty -InputObject $Baseline -Name $name) { $properties[$name] = $Baseline.$name } } return [pscustomobject]$properties } function Get-HLProbeRegistry { [CmdletBinding()] param() if ($null -ne $script:HLProbeRegistryCache) { return $script:HLProbeRegistryCache } $registry = @{ RegistryValue = [pscustomobject]@{ RequiredCommands = @() ParameterNames = @('expected', 'missingIsPass', 'missingIsPassOnBuildAtLeast', 'name', 'operator', 'path') Handler = { param($control, $systemContext, $context) Invoke-HLRegistryValueProbe -Control $control -SystemContext $systemContext -CollectionContext $context } } LocalGuestAccount = [pscustomobject]@{ RequiredCommands = @('Get-CimInstance') ParameterNames = @() Handler = { param($control, $systemContext, $context) Invoke-HLLocalGuestAccountProbe } } CredentialGuard = [pscustomobject]@{ RequiredCommands = @('Get-CimInstance') ParameterNames = @() Handler = { param($control, $systemContext, $context) Invoke-HLCredentialGuardProbe } } DeviceGuardService = [pscustomobject]@{ RequiredCommands = @('Get-CimInstance') ParameterNames = @('serviceId', 'serviceName') Handler = { param($control, $systemContext, $context) Invoke-HLDeviceGuardServiceProbe -Control $control -CollectionContext $context } } LapsBackup = [pscustomobject]@{ RequiredCommands = @() ParameterNames = @() Handler = { param($control, $systemContext, $context) Invoke-HLLapsBackupProbe -CollectionContext $context } } LapsPasswordAge = [pscustomobject]@{ RequiredCommands = @() ParameterNames = @('maximumDays') Handler = { param($control, $systemContext, $context) Invoke-HLLapsPasswordAgeProbe -Control $control -CollectionContext $context } } LapsAdEncryption = [pscustomobject]@{ RequiredCommands = @() ParameterNames = @() Handler = { param($control, $systemContext, $context) Invoke-HLLapsAdEncryptionProbe -CollectionContext $context } } LapsDsrmBackup = [pscustomobject]@{ RequiredCommands = @() ParameterNames = @() Handler = { param($control, $systemContext, $context) Invoke-HLLapsDsrmBackupProbe -SystemContext $systemContext -CollectionContext $context } } FirewallProfiles = [pscustomobject]@{ RequiredCommands = @('Get-NetFirewallProfile') ParameterNames = @('requireDefaultInboundBlock', 'requireEnabled') Handler = { param($control, $systemContext, $context) Invoke-HLFirewallProfilesProbe -Control $control -CollectionContext $context } } WindowsOptionalFeature = [pscustomobject]@{ RequiredCommands = @('Get-WindowsOptionalFeature') ParameterNames = @('evaluationMode', 'expectedState', 'features') Handler = { param($control, $systemContext, $context) Invoke-HLWindowsOptionalFeatureProbe -Control $control } } SmbServer = [pscustomobject]@{ RequiredCommands = @('Get-SmbServerConfiguration') ParameterNames = @('expected', 'property') Handler = { param($control, $systemContext, $context) Invoke-HLSmbConfigurationProbe -Control $control -Side Server -CollectionContext $context } } SmbClient = [pscustomobject]@{ RequiredCommands = @('Get-SmbClientConfiguration') ParameterNames = @('expected', 'property') Handler = { param($control, $systemContext, $context) Invoke-HLSmbConfigurationProbe -Control $control -Side Client -CollectionContext $context } } WinRM = [pscustomobject]@{ RequiredCommands = @() ParameterNames = @('expected', 'property', 'targets') Handler = { param($control, $systemContext, $context) Invoke-HLWinRMProbe -Control $control } } Service = [pscustomobject]@{ RequiredCommands = @('Get-CimInstance') ParameterNames = @('name', 'requireStopped', 'startupType') Handler = { param($control, $systemContext, $context) Invoke-HLServiceProbe -Control $control } } DefenderStatus = [pscustomobject]@{ RequiredCommands = @('Get-MpComputerStatus') ParameterNames = @('expected', 'operator', 'property') Handler = { param($control, $systemContext, $context) Invoke-HLDefenderStatusProbe -Control $control -CollectionContext $context } } DefenderPreference = [pscustomobject]@{ RequiredCommands = @('Get-MpPreference') ParameterNames = @('expected', 'operator', 'property', 'warningValues') Handler = { param($control, $systemContext, $context) Invoke-HLDefenderPreferenceProbe -Control $control -CollectionContext $context } } DefenderSignatureAge = [pscustomobject]@{ RequiredCommands = @('Get-MpComputerStatus') ParameterNames = @('maximumAgeDays') Handler = { param($control, $systemContext, $context) Invoke-HLDefenderSignatureAgeProbe -Control $control -CollectionContext $context } } AsrRules = [pscustomobject]@{ RequiredCommands = @('Get-MpPreference') ParameterNames = @('requiredRules') Handler = { param($control, $systemContext, $context) Invoke-HLAsrRulesProbe -Control $control -CollectionContext $context } } PowerShellModuleLogging = [pscustomobject]@{ RequiredCommands = @() ParameterNames = @() Handler = { param($control, $systemContext, $context) Invoke-HLPowerShellModuleLoggingProbe } } AuditPolicy = [pscustomobject]@{ RequiredCommands = @() ParameterNames = @('requiredFlags', 'subcategoryGuid', 'subcategoryName') Handler = { param($control, $systemContext, $context) Invoke-HLAuditPolicyProbe -Control $control } } EventLog = [pscustomobject]@{ RequiredCommands = @('Get-WinEvent') ParameterNames = @('logName', 'minimumSizeBytes', 'requireEnabled') Handler = { param($control, $systemContext, $context) Invoke-HLEventLogProbe -Control $control } } BitLocker = [pscustomobject]@{ RequiredCommands = @('Get-BitLockerVolume') ParameterNames = @('mountPoint') Handler = { param($control, $systemContext, $context) Invoke-HLBitLockerProbe -Control $control } } SecureBoot = [pscustomobject]@{ RequiredCommands = @('Confirm-SecureBootUEFI') ParameterNames = @() Handler = { param($control, $systemContext, $context) Invoke-HLSecureBootProbe } } AutoRun = [pscustomobject]@{ RequiredCommands = @() ParameterNames = @() Handler = { param($control, $systemContext, $context) Invoke-HLAutoRunProbe } } } $script:HLProbeRegistryCache = $registry return $registry } function Get-HLProbeCapability { [CmdletBinding()] param( [Parameter(Mandatory)] [object[]]$Controls ) $registry = Get-HLProbeRegistry $capabilities = New-Object System.Collections.Generic.List[object] $probeNames = @($Controls | ForEach-Object { [string]$_.probe } | Sort-Object -Unique) foreach ($probeName in $probeNames) { if (-not $registry.ContainsKey($probeName)) { $capabilities.Add([pscustomobject][ordered]@{ name = $probeName available = $false detail = 'Probe is not registered.' }) continue } $missing = @($registry[$probeName].RequiredCommands | Where-Object { $null -eq (Get-Command -Name $_ -ErrorAction SilentlyContinue) }) $capabilities.Add([pscustomobject][ordered]@{ name = $probeName available = [bool]($missing.Count -eq 0) detail = if ($missing.Count -eq 0) { $null } else { 'Missing command(s): {0}' -f ($missing -join ', ') } }) } return $capabilities.ToArray() } |