$ErrorActionPreference='Stop' $repo=Split-Path $PSScriptRoot -Parent . "$repo/scripts/AppLockerReadiness.ps1" . "$repo/scripts/WefArrival.ps1" . "$repo/scripts/AppLockerScriptProbe.ps1" # Mocking Get-Service skips the cmdlet's normal .NET Framework assembly load. if(-not ('System.ServiceProcess.ServiceControllerStatus' -as [type])){Add-Type -AssemblyName System.ServiceProcess -ErrorAction Stop} $count=0 function Assert($Value,$Message){if(-not $Value){throw $Message};$script:count++} function Reject([scriptblock]$Action,[string]$Pattern){$message='';try{&$Action|Out-Null}catch{$message=$_.Exception.Message};Assert ($message -match $Pattern) "Expected $Pattern; got $message"} # Prove stopped service refusal happens before the actual state reader reaches CIM. $script:missingService='';$script:scm=@();$script:cimCalls=0 function Get-Service {param($Name);$script:scm+=$Name;[pscustomobject]@{Name=$Name;Status=$(if($Name -ceq $script:missingService){[ServiceProcess.ServiceControllerStatus]::Stopped}else{[ServiceProcess.ServiceControllerStatus]::Running})}} function Get-WelaAppLockerHost {$script:cimCalls++;throw 'CIM boundary reached after SCM checks'} foreach($name in @('Winmgmt','EventLog','AppIDSvc')){ $script:missingService=$name;$script:scm=@();$script:cimCalls=0 Reject {Get-WelaAppLockerScriptState} ($name+' must already be running') Assert ($script:cimCalls -eq 0) 'Stopped service refusal precedes all CIM observations' } $script:missingService='';$script:scm=@();$script:cimCalls=0 Reject {Get-WelaAppLockerScriptState} 'CIM boundary reached after SCM checks' Assert (($script:scm -join ',') -ceq 'Winmgmt,EventLog,AppIDSvc' -and $script:cimCalls -eq 1) 'All direct SCM checks precede the first CIM observation' $policy='' $state=[pscustomobject]@{Services=@([pscustomobject]@{Name='Winmgmt';Status='Running'},[pscustomobject]@{Name='EventLog';Status='Running'},[pscustomobject]@{Name='AppIDSvc';Status='Running'});Host=[pscustomobject]@{Status='Candidate';Is64BitProcess=$true;PartOfDomain=$false;ProductType=3;Build=20348};MachineGuid='11111111-1111-1111-1111-111111111111';Management=[pscustomobject]@{Status='Observed'};Computer='TEST';Domain='WORKGROUP';Reader=[pscustomobject]@{Sid='S-1-5-21-1-2-3-1000'};EffectivePolicy=[pscustomobject]@{Status='Observed';Policy=(ConvertFrom-WelaAppLockerXml $policy)};Service=[pscustomobject]@{Status='Observed';State='Running';StartMode='Manual'};Channel=[pscustomobject]@{Name='Microsoft-Windows-AppLocker/MSI and Script';Enabled=$true;SecurityDescriptor='O:SYG:SYD:(A;;0x1;;;SY)'};Source='C:\Windows\System32\cmd.ps1';SourceHash=('a'*64)} $state|Add-Member NoteProperty LocalPolicy ($state.EffectivePolicy|ConvertTo-Json -Depth 20|ConvertFrom-Json) $null=Get-WelaAppLockerScriptStateKey $state;Assert $true 'Valid audit-only prereqs' foreach($parent in @('Host','LocalPolicy','EffectivePolicy','Service','Management')){ $saved=$state.$parent.Status;$state.$parent.Status=$true Reject {Get-WelaAppLockerScriptStateKey $state} 'typed string' $state.$parent.Status=$saved } $state.Services[0].Status=$true;Reject {Get-WelaAppLockerScriptStateKey $state} 'preflight';$state.Services[0].Status='Running' foreach($mode in @('Enabled','NotConfigured')){$state.EffectivePolicy.Policy=ConvertFrom-WelaAppLockerXml ($policy.Replace('AuditOnly',$mode));Reject {Get-WelaAppLockerScriptStateKey $state} 'AuditOnly'} $state.EffectivePolicy.Policy=ConvertFrom-WelaAppLockerXml $policy $state.Service.State='Stopped';Reject {Get-WelaAppLockerScriptStateKey $state} 'already be running';$state.Service.State='Running' $state.Channel.Enabled=$false;Reject {Get-WelaAppLockerScriptStateKey $state} 'enabled';$state.Channel.Enabled=$true $process=[pscustomobject]@{ScriptPath='C:\Temp\wela-owned.ps1';ProcessId=1234;UserSid=$state.Reader.Sid;StartedUtc='2026-09-01T00:00:00.0000000Z';CompletedUtc='2026-09-01T00:00:02.0000000Z'} $event='8006042Microsoft-Windows-AppLocker/MSI and ScriptTESTSCRIPTS-1-5-21-1-2-3-10001234C:\Temp\wela-owned.ps1' $end=[long]41 Assert (Test-WelaAppLockerScriptEvent $event $process $state $end) 'Exact fixture must match' Assert (Test-WelaAppLockerScriptEvent ($event.Replace('8006','8005')) $process $state $end) 'Allowed event matches but has distinct EventId' $mutations=@(@('8006','8007'),@('1234','1235'),@('S-1-5-21-1-2-3-1000','S-1-5-21-1-2-3-1001'),@('C:\Temp\wela-owned.ps1','C:\Temp\other.ps1'),@('SCRIPT','DLL'),@('TEST','OTHER'),@('cbda4dbf','abda4dbf'),@('0','1'),@('00:00:01.0000000Z','00:00:03.0000000Z'),@('','8006'),@('','S-1-1-0')) foreach($pair in $mutations){$bad=$event.Replace($pair[0],$pair[1]);Assert ($bad -cne $event) 'Mutation changed fixture';Assert (-not(Test-WelaAppLockerScriptEvent $bad $process $state $end)) ('Reject '+$pair[0])} Assert (-not(Test-WelaAppLockerScriptEvent (']>'+$event) $process $state $end)) 'DTD rejected' Reject {Invoke-WelaAppLockerScriptProbe -Action Run} 'requires' Reject {Invoke-WelaAppLockerScriptProbe -Action Plan -OutputPath ignored} 'requires' Assert (-not(Test-WelaAppLockerScriptEvent $event $process $state 42)) 'Previously observed record is rejected' Assert (-not(Test-WelaAppLockerScriptEvent ($event.Replace('00:00:01.0000000Z','00:00:02.0000001Z')) $process $state $end)) 'A 100ns late record is rejected without clock padding' Assert (Test-WelaAppLockerScriptEvent ($event.Replace('00:00:01.0000000Z','00:00:00.0000000Z')) $process $state $end) 'Exact inclusive start is accepted' Assert (Test-WelaAppLockerScriptEvent ($event.Replace('00:00:01.0000000Z','00:00:02.0000000Z')) $process $state $end) 'Exact inclusive completion is accepted' $worker=[IO.File]::ReadAllText("$repo/scripts/AppLockerScriptWorker.ps1") $text=New-WelaAppLockerScriptText $worker ('a'*32) Assert ($text -notlike '*__WELA_SCRIPT_NONCE__*') 'All three fixed nonce placeholders replaced' Reject {New-WelaAppLockerScriptText $worker ('a'*31+"'" )} 'nonce' Reject {New-WelaAppLockerScriptText ($worker+'__WELA_SCRIPT_NONCE__') ('a'*32)} 'template' $tokens=$null;$errors=$null;$null=[Management.Automation.Language.Parser]::ParseInput($text,[ref]$tokens,[ref]$errors) Assert (-not $errors.Count) 'Generated worker parses' # Use the production orchestration with mocked native read/launch boundaries. # These fixtures never stand in for actual native success; the Windows matrix does that. $script:ScriptRoot=$repo;$script:scenario='ok';$script:reads=0;$script:state=$state;$script:event=$event $script:token=[pscustomobject]@{Sid='S-1-5-21-1-2-3-1000';AuthenticationId='0x123';Groups=@();Privileges=@();TokenId='0x456';ModifiedId='0x789'} function Initialize-WelaAppLockerScriptNative {} function Get-WelaAppLockerScriptReader {if($script:scenario -eq 'reader-drift' -and $script:reads -gt 2){$script:token.ModifiedId='0xabc'};$script:token|ConvertTo-Json -Depth 8|ConvertFrom-Json} function Get-WelaAppLockerScriptUtcNow {([DateTimeOffset]::Parse('2026-09-01T00:00:00Z')).UtcDateTime} function Get-WelaAppLockerScriptState {$script:reads++;if($script:scenario -eq 'drift' -and $script:reads -gt 2){$script:state.Service.StartMode='Auto'};$script:state|ConvertTo-Json -Depth 20|ConvertFrom-Json} function Read-WelaAppLockerScriptBoundary {41} function Start-WelaAppLockerScriptProcess { param($Root,$State,$Reader,$SourcesKey) if($script:scenario -eq 'reader-drift'){$script:token.ModifiedId='0xabc'} $artifact=Write-WelaArrivalArtifact $Root 'fixed.ps1' 'fixed' [pscustomobject]@{ScriptPath=(Join-Path $Root 'fixed.ps1');ScriptSha256=$artifact.Sha256;ScriptArtifact=$artifact;Nonce=('a'*32)} } function Read-WelaAppLockerScriptEvents {param($Boundary);if($script:scenario -eq 'denied'){throw [UnauthorizedAccessException]::new('Native query denied')};[pscustomobject]@{Xml=if($script:scenario -eq 'duplicate'){@($script:event,$script:event)}elseif($script:scenario -eq 'absent'){@()}else{@($script:event)};Complete=($script:scenario -ne 'cap')}} function Test-WelaAppLockerScriptEvent {$true} $root=Join-Path ([IO.Path]::GetTempPath()) ('wela-applocker-script-test-'+[guid]::NewGuid().ToString('N'));$null=New-Item -ItemType Directory $root try { foreach($scenario in @('ok','duplicate','drift','cap','denied','absent','reader-drift')){ $script:scenario=$scenario;$script:reads=0;$state.Service.StartMode='Manual';$script:token.ModifiedId='0x789' $result=Invoke-WelaAppLockerScriptProbe Run (Join-Path $root $scenario) 1 Assert ($result.ReadyRuleCredit -eq 0 -and $result.PolicyChanges -eq 0) 'No readiness or configuration credit' Assert (($result.ExitCode -eq 0) -eq ($scenario -eq 'ok')) "Expected outcome $scenario : $($result.Diagnostic)" Assert (($result.Status -ceq 'NativeScriptEventObserved') -eq ($scenario -eq 'ok')) 'Only complete success receives observed status' Assert (Test-Path (Join-Path $result.OutputPath 'manifest.json')) 'Success/failure manifest retained' } }finally{Remove-Item -LiteralPath $root -Recurse -Force} Write-Host "AppLocker Script fixtures passed: $count assertions."