feat(iwooos): validate wazuh owner registry exports
Some checks failed
CD Pipeline / tests (push) Successful in 1m43s
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
Code Review / ai-code-review (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-27 19:31:47 +08:00
parent ce0c7cbaf8
commit 82a73250f4
8 changed files with 673 additions and 4 deletions

View File

@@ -6,15 +6,78 @@ from fastapi.testclient import TestClient
from src.api.v1.iwooos import router
from src.services.iwooos_wazuh_manager_registry_reviewer_validation import (
load_latest_iwooos_wazuh_manager_registry_reviewer_validation,
validate_iwooos_wazuh_manager_registry_owner_export,
)
EXPECTED_ALIASES = [
"managed_core_node_a",
"managed_core_node_b",
"managed_dev_node_a",
"managed_dev_node_b",
"managed_control_node_a",
"managed_control_node_b",
]
def _client() -> TestClient:
app = FastAPI()
app.include_router(router)
return TestClient(app)
def _valid_owner_export() -> dict:
return {
"owner_role": "資安負責人",
"team": "IwoooS",
"decision": "accept_readonly_registry_export_for_reviewer_validation",
"decision_reason": "Wazuh manager registry 已脫敏,供 reviewer 驗證主機覆蓋與 Dashboard API 修復狀態。",
"affected_scope": "iwooos_wazuh_expected_scope_aliases",
"collection_method": "owner_export_redacted_summary",
"agent_total": 6,
"agent_active": 2,
"agent_disconnected": 3,
"agent_never_connected": 1,
"last_seen_window_start": "2026-06-27T15:00:00+08:00",
"last_seen_window_end": "2026-06-27T16:00:00+08:00",
"registry_collected_at": "2026-06-27T16:05:00+08:00",
"registry_export_scope_aliases": EXPECTED_ALIASES,
"per_host_registry_matrix": [
{
"node_alias": alias,
"scope_role": "managed_scope",
"registry_presence": "present",
"agent_status_bucket": "active" if index < 2 else "disconnected",
"last_seen_state": "within_owner_window" if index < 2 else "outside_owner_window",
"manager_group_ref": f"group-ref-{index}",
"agent_id_redacted_ref": f"agent-redacted-ref-{index}",
"gap_reason": "none" if index < 2 else "owner_followup_required",
"redacted_evidence_ref": f"evidence-ref-{index}",
}
for index, alias in enumerate(EXPECTED_ALIASES)
],
"registry_gap_reason_by_alias": {
alias: "none" if index < 2 else "owner_followup_required"
for index, alias in enumerate(EXPECTED_ALIASES)
},
"registry_export_summary_ref": "evidence-ref-registry-summary",
"manager_health_ref": "evidence-ref-manager-health",
"dashboard_api_status_ref": "evidence-ref-dashboard-api",
"dashboard_api_connection_check_status": "ok",
"dashboard_api_version_check_status": "ok",
"dashboard_index_pattern_statuses": ["alerts_ok", "monitoring_ok", "statistics_ok"],
"dashboard_api_degradation_root_cause": "stored_api_connection_repaired_by_owner",
"dashboard_api_repair_postcheck_ref": "evidence-ref-dashboard-postcheck",
"redacted_evidence_refs": [
"evidence-ref-registry-summary",
"evidence-ref-dashboard-postcheck",
],
"followup_owner": "IwoooS reviewer",
"rollback_owner": "IwoooS runtime owner",
"postcheck_plan": "post_enable_iwooos_readback_no_raw_payload",
}
def test_iwooos_wazuh_manager_registry_reviewer_validation_contract_is_waiting_only() -> None:
payload = load_latest_iwooos_wazuh_manager_registry_reviewer_validation()
@@ -94,3 +157,82 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe()
assert "source_thread_id" not in response.text
assert "owenhytsai/" not in response.text
assert "WAZUH_API_PASSWORD" not in response.text
def test_iwooos_wazuh_manager_registry_owner_export_validation_accepts_redacted_payload() -> None:
payload = validate_iwooos_wazuh_manager_registry_owner_export(_valid_owner_export())
assert payload["schema_version"] == "iwooos_wazuh_manager_registry_owner_export_validation_result_v1"
assert payload["status"] == "accepted_for_readonly_posture_only"
assert payload["accepted_for_readonly_posture_only"] is True
assert payload["reviewer_validation_passed"] is True
assert payload["summary"]["owner_registry_export_received_count"] == 1
assert payload["summary"]["owner_registry_export_accepted_count"] == 1
assert payload["summary"]["manager_registry_accepted_count"] == 0
assert payload["summary"]["runtime_gate_count"] == 0
assert payload["boundaries"]["payload_persisted"] is False
assert payload["boundaries"]["runtime_execution_authorized"] is False
assert payload["boundaries"]["manager_registry_accepted_updated"] is False
assert all(slot["received"] is True for slot in payload["evidence_slots"])
assert all(slot["accepted"] is True for slot in payload["evidence_slots"])
def test_iwooos_wazuh_manager_registry_owner_export_validation_api_does_not_update_global_counters() -> None:
client = _client()
response = client.post(
"/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-owner-export",
json=_valid_owner_export(),
)
assert response.status_code == 200
result = response.json()
assert result["status"] == "accepted_for_readonly_posture_only"
assert result["summary"]["owner_registry_export_accepted_count"] == 1
assert result["summary"]["manager_registry_accepted_count"] == 0
assert result["summary"]["runtime_gate_count"] == 0
readback = client.get("/api/v1/iwooos/wazuh-manager-registry-reviewer-validation").json()
assert readback["summary"]["owner_registry_export_received_count"] == 0
assert readback["summary"]["owner_registry_export_accepted_count"] == 0
assert readback["summary"]["manager_registry_accepted_count"] == 0
assert readback["summary"]["runtime_gate_count"] == 0
def test_iwooos_wazuh_manager_registry_owner_export_validation_requests_missing_fields() -> None:
candidate = _valid_owner_export()
candidate.pop("decision_reason")
payload = validate_iwooos_wazuh_manager_registry_owner_export(candidate)
assert payload["status"] == "request_missing_fields"
assert payload["accepted_for_readonly_posture_only"] is False
assert payload["summary"]["owner_registry_export_received_count"] == 1
assert payload["summary"]["owner_registry_export_accepted_count"] == 0
assert any("decision_reason" in finding["field_paths"] for finding in payload["validation_findings"])
def test_iwooos_wazuh_manager_registry_owner_export_validation_quarantines_sensitive_payload() -> None:
candidate = _valid_owner_export()
candidate["manager_health_ref"] = "health evidence mentioned 10.250.250.250 by mistake"
payload = validate_iwooos_wazuh_manager_registry_owner_export(candidate)
assert payload["status"] == "quarantine_sensitive_payload"
assert payload["quarantined"] is True
assert payload["summary"]["reviewer_validation_quarantined_count"] == 1
assert payload["summary"]["owner_registry_export_accepted_count"] == 0
assert "10.250.250.250" not in str(payload)
assert any(finding["check_id"] == "RV-07" for finding in payload["validation_findings"])
def test_iwooos_wazuh_manager_registry_owner_export_validation_rejects_runtime_action_request() -> None:
candidate = _valid_owner_export()
candidate["requested_actions"] = ["wazuh_active_response"]
payload = validate_iwooos_wazuh_manager_registry_owner_export(candidate)
assert payload["status"] == "reject_runtime_action_request"
assert payload["runtime_action_rejected"] is True
assert payload["summary"]["owner_registry_export_accepted_count"] == 0
assert payload["summary"]["runtime_gate_count"] == 0
assert any(finding["check_id"] == "RV-09" for finding in payload["validation_findings"])