fix(agent99): bind host112 apply to immutable receipt
This commit is contained in:
@@ -235,7 +235,13 @@ def test_phase_budget_and_systemd_outer_timeout_are_aligned() -> None:
|
||||
assert 'if [ "$APPLY_PHASE_TIMEOUT_BUDGET_SECONDS" -gt "$EXECUTOR_DEADLINE_SECONDS" ]; then' in source
|
||||
assert 'if [ "$MANAGER_VERIFY_INTERVAL_SECONDS" -gt "$MANAGER_VERIFY_TIMEOUT_SECONDS" ]; then' in source
|
||||
assert "TimeoutStartSec=480" in service
|
||||
assert "Invoke-HostSshText $targetHost $applyCommand 590 1" in control
|
||||
assert "$script:Agent99Host112ExecutorBudgetSeconds = 420" in control
|
||||
assert "$script:Agent99Host112ApplyTransportTimeoutSeconds = 510" in control
|
||||
assert "Invoke-AgentHost112SshText $applyCommand $script:Agent99Host112ApplyTransportTimeoutSeconds 1" in control
|
||||
assert "executorReserveSeconds -ge 60" in control
|
||||
assert "queueReserveSeconds -ge 300" in control
|
||||
assert "clientReserveSeconds -ge 120" in control
|
||||
assert '$process.WaitForExit($script:Agent99QueueOuterDeadlineSeconds * 1000)' in control
|
||||
|
||||
|
||||
def test_receipts_and_readbacks_are_immutable_boot_bound_and_replay_safe() -> None:
|
||||
@@ -396,8 +402,8 @@ def test_agent99_propagates_identity_and_requires_independent_receipt() -> None:
|
||||
assert f'[string]`${parameter} = ""' in bootstrap
|
||||
assert 'reason = "controlled_dispatch_identity_unsafe"' in control
|
||||
assert 'applyBlockedReason = "control_identity_incomplete_or_unsafe"' in function
|
||||
assert '$before.managerPreflightStatus -eq "ready"' in function
|
||||
assert 'receiptStatus -eq "written"' in function
|
||||
assert '$dryRun.managerPreflightStatus -eq "ready"' in function
|
||||
assert 'receiptStatus -eq "written_immutable"' in function
|
||||
assert '$after.managerVerified' in function
|
||||
assert 'name = "wazuh_manager_service_active"' in function
|
||||
assert 'name = "wazuh_analysisd_process_present"' in function
|
||||
@@ -406,3 +412,94 @@ def test_agent99_propagates_identity_and_requires_independent_receipt() -> None:
|
||||
assert 'name = "wazuh_manager_api_55000_tcp"' in function
|
||||
assert "host112_readback_transport_unavailable" in function
|
||||
assert 'terminal = "no_write_control_identity_incomplete_or_unsafe"' in function
|
||||
|
||||
|
||||
def test_agent99_manager_success_requires_apply_and_immutable_receipt_chain() -> None:
|
||||
control = read(CONTROL)
|
||||
function = control[control.index("function Invoke-AgentHost112GuestRecovery") :]
|
||||
function = function[: function.index("function Test-HostReachability")]
|
||||
|
||||
assert "$applyTransport -and $applyTransport.ok" in function
|
||||
assert '$applyReceipt.durableReceiptTerminal -eq "verified_healthy"' in function
|
||||
assert "$applyReceipt.managerIndependentVerifierReady" in function
|
||||
assert "$applyReceipt.managerRuntimeWritePerformed" in function
|
||||
assert '[string]$applyReceipt.managerAttemptCount -eq "1"' in function
|
||||
assert "$durableReceiptReadback.receiptPath -eq $applyReceipt.receiptPath" in function
|
||||
assert "$durableReceiptReadback.receiptSha256 -eq $applyReceipt.receiptSha256" in function
|
||||
assert "$durableReceiptReadback.durableReceiptTerminal -eq $applyReceipt.durableReceiptTerminal" in function
|
||||
assert '$durableReceiptReadback.durableReceiptTerminal -eq "verified_healthy"' in function
|
||||
assert "$afterManagerVerifierVerified" in function
|
||||
assert 'afterVerifierRole = "independent_second_verifier_only"' in function
|
||||
assert "$after.ready -and" in function
|
||||
assert "$managerApplyVerified" in function
|
||||
assert "existingReceiptTerminalAccepted" in function
|
||||
assert "immutable_receipt_replay_not_successful" in function
|
||||
assert "host112_failed_apply_cannot_be_washed_green" in read(
|
||||
ROOT / "agent99-synthetic-tests.ps1"
|
||||
)
|
||||
|
||||
|
||||
def test_agent99_uses_correlated_sudo_dry_run_over_canonical_direct_key() -> None:
|
||||
control = read(CONTROL)
|
||||
|
||||
assert "function Invoke-AgentHost112SshText" in control
|
||||
assert 'expectedUser = "kali"' in control
|
||||
assert 'route = "direct_host112_kali"' in control
|
||||
assert "Invoke-SshText -TargetHost $transportContract.targetHost" in control
|
||||
assert "--dry-run --trace-id {0} --run-id {1} --work-item-id {2}" in control
|
||||
assert "Invoke-AgentHost112SshText $dryRunCommand" in control
|
||||
assert "$dryRun.identityMatches" in control
|
||||
assert "$dryRun.managerPreflightPresent" in control
|
||||
assert "$dryRun.transportOk" in control
|
||||
assert "-not $dryRun.durableReceiptPresent" in control
|
||||
|
||||
|
||||
def test_unhealthy_executor_exit_one_is_delivered_readback_not_transport_failure() -> None:
|
||||
control = read(CONTROL)
|
||||
function = control[control.index("function Invoke-AgentHost112GuestRecovery") :]
|
||||
function = function[: function.index("function Test-HostReachability")]
|
||||
synthetic = read(ROOT / "agent99-synthetic-tests.ps1")
|
||||
|
||||
assert function.count('case "$executor_exit" in 0|1) exit 0') == 3
|
||||
assert "$before.executorExitCode -in @(\"0\", \"1\")" in function
|
||||
assert "$before.readbackPresent -and" in function
|
||||
assert "$before.identityMatches -and" in function
|
||||
assert '$beforeTransport = Invoke-AgentHost112SshText $checkCommand' in function
|
||||
assert 'New-Host112SyntheticRow "check" 0 0 "none" 0 "not_requested" 0 "unknown" 1' in synthetic
|
||||
assert "host112_general_candidate_not_blocked_by_healthy_manager" in synthetic
|
||||
|
||||
|
||||
def test_general_guest_candidate_is_not_blocked_by_manager_candidate() -> None:
|
||||
control = read(CONTROL)
|
||||
function = control[control.index("function Invoke-AgentHost112GuestRecovery") :]
|
||||
function = function[: function.index("function Test-HostReachability")]
|
||||
synthetic = read(ROOT / "agent99-synthetic-tests.ps1")
|
||||
|
||||
assert "$generalCandidateRequired = [bool]($beforeVerified -and -not $before.generalReady)" in function
|
||||
assert "$managerCandidateRequired = [bool]($beforeVerified -and -not $before.managerVerified)" in function
|
||||
assert "$generalApplyEligible = [bool]($generalCandidateRequired -and $generalDryRunVerified)" in function
|
||||
assert "$managerApplyEligible = [bool]($managerCandidateRequired -and $managerDryRunVerified)" in function
|
||||
assert "$applyEligible = [bool]($generalApplyEligible -or $managerApplyEligible)" in function
|
||||
assert "managerNoAttemptPreserved" in function
|
||||
assert "host112_general_candidate_not_blocked_by_healthy_manager" in synthetic
|
||||
assert '-not $generalOnly.managerCandidateRequired' in synthetic
|
||||
assert '[string]$generalOnly.applyReceipt.managerAttemptCount -eq "0"' in synthetic
|
||||
assert '-not $generalOnly.applyReceipt.managerRuntimeWritePerformed' in synthetic
|
||||
|
||||
|
||||
def test_deploy_and_bootstrap_canonicalize_host112_direct_transport() -> None:
|
||||
deploy = read(ROOT / "agent99-deploy.ps1")
|
||||
bootstrap = read(BOOTSTRAP)
|
||||
generic_config = read(ROOT / "agent99.config.example.json")
|
||||
host_config = read(ROOT / "agent99.config.99.example.json")
|
||||
|
||||
assert 'Set-AgentProperty $config.sshUsers "192.168.0.112" "kali"' in deploy
|
||||
assert 'name = "host112_canonical_direct_route"' in deploy
|
||||
assert 'Host112 canonical direct SSH config post-verifier failed.' in deploy
|
||||
assert "Ensure-AgentHost112CanonicalSshConfig" in bootstrap
|
||||
assert 'Set-AgentBootstrapProperty $config.sshUsers "192.168.0.112" "kali"' in bootstrap
|
||||
assert 'Host112 canonical direct SSH config bootstrap verifier failed.' in bootstrap
|
||||
for config in (generic_config, host_config):
|
||||
assert '"192.168.0.112": "kali"' in config
|
||||
assert '"directHosts"' in config
|
||||
assert '"192.168.0.112"' in config
|
||||
|
||||
Reference in New Issue
Block a user