mirror of
https://github.com/Yamato-Security/WELA.git
synced 2026-10-04 13:34:46 +02:00
73 lines
3.5 KiB
PowerShell
73 lines
3.5 KiB
PowerShell
# Identifier validation is separate from recommendations and detection readiness.
|
|
Set-StrictMode -Version 2.0
|
|
|
|
function ConvertTo-WelaCanonicalAuditName {
|
|
param([string]$Name)
|
|
# Only reviewed spelling aliases; do not accept arbitrary fuzzy matches.
|
|
switch -CaseSensitive ($Name) {
|
|
'Non-Sensitive Privilege Use' { return 'Non Sensitive Privilege Use' }
|
|
'User / Device Claims' { return 'User/Device Claims' }
|
|
'Central Policy Staging' { return 'Central Access Policy Staging' }
|
|
default { return $Name }
|
|
}
|
|
}
|
|
|
|
function Assert-WelaAuditCatalog {
|
|
[CmdletBinding()]
|
|
param([Parameter(Mandatory)]$Catalog, [Parameter(Mandatory)]$CanonicalCatalog)
|
|
$canonical = @{}; $canonicalGuids = @{}; $seen = @{}; $ids = @{}
|
|
foreach ($row in $CanonicalCatalog) {
|
|
if (-not $row.id -or $canonical.ContainsKey($row.id) -or
|
|
$row.guid -notmatch '^[0-9A-Fa-f]{8}(-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$' -or $canonicalGuids.ContainsKey($row.guid)) {
|
|
throw 'Invalid or duplicate canonical audit identifier.'
|
|
}
|
|
$canonical[$row.id] = $row.guid; $canonicalGuids[$row.guid] = $true
|
|
}
|
|
foreach ($row in @($Catalog | Where-Object { $_.currentSetting.type -eq 'auditpol' })) {
|
|
$name = ConvertTo-WelaCanonicalAuditName $row.subCategory
|
|
if (-not $row.id -or $ids.ContainsKey($row.id)) { throw "Duplicate or empty legacy audit id: $($row.id)" }
|
|
if ($row.select.type -ne 'guid' -or -not $canonical.ContainsKey($name) -or $canonical[$name] -ine $row.select.guid) {
|
|
throw "Audit catalog name/GUID mismatch: $($row.subCategory) / $($row.select.guid)"
|
|
}
|
|
if ($seen.ContainsKey($row.select.guid)) { throw "Duplicate legacy audit GUID: $($row.select.guid)" }
|
|
$seen[$row.select.guid] = $true; $ids[$row.id] = $true
|
|
}
|
|
}
|
|
|
|
function Get-WelaEventMappingReview {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Mappings,
|
|
[Parameter(Mandatory)]$CanonicalCatalog,
|
|
[Parameter(Mandatory)][ValidateRange(0,65535)][int]$EventId
|
|
)
|
|
$byGuid = @{}
|
|
foreach ($row in $CanonicalCatalog) { $byGuid[$row.guid] = $row.id }
|
|
# Empty category-heading rows must never become EventID 0 through a cast.
|
|
$matches = @($Mappings | Where-Object { $_.'Event ID' -match '^\d+$' -and [int]$_.'Event ID' -eq $EventId })
|
|
$candidates = @(); $uncertain = @()
|
|
foreach ($row in $matches) {
|
|
if (-not $row.Subcategory -or -not $byGuid.ContainsKey($row.GUID)) {
|
|
$uncertain += 'CategoryOnlyOrUnknownGuid'; continue
|
|
}
|
|
if ((ConvertTo-WelaCanonicalAuditName $row.Subcategory) -cne $byGuid[$row.GUID]) {
|
|
$uncertain += 'NameGuidMismatch'; continue
|
|
}
|
|
$candidates += [pscustomobject]@{ Name=$byGuid[$row.GUID]; Guid=$row.GUID }
|
|
}
|
|
$candidates = @($candidates | Sort-Object Guid -Unique)
|
|
$reasons = @($uncertain | Select-Object -Unique)
|
|
if (-not $matches.Count) { $reasons += 'NoMapping' }
|
|
if ($candidates.Count -gt 1) { $reasons += 'MultipleSubcategories' }
|
|
# Even an unambiguous mapping does not prove outcome, object, fields or source role.
|
|
$reasons += 'OutcomeObjectAndRoleUnverified'
|
|
[pscustomobject]@{
|
|
EventId=$EventId
|
|
State=$(if (-not $matches.Count -or -not $candidates.Count) { 'Unknown' } else { 'Conditional' })
|
|
MappingCount=$matches.Count; Candidates=$candidates; Reasons=$reasons
|
|
DetectionReady=$false
|
|
}
|
|
}
|
|
|
|
Export-ModuleMember -Function Assert-WelaAuditCatalog, Get-WelaEventMappingReview
|