fix(wazuh): persist critical capability packets
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m28s
CD Pipeline / build-and-deploy (push) Successful in 15m8s
CD Pipeline / post-deploy-checks (push) Successful in 2m18s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m28s
CD Pipeline / build-and-deploy (push) Successful in 15m8s
CD Pipeline / post-deploy-checks (push) Successful in 2m18s
This commit is contained in:
@@ -77,6 +77,15 @@ _EXECUTION_CAPABILITY_SAFETY_MARGIN_SECONDS = 5
|
||||
_WORKING_MEMORY_PROJECTION_WINDOW_HOURS = 8 * 24
|
||||
_WORKING_MEMORY_PROJECTION_CURSOR: dict[str, tuple[datetime, str]] = {}
|
||||
_EXECUTION_CAPABILITY_FALLBACK_OPERATION_TYPE = "remediation_executed"
|
||||
_WAZUH_ALERTMANAGER_INTEGRATION_CATALOG_ID = (
|
||||
"ansible:wazuh-alertmanager-integration"
|
||||
)
|
||||
_WAZUH_PRIVILEGED_CONVERGENCE_CAPABILITY_MISSING = (
|
||||
"wazuh_privileged_convergence_capability_missing"
|
||||
)
|
||||
_WAZUH_BREAK_GLASS_CAPABILITY_PACKET_OPERATION_TYPE = (
|
||||
"ansible_break_glass_capability_work_item_queued"
|
||||
)
|
||||
_EXECUTION_CAPABILITY_OPERATION_TYPES = frozenset(
|
||||
{
|
||||
"ansible_executor_capability_issued",
|
||||
@@ -6135,6 +6144,9 @@ async def backfill_missing_auto_repair_execution_receipts_once(
|
||||
"retry_terminal_projection_lifecycle_written": 0,
|
||||
"retry_terminal_projection_verified": 0,
|
||||
"retry_terminal_projection_runtime_apply_executed": False,
|
||||
"critical_break_glass_packet_scanned": 0,
|
||||
"critical_break_glass_packet_written": 0,
|
||||
"critical_break_glass_packet_error": None,
|
||||
"skipped": 0,
|
||||
"error": None,
|
||||
}
|
||||
@@ -6399,6 +6411,22 @@ async def backfill_missing_auto_repair_execution_receipts_once(
|
||||
stats["telegram_receipt_acknowledged"] += 1
|
||||
if closure.get("closed") is True:
|
||||
stats["incident_closure_written"] += 1
|
||||
break_glass_packet = (
|
||||
await backfill_missing_wazuh_break_glass_capability_packets_once(
|
||||
project_id=project_id,
|
||||
window_hours=window_hours,
|
||||
limit=max(1, min(limit, 5)),
|
||||
)
|
||||
)
|
||||
stats["critical_break_glass_packet_scanned"] = int(
|
||||
break_glass_packet.get("scanned") or 0
|
||||
)
|
||||
stats["critical_break_glass_packet_written"] = int(
|
||||
break_glass_packet.get("written") or 0
|
||||
)
|
||||
stats["critical_break_glass_packet_error"] = (
|
||||
str(break_glass_packet.get("error") or "")[:500] or None
|
||||
)
|
||||
except Exception as exc:
|
||||
stats["error"] = f"{type(exc).__name__}: {exc}"[:500]
|
||||
logger.warning("ansible_auto_repair_execution_receipt_backfill_failed", **stats)
|
||||
@@ -8603,6 +8631,175 @@ async def _insert_skipped_candidate(
|
||||
)
|
||||
|
||||
|
||||
def _wazuh_privileged_convergence_capability_is_missing(
|
||||
claim: AnsibleCheckModeClaim,
|
||||
result: AnsibleRunResult,
|
||||
) -> bool:
|
||||
if (
|
||||
claim.catalog_id != _WAZUH_ALERTMANAGER_INTEGRATION_CATALOG_ID
|
||||
or result.returncode == 0
|
||||
):
|
||||
return False
|
||||
return any(
|
||||
_WAZUH_PRIVILEGED_CONVERGENCE_CAPABILITY_MISSING in value
|
||||
for value in (result.stdout, result.stderr)
|
||||
)
|
||||
|
||||
|
||||
def _build_wazuh_break_glass_capability_packet(
|
||||
*,
|
||||
check_op_id: str,
|
||||
source_candidate_op_id: str,
|
||||
incident_id: str,
|
||||
catalog_id: str,
|
||||
source_input: Mapping[str, Any],
|
||||
) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]:
|
||||
automation_run_id = str(
|
||||
source_input.get("automation_run_id") or source_candidate_op_id
|
||||
).strip()[:180]
|
||||
trace_id = str(
|
||||
source_input.get("trace_id") or automation_run_id
|
||||
).strip()[:180]
|
||||
run_id = str(
|
||||
source_input.get("run_id") or automation_run_id
|
||||
).strip()[:180]
|
||||
work_item_id = str(
|
||||
source_input.get("work_item_id") or "P0-03-WAZUH-ALERT-INGRESS"
|
||||
).strip()[:180]
|
||||
source_recurrence = source_input.get("source_recurrence")
|
||||
if not isinstance(source_recurrence, Mapping):
|
||||
source_recurrence = {}
|
||||
packet_uuid = uuid5(
|
||||
NAMESPACE_URL,
|
||||
f"awoooi:wazuh-break-glass:{automation_run_id}:{check_op_id}",
|
||||
)
|
||||
capability_packet_id = f"IWZ-BG-{packet_uuid.hex[:16].upper()}"
|
||||
packet_input = {
|
||||
"schema_version": "wazuh_privileged_convergence_capability_packet_v1",
|
||||
"capability_packet_id": capability_packet_id,
|
||||
"automation_run_id": automation_run_id,
|
||||
"trace_id": trace_id,
|
||||
"run_id": run_id,
|
||||
"work_item_id": work_item_id,
|
||||
"project_id": str(source_input.get("project_id") or "")[:180],
|
||||
"run_namespace": str(
|
||||
source_input.get("run_namespace") or ""
|
||||
)[:180],
|
||||
"proposal_source": str(
|
||||
source_input.get("proposal_source") or ""
|
||||
)[:180],
|
||||
"source_receipt_ref": str(
|
||||
source_input.get("source_receipt_ref") or ""
|
||||
)[:180],
|
||||
"source_recurrence": dict(source_recurrence),
|
||||
"incident_id": str(incident_id or "").strip()[:180],
|
||||
"source_candidate_op_id": str(source_candidate_op_id),
|
||||
"source_check_mode_op_id": str(check_op_id),
|
||||
"catalog_id": str(catalog_id),
|
||||
"capability_type": "bounded_privileged_convergence",
|
||||
"risk_level": "critical",
|
||||
"policy_route": "critical_break_glass_queue",
|
||||
"queue_owner": "ai_critical_capability_controller",
|
||||
"same_run_correlation_required": True,
|
||||
"execution_allowed": False,
|
||||
"host_write_performed": False,
|
||||
"secret_value_exposed": False,
|
||||
"secret_value_logged": False,
|
||||
"secret_value_read_back": False,
|
||||
}
|
||||
packet_output = {
|
||||
"queue_status": "queued",
|
||||
"failure_class": _WAZUH_PRIVILEGED_CONVERGENCE_CAPABILITY_MISSING,
|
||||
"required_receipts": [
|
||||
"capability_reference_presence",
|
||||
"no_secret_privilege_preflight",
|
||||
"bounded_check_mode_retry",
|
||||
"independent_post_verifier",
|
||||
"capability_revocation",
|
||||
],
|
||||
"next_controlled_action": (
|
||||
"verify_bounded_privileged_capability_then_"
|
||||
"requeue_same_run_check_mode"
|
||||
),
|
||||
"telegram_notification_emitted": False,
|
||||
}
|
||||
dry_run_result = {
|
||||
"capability_packet_persisted": True,
|
||||
"capability_granted": False,
|
||||
"check_mode_replayed": False,
|
||||
"apply_executed": False,
|
||||
"host_write_performed": False,
|
||||
"secret_value_exposed": False,
|
||||
"secret_value_logged": False,
|
||||
"secret_value_read_back": False,
|
||||
}
|
||||
return packet_input, packet_output, dry_run_result
|
||||
|
||||
|
||||
async def _insert_wazuh_break_glass_capability_packet(
|
||||
db: Any,
|
||||
*,
|
||||
check_op_id: str,
|
||||
source_candidate_op_id: str,
|
||||
incident_id: str,
|
||||
catalog_id: str,
|
||||
source_input: Mapping[str, Any],
|
||||
) -> bool:
|
||||
packet_input, packet_output, dry_run_result = (
|
||||
_build_wazuh_break_glass_capability_packet(
|
||||
check_op_id=check_op_id,
|
||||
source_candidate_op_id=source_candidate_op_id,
|
||||
incident_id=incident_id,
|
||||
catalog_id=catalog_id,
|
||||
source_input=source_input,
|
||||
)
|
||||
)
|
||||
inserted = await db.execute(
|
||||
text(f"""
|
||||
INSERT INTO automation_operation_log (
|
||||
operation_type, actor, status, incident_id,
|
||||
input, output, dry_run_result, parent_op_id, tags
|
||||
)
|
||||
SELECT
|
||||
'{_WAZUH_BREAK_GLASS_CAPABILITY_PACKET_OPERATION_TYPE}',
|
||||
'ansible_check_mode_worker',
|
||||
'pending',
|
||||
:incident_db_id,
|
||||
CAST(:input AS jsonb),
|
||||
CAST(:output AS jsonb),
|
||||
CAST(:dry_run_result AS jsonb),
|
||||
CAST(:parent_op_id AS uuid),
|
||||
ARRAY[
|
||||
'ansible', 'wazuh', 'critical_break_glass',
|
||||
'capability_packet', 'no_host_write', 'no_secret'
|
||||
]::varchar[]
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM automation_operation_log existing
|
||||
WHERE existing.operation_type =
|
||||
'{_WAZUH_BREAK_GLASS_CAPABILITY_PACKET_OPERATION_TYPE}'
|
||||
AND existing.input ->> 'capability_packet_id'
|
||||
= :capability_packet_id
|
||||
)
|
||||
RETURNING op_id::text
|
||||
"""),
|
||||
{
|
||||
"incident_db_id": _automation_operation_log_incident_id(
|
||||
incident_id
|
||||
),
|
||||
"input": json.dumps(packet_input, ensure_ascii=False),
|
||||
"output": json.dumps(packet_output, ensure_ascii=False),
|
||||
"dry_run_result": json.dumps(
|
||||
dry_run_result,
|
||||
ensure_ascii=False,
|
||||
),
|
||||
"parent_op_id": check_op_id,
|
||||
"capability_packet_id": packet_input["capability_packet_id"],
|
||||
},
|
||||
)
|
||||
return inserted.scalar_one_or_none() is not None
|
||||
|
||||
|
||||
async def finalize_check_mode_claim(
|
||||
claim: AnsibleCheckModeClaim,
|
||||
result: AnsibleRunResult,
|
||||
@@ -8661,6 +8858,140 @@ async def finalize_check_mode_claim(
|
||||
"op_id": claim.op_id,
|
||||
},
|
||||
)
|
||||
if _wazuh_privileged_convergence_capability_is_missing(claim, result):
|
||||
await _insert_wazuh_break_glass_capability_packet(
|
||||
db,
|
||||
check_op_id=claim.op_id,
|
||||
source_candidate_op_id=claim.source_candidate_op_id,
|
||||
incident_id=claim.incident_id,
|
||||
catalog_id=claim.catalog_id,
|
||||
source_input=claim.input_payload,
|
||||
)
|
||||
|
||||
|
||||
async def backfill_missing_wazuh_break_glass_capability_packets_once(
|
||||
*,
|
||||
project_id: str = "awoooi",
|
||||
window_hours: int = 24,
|
||||
limit: int = 5,
|
||||
) -> dict[str, Any]:
|
||||
"""Persist safe critical-capability packets for earlier failed Wazuh checks."""
|
||||
|
||||
stats: dict[str, Any] = {"scanned": 0, "written": 0, "error": None}
|
||||
try:
|
||||
async with get_db_context(project_id) as db:
|
||||
await db.execute(text("SET LOCAL statement_timeout = '3000ms'"))
|
||||
result = await db.execute(
|
||||
text(f"""
|
||||
SELECT
|
||||
check_mode.op_id::text AS check_op_id,
|
||||
candidate.op_id::text AS source_candidate_op_id,
|
||||
coalesce(
|
||||
check_mode.input ->> 'incident_id',
|
||||
candidate.input ->> 'incident_id',
|
||||
check_mode.incident_id::text
|
||||
) AS incident_id,
|
||||
coalesce(
|
||||
candidate.input ->> 'automation_run_id',
|
||||
candidate.op_id::text
|
||||
) AS automation_run_id,
|
||||
coalesce(
|
||||
candidate.input ->> 'trace_id',
|
||||
candidate.input ->> 'automation_run_id',
|
||||
candidate.op_id::text
|
||||
) AS trace_id,
|
||||
coalesce(
|
||||
candidate.input ->> 'run_id',
|
||||
candidate.input ->> 'automation_run_id',
|
||||
candidate.op_id::text
|
||||
) AS run_id,
|
||||
candidate.input ->> 'work_item_id' AS work_item_id,
|
||||
candidate.input ->> 'project_id' AS project_id,
|
||||
candidate.input ->> 'run_namespace' AS run_namespace,
|
||||
candidate.input ->> 'proposal_source' AS proposal_source,
|
||||
candidate.input ->> 'source_receipt_ref'
|
||||
AS source_receipt_ref,
|
||||
candidate.input -> 'source_recurrence'
|
||||
AS source_recurrence
|
||||
FROM automation_operation_log check_mode
|
||||
JOIN automation_operation_log candidate
|
||||
ON candidate.op_id = check_mode.parent_op_id
|
||||
WHERE check_mode.operation_type =
|
||||
'ansible_check_mode_executed'
|
||||
AND check_mode.status = 'failed'
|
||||
AND check_mode.created_at >= NOW() - (
|
||||
:window_hours * INTERVAL '1 hour'
|
||||
)
|
||||
AND candidate.operation_type =
|
||||
'ansible_candidate_matched'
|
||||
AND candidate.input -> 'executor_candidates'
|
||||
@> CAST(:catalog_match AS jsonb)
|
||||
AND concat_ws(
|
||||
' ',
|
||||
check_mode.error,
|
||||
check_mode.output ->> 'stdout_tail',
|
||||
check_mode.output ->> 'stderr_tail',
|
||||
check_mode.dry_run_result ->> 'stdout_tail',
|
||||
check_mode.dry_run_result ->> 'stderr_tail'
|
||||
) LIKE ('%' || :failure_marker || '%')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM automation_operation_log packet
|
||||
WHERE packet.operation_type =
|
||||
'{_WAZUH_BREAK_GLASS_CAPABILITY_PACKET_OPERATION_TYPE}'
|
||||
AND packet.parent_op_id = check_mode.op_id
|
||||
)
|
||||
ORDER BY check_mode.created_at DESC
|
||||
LIMIT :limit
|
||||
"""),
|
||||
{
|
||||
"window_hours": max(1, int(window_hours)),
|
||||
"limit": max(1, min(int(limit), 20)),
|
||||
"catalog_match": json.dumps(
|
||||
[{
|
||||
"catalog_id": (
|
||||
_WAZUH_ALERTMANAGER_INTEGRATION_CATALOG_ID
|
||||
)
|
||||
}]
|
||||
),
|
||||
"failure_marker": (
|
||||
_WAZUH_PRIVILEGED_CONVERGENCE_CAPABILITY_MISSING
|
||||
),
|
||||
},
|
||||
)
|
||||
rows = [dict(row) for row in result.mappings().all()]
|
||||
stats["scanned"] = len(rows)
|
||||
for row in rows:
|
||||
source_input = {
|
||||
"automation_run_id": row.get("automation_run_id"),
|
||||
"trace_id": row.get("trace_id"),
|
||||
"run_id": row.get("run_id"),
|
||||
"work_item_id": row.get("work_item_id"),
|
||||
"project_id": row.get("project_id"),
|
||||
"run_namespace": row.get("run_namespace"),
|
||||
"proposal_source": row.get("proposal_source"),
|
||||
"source_receipt_ref": row.get("source_receipt_ref"),
|
||||
"source_recurrence": row.get("source_recurrence"),
|
||||
}
|
||||
if await _insert_wazuh_break_glass_capability_packet(
|
||||
db,
|
||||
check_op_id=str(row.get("check_op_id") or ""),
|
||||
source_candidate_op_id=str(
|
||||
row.get("source_candidate_op_id") or ""
|
||||
),
|
||||
incident_id=str(row.get("incident_id") or ""),
|
||||
catalog_id=_WAZUH_ALERTMANAGER_INTEGRATION_CATALOG_ID,
|
||||
source_input=source_input,
|
||||
):
|
||||
stats["written"] += 1
|
||||
except Exception as exc:
|
||||
stats["error"] = f"{type(exc).__name__}: {exc}"[:500]
|
||||
logger.warning(
|
||||
"wazuh_break_glass_capability_packet_backfill_failed",
|
||||
project_id=project_id,
|
||||
**stats,
|
||||
)
|
||||
return stats
|
||||
|
||||
|
||||
async def run_claimed_check_mode(
|
||||
@@ -9144,6 +9475,18 @@ async def run_pending_check_modes_once(
|
||||
receipt_stats.get("retry_terminal_projection_verified") or 0
|
||||
),
|
||||
"retry_terminal_projection_runtime_apply_executed": False,
|
||||
"critical_break_glass_packet_scanned": int(
|
||||
receipt_stats.get("critical_break_glass_packet_scanned") or 0
|
||||
),
|
||||
"critical_break_glass_packet_written": int(
|
||||
receipt_stats.get("critical_break_glass_packet_written") or 0
|
||||
),
|
||||
"critical_break_glass_packet_error": (
|
||||
str(
|
||||
receipt_stats.get("critical_break_glass_packet_error") or ""
|
||||
)[:500]
|
||||
or None
|
||||
),
|
||||
"repair_receipt_backfill_error": (
|
||||
str(receipt_stats.get("error") or "")[:500] or None
|
||||
),
|
||||
|
||||
@@ -130,6 +130,45 @@ _LIVE_RUNTIME_SQL = """
|
||||
WHERE apply.operation_type = 'ansible_apply_executed'
|
||||
ORDER BY apply.created_at DESC
|
||||
LIMIT 1
|
||||
), latest_break_glass_packet AS (
|
||||
SELECT
|
||||
packet.op_id::text AS break_glass_packet_receipt_id,
|
||||
packet.status AS break_glass_packet_status,
|
||||
packet.input ->> 'capability_packet_id' AS capability_packet_id,
|
||||
packet.input ->> 'automation_run_id'
|
||||
AS capability_automation_run_id,
|
||||
packet.input ->> 'trace_id' AS capability_trace_id,
|
||||
packet.input ->> 'run_id' AS capability_run_id,
|
||||
packet.input ->> 'work_item_id' AS capability_work_item_id,
|
||||
packet.input ->> 'project_id' AS capability_project_id,
|
||||
packet.input ->> 'run_namespace' AS capability_run_namespace,
|
||||
packet.input ->> 'proposal_source' AS capability_proposal_source,
|
||||
packet.input -> 'source_recurrence'
|
||||
AS capability_source_recurrence,
|
||||
packet.input ->> 'capability_type' AS capability_type,
|
||||
packet.input ->> 'risk_level' AS capability_risk_level,
|
||||
packet.input ->> 'policy_route' AS capability_policy_route,
|
||||
packet.output ->> 'queue_status' AS capability_queue_status,
|
||||
coalesce(
|
||||
(packet.input ->> 'execution_allowed')::boolean,
|
||||
false
|
||||
) AS capability_execution_allowed,
|
||||
coalesce(
|
||||
(packet.dry_run_result ->> 'capability_granted')::boolean,
|
||||
false
|
||||
) AS capability_granted,
|
||||
coalesce(
|
||||
(packet.dry_run_result ->> 'host_write_performed')::boolean,
|
||||
false
|
||||
) AS capability_packet_host_write_performed,
|
||||
packet.created_at AS break_glass_packet_created_at
|
||||
FROM automation_operation_log packet
|
||||
JOIN latest_check check_mode
|
||||
ON packet.parent_op_id::text = check_mode.check_op_id
|
||||
WHERE packet.operation_type =
|
||||
'ansible_break_glass_capability_work_item_queued'
|
||||
ORDER BY packet.created_at DESC
|
||||
LIMIT 1
|
||||
), runtime_stages AS (
|
||||
SELECT
|
||||
coalesce(
|
||||
@@ -239,6 +278,7 @@ _LIVE_RUNTIME_SQL = """
|
||||
SELECT
|
||||
candidate.*,
|
||||
check_mode.*,
|
||||
break_glass_packet.*,
|
||||
apply.*,
|
||||
runtime_stages.stage_ids,
|
||||
runtime_stages.runtime_stage_identity_verified,
|
||||
@@ -249,6 +289,7 @@ _LIVE_RUNTIME_SQL = """
|
||||
telegram.*
|
||||
FROM latest_candidate candidate
|
||||
LEFT JOIN latest_check check_mode ON TRUE
|
||||
LEFT JOIN latest_break_glass_packet break_glass_packet ON TRUE
|
||||
LEFT JOIN latest_apply apply ON TRUE
|
||||
LEFT JOIN runtime_stages ON TRUE
|
||||
LEFT JOIN latest_verifier verifier ON TRUE
|
||||
@@ -416,6 +457,43 @@ def build_iwooos_wazuh_controlled_executor_runtime_readback(
|
||||
)
|
||||
check_failed = data.get("check_status") == "failed"
|
||||
check_failure_class = _check_failure_class(data)
|
||||
break_glass_packet_receipt_present = bool(
|
||||
data.get("break_glass_packet_receipt_id")
|
||||
)
|
||||
capability_packet_id = str(data.get("capability_packet_id") or "")
|
||||
break_glass_packet_identity_verified = bool(
|
||||
break_glass_packet_receipt_present
|
||||
and candidate_identity_verified
|
||||
and check_identity_verified
|
||||
and str(data.get("capability_automation_run_id") or "")
|
||||
== candidate_op_id
|
||||
and str(data.get("capability_trace_id") or "") == candidate_op_id
|
||||
and str(data.get("capability_run_id") or "") == candidate_op_id
|
||||
and str(data.get("capability_work_item_id") or "")
|
||||
== expected_work_item_id
|
||||
and str(data.get("capability_project_id") or "").strip()
|
||||
and str(data.get("capability_run_namespace") or "")
|
||||
== expected_run_namespace
|
||||
and str(data.get("capability_proposal_source") or "")
|
||||
== expected_proposal_source
|
||||
and data.get("capability_source_recurrence")
|
||||
== candidate_source_recurrence
|
||||
)
|
||||
break_glass_packet_present = all(
|
||||
(
|
||||
break_glass_packet_identity_verified,
|
||||
data.get("break_glass_packet_status") == "pending",
|
||||
capability_packet_id.startswith("IWZ-BG-"),
|
||||
len(capability_packet_id) == 23,
|
||||
data.get("capability_type") == "bounded_privileged_convergence",
|
||||
data.get("capability_risk_level") == "critical",
|
||||
data.get("capability_policy_route") == "critical_break_glass_queue",
|
||||
data.get("capability_queue_status") == "queued",
|
||||
data.get("capability_execution_allowed") is False,
|
||||
data.get("capability_granted") is False,
|
||||
data.get("capability_packet_host_write_performed") is False,
|
||||
)
|
||||
)
|
||||
apply_terminal = data.get("apply_status") in {"success", "failed"}
|
||||
apply_passed = apply_terminal and str(
|
||||
data.get("apply_returncode") or ""
|
||||
@@ -495,6 +573,16 @@ def build_iwooos_wazuh_controlled_executor_runtime_readback(
|
||||
active_blockers.append("wazuh_controlled_check_mode_failed")
|
||||
if check_failure_class:
|
||||
active_blockers.append(check_failure_class)
|
||||
if check_failure_class == _PRIVILEGED_CONVERGENCE_CAPABILITY_MISSING:
|
||||
active_blockers.append(
|
||||
"critical_break_glass_capability_packet_pending"
|
||||
if break_glass_packet_present
|
||||
else (
|
||||
"critical_break_glass_capability_packet_identity_mismatch"
|
||||
if break_glass_packet_receipt_present
|
||||
else "critical_break_glass_capability_packet_missing"
|
||||
)
|
||||
)
|
||||
if apply_failed:
|
||||
active_blockers.append("wazuh_controlled_apply_failed")
|
||||
if apply_passed and not verifier_passed:
|
||||
@@ -536,6 +624,9 @@ def build_iwooos_wazuh_controlled_executor_runtime_readback(
|
||||
"dispatch_candidate_count": 1 if candidate_present else 0,
|
||||
"check_mode_passed_count": 1 if check_passed else 0,
|
||||
"check_mode_failed_count": 1 if check_failed else 0,
|
||||
"critical_break_glass_capability_packet_count": (
|
||||
1 if break_glass_packet_present else 0
|
||||
),
|
||||
"runtime_execution_performed_count": 1 if apply_terminal else 0,
|
||||
"bounded_posture_execution_passed_count": (
|
||||
1 if apply_passed and not ingress_mode else 0
|
||||
@@ -580,12 +671,36 @@ def build_iwooos_wazuh_controlled_executor_runtime_readback(
|
||||
],
|
||||
"missing_stage_ids": missing_stage_ids,
|
||||
"check_failure_class": check_failure_class,
|
||||
"critical_break_glass_capability_packet": (
|
||||
{
|
||||
"receipt_id": str(
|
||||
data.get("break_glass_packet_receipt_id") or ""
|
||||
),
|
||||
"capability_packet_id": str(
|
||||
capability_packet_id
|
||||
),
|
||||
"status": "pending",
|
||||
"queue_status": "queued",
|
||||
"capability_type": "bounded_privileged_convergence",
|
||||
"risk_level": "critical",
|
||||
"policy_route": "critical_break_glass_queue",
|
||||
"execution_allowed": False,
|
||||
"capability_granted": False,
|
||||
"host_write_performed": False,
|
||||
"secret_value_exposed": False,
|
||||
"secret_value_logged": False,
|
||||
"secret_value_read_back": False,
|
||||
}
|
||||
if break_glass_packet_present
|
||||
else None
|
||||
),
|
||||
"next_safe_action": _next_action(
|
||||
ingress_mode=ingress_mode,
|
||||
candidate_present=candidate_present,
|
||||
check_passed=check_passed,
|
||||
check_failed=check_failed,
|
||||
check_failure_class=check_failure_class,
|
||||
break_glass_packet_present=break_glass_packet_present,
|
||||
apply_terminal=apply_terminal,
|
||||
apply_passed=apply_passed,
|
||||
verifier_passed=verifier_passed,
|
||||
@@ -609,6 +724,17 @@ def build_iwooos_wazuh_controlled_executor_runtime_readback(
|
||||
"command_output_returned": False,
|
||||
"inventory_identity_returned": False,
|
||||
"secret_value_collection_allowed": False,
|
||||
"critical_break_glass_required": (
|
||||
check_failure_class
|
||||
== _PRIVILEGED_CONVERGENCE_CAPABILITY_MISSING
|
||||
),
|
||||
"critical_break_glass_capability_packet_persisted": (
|
||||
break_glass_packet_present
|
||||
),
|
||||
"critical_break_glass_capability_packet_identity_verified": (
|
||||
break_glass_packet_identity_verified
|
||||
),
|
||||
"critical_break_glass_capability_granted": False,
|
||||
"github_api_used": False,
|
||||
},
|
||||
"source_refs": [
|
||||
@@ -658,6 +784,7 @@ def _next_action(
|
||||
check_passed: bool,
|
||||
check_failed: bool,
|
||||
check_failure_class: str | None,
|
||||
break_glass_packet_present: bool,
|
||||
apply_terminal: bool,
|
||||
apply_passed: bool,
|
||||
verifier_passed: bool,
|
||||
@@ -684,6 +811,11 @@ def _next_action(
|
||||
check_failure_class
|
||||
== _PRIVILEGED_CONVERGENCE_CAPABILITY_MISSING
|
||||
):
|
||||
if break_glass_packet_present:
|
||||
return (
|
||||
"critical_break_glass_capability_packet_queued_waiting_"
|
||||
"bounded_capability_verifier_then_retry"
|
||||
)
|
||||
return (
|
||||
"queue_critical_break_glass_privileged_convergence_capability_"
|
||||
"then_bounded_retry"
|
||||
|
||||
@@ -19,6 +19,14 @@ os.environ.setdefault("DATABASE_URL", "postgresql+asyncpg://test:test@localhost/
|
||||
from src.api.v1 import iwooos
|
||||
from src.api.v1.iwooos import router
|
||||
from src.services.awooop_ansible_audit_service import get_ansible_catalog_item
|
||||
from src.services.awooop_ansible_check_mode_service import (
|
||||
AnsibleCheckModeClaim,
|
||||
AnsibleRunResult,
|
||||
_build_wazuh_break_glass_capability_packet,
|
||||
_wazuh_privileged_convergence_capability_is_missing,
|
||||
backfill_missing_wazuh_break_glass_capability_packets_once,
|
||||
finalize_check_mode_claim,
|
||||
)
|
||||
from src.services.awooop_ansible_post_verifier import postconditions_for_catalog
|
||||
from src.services.iwooos_wazuh_controlled_executor_runtime import (
|
||||
build_iwooos_wazuh_controlled_executor_runtime_readback,
|
||||
@@ -323,6 +331,94 @@ def test_wazuh_runtime_readback_projects_allowlisted_privilege_blocker() -> None
|
||||
)
|
||||
assert payload["boundaries"]["host_write_performed"] is False
|
||||
assert payload["boundaries"]["secret_value_collection_allowed"] is False
|
||||
assert payload["summary"][
|
||||
"critical_break_glass_capability_packet_count"
|
||||
] == 0
|
||||
assert payload["critical_break_glass_capability_packet"] is None
|
||||
assert "critical_break_glass_capability_packet_missing" in (
|
||||
payload["active_blockers"]
|
||||
)
|
||||
|
||||
|
||||
def test_wazuh_runtime_readback_projects_durable_break_glass_packet() -> None:
|
||||
run_id = "00000000-0000-0000-0000-000000000314"
|
||||
recurrence = _current_wazuh_source_recurrence(
|
||||
"2026-07-15T05:59:58+08:00"
|
||||
)
|
||||
row = {
|
||||
"candidate_op_id": run_id,
|
||||
"automation_run_id": run_id,
|
||||
"trace_id": run_id,
|
||||
"run_id": run_id,
|
||||
"candidate_project_id": "awoooi",
|
||||
"work_item_id": "P0-03-WAZUH-ALERT-INGRESS",
|
||||
"proposal_source": "iwooos_wazuh_alert_ingress_scheduler",
|
||||
"run_namespace": "awoooi:iwooos:wazuh-alert-ingress",
|
||||
"source_receipt_ref": "wazuh-alert:2026071500",
|
||||
"candidate_source_recurrence": recurrence,
|
||||
"candidate_source_recurrence_verified": True,
|
||||
"catalog_id": "ansible:wazuh-alertmanager-integration",
|
||||
"check_op_id": "00000000-0000-0000-0000-000000000313",
|
||||
"check_status": "failed",
|
||||
"check_returncode": "2",
|
||||
"check_timed_out": "false",
|
||||
"check_automation_run_id": run_id,
|
||||
"check_trace_id": run_id,
|
||||
"check_run_id": run_id,
|
||||
"check_work_item_id": "P0-03-WAZUH-ALERT-INGRESS",
|
||||
"check_source_recurrence": recurrence,
|
||||
"check_failure_marker": (
|
||||
"wazuh_privileged_convergence_capability_missing"
|
||||
),
|
||||
"break_glass_packet_receipt_id": (
|
||||
"00000000-0000-0000-0000-000000000315"
|
||||
),
|
||||
"break_glass_packet_status": "pending",
|
||||
"capability_packet_id": "IWZ-BG-0011223344556677",
|
||||
"capability_automation_run_id": run_id,
|
||||
"capability_trace_id": run_id,
|
||||
"capability_run_id": run_id,
|
||||
"capability_work_item_id": "P0-03-WAZUH-ALERT-INGRESS",
|
||||
"capability_project_id": "awoooi",
|
||||
"capability_run_namespace": "awoooi:iwooos:wazuh-alert-ingress",
|
||||
"capability_proposal_source": (
|
||||
"iwooos_wazuh_alert_ingress_scheduler"
|
||||
),
|
||||
"capability_source_recurrence": recurrence,
|
||||
"capability_type": "bounded_privileged_convergence",
|
||||
"capability_risk_level": "critical",
|
||||
"capability_policy_route": "critical_break_glass_queue",
|
||||
"capability_queue_status": "queued",
|
||||
"capability_execution_allowed": False,
|
||||
"capability_granted": False,
|
||||
"capability_packet_host_write_performed": False,
|
||||
"stage_ids": [],
|
||||
}
|
||||
|
||||
payload = build_iwooos_wazuh_controlled_executor_runtime_readback(
|
||||
row,
|
||||
executor_enabled=True,
|
||||
)
|
||||
|
||||
assert payload["summary"][
|
||||
"critical_break_glass_capability_packet_count"
|
||||
] == 1
|
||||
assert payload["next_safe_action"] == (
|
||||
"critical_break_glass_capability_packet_queued_waiting_"
|
||||
"bounded_capability_verifier_then_retry"
|
||||
)
|
||||
packet = payload["critical_break_glass_capability_packet"]
|
||||
assert packet["queue_status"] == "queued"
|
||||
assert packet["execution_allowed"] is False
|
||||
assert packet["capability_granted"] is False
|
||||
assert packet["host_write_performed"] is False
|
||||
assert packet["secret_value_exposed"] is False
|
||||
assert "critical_break_glass_capability_packet_pending" in (
|
||||
payload["active_blockers"]
|
||||
)
|
||||
assert payload["boundaries"][
|
||||
"critical_break_glass_capability_packet_persisted"
|
||||
] is True
|
||||
|
||||
|
||||
def test_wazuh_runtime_readback_rejects_untrusted_failure_marker() -> None:
|
||||
@@ -345,6 +441,184 @@ def test_wazuh_runtime_readback_rejects_untrusted_failure_marker() -> None:
|
||||
assert payload["boundaries"]["host_write_performed"] is False
|
||||
|
||||
|
||||
def test_wazuh_break_glass_packet_is_deterministic_and_public_safe() -> None:
|
||||
kwargs = {
|
||||
"check_op_id": "00000000-0000-0000-0000-000000000316",
|
||||
"source_candidate_op_id": "00000000-0000-0000-0000-000000000317",
|
||||
"incident_id": "IWZ-I-2026071512-TEST",
|
||||
"catalog_id": "ansible:wazuh-alertmanager-integration",
|
||||
"source_input": {
|
||||
"automation_run_id": "00000000-0000-0000-0000-000000000317",
|
||||
"trace_id": "trace-317",
|
||||
"run_id": "run-317",
|
||||
"work_item_id": "P0-03-WAZUH-ALERT-INGRESS",
|
||||
"project_id": "awoooi",
|
||||
"run_namespace": "awoooi:iwooos:wazuh-alert-ingress",
|
||||
"proposal_source": "iwooos_wazuh_alert_ingress_scheduler",
|
||||
"source_receipt_ref": "wazuh-alert:2026071500",
|
||||
"source_recurrence": _current_wazuh_source_recurrence(
|
||||
"2026-07-15T05:59:58+08:00"
|
||||
),
|
||||
"unexpected_material": "must-not-be-projected",
|
||||
"command_output": "must-not-be-projected",
|
||||
},
|
||||
}
|
||||
|
||||
packet_input, packet_output, dry_run = (
|
||||
_build_wazuh_break_glass_capability_packet(**kwargs)
|
||||
)
|
||||
repeated_input, _, _ = _build_wazuh_break_glass_capability_packet(
|
||||
**kwargs
|
||||
)
|
||||
serialized = json.dumps(
|
||||
{"input": packet_input, "output": packet_output, "dry": dry_run}
|
||||
)
|
||||
|
||||
assert packet_input["capability_packet_id"] == repeated_input[
|
||||
"capability_packet_id"
|
||||
]
|
||||
assert packet_input["risk_level"] == "critical"
|
||||
assert packet_input["execution_allowed"] is False
|
||||
assert packet_input["host_write_performed"] is False
|
||||
assert packet_input["project_id"] == "awoooi"
|
||||
assert packet_input["source_recurrence"]["verified"] is True
|
||||
assert packet_output["queue_status"] == "queued"
|
||||
assert dry_run["capability_granted"] is False
|
||||
assert "must-not-be-projected" not in serialized
|
||||
assert "command_output" not in serialized
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_failed_wazuh_check_atomically_queues_deduplicated_packet(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
from src.services import awooop_ansible_check_mode_service as service
|
||||
|
||||
calls: list[tuple[str, dict[str, object]]] = []
|
||||
|
||||
class _Result:
|
||||
def __init__(self, inserted: bool = False):
|
||||
self.inserted = inserted
|
||||
|
||||
def scalar_one_or_none(self):
|
||||
if self.inserted:
|
||||
return "00000000-0000-0000-0000-000000000318"
|
||||
return None
|
||||
|
||||
class _Db:
|
||||
async def execute(self, statement, params):
|
||||
sql = str(statement)
|
||||
calls.append((sql, params))
|
||||
return _Result(inserted="RETURNING op_id::text" in sql)
|
||||
|
||||
@asynccontextmanager
|
||||
async def fake_db_context(project_id: str):
|
||||
assert project_id == "awoooi"
|
||||
yield _Db()
|
||||
|
||||
monkeypatch.setattr(service, "get_db_context", fake_db_context)
|
||||
claim = AnsibleCheckModeClaim(
|
||||
op_id="00000000-0000-0000-0000-000000000319",
|
||||
source_candidate_op_id="00000000-0000-0000-0000-000000000320",
|
||||
incident_id="IWZ-I-2026071512-TEST",
|
||||
catalog_id="ansible:wazuh-alertmanager-integration",
|
||||
playbook_path=(
|
||||
"infra/ansible/playbooks/wazuh-alertmanager-integration.yml"
|
||||
),
|
||||
apply_playbook_path=(
|
||||
"infra/ansible/playbooks/wazuh-alertmanager-integration.yml"
|
||||
),
|
||||
inventory_hosts=("host_112",),
|
||||
risk_level="medium",
|
||||
input_payload={
|
||||
"automation_run_id": "00000000-0000-0000-0000-000000000320",
|
||||
"trace_id": "trace-320",
|
||||
"run_id": "run-320",
|
||||
"work_item_id": "P0-03-WAZUH-ALERT-INGRESS",
|
||||
"project_id": "awoooi",
|
||||
"run_namespace": "awoooi:iwooos:wazuh-alert-ingress",
|
||||
"proposal_source": "iwooos_wazuh_alert_ingress_scheduler",
|
||||
"source_receipt_ref": "wazuh-alert:2026071500",
|
||||
"source_recurrence": _current_wazuh_source_recurrence(
|
||||
"2026-07-15T05:59:58+08:00"
|
||||
),
|
||||
},
|
||||
)
|
||||
result = AnsibleRunResult(
|
||||
returncode=2,
|
||||
stdout="wazuh_privileged_convergence_capability_missing",
|
||||
stderr="",
|
||||
duration_ms=20,
|
||||
)
|
||||
|
||||
assert _wazuh_privileged_convergence_capability_is_missing(claim, result)
|
||||
await finalize_check_mode_claim(claim, result)
|
||||
|
||||
assert len(calls) == 2
|
||||
insert_sql, insert_params = calls[1]
|
||||
assert "ansible_break_glass_capability_work_item_queued" in insert_sql
|
||||
assert "WHERE NOT EXISTS" in insert_sql
|
||||
packet_input = json.loads(str(insert_params["input"]))
|
||||
assert packet_input["work_item_id"] == "P0-03-WAZUH-ALERT-INGRESS"
|
||||
assert packet_input["secret_value_exposed"] is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wazuh_break_glass_packet_backfill_closes_existing_gap(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
from src.services import awooop_ansible_check_mode_service as service
|
||||
|
||||
rows = [{
|
||||
"check_op_id": "00000000-0000-0000-0000-000000000321",
|
||||
"source_candidate_op_id": "00000000-0000-0000-0000-000000000322",
|
||||
"incident_id": "IWZ-I-2026071512-TEST",
|
||||
"automation_run_id": "00000000-0000-0000-0000-000000000322",
|
||||
"trace_id": "trace-322",
|
||||
"run_id": "run-322",
|
||||
"work_item_id": "P0-03-WAZUH-ALERT-INGRESS",
|
||||
"project_id": "awoooi",
|
||||
"run_namespace": "awoooi:iwooos:wazuh-alert-ingress",
|
||||
"proposal_source": "iwooos_wazuh_alert_ingress_scheduler",
|
||||
"source_receipt_ref": "wazuh-alert:2026071500",
|
||||
"source_recurrence": _current_wazuh_source_recurrence(
|
||||
"2026-07-15T05:59:58+08:00"
|
||||
),
|
||||
}]
|
||||
|
||||
class _Mappings:
|
||||
def all(self):
|
||||
return rows
|
||||
|
||||
class _Result:
|
||||
def __init__(self, *, selected: bool = False):
|
||||
self.selected = selected
|
||||
|
||||
def mappings(self):
|
||||
return _Mappings() if self.selected else MagicMock()
|
||||
|
||||
def scalar_one_or_none(self):
|
||||
if not self.selected:
|
||||
return "00000000-0000-0000-0000-000000000323"
|
||||
return None
|
||||
|
||||
class _Db:
|
||||
async def execute(self, statement, _params=None):
|
||||
sql = str(statement)
|
||||
return _Result(selected="SELECT" in sql and "JOIN" in sql)
|
||||
|
||||
@asynccontextmanager
|
||||
async def fake_db_context(project_id: str):
|
||||
assert project_id == "awoooi"
|
||||
yield _Db()
|
||||
|
||||
monkeypatch.setattr(service, "get_db_context", fake_db_context)
|
||||
|
||||
result = await backfill_missing_wazuh_break_glass_capability_packets_once()
|
||||
|
||||
assert result == {"scanned": 1, "written": 1, "error": None}
|
||||
|
||||
|
||||
def test_wazuh_posture_catalog_and_playbook_are_bounded_no_write() -> None:
|
||||
catalog = get_ansible_catalog_item("ansible:wazuh-manager-posture-readback")
|
||||
assert catalog is not None
|
||||
|
||||
Reference in New Issue
Block a user