feat(iwooos): add wazuh incident closure contract
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / tests (push) Has been cancelled
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / tests (push) Has been cancelled
This commit is contained in:
@@ -24,7 +24,7 @@ def test_iwooos_security_tool_closure_readback_rolls_up_tool_tracks() -> None:
|
||||
|
||||
assert payload["schema_version"] == "iwooos_security_tool_closure_readback_v2"
|
||||
assert payload["status"] == "tool_source_readiness_available_runtime_closure_open"
|
||||
assert payload["summary"]["source_readback_count"] == 8
|
||||
assert payload["summary"]["source_readback_count"] == 9
|
||||
assert payload["summary"]["tool_track_count"] == 8
|
||||
assert payload["summary"]["p0_tool_track_count"] == 3
|
||||
assert payload["summary"]["wazuh_expected_host_scope_count"] == 6
|
||||
@@ -43,6 +43,18 @@ def test_iwooos_security_tool_closure_readback_rolls_up_tool_tracks() -> None:
|
||||
payload["summary"]["wazuh_fim_vulnerability_alert_verifier_ready_signal_count"]
|
||||
== 5
|
||||
)
|
||||
assert (
|
||||
payload["summary"][
|
||||
"wazuh_incident_response_closure_validator_available_count"
|
||||
]
|
||||
== 1
|
||||
)
|
||||
assert (
|
||||
payload["summary"][
|
||||
"wazuh_incident_response_same_run_required_stage_count"
|
||||
]
|
||||
== 9
|
||||
)
|
||||
assert payload["summary"]["supply_chain_scan_closed_count"] == 0
|
||||
assert payload["summary"]["runtime_detection_closed_count"] == 0
|
||||
assert payload["summary"]["web_api_dast_closed_count"] == 0
|
||||
@@ -118,6 +130,11 @@ def test_iwooos_security_tool_closure_queue_has_executable_receipt_contract() ->
|
||||
assert queue["P0-03"]["track_id"] == "wazuh_detection_response"
|
||||
assert queue["P0-03"]["required_signal_count"] == 6
|
||||
assert queue["P0-03"]["missing_signal_count"] >= 0
|
||||
assert queue["P0-03"]["same_run_required_stage_count"] == 9
|
||||
assert "incident_response_packet_validation_endpoint" in queue["P0-03"]
|
||||
assert queue["P0-03"]["incident_response_packet_validation_endpoint"].endswith(
|
||||
"/validate-incident-response-packet"
|
||||
)
|
||||
assert queue["P0-03"]["required_verifier"] == (
|
||||
"wazuh_registry_fim_vulnerability_alert_receipt_post_verifier"
|
||||
)
|
||||
@@ -164,6 +181,15 @@ def test_iwooos_security_tool_closure_readback_accepts_alert_receipt_signal() ->
|
||||
assert tracks["wazuh_detection_response"]["execution_candidate_ready"] is True
|
||||
assert tracks["normalized_detection_schema"]["ready_signal_count"] == 1
|
||||
assert payload["summary"]["controlled_apply_policy_active_count"] == 1
|
||||
queue = {item["queue_id"]: item for item in payload["next_executable_queue"]}
|
||||
assert queue["P0-03"]["state"] == (
|
||||
"wazuh_source_verifier_ready_runtime_closure_open"
|
||||
)
|
||||
assert queue["P0-03"]["same_run_closure_candidate_ready_count"] == 0
|
||||
assert queue["P0-03"]["next_executable_action"] == (
|
||||
"attach_live_wazuh_event_forensics_incident_case_controlled_response_"
|
||||
"and_independent_post_verifier_receipts"
|
||||
)
|
||||
|
||||
|
||||
def test_iwooos_security_tool_closure_readback_api_is_public_safe(monkeypatch) -> None:
|
||||
@@ -185,6 +211,7 @@ def test_iwooos_security_tool_closure_readback_api_is_public_safe(monkeypatch) -
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["schema_version"] == "iwooos_security_tool_closure_readback_v2"
|
||||
assert data["summary"]["source_readback_count"] == 9
|
||||
assert data["summary"]["tool_track_count"] == 8
|
||||
assert data["summary"]["alert_receipt_accepted_count"] == 1
|
||||
assert data["summary"]["wazuh_fim_vulnerability_alert_closed_count"] == 0
|
||||
|
||||
208
apps/api/tests/test_iwooos_wazuh_incident_response_closure.py
Normal file
208
apps/api/tests/test_iwooos_wazuh_incident_response_closure.py
Normal file
@@ -0,0 +1,208 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
os.environ.setdefault("DATABASE_URL", "postgresql+asyncpg://test:test@localhost/test")
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from src.api.v1.iwooos import router
|
||||
from src.services.iwooos_wazuh_incident_response_closure import (
|
||||
load_latest_iwooos_wazuh_incident_response_closure,
|
||||
validate_iwooos_wazuh_incident_response_closure_packet,
|
||||
)
|
||||
|
||||
|
||||
def _client() -> TestClient:
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
def _alert_receipt() -> dict[str, object]:
|
||||
return {
|
||||
"events": [{"provider_event_id": "alertmanager:received:wazuh-closure"}],
|
||||
"total": 1,
|
||||
"limit": 5,
|
||||
"source_status": "ready",
|
||||
}
|
||||
|
||||
|
||||
def _stage_receipts() -> list[dict[str, str]]:
|
||||
return [
|
||||
{"stage_id": "sensor_source_receipt", "receipt_ref": "ref-sensor"},
|
||||
{"stage_id": "normalized_asset_identity", "receipt_ref": "ref-asset"},
|
||||
{"stage_id": "source_truth_diff", "receipt_ref": "ref-diff"},
|
||||
{"stage_id": "ai_decision_candidate_action", "receipt_ref": "ref-ai"},
|
||||
{"stage_id": "risk_policy_decision", "receipt_ref": "ref-policy"},
|
||||
{"stage_id": "check_mode_dry_run_receipt", "receipt_ref": "ref-dry-run"},
|
||||
{"stage_id": "bounded_execution_receipt", "receipt_ref": "ref-bounded"},
|
||||
{"stage_id": "independent_post_verifier", "receipt_ref": "ref-post"},
|
||||
{
|
||||
"stage_id": "km_rag_mcp_playbook_write_ack",
|
||||
"receipt_ref": "ref-km-rag-mcp-playbook",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _valid_packet() -> dict[str, object]:
|
||||
return {
|
||||
"trace_id": "iwooos-wazuh-p0-03-trace-001",
|
||||
"run_id": "iwooos-wazuh-p0-03-run-001",
|
||||
"asset_scope_aliases": ["managed_core_node_a", "managed_core_node_b"],
|
||||
"wazuh_event_receipt_ref": "ref-wazuh-event",
|
||||
"forensics_evidence_ref": "ref-forensics",
|
||||
"incident_case_ref": "ref-incident-case",
|
||||
"ai_decision_ref": "ref-ai-decision",
|
||||
"candidate_controlled_response_ref": "ref-controlled-response-candidate",
|
||||
"source_truth_diff_ref": "ref-source-truth-diff",
|
||||
"risk_policy_decision_ref": "ref-risk-policy",
|
||||
"check_mode_dry_run_ref": "ref-check-mode-dry-run",
|
||||
"bounded_execution_receipt_ref": "ref-bounded-execution",
|
||||
"rollback_plan_ref": "ref-rollback-plan",
|
||||
"post_verifier_ref": "ref-independent-post-verifier",
|
||||
"km_rag_mcp_playbook_writeback_ref": "ref-km-rag-mcp-playbook",
|
||||
"same_run_stage_receipts": _stage_receipts(),
|
||||
"runtime_boundary_ack": (
|
||||
"runtime_gate_remains_closed_until_executor_post_verifier"
|
||||
),
|
||||
"host_write_boundary_ack": "no_host_write_performed_by_validator",
|
||||
"secret_boundary_ack": "no_secret_value_collected_or_submitted",
|
||||
"live_wazuh_query_boundary_ack": (
|
||||
"no_live_wazuh_query_performed_by_validator"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def test_wazuh_incident_response_closure_readback_exposes_same_run_contract() -> None:
|
||||
payload = load_latest_iwooos_wazuh_incident_response_closure(
|
||||
alert_receipt_readback=_alert_receipt()
|
||||
)
|
||||
|
||||
assert (
|
||||
payload["schema_version"]
|
||||
== "iwooos_wazuh_incident_response_closure_readback_v1"
|
||||
)
|
||||
assert (
|
||||
payload["status"]
|
||||
== "wazuh_incident_response_same_run_candidate_missing_receipts"
|
||||
)
|
||||
assert payload["summary"]["closure_packet_validator_available_count"] == 1
|
||||
assert payload["summary"]["source_verifier_ready_count"] == 1
|
||||
assert payload["summary"]["controlled_apply_preflight_ready_count"] == 1
|
||||
assert payload["summary"]["allowlisted_dry_run_ready_count"] == 1
|
||||
assert payload["summary"]["same_run_required_stage_count"] == 9
|
||||
assert payload["summary"]["wazuh_event_accepted_count"] == 0
|
||||
assert payload["summary"]["forensic_evidence_accepted_count"] == 0
|
||||
assert payload["summary"]["incident_case_accepted_count"] == 0
|
||||
assert payload["summary"]["same_run_source_stage_ready_count"] == 5
|
||||
assert payload["summary"]["same_run_closure_candidate_ready_count"] == 0
|
||||
assert payload["summary"]["same_run_closure_candidate_missing_count"] == 1
|
||||
assert payload["summary"]["wazuh_incident_response_runtime_closed_count"] == 0
|
||||
assert payload["summary"]["wazuh_active_response_authorized_count"] == 0
|
||||
assert payload["boundaries"]["runtime_execution_authorized"] is False
|
||||
assert payload["boundaries"]["wazuh_active_response_authorized"] is False
|
||||
assert payload["boundaries"]["secret_value_collection_allowed"] is False
|
||||
|
||||
|
||||
def test_wazuh_incident_response_closure_packet_accepts_redacted_same_run_receipts() -> None:
|
||||
payload = validate_iwooos_wazuh_incident_response_closure_packet(
|
||||
_valid_packet(),
|
||||
alert_receipt_readback=_alert_receipt(),
|
||||
)
|
||||
|
||||
assert (
|
||||
payload["schema_version"]
|
||||
== "iwooos_wazuh_incident_response_closure_validation_result_v1"
|
||||
)
|
||||
assert (
|
||||
payload["status"]
|
||||
== "accepted_for_wazuh_incident_response_closure_review_only"
|
||||
)
|
||||
assert payload["accepted_for_incident_response_closure_review_only"] is True
|
||||
assert payload["summary"]["incident_response_packet_review_ready_count"] == 1
|
||||
assert payload["summary"]["same_run_packet_stage_ready_count"] == 9
|
||||
assert payload["summary"]["wazuh_incident_response_runtime_closed_count"] == 0
|
||||
assert payload["summary"]["runtime_gate_count"] == 0
|
||||
assert payload["boundaries"]["payload_persisted"] is False
|
||||
assert payload["boundaries"]["runtime_execution_authorized"] is False
|
||||
|
||||
|
||||
def test_wazuh_incident_response_closure_packet_requests_missing_stage_receipts() -> None:
|
||||
packet = _valid_packet()
|
||||
packet["same_run_stage_receipts"] = _stage_receipts()[:-1]
|
||||
|
||||
payload = validate_iwooos_wazuh_incident_response_closure_packet(
|
||||
packet,
|
||||
alert_receipt_readback=_alert_receipt(),
|
||||
)
|
||||
|
||||
assert payload["status"] == "request_same_run_stage_receipts_fix"
|
||||
assert payload["summary"]["incident_response_packet_supplement_required_count"] == 1
|
||||
assert any(
|
||||
finding["check_id"] == "WIRC-06"
|
||||
and "km_rag_mcp_playbook_write_ack"
|
||||
in finding["metadata"]["missing_stage_ids"]
|
||||
for finding in payload["validation_findings"]
|
||||
)
|
||||
|
||||
|
||||
def test_wazuh_incident_response_closure_packet_quarantines_sensitive_payload() -> None:
|
||||
packet = _valid_packet()
|
||||
packet["wazuh_event_receipt_ref"] = "ref accidentally included 10.10.10.10"
|
||||
|
||||
payload = validate_iwooos_wazuh_incident_response_closure_packet(
|
||||
packet,
|
||||
alert_receipt_readback=_alert_receipt(),
|
||||
)
|
||||
|
||||
assert payload["status"] == "quarantine_sensitive_payload"
|
||||
assert payload["quarantined"] is True
|
||||
assert payload["summary"]["incident_response_packet_quarantined_count"] == 1
|
||||
assert "10.10.10.10" not in str(payload)
|
||||
|
||||
|
||||
def test_wazuh_incident_response_closure_packet_rejects_runtime_action() -> None:
|
||||
packet = _valid_packet()
|
||||
packet["requested_actions"] = ["wazuh_active_response"]
|
||||
|
||||
payload = validate_iwooos_wazuh_incident_response_closure_packet(
|
||||
packet,
|
||||
alert_receipt_readback=_alert_receipt(),
|
||||
)
|
||||
|
||||
assert payload["status"] == "reject_runtime_action_request"
|
||||
assert payload["runtime_action_rejected"] is True
|
||||
assert payload["summary"]["incident_response_packet_runtime_action_rejected_count"] == 1
|
||||
assert payload["summary"]["wazuh_active_response_authorized_count"] == 0
|
||||
|
||||
|
||||
def test_wazuh_incident_response_closure_api_is_public_safe(monkeypatch) -> None:
|
||||
async def fake_recent_events(**_: object) -> dict[str, object]:
|
||||
return _alert_receipt()
|
||||
|
||||
monkeypatch.setattr(
|
||||
"src.api.v1.iwooos.list_recent_channel_events",
|
||||
fake_recent_events,
|
||||
)
|
||||
|
||||
client = _client()
|
||||
readback = client.get("/api/v1/iwooos/wazuh-incident-response-closure-readback")
|
||||
validation = client.post(
|
||||
"/api/v1/iwooos/wazuh-incident-response-closure/validate-incident-response-packet",
|
||||
json=_valid_packet(),
|
||||
)
|
||||
|
||||
assert readback.status_code == 200
|
||||
assert validation.status_code == 200
|
||||
assert readback.json()["summary"]["same_run_closure_candidate_ready_count"] == 0
|
||||
assert readback.json()["summary"]["same_run_closure_candidate_missing_count"] == 1
|
||||
assert (
|
||||
validation.json()["status"]
|
||||
== "accepted_for_wazuh_incident_response_closure_review_only"
|
||||
)
|
||||
assert "192.168.0." not in readback.text
|
||||
assert "192.168.0." not in validation.text
|
||||
assert "WAZUH_API_PASSWORD" not in validation.text
|
||||
assert "批准!繼續" not in validation.text
|
||||
Reference in New Issue
Block a user