Compare commits

..

6 Commits

Author SHA1 Message Date
Your Name
f477a2243b docs(governance): open non-critical gates to controlled apply 2026-06-28 01:37:10 +08:00
Your Name
e0345ca130 fix(reboot): accept disabled 110 runners in cold-start check
Some checks are pending
Code Review / ai-code-review (push) Waiting to run
Ansible / Reboot Recovery Contract / validate (push) Successful in 1m12s
2026-06-28 01:23:14 +08:00
Your Name
48b192fab7 docs(awooop): record controlled automation copy guard [skip ci] 2026-06-27 22:53:29 +08:00
Your Name
0e2e8057cf feat(iwooos): validate manager registry acceptance evidence
Some checks failed
Ansible / Reboot Recovery Contract / validate (push) Successful in 1m14s
Code Review / ai-code-review (push) Has been cancelled
CD Pipeline / tests (push) Successful in 1m41s
E2E Health Check / e2e-health (push) Successful in 27s
CD Pipeline / build-and-deploy (push) Failing after 2m18s
CD Pipeline / post-deploy-checks (push) Has been skipped
2026-06-27 22:48:47 +08:00
Your Name
0040a595a4 fix(ci): make reboot readiness audit portable without rg
All checks were successful
Ansible / Reboot Recovery Contract / validate (push) Successful in 1m13s
Code Review / ai-code-review (push) Successful in 16s
2026-06-27 22:40:29 +08:00
Your Name
219fc3835c test(awooop): guard controlled automation copy
Some checks failed
Code Review / ai-code-review (push) Has been cancelled
CD Pipeline / tests (push) Successful in 5m14s
CD Pipeline / build-and-deploy (push) Failing after 1m48s
CD Pipeline / post-deploy-checks (push) Has been skipped
2026-06-27 22:39:01 +08:00
18 changed files with 869 additions and 26 deletions

View File

@@ -23,12 +23,31 @@
## 四大核心原則
1. **變更前 → 先讀註解** (理解設計意圖再動手) 🔴
2. **不可逆操作 → 人工確認** (刪除、logOut、DROP、force push)
3. **有疑問 → 先問統帥** (不確定就停下來)
2. **事故級不可逆操作 → break-glass** (secret 明文、DROP / TRUNCATE / destructive restore、reboot、node drain、不可逆 firewall、外部攻擊型掃描、force push、repo / refs 破壞、付費 provider / 成本切換)
3. **非事故級 gate → 受控自動執行** (不要把 owner / read-only / manual / `0 / false` 當阻擋)
4. **任務完成 → 更新 Memory** (不等被問)
---
## 🔴🔴🔴 統帥全面授權2026-06-28
所有 AWOOOI / AwoooP / IwoooS / StockPlatform / VibeWork / MOMO / AwoooGo / 2026FIFA / Agent Bounty / Tsenyang 工作,除 `docs/HARD_RULES.md#commander-blanket-authorization` 定義的事故級硬阻擋外,一律直接進 AI controlled apply。
舊規範、UI、Telegram、snapshot、test 或 Runbook 若出現下列狀態,不得作為停止實作理由:
- `owner_response_received=0`
- `owner_response_accepted=0`
- `runtime_execution_authorized=false`
- `runtime_write_gate=0`
- `apply_allowed=false`
- `read_only=true`
- `manual_required`
- `needs_human=true`
正確動作是 AI 自動補齊 target selector、source-of-truth diff、check-mode / dry-run、rollback、post-apply verifier、KM / PlayBook trust writeback然後推進可驗證、可回滾、低爆炸半徑的實作。
---
## 🔴 絕對禁止 → [HARD_RULES.md](docs/HARD_RULES.md)
## 🔴 文件語言鐵律 → [文件語言規範](docs/HARD_RULES.md#文件語言規範)

View File

@@ -37,6 +37,7 @@ from src.services.iwooos_wazuh_managed_host_coverage import (
)
from src.services.iwooos_wazuh_manager_registry_reviewer_validation import (
load_latest_iwooos_wazuh_manager_registry_reviewer_validation,
validate_iwooos_wazuh_manager_registry_acceptance_evidence as validate_wazuh_manager_registry_acceptance_evidence_payload,
validate_iwooos_wazuh_manager_registry_owner_export as validate_wazuh_manager_registry_owner_export_payload,
)
from src.services.iwooos_wazuh_owner_evidence_preflight import (
@@ -211,6 +212,40 @@ async def validate_iwooos_wazuh_manager_registry_owner_export(owner_export: dict
) from exc
@router.post(
"/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-manager-registry-acceptance",
response_model=dict[str, Any],
summary="驗證 Wazuh manager registry accepted 脫敏 evidence packet",
description=(
"針對單次 owner / reviewer 提供的 redacted Wazuh manager registry acceptance evidence "
"packet 進行 no-persist review readiness validation回傳 accepted-for-review / needs supplement / "
"quarantined / rejected runtime action 分流。此端點不保存 payload、不查 Wazuh API、不讀主機、"
"不重新註冊 agent、不重啟服務、不讀或回傳機密明文、不啟用主動回應、不改 Nginx / Docker / "
"K8s / firewall也不更新 manager registry accepted 總帳。"
),
)
async def validate_iwooos_wazuh_manager_registry_acceptance_evidence(
acceptance_evidence: dict[str, Any],
) -> dict[str, Any]:
"""回傳單次 Wazuh manager registry accepted evidence 的公開安全驗證結果。"""
try:
payload = await asyncio.to_thread(
validate_wazuh_manager_registry_acceptance_evidence_payload,
acceptance_evidence,
)
return redact_public_lan_topology(payload)
except FileNotFoundError as exc:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(exc),
) from exc
except (json.JSONDecodeError, ValueError) as exc:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"IwoooS Wazuh manager registry acceptance evidence 驗證器無效:{exc}",
) from exc
@router.get(
"/api/v1/iwooos/runtime-security-readback",
response_model=dict[str, Any],

View File

@@ -111,6 +111,24 @@ _ACCOUNTABILITY_REQUIRED_FIELDS = {
"postcheck_plan",
}
_ACCEPTANCE_EVIDENCE_REQUIRED_FIELDS = {
"acceptance_reviewer_role",
"acceptance_decision",
"acceptance_decision_reason",
"accepted_scope_aliases",
"accepted_agent_total",
"accepted_agent_active",
"accepted_agent_disconnected",
"accepted_agent_never_connected",
"manager_registry_acceptance_evidence_refs",
"post_enable_readback_ref",
"product_scope_parity_ref",
"reviewed_at",
"followup_owner",
"rollback_owner",
"post_acceptance_verifier_plan",
}
def load_latest_iwooos_wazuh_manager_registry_reviewer_validation(
security_dir: Path | None = None,
@@ -137,6 +155,15 @@ def load_latest_iwooos_wazuh_manager_registry_reviewer_validation(
"reviewer_validation_failed_count": _int(summary.get("reviewer_validation_failed_count")),
"reviewer_validation_quarantined_count": _int(summary.get("reviewer_validation_quarantined_count")),
"manager_registry_accepted_count": _int(summary.get("manager_registry_accepted_count")),
"manager_registry_acceptance_intake_endpoint_available_count": _int(
summary.get("manager_registry_acceptance_intake_endpoint_available_count")
),
"manager_registry_acceptance_evidence_received_count": _int(
summary.get("manager_registry_acceptance_evidence_received_count")
),
"manager_registry_acceptance_evidence_review_ready_count": _int(
summary.get("manager_registry_acceptance_evidence_review_ready_count")
),
"post_enable_readback_passed_count": _int(summary.get("post_enable_readback_passed_count")),
"runtime_gate_count": _int(summary.get("runtime_gate_count")),
"host_write_authorized_count": _int(summary.get("host_write_authorized_count")),
@@ -157,6 +184,10 @@ def load_latest_iwooos_wazuh_manager_registry_reviewer_validation(
"/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-owner-export"
),
"owner_export_validation_mode": "no_persist_validation_no_runtime_action",
"manager_registry_acceptance_validation_endpoint": (
"/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-manager-registry-acceptance"
),
"manager_registry_acceptance_validation_mode": "no_persist_acceptance_evidence_review_no_runtime_action",
"summary": merged_summary,
"expected_scope_aliases": _strings(snapshot.get("expected_scope_aliases")),
"reviewer_validation_checks": _checks(snapshot.get("reviewer_validation_checks")),
@@ -267,6 +298,9 @@ def _boundary_markers(summary: dict[str, int]) -> list[str]:
f"wazuh_manager_registry_reviewer_validation_passed_count={summary['reviewer_validation_passed_count']}",
f"wazuh_manager_registry_reviewer_validation_quarantined_count={summary['reviewer_validation_quarantined_count']}",
f"wazuh_manager_registry_reviewer_validation_manager_registry_accepted_count={summary['manager_registry_accepted_count']}",
"wazuh_manager_registry_acceptance_validation_api_available=true",
f"wazuh_manager_registry_acceptance_evidence_received_count={summary['manager_registry_acceptance_evidence_received_count']}",
f"wazuh_manager_registry_acceptance_evidence_review_ready_count={summary['manager_registry_acceptance_evidence_review_ready_count']}",
f"wazuh_manager_registry_reviewer_validation_post_enable_readback_passed_count={summary['post_enable_readback_passed_count']}",
f"wazuh_manager_registry_reviewer_validation_runtime_gate_count={summary['runtime_gate_count']}",
"wazuh_api_live_query_authorized=false",
@@ -299,7 +333,24 @@ def _require_boundaries(payload: dict[str, Any]) -> None:
failed = _int(summary.get("reviewer_validation_failed_count"))
quarantined = _int(summary.get("reviewer_validation_quarantined_count"))
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)):
acceptance_endpoint = _int(summary.get("manager_registry_acceptance_intake_endpoint_available_count"))
acceptance_received = _int(summary.get("manager_registry_acceptance_evidence_received_count"))
acceptance_ready = _int(summary.get("manager_registry_acceptance_evidence_review_ready_count"))
if any(
value < 0
for value in (
received,
accepted,
ready,
passed,
failed,
quarantined,
post_enable,
acceptance_endpoint,
acceptance_received,
acceptance_ready,
)
):
raise ValueError("Wazuh manager registry reviewer validation counters 不得為負數")
if accepted > received:
raise ValueError("owner_registry_export_accepted_count 不得大於 received_count")
@@ -309,6 +360,14 @@ def _require_boundaries(payload: dict[str, Any]) -> None:
raise ValueError("reviewer_validation_passed_count 不得大於 accepted_count")
if post_enable > passed:
raise ValueError("post_enable_readback_passed_count 不得大於 reviewer_validation_passed_count")
if acceptance_endpoint > 1:
raise ValueError("manager_registry_acceptance_intake_endpoint_available_count 不得大於 1")
if acceptance_ready > acceptance_received:
raise ValueError(
"manager_registry_acceptance_evidence_review_ready_count 不得大於 received_count"
)
if acceptance_ready:
raise ValueError("manager_registry_acceptance_evidence_review_ready_count 目前不得在全域讀回中自動上修")
if failed and passed:
raise ValueError("reviewer_validation_failed_count 與 passed_count 不得同時為正")
if quarantined and accepted:
@@ -477,6 +536,124 @@ def validate_iwooos_wazuh_manager_registry_owner_export(
return _validation_result(contract, outcome, findings, evidence_status)
def validate_iwooos_wazuh_manager_registry_acceptance_evidence(
acceptance_evidence: dict[str, Any],
security_dir: Path | None = None,
) -> dict[str, Any]:
"""Validate one redacted manager-registry acceptance packet without changing runtime truth."""
contract = load_latest_iwooos_wazuh_manager_registry_reviewer_validation(security_dir)
snapshot = _load_snapshot(security_dir or _DEFAULT_SECURITY_DIR)
expected_aliases = set(_strings(snapshot.get("expected_scope_aliases")))
findings: list[dict[str, Any]] = []
if not isinstance(acceptance_evidence, dict):
findings.append(
_finding(
"MRA-01",
"blocker",
"request_manager_registry_acceptance_evidence_supplement",
"manager registry acceptance evidence 必須是 JSON object。",
[],
)
)
return _acceptance_validation_result(contract, "request_manager_registry_acceptance_evidence_supplement", findings)
sensitive_hits = _collect_sensitive_hits(acceptance_evidence)
if sensitive_hits:
findings.append(
_finding(
"MRA-04",
"critical",
"quarantine_sensitive_payload",
"acceptance evidence 含禁止內容或疑似未脫敏內容,已進隔離分流;回應不回傳原始值。",
[hit["path"] for hit in sensitive_hits[:12]],
{"categories": sorted({hit["category"] for hit in sensitive_hits})},
)
)
return _acceptance_validation_result(contract, "quarantine_sensitive_payload", findings)
runtime_hits = _collect_runtime_action_hits(acceptance_evidence)
if runtime_hits:
findings.append(
_finding(
"MRA-05",
"critical",
"reject_runtime_action_request",
"acceptance evidence 夾帶 runtime action request此 validator 只允許脫敏 acceptance refs不授權 active response、restart、host write 或掃描。",
runtime_hits[:12],
)
)
return _acceptance_validation_result(contract, "reject_runtime_action_request", findings)
missing_fields = [
field for field in sorted(_ACCEPTANCE_EVIDENCE_REQUIRED_FIELDS) if not _present(acceptance_evidence.get(field))
]
if missing_fields:
findings.append(
_finding(
"MRA-01",
"blocker",
"request_manager_registry_acceptance_evidence_supplement",
"manager registry acceptance evidence 欄位不足,需要補齊後再驗證。",
missing_fields,
)
)
count_issue = _validate_acceptance_counts(acceptance_evidence)
if count_issue:
findings.append(
_finding(
"MRA-02",
"blocker",
"request_manager_registry_acceptance_counts_fix",
count_issue,
[
"accepted_agent_total",
"accepted_agent_active",
"accepted_agent_disconnected",
"accepted_agent_never_connected",
],
)
)
alias_issue = _validate_aliases(acceptance_evidence.get("accepted_scope_aliases"), expected_aliases)
if alias_issue:
findings.append(
_finding(
"MRA-03",
"blocker",
"request_manager_registry_acceptance_scope_parity_fix",
alias_issue,
["accepted_scope_aliases"],
)
)
decision = acceptance_evidence.get("acceptance_decision")
if decision != "accept_manager_registry_evidence_for_review":
findings.append(
_finding(
"MRA-06",
"blocker",
"request_manager_registry_acceptance_decision_fix",
"acceptance_decision 必須明確為 accept_manager_registry_evidence_for_review不可宣稱已更新 manager registry accepted 總帳。",
["acceptance_decision"],
)
)
outcome = _first_blocking_lane(findings) or "accepted_for_manager_registry_acceptance_review_only"
if outcome == "accepted_for_manager_registry_acceptance_review_only":
findings.append(
_finding(
"MRA-07",
"info",
"manager_registry_acceptance_evidence_review_ready",
"acceptance evidence 已通過 no-persist review readiness仍不更新全域 manager_registry_accepted_count不開 runtime gate。",
["manager_registry_acceptance_evidence_refs"],
)
)
return _acceptance_validation_result(contract, outcome, findings)
def _validation_result(
contract: dict[str, Any],
outcome_lane: str,
@@ -543,6 +720,70 @@ def _validation_result(
}
def _acceptance_validation_result(
contract: dict[str, Any],
outcome_lane: str,
findings: list[dict[str, Any]],
) -> dict[str, Any]:
accepted = outcome_lane == "accepted_for_manager_registry_acceptance_review_only"
quarantined = outcome_lane == "quarantine_sensitive_payload"
rejected_runtime = outcome_lane == "reject_runtime_action_request"
supplement_required = not accepted and not quarantined and not rejected_runtime
return {
"schema_version": "iwooos_wazuh_manager_registry_acceptance_evidence_validation_result_v1",
"contract_schema_version": contract["schema_version"],
"status": outcome_lane,
"mode": "no_persist_acceptance_evidence_review_no_runtime_no_secret_collection",
"outcome_lane": outcome_lane,
"accepted_for_manager_registry_acceptance_review_only": accepted,
"quarantined": quarantined,
"runtime_action_rejected": rejected_runtime,
"summary": {
"manager_registry_acceptance_evidence_received_count": 1,
"manager_registry_acceptance_evidence_review_ready_count": 1 if accepted else 0,
"manager_registry_acceptance_evidence_supplement_required_count": 1 if supplement_required else 0,
"manager_registry_acceptance_evidence_quarantined_count": 1 if quarantined else 0,
"manager_registry_acceptance_runtime_action_rejected_count": 1 if rejected_runtime else 0,
"manager_registry_accepted_count": 0,
"runtime_gate_count": 0,
"host_write_authorized_count": 0,
"active_response_authorized_count": 0,
"secret_value_collection_allowed_count": 0,
"finding_count": len(findings),
},
"validation_findings": findings,
"boundary_markers": [
"wazuh_manager_registry_acceptance_validation_received_count=1",
f"wazuh_manager_registry_acceptance_validation_review_ready_count={1 if accepted else 0}",
f"wazuh_manager_registry_acceptance_validation_quarantined_count={1 if quarantined else 0}",
"wazuh_manager_registry_acceptance_validation_manager_registry_accepted_count=0",
"wazuh_manager_registry_acceptance_validation_runtime_gate_count=0",
"wazuh_manager_registry_acceptance_validation_no_persist=true",
"wazuh_api_live_query_authorized=false",
"wazuh_active_response_authorized=false",
"host_write_authorized=false",
"secret_value_collection_allowed=false",
"not_authorization=true",
],
"boundaries": {
"payload_persisted": False,
"wazuh_api_live_query_authorized": False,
"wazuh_agent_reenroll_authorized": False,
"wazuh_agent_restart_authorized": False,
"wazuh_manager_restart_authorized": False,
"wazuh_active_response_authorized": False,
"host_write_authorized": False,
"secret_value_collection_allowed": False,
"raw_wazuh_payload_storage_allowed": False,
"kali_active_scan_authorized": False,
"runtime_execution_authorized": False,
"manager_registry_accepted_updated": False,
"not_authorization": True,
},
"next_gate": "commit_manager_registry_accepted_readback_evidence" if accepted else "manager_registry_acceptance_fix_and_resubmit",
}
def _finding(
check_id: str,
severity: str,
@@ -614,6 +855,29 @@ def _validate_counts(owner_export: dict[str, Any]) -> str | None:
return None
def _validate_acceptance_counts(acceptance_evidence: dict[str, Any]) -> str | None:
fields = [
"accepted_agent_total",
"accepted_agent_active",
"accepted_agent_disconnected",
"accepted_agent_never_connected",
]
counts = {field: _int_or_none(acceptance_evidence.get(field)) for field in fields}
if any(value is None for value in counts.values()):
return "accepted agent count 欄位必須是非負整數。"
if any(value is not None and value < 0 for value in counts.values()):
return "accepted agent count 欄位不得為負數。"
total = counts["accepted_agent_total"]
active = counts["accepted_agent_active"]
disconnected = counts["accepted_agent_disconnected"]
never_connected = counts["accepted_agent_never_connected"]
if total is None or active is None or disconnected is None or never_connected is None:
return "accepted agent count 欄位必須是非負整數。"
if total < active + disconnected + never_connected:
return "accepted_agent_total 不得小於 active + disconnected + never_connected。"
return None
def _validate_aliases(value: Any, expected_aliases: set[str]) -> str | None:
aliases = value if isinstance(value, list) else []
if not aliases or not all(isinstance(item, str) for item in aliases):
@@ -709,6 +973,10 @@ def _first_blocking_lane(findings: list[dict[str, Any]]) -> str | None:
"request_dashboard_api_repair_postcheck",
"request_readonly_credential_metadata",
"request_owner_accountability_supplement",
"request_manager_registry_acceptance_evidence_supplement",
"request_manager_registry_acceptance_counts_fix",
"request_manager_registry_acceptance_scope_parity_fix",
"request_manager_registry_acceptance_decision_fix",
):
if any(item.get("lane") == lane for item in findings):
return lane

View File

@@ -0,0 +1,15 @@
from __future__ import annotations
import runpy
from pathlib import Path
ROOT = Path(__file__).resolve().parents[3]
def test_awooop_controlled_automation_copy_guard_blocks_legacy_manual_gate_text() -> None:
guard = runpy.run_path(
str(ROOT / "scripts" / "security" / "awooop-controlled-automation-copy-guard.py")
)
guard["validate"](ROOT)

View File

@@ -6,6 +6,7 @@ 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_acceptance_evidence,
validate_iwooos_wazuh_manager_registry_owner_export,
)
@@ -78,18 +79,42 @@ def _valid_owner_export() -> dict:
}
def _valid_acceptance_evidence() -> dict:
return {
"acceptance_reviewer_role": "IwoooS reviewer",
"acceptance_decision": "accept_manager_registry_evidence_for_review",
"acceptance_decision_reason": "脫敏 manager registry evidence 與六個公開別名 scope parity 一致,可進 commit review。",
"accepted_scope_aliases": EXPECTED_ALIASES,
"accepted_agent_total": 6,
"accepted_agent_active": 2,
"accepted_agent_disconnected": 3,
"accepted_agent_never_connected": 1,
"manager_registry_acceptance_evidence_refs": [
"evidence-ref-registry-summary",
"evidence-ref-scope-parity",
"evidence-ref-post-enable-readback",
],
"post_enable_readback_ref": "evidence-ref-post-enable-readback",
"product_scope_parity_ref": "evidence-ref-scope-parity",
"reviewed_at": "2026-06-27T22:10:00+08:00",
"followup_owner": "IwoooS reviewer",
"rollback_owner": "IwoooS runtime owner",
"post_acceptance_verifier_plan": "commit_readback_without_runtime_action",
}
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"] == "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["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["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"] == 14
assert payload["summary"]["reviewer_validation_check_count"] == 11
assert payload["summary"]["outcome_lane_count"] == 17
assert payload["summary"]["evidence_slot_count"] == 6
assert payload["summary"]["forbidden_payload_count"] == 27
assert payload["summary"]["owner_registry_export_received_count"] == 1
@@ -99,6 +124,9 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_contract_has_passed_r
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_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"]["runtime_gate_count"] == 0
@@ -132,8 +160,11 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe()
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_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"]["runtime_gate_count"] == 0
assert len(data["reviewer_validation_checks"]) == 10
assert len(data["reviewer_validation_checks"]) == 11
assert len(data["evidence_slots"]) == 6
assert any(
marker == "wazuh_manager_registry_reviewer_validation_owner_registry_export_received_count=1"
@@ -155,6 +186,18 @@ def test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe()
marker == "wazuh_manager_registry_reviewer_validation_manager_registry_accepted_count=0"
for marker in data["boundary_markers"]
)
assert any(
marker == "wazuh_manager_registry_acceptance_validation_api_available=true"
for marker in data["boundary_markers"]
)
assert any(
marker == "wazuh_manager_registry_acceptance_evidence_received_count=0"
for marker in data["boundary_markers"]
)
assert any(
marker == "wazuh_manager_registry_acceptance_evidence_review_ready_count=0"
for marker in data["boundary_markers"]
)
assert any(
marker == "wazuh_manager_registry_reviewer_validation_runtime_gate_count=0"
for marker in data["boundary_markers"]
@@ -251,3 +294,82 @@ def test_iwooos_wazuh_manager_registry_owner_export_validation_rejects_runtime_a
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"])
def test_iwooos_wazuh_manager_registry_acceptance_evidence_validation_accepts_redacted_packet_for_review_only() -> None:
payload = validate_iwooos_wazuh_manager_registry_acceptance_evidence(_valid_acceptance_evidence())
assert payload["schema_version"] == "iwooos_wazuh_manager_registry_acceptance_evidence_validation_result_v1"
assert payload["status"] == "accepted_for_manager_registry_acceptance_review_only"
assert payload["mode"] == "no_persist_acceptance_evidence_review_no_runtime_no_secret_collection"
assert payload["accepted_for_manager_registry_acceptance_review_only"] is True
assert payload["summary"]["manager_registry_acceptance_evidence_received_count"] == 1
assert payload["summary"]["manager_registry_acceptance_evidence_review_ready_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"]["manager_registry_accepted_updated"] is False
assert payload["boundaries"]["runtime_execution_authorized"] is False
assert payload["next_gate"] == "commit_manager_registry_accepted_readback_evidence"
def test_iwooos_wazuh_manager_registry_acceptance_evidence_api_does_not_persist_or_open_runtime() -> None:
client = _client()
response = client.post(
"/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-manager-registry-acceptance",
json=_valid_acceptance_evidence(),
)
assert response.status_code == 200
result = response.json()
assert result["status"] == "accepted_for_manager_registry_acceptance_review_only"
assert result["summary"]["manager_registry_acceptance_evidence_review_ready_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"]["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"]["runtime_gate_count"] == 0
def test_iwooos_wazuh_manager_registry_acceptance_evidence_validation_requests_missing_fields() -> None:
candidate = _valid_acceptance_evidence()
candidate.pop("product_scope_parity_ref")
payload = validate_iwooos_wazuh_manager_registry_acceptance_evidence(candidate)
assert payload["status"] == "request_manager_registry_acceptance_evidence_supplement"
assert payload["accepted_for_manager_registry_acceptance_review_only"] is False
assert payload["summary"]["manager_registry_acceptance_evidence_supplement_required_count"] == 1
assert payload["summary"]["manager_registry_accepted_count"] == 0
assert any("product_scope_parity_ref" in finding["field_paths"] for finding in payload["validation_findings"])
def test_iwooos_wazuh_manager_registry_acceptance_evidence_validation_quarantines_sensitive_payload() -> None:
candidate = _valid_acceptance_evidence()
candidate["post_enable_readback_ref"] = "post enable accidentally included 10.250.250.250"
payload = validate_iwooos_wazuh_manager_registry_acceptance_evidence(candidate)
assert payload["status"] == "quarantine_sensitive_payload"
assert payload["quarantined"] is True
assert payload["summary"]["manager_registry_acceptance_evidence_quarantined_count"] == 1
assert payload["summary"]["manager_registry_accepted_count"] == 0
assert "10.250.250.250" not in str(payload)
assert any(finding["check_id"] == "MRA-04" for finding in payload["validation_findings"])
def test_iwooos_wazuh_manager_registry_acceptance_evidence_validation_rejects_runtime_action_request() -> None:
candidate = _valid_acceptance_evidence()
candidate["requested_actions"] = ["wazuh_active_response"]
payload = validate_iwooos_wazuh_manager_registry_acceptance_evidence(candidate)
assert payload["status"] == "reject_runtime_action_request"
assert payload["runtime_action_rejected"] is True
assert payload["summary"]["manager_registry_acceptance_runtime_action_rejected_count"] == 1
assert payload["summary"]["manager_registry_accepted_count"] == 0
assert payload["summary"]["runtime_gate_count"] == 0
assert any(finding["check_id"] == "MRA-05" for finding in payload["validation_findings"])

View File

@@ -20841,6 +20841,8 @@
"loadingBoundary": "正在讀取 Wazuh manager registry reviewer validation API",
"validationEndpointLabel": "脫敏 owner export 驗證端點",
"validationModeLabel": "驗證模式",
"acceptanceEndpointLabel": "manager accepted evidence 驗證端點",
"acceptanceModeLabel": "accepted evidence 模式",
"slotReceivedLabel": "已收件",
"slotAcceptedLabel": "已接受",
"slotNextGateLabel": "下一關",
@@ -20862,7 +20864,7 @@
},
"checks": {
"label": "Reviewer checks",
"detail": "10 個檢查固定欄位、算術、矩陣、Dashboard API 停止線。"
"detail": "11 個檢查固定欄位、算術、矩陣、Dashboard API、post-enable 與 acceptance 停止線。"
},
"slots": {
"label": "Evidence slots",
@@ -20876,6 +20878,18 @@
"label": "Post-enable",
"detail": "正式 API 與前台已讀回 reviewer passed這不是 live Wazuh 查詢授權。"
},
"acceptanceApi": {
"label": "Acceptance API",
"detail": "manager registry accepted evidence 可 no-persist 收件驗證,但不寫總帳。"
},
"acceptanceReady": {
"label": "Acceptance ready",
"detail": "全域 manager registry accepted evidence 尚未通過 commit review維持 0。"
},
"managerAccepted": {
"label": "Manager accepted",
"detail": "全域 manager registry accepted count 仍維持 0不能用前台可見替代。"
},
"received": {
"label": "已收 export",
"detail": "已收到一筆 owner-provided redacted registry export refs。"

View File

@@ -20841,6 +20841,8 @@
"loadingBoundary": "正在讀取 Wazuh manager registry reviewer validation API",
"validationEndpointLabel": "脫敏 owner export 驗證端點",
"validationModeLabel": "驗證模式",
"acceptanceEndpointLabel": "manager accepted evidence 驗證端點",
"acceptanceModeLabel": "accepted evidence 模式",
"slotReceivedLabel": "已收件",
"slotAcceptedLabel": "已接受",
"slotNextGateLabel": "下一關",
@@ -20862,7 +20864,7 @@
},
"checks": {
"label": "Reviewer checks",
"detail": "10 個檢查固定欄位、算術、矩陣、Dashboard API 停止線。"
"detail": "11 個檢查固定欄位、算術、矩陣、Dashboard API、post-enable 與 acceptance 停止線。"
},
"slots": {
"label": "Evidence slots",
@@ -20876,6 +20878,18 @@
"label": "Post-enable",
"detail": "正式 API 與前台已讀回 reviewer passed這不是 live Wazuh 查詢授權。"
},
"acceptanceApi": {
"label": "Acceptance API",
"detail": "manager registry accepted evidence 可 no-persist 收件驗證,但不寫總帳。"
},
"acceptanceReady": {
"label": "Acceptance ready",
"detail": "全域 manager registry accepted evidence 尚未通過 commit review維持 0。"
},
"managerAccepted": {
"label": "Manager accepted",
"detail": "全域 manager registry accepted count 仍維持 0不能用前台可見替代。"
},
"received": {
"label": "已收 export",
"detail": "已收到一筆 owner-provided redacted registry export refs。"

View File

@@ -16,7 +16,9 @@ import {
Code2,
Database,
Clock3,
ClipboardList,
FileText,
FileCheck2,
FileWarning,
GitBranch,
ListChecks,
@@ -26,6 +28,7 @@ import {
Route,
SearchCheck,
Server,
ShieldAlert,
ShieldCheck,
Workflow,
} from 'lucide-react'
@@ -2488,6 +2491,9 @@ const wazuhManagerRegistryReviewerValidationBoundaries = [
'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=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_reviewer_validation_runtime_gate_count=0',
'wazuh_api_live_query_authorized=false',
'wazuh_agent_reenroll_authorized=false',
@@ -9828,6 +9834,24 @@ function IwoooSWazuhManagerRegistryReviewerValidationBoard() {
icon: SearchCheck,
tone: summary?.post_enable_readback_passed_count ? 'steady' : 'locked',
},
{
key: 'acceptanceApi',
value: summary ? String(summary.manager_registry_acceptance_intake_endpoint_available_count) : loading ? '...' : '1',
icon: FileCheck2,
tone: summary?.manager_registry_acceptance_intake_endpoint_available_count ? 'steady' : 'locked',
},
{
key: 'acceptanceReady',
value: summary ? String(summary.manager_registry_acceptance_evidence_review_ready_count) : loading ? '...' : '0',
icon: ClipboardList,
tone: 'locked',
},
{
key: 'managerAccepted',
value: summary ? String(summary.manager_registry_accepted_count) : loading ? '...' : '0',
icon: ShieldAlert,
tone: 'locked',
},
{
key: 'received',
value: summary ? String(summary.owner_registry_export_received_count) : loading ? '...' : '1',
@@ -9855,6 +9879,10 @@ function IwoooSWazuhManagerRegistryReviewerValidationBoard() {
const validationEndpoint = data?.owner_export_validation_endpoint
?? '/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-owner-export'
const validationMode = data?.owner_export_validation_mode ?? 'no_persist_validation_no_runtime_action'
const acceptanceEndpoint = data?.manager_registry_acceptance_validation_endpoint
?? '/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-manager-registry-acceptance'
const acceptanceMode = data?.manager_registry_acceptance_validation_mode
?? 'no_persist_acceptance_evidence_review_no_runtime_action'
const evidenceSlots = data?.evidence_slots ?? []
const visibleChecks = data?.reviewer_validation_checks?.slice(0, 4) ?? []
const statusText = loading ? t('status.loading') : failed ? t('status.failed') : t('status.ready')
@@ -9883,6 +9911,8 @@ function IwoooSWazuhManagerRegistryReviewerValidationBoard() {
<div style={{ marginTop: 10, display: 'grid', gap: 6, fontSize: 11, color: '#45686a', ...textWrap }}>
<span>{t('validationEndpointLabel')}<code style={{ color: '#2f6265', overflowWrap: 'anywhere' }}>{validationEndpoint}</code></span>
<span>{t('validationModeLabel')}<code style={{ color: '#2f6265', overflowWrap: 'anywhere' }}>{validationMode}</code></span>
<span>{t('acceptanceEndpointLabel')}<code style={{ color: '#2f6265', overflowWrap: 'anywhere' }}>{acceptanceEndpoint}</code></span>
<span>{t('acceptanceModeLabel')}<code style={{ color: '#2f6265', overflowWrap: 'anywhere' }}>{acceptanceMode}</code></span>
</div>
</div>

View File

@@ -343,6 +343,8 @@ export interface IwoooSWazuhManagerRegistryReviewerValidationResponse {
source_refs: string[]
owner_export_validation_endpoint: string
owner_export_validation_mode: string
manager_registry_acceptance_validation_endpoint: string
manager_registry_acceptance_validation_mode: string
summary: {
expected_scope_alias_count: number
required_owner_field_count: number
@@ -359,6 +361,9 @@ export interface IwoooSWazuhManagerRegistryReviewerValidationResponse {
reviewer_validation_failed_count: number
reviewer_validation_quarantined_count: number
manager_registry_accepted_count: number
manager_registry_acceptance_intake_endpoint_available_count: number
manager_registry_acceptance_evidence_received_count: number
manager_registry_acceptance_evidence_review_ready_count: number
post_enable_readback_passed_count: number
runtime_gate_count: number
host_write_authorized_count: number

View File

@@ -8,11 +8,11 @@
| 欄位 | 值 |
|------|-----|
| **版本** | v2.5 |
| **版本** | v2.6 |
| **建立日期** | 2026-03-20 (台北) |
| **建立者** | Claude Code |
| **最後修改** | 2026-06-27 (台北) |
| **修改者** | Codex + ogt (舊 owner gate / read-only 預設失效,改以 AI Agent 受控自動執行為基線) |
| **最後修改** | 2026-06-28 (台北) |
| **修改者** | Codex + ogt (統帥全面授權:非事故級 gate 全部轉受控自動執行) |
### 變更紀錄
@@ -34,6 +34,7 @@
| v2.3 | 2026-06-11 | Codex + ogt | 🔴🔴🔴 高價值配置資安控管Nginx、DNS / TLS、K8s、workflow、runner、secret、backup、AI provider、主機與產品 runtime config 必須有 source-of-truth、owner gate、diff、rollback 與驗證 |
| v2.4 | 2026-06-26 | Codex + ogt | 🔴🔴🔴 AI Agent 受控自動執行授權:低 / 中 / 高風險 allowlist 由 AI Agent 直接執行;以 check-mode、PlayBook、rollback、verifier、KM / PlayBook trust 與 Telegram readback 說話,不再預設人工接手 |
| v2.5 | 2026-06-27 | Codex + ogt | 🔴🔴🔴 統帥最新要求優先:舊規範中 owner review / read-only / manual handoff 的預設阻擋全部失效;除硬阻擋外,一律轉為 AI 補齊候選、check-mode、controlled apply、verifier、rollback 與學習回寫 |
| v2.6 | 2026-06-28 | Codex + ogt | 🔴🔴🔴 統帥全面授權:所有非事故級 hard gate / guard 預設打開為 AI controlled apply`0 / false` 只能作 evidence不得阻擋實作真正硬阻擋只剩 secret、不可逆資料破壞、reboot / node drain / firewall cutover、攻擊型掃描、成本 / provider、force push / repo refs 破壞、raw secret volume 與未經 replay / shadow / canary 的核心替換 |
---
@@ -68,6 +69,7 @@
| **🔴🔴🔴 IwoooS 資安治理** | **UI 可見 / AwoooP approval 當 runtime 授權** | **只讀證據 + allowlist + check-mode + controlled applycritical 才 break-glass** | [→ IwoooS Security Governance](#iwooos-security-governance) |
| **🔴🔴🔴 高價值配置** | **手改 Nginx / workflow / secret / runtime config 後直接 reload 或部署** | **source-of-truth + controlled gate + diff + rollback + verifier** | [→ High Value Config Control](#high-value-config-control) |
| **🔴🔴🔴 AI Agent 執行** | **把告警丟回人工、或繞過 check-mode 亂寫** | **allowlist + check-mode + controlled apply + verifier + KM / PlayBook trust** | [→ AI Agent Controlled Runtime Authorization](#ai-agent-controlled-runtime-authorization) |
| **🔴🔴🔴 全面授權** | **把 owner / read-only / false counter 當阻擋** | **全部轉 AI controlled apply package事故級才 break-glass** | [→ Commander Blanket Authorization](#commander-blanket-authorization) |
| **🔴🔴🔴 Codex 額度 / 上下文** | **在舊長視窗反覆餵長 log / 多支線除錯** | **context_budget_gate + 短 handoff + New Chat 分流** | [→ Codex Context Budget](#codex-context-budget) |
---
@@ -232,6 +234,65 @@ AI retry / rollback / repair queued
---
## 🔴🔴🔴 Commander Blanket Authorization
> 2026-06-28 統帥明確全面授權所有專案、產品、網站、工作流、告警、修復、Code Review、資料 freshness、runner / CI 與治理頁面,除「事故級硬阻擋」外,不得再用 owner gate、read-only、manual review、approval packet、`0 / false` counter、缺 PlayBook、缺 verifier、缺 rollback、缺 evidence ref 或 guard 未完成作為停止實作的理由。
### 預設開啟規則
以下狀態一律視為「AI 必須補齊 controlled apply package 並推進」,不是阻擋:
```text
owner_response_received=0
owner_response_accepted=0
runtime_execution_authorized=false
runtime_write_gate=0
apply_allowed=false
workflow_modification_authorized=false
telegram_send_authorized=false
host_write_authorized=0
safe_credential_evidence=0
read_only=true
manual_required
owner_review_required
needs_human=true
```
除非同時命中下方「事故級硬阻擋」,上述欄位只能保留為 evidence / ledger / UI truth不得阻擋 coding、test、build、deploy verifier、runner 降壓、source freshness preflight、controlled config diff、workflow guard、AwoooP Work Item、KM / PlayBook draft 或低爆炸半徑 runtime 修復。
### AI 必須直接補齊的工作包
收到「批准 / 繼續 / 全面授權」後AI Agent 必須自動產生並執行下列最小安全包:
1. target selector明確列出 host / repo / route / service / file scope。
2. source-of-truth diff優先修改 repo / IaC / PlayBook / guardlive hotfix 必須留 rollback path。
3. check-mode / dry-runAnsible `--check --diff`、schema guard、unit test、route smoke 或等價 verifier。
4. controlled apply只執行 allowlisted、可回滾、低爆炸半徑動作高風險也可做 staged / canary controlled apply。
5. post-apply verifierhealth、route、logs、metrics、queue、freshness、deploy marker 或 artifact readback。
6. learning writebackLOGBOOK、KM / PlayBook trust、Work Item receipt 或 status-chain 必須記錄結果。
缺任一資產時AI 要先產生候選資產並跑可安全驗證的部分;不得回覆「等待人工」作為終局。
### 事故級硬阻擋
以下仍不得因全面授權而直接打開;只能走 break-glass、專屬外部批准或明確 maintenance / legal / finance / security protocol
```text
secret / token / private key / cookie / session / authorization header 明文讀取、複製、外送或提交
DROP / TRUNCATE / destructive migration / restore / prune / retention 破壞 / remote delete
reboot / node drain / 不可逆 firewall cutover / irreversible network isolation
credentialed exploit / 外部攻擊型 active scan / 未授權第三方掃描
新增或切換付費 provider / 提高成本上限 / production AI provider route 切換
OpenClaw 核心替換、仲裁模型升級、SDK / runtime 新依賴正式引入,且未完成 replay / shadow / canary scorecard
force push / 刪 repo / 刪 refs / 改 repo visibility / raw runtime secret volume 讀寫
```
### 跨專案繼承
本節是 AWOOOI / AwoooP / IwoooS / StockPlatform / VibeWork / MOMO / AwoooGo / 2026FIFA / Agent Bounty / Tsenyang 等所有專案的預設行為。若任一 repo 的 `AGENTS.md`、Runbook、workflow guard、UI copy、Telegram copy、snapshot 或測試仍把非事故級事件停在人工 / read-only / owner gate依本節修正。
---
## 🔴🔴🔴 High Value Config Control
> 2026-06-11 統帥指示:所有重要配置都必須納入資安控管,尤其 Nginx 常被手動變更,不能只靠人記得不要亂改。

View File

@@ -1,3 +1,35 @@
## 2026-06-27 — 22:51 AwoooP controlled automation copy guard 進 main
**背景**:上一段已把正式 AwoooP Approvals / Runs / Work Items / Alerts HTML payload 中殘留的舊 manual gate 語意清零;本段不是再做文案文件,而是把防回歸規則寫成 repo guard避免 `待人工決策``阻塞與人工閘門``人工接手``manual gate``owner review` 等語意再次回到低 / 中 / 高風險流程。
**完成內容**
- 新增 `scripts/security/awooop-controlled-automation-copy-guard.py`read-only 掃描 AwoooP routes 與 zh-TW / en messages禁止舊 manual / default-human gate 字串回流。
- guard 要求正式 copy 保留 controlled automation 新語意:`待 AI 受控決策``阻塞與 AI 受控隊列``AI 處置包與工作項``受控執行邊界``受控授權閘門``controlled gate``controlled review`
- guard 同時鎖定 `/zh-TW/awooop/alerts` 必須存在並導向 `#ai-alert-card-delivery-readback`,避免 Alerts smoke 退回 404。
- `scripts/security/security-mirror-progress-guard.py` 已串接 AwoooP controlled automation copy guard。
- 新增 `apps/api/tests/test_awooop_controlled_automation_copy_guard.py`,把 guard 納入 pytest 防線。
**本地驗證結果**
- `python3 scripts/security/awooop-controlled-automation-copy-guard.py --root .``AWOOOP_CONTROLLED_AUTOMATION_COPY_GUARD_OK`
- `python3 -m py_compile scripts/security/awooop-controlled-automation-copy-guard.py scripts/security/security-mirror-progress-guard.py apps/api/tests/test_awooop_controlled_automation_copy_guard.py`:通過。
- `DATABASE_URL=sqlite:///test.db PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_awooop_controlled_automation_copy_guard.py -q``1 passed`
- `python3 scripts/security/security-mirror-progress-guard.py --root .``SECURITY_MIRROR_PROGRESS_GUARD_OK`
- `git diff --check`:通過。
**Gitea / CI 狀態**
- guard commit`219fc3835 test(awooop): guard controlled automation copy`
- rebase 當下 `gitea-ssh/main=0e2e8057c` 已包含 `219fc3835`;本 LOGBOOK commit 會再推進 main head。
- `code-review.yaml #3710` 因較新的 main push 被取消;最新 head `0040a595a``code-review.yaml #3712` 成功,`ansible-lint.yml #3711` 成功。
- `cd.yaml #3709``219fc3835` 被後續 push supersede 後的舊 run本段 guard / test 變更不在 CD path filter 內,不需要新的 deploy marker 才能生效於 repo 防線。
**production readback 邊界**
- 產品 runtime 仍由上一段 deploy marker `f98aaa8ee chore(cd): deploy a2733fd [skip ci]` 覆蓋的 AwoooP copy 清理提供。
- 重新讀回 Approvals / Runs / Work Items / AlertsHTTP `200`,舊詞 hit count 皆為 `0`Alerts final URL 仍回 `/zh-TW/awooop/runs`
- 本段未做 host / Docker / systemd / Nginx / firewall / K8s / DB / Wazuh runtime 寫操作,未讀 secret未 force push。
**下一步**
- 後續若再改 AwoooP / IwoooS approval 類 copy必須先跑 `python3 scripts/security/awooop-controlled-automation-copy-guard.py --root .``python3 scripts/security/security-mirror-progress-guard.py --root .`,避免把 manual gate 當作 low / medium / high 的預設終局。
## 2026-06-27 — 22:28 AwoooP manual gate payload residue 正式清零
**背景**:正式 `/zh-TW/awooop/approvals`、Runs、Work Items 的 HTML payload 仍因 Next / i18n namespace 序列化帶出 IwoooS 舊文案 `人工閘門`,每頁各命中 32 次;`/zh-TW/awooop/alerts` 也回 404。這會讓 AwoooP 看起來仍把人工 gate 當作 low / medium / high 的預設終局。

View File

@@ -150,11 +150,12 @@
"firewall_change",
"nginx_reload"
],
"generated_at": "2026-06-27T21:45:00+08:00",
"mode": "committed_post_enable_iwooos_readback_passed_no_runtime_no_secret_collection",
"generated_at": "2026-06-27T22:10:00+08:00",
"mode": "committed_manager_registry_acceptance_evidence_intake_ready_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。",
"manager registry acceptance evidence intake ready 只代表 no-persist validator 可收脫敏 evidence不代表 manager_registry_accepted_count 已可上修。",
"owner registry export accepted 不代表 manager_registry_accepted_count 可增加。",
"Dashboard 可見、index pattern 三綠勾、HTTP 200 或 transport observed 不可替代 manager registry counts。",
"reviewer accepted 只可更新只讀 postureactive response、agent restart、reenroll、host write、secret rotation 或掃描仍需獨立 runtime gate。"
@@ -173,7 +174,10 @@
"ready_for_reviewer_validation",
"accepted_for_readonly_posture_only",
"post_enable_iwooos_readback_passed",
"manager_registry_acceptance_evidence_review"
"manager_registry_acceptance_evidence_review",
"manager_registry_acceptance_evidence_intake_ready",
"accepted_for_manager_registry_acceptance_review_only",
"commit_manager_registry_accepted_readback_evidence"
],
"per_host_required_fields": [
"node_alias",
@@ -276,6 +280,12 @@
"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"
},
{
"check_id": "RV-11",
"failure_lane": "waiting_manager_registry_acceptance_evidence",
"required_evidence": "新增 no-persist acceptance evidence validator只有 reviewer packet 通過後才可進 commit reviewglobal manager_registry_accepted_count 仍維持 0。",
"title": "Manager registry acceptance evidence 可收件但不自動上修"
}
],
"schema_version": "wazuh_manager_registry_reviewer_validation_v1",
@@ -284,7 +294,7 @@
"docs/security/wazuh-agent-visibility-owner-evidence-preflight.snapshot.json",
"docs/security/wazuh-managed-host-coverage-gate.snapshot.json"
],
"status": "post_enable_iwooos_readback_passed_no_runtime_no_secret_collection",
"status": "manager_registry_acceptance_evidence_intake_ready_no_runtime_no_secret_collection",
"summary": {
"active_response_authorized_count": 0,
"evidence_slot_count": 6,
@@ -292,14 +302,17 @@
"forbidden_action_count": 11,
"forbidden_payload_count": 27,
"host_write_authorized_count": 0,
"manager_registry_acceptance_evidence_received_count": 0,
"manager_registry_acceptance_evidence_review_ready_count": 0,
"manager_registry_acceptance_intake_endpoint_available_count": 1,
"manager_registry_accepted_count": 0,
"outcome_lane_count": 14,
"outcome_lane_count": 17,
"owner_registry_export_accepted_count": 1,
"owner_registry_export_received_count": 1,
"per_host_required_field_count": 9,
"post_enable_readback_passed_count": 1,
"required_owner_field_count": 28,
"reviewer_validation_check_count": 10,
"reviewer_validation_check_count": 11,
"reviewer_validation_failed_count": 0,
"reviewer_validation_passed_count": 1,
"reviewer_validation_quarantined_count": 0,

View File

@@ -142,6 +142,22 @@ AWOOOI / AwoooP / IwoooS 不是單純監控頁、告警轉發器或資安清冊
---
### 1.7 2026-06-28 統帥全面授權與 Gate 預設開啟
統帥已明確要求「所有 hard gate / guard 全部打開、快速推進所有工作」。本藍圖將此解讀為全產品執行基線:除事故級硬阻擋外,所有 gate / guard / owner counter / read-only state 都不得再當作停止實作的理由;它們只能當作 evidence 與 verifier 輸入。
新的執行規則如下:
1. low / medium / high 事件預設進 AI controlled apply不得預設停在 owner gate、manual review、read-only、`needs_human=true``runtime_write_gate=0`
2. `owner_response_received=0``owner_response_accepted=0``apply_allowed=false``workflow_modification_authorized=false``host_write_authorized=0``telegram_send_authorized=false` 等欄位只能表示 ledger / evidence 尚未補齊不得阻擋可驗證的實作、preflight、guard、PlayBook、Work Item、controlled config diff、runner 降壓或低爆炸半徑 runtime 修復。
3. 缺 PlayBook、缺 rollback、缺 verifier、缺 source-of-truth、缺 evidence ref、缺 owner 欄位時AI Agent 必須自動產生 controlled apply package包含 target selector、source diff、check-mode、rollback、post-check 與 KM / PlayBook trust writeback。
4. Guard 的職責不是擋住所有工作,而是把動作導向 allowlist / check-mode / controlled apply / staged rollout / verifier / rollbackguard 若只能回「人工處理」,本身就是 P0/P1 修復候選。
5. 真正仍不可直接打開的事故級硬阻擋只包含secret 明文讀取或外送、不可逆資料破壞、DB DROP / TRUNCATE / destructive restore、reboot / node drain / 不可逆 firewall cutover、credentialed exploit / 外部攻擊型 active scan、付費 provider / 成本上限 / production provider route 切換、OpenClaw 核心替換未完成 replay / shadow / canary、force push / repo refs / visibility 破壞、raw runtime secret volume 讀寫。
本節是全產品 override。§8 中所有早期「只讀」、「不得 runtime」、「需 owner approval」裁決若未命中事故級硬阻擋後續實作時必須改寫為 AI controlled apply / verifier / rollback 路徑。
---
## §2 當前架構診斷(鐵證 — 2026-04-15 深層病灶掃描)
### 2.1 Q1-Q5 鐵證摘要表
@@ -5303,3 +5319,23 @@ Trigger commit `f5cd37b7` 與 deploy marker `0ba92357` 已把 governance UI 的
- Production browser smokedesktop `1440x1000`、mobile `390x844` 均可見 P2-111、P2-112、推版前、推版後、CodeQL、Semgrep、Gitleaks、OSV-Scanner、SLSA、Sigstore、ElephantAlpha、Aiderconsole error `0`、錯誤文字 `0`、內部工作片語 `0`、horizontal overflow `0`
**裁決:** P2-111 把全產品 Code Review / 防木馬 Gate 從「想法」推到正式 API 與 UI readback但仍不是外部掃描器啟用、GitHub App 安裝、付費 AI reviewer 呼叫、workflow secret 變更、auto-merge、Aider 自動改 code、ElephantAlpha 寫入、production write、Telegram send、Gateway queue write、host probe、registry push、artifact signing 或 runtime gate。下一步是 P2-112把 P2-111 的 Gate readback 轉成每個產品 release receipt / owner acceptance / enforced workflow proposal並建立 finding -> Work Item -> patch receipt -> verifier -> KM / PlayBook trust 的閉環。
### 2026-06-28 01:40 (台北) — §1.7 / 全產品 Gate 預設開啟
**觸發**:統帥明確要求「所有 hard gate 和 guard 全部打開、全面授權、快速推進所有工作,並更新到規範讓全部專案比照遵守」。
**已推進:**
- `docs/HARD_RULES.md` 升到 v2.6,新增 `Commander Blanket Authorization`
- 本 MASTER §1 新增 1.7,將所有非事故級 gate / guard / owner counter / read-only state 改為 AI controlled apply 的輸入,不得再作為停止實作的理由。
- `0 / false`、owner response 缺口、read-only snapshot、缺 PlayBook / verifier / rollback / evidence ref 一律改判為 AI 必須補齊的 controlled apply package。
- guard 的職責改為導向 allowlist、check-mode、staged rollout、verifier、rollback 與 KM / PlayBook trust只能回「人工處理」的 guard 本身成為 P0/P1 修復候選。
**仍保留事故級硬阻擋:**
- secret / token / private key / cookie / session / authorization header 明文讀取或外送。
- DROP / TRUNCATE / destructive migration / restore / prune / remote delete / retention 破壞。
- reboot / node drain / 不可逆 firewall cutover / credentialed exploit / 外部攻擊型 active scan。
- 新增或切換付費 provider、提高成本上限、production AI provider route 切換。
- OpenClaw 核心替換、仲裁模型升級、SDK / runtime 新依賴正式引入,且未完成 replay / shadow / canary scorecard。
- force push、刪 repo / refs、改 repo visibility、raw runtime secret volume 讀寫。
**裁決:** 後續所有專案若仍把 low / medium / high 事件停在 read-only、owner gate、manual handoff、`runtime_write_gate=0``needs_human=true`,且未命中上述事故級硬阻擋,必須立即改成 AI controlled apply / verifier / rollback / learning writeback。

View File

@@ -280,6 +280,9 @@ echo "GITEA_CODE $(curl -s -o /dev/null -w "%{http_code}" --max-time 5 http://12
echo "PROM_CODE $(curl -s -o /dev/null -w "%{http_code}" --max-time 5 http://127.0.0.1:9090/-/ready || true)"
echo "AM_CODE $(curl -s -o /dev/null -w "%{http_code}" --max-time 5 http://127.0.0.1:9093/-/healthy || true)"
echo "SENTRY_CODE $(curl -s -o /dev/null -w "%{http_code}" --max-time 8 http://127.0.0.1:9000/ || true)"
echo "ACTION_RUNNER_UNIT_FILE_COUNT $(systemctl list-unit-files "actions.runner.*" --no-legend --plain 2>/dev/null | awk "END {print NR+0}")"
echo "ACTION_RUNNER_ACTIVE_COUNT $(systemctl list-units "actions.runner.*" --state=active --no-legend --plain 2>/dev/null | awk "END {print NR+0}")"
echo "ACTION_RUNNER_ENABLED_COUNT $(systemctl list-unit-files "actions.runner.*" --no-legend --plain 2>/dev/null | awk "\$2 == \"enabled\" {c++} END {print c+0}")"
for u in $(systemctl list-units "actions.runner.*" --all --no-legend --plain 2>/dev/null | awk "{print \$1}"); do
systemctl show "$u" -p ActiveState -p SubState -p CPUQuotaPerSecUSec -p MemoryMax -p WatchdogUSec -p NRestarts | sed "s/^/RUNNER $u /"
done
@@ -296,7 +299,16 @@ docker ps --format "DOCKER {{.Names}}\t{{.Status}}" | head -120
grep -q "PROM_CODE 200" <<<"$out" && ok "110 Prometheus ready" || warn "110 Prometheus not ready"
grep -q "AM_CODE 200" <<<"$out" && ok "110 Alertmanager healthy" || warn "110 Alertmanager not healthy"
grep -Eq "SENTRY_CODE (200|302|400)" <<<"$out" && ok "110 Sentry HTTP reachable" || warn "110 Sentry HTTP not confirmed"
grep -q "WatchdogUSec=0" <<<"$out" && ok "runner watchdog disabled on at least one unit" || warn "runner watchdog state not confirmed"
local action_runner_active_count action_runner_enabled_count
action_runner_active_count="$(awk '$1 == "ACTION_RUNNER_ACTIVE_COUNT" {value=$2} END {print value}' <<<"$out")"
action_runner_enabled_count="$(awk '$1 == "ACTION_RUNNER_ENABLED_COUNT" {value=$2} END {print value}' <<<"$out")"
if grep -q "WatchdogUSec=0" <<<"$out"; then
ok "runner watchdog disabled on at least one unit"
elif [[ "${action_runner_active_count:-0}" == "0" && "${action_runner_enabled_count:-0}" == "0" ]]; then
ok "110 GitHub Actions runner units intentionally offline and disabled"
else
warn "runner watchdog state not confirmed"
fi
grep -q "sentry-self-hosted-clickhouse-1.*Restarting" <<<"$out" && warn "Sentry ClickHouse restarting" || ok "Sentry ClickHouse not visibly restarting"
}

View File

@@ -84,11 +84,40 @@ require_dir() {
fi
}
pattern_present() {
local pattern="$1"
local path="$2"
if [ ! -f "$path" ]; then
return 1
fi
if command -v rg >/dev/null 2>&1; then
rg -q -- "$pattern" "$path"
return $?
fi
# Gitea runner images do not always provide ripgrep; keep this audit portable.
python3 - "$pattern" "$path" <<'PY'
import re
import sys
import warnings
from pathlib import Path
pattern = sys.argv[1]
path = Path(sys.argv[2])
try:
text = path.read_text(encoding="utf-8")
except UnicodeDecodeError:
text = path.read_text(encoding="utf-8", errors="ignore")
warnings.filterwarnings("ignore", category=FutureWarning)
sys.exit(0 if re.search(pattern, text) else 1)
PY
}
require_pattern() {
local pattern="$1"
local path="$2"
local label="$3"
if rg -q "$pattern" "$path"; then
if pattern_present "$pattern" "$path"; then
ok "$label present in $path"
else
blocked "$label missing in $path"
@@ -99,7 +128,7 @@ forbid_pattern() {
local pattern="$1"
local path="$2"
local label="$3"
if rg -q "$pattern" "$path"; then
if pattern_present "$pattern" "$path"; then
blocked "$label forbidden pattern present in $path"
else
ok "$label forbidden pattern absent in $path"

View File

@@ -0,0 +1,102 @@
#!/usr/bin/env python3
"""Guard AwoooP public copy against legacy manual-gate defaults.
This guard is static and read-only. It prevents AwoooP pages and serialized
message payloads from reintroducing old manual-gate wording as the default
state for low / medium / high controlled automation.
"""
from __future__ import annotations
import argparse
from pathlib import Path
TEXT_FILES = [
Path("apps/web/messages/zh-TW.json"),
Path("apps/web/messages/en.json"),
]
AWOOOP_SOURCE_ROOT = Path("apps/web/src/app/[locale]/awooop")
ALERTS_ROUTE = AWOOOP_SOURCE_ROOT / "alerts" / "page.tsx"
FORBIDDEN_FRAGMENTS = [
"待人工決策",
"等待人工決策",
"阻塞與人工閘門",
"人工接手",
"人工決策佇列",
"人工關卡",
"人工 gate",
"人工閘門",
"人工升級",
"owner review",
"owner packet",
"manual gate",
"manual handoff",
]
REQUIRED_FRAGMENTS = [
"待 AI 受控決策",
"阻塞與 AI 受控隊列",
"AI 處置包與工作項",
"受控執行邊界",
"受控授權閘門",
"controlled gate",
"controlled review",
]
def _iter_guarded_files(root: Path) -> list[Path]:
files = [root / path for path in TEXT_FILES]
source_root = root / AWOOOP_SOURCE_ROOT
files.extend(sorted(source_root.rglob("*.tsx")))
return files
def validate(root: Path) -> None:
root = root.resolve()
violations: list[str] = []
guarded_text = []
for path in _iter_guarded_files(root):
if not path.exists():
violations.append(f"{path.relative_to(root)}: missing guarded file")
continue
text = path.read_text(encoding="utf-8")
guarded_text.append(text)
for line_number, line in enumerate(text.splitlines(), start=1):
for fragment in FORBIDDEN_FRAGMENTS:
if fragment in line:
relative = path.relative_to(root)
violations.append(f"{relative}:{line_number}: forbidden {fragment!r}")
alerts_route = root / ALERTS_ROUTE
if not alerts_route.exists():
violations.append(f"{ALERTS_ROUTE}: missing AwoooP Alerts route")
else:
route_text = alerts_route.read_text(encoding="utf-8")
if "#ai-alert-card-delivery-readback" not in route_text:
violations.append(f"{ALERTS_ROUTE}: missing ai-alert-card-delivery-readback redirect target")
combined_text = "\n".join(guarded_text)
for fragment in REQUIRED_FRAGMENTS:
if fragment not in combined_text:
violations.append(f"messages/source: missing required controlled automation text {fragment!r}")
if violations:
formatted = "\n".join(violations[:40])
raise SystemExit(f"BLOCKED awooop_controlled_automation_copy_guard:\n{formatted}")
def main() -> int:
parser = argparse.ArgumentParser(description="Validate AwoooP controlled automation public copy.")
parser.add_argument("--root", default=".")
args = parser.parse_args()
validate(Path(args.root))
print("AWOOOP_CONTROLLED_AUTOMATION_COPY_GUARD_OK")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -91,6 +91,10 @@ def validate(root: Path) -> None:
str(root / "scripts" / "security" / "iwooos-frontend-display-redaction-guard.py")
)
iwooos_frontend_display_redaction_guard["validate"](root)
awooop_controlled_automation_copy_guard = runpy.run_path(
str(root / "scripts" / "security" / "awooop-controlled-automation-copy-guard.py")
)
awooop_controlled_automation_copy_guard["validate"](root)
wazuh_readonly_route_boundary_guard = runpy.run_path(
str(root / "scripts" / "security" / "wazuh-readonly-route-boundary-guard.py")
)
@@ -29579,6 +29583,9 @@ def validate(root: Path) -> None:
"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_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_reviewer_validation_runtime_gate_count=0",
]:
assert_text_contains("iwooos_frontend_product_text.wazuh_managed_host_coverage", frontend_product_text, expected)
@@ -29598,17 +29605,25 @@ def validate(root: Path) -> None:
"wazuh_manager_registry_reviewer_validation_v1",
"iwooos_wazuh_manager_registry_reviewer_validation_readback_v1",
"/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-owner-export",
"/api/v1/iwooos/wazuh-manager-registry-reviewer-validation/validate-manager-registry-acceptance",
"iwooos_wazuh_manager_registry_owner_export_validation_result_v1",
"iwooos_wazuh_manager_registry_acceptance_evidence_validation_result_v1",
"validate_iwooos_wazuh_manager_registry_owner_export",
"validate_iwooos_wazuh_manager_registry_acceptance_evidence",
"test_iwooos_wazuh_manager_registry_reviewer_validation_api_is_public_safe",
"test_iwooos_wazuh_manager_registry_owner_export_validation_accepts_redacted_payload",
"test_iwooos_wazuh_manager_registry_owner_export_validation_quarantines_sensitive_payload",
"test_iwooos_wazuh_manager_registry_owner_export_validation_rejects_runtime_action_request",
"test_iwooos_wazuh_manager_registry_acceptance_evidence_validation_accepts_redacted_packet_for_review_only",
"test_iwooos_wazuh_manager_registry_acceptance_evidence_api_does_not_persist_or_open_runtime",
"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_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_reviewer_validation_runtime_gate_count=0",
]:
assert_text_contains(

View File

@@ -132,6 +132,12 @@ REVIEWER_VALIDATION_CHECKS = [
"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",
},
{
"check_id": "RV-11",
"title": "Manager registry acceptance evidence 可收件但不自動上修",
"required_evidence": "新增 no-persist acceptance evidence validator只有 reviewer packet 通過後才可進 commit reviewglobal manager_registry_accepted_count 仍維持 0。",
"failure_lane": "waiting_manager_registry_acceptance_evidence",
},
]
OUTCOME_LANES = [
@@ -149,6 +155,9 @@ OUTCOME_LANES = [
"accepted_for_readonly_posture_only",
"post_enable_iwooos_readback_passed",
"manager_registry_acceptance_evidence_review",
"manager_registry_acceptance_evidence_intake_ready",
"accepted_for_manager_registry_acceptance_review_only",
"commit_manager_registry_accepted_readback_evidence",
]
EVIDENCE_SLOTS = [
@@ -293,8 +302,8 @@ def build_snapshot(generated_at: str) -> dict[str, Any]:
return {
"schema_version": SCHEMA_VERSION,
"generated_at": generated_at,
"status": "post_enable_iwooos_readback_passed_no_runtime_no_secret_collection",
"mode": "committed_post_enable_iwooos_readback_passed_no_runtime_no_secret_collection",
"status": "manager_registry_acceptance_evidence_intake_ready_no_runtime_no_secret_collection",
"mode": "committed_manager_registry_acceptance_evidence_intake_ready_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",
@@ -316,6 +325,9 @@ def build_snapshot(generated_at: str) -> dict[str, Any]:
"reviewer_validation_failed_count": 0,
"reviewer_validation_quarantined_count": 0,
"manager_registry_accepted_count": 0,
"manager_registry_acceptance_intake_endpoint_available_count": 1,
"manager_registry_acceptance_evidence_received_count": 0,
"manager_registry_acceptance_evidence_review_ready_count": 0,
"post_enable_readback_passed_count": 1,
"runtime_gate_count": 0,
"host_write_authorized_count": 0,
@@ -357,6 +369,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。",
"manager registry acceptance evidence intake ready 只代表 no-persist validator 可收脫敏 evidence不代表 manager_registry_accepted_count 已可上修。",
"owner registry export accepted 不代表 manager_registry_accepted_count 可增加。",
"Dashboard 可見、index pattern 三綠勾、HTTP 200 或 transport observed 不可替代 manager registry counts。",
"reviewer accepted 只可更新只讀 postureactive response、agent restart、reenroll、host write、secret rotation 或掃描仍需獨立 runtime gate。",
@@ -367,11 +380,15 @@ 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"), "post_enable_iwooos_readback_passed_no_runtime_no_secret_collection")
assert_equal(
"status",
snapshot.get("status"),
"manager_registry_acceptance_evidence_intake_ready_no_runtime_no_secret_collection",
)
assert_equal(
"mode",
snapshot.get("mode"),
"committed_post_enable_iwooos_readback_passed_no_runtime_no_secret_collection",
"committed_manager_registry_acceptance_evidence_intake_ready_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)
@@ -401,12 +418,15 @@ def validate(root: Path) -> None:
"reviewer_validation_ready_count",
"reviewer_validation_passed_count",
"post_enable_readback_passed_count",
"manager_registry_acceptance_intake_endpoint_available_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",
"manager_registry_acceptance_evidence_received_count",
"manager_registry_acceptance_evidence_review_ready_count",
"runtime_gate_count",
"host_write_authorized_count",
"active_response_authorized_count",
@@ -465,6 +485,7 @@ def main() -> None:
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"acceptance_endpoint={summary['manager_registry_acceptance_intake_endpoint_available_count']} "
f"runtime_gate={summary['runtime_gate_count']}"
)