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 9ba655c8..766fa105 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 @@ -284,7 +284,6 @@ def _require_boundaries(payload: dict[str, Any]) -> None: summary = _summary(payload) for key in ( "manager_registry_accepted_count", - "post_enable_readback_passed_count", "runtime_gate_count", "host_write_authorized_count", "active_response_authorized_count", @@ -299,7 +298,8 @@ def _require_boundaries(payload: dict[str, Any]) -> None: 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)): + post_enable = _int(summary.get("post_enable_readback_passed_count")) + if any(value < 0 for value in (received, accepted, ready, passed, failed, quarantined, post_enable)): raise ValueError("Wazuh manager registry reviewer validation counters 不得為負數") if accepted > received: raise ValueError("owner_registry_export_accepted_count 不得大於 received_count") @@ -307,6 +307,8 @@ def _require_boundaries(payload: dict[str, Any]) -> None: raise ValueError("reviewer_validation_ready_count 不得大於 received_count") if passed > accepted: raise ValueError("reviewer_validation_passed_count 不得大於 accepted_count") + if post_enable > passed: + raise ValueError("post_enable_readback_passed_count 不得大於 reviewer_validation_passed_count") if failed and passed: raise ValueError("reviewer_validation_failed_count 與 passed_count 不得同時為正") if quarantined and accepted: 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 ccadc9c3..dc607c3d 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 @@ -83,19 +83,20 @@ 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"] == "accepted_for_readonly_posture_only" - assert payload["mode"] == "committed_validation_passed_readback_no_runtime_no_secret_collection" + assert payload["status"] == "post_enable_iwooos_readback_passed_no_runtime_no_secret_collection" + assert payload["mode"] == "committed_post_enable_iwooos_readback_passed_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 assert payload["summary"]["reviewer_validation_check_count"] == 10 - assert payload["summary"]["outcome_lane_count"] == 13 + assert payload["summary"]["outcome_lane_count"] == 14 assert payload["summary"]["evidence_slot_count"] == 6 assert payload["summary"]["forbidden_payload_count"] == 27 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"]["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"]["runtime_gate_count"] == 0 @@ -115,7 +116,7 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_evidence_slots_are_ac 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 all(item["next_gate"] == "manager_registry_acceptance_evidence_review" 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"]] @@ -129,6 +130,7 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe() 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"]["post_enable_readback_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 @@ -145,6 +147,10 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe() marker == "wazuh_manager_registry_reviewer_validation_passed_count=1" for marker in data["boundary_markers"] ) + assert any( + marker == "wazuh_manager_registry_reviewer_validation_post_enable_readback_passed_count=1" + for marker in data["boundary_markers"] + ) assert any( marker == "wazuh_manager_registry_reviewer_validation_manager_registry_accepted_count=0" for marker in data["boundary_markers"] @@ -202,6 +208,7 @@ def test_iwooos_wazuh_manager_registry_owner_export_validation_api_does_not_pers 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"]["post_enable_readback_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 0e1a6c98..951a64a6 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -20872,6 +20872,10 @@ "label": "Reviewer passed", "detail": "一筆脫敏 owner export refs 已通過 no-persist reviewer validation。" }, + "postEnable": { + "label": "Post-enable", + "detail": "正式 API 與前台已讀回 reviewer passed;這不是 live Wazuh 查詢授權。" + }, "received": { "label": "已收 export", "detail": "已收到一筆 owner-provided redacted registry export refs。" diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 0e1a6c98..951a64a6 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -20872,6 +20872,10 @@ "label": "Reviewer passed", "detail": "一筆脫敏 owner export refs 已通過 no-persist reviewer validation。" }, + "postEnable": { + "label": "Post-enable", + "detail": "正式 API 與前台已讀回 reviewer passed;這不是 live Wazuh 查詢授權。" + }, "received": { "label": "已收 export", "detail": "已收到一筆 owner-provided redacted registry export refs。" diff --git a/apps/web/src/app/[locale]/governance/page.tsx b/apps/web/src/app/[locale]/governance/page.tsx index e46f2dcf..f00b564a 100644 --- a/apps/web/src/app/[locale]/governance/page.tsx +++ b/apps/web/src/app/[locale]/governance/page.tsx @@ -32,6 +32,21 @@ import { QueueTab } from './tabs/queue-tab' import { AgentMarketTab } from './tabs/agent-market-tab' import { AutomationInventoryTab } from './tabs/automation-inventory-tab' +const GOVERNANCE_SECTION_IDS = [ + 'slo', + 'events', + 'queue', + 'agent-market', + 'automation-inventory', +] as const + +function normalizeGovernanceSectionId(value: string | null): string | undefined { + if (!value) return undefined + return GOVERNANCE_SECTION_IDS.includes(value as (typeof GOVERNANCE_SECTION_IDS)[number]) + ? value + : undefined +} + export default function GovernancePage({ params, }: { @@ -50,16 +65,32 @@ export default function GovernancePage({ const activeSection = governanceSections.find(section => section.id === requestedTab) ?? governanceSections[0] useEffect(() => { - const tab = new URLSearchParams(window.location.search).get('tab') ?? undefined + const hashTab = window.location.hash ? window.location.hash.slice(1) : null + const tab = normalizeGovernanceSectionId( + new URLSearchParams(window.location.search).get('tab') ?? hashTab + ) setRequestedTab(tab) if (!tab) return - const section = document.getElementById(tab) - section?.scrollIntoView({ block: 'start' }) + let cancelled = false + const scrollToRequestedSection = () => { + if (cancelled) return + document.getElementById(tab)?.scrollIntoView({ block: 'start' }) + } + const frame = window.requestAnimationFrame(scrollToRequestedSection) + const timers = [150, 600, 1500, 3000].map(delay => + window.setTimeout(scrollToRequestedSection, delay) + ) + return () => { + cancelled = true + window.cancelAnimationFrame(frame) + timers.forEach(timer => window.clearTimeout(timer)) + } }, []) return ( +
{/* ComplianceBadge 橫幅 — PR 3 接 /governance/compliance-score API */}
@@ -79,7 +110,7 @@ export default function GovernancePage({ return ( +
) } diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index a0929668..c6b2380c 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -2479,7 +2479,7 @@ const wazuhManagerRegistryReviewerValidationBoundaries = [ 'wazuh_manager_registry_reviewer_validation_required_owner_field_count=28', 'wazuh_manager_registry_reviewer_validation_per_host_required_field_count=9', 'wazuh_manager_registry_reviewer_validation_check_count=10', - 'wazuh_manager_registry_reviewer_validation_outcome_lane_count=13', + 'wazuh_manager_registry_reviewer_validation_outcome_lane_count=14', '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=1', @@ -2487,7 +2487,7 @@ const wazuhManagerRegistryReviewerValidationBoundaries = [ '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', + 'wazuh_manager_registry_reviewer_validation_post_enable_readback_passed_count=1', 'wazuh_manager_registry_reviewer_validation_runtime_gate_count=0', 'wazuh_api_live_query_authorized=false', 'wazuh_agent_reenroll_authorized=false', @@ -9822,6 +9822,12 @@ function IwoooSWazuhManagerRegistryReviewerValidationBoard() { icon: ClipboardCheck, tone: 'steady', }, + { + key: 'postEnable', + value: summary ? String(summary.post_enable_readback_passed_count) : loading ? '...' : '1', + icon: SearchCheck, + tone: summary?.post_enable_readback_passed_count ? 'steady' : 'locked', + }, { key: 'received', value: summary ? String(summary.owner_registry_export_received_count) : loading ? '...' : '1', diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 04598a37..ea722cfb 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -78,6 +78,53 @@ - 下一個 P0:把 StockPlatform smoke 改成排程限流或搬到非 110 runner;再做全主機 cold-start scorecard 與資料 freshness readback。 ## 2026-06-27|IwoooS Wazuh owner export reviewer validation passed 本地完成 +## 2026-06-27 — 21:24 GitHub backup owner response intake readiness 正式讀回完成 + +**時間與來源**: +- 2026-06-27 20:49-21:24 Asia/Taipei。 +- 來源:feature branch `codex/github-backup-missing-targets-20260627`、Gitea main、Gitea Actions、production API readback。 + +**完成內容**: +- `GET /api/v1/agents/github-target-private-backup-evidence-gate` 已正式讀回 `owner_response_intake_readiness`。 +- GitHub target owner response request packet、9 個 response templates、allowed fields、forbidden payloads、collection checks、intake preflight checks 與 acceptance checks 已能由 production API 讀出。 +- 每個 target 已能讀回 owner response template mapping 與 `owner_response_execution_authorized=false`。 +- Validator 已防止 owner response request packet、templates 或 checks 夾帶 execution authorization。 + +**Gitea / deploy 狀態**: +- code commit:`80138e985 feat(api): expose github owner response intake readiness`。 +- merge / main push commit:`9f5097f66 Merge remote-tracking branch 'gitea/main' into codex/github-backup-missing-targets-20260627`。 +- deploy marker:`e49c6190e chore(cd): deploy 9f5097f [skip ci]`。 +- code-review:`#3690` 成功。 +- CD:`#3689` 成功;tests、build-and-deploy、post-deploy-checks 均完成。 +- 最新 main 後續 docs marker:`7b2b3db45 docs(awooop): record controlled automation readback [skip ci]`,不改本段 production code 基準。 + +**production API readback**: +- `/api/v1/health?_v=9f5097f66-github-owner-intake`:HTTP `200`、`status=healthy`、`environment=prod`、`mock_mode=false`。 +- `GET /api/v1/agents/github-target-private-backup-evidence-gate?_v=9f5097f66-github-owner-intake`:HTTP `200`。 +- `approval_required_target_count=9`、`private_backup_verified_count=4`、`private_visibility_verified_count=4`。 +- `github_missing_target_resolution_count=5`、`github_missing_target_create_private_repo_ready_count=0`、`github_missing_target_refs_sync_ready_count=0`。 +- `owner_response_request_ready=true`、`owner_response_required_response_item_count=9`、`owner_response_requested_template_count=9`、`owner_response_template_count=9`。 +- `owner_response_allowed_response_field_count=25`、`owner_response_forbidden_payload_count=15`、`owner_response_collection_check_count=6`、`owner_response_intake_preflight_check_count=6`。 +- `owner_response_request_execution_authorized=false`、`owner_response_received_count=0`、`owner_response_accepted_count=0`、`safe_credential_accepted_evidence_count=0`、`execution_ready_count=0`、`blocked_target_count=9`。 +- `owner_response_intake_readiness.status=ready_to_collect_read_only_owner_response_not_authorization`、`request_ready=true`、`execution_authorized=false`、`not_approval=true`。 +- forbidden / redaction proof:`private_clone_url_credential` 仍在 forbidden payloads,`read_only_markdown_response` 是允許 submission mode,`create_github_repo` 仍在 still forbidden,正式 API 回應未命中 `192.168.0.`。 + +**Delivery Workbench readback**: +- `GET /api/v1/agents/delivery-closure-workbench?_v=9f5097f66-github-owner-intake`:HTTP `200`。 +- schema:`delivery_closure_workbench_v1`。 +- GitHub lane:`status=blocked_private_visibility_and_safe_credential_evidence_required`、metric `private_backup_verified=4/9`、`completion_percent=44`、`blocker_count=9`。 + +**仍維持 0 / false**: +- `github_missing_target_create_private_repo_ready_count=0`、`github_missing_target_refs_sync_ready_count=0`、`owner_response_received_count=0`、`owner_response_accepted_count=0`、`safe_credential_accepted_evidence_count=0`、`execution_ready_count=0`。 +- `github_api_write_allowed=false`、`repo_creation_allowed=false`、`visibility_change_allowed=false`、`refs_sync_allowed=false`、`workflow_trigger_allowed=false`、`private_clone_url_collection_allowed=false`、`secret_value_collection_allowed=false`。 + +**未做**: +- 沒有 GitHub repo creation、visibility change、refs sync、workflow trigger、private clone URL collection、secret value collection;沒有 host / Docker / systemd / Nginx / firewall / K8s / DB / Wazuh runtime 寫操作;沒有 force push。 + +**完成度 / 下一步**: +- 本段「GitHub owner response intake readiness API / production readback」:`85% -> 100%`。 +- GitHub backup mirror governance 仍為 blocked;下一個 P0 是 owner-provided safe credential evidence / redacted evidence refs intake,但不得收 private clone URL credential 或 secret value。 + ## 2026-06-27|AwoooP Approvals controlled automation 文案正式讀回完成 **背景**:P2-416 D1N 已把 AI Agent 舊 manual gate 規範改為 controlled automation;low / medium / high 風險應走 allowlist、check-mode、controlled apply、verifier、rollback 與 KM / PlayBook writeback,critical 才 break-glass。正式 `/zh-TW/awooop/approvals` 仍因 Next HTML payload 序列化其他 namespace,殘留 `待人工決策`、`阻塞與人工閘門`、`人工接手`、`owner review`、`owner packet` 等舊語意,容易讓 Approvals / Runs / Work Items / Alerts 看起來把人工當預設終局。 diff --git a/docs/security/wazuh-manager-registry-reviewer-validation.snapshot.json b/docs/security/wazuh-manager-registry-reviewer-validation.snapshot.json index edbb69b9..c9ff32a0 100644 --- a/docs/security/wazuh-manager-registry-reviewer-validation.snapshot.json +++ b/docs/security/wazuh-manager-registry-reviewer-validation.snapshot.json @@ -2,7 +2,7 @@ "evidence_slots": [ { "accepted": true, - "next_gate": "post_enable_iwooos_readback", + "next_gate": "manager_registry_acceptance_evidence_review", "quarantined": false, "received": true, "required_fields": [ @@ -17,7 +17,7 @@ }, { "accepted": true, - "next_gate": "post_enable_iwooos_readback", + "next_gate": "manager_registry_acceptance_evidence_review", "quarantined": false, "received": true, "required_fields": [ @@ -30,7 +30,7 @@ }, { "accepted": true, - "next_gate": "post_enable_iwooos_readback", + "next_gate": "manager_registry_acceptance_evidence_review", "quarantined": false, "received": true, "required_fields": [ @@ -45,7 +45,7 @@ }, { "accepted": true, - "next_gate": "post_enable_iwooos_readback", + "next_gate": "manager_registry_acceptance_evidence_review", "quarantined": false, "received": true, "required_fields": [ @@ -58,7 +58,7 @@ }, { "accepted": true, - "next_gate": "post_enable_iwooos_readback", + "next_gate": "manager_registry_acceptance_evidence_review", "quarantined": false, "received": true, "required_fields": [ @@ -74,7 +74,7 @@ }, { "accepted": true, - "next_gate": "post_enable_iwooos_readback", + "next_gate": "manager_registry_acceptance_evidence_review", "quarantined": false, "received": true, "required_fields": [ @@ -150,10 +150,11 @@ "firewall_change", "nginx_reload" ], - "generated_at": "2026-06-27T20:42:31+08:00", - "mode": "committed_validation_passed_readback_no_runtime_no_secret_collection", + "generated_at": "2026-06-27T21:45:00+08:00", + "mode": "committed_post_enable_iwooos_readback_passed_no_runtime_no_secret_collection", "no_false_green_rules": [ "reviewer validation passed 只代表脫敏 owner export refs 通過 no-persist 驗證。", + "post-enable IwoooS readback passed 只代表 production API / 前台已讀回 reviewer passed,不代表 live Wazuh 查詢或 runtime action。", "owner registry export accepted 不代表 manager_registry_accepted_count 可增加。", "Dashboard 可見、index pattern 三綠勾、HTTP 200 或 transport observed 不可替代 manager registry counts。", "reviewer accepted 只可更新只讀 posture;active response、agent restart、reenroll、host write、secret rotation 或掃描仍需獨立 runtime gate。" @@ -171,7 +172,8 @@ "reject_runtime_action_request", "ready_for_reviewer_validation", "accepted_for_readonly_posture_only", - "waiting_post_enable_iwooos_readback" + "post_enable_iwooos_readback_passed", + "manager_registry_acceptance_evidence_review" ], "per_host_required_fields": [ "node_alias", @@ -271,9 +273,9 @@ }, { "check_id": "RV-10", - "failure_lane": "waiting_post_enable_iwooos_readback", - "required_evidence": "即使 reviewer 未來接受 evidence,也只能進 read-only posture;必須另有 post-enable readback 才能更新 runtime truth。", - "title": "Post-enable IwoooS readback 仍是下一關" + "failure_lane": "post_enable_iwooos_readback_passed_no_runtime", + "required_evidence": "production API 與前台 smoke 已讀回 reviewer passed;此讀回只更新 read-only posture,不查 live Wazuh、不保存 raw payload、不開 runtime gate。", + "title": "Post-enable IwoooS readback 已讀回但不開 runtime" } ], "schema_version": "wazuh_manager_registry_reviewer_validation_v1", @@ -282,7 +284,7 @@ "docs/security/wazuh-agent-visibility-owner-evidence-preflight.snapshot.json", "docs/security/wazuh-managed-host-coverage-gate.snapshot.json" ], - "status": "accepted_for_readonly_posture_only", + "status": "post_enable_iwooos_readback_passed_no_runtime_no_secret_collection", "summary": { "active_response_authorized_count": 0, "evidence_slot_count": 6, @@ -291,11 +293,11 @@ "forbidden_payload_count": 27, "host_write_authorized_count": 0, "manager_registry_accepted_count": 0, - "outcome_lane_count": 13, + "outcome_lane_count": 14, "owner_registry_export_accepted_count": 1, "owner_registry_export_received_count": 1, "per_host_required_field_count": 9, - "post_enable_readback_passed_count": 0, + "post_enable_readback_passed_count": 1, "required_owner_field_count": 28, "reviewer_validation_check_count": 10, "reviewer_validation_failed_count": 0, diff --git a/k8s/awoooi-prod/kustomization.yaml b/k8s/awoooi-prod/kustomization.yaml index 1eb675a9..80be1f35 100644 --- a/k8s/awoooi-prod/kustomization.yaml +++ b/k8s/awoooi-prod/kustomization.yaml @@ -41,7 +41,7 @@ resources: images: - name: 192.168.0.110:5000/library/api:IMAGE_TAG_PLACEHOLDER newName: 192.168.0.110:5000/awoooi/api - newTag: 9f5097f664ef62721c7ea6afa60cb4910da568ef + newTag: 1a8613c9e6641f5d07ab3ff3dad4f8550237d04f - name: 192.168.0.110:5000/library/web:IMAGE_TAG_PLACEHOLDER newName: 192.168.0.110:5000/awoooi/web - newTag: 9f5097f664ef62721c7ea6afa60cb4910da568ef + newTag: 1a8613c9e6641f5d07ab3ff3dad4f8550237d04f diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index 766b52a9..e57a57e5 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -29577,6 +29577,7 @@ def validate(root: Path) -> None: "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_post_enable_readback_passed_count=1", "wazuh_manager_registry_reviewer_validation_manager_registry_accepted_count=0", "wazuh_manager_registry_reviewer_validation_runtime_gate_count=0", ]: @@ -29606,6 +29607,7 @@ def validate(root: Path) -> None: "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_post_enable_readback_passed_count=1", "wazuh_manager_registry_reviewer_validation_manager_registry_accepted_count=0", "wazuh_manager_registry_reviewer_validation_runtime_gate_count=0", ]: diff --git a/scripts/security/wazuh-manager-registry-reviewer-validation.py b/scripts/security/wazuh-manager-registry-reviewer-validation.py index d934250b..ac438819 100644 --- a/scripts/security/wazuh-manager-registry-reviewer-validation.py +++ b/scripts/security/wazuh-manager-registry-reviewer-validation.py @@ -128,9 +128,9 @@ REVIEWER_VALIDATION_CHECKS = [ }, { "check_id": "RV-10", - "title": "Post-enable IwoooS readback 仍是下一關", - "required_evidence": "即使 reviewer 未來接受 evidence,也只能進 read-only posture;必須另有 post-enable readback 才能更新 runtime truth。", - "failure_lane": "waiting_post_enable_iwooos_readback", + "title": "Post-enable IwoooS readback 已讀回但不開 runtime", + "required_evidence": "production API 與前台 smoke 已讀回 reviewer passed;此讀回只更新 read-only posture,不查 live Wazuh、不保存 raw payload、不開 runtime gate。", + "failure_lane": "post_enable_iwooos_readback_passed_no_runtime", }, ] @@ -147,7 +147,8 @@ OUTCOME_LANES = [ "reject_runtime_action_request", "ready_for_reviewer_validation", "accepted_for_readonly_posture_only", - "waiting_post_enable_iwooos_readback", + "post_enable_iwooos_readback_passed", + "manager_registry_acceptance_evidence_review", ] EVIDENCE_SLOTS = [ @@ -292,8 +293,8 @@ def build_snapshot(generated_at: str) -> dict[str, Any]: return { "schema_version": SCHEMA_VERSION, "generated_at": generated_at, - "status": "accepted_for_readonly_posture_only", - "mode": "committed_validation_passed_readback_no_runtime_no_secret_collection", + "status": "post_enable_iwooos_readback_passed_no_runtime_no_secret_collection", + "mode": "committed_post_enable_iwooos_readback_passed_no_runtime_no_secret_collection", "scope": "wazuh_manager_registry_owner_export_reviewer_validation", "source_refs": [ "docs/security/wazuh-agent-visibility-owner-evidence-preflight.snapshot.json", @@ -315,7 +316,7 @@ def build_snapshot(generated_at: str) -> dict[str, Any]: "reviewer_validation_failed_count": 0, "reviewer_validation_quarantined_count": 0, "manager_registry_accepted_count": 0, - "post_enable_readback_passed_count": 0, + "post_enable_readback_passed_count": 1, "runtime_gate_count": 0, "host_write_authorized_count": 0, "active_response_authorized_count": 0, @@ -332,7 +333,7 @@ def build_snapshot(generated_at: str) -> dict[str, Any]: "received": True, "accepted": True, "quarantined": False, - "next_gate": "post_enable_iwooos_readback", + "next_gate": "manager_registry_acceptance_evidence_review", } for slot in EVIDENCE_SLOTS ], @@ -355,6 +356,7 @@ def build_snapshot(generated_at: str) -> dict[str, Any]: }, "no_false_green_rules": [ "reviewer validation passed 只代表脫敏 owner export refs 通過 no-persist 驗證。", + "post-enable IwoooS readback passed 只代表 production API / 前台已讀回 reviewer passed,不代表 live Wazuh 查詢或 runtime action。", "owner registry export accepted 不代表 manager_registry_accepted_count 可增加。", "Dashboard 可見、index pattern 三綠勾、HTTP 200 或 transport observed 不可替代 manager registry counts。", "reviewer accepted 只可更新只讀 posture;active response、agent restart、reenroll、host write、secret rotation 或掃描仍需獨立 runtime gate。", @@ -365,8 +367,12 @@ def build_snapshot(generated_at: str) -> dict[str, Any]: def validate(root: Path) -> None: snapshot = load_json(root / SNAPSHOT_PATH) assert_equal("schema_version", snapshot.get("schema_version"), SCHEMA_VERSION) - assert_equal("status", snapshot.get("status"), "accepted_for_readonly_posture_only") - assert_equal("mode", snapshot.get("mode"), "committed_validation_passed_readback_no_runtime_no_secret_collection") + assert_equal("status", snapshot.get("status"), "post_enable_iwooos_readback_passed_no_runtime_no_secret_collection") + assert_equal( + "mode", + snapshot.get("mode"), + "committed_post_enable_iwooos_readback_passed_no_runtime_no_secret_collection", + ) assert_equal("scope", snapshot.get("scope"), "wazuh_manager_registry_owner_export_reviewer_validation") assert_equal("expected_scope_aliases", snapshot.get("expected_scope_aliases"), EXPECTED_SCOPE_ALIASES) assert_equal("required_owner_fields", snapshot.get("required_owner_fields"), REQUIRED_OWNER_FIELDS) @@ -394,13 +400,13 @@ def validate(root: Path) -> None: "owner_registry_export_accepted_count", "reviewer_validation_ready_count", "reviewer_validation_passed_count", + "post_enable_readback_passed_count", ]: assert_equal(f"summary.{key}", summary.get(key), 1) for key in [ "reviewer_validation_failed_count", "reviewer_validation_quarantined_count", "manager_registry_accepted_count", - "post_enable_readback_passed_count", "runtime_gate_count", "host_write_authorized_count", "active_response_authorized_count", @@ -415,7 +421,11 @@ def validate(root: Path) -> None: assert_equal(f"evidence_slots.{slot.get('slot_id')}.received", slot.get("received"), True) assert_equal(f"evidence_slots.{slot.get('slot_id')}.accepted", slot.get("accepted"), True) assert_false(f"evidence_slots.{slot.get('slot_id')}.quarantined", slot.get("quarantined")) - assert_equal(f"evidence_slots.{slot.get('slot_id')}.next_gate", slot.get("next_gate"), "post_enable_iwooos_readback") + assert_equal( + f"evidence_slots.{slot.get('slot_id')}.next_gate", + slot.get("next_gate"), + "manager_registry_acceptance_evidence_review", + ) boundaries = snapshot.get("execution_boundaries", {}) for key, value in boundaries.items(): @@ -454,6 +464,7 @@ def main() -> None: f"slots={summary['evidence_slot_count']} " f"received={summary['owner_registry_export_received_count']} " f"accepted={summary['owner_registry_export_accepted_count']} " + f"post_enable={summary['post_enable_readback_passed_count']} " f"runtime_gate={summary['runtime_gate_count']}" )