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 934413fb..9ba655c8 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
@@ -148,7 +148,7 @@ def load_latest_iwooos_wazuh_manager_registry_reviewer_validation(
"schema_version": "iwooos_wazuh_manager_registry_reviewer_validation_readback_v1",
"source_schema_version": snapshot["schema_version"],
"status": snapshot.get("status", "waiting_owner_registry_export_for_reviewer_validation"),
- "mode": "committed_validation_contract_readback_no_runtime_no_secret_collection",
+ "mode": snapshot.get("mode", "committed_validation_contract_readback_no_runtime_no_secret_collection"),
"source_refs": [
f"docs/security/{_SNAPSHOT_FILE}",
"scripts/security/wazuh-manager-registry-reviewer-validation.py",
@@ -283,12 +283,6 @@ def _boundary_markers(summary: dict[str, int]) -> list[str]:
def _require_boundaries(payload: dict[str, Any]) -> None:
summary = _summary(payload)
for key in (
- "owner_registry_export_received_count",
- "owner_registry_export_accepted_count",
- "reviewer_validation_ready_count",
- "reviewer_validation_passed_count",
- "reviewer_validation_failed_count",
- "reviewer_validation_quarantined_count",
"manager_registry_accepted_count",
"post_enable_readback_passed_count",
"runtime_gate_count",
@@ -299,6 +293,25 @@ 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")
+ 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"))
+ passed = _int(summary.get("reviewer_validation_passed_count"))
+ failed = _int(summary.get("reviewer_validation_failed_count"))
+ quarantined = _int(summary.get("reviewer_validation_quarantined_count"))
+ if any(value < 0 for value in (received, accepted, ready, passed, failed, quarantined)):
+ raise ValueError("Wazuh manager registry reviewer validation counters 不得為負數")
+ if accepted > received:
+ raise ValueError("owner_registry_export_accepted_count 不得大於 received_count")
+ if ready > received:
+ raise ValueError("reviewer_validation_ready_count 不得大於 received_count")
+ if passed > accepted:
+ raise ValueError("reviewer_validation_passed_count 不得大於 accepted_count")
+ if failed and passed:
+ raise ValueError("reviewer_validation_failed_count 與 passed_count 不得同時為正")
+ if quarantined and accepted:
+ raise ValueError("reviewer_validation_quarantined_count 與 accepted_count 不得同時為正")
+
boundaries = payload.get("execution_boundaries")
if not isinstance(boundaries, dict):
raise ValueError("Wazuh manager registry reviewer validation execution_boundaries 缺失")
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 7b943d95..ccadc9c3 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
@@ -78,13 +78,13 @@ def _valid_owner_export() -> dict:
}
-def test_iwooos_wazuh_manager_registry_reviewer_validation_contract_is_waiting_only() -> None:
+def test_iwooos_wazuh_manager_registry_reviewer_validation_contract_has_passed_redacted_export() -> None:
payload = load_latest_iwooos_wazuh_manager_registry_reviewer_validation()
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"] == "waiting_owner_registry_export_for_reviewer_validation"
- assert payload["mode"] == "committed_validation_contract_readback_no_runtime_no_secret_collection"
+ assert payload["status"] == "accepted_for_readonly_posture_only"
+ assert payload["mode"] == "committed_validation_passed_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
@@ -92,15 +92,16 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_contract_is_waiting_o
assert payload["summary"]["outcome_lane_count"] == 13
assert payload["summary"]["evidence_slot_count"] == 6
assert payload["summary"]["forbidden_payload_count"] == 27
- assert payload["summary"]["owner_registry_export_received_count"] == 0
- assert payload["summary"]["owner_registry_export_accepted_count"] == 0
- assert payload["summary"]["reviewer_validation_passed_count"] == 0
+ assert payload["summary"]["owner_registry_export_received_count"] == 1
+ assert payload["summary"]["owner_registry_export_accepted_count"] == 1
+ assert payload["summary"]["reviewer_validation_ready_count"] == 1
+ assert payload["summary"]["reviewer_validation_passed_count"] == 1
assert payload["summary"]["reviewer_validation_quarantined_count"] == 0
assert payload["summary"]["manager_registry_accepted_count"] == 0
assert payload["summary"]["runtime_gate_count"] == 0
-def test_iwooos_wazuh_manager_registry_reviewer_validation_evidence_slots_are_closed() -> None:
+def test_iwooos_wazuh_manager_registry_reviewer_validation_evidence_slots_are_accepted_only_for_posture() -> None:
payload = load_latest_iwooos_wazuh_manager_registry_reviewer_validation()
assert [item["slot_id"] for item in payload["evidence_slots"]] == [
@@ -111,9 +112,10 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_evidence_slots_are_cl
"owner_response_and_rollback_owner",
"post_enable_iwooos_readback",
]
- assert all(item["received"] is False for item in payload["evidence_slots"])
- assert all(item["accepted"] is False for item in payload["evidence_slots"])
+ assert all(item["received"] is True for item in payload["evidence_slots"])
+ assert all(item["accepted"] is True for item in payload["evidence_slots"])
assert all(item["quarantined"] is False for item in payload["evidence_slots"])
+ assert all(item["next_gate"] == "post_enable_iwooos_readback" for item in payload["evidence_slots"])
assert "managed_core_node_a" in payload["expected_scope_aliases"]
assert "manager_registry_agent_counts" in [item["slot_id"] for item in payload["evidence_slots"]]
@@ -124,18 +126,23 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe()
assert response.status_code == 200
data = response.json()
assert data["schema_version"] == "iwooos_wazuh_manager_registry_reviewer_validation_readback_v1"
- assert data["summary"]["owner_registry_export_received_count"] == 0
- assert data["summary"]["owner_registry_export_accepted_count"] == 0
+ assert data["summary"]["owner_registry_export_received_count"] == 1
+ assert data["summary"]["owner_registry_export_accepted_count"] == 1
+ assert data["summary"]["reviewer_validation_passed_count"] == 1
assert data["summary"]["manager_registry_accepted_count"] == 0
assert data["summary"]["runtime_gate_count"] == 0
assert len(data["reviewer_validation_checks"]) == 10
assert len(data["evidence_slots"]) == 6
assert any(
- marker == "wazuh_manager_registry_reviewer_validation_owner_registry_export_received_count=0"
+ marker == "wazuh_manager_registry_reviewer_validation_owner_registry_export_received_count=1"
for marker in data["boundary_markers"]
)
assert any(
- marker == "wazuh_manager_registry_reviewer_validation_owner_registry_export_accepted_count=0"
+ marker == "wazuh_manager_registry_reviewer_validation_owner_registry_export_accepted_count=1"
+ for marker in data["boundary_markers"]
+ )
+ assert any(
+ marker == "wazuh_manager_registry_reviewer_validation_passed_count=1"
for marker in data["boundary_markers"]
)
assert any(
@@ -146,7 +153,7 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe()
marker == "wazuh_manager_registry_reviewer_validation_runtime_gate_count=0"
for marker in data["boundary_markers"]
)
- assert any(rule.startswith("reviewer validation contract 可見") for rule in data["no_false_green_rules"])
+ assert any(rule.startswith("reviewer validation passed") for rule in data["no_false_green_rules"])
assert data["boundaries"]["runtime_execution_authorized"] is False
assert data["boundaries"]["wazuh_active_response_authorized"] is False
assert data["boundaries"]["host_write_authorized"] is False
@@ -177,7 +184,7 @@ def test_iwooos_wazuh_manager_registry_owner_export_validation_accepts_redacted_
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:
+def test_iwooos_wazuh_manager_registry_owner_export_validation_api_does_not_persist_payload_or_open_runtime() -> None:
client = _client()
response = client.post(
"/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-owner-export",
@@ -192,8 +199,9 @@ def test_iwooos_wazuh_manager_registry_owner_export_validation_api_does_not_upda
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"]["owner_registry_export_received_count"] == 1
+ assert readback["summary"]["owner_registry_export_accepted_count"] == 1
+ assert readback["summary"]["reviewer_validation_passed_count"] == 1
assert readback["summary"]["manager_registry_accepted_count"] == 0
assert readback["summary"]["runtime_gate_count"] == 0
diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json
index b465eb38..c3100321 100644
--- a/apps/web/messages/en.json
+++ b/apps/web/messages/en.json
@@ -20837,7 +20837,7 @@
"wazuhManagerRegistryReviewerValidation": {
"eyebrow": "Wazuh manager registry reviewer validation",
"title": "Owner export 進來後,先由 reviewer 驗收脫敏清單",
- "subtitle": "這張卡固定 Wazuh manager registry owner export 的驗收規則:欄位、計數、公開別名矩陣、Dashboard API 修復讀回、唯讀 credential metadata、拒收內容與下一個 Gate 都先可視化;目前尚未收到、尚未接受,也不開 runtime。",
+ "subtitle": "這張卡固定 Wazuh manager registry owner export 的驗收規則:欄位、計數、公開別名矩陣、Dashboard API 修復讀回、唯讀 credential metadata、拒收內容與下一個 Gate 都先可視化;目前已有一筆脫敏 evidence refs 通過 reviewer validation,但仍不開 runtime。",
"loadingBoundary": "正在讀取 Wazuh manager registry reviewer validation API",
"validationEndpointLabel": "脫敏 owner export 驗證端點",
"validationModeLabel": "驗證模式",
@@ -20849,11 +20849,11 @@
"checksLoading": "正在讀取 reviewer checks。",
"checksFallback": "Reviewer checks 尚未由正式 API 讀回,維持 fallback 停止線。",
"boundaryTitle": "Reviewer validation 停止線",
- "boundaryIntro": "以下鍵值固定:reviewer validation contract 可見不代表 owner export 已收到;export received 不代表 accepted;accepted 也不代表 active response、agent restart、host write、secret rotation 或 runtime gate 已授權。",
+ "boundaryIntro": "以下鍵值固定:reviewer validation passed 只代表脫敏 evidence refs 通過 no-persist 驗證;accepted 不代表 manager registry accepted、active response、agent restart、host write、secret rotation 或 runtime gate 已授權。",
"status": {
"loading": "正在讀取 Wazuh manager registry reviewer validation",
"failed": "Wazuh manager registry reviewer validation API 尚未部署或讀取失敗",
- "ready": "Wazuh manager registry reviewer validation 已讀回;owner export、accepted 與 runtime 仍為 0"
+ "ready": "Wazuh manager registry reviewer validation 已讀回;一筆脫敏 export 已通過,runtime 仍為 0"
},
"summary": {
"aliases": {
@@ -20868,13 +20868,17 @@
"label": "Evidence slots",
"detail": "6 個 slots 對應 manager counts、逐主機矩陣、Dashboard、credential、owner 與 postcheck。"
},
+ "passed": {
+ "label": "Reviewer passed",
+ "detail": "一筆脫敏 owner export refs 已通過 no-persist reviewer validation。"
+ },
"received": {
"label": "已收 export",
- "detail": "目前尚未收到 owner-provided redacted registry export。"
+ "detail": "已收到一筆 owner-provided redacted registry export refs。"
},
"accepted": {
"label": "已接受",
- "detail": "Reviewer 尚未接受任何 manager registry evidence。"
+ "detail": "Reviewer 接受只讀 posture evidence,不代表主機納管完成。"
},
"runtime": {
"label": "執行期",
diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json
index b465eb38..c3100321 100644
--- a/apps/web/messages/zh-TW.json
+++ b/apps/web/messages/zh-TW.json
@@ -20837,7 +20837,7 @@
"wazuhManagerRegistryReviewerValidation": {
"eyebrow": "Wazuh manager registry reviewer validation",
"title": "Owner export 進來後,先由 reviewer 驗收脫敏清單",
- "subtitle": "這張卡固定 Wazuh manager registry owner export 的驗收規則:欄位、計數、公開別名矩陣、Dashboard API 修復讀回、唯讀 credential metadata、拒收內容與下一個 Gate 都先可視化;目前尚未收到、尚未接受,也不開 runtime。",
+ "subtitle": "這張卡固定 Wazuh manager registry owner export 的驗收規則:欄位、計數、公開別名矩陣、Dashboard API 修復讀回、唯讀 credential metadata、拒收內容與下一個 Gate 都先可視化;目前已有一筆脫敏 evidence refs 通過 reviewer validation,但仍不開 runtime。",
"loadingBoundary": "正在讀取 Wazuh manager registry reviewer validation API",
"validationEndpointLabel": "脫敏 owner export 驗證端點",
"validationModeLabel": "驗證模式",
@@ -20849,11 +20849,11 @@
"checksLoading": "正在讀取 reviewer checks。",
"checksFallback": "Reviewer checks 尚未由正式 API 讀回,維持 fallback 停止線。",
"boundaryTitle": "Reviewer validation 停止線",
- "boundaryIntro": "以下鍵值固定:reviewer validation contract 可見不代表 owner export 已收到;export received 不代表 accepted;accepted 也不代表 active response、agent restart、host write、secret rotation 或 runtime gate 已授權。",
+ "boundaryIntro": "以下鍵值固定:reviewer validation passed 只代表脫敏 evidence refs 通過 no-persist 驗證;accepted 不代表 manager registry accepted、active response、agent restart、host write、secret rotation 或 runtime gate 已授權。",
"status": {
"loading": "正在讀取 Wazuh manager registry reviewer validation",
"failed": "Wazuh manager registry reviewer validation API 尚未部署或讀取失敗",
- "ready": "Wazuh manager registry reviewer validation 已讀回;owner export、accepted 與 runtime 仍為 0"
+ "ready": "Wazuh manager registry reviewer validation 已讀回;一筆脫敏 export 已通過,runtime 仍為 0"
},
"summary": {
"aliases": {
@@ -20868,13 +20868,17 @@
"label": "Evidence slots",
"detail": "6 個 slots 對應 manager counts、逐主機矩陣、Dashboard、credential、owner 與 postcheck。"
},
+ "passed": {
+ "label": "Reviewer passed",
+ "detail": "一筆脫敏 owner export refs 已通過 no-persist reviewer validation。"
+ },
"received": {
"label": "已收 export",
- "detail": "目前尚未收到 owner-provided redacted registry export。"
+ "detail": "已收到一筆 owner-provided redacted registry export refs。"
},
"accepted": {
"label": "已接受",
- "detail": "Reviewer 尚未接受任何 manager registry evidence。"
+ "detail": "Reviewer 接受只讀 posture evidence,不代表主機納管完成。"
},
"runtime": {
"label": "執行期",
diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx
index e37ac42a..a0929668 100644
--- a/apps/web/src/app/[locale]/iwooos/page.tsx
+++ b/apps/web/src/app/[locale]/iwooos/page.tsx
@@ -2482,9 +2482,9 @@ const wazuhManagerRegistryReviewerValidationBoundaries = [
'wazuh_manager_registry_reviewer_validation_outcome_lane_count=13',
'wazuh_manager_registry_reviewer_validation_evidence_slot_count=6',
'wazuh_manager_registry_reviewer_validation_forbidden_payload_count=27',
- 'wazuh_manager_registry_reviewer_validation_owner_registry_export_received_count=0',
- 'wazuh_manager_registry_reviewer_validation_owner_registry_export_accepted_count=0',
- 'wazuh_manager_registry_reviewer_validation_passed_count=0',
+ 'wazuh_manager_registry_reviewer_validation_owner_registry_export_received_count=1',
+ '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_post_enable_readback_passed_count=0',
@@ -9817,22 +9817,22 @@ function IwoooSWazuhManagerRegistryReviewerValidationBoard() {
tone: 'steady',
},
{
- key: 'slots',
- value: summary ? String(summary.evidence_slot_count) : loading ? '...' : '6',
+ key: 'passed',
+ value: summary ? String(summary.reviewer_validation_passed_count) : loading ? '...' : '1',
icon: ClipboardCheck,
- tone: 'warn',
+ tone: 'steady',
},
{
key: 'received',
- value: summary ? String(summary.owner_registry_export_received_count) : loading ? '...' : '0',
+ value: summary ? String(summary.owner_registry_export_received_count) : loading ? '...' : '1',
icon: FileWarning,
- tone: 'locked',
+ tone: summary?.owner_registry_export_received_count ? 'steady' : 'locked',
},
{
key: 'accepted',
- value: summary ? String(summary.owner_registry_export_accepted_count) : loading ? '...' : '0',
+ value: summary ? String(summary.owner_registry_export_accepted_count) : loading ? '...' : '1',
icon: Lock,
- tone: 'locked',
+ tone: summary?.owner_registry_export_accepted_count ? 'steady' : 'locked',
},
{
key: 'runtime',
@@ -9925,8 +9925,8 @@ function IwoooSWazuhManagerRegistryReviewerValidationBoard() {
{slot.slot_id}