Merge remote-tracking branch 'gitea-ssh/main' into codex/p0-b-incident-closure-20260722
All checks were successful
CD Pipeline / select-latest-carrier (push) Successful in 50s
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m42s
CD Pipeline / revalidate-deploy-carrier (push) Successful in 40s
CD Pipeline / build-and-deploy (push) Successful in 14m0s
CD Pipeline / revalidate-post-deploy-carrier (push) Successful in 27s
CD Pipeline / post-deploy-checks (push) Successful in 2m26s

This commit is contained in:
Your Name
2026-07-22 17:35:26 +08:00
4 changed files with 77 additions and 15 deletions

View File

@@ -9580,6 +9580,9 @@ async def claim_pending_check_modes(
)
ORDER BY
CASE
WHEN candidate.input ->> 'work_item_id'
= 'P0-03-WAZUH-MANAGER-POSTURE'
THEN -1
WHEN candidate.input ->> 'work_item_id' LIKE 'P0-%'
THEN 0
WHEN candidate.input ->> 'proposal_source'
@@ -12022,6 +12025,18 @@ async def run_pending_check_modes_once(
0,
remaining_limit - len(wazuh_capability_retry_claims),
)
fresh_claims = (
await claim_pending_check_modes(
project_id=project_id,
limit=min(1, remaining_limit),
candidate_max_age_hours=(
settings.AWOOOP_ANSIBLE_CHECK_MODE_CANDIDATE_MAX_AGE_HOURS
),
)
if remaining_limit
else []
)
remaining_limit = max(0, remaining_limit - len(fresh_claims))
reclaimed_claims = (
await claim_stale_pending_check_modes(
project_id=project_id,
@@ -12123,25 +12138,14 @@ async def run_pending_check_modes_once(
fresh_candidate_claim_continues=True,
)
remaining_limit = max(0, remaining_limit - len(catalog_replay_claims))
fresh_claims = (
await claim_pending_check_modes(
project_id=project_id,
limit=remaining_limit,
candidate_max_age_hours=(
settings.AWOOOP_ANSIBLE_CHECK_MODE_CANDIDATE_MAX_AGE_HOURS
),
)
if remaining_limit
else []
)
claims = [
*wazuh_capability_retry_claims,
*fresh_claims,
*reclaimed_claims,
*stdin_boundary_replay_claims,
*semantic_catalog_reconciliation_claims,
*failed_apply_catalog_replay_claims,
*catalog_replay_claims,
*fresh_claims,
]
execution_claims = [*wazuh_break_glass_apply_claims, *claims]
completed = 0

View File

@@ -3295,6 +3295,11 @@ async def test_execution_broker_stdin_replay_never_enters_apply(
"claim_stale_pending_check_modes",
no_claims,
)
monkeypatch.setattr(
service,
"claim_pending_check_modes",
no_claims,
)
monkeypatch.setattr(
service,
"claim_stdin_boundary_failed_check_modes",
@@ -4139,15 +4144,27 @@ def test_ansible_claim_query_limits_recent_candidate_backlog() -> None:
def test_ansible_claim_query_honors_bounded_execution_priority_before_fifo() -> None:
source = inspect.getsource(claim_pending_check_modes)
assert "P0-03-WAZUH-MANAGER-POSTURE" in source
assert "candidate.input ->> 'work_item_id' LIKE 'P0-%'" in source
assert "alert_webhook_controlled_router" in source
assert "ELSE 30" in source
assert "candidate.input ->> 'execution_priority'" in source
assert "LEAST(" in source
assert "100" in source
assert source.index("P0-03-WAZUH-MANAGER-POSTURE") < source.index(
"candidate.input ->> 'work_item_id' LIKE 'P0-%'"
)
assert source.index("execution_priority") < source.index("candidate.created_at ASC")
def test_broker_claims_fresh_p0_before_general_replay_lanes() -> None:
source = inspect.getsource(run_pending_check_modes_once)
assert source.index("fresh_claims = (") < source.index("reclaimed_claims = (")
assert "limit=min(1, remaining_limit)" in source
assert source.index("*fresh_claims,") < source.index("*reclaimed_claims,")
def test_ansible_transport_blocker_detects_repair_forced_command_denial() -> None:
blockers = detect_ansible_transport_blockers(
"fatal: host unreachable REPAIR_DENIED:invalid_command",

View File

@@ -5,8 +5,10 @@ param(
[Parameter(Mandatory = $true)]
[ValidatePattern("^agent99-control-loop-maintenance-2[0-9]{7}-[0-9]{6}-[0-9]{3}-[0-9a-f]{8}\.json$")]
[string]$MaintenanceReceiptRef,
[ValidateRange(1, 60)]
[ValidateRange(1, 180)]
[int]$MaxReceiptAgeMinutes = 30,
[ValidatePattern("^(?:|[0-9a-f]{64})$")]
[string]$ExpectedMaintenanceReceiptSha256 = "",
[string]$AgentRoot = "C:\Wooo\Agent99"
)
@@ -155,13 +157,33 @@ try {
$blockers.Add("maintenance_receipt_missing")
} else {
try {
$maintenance = Get-Content -LiteralPath $maintenancePath -Raw | ConvertFrom-Json
$maintenanceSha256 = (Get-FileHash -LiteralPath $maintenancePath -Algorithm SHA256).Hash.ToLowerInvariant()
$maintenanceBytes = [IO.File]::ReadAllBytes($maintenancePath)
$sha256 = [Security.Cryptography.SHA256]::Create()
try {
$maintenanceSha256 = ([BitConverter]::ToString($sha256.ComputeHash($maintenanceBytes))).Replace("-", "").ToLowerInvariant()
} finally {
$sha256.Dispose()
}
$strictUtf8 = New-Object Text.UTF8Encoding($false, $true)
$maintenanceJson = $strictUtf8.GetString($maintenanceBytes)
$maintenance = $maintenanceJson | ConvertFrom-Json
if ($null -eq $maintenance -or $maintenance -isnot [pscustomobject]) {
throw "maintenance_receipt_shape_invalid"
}
$maintenanceAgeMinutes = ((Get-Date) - (Get-Item -LiteralPath $maintenancePath).LastWriteTime).TotalMinutes
} catch {
$blockers.Add("maintenance_receipt_invalid")
}
}
if ($MaxReceiptAgeMinutes -gt 60 -and [string]::IsNullOrWhiteSpace($ExpectedMaintenanceReceiptSha256)) {
$blockers.Add("extended_receipt_window_requires_exact_sha256")
}
if (
-not [string]::IsNullOrWhiteSpace($ExpectedMaintenanceReceiptSha256) -and
-not [string]::Equals($maintenanceSha256, $ExpectedMaintenanceReceiptSha256, [StringComparison]::Ordinal)
) {
$blockers.Add("maintenance_receipt_sha256_mismatch")
}
if ($maintenance) {
if (
[string]$maintenance.schemaVersion -ne "agent99_control_loop_maintenance_v2" -or
@@ -191,6 +213,9 @@ try {
$intentPath = Join-Path $evidenceDir $intentRef
try {
$maintenanceIntent = Get-Content -LiteralPath $intentPath -Raw -ErrorAction Stop | ConvertFrom-Json
if ($null -eq $maintenanceIntent -or $maintenanceIntent -isnot [pscustomobject]) {
throw "maintenance_intent_shape_invalid"
}
} catch {
$blockers.Add("maintenance_intent_invalid")
}
@@ -257,6 +282,8 @@ try {
taskName = $taskName
maintenanceReceiptRef = $MaintenanceReceiptRef
maintenanceReceiptSha256 = $maintenanceSha256
maxReceiptAgeMinutes = $MaxReceiptAgeMinutes
extendedReceiptWindow = [bool]($MaxReceiptAgeMinutes -gt 60)
plannedActions = @("enable_exact_control_task", "start_exact_control_task", "verify_control_task_started")
rollback = "stop and disable the exact control task if postcheck fails"
vmPowerChangePerformed = $false
@@ -310,6 +337,8 @@ try {
maintenanceReceiptRef = $MaintenanceReceiptRef
maintenanceReceiptSha256 = $maintenanceSha256
maintenanceAgeMinutes = if ($null -ne $maintenanceAgeMinutes) { [math]::Round($maintenanceAgeMinutes, 2) } else { $null }
maxReceiptAgeMinutes = $MaxReceiptAgeMinutes
extendedReceiptWindow = [bool]($MaxReceiptAgeMinutes -gt 60)
precheckPassed = $precheckPassed
blockers = @($blockers)
operationIntentRef = $operationIntentRef

View File

@@ -191,6 +191,18 @@ def test_control_loop_maintenance_restore_is_bounded_and_receipted() -> None:
assert 'if ($env:COMPUTERNAME -ne "WOOO-SUPER")' in source
assert "Global\\WoooAgent99ControlLoopMaintenanceV1" in source
assert 'ValidateSet("Check", "Apply")' in source
assert "ValidateRange(1, 180)" in source
assert "ExpectedMaintenanceReceiptSha256" in source
assert "extended_receipt_window_requires_exact_sha256" in source
assert "maintenance_receipt_sha256_mismatch" in source
assert "[IO.File]::ReadAllBytes($maintenancePath)" in source
assert "ComputeHash($maintenanceBytes)" in source
assert "maintenance_receipt_shape_invalid" in source
assert "maintenance_intent_shape_invalid" in source
assert "Get-Content -LiteralPath $maintenancePath" not in source
extended_guard = source.index("extended_receipt_window_requires_exact_sha256")
maintenance_contract = source.index('schemaVersion -ne "agent99_control_loop_maintenance_v2"')
assert extended_guard < maintenance_contract
assert "maintenanceReceiptSha256" in source
assert 'schemaVersion -ne "agent99_control_loop_maintenance_v2"' in source
assert 'terminal -ne "control_loop_maintenance_frozen"' in source