Private/Validation.ps1
|
function Test-HLScanValidationProperty { [CmdletBinding()] param( [AllowNull()] [object]$InputObject, [Parameter(Mandatory)] [string]$Name ) if ($null -eq $InputObject) { return $false } if ($InputObject -is [System.Collections.IDictionary]) { return $InputObject.Contains($Name) } return $null -ne $InputObject.PSObject.Properties[$Name] } function Get-HLScanValidationProperty { [CmdletBinding()] param( [Parameter(Mandatory)] [object]$InputObject, [Parameter(Mandatory)] [string]$Name ) if ($InputObject -is [System.Collections.IDictionary]) { $value = $InputObject[$Name] } else { $value = $InputObject.PSObject.Properties[$Name].Value } return ,$value } function Test-HLScanValidationObject { [CmdletBinding()] param([AllowNull()][object]$Value) return $null -ne $Value -and ( $Value -is [pscustomobject] -or $Value -is [System.Collections.IDictionary] ) } function Test-HLScanValidationArray { [CmdletBinding()] param([AllowNull()][object]$Value) return $null -ne $Value -and $Value -isnot [string] -and $Value -isnot [System.Collections.IDictionary] -and $Value -is [System.Collections.IEnumerable] } function Test-HLScanValidationInteger { [CmdletBinding()] param([AllowNull()][object]$Value) return $Value -is [byte] -or $Value -is [sbyte] -or $Value -is [int16] -or $Value -is [uint16] -or $Value -is [int32] -or $Value -is [uint32] -or $Value -is [int64] -or $Value -is [uint64] } function Test-HLScanValidationNumber { [CmdletBinding()] param([AllowNull()][object]$Value) return (Test-HLScanValidationInteger -Value $Value) -or $Value -is [single] -or $Value -is [double] -or $Value -is [decimal] } function Add-HLScanValidationError { [CmdletBinding()] param( [Parameter(Mandatory)] [AllowEmptyCollection()] [System.Collections.Generic.List[string]]$Errors, [Parameter(Mandatory)] [AllowEmptyString()] [string]$Path, [Parameter(Mandatory)] [string]$Message ) $Errors.Add(('{0}: {1}' -f $Path, $Message)) } function Test-HLScanRequiredPropertySet { [CmdletBinding()] param( [Parameter(Mandatory)] [object]$InputObject, [Parameter(Mandatory)] [string[]]$Names, [Parameter(Mandatory)] [AllowEmptyString()] [string]$Path, [Parameter(Mandatory)] [AllowEmptyCollection()] [System.Collections.Generic.List[string]]$Errors ) foreach ($name in $Names) { if (-not (Test-HLScanValidationProperty -InputObject $InputObject -Name $name)) { Add-HLScanValidationError -Errors $Errors -Path (('{0}.{1}' -f $Path, $name).TrimStart('.')) -Message 'required property is missing.' } } } function Assert-HLScanObjectSection { [CmdletBinding()] param( [Parameter(Mandatory)] [object]$Root, [Parameter(Mandatory)] [string]$Name, [Parameter(Mandatory)] [string[]]$RequiredProperties, [Parameter(Mandatory)] [AllowEmptyCollection()] [System.Collections.Generic.List[string]]$Errors ) if (-not (Test-HLScanValidationProperty -InputObject $Root -Name $Name)) { return $null } $section = Get-HLScanValidationProperty -InputObject $Root -Name $Name if (-not (Test-HLScanValidationObject -Value $section)) { Add-HLScanValidationError -Errors $Errors -Path $Name -Message 'must be an object.' return $null } Test-HLScanRequiredPropertySet -InputObject $section -Names $RequiredProperties -Path $Name -Errors $Errors return $section } function Assert-HLScanResult { [CmdletBinding()] param( [Parameter(Mandatory)] [AllowNull()] [object]$ScanResult ) $errors = New-Object System.Collections.Generic.List[string] if (-not (Test-HLScanValidationObject -Value $ScanResult)) { throw 'Invalid Hardening Lens scan result: the input must be an object.' } $schemaVersion = $null if (Test-HLScanValidationProperty -InputObject $ScanResult -Name 'schemaVersion') { $schemaVersion = Get-HLScanValidationProperty -InputObject $ScanResult -Name 'schemaVersion' if ($schemaVersion -isnot [string] -or [string]$schemaVersion -cnotin @('1.0', '1.1')) { Add-HLScanValidationError -Errors $errors -Path 'schemaVersion' -Message "must be the string '1.0' or '1.1'." } } $isSchema11 = $schemaVersion -is [string] -and [string]$schemaVersion -ceq '1.1' $rootRequired = @('schemaVersion', 'scan', 'system', 'baseline', 'summary', 'results') if ($isSchema11) { $rootRequired += 'provenance' } Test-HLScanRequiredPropertySet -InputObject $ScanResult -Names $rootRequired -Path '' -Errors $errors $scanRequired = @( 'id', 'collectedAt', 'moduleVersion', 'redacted', 'readOnly', 'elevated', 'partialCollection', 'exceptionRegisterUsed', 'selectedControlCount' ) if ($isSchema11) { $scanRequired += 'collectionDurationMs' } $scan = Assert-HLScanObjectSection -Root $ScanResult -Name 'scan' -RequiredProperties $scanRequired -Errors $errors $system = Assert-HLScanObjectSection -Root $ScanResult -Name 'system' -RequiredProperties @( 'ComputerName', 'Domain', 'DomainJoined', 'DetectedRole', 'ProductType', 'OSCaption', 'OSVersion', 'BuildNumber', 'OSArchitecture', 'Manufacturer', 'Model', 'PowerShellVersion', 'PowerShellEdition', 'CurrentUser', 'IsElevated' ) -Errors $errors $baseline = Assert-HLScanObjectSection -Root $ScanResult -Name 'baseline' -RequiredProperties @( 'name', 'displayName', 'version', 'description', 'source', 'sourceBasis', 'supportedRoles', 'controlCount', 'notes' ) -Errors $errors $summary = Assert-HLScanObjectSection -Root $ScanResult -Name 'summary' -RequiredProperties @( 'Total', 'Applicable', 'Pass', 'Fail', 'Warning', 'Excepted', 'Unknown', 'Error', 'NotApplicable', 'HardeningScore', 'EvidenceCoverage', 'HighestOpenSeverity', 'ScoringModel' ) -Errors $errors $provenance = $null if ($isSchema11) { $provenance = Assert-HLScanObjectSection -Root $ScanResult -Name 'provenance' -RequiredProperties @( 'catalogVersion', 'catalogDigest', 'baselineDigest', 'capabilities' ) -Errors $errors } if ($null -ne $scan) { $id = if (Test-HLScanValidationProperty -InputObject $scan -Name 'id') { Get-HLScanValidationProperty -InputObject $scan -Name 'id' } else { $null } $parsedGuid = [guid]::Empty if ($id -isnot [string] -or -not [guid]::TryParse([string]$id, [ref]$parsedGuid)) { Add-HLScanValidationError -Errors $errors -Path 'scan.id' -Message 'must be a UUID string.' } $collectedAt = if (Test-HLScanValidationProperty -InputObject $scan -Name 'collectedAt') { Get-HLScanValidationProperty -InputObject $scan -Name 'collectedAt' } else { $null } $parsedTimestamp = [datetimeoffset]::MinValue if ($collectedAt -isnot [datetime] -and $collectedAt -isnot [datetimeoffset] -and ($collectedAt -isnot [string] -or -not [datetimeoffset]::TryParse([string]$collectedAt, [ref]$parsedTimestamp))) { Add-HLScanValidationError -Errors $errors -Path 'scan.collectedAt' -Message 'must be a date-time string.' } $moduleVersion = if (Test-HLScanValidationProperty -InputObject $scan -Name 'moduleVersion') { Get-HLScanValidationProperty -InputObject $scan -Name 'moduleVersion' } else { $null } if ($moduleVersion -isnot [string] -or [string]$moduleVersion -notmatch '^\d+\.\d+\.\d+$') { Add-HLScanValidationError -Errors $errors -Path 'scan.moduleVersion' -Message 'must use semantic version form x.y.z.' } foreach ($booleanName in @('redacted', 'readOnly', 'elevated', 'partialCollection', 'exceptionRegisterUsed')) { if (Test-HLScanValidationProperty -InputObject $scan -Name $booleanName) { $booleanValue = Get-HLScanValidationProperty -InputObject $scan -Name $booleanName if ($booleanValue -isnot [bool]) { Add-HLScanValidationError -Errors $errors -Path "scan.$booleanName" -Message 'must be a Boolean.' } } } if ((Test-HLScanValidationProperty -InputObject $scan -Name 'readOnly') -and (Get-HLScanValidationProperty -InputObject $scan -Name 'readOnly') -ne $true) { Add-HLScanValidationError -Errors $errors -Path 'scan.readOnly' -Message 'must be true.' } if (Test-HLScanValidationProperty -InputObject $scan -Name 'selectedControlCount') { $selectedControlCount = Get-HLScanValidationProperty -InputObject $scan -Name 'selectedControlCount' if (-not (Test-HLScanValidationInteger -Value $selectedControlCount) -or [decimal]$selectedControlCount -lt 0) { Add-HLScanValidationError -Errors $errors -Path 'scan.selectedControlCount' -Message 'must be a non-negative integer.' } } if ($isSchema11 -and (Test-HLScanValidationProperty -InputObject $scan -Name 'collectionDurationMs')) { $collectionDuration = Get-HLScanValidationProperty -InputObject $scan -Name 'collectionDurationMs' if (-not (Test-HLScanValidationInteger -Value $collectionDuration) -or [decimal]$collectionDuration -lt 0) { Add-HLScanValidationError -Errors $errors -Path 'scan.collectionDurationMs' -Message 'must be a non-negative integer.' } } } if ($null -ne $provenance) { if (Test-HLScanValidationProperty -InputObject $provenance -Name 'catalogVersion') { $catalogVersion = Get-HLScanValidationProperty -InputObject $provenance -Name 'catalogVersion' if ($catalogVersion -isnot [string] -or [string]$catalogVersion -notmatch '^\d+\.\d+\.\d+$') { Add-HLScanValidationError -Errors $errors -Path 'provenance.catalogVersion' -Message 'must use semantic version form x.y.z.' } } foreach ($digestName in @('catalogDigest', 'baselineDigest', 'exceptionDigest')) { if (Test-HLScanValidationProperty -InputObject $provenance -Name $digestName) { $digest = Get-HLScanValidationProperty -InputObject $provenance -Name $digestName if ($digest -isnot [string] -or [string]$digest -cnotmatch '^[0-9a-f]{64}$') { Add-HLScanValidationError -Errors $errors -Path "provenance.$digestName" -Message 'must be a lowercase SHA-256 digest.' } } } if ($null -ne $scan -and (Test-HLScanValidationProperty -InputObject $scan -Name 'exceptionRegisterUsed')) { $registerUsed = Get-HLScanValidationProperty -InputObject $scan -Name 'exceptionRegisterUsed' $hasExceptionDigest = Test-HLScanValidationProperty -InputObject $provenance -Name 'exceptionDigest' if ($registerUsed -eq $true -and -not $hasExceptionDigest) { Add-HLScanValidationError -Errors $errors -Path 'provenance.exceptionDigest' -Message 'is required when an exception register was used.' } if ($registerUsed -eq $false -and $hasExceptionDigest) { Add-HLScanValidationError -Errors $errors -Path 'provenance.exceptionDigest' -Message 'must be omitted when no exception register was used.' } } if (Test-HLScanValidationProperty -InputObject $provenance -Name 'capabilities') { $capabilities = Get-HLScanValidationProperty -InputObject $provenance -Name 'capabilities' if (-not (Test-HLScanValidationArray -Value $capabilities)) { Add-HLScanValidationError -Errors $errors -Path 'provenance.capabilities' -Message 'must be an array.' } else { $capabilityNames = New-Object System.Collections.Generic.List[string] $capabilityIndex = 0 foreach ($capability in @($capabilities)) { $capabilityPath = 'provenance.capabilities[{0}]' -f $capabilityIndex $capabilityIndex++ if (-not (Test-HLScanValidationObject -Value $capability)) { Add-HLScanValidationError -Errors $errors -Path $capabilityPath -Message 'must be an object.' continue } Test-HLScanRequiredPropertySet -InputObject $capability -Names @('name', 'available', 'detail') -Path $capabilityPath -Errors $errors if (Test-HLScanValidationProperty -InputObject $capability -Name 'name') { $capabilityName = Get-HLScanValidationProperty -InputObject $capability -Name 'name' if ($capabilityName -isnot [string] -or [string]::IsNullOrWhiteSpace([string]$capabilityName)) { Add-HLScanValidationError -Errors $errors -Path "$capabilityPath.name" -Message 'must be a non-empty string.' } else { $capabilityNames.Add([string]$capabilityName) } } if ((Test-HLScanValidationProperty -InputObject $capability -Name 'available') -and (Get-HLScanValidationProperty -InputObject $capability -Name 'available') -isnot [bool]) { Add-HLScanValidationError -Errors $errors -Path "$capabilityPath.available" -Message 'must be a Boolean.' } if (Test-HLScanValidationProperty -InputObject $capability -Name 'detail') { $detail = Get-HLScanValidationProperty -InputObject $capability -Name 'detail' if ($null -ne $detail -and $detail -isnot [string]) { Add-HLScanValidationError -Errors $errors -Path "$capabilityPath.detail" -Message 'must be null or a string.' } } } if (@($capabilityNames | Sort-Object -Unique).Count -ne $capabilityNames.Count) { Add-HLScanValidationError -Errors $errors -Path 'provenance.capabilities' -Message 'must contain unique capability names.' } if (($capabilityNames.ToArray() -join '|') -cne (@($capabilityNames.ToArray() | Sort-Object) -join '|')) { Add-HLScanValidationError -Errors $errors -Path 'provenance.capabilities' -Message 'must be sorted by name.' } } } } if ($null -ne $system) { foreach ($stringName in @( 'ComputerName', 'Domain', 'OSCaption', 'OSVersion', 'BuildNumber', 'OSArchitecture', 'Manufacturer', 'Model', 'PowerShellVersion', 'PowerShellEdition', 'CurrentUser' )) { if ((Test-HLScanValidationProperty -InputObject $system -Name $stringName) -and (Get-HLScanValidationProperty -InputObject $system -Name $stringName) -isnot [string]) { Add-HLScanValidationError -Errors $errors -Path "system.$stringName" -Message 'must be a string.' } } if (Test-HLScanValidationProperty -InputObject $system -Name 'DetectedRole') { $role = Get-HLScanValidationProperty -InputObject $system -Name 'DetectedRole' if ($role -isnot [string] -or [string]$role -cnotin @('Workstation', 'MemberServer', 'DomainController', 'Unknown')) { Add-HLScanValidationError -Errors $errors -Path 'system.DetectedRole' -Message 'must be Workstation, MemberServer, DomainController, or Unknown.' } } if (Test-HLScanValidationProperty -InputObject $system -Name 'DomainJoined') { $domainJoined = Get-HLScanValidationProperty -InputObject $system -Name 'DomainJoined' if ($null -ne $domainJoined -and $domainJoined -isnot [bool]) { Add-HLScanValidationError -Errors $errors -Path 'system.DomainJoined' -Message 'must be null or a Boolean.' } } if ((Test-HLScanValidationProperty -InputObject $system -Name 'IsElevated') -and (Get-HLScanValidationProperty -InputObject $system -Name 'IsElevated') -isnot [bool]) { Add-HLScanValidationError -Errors $errors -Path 'system.IsElevated' -Message 'must be a Boolean.' } if (Test-HLScanValidationProperty -InputObject $system -Name 'ProductType') { $productType = Get-HLScanValidationProperty -InputObject $system -Name 'ProductType' if (-not (Test-HLScanValidationInteger -Value $productType) -or [decimal]$productType -lt 0 -or [decimal]$productType -gt 3) { Add-HLScanValidationError -Errors $errors -Path 'system.ProductType' -Message 'must be an integer from 0 through 3.' } } } if ($null -ne $baseline) { foreach ($stringName in @('name', 'displayName', 'description')) { if ((Test-HLScanValidationProperty -InputObject $baseline -Name $stringName) -and (Get-HLScanValidationProperty -InputObject $baseline -Name $stringName) -isnot [string]) { Add-HLScanValidationError -Errors $errors -Path "baseline.$stringName" -Message 'must be a string.' } } if (Test-HLScanValidationProperty -InputObject $baseline -Name 'version') { $baselineVersion = Get-HLScanValidationProperty -InputObject $baseline -Name 'version' if ($baselineVersion -isnot [string] -or [string]$baselineVersion -notmatch '^\d+\.\d+\.\d+$') { Add-HLScanValidationError -Errors $errors -Path 'baseline.version' -Message 'must use semantic version form x.y.z.' } } if (Test-HLScanValidationProperty -InputObject $baseline -Name 'source') { $source = Get-HLScanValidationProperty -InputObject $baseline -Name 'source' if ($source -isnot [string] -or [string]$source -cnotin @('BuiltIn', 'Custom')) { Add-HLScanValidationError -Errors $errors -Path 'baseline.source' -Message 'must be BuiltIn or Custom.' } } foreach ($arrayName in @('sourceBasis', 'supportedRoles', 'notes')) { if (Test-HLScanValidationProperty -InputObject $baseline -Name $arrayName) { $arrayValue = Get-HLScanValidationProperty -InputObject $baseline -Name $arrayName if (-not (Test-HLScanValidationArray -Value $arrayValue)) { Add-HLScanValidationError -Errors $errors -Path "baseline.$arrayName" -Message 'must be an array.' } elseif (@($arrayValue | Where-Object { $_ -isnot [string] }).Count -gt 0) { Add-HLScanValidationError -Errors $errors -Path "baseline.$arrayName" -Message 'must contain only strings.' } } } if (Test-HLScanValidationProperty -InputObject $baseline -Name 'controlCount') { $controlCount = Get-HLScanValidationProperty -InputObject $baseline -Name 'controlCount' if (-not (Test-HLScanValidationInteger -Value $controlCount) -or [decimal]$controlCount -lt 0) { Add-HLScanValidationError -Errors $errors -Path 'baseline.controlCount' -Message 'must be a non-negative integer.' } } } $results = $null if (Test-HLScanValidationProperty -InputObject $ScanResult -Name 'results') { $results = Get-HLScanValidationProperty -InputObject $ScanResult -Name 'results' if (-not (Test-HLScanValidationArray -Value $results)) { Add-HLScanValidationError -Errors $errors -Path 'results' -Message 'must be an array.' $results = $null } } $statusNames = @('Pass', 'Fail', 'Warning', 'Excepted', 'Unknown', 'Error', 'NotApplicable') $severityNames = @('Critical', 'High', 'Medium', 'Low', 'Informational') $statusCounts = @{} foreach ($statusName in $statusNames) { $statusCounts[$statusName] = 0 } $controlIds = @{} $resultCount = 0 if ($null -ne $results) { foreach ($result in @($results)) { $index = $resultCount $resultCount++ $resultPath = 'results[{0}]' -f $index if (-not (Test-HLScanValidationObject -Value $result)) { Add-HLScanValidationError -Errors $errors -Path $resultPath -Message 'must be an object.' continue } $resultRequired = @( 'controlId', 'title', 'category', 'severity', 'status', 'originalStatus', 'expected', 'actual', 'message', 'evidence', 'rationale', 'remediation', 'references', 'tags', 'probe', 'exception', 'collectedAt' ) if ($isSchema11) { $resultRequired += 'probeDurationMs' } Test-HLScanRequiredPropertySet -InputObject $result -Names $resultRequired -Path $resultPath -Errors $errors if (Test-HLScanValidationProperty -InputObject $result -Name 'controlId') { $controlId = Get-HLScanValidationProperty -InputObject $result -Name 'controlId' if ($controlId -isnot [string] -or [string]$controlId -cnotmatch '^HL-[A-Z]+-[0-9]{3}$') { Add-HLScanValidationError -Errors $errors -Path "$resultPath.controlId" -Message 'must match HL-<CATEGORY>-<NNN>.' } elseif ($controlIds.ContainsKey([string]$controlId)) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.controlId" -Message "duplicate controlId '$controlId'." } else { $controlIds[[string]$controlId] = $true } } foreach ($stringName in @('title', 'category', 'message', 'rationale', 'remediation', 'probe')) { if ((Test-HLScanValidationProperty -InputObject $result -Name $stringName) -and (Get-HLScanValidationProperty -InputObject $result -Name $stringName) -isnot [string]) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.$stringName" -Message 'must be a string.' } } if (Test-HLScanValidationProperty -InputObject $result -Name 'severity') { $severity = Get-HLScanValidationProperty -InputObject $result -Name 'severity' if ($severity -isnot [string] -or [string]$severity -cnotin $severityNames) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.severity" -Message ('must be one of: {0}.' -f ($severityNames -join ', ')) } } if (Test-HLScanValidationProperty -InputObject $result -Name 'status') { $status = Get-HLScanValidationProperty -InputObject $result -Name 'status' if ($status -isnot [string] -or [string]$status -cnotin $statusNames) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.status" -Message ('must be one of: {0}.' -f ($statusNames -join ', ')) } else { $statusCounts[[string]$status]++ } } foreach ($arrayName in @('references', 'tags')) { if (Test-HLScanValidationProperty -InputObject $result -Name $arrayName) { $arrayValue = Get-HLScanValidationProperty -InputObject $result -Name $arrayName if (-not (Test-HLScanValidationArray -Value $arrayValue)) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.$arrayName" -Message 'must be an array.' } elseif (@($arrayValue | Where-Object { $_ -isnot [string] }).Count -gt 0) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.$arrayName" -Message 'must contain only strings.' } } } if (Test-HLScanValidationProperty -InputObject $result -Name 'originalStatus') { $originalStatus = Get-HLScanValidationProperty -InputObject $result -Name 'originalStatus' if ($null -ne $originalStatus -and $originalStatus -isnot [string]) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.originalStatus" -Message 'must be null or a string.' } } if (Test-HLScanValidationProperty -InputObject $result -Name 'exception') { $exception = Get-HLScanValidationProperty -InputObject $result -Name 'exception' if ($null -ne $exception -and -not (Test-HLScanValidationObject -Value $exception)) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.exception" -Message 'must be null or an object.' } } if (Test-HLScanValidationProperty -InputObject $result -Name 'collectedAt') { $resultTimestamp = Get-HLScanValidationProperty -InputObject $result -Name 'collectedAt' $parsedResultTimestamp = [datetimeoffset]::MinValue if ($resultTimestamp -isnot [datetime] -and $resultTimestamp -isnot [datetimeoffset] -and ($resultTimestamp -isnot [string] -or -not [datetimeoffset]::TryParse([string]$resultTimestamp, [ref]$parsedResultTimestamp))) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.collectedAt" -Message 'must be a date-time string.' } } if ($isSchema11 -and (Test-HLScanValidationProperty -InputObject $result -Name 'probeDurationMs')) { $probeDuration = Get-HLScanValidationProperty -InputObject $result -Name 'probeDurationMs' if (-not (Test-HLScanValidationInteger -Value $probeDuration) -or [decimal]$probeDuration -lt 0) { Add-HLScanValidationError -Errors $errors -Path "$resultPath.probeDurationMs" -Message 'must be a non-negative integer.' } } } } if ($null -ne $summary) { foreach ($countName in @('Total', 'Applicable') + $statusNames) { if (Test-HLScanValidationProperty -InputObject $summary -Name $countName) { $countValue = Get-HLScanValidationProperty -InputObject $summary -Name $countName if (-not (Test-HLScanValidationInteger -Value $countValue) -or [decimal]$countValue -lt 0) { Add-HLScanValidationError -Errors $errors -Path "summary.$countName" -Message 'must be a non-negative integer.' } } } foreach ($percentageName in @('HardeningScore', 'EvidenceCoverage')) { if (Test-HLScanValidationProperty -InputObject $summary -Name $percentageName) { $percentage = Get-HLScanValidationProperty -InputObject $summary -Name $percentageName if ($null -ne $percentage -and (-not (Test-HLScanValidationNumber -Value $percentage) -or [decimal]$percentage -lt 0 -or [decimal]$percentage -gt 100)) { Add-HLScanValidationError -Errors $errors -Path "summary.$percentageName" -Message 'must be null or a number from 0 through 100.' } } } if (Test-HLScanValidationProperty -InputObject $summary -Name 'HighestOpenSeverity') { $highest = Get-HLScanValidationProperty -InputObject $summary -Name 'HighestOpenSeverity' if ($highest -isnot [string] -or [string]$highest -cnotin (@('None') + $severityNames)) { Add-HLScanValidationError -Errors $errors -Path 'summary.HighestOpenSeverity' -Message 'contains an unsupported severity.' } } if ((Test-HLScanValidationProperty -InputObject $summary -Name 'ScoringModel') -and (Get-HLScanValidationProperty -InputObject $summary -Name 'ScoringModel') -isnot [string]) { Add-HLScanValidationError -Errors $errors -Path 'summary.ScoringModel' -Message 'must be a string.' } if ((Test-HLScanValidationProperty -InputObject $summary -Name 'Total') -and (Test-HLScanValidationInteger -Value (Get-HLScanValidationProperty -InputObject $summary -Name 'Total'))) { if ([int64](Get-HLScanValidationProperty -InputObject $summary -Name 'Total') -ne $resultCount) { Add-HLScanValidationError -Errors $errors -Path 'summary.Total' -Message "must equal the results count ($resultCount)." } } foreach ($statusName in $statusNames) { if ((Test-HLScanValidationProperty -InputObject $summary -Name $statusName) -and (Test-HLScanValidationInteger -Value (Get-HLScanValidationProperty -InputObject $summary -Name $statusName))) { if ([int64](Get-HLScanValidationProperty -InputObject $summary -Name $statusName) -ne [int64]$statusCounts[$statusName]) { Add-HLScanValidationError -Errors $errors -Path "summary.$statusName" -Message "must equal the matching results count ($($statusCounts[$statusName]))." } } } if ((Test-HLScanValidationProperty -InputObject $summary -Name 'Applicable') -and (Test-HLScanValidationInteger -Value (Get-HLScanValidationProperty -InputObject $summary -Name 'Applicable'))) { $expectedApplicable = $resultCount - [int]$statusCounts.NotApplicable if ([int64](Get-HLScanValidationProperty -InputObject $summary -Name 'Applicable') -ne $expectedApplicable) { Add-HLScanValidationError -Errors $errors -Path 'summary.Applicable' -Message "must equal the applicable results count ($expectedApplicable)." } } } if ($null -ne $scan -and (Test-HLScanValidationProperty -InputObject $scan -Name 'selectedControlCount')) { $selectedControlCount = Get-HLScanValidationProperty -InputObject $scan -Name 'selectedControlCount' if ((Test-HLScanValidationInteger -Value $selectedControlCount) -and [int64]$selectedControlCount -ne $resultCount) { Add-HLScanValidationError -Errors $errors -Path 'scan.selectedControlCount' -Message "must equal the results count ($resultCount)." } } if ($errors.Count -gt 0) { throw ("Invalid Hardening Lens scan result:{0} - {1}" -f [Environment]::NewLine, ($errors.ToArray() -join ([Environment]::NewLine + ' - '))) } } |