mirror of
https://github.com/Yamato-Security/WELA.git
synced 2026-10-08 07:15:25 +02:00
Bind native listener adapter modules and normalize cross-engine context JSON
This commit is contained in:
1 parent
3e9ba8c403
commit
2301bf1e85
3 files changed
+18
-5
No files matched your search
@@ -18,14 +18,14 @@ Plan writes review artifacts, including `plan.json` and `manifest.json` with `Pl
|
||||
|
||||
The fixed desired listener is `Address=IP:<selected IPv4>`, transport `HTTP`, port `5985`, URL prefix `wsman`, enabled, with blank hostname and certificate thumbprint. Wildcard listeners, any existing HTTP5985 listener and an existing selected Address/Transport pair prevent creation. WELA leaves those listeners in place for manual review. It does not narrow, replace, disable or remove an existing endpoint.
|
||||
|
||||
The plan binds the actual machine, operator/logon context, assigned address, implementation and original WinRM configuration/policy/listeners and firewall observations. Apply checks the reviewed hash and fresh context, writes pending evidence before its single creation attempt, then checks actual native configuration and `ListeningOn`. The fixed local creation worker uses the trusted native Windows PowerShell 5.1 engine under both Windows PowerShell 5.1 and PowerShell 7 hosts, with no execution-policy override. Its actual process, token and engine are retained as evidence. A host that cannot run this fixed adapter must resolve that prerequisite before applying.
|
||||
The plan binds the actual machine, operator/logon context, assigned address, implementation and original WinRM configuration/policy/listeners and firewall observations. Apply checks the reviewed hash and fresh context, writes pending evidence before its single creation attempt, then checks actual native configuration and `ListeningOn`. The fixed local creation worker uses the trusted native Windows PowerShell 5.1 engine under both Windows PowerShell 5.1 and PowerShell 7 hosts, with the fixed native 5.1 module directory and no execution-policy override. Its actual process, token and engine are retained as evidence. A host that cannot run this fixed adapter must resolve that prerequisite before applying.
|
||||
|
||||
| Result | Meaning |
|
||||
| --- | --- |
|
||||
| `ReviewRequired` | Plan artifacts are ready for review; no listener was created. |
|
||||
| `CreatedAndVerified` | The new listener and expected native readback were observed, with the required preservation checks. |
|
||||
| `Refused` | Preconditions, evidence or context failed before a creation attempt. |
|
||||
| `CreateAttemptedUnverified` | Creation was attempted but the final state could not be completely verified. Review the pending/native evidence and current listeners before taking further action. |
|
||||
| `CreateAttemptedUnverified` | The adapter started and creation was attempted or cannot be ruled out; the final state could not be completely verified. Review the pending/native evidence and current listeners before taking further action. |
|
||||
|
||||
No atomic Windows compare-and-set is available; another administrator or policy process can race observation and creation. There is no automatic rollback. An interrupted process can leave pending evidence and a created listener without a completed report. Use the retained original and current snapshots to identify what changed; this command never deletes a listener as a recovery shortcut.
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# One reviewed exact-IP HTTP listener; native creation always runs in Windows PowerShell 5.1.
|
||||
function Get-WelaListenerKey {param($Value) ConvertTo-Json -InputObject $Value -Depth 24 -Compress}
|
||||
function Get-WelaListenerKey {
|
||||
param($Value)
|
||||
# Windows PowerShell 5.1 escapes these HTML characters even with default JSON settings.
|
||||
# Normalize the same spelling in both engines before binding nested context strings.
|
||||
$json=ConvertTo-Json -InputObject $Value -Depth 24 -Compress
|
||||
$json.Replace('<','\u003c').Replace('>','\u003e').Replace('&','\u0026').Replace("'",'\u0027')
|
||||
}
|
||||
function Get-WelaListenerSelection {
|
||||
param($ComputerName,$LocalAddress)
|
||||
if($ComputerName -isnot [string] -or $ComputerName -cnotmatch '^[A-Za-z0-9][A-Za-z0-9-]{0,62}$'){throw 'Select the actual local computer name.'}
|
||||
@@ -30,7 +36,7 @@ function ConvertFrom-WelaListenerXml {
|
||||
$listening=@($root.ChildNodes|Where-Object {$_.NodeType -eq 'Element' -and $_.LocalName -ceq 'ListeningOn'}|ForEach-Object InnerText|Sort-Object)
|
||||
if($listening.Count -gt 64 -or @($listening|Sort-Object -Unique).Count -ne $listening.Count){throw 'Ambiguous or excessive ListeningOn addresses.'}
|
||||
foreach($value in $listening){$ip=$null;if(-not [Net.IPAddress]::TryParse($value,[ref]$ip)){throw 'Invalid native ListeningOn address.'}}
|
||||
$result.ListeningOn=$listening;$result.PolicyOwned=$policy;$result.XmlKey=Get-WelaWefXmlKey $root;$result.RawXml=$Xml
|
||||
$result.ListeningOn=$listening;$result.PolicyOwned=$policy;$result.RawXml=$Xml
|
||||
[pscustomobject]$result
|
||||
}
|
||||
function Read-WelaListenerInventory {
|
||||
@@ -206,6 +212,7 @@ function Start-WelaListenerAdapter {
|
||||
foreach($path in @($State.Adapter.Engine,$State.Adapter.Worker,$RequestPath)){if($path.Contains('"') -or $path.EndsWith('\') -or $path -match '[\x00-\x1f]'){throw 'Unsupported native adapter path.'}}
|
||||
$info=[Diagnostics.ProcessStartInfo]::new();$info.FileName=$State.Adapter.Engine
|
||||
$info.Arguments='-NoLogo -NoProfile -NonInteractive -File "'+$State.Adapter.Worker+'" -RequestPath "'+$RequestPath+'" -RequestHash '+$RequestHash
|
||||
$info.EnvironmentVariables['PSModulePath']=Join-Path ([Environment]::SystemDirectory) 'WindowsPowerShell/v1.0/Modules'
|
||||
$info.UseShellExecute=$false;$info.CreateNoWindow=$true;$info.RedirectStandardOutput=$true;$info.RedirectStandardError=$true;$info.StandardOutputEncoding=[Text.UTF8Encoding]::new($false);$info.StandardErrorEncoding=[Text.UTF8Encoding]::new($false)
|
||||
Initialize-WelaListenerPipe
|
||||
$result=[pscustomobject][ordered]@{Started=$false;ProcessId=$null;ExitCode=$null;TimedOut=$false;TerminationConfirmed=$false;Receipt=$null;Diagnostic=''};$process=[Diagnostics.Process]::new();$process.StartInfo=$info
|
||||
|
||||
@@ -8,6 +8,12 @@ $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 refusal $Pattern, got: $message; input: $bad; action: $Action"}
|
||||
function Copy-TestListener($Value){Get-WelaListenerKey $Value|ConvertFrom-Json}
|
||||
$special=[pscustomobject]@{Xml='<a x="v">&</a>';Name="O'Neil"}
|
||||
$specialKey=Get-WelaListenerKey $special
|
||||
Assert ($specialKey -notmatch "[<>&']" -and $specialKey.Contains('\u003c') -and $specialKey.Contains('\u0027')) 'Context JSON spelling is consistent across native5.1 and host7.'
|
||||
Assert (($specialKey|ConvertFrom-Json).Xml -ceq $special.Xml -and ($specialKey|ConvertFrom-Json).Name -ceq $special.Name) 'Canonical JSON escaping preserves exact values.'
|
||||
Assert ((Get-WelaListenerKey (Copy-TestListener ([pscustomobject]@{Nested=$specialKey}))) -ceq (Get-WelaListenerKey ([pscustomobject]@{Nested=$specialKey}))) 'Nested context JSON keeps its reviewed value.'
|
||||
|
||||
$selection=Get-WelaListenerSelection 'test-host' '192.0.2.10'
|
||||
$xml='<cfg:Listener xmlns:cfg="http://schemas.microsoft.com/wbem/wsman/1/config/listener" xml:lang="en-US"><cfg:Address>IP:192.0.2.10</cfg:Address><cfg:Transport>HTTP</cfg:Transport><cfg:Port>5985</cfg:Port><cfg:Hostname/><cfg:Enabled>true</cfg:Enabled><cfg:URLPrefix>wsman</cfg:URLPrefix><cfg:CertificateThumbprint/><cfg:ListeningOn>192.0.2.10</cfg:ListeningOn></cfg:Listener>'
|
||||
Assert ($selection.ComputerName -ceq 'TEST-HOST') 'Actual computer selection is canonical.'
|
||||
@@ -87,7 +93,7 @@ try {
|
||||
if($scenario -eq 'hash'){$hash='f'*64}
|
||||
if($scenario -in @('schema','duplicate-json','context')){
|
||||
$text=[IO.File]::ReadAllText($path)
|
||||
if($scenario -eq 'schema'){$text=$text.Replace('"SchemaVersion": 1','"SchemaVersion": true')}
|
||||
if($scenario -eq 'schema'){$text=$text -replace '"SchemaVersion"\s*:\s*1','"SchemaVersion": true'}
|
||||
if($scenario -eq 'duplicate-json'){$text=$text.Replace('"SchemaVersion":','"SchemaVersion":1,"SchemaVersion":')}
|
||||
if($scenario -eq 'context'){$text=$text.Replace('TEST-HOST','OTHER-HOST')}
|
||||
[IO.File]::WriteAllText($path,$text);$hash=(Get-FileHash $path).Hash.ToLowerInvariant()
|
||||
|
||||
Reference in new issue
Block a user