Private/Kinds/Security.ps1
|
# The Security Kind: what is scanning every file this machine touches. # # One antivirus product is how a machine is supposed to be. Two is a configuration nobody # chose deliberately - a vendor product installed without Defender being stood down - and # it is one of the few findings that explains a uniformly slow machine on its own, because # every file open is scanned twice by two products that also contend with each other. # Windows Security Center reports each product's state as a bitmask. The bit that matters # is whether on-access scanning is running: a product installed but switched off is not # scanning anything and must not count towards the total. # # A constant rather than a Check Definition parameter: it is a fact about the Windows API, # and no Customer has an opinion about it. $script:SecurityRealtimeScanningBit = 0x1000 function Get-SecurityData { [CmdletBinding()] [OutputType([psobject])] param([hashtable]$Parameters = @{}) $products = @() $available = $true try { $products = @(Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct -ErrorAction Stop | ForEach-Object { # The raw bitmask, undecoded. Which products count is the Judge's call. [pscustomobject]@{ DisplayName = "$($_.displayName)" ProductState = $_.productState } }) } catch { # Server SKUs and some hardened builds do not expose Security Center at all, which # is not the same fact as a machine with no antivirus on it. $available = $false } [pscustomobject]@{ PSTypeName = 'Gutcheck.Data.Security' SecurityCentrePresent = $available AntivirusProducts = $products } } function ConvertTo-SecurityFinding { [CmdletBinding()] [OutputType([psobject])] param( [AllowNull()]$Data, [hashtable]$Parameters = @{} ) New-AntivirusFinding -Data $Data -Parameters $Parameters } function ConvertTo-SecuritySection { [CmdletBinding()] [OutputType([psobject])] param([AllowNull()]$Data) $products = Get-DataCollection $Data 'AntivirusProducts' if (-not $products.Count) { return } New-Section -Title (Get-Text 'Title.Security.AntivirusProductsRegisteredWith') -Row @( $products | ForEach-Object { [pscustomobject]@{ Product = $_.DisplayName ProductState = $_.ProductState Scanning = (Test-AntivirusScanning -ProductState $_.ProductState) } } ) } function Test-AntivirusScanning { <# .SYNOPSIS Whether a Security Center product state says on-access scanning is running. .DESCRIPTION Read from the bitmask rather than from the display name, because the display name is a vendor's marketing string and says nothing about whether the product is on. #> [CmdletBinding()] [OutputType([bool])] param([AllowNull()]$ProductState) $state = ConvertTo-Number $ProductState if ($null -eq $state) { return $false } ([int]$state -band $script:SecurityRealtimeScanningBit) -ne 0 } function New-AntivirusFinding { [CmdletBinding()] param([AllowNull()]$Data, [hashtable]$Parameters) # More than one scanner is the problem, so the threshold is a count of active products. $warnAbove = Get-Parameter $Parameters 'ActiveAntivirusWarnAbove' 1 if (-not (Get-DataProperty $Data 'SecurityCentrePresent')) { return New-UnavailableFinding -Category Security -Check (Get-Text 'Check.Security.ActiveAntivirus') ` -Hint (Get-Text 'Hint.Security.WindowsSecurityCenterDidNot') } $active = @((Get-DataCollection $Data 'AntivirusProducts') | Where-Object { Test-AntivirusScanning -ProductState $_.ProductState }) if (-not $active.Count) { # The script said nothing here, which in a Report is indistinguishable from a # machine that was never asked. A Windows machine with nothing scanning it has # either had its protection turned off or had it fail. return New-Finding -Category Security -Check (Get-Text 'Check.Security.ActiveAntivirus') -Severity WARN -Value (Get-Text 'Value.Security.NoneActive') ` -Hint (Get-Text 'Hint.Security.NothingIsScanningThisMachine') } New-Finding -Category Security -Check (Get-Text 'Check.Security.ActiveAntivirus') ` -Severity (Get-Severity $active.Count $warnAbove ([double]::MaxValue)) ` -Value (($active | ForEach-Object { $_.DisplayName }) -join ', ') ` -Hint (Get-Text 'Hint.Security.MoreThanOneActiveAV') } |