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
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:
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user