From 98d37fbf3715ba4375b12239ea46dcf1d1f8cb54 Mon Sep 17 00:00:00 2001 From: Shirofune-Security <43838376+Shirofune-Security@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:59:59 +0900 Subject: [PATCH] Retain policy prerequisites and evidence in apply results --- WELA.ps1 | 11 +++++++++++ modules/AuditProfiles.psm1 | 8 ++++++-- tests/audit-profiles.Tests.ps1 | 5 +++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/WELA.ps1 b/WELA.ps1 index d423ccf2..541bb0e2 100644 --- a/WELA.ps1 +++ b/WELA.ps1 @@ -289,6 +289,15 @@ function Get-WelaSelectedContext { Get-WelaHostContext } +function Show-WelaAuditProfilePrerequisites { + param($Plan) + foreach ($policy in $Plan.policies) { + if ($policy.prerequisites -and ($policy.mode -in @('exact', 'minimum') -or ($policy.mode -eq 'optional' -and $Plan.includeOptional))) { + Write-Host "Prerequisite - $($policy.id): $($policy.prerequisites)" -ForegroundColor DarkYellow + } + } +} + function Invoke-WelaProfileCommand { param([string]$Command) if ($script:Baseline) { throw "Use -Profile or -Baseline, not both. Versioned profiles cover advanced audit policy only." } @@ -305,6 +314,7 @@ function Invoke-WelaProfileCommand { $plan = Get-WelaAuditProfilePlan -Profile $script:Profile -Role $context.Role -Build $context.Build -Current $current -IncludeOptional:$script:IncludeOptional Write-Host "Profile: $($plan.profile); role: $($plan.role); build: $($plan.build)" Write-Host "Scope: advanced audit policy only. Channels, command-line capture, PowerShell, NTLM, SACLs, CA AuditFilter and forwarding are separate." + Show-WelaAuditProfilePrerequisites -Plan $plan $result = $plan if ($Command -eq 'configure') { if (-not (TestAdministrator)) { throw "Configuring advanced audit policy requires Administrator privileges." } @@ -1393,6 +1403,7 @@ function ConfigureAuditSettings { # Audit and configure consume the same versioned policy definition. Write-Host "Configuring advanced audit policy from wela-2.2.0..." + Show-WelaAuditProfilePrerequisites -Plan $profilePlan $profileResult = Invoke-WelaAuditProfilePlan -Plan $profilePlan -Confirm:(-not $Auto) $profileResult.results | Format-Table id, beforeMask, targetMask, effectiveMask, status -AutoSize if (-not $profileResult.success) { throw "Advanced audit-policy configuration failed. Review effective-state results above." } diff --git a/modules/AuditProfiles.psm1 b/modules/AuditProfiles.psm1 index 1f7d101c..8a53aba3 100644 --- a/modules/AuditProfiles.psm1 +++ b/modules/AuditProfiles.psm1 @@ -240,10 +240,14 @@ function Invoke-WelaAuditProfilePlan { } catch { $status = 'Failed'; $errorText = $_.Exception.Message; $effective = $null } } else { $status = 'Skipped' } } - [pscustomobject]@{ id = $policy.id; guid = $policy.guid; mode = $policy.mode; beforeMask = $initial; targetMask = $target; effectiveMask = $effective; status = $status; error = $errorText } + [pscustomobject]@{ + id = $policy.id; guid = $policy.guid; mode = $policy.mode + beforeMask = $initial; targetMask = $target; effectiveMask = $effective; status = $status; error = $errorText + prerequisites = $policy.prerequisites; evidence = $policy.evidence; sourceIds = @($policy.sourceIds) + } } [pscustomobject]@{ - profile = $Plan.profile; scope = $Plan.scope; role = $Plan.role; build = $Plan.build + profile = $Plan.profile; version = $Plan.version; scope = $Plan.scope; role = $Plan.role; build = $Plan.build schemaSha256 = $Plan.schemaSha256; provenance = $Plan.provenance success = (@($results | Where-Object { $_.status -eq 'Failed' }).Count -eq 0) results = @($results) diff --git a/tests/audit-profiles.Tests.ps1 b/tests/audit-profiles.Tests.ps1 index 6d61d5b3..9700fc07 100644 --- a/tests/audit-profiles.Tests.ps1 +++ b/tests/audit-profiles.Tests.ps1 @@ -58,6 +58,9 @@ $writer = { param($Guid, $Mask) $script:Writes += $Guid; $script:State[$Guid] = $context = { [pscustomobject]@{ Role = 'Client'; Build = 26100 } } $applied = Invoke-WelaAuditProfilePlan -Plan $wela -ReadPolicy $reader -WritePolicy $writer -ReadContext $context -Confirm:$false Assert $applied.success 'apply succeeds after verified effective reads' +$processResult = $applied.results | Where-Object { $_.id -eq 'Process Creation' } +Assert ($processResult.prerequisites -match 'Command-line' -and $processResult.sourceIds -contains 'wela') 'apply results retain source and event-generation prerequisites' +Assert ($applied.version -eq $wela.version) 'apply result includes selected source version' Assert ($script:Writes.Count -gt 0) 'selected exact policies were applied' Assert (@($applied.results | Where-Object { $_.status -eq 'Applied' -and $_.effectiveMask -ne $_.targetMask }).Count -eq 0) 'applied always means verified' $count = $script:Writes.Count @@ -72,6 +75,8 @@ $failed = Invoke-WelaAuditProfilePlan -Plan $wela -ReadPolicy $reader -WritePoli Assert (-not $failed.success -and @($failed.results | Where-Object { $_.status -eq 'Failed' }).Count -gt 0) 'native failure is machine-readable' $mismatch = Invoke-WelaAuditProfilePlan -Plan $wela -ReadPolicy $reader -WritePolicy { param($Guid, $Mask) } -ReadContext $context -Confirm:$false Assert (-not $mismatch.success) 'zero exit without effective change does not count as success' +$failedProcess = $mismatch.results | Where-Object { $_.id -eq 'Process Creation' } +Assert ($failedProcess.status -eq 'Failed' -and $null -eq $failedProcess.effectiveMask -and $failedProcess.prerequisites -match 'Command-line') 'failed/unknown effective state still retains prerequisites' $script:Writes = @() $whatIf = Invoke-WelaAuditProfilePlan -Plan $wela -ReadPolicy $reader -WritePolicy $writer -ReadContext $context -WhatIf Assert ($script:Writes.Count -eq 0) 'WhatIf never invokes native writer'