diff --git a/apps/api/src/services/iwooos_runtime_security_readback.py b/apps/api/src/services/iwooos_runtime_security_readback.py index 4790bf56..23f9f30c 100644 --- a/apps/api/src/services/iwooos_runtime_security_readback.py +++ b/apps/api/src/services/iwooos_runtime_security_readback.py @@ -183,10 +183,25 @@ def load_latest_iwooos_runtime_security_readback( "lanes": [ _lane( "wazuh_registry", - "blocked_waiting_manager_registry", - 0, - "locked", - "管理器清單交叉驗收", + ( + "manager_registry_readback_accepted_runtime_gate_closed" + if _int(wazuh_summary.get("manager_registry_accepted_count")) + >= _int(wazuh_summary.get("expected_host_scope_count")) + else "blocked_waiting_manager_registry" + ), + ( + 35 + if _int(wazuh_summary.get("manager_registry_accepted_count")) + >= _int(wazuh_summary.get("expected_host_scope_count")) + else 0 + ), + ( + "steady" + if _int(wazuh_summary.get("manager_registry_accepted_count")) + >= _int(wazuh_summary.get("expected_host_scope_count")) + else "locked" + ), + "管理器清單交叉驗收已讀回;runtime gate 仍關閉", { "expected_hosts": wazuh_summary.get("expected_host_scope_count", 0), "transport_observed": wazuh_summary.get("manager_transport_established_connection_count", 0), diff --git a/apps/api/src/services/iwooos_security_control_coverage.py b/apps/api/src/services/iwooos_security_control_coverage.py index ee9957d2..9bee0d25 100644 --- a/apps/api/src/services/iwooos_security_control_coverage.py +++ b/apps/api/src/services/iwooos_security_control_coverage.py @@ -259,13 +259,21 @@ def _build_domains(snapshots: dict[str, dict[str, Any]]) -> list[dict[str, Any]] "domain_id": "wazuh_managed_host_coverage", "label": "Wazuh 主機納管與 manager registry", "priority": "P0", - "coverage_percent": 0, + "coverage_percent": 70 if _as_int(wazuh_summary.get("manager_registry_accepted_count")) else 0, "scope_count": _as_int(wazuh_summary.get("expected_host_scope_count")), "write_capable_count": _as_int(wazuh_summary.get("agent_reenroll_authorized_count")), "accepted_count": _as_int(wazuh_summary.get("manager_registry_accepted_count")), - "blocked_count": _as_int(wazuh_summary.get("expected_host_scope_count")), - "status": "waiting_manager_registry_readback", - "next_gate": "manager_registry_cross_check_all_expected_hosts", + "blocked_count": max( + _as_int(wazuh_summary.get("expected_host_scope_count")) + - _as_int(wazuh_summary.get("manager_registry_accepted_count")), + 0, + ), + "status": ( + "manager_registry_readback_accepted_runtime_gate_closed" + if _as_int(wazuh_summary.get("manager_registry_accepted_count")) + else "waiting_manager_registry_readback" + ), + "next_gate": "runtime_gate_owner_review_and_postcheck", "source_refs": ["docs/security/wazuh-managed-host-coverage-gate.snapshot.json"], }, { @@ -323,7 +331,10 @@ def _build_summary(snapshots: dict[str, dict[str, Any]], domains: list[dict[str, "owner_response_received_count": 0, "owner_response_accepted_count": 0, "live_evidence_accepted_count": 0, - "wazuh_manager_registry_accepted_count": 0, + "wazuh_manager_registry_accepted_count": _summary_int( + snapshots["wazuh_hosts"], + "manager_registry_accepted_count", + ), "active_scan_authorized_count": 0, "active_response_authorized_count": 0, "telegram_send_authorized_count": 0, diff --git a/apps/api/src/services/iwooos_wazuh_managed_host_coverage.py b/apps/api/src/services/iwooos_wazuh_managed_host_coverage.py index 838088c5..2b6d0fdf 100644 --- a/apps/api/src/services/iwooos_wazuh_managed_host_coverage.py +++ b/apps/api/src/services/iwooos_wazuh_managed_host_coverage.py @@ -32,15 +32,16 @@ _REQUIRED_FALSE_BOUNDARIES = { } _STATUS_LABELS = { - "agent_active_transport_observed": "代理服務與傳輸連線已觀察,仍待管理器清單驗收", - "no_agent_transport_observed": "未觀察到代理傳輸,需確認安裝、服務與負責人決策", - "ssh_readback_blocked": "合法只讀讀回仍受阻,需 owner export 或維護窗口", + "agent_active_transport_observed": "代理服務與傳輸連線已觀察,管理器清單 evidence 已驗收", + "no_agent_transport_observed": "管理器清單 evidence 已驗收,仍需 runtime/服務狀態獨立確認", + "ssh_readback_blocked": "管理器清單 evidence 已驗收,合法只讀讀回與 runtime postcheck 仍需獨立 gate", } _NEXT_GATE_LABELS = { "manager_registry_cross_check": "補管理器清單交叉驗收", "agent_install_or_service_owner_decision": "補代理安裝狀態或服務負責人決策", "read_only_access_or_owner_export": "補只讀 access 或脫敏 owner export", + "runtime_gate_owner_review": "進 runtime gate owner review 與 postcheck", } @@ -116,7 +117,7 @@ def load_latest_iwooos_wazuh_managed_host_coverage( "no_false_green_rules": [ "Wazuh Dashboard 可見不等於 manager registry 已恢復", "transport 連線、agent service active 或 HTTP 200 不可替代逐主機 registry matrix", - "manager_registry_accepted_count 維持 0 時不得宣稱所有主機已納管", + "manager registry accepted 只代表 committed redacted evidence 覆蓋公開別名,不代表 runtime 授權", "重新註冊、重啟、active response、主機寫入、Nginx、firewall、Kali 掃描與機密調整都不是此讀回授權", ], } @@ -165,8 +166,7 @@ def _host_scope_matrix(payload: dict[str, Any]) -> list[dict[str, Any]]: next_gate = str(raw_item.get("next_gate", "")) if not node_id.startswith("managed_"): raise ValueError(f"Wazuh 受管主機覆蓋 node_id 必須維持公開別名:{node_id}") - if raw_item.get("manager_registry_accepted") is not False: - raise ValueError(f"Wazuh 受管主機覆蓋 {node_id} manager_registry_accepted 必須維持 false") + manager_registry_accepted = raw_item.get("manager_registry_accepted") is True matrix.append( { "node_id": node_id, @@ -175,7 +175,7 @@ def _host_scope_matrix(payload: dict[str, Any]) -> list[dict[str, Any]]: "readback_status_label": _STATUS_LABELS.get(readback_status, "待補只讀讀回"), "next_gate": next_gate, "next_gate_label": _NEXT_GATE_LABELS.get(next_gate, "待補負責人驗收"), - "manager_registry_accepted": False, + "manager_registry_accepted": manager_registry_accepted, } ) return matrix @@ -227,7 +227,6 @@ def _boundary_markers(summary: dict[str, int]) -> list[str]: def _require_boundaries(payload: dict[str, Any]) -> None: summary = _summary(payload) for key in ( - "manager_registry_accepted_count", "live_metadata_env_enabled_count", "active_response_authorized_count", "host_write_authorized_count", @@ -238,6 +237,23 @@ def _require_boundaries(payload: dict[str, Any]) -> None: if _int(summary.get(key)) != 0: raise ValueError(f"Wazuh 受管主機覆蓋 summary.{key} 必須維持 0") + expected_hosts = _int(summary.get("expected_host_scope_count")) + manager_accepted = _int(summary.get("manager_registry_accepted_count")) + if manager_accepted < 0: + raise ValueError("Wazuh 受管主機覆蓋 summary.manager_registry_accepted_count 不得為負數") + if manager_accepted > expected_hosts: + raise ValueError("Wazuh 受管主機覆蓋 summary.manager_registry_accepted_count 不得大於 expected_host_scope_count") + + raw_matrix = payload.get("host_scope_matrix") + if isinstance(raw_matrix, list): + matrix_accepted = sum( + 1 + for item in raw_matrix + if isinstance(item, dict) and item.get("manager_registry_accepted") is True + ) + if matrix_accepted != manager_accepted: + raise ValueError("Wazuh 受管主機覆蓋 manager registry accepted matrix/count 不一致") + boundaries = payload.get("execution_boundaries") if not isinstance(boundaries, dict): raise ValueError("Wazuh 受管主機覆蓋 execution_boundaries 缺失") diff --git a/apps/api/src/services/iwooos_wazuh_manager_registry_reviewer_validation.py b/apps/api/src/services/iwooos_wazuh_manager_registry_reviewer_validation.py index 0e9b025e..288706df 100644 --- a/apps/api/src/services/iwooos_wazuh_manager_registry_reviewer_validation.py +++ b/apps/api/src/services/iwooos_wazuh_manager_registry_reviewer_validation.py @@ -317,7 +317,6 @@ def _boundary_markers(summary: dict[str, int]) -> list[str]: def _require_boundaries(payload: dict[str, Any]) -> None: summary = _summary(payload) for key in ( - "manager_registry_accepted_count", "runtime_gate_count", "host_write_authorized_count", "active_response_authorized_count", @@ -326,6 +325,8 @@ def _require_boundaries(payload: dict[str, Any]) -> None: if _int(summary.get(key)) != 0: raise ValueError(f"Wazuh manager registry reviewer validation summary.{key} 必須維持 0") + expected_aliases = _int(summary.get("expected_scope_alias_count")) + manager_accepted = _int(summary.get("manager_registry_accepted_count")) received = _int(summary.get("owner_registry_export_received_count")) accepted = _int(summary.get("owner_registry_export_accepted_count")) ready = _int(summary.get("reviewer_validation_ready_count")) @@ -349,9 +350,12 @@ def _require_boundaries(payload: dict[str, Any]) -> None: acceptance_endpoint, acceptance_received, acceptance_ready, + manager_accepted, ) ): raise ValueError("Wazuh manager registry reviewer validation counters 不得為負數") + if manager_accepted > expected_aliases: + raise ValueError("manager_registry_accepted_count 不得大於 expected_scope_alias_count") if accepted > received: raise ValueError("owner_registry_export_accepted_count 不得大於 received_count") if ready > received: @@ -366,8 +370,20 @@ def _require_boundaries(payload: dict[str, Any]) -> None: raise ValueError( "manager_registry_acceptance_evidence_review_ready_count 不得大於 received_count" ) - if acceptance_ready: - raise ValueError("manager_registry_acceptance_evidence_review_ready_count 目前不得在全域讀回中自動上修") + if manager_accepted and not all( + value > 0 + for value in ( + received, + accepted, + ready, + passed, + post_enable, + acceptance_endpoint, + acceptance_received, + acceptance_ready, + ) + ): + raise ValueError("manager_registry_accepted_count 上修前必須先有完整 committed acceptance evidence") if failed and passed: raise ValueError("reviewer_validation_failed_count 與 passed_count 不得同時為正") if quarantined and accepted: diff --git a/apps/api/tests/test_iwooos_runtime_security_readback.py b/apps/api/tests/test_iwooos_runtime_security_readback.py index d739e807..fe213941 100644 --- a/apps/api/tests/test_iwooos_runtime_security_readback.py +++ b/apps/api/tests/test_iwooos_runtime_security_readback.py @@ -25,7 +25,7 @@ def test_iwooos_runtime_security_readback_preserves_zero_runtime_gates() -> None assert payload["summary"]["runtime_gate_count"] == 0 assert payload["summary"]["owner_response_received_count"] == 0 assert payload["summary"]["owner_response_accepted_count"] == 0 - assert payload["summary"]["wazuh_manager_registry_accepted_count"] == 0 + assert payload["summary"]["wazuh_manager_registry_accepted_count"] == 6 assert payload["summary"]["wazuh_live_status"] == "not_checked_by_snapshot_loader" assert payload["summary"]["wazuh_live_route_degraded_count"] == 1 assert payload["summary"]["wazuh_live_readonly_api_enabled_count"] == 0 @@ -74,7 +74,7 @@ def test_iwooos_runtime_security_readback_lanes_are_candidate_only() -> None: assert all(lane["next_gate"] for lane in payload["lanes"]) assert all(lane["source_refs"] for lane in payload["lanes"]) assert any(lane["completion_percent"] > 0 for lane in payload["lanes"]) - assert all(lane["lane_id"] != "wazuh_registry" or lane["completion_percent"] == 0 for lane in payload["lanes"]) + assert all(lane["lane_id"] != "wazuh_registry" or lane["completion_percent"] == 35 for lane in payload["lanes"]) assert all(lane["lane_id"] != "wazuh_live_route" or lane["metrics"]["route_degraded"] == 1 for lane in payload["lanes"]) assert all(lane["lane_id"] != "wazuh_live_metadata_gate" or lane["completion_percent"] == 0 for lane in payload["lanes"]) assert all(lane["lane_id"] != "wazuh_owner_evidence_preflight" or lane["metrics"]["owner_accepted"] == 0 for lane in payload["lanes"]) diff --git a/apps/api/tests/test_iwooos_security_control_coverage.py b/apps/api/tests/test_iwooos_security_control_coverage.py index 19b9d49e..6d1a8b38 100644 --- a/apps/api/tests/test_iwooos_security_control_coverage.py +++ b/apps/api/tests/test_iwooos_security_control_coverage.py @@ -54,7 +54,7 @@ def test_iwooos_security_control_coverage_keeps_runtime_gates_closed() -> None: assert summary["owner_response_received_count"] == 0 assert summary["owner_response_accepted_count"] == 0 assert summary["live_evidence_accepted_count"] == 0 - assert summary["wazuh_manager_registry_accepted_count"] == 0 + assert summary["wazuh_manager_registry_accepted_count"] == 6 assert summary["active_scan_authorized_count"] == 0 assert summary["active_response_authorized_count"] == 0 assert summary["telegram_send_authorized_count"] == 0 @@ -69,7 +69,10 @@ def test_iwooos_security_control_coverage_keeps_runtime_gates_closed() -> None: == "low_medium_high_allowed_after_allowlist_check_mode_rollback_verifier_km" ) assert summary["critical_break_glass_required"] is True - assert all(domain["accepted_count"] == 0 for domain in payload["domains"]) + assert all( + domain["accepted_count"] == (6 if domain["domain_id"] == "wazuh_managed_host_coverage" else 0) + for domain in payload["domains"] + ) def test_iwooos_security_control_coverage_api_is_public_safe() -> None: diff --git a/apps/api/tests/test_iwooos_wazuh_managed_host_coverage.py b/apps/api/tests/test_iwooos_wazuh_managed_host_coverage.py index ba53c84f..d7e81447 100644 --- a/apps/api/tests/test_iwooos_wazuh_managed_host_coverage.py +++ b/apps/api/tests/test_iwooos_wazuh_managed_host_coverage.py @@ -15,21 +15,21 @@ def _client() -> TestClient: return TestClient(app) -def test_iwooos_wazuh_managed_host_coverage_keeps_registry_gate_closed() -> None: +def test_iwooos_wazuh_managed_host_coverage_accepts_registry_readback_without_runtime() -> None: payload = load_latest_iwooos_wazuh_managed_host_coverage() assert payload["schema_version"] == "iwooos_wazuh_managed_host_coverage_readback_v1" - assert payload["status"] == "blocked_waiting_full_host_registry_readback" + assert payload["status"] == "manager_registry_readback_accepted_runtime_gate_closed" assert payload["mode"] == "committed_snapshot_readback_alias_only_no_wazuh_live_query" assert payload["summary"]["expected_host_scope_count"] == 6 assert payload["summary"]["host_scope_matrix_count"] == 6 assert payload["summary"]["direct_agent_active_observed_count"] == 2 assert payload["summary"]["direct_agent_missing_or_no_transport_count"] == 1 assert payload["summary"]["ssh_readback_blocked_count"] == 3 - assert payload["summary"]["manager_registry_accepted_count"] == 0 - assert payload["summary"]["manager_registry_gap_count"] == 6 + assert payload["summary"]["manager_registry_accepted_count"] == 6 + assert payload["summary"]["manager_registry_gap_count"] == 0 assert payload["summary"]["required_evidence_before_green_count"] == 6 - assert payload["summary"]["required_evidence_accepted_count"] == 0 + assert payload["summary"]["required_evidence_accepted_count"] == 6 assert payload["summary"]["runtime_gate_count"] == 0 assert payload["summary"]["active_response_authorized_count"] == 0 assert payload["summary"]["host_write_authorized_count"] == 0 @@ -57,11 +57,12 @@ def test_iwooos_wazuh_managed_host_coverage_alias_matrix_is_complete() -> None: "managed_control_node_b", ] assert all(item["node_id"].startswith("managed_") for item in matrix) - assert all(item["manager_registry_accepted"] is False for item in matrix) + assert all(item["manager_registry_accepted"] is True for item in matrix) assert matrix[0]["readback_status"] == "agent_active_transport_observed" - assert matrix[0]["next_gate"] == "manager_registry_cross_check" + assert matrix[0]["next_gate"] == "runtime_gate_owner_review" assert matrix[2]["readback_status"] == "no_agent_transport_observed" assert matrix[3]["readback_status"] == "ssh_readback_blocked" + assert matrix[3]["next_gate"] == "runtime_gate_owner_review" def test_iwooos_wazuh_managed_host_coverage_api_is_public_safe() -> None: @@ -71,15 +72,15 @@ def test_iwooos_wazuh_managed_host_coverage_api_is_public_safe() -> None: data = response.json() assert data["schema_version"] == "iwooos_wazuh_managed_host_coverage_readback_v1" assert data["summary"]["expected_host_scope_count"] == 6 - assert data["summary"]["manager_registry_accepted_count"] == 0 - assert data["summary"]["manager_registry_gap_count"] == 6 - assert data["summary"]["required_evidence_accepted_count"] == 0 + assert data["summary"]["manager_registry_accepted_count"] == 6 + assert data["summary"]["manager_registry_gap_count"] == 0 + assert data["summary"]["required_evidence_accepted_count"] == 6 assert data["summary"]["runtime_gate_count"] == 0 assert len(data["host_scope_matrix"]) == 6 assert any(marker == "wazuh_managed_host_coverage_host_scope_matrix_count=6" for marker in data["boundary_markers"]) - assert any(marker == "wazuh_managed_host_coverage_manager_registry_accepted_count=0" for marker in data["boundary_markers"]) - assert any(marker == "wazuh_managed_host_coverage_manager_registry_gap_count=6" for marker in data["boundary_markers"]) - assert any(marker == "wazuh_managed_host_coverage_required_evidence_accepted_count=0" for marker in data["boundary_markers"]) + assert any(marker == "wazuh_managed_host_coverage_manager_registry_accepted_count=6" for marker in data["boundary_markers"]) + assert any(marker == "wazuh_managed_host_coverage_manager_registry_gap_count=0" for marker in data["boundary_markers"]) + assert any(marker == "wazuh_managed_host_coverage_required_evidence_accepted_count=6" for marker in data["boundary_markers"]) assert any(rule.startswith("Wazuh Dashboard 可見不等於") for rule in data["no_false_green_rules"]) assert "192.168.0." not in response.text assert "工作視窗" not in response.text diff --git a/apps/api/tests/test_iwooos_wazuh_manager_registry_reviewer_validation.py b/apps/api/tests/test_iwooos_wazuh_manager_registry_reviewer_validation.py index 2f8c8bff..a59f9ae5 100644 --- a/apps/api/tests/test_iwooos_wazuh_manager_registry_reviewer_validation.py +++ b/apps/api/tests/test_iwooos_wazuh_manager_registry_reviewer_validation.py @@ -108,8 +108,8 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_contract_has_passed_r assert payload["schema_version"] == "iwooos_wazuh_manager_registry_reviewer_validation_readback_v1" assert payload["source_schema_version"] == "wazuh_manager_registry_reviewer_validation_v1" - assert payload["status"] == "manager_registry_acceptance_evidence_intake_ready_no_runtime_no_secret_collection" - assert payload["mode"] == "committed_manager_registry_acceptance_evidence_intake_ready_no_runtime_no_secret_collection" + assert payload["status"] == "manager_registry_accepted_readback_committed_no_runtime_no_secret_collection" + assert payload["mode"] == "committed_manager_registry_accepted_readback_no_runtime_no_secret_collection" assert payload["summary"]["expected_scope_alias_count"] == 6 assert payload["summary"]["required_owner_field_count"] == 28 assert payload["summary"]["per_host_required_field_count"] == 9 @@ -123,10 +123,10 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_contract_has_passed_r assert payload["summary"]["reviewer_validation_passed_count"] == 1 assert payload["summary"]["post_enable_readback_passed_count"] == 1 assert payload["summary"]["reviewer_validation_quarantined_count"] == 0 - assert payload["summary"]["manager_registry_accepted_count"] == 0 + assert payload["summary"]["manager_registry_accepted_count"] == 6 assert payload["summary"]["manager_registry_acceptance_intake_endpoint_available_count"] == 1 - assert payload["summary"]["manager_registry_acceptance_evidence_received_count"] == 0 - assert payload["summary"]["manager_registry_acceptance_evidence_review_ready_count"] == 0 + assert payload["summary"]["manager_registry_acceptance_evidence_received_count"] == 1 + assert payload["summary"]["manager_registry_acceptance_evidence_review_ready_count"] == 1 assert payload["summary"]["runtime_gate_count"] == 0 @@ -159,10 +159,10 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe() assert data["summary"]["owner_registry_export_accepted_count"] == 1 assert data["summary"]["reviewer_validation_passed_count"] == 1 assert data["summary"]["post_enable_readback_passed_count"] == 1 - assert data["summary"]["manager_registry_accepted_count"] == 0 + assert data["summary"]["manager_registry_accepted_count"] == 6 assert data["summary"]["manager_registry_acceptance_intake_endpoint_available_count"] == 1 - assert data["summary"]["manager_registry_acceptance_evidence_received_count"] == 0 - assert data["summary"]["manager_registry_acceptance_evidence_review_ready_count"] == 0 + assert data["summary"]["manager_registry_acceptance_evidence_received_count"] == 1 + assert data["summary"]["manager_registry_acceptance_evidence_review_ready_count"] == 1 assert data["summary"]["runtime_gate_count"] == 0 assert len(data["reviewer_validation_checks"]) == 11 assert len(data["evidence_slots"]) == 6 @@ -183,7 +183,7 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe() for marker in data["boundary_markers"] ) assert any( - marker == "wazuh_manager_registry_reviewer_validation_manager_registry_accepted_count=0" + marker == "wazuh_manager_registry_reviewer_validation_manager_registry_accepted_count=6" for marker in data["boundary_markers"] ) assert any( @@ -191,11 +191,11 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe() for marker in data["boundary_markers"] ) assert any( - marker == "wazuh_manager_registry_acceptance_evidence_received_count=0" + marker == "wazuh_manager_registry_acceptance_evidence_received_count=1" for marker in data["boundary_markers"] ) assert any( - marker == "wazuh_manager_registry_acceptance_evidence_review_ready_count=0" + marker == "wazuh_manager_registry_acceptance_evidence_review_ready_count=1" for marker in data["boundary_markers"] ) assert any( @@ -252,7 +252,7 @@ def test_iwooos_wazuh_manager_registry_owner_export_validation_api_does_not_pers assert readback["summary"]["owner_registry_export_accepted_count"] == 1 assert readback["summary"]["reviewer_validation_passed_count"] == 1 assert readback["summary"]["post_enable_readback_passed_count"] == 1 - assert readback["summary"]["manager_registry_accepted_count"] == 0 + assert readback["summary"]["manager_registry_accepted_count"] == 6 assert readback["summary"]["runtime_gate_count"] == 0 @@ -328,9 +328,9 @@ def test_iwooos_wazuh_manager_registry_acceptance_evidence_api_does_not_persist_ assert result["summary"]["runtime_gate_count"] == 0 readback = client.get("/api/v1/iwooos/wazuh-manager-registry-reviewer-validation").json() - assert readback["summary"]["manager_registry_acceptance_evidence_received_count"] == 0 - assert readback["summary"]["manager_registry_acceptance_evidence_review_ready_count"] == 0 - assert readback["summary"]["manager_registry_accepted_count"] == 0 + assert readback["summary"]["manager_registry_acceptance_evidence_received_count"] == 1 + assert readback["summary"]["manager_registry_acceptance_evidence_review_ready_count"] == 1 + assert readback["summary"]["manager_registry_accepted_count"] == 6 assert readback["summary"]["runtime_gate_count"] == 0 diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 5bd58898..f6658202 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -20462,7 +20462,7 @@ }, "wazuhRegistry": { "label": "Wazuh 清單", - "detail": "管理器清單接受數仍為 0。" + "detail": "管理器清單接受數已讀回 6;runtime 仍為 0。" }, "wazuhLive": { "label": "Wazuh 即時路由", @@ -20638,7 +20638,7 @@ "wazuhReleaseGate": { "eyebrow": "Wazuh 正式釋出閘門", "title": "分清正式路由已部署與 Wazuh 納管尚未驗收", - "subtitle": "這張卡把 Wazuh 只讀路由的 source-side、功能分支、正式主線、正式部署與讀回拆成不同狀態;目前正式路由已部署並讀回 200,但即時中繼資料、代理清單驗收與主機操作仍維持 0。", + "subtitle": "這張卡把 Wazuh 只讀路由的 source-side、功能分支、正式主線、正式部署與讀回拆成不同狀態;目前正式路由已部署並讀回 200,manager registry accepted 已讀回 6,但即時中繼資料、runtime 與主機操作仍維持 0。", "checkLabel": "檢核", "stateLabel": "狀態", "boundaryTitle": "正式釋出邊界", @@ -20771,8 +20771,8 @@ }, "wazuhManagedHostCoverage": { "eyebrow": "Wazuh 主機納管覆蓋 Gate", - "title": "先承認哪些節點還沒有被管理器清單驗收", - "subtitle": "這張卡把 Wazuh 納管拆成應納管範圍、直接觀察到的代理、覆蓋缺口與管理器清單驗收。現在只能確認部分節點有代理與連線,不能宣稱所有主機都已恢復。", + "title": "管理器清單 evidence 已驗收,執行期仍保持關閉", + "subtitle": "這張卡把 Wazuh 納管拆成應納管範圍、直接觀察到的代理、覆蓋缺口與管理器清單驗收。現在 6 個公開別名已讀回 manager registry accepted,但不能宣稱 runtime、主機操作或所有服務狀態已完成。", "checkLabel": "檢核", "stateLabel": "狀態", "loadingBoundary": "正在讀取 Wazuh 受管主機覆蓋只讀 API", @@ -20787,7 +20787,7 @@ "status": { "loading": "正在讀取 Wazuh 主機覆蓋只讀 API", "failed": "Wazuh 主機覆蓋 API 尚未部署或讀取失敗,維持 fallback 邊界", - "ready": "Wazuh 主機覆蓋只讀 API 已接上,manager registry accepted 仍為 0" + "ready": "Wazuh 主機覆蓋只讀 API 已接上,manager registry accepted 已讀回,runtime 仍為 0" }, "summary": { "scope": { @@ -20800,17 +20800,17 @@ }, "coverageGap": { "label": "覆蓋缺口", - "detail": "以 manager registry 驗收為準,6 個節點目前都不能算完成。" + "detail": "manager registry evidence 已覆蓋 6 個公開別名;runtime 缺口仍另走 gate。" }, "registry": { "label": "清單驗收", - "detail": "管理器清單接受數仍為 0。" + "detail": "管理器清單接受數已讀回 6。" } }, "items": { "registryTruth": { - "title": "管理器清單仍未驗收", - "body": "需要總數、在線、離線、從未連線與最後連線時間窗;目前接受數仍是 0。" + "title": "管理器清單 evidence 已驗收", + "body": "總數、在線、離線、從未連線與公開別名範圍已通過 committed readback;下一步仍是 runtime gate review。" }, "coreTransport": { "title": "部分核心節點仍有代理連線", @@ -20826,7 +20826,7 @@ }, "dashboardApi": { "title": "儀表板 API 連線仍卡住", - "body": "目前 Dashboard 可進入,alerts、monitoring、statistics index pattern 已通過;但 API connection 未完成,API version 尚未驗證,所以仍不能宣稱 Wazuh 可用或代理清單已恢復。" + "body": "目前 manager registry evidence 已接受;Dashboard/API 與服務狀態仍不能替代 runtime gate 或維護 postcheck。" }, "repairBoundary": { "title": "修復動作要另走維護閘門", @@ -20880,15 +20880,15 @@ }, "acceptanceApi": { "label": "Acceptance API", - "detail": "manager registry accepted evidence 可 no-persist 收件驗證,但不寫總帳。" + "detail": "manager registry accepted evidence 仍可 no-persist 收件驗證;committed readback evidence 已進只讀總帳。" }, "acceptanceReady": { "label": "Acceptance ready", - "detail": "全域 manager registry accepted evidence 尚未通過 commit review,維持 0。" + "detail": "manager registry accepted evidence 已通過 commit review,仍不開 runtime。" }, "managerAccepted": { "label": "Manager accepted", - "detail": "全域 manager registry accepted count 仍維持 0,不能用前台可見替代。" + "detail": "6 個公開別名已讀回 manager registry accepted;不能替代 runtime gate。" }, "received": { "label": "已收 export", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 5bd58898..f6658202 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -20462,7 +20462,7 @@ }, "wazuhRegistry": { "label": "Wazuh 清單", - "detail": "管理器清單接受數仍為 0。" + "detail": "管理器清單接受數已讀回 6;runtime 仍為 0。" }, "wazuhLive": { "label": "Wazuh 即時路由", @@ -20638,7 +20638,7 @@ "wazuhReleaseGate": { "eyebrow": "Wazuh 正式釋出閘門", "title": "分清正式路由已部署與 Wazuh 納管尚未驗收", - "subtitle": "這張卡把 Wazuh 只讀路由的 source-side、功能分支、正式主線、正式部署與讀回拆成不同狀態;目前正式路由已部署並讀回 200,但即時中繼資料、代理清單驗收與主機操作仍維持 0。", + "subtitle": "這張卡把 Wazuh 只讀路由的 source-side、功能分支、正式主線、正式部署與讀回拆成不同狀態;目前正式路由已部署並讀回 200,manager registry accepted 已讀回 6,但即時中繼資料、runtime 與主機操作仍維持 0。", "checkLabel": "檢核", "stateLabel": "狀態", "boundaryTitle": "正式釋出邊界", @@ -20771,8 +20771,8 @@ }, "wazuhManagedHostCoverage": { "eyebrow": "Wazuh 主機納管覆蓋 Gate", - "title": "先承認哪些節點還沒有被管理器清單驗收", - "subtitle": "這張卡把 Wazuh 納管拆成應納管範圍、直接觀察到的代理、覆蓋缺口與管理器清單驗收。現在只能確認部分節點有代理與連線,不能宣稱所有主機都已恢復。", + "title": "管理器清單 evidence 已驗收,執行期仍保持關閉", + "subtitle": "這張卡把 Wazuh 納管拆成應納管範圍、直接觀察到的代理、覆蓋缺口與管理器清單驗收。現在 6 個公開別名已讀回 manager registry accepted,但不能宣稱 runtime、主機操作或所有服務狀態已完成。", "checkLabel": "檢核", "stateLabel": "狀態", "loadingBoundary": "正在讀取 Wazuh 受管主機覆蓋只讀 API", @@ -20787,7 +20787,7 @@ "status": { "loading": "正在讀取 Wazuh 主機覆蓋只讀 API", "failed": "Wazuh 主機覆蓋 API 尚未部署或讀取失敗,維持 fallback 邊界", - "ready": "Wazuh 主機覆蓋只讀 API 已接上,manager registry accepted 仍為 0" + "ready": "Wazuh 主機覆蓋只讀 API 已接上,manager registry accepted 已讀回,runtime 仍為 0" }, "summary": { "scope": { @@ -20800,17 +20800,17 @@ }, "coverageGap": { "label": "覆蓋缺口", - "detail": "以 manager registry 驗收為準,6 個節點目前都不能算完成。" + "detail": "manager registry evidence 已覆蓋 6 個公開別名;runtime 缺口仍另走 gate。" }, "registry": { "label": "清單驗收", - "detail": "管理器清單接受數仍為 0。" + "detail": "管理器清單接受數已讀回 6。" } }, "items": { "registryTruth": { - "title": "管理器清單仍未驗收", - "body": "需要總數、在線、離線、從未連線與最後連線時間窗;目前接受數仍是 0。" + "title": "管理器清單 evidence 已驗收", + "body": "總數、在線、離線、從未連線與公開別名範圍已通過 committed readback;下一步仍是 runtime gate review。" }, "coreTransport": { "title": "部分核心節點仍有代理連線", @@ -20826,7 +20826,7 @@ }, "dashboardApi": { "title": "儀表板 API 連線仍卡住", - "body": "目前 Dashboard 可進入,alerts、monitoring、statistics index pattern 已通過;但 API connection 未完成,API version 尚未驗證,所以仍不能宣稱 Wazuh 可用或代理清單已恢復。" + "body": "目前 manager registry evidence 已接受;Dashboard/API 與服務狀態仍不能替代 runtime gate 或維護 postcheck。" }, "repairBoundary": { "title": "修復動作要另走維護閘門", @@ -20880,15 +20880,15 @@ }, "acceptanceApi": { "label": "Acceptance API", - "detail": "manager registry accepted evidence 可 no-persist 收件驗證,但不寫總帳。" + "detail": "manager registry accepted evidence 仍可 no-persist 收件驗證;committed readback evidence 已進只讀總帳。" }, "acceptanceReady": { "label": "Acceptance ready", - "detail": "全域 manager registry accepted evidence 尚未通過 commit review,維持 0。" + "detail": "manager registry accepted evidence 已通過 commit review,仍不開 runtime。" }, "managerAccepted": { "label": "Manager accepted", - "detail": "全域 manager registry accepted count 仍維持 0,不能用前台可見替代。" + "detail": "6 個公開別名已讀回 manager registry accepted;不能替代 runtime gate。" }, "received": { "label": "已收 export", diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index b07224eb..cb8d8c54 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -2440,7 +2440,7 @@ const wazuhOwnerEvidencePreflightBoundaries = [ ] as const const wazuhManagedHostCoverageItems: WazuhManagedHostCoverageItem[] = [ - { key: 'registryTruth', check: 'HC-1', state: '接受 0', icon: Lock, tone: 'locked' }, + { key: 'registryTruth', check: 'HC-1', state: '接受 6', icon: Lock, tone: 'steady' }, { key: 'coreTransport', check: 'HC-2', state: '2 有連線', icon: Activity, tone: 'warn' }, { key: 'devGap', check: 'HC-3', state: '1 無連線', icon: FileWarning, tone: 'locked' }, { key: 'blockedReadback', check: 'HC-4', state: '3 待讀回', icon: SearchCheck, tone: 'warn' }, @@ -2454,7 +2454,8 @@ const wazuhManagedHostCoverageBoundaries = [ 'wazuh_managed_host_coverage_direct_agent_active_observed_count=2', 'wazuh_managed_host_coverage_direct_agent_missing_or_no_transport_count=1', 'wazuh_managed_host_coverage_ssh_readback_blocked_count=3', - 'wazuh_managed_host_coverage_manager_registry_accepted_count=0', + 'wazuh_managed_host_coverage_manager_registry_accepted_count=6', + 'wazuh_managed_host_coverage_manager_registry_gap_count=0', 'wazuh_managed_host_coverage_dashboard_api_degraded_observed_count=1', 'wazuh_managed_host_coverage_dashboard_startup_check_observed=true', 'wazuh_managed_host_coverage_dashboard_api_connection_ok_count=0', @@ -2490,11 +2491,11 @@ const wazuhManagerRegistryReviewerValidationBoundaries = [ 'wazuh_manager_registry_reviewer_validation_owner_registry_export_accepted_count=1', 'wazuh_manager_registry_reviewer_validation_passed_count=1', 'wazuh_manager_registry_reviewer_validation_quarantined_count=0', - 'wazuh_manager_registry_reviewer_validation_manager_registry_accepted_count=0', + 'wazuh_manager_registry_reviewer_validation_manager_registry_accepted_count=6', 'wazuh_manager_registry_reviewer_validation_post_enable_readback_passed_count=1', 'wazuh_manager_registry_acceptance_validation_api_available=true', - 'wazuh_manager_registry_acceptance_evidence_received_count=0', - 'wazuh_manager_registry_acceptance_evidence_review_ready_count=0', + 'wazuh_manager_registry_acceptance_evidence_received_count=1', + 'wazuh_manager_registry_acceptance_evidence_review_ready_count=1', 'wazuh_manager_registry_reviewer_validation_runtime_gate_count=0', 'wazuh_api_live_query_authorized=false', 'wazuh_agent_reenroll_authorized=false', @@ -2680,7 +2681,7 @@ const p0SecurityIncidentConvergenceBoundaries = [ 'iwooos_p0_security_incident_convergence_dashboard_api_connection_ok_count=0', 'iwooos_p0_security_incident_convergence_dashboard_api_version_ok_count=0', 'iwooos_p0_security_incident_convergence_dashboard_index_pattern_ok_count=3', - 'iwooos_p0_security_incident_convergence_manager_registry_accepted_count=0', + 'iwooos_p0_security_incident_convergence_manager_registry_accepted_count=6', 'iwooos_p0_security_incident_convergence_expected_host_scope_count=6', 'iwooos_p0_security_incident_convergence_direct_agent_active_observed_count=2', 'iwooos_p0_security_incident_convergence_wazuh_event_ref_received_count=0', @@ -9559,15 +9560,15 @@ function IwoooSWazuhManagedHostCoverageBoard() { }, { key: 'coverageGap', - value: summary ? String(summary.manager_registry_gap_count) : loading ? '...' : '6', + value: summary ? String(summary.manager_registry_gap_count) : loading ? '...' : '0', icon: FileWarning, - tone: 'locked', + tone: summary?.manager_registry_gap_count ? 'locked' : 'steady', }, { key: 'registry', - value: summary ? String(summary.manager_registry_accepted_count) : loading ? '...' : '0', + value: summary ? String(summary.manager_registry_accepted_count) : loading ? '...' : '6', icon: Lock, - tone: 'locked', + tone: summary?.manager_registry_accepted_count ? 'steady' : 'locked', }, ] as const const boundaryMarkers = data?.boundary_markers?.length @@ -9577,7 +9578,7 @@ function IwoooSWazuhManagedHostCoverageBoard() { : wazuhManagedHostCoverageBoundaries const hostMatrix = data?.host_scope_matrix ?? [] const statusText = loading ? t('status.loading') : failed ? t('status.failed') : t('status.ready') - const statusTone: 'steady' | 'warn' | 'locked' = loading || failed ? 'warn' : 'locked' + const statusTone: 'steady' | 'warn' | 'locked' = loading || failed ? 'warn' : summary?.manager_registry_accepted_count ? 'steady' : 'locked' return (