""" IwoooS security control coverage rollup. This service consolidates committed security inventory snapshots into one read-only control-plane view. It never queries live hosts, Wazuh, Kali, Kubernetes, Docker, Nginx, Telegram, or secret stores. """ from __future__ import annotations import json from pathlib import Path from typing import Any from src.services.snapshot_paths import default_evaluations_dir, default_security_dir _DEFAULT_SECURITY_DIR = default_security_dir(Path(__file__)) _DEFAULT_EVALUATIONS_DIR = default_evaluations_dir(Path(__file__)) _SCHEMA_VERSION = "iwooos_security_control_coverage_v1" _SECURITY_SNAPSHOTS = { "asset_ledger": "security-asset-control-ledger.snapshot.json", "host_service": "host-service-config-inventory.snapshot.json", "monitoring": "monitoring-alerting-observability-inventory.snapshot.json", "ssh_network": "ssh-network-access-inventory.snapshot.json", "wazuh_hosts": "wazuh-managed-host-coverage-gate.snapshot.json", "agent_bounty": "agent-bounty-iwooos-onboarding-handoff.snapshot.json", } def load_latest_iwooos_security_control_coverage( security_dir: Path | None = None, evaluations_dir: Path | None = None, ) -> dict[str, Any]: """Load committed security-control inventory snapshots as one rollup.""" sec_dir = security_dir or _DEFAULT_SECURITY_DIR eval_dir = evaluations_dir or _DEFAULT_EVALUATIONS_DIR snapshots = { key: _load_json(sec_dir / filename) for key, filename in _SECURITY_SNAPSHOTS.items() } runtime_inventory = _load_latest(eval_dir, "runtime_surface_inventory_*.json") ai_inventory = _load_latest(eval_dir, "ai_agent_automation_inventory_snapshot_*.json") snapshots["runtime_surface"] = runtime_inventory snapshots["ai_agent_automation"] = ai_inventory _require_snapshot_schemas(snapshots) _require_zero_runtime_boundaries(snapshots) domains = _build_domains(snapshots) summary = _build_summary(snapshots, domains) return { "schema_version": _SCHEMA_VERSION, "status": "committed_scope_rollup_ready_with_controlled_apply_exception", "mode": "committed_snapshot_rollup_only_no_live_runtime_query", "summary": summary, "domains": domains, "p0_next_actions": _build_p0_next_actions(), "no_false_green_rules": [ "納管覆蓋總表只代表 committed snapshot 可讀,不代表所有主機已被 Wazuh manager registry 驗收。", "route 200、transport observed、UI 可見、一般工作批准都不能當成 runtime 授權。", "IwoooS ledger 的 owner response received / accepted、live evidence accepted、active scan、active response、Telegram send、host write 仍維持 0 / false;這不阻擋 AwoooP allowlisted controlled apply。", "Nginx、Firewall、Workflow、Secret、K8s、Docker、systemd、AI Agent provider 變更都必須先進 execution packet、check-mode、rollback、verifier 與 KM / PlayBook trust,不得繞過受控路由。", ], "source_refs": [ "docs/security/security-asset-control-ledger.snapshot.json", "docs/security/host-service-config-inventory.snapshot.json", "docs/security/monitoring-alerting-observability-inventory.snapshot.json", "docs/security/ssh-network-access-inventory.snapshot.json", "docs/security/wazuh-managed-host-coverage-gate.snapshot.json", "docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json", f"docs/evaluations/{runtime_inventory['_snapshot_name']}", f"docs/evaluations/{ai_inventory['_snapshot_name']}", ], } def _load_json(path: Path) -> dict[str, Any]: with path.open(encoding="utf-8") as handle: payload = json.load(handle) if not isinstance(payload, dict): raise ValueError(f"{path}: expected JSON object") payload["_snapshot_name"] = path.name return payload def _load_latest(directory: Path, pattern: str) -> dict[str, Any]: candidates = sorted(directory.glob(pattern)) if not candidates: raise FileNotFoundError(f"no snapshots found: {directory / pattern}") return _load_json(candidates[-1]) def _require_snapshot_schemas(snapshots: dict[str, dict[str, Any]]) -> None: expected = { "asset_ledger": "security_asset_control_ledger_v1", "host_service": "host_service_config_inventory_v1", "monitoring": "monitoring_alerting_observability_inventory_v1", "ssh_network": "ssh_network_access_inventory_v1", "wazuh_hosts": "wazuh_managed_host_coverage_gate_v1", "agent_bounty": "agent_bounty_iwooos_onboarding_handoff_v1", "runtime_surface": "runtime_surface_inventory_v1", "ai_agent_automation": "ai_agent_automation_inventory_snapshot_v1", } for key, schema in expected.items(): actual = snapshots[key].get("schema_version") if actual != schema: raise ValueError(f"{key}: expected schema_version={schema}, got {actual!r}") def _require_zero_runtime_boundaries(snapshots: dict[str, dict[str, Any]]) -> None: for key in ("asset_ledger", "host_service", "monitoring", "ssh_network", "wazuh_hosts"): boundaries = snapshots[key].get("execution_boundaries") or {} allowed = sorted( name for name, value in boundaries.items() if name != "not_authorization" and value is not False ) if allowed: raise ValueError(f"{key}: execution boundaries must stay false: {allowed}") runtime_approvals = snapshots["runtime_surface"].get("approval_boundaries") or {} allowed_runtime = sorted(name for name, value in runtime_approvals.items() if value is not False) if allowed_runtime: raise ValueError(f"runtime_surface: approval boundaries must stay false: {allowed_runtime}") runtime_operations = snapshots["runtime_surface"].get("operation_boundaries") or {} if runtime_operations.get("read_only_api_allowed") is not True: raise ValueError("runtime_surface: read_only_api_allowed must remain true") blocked_runtime_ops = { "live_k8s_query_allowed", "kubectl_allowed", "rollout_allowed", "restart_allowed", "scale_allowed", "delete_allowed", "secret_read_allowed", "secret_plaintext_allowed", "active_scan_allowed", "production_route_change_allowed", } allowed_ops = sorted(name for name in blocked_runtime_ops if runtime_operations.get(name) is not False) if allowed_ops: raise ValueError(f"runtime_surface: operation boundaries must stay false: {allowed_ops}") ai_program = snapshots["ai_agent_automation"].get("program_status") or {} if ai_program.get("read_only_mode") is not True: raise ValueError("ai_agent_automation: read_only_mode must remain true") ai_approvals = snapshots["ai_agent_automation"].get("approval_boundaries") or {} allowed_ai = sorted(name for name, value in ai_approvals.items() if value is not False) if allowed_ai: raise ValueError(f"ai_agent_automation: approval boundaries must stay false: {allowed_ai}") agent_bounty_summary = snapshots["agent_bounty"].get("summary") or {} blocked_agent_bounty = { "owner_response_received", "owner_response_accepted", "repo_refs_truth_accepted", "data_classification_accepted", "deployment_boundary_accepted", "external_agent_boundary_accepted", "runtime_execution_authorized", "runtime_gate_open", "production_deploy_authorized", "workflow_modification_authorized", "secret_value_collection_authorized", "external_agent_autonomy_authorized", "auto_claim_submit_authorized", "bounty_payout_authorized", } allowed_agent_bounty = sorted( name for name in blocked_agent_bounty if agent_bounty_summary.get(name) is not False ) if allowed_agent_bounty: raise ValueError(f"agent_bounty: blocked summary flags must stay false: {allowed_agent_bounty}") def _build_domains(snapshots: dict[str, dict[str, Any]]) -> list[dict[str, Any]]: asset_summary = snapshots["asset_ledger"].get("summary") or {} host_summary = snapshots["host_service"].get("summary") or {} monitoring_summary = snapshots["monitoring"].get("summary") or {} ssh_summary = snapshots["ssh_network"].get("summary") or {} wazuh_summary = snapshots["wazuh_hosts"].get("summary") or {} runtime_rollups = snapshots["runtime_surface"].get("rollups") or {} runtime_program = snapshots["runtime_surface"].get("program_status") or {} agent_bounty_summary = snapshots["agent_bounty"].get("summary") or {} ai_program = snapshots["ai_agent_automation"].get("program_status") or {} ai_assets = snapshots["ai_agent_automation"].get("assets") or [] return [ { "domain_id": "high_value_asset_control", "label": "高價值資產與配置總帳", "priority": "P0", "coverage_percent": _as_int(asset_summary.get("security_asset_control_ledger_completion_percent")), "scope_count": _as_int(asset_summary.get("asset_group_count")), "write_capable_count": _as_int(asset_summary.get("c0_asset_group_count")), "accepted_count": _as_int(asset_summary.get("owner_response_accepted_count")), "blocked_count": _as_int(asset_summary.get("owner_packet_required_count")), "status": "owner_packet_required", "next_gate": "owner_packet_and_redacted_live_evidence", "source_refs": ["docs/security/security-asset-control-ledger.snapshot.json"], }, { "domain_id": "host_service_runtime", "label": "主機服務 / Docker / systemd", "priority": "P0", "coverage_percent": _as_int(host_summary.get("coverage_percent_after_inventory")), "scope_count": _as_int(host_summary.get("surface_count")), "write_capable_count": _as_int(host_summary.get("write_capable_surface_count")), "accepted_count": _as_int(host_summary.get("owner_response_accepted_count")), "blocked_count": _as_int(host_summary.get("surfaces_requiring_owner_response_count")), "status": "waiting_live_hash_and_owner_response", "next_gate": "host_live_hash_restart_window_rollback_owner", "source_refs": ["docs/security/host-service-config-inventory.snapshot.json"], }, { "domain_id": "monitoring_alerting_observability", "label": "監控 / 告警 / 可觀測性 / 通知", "priority": "P0", "coverage_percent": _as_int(monitoring_summary.get("coverage_percent_after_inventory")), "scope_count": _as_int(monitoring_summary.get("surface_count")), "write_capable_count": _as_int(monitoring_summary.get("write_capable_surface_count")), "accepted_count": _as_int(monitoring_summary.get("owner_response_accepted_count")), "blocked_count": _as_int(monitoring_summary.get("surfaces_requiring_owner_response_count")), "status": "waiting_receiver_route_and_receipt_evidence", "next_gate": "alert_readability_receiver_receipt_reload_owner", "source_refs": ["docs/security/monitoring-alerting-observability-inventory.snapshot.json"], }, { "domain_id": "ssh_firewall_network_access", "label": "SSH / Firewall / WireGuard / NodePort / NetworkPolicy", "priority": "P0", "coverage_percent": _as_int(ssh_summary.get("coverage_percent_after_inventory")), "scope_count": _as_int(ssh_summary.get("surface_count")), "write_capable_count": _as_int(ssh_summary.get("write_capable_surface_count")), "accepted_count": _as_int(ssh_summary.get("owner_response_accepted_count")), "blocked_count": _as_int(ssh_summary.get("surfaces_requiring_owner_response_count")), "status": "waiting_actor_before_after_and_recurrence_guard", "next_gate": "post_incident_network_change_readback", "source_refs": ["docs/security/ssh-network-access-inventory.snapshot.json"], }, { "domain_id": "awoooi_runtime_surfaces", "label": "AWOOOI runtime 工作負載 / Secret / Ingress / CronJob", "priority": "P0", "coverage_percent": _as_int(runtime_program.get("overall_completion_percent")), "scope_count": _as_int(runtime_rollups.get("total_surfaces")), "write_capable_count": len(runtime_rollups.get("secret_surface_ids") or []), "accepted_count": 0, "blocked_count": len(runtime_rollups.get("action_required_surface_ids") or []), "status": "manifest_mapped_read_only_runtime_gate_closed", "next_gate": "live_runtime_binding_owner_acceptance", "source_refs": [f"docs/evaluations/{snapshots['runtime_surface']['_snapshot_name']}"], }, { "domain_id": "wazuh_managed_host_coverage", "label": "Wazuh 主機納管與 manager registry", "priority": "P0", "coverage_percent": 70 if _as_int(wazuh_summary.get("manager_registry_accepted_count")) else 0, "scope_count": _as_int(wazuh_summary.get("expected_host_scope_count")), "write_capable_count": _as_int(wazuh_summary.get("agent_reenroll_authorized_count")), "accepted_count": _as_int(wazuh_summary.get("manager_registry_accepted_count")), "blocked_count": max( _as_int(wazuh_summary.get("expected_host_scope_count")) - _as_int(wazuh_summary.get("manager_registry_accepted_count")), 0, ), "status": ( "manager_registry_readback_accepted_runtime_gate_closed" if _as_int(wazuh_summary.get("manager_registry_accepted_count")) else "waiting_manager_registry_readback" ), "next_gate": "runtime_gate_owner_review_and_postcheck", "source_refs": ["docs/security/wazuh-managed-host-coverage-gate.snapshot.json"], }, { "domain_id": "agent_bounty_protocol", "label": "agent-bounty-protocol 產品邊界 / MCP / A2A", "priority": "P0", "coverage_percent": _as_int(agent_bounty_summary.get("onboarding_handoff_completion_percent")), "scope_count": len(snapshots["agent_bounty"].get("product_surfaces") or []), "write_capable_count": 0, "accepted_count": 0, "blocked_count": len(snapshots["agent_bounty"].get("product_surfaces") or []), "status": "draft_waiting_owner_review_runtime_gate_closed", "next_gate": "repo_refs_data_classification_deployment_boundary_owner_acceptance", "source_refs": ["docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json"], }, { "domain_id": "ai_agent_automation", "label": "AI Agent / Provider / 自動化資產", "priority": "P0", "coverage_percent": _as_int(ai_program.get("overall_completion_percent")), "scope_count": len(ai_assets), "write_capable_count": 0, "accepted_count": 0, "blocked_count": len( [ asset for asset in ai_assets if isinstance(asset, dict) and asset.get("status") != "done" ] ), "status": "read_only_inventory_runtime_write_gate_closed", "next_gate": "owner_approved_runtime_write_gate_and_replay_shadow_canary", "source_refs": [f"docs/evaluations/{snapshots['ai_agent_automation']['_snapshot_name']}"], }, ] def _build_summary(snapshots: dict[str, dict[str, Any]], domains: list[dict[str, Any]]) -> dict[str, Any]: visible_scope_count = sum(_as_int(domain.get("scope_count")) for domain in domains) write_capable_count = sum(_as_int(domain.get("write_capable_count")) for domain in domains) blocked_count = sum(_as_int(domain.get("blocked_count")) for domain in domains) accepted_count = sum(_as_int(domain.get("accepted_count")) for domain in domains) coverage_values = [_as_int(domain.get("coverage_percent")) for domain in domains] return { "source_snapshot_count": 8, "control_domain_count": len(domains), "visible_scope_unit_count": visible_scope_count, "write_capable_scope_count": write_capable_count, "blocked_scope_count": blocked_count, "accepted_scope_count": accepted_count, "control_plane_visibility_percent": round(sum(coverage_values) / len(coverage_values)), "actual_runtime_acceptance_percent": 0, "runtime_gate_count": 0, "owner_response_received_count": 0, "owner_response_accepted_count": 0, "live_evidence_accepted_count": 0, "wazuh_manager_registry_accepted_count": _summary_int( snapshots["wazuh_hosts"], "manager_registry_accepted_count", ), "active_scan_authorized_count": 0, "active_response_authorized_count": 0, "telegram_send_authorized_count": 0, "host_write_authorized_count": 0, "secret_value_collected_count": 0, "agent_bounty_runtime_gate_open_count": 0, "ai_agent_runtime_write_gate_open_count": 0, "asset_group_count": _summary_int(snapshots["asset_ledger"], "asset_group_count"), "host_service_surface_count": _summary_int(snapshots["host_service"], "surface_count"), "monitoring_surface_count": _summary_int(snapshots["monitoring"], "surface_count"), "ssh_network_surface_count": _summary_int(snapshots["ssh_network"], "surface_count"), "runtime_surface_count": _as_int((snapshots["runtime_surface"].get("rollups") or {}).get("total_surfaces")), "wazuh_expected_host_scope_count": _summary_int(snapshots["wazuh_hosts"], "expected_host_scope_count"), "agent_bounty_product_surface_count": len(snapshots["agent_bounty"].get("product_surfaces") or []), "ai_agent_asset_count": len(snapshots["ai_agent_automation"].get("assets") or []), "all_scope_runtime_controlled": False, "allowlisted_controlled_apply_bypasses_iwooos_ledger": True, "controlled_apply_policy": "low_medium_high_allowed_after_allowlist_check_mode_rollback_verifier_km", "critical_break_glass_required": True, } def _build_p0_next_actions() -> list[dict[str, str]]: return [ { "priority": "P0-01", "title": "Wazuh manager registry 全主機交叉驗收", "required_evidence": "manager registry、agent status、dashboard API / RBAC / TLS readback、缺席主機 owner decision。", }, { "priority": "P0-02", "title": "Host / Docker / systemd live hash 與維護窗口", "required_evidence": "live config hash、restart window、rollback owner、post-check 指標、drift disposition。", }, { "priority": "P0-03", "title": "Nginx / 公開入口 / Firewall 變更控管", "required_evidence": "脫敏 live conf export、rendered diff、nginx -t、route smoke、actor before / after。", }, { "priority": "P0-04", "title": "監控告警卡片化與 receipt", "required_evidence": "可讀卡片格式、receiver route、Telegram / webhook receipt、silence / dedup / inhibit review。", }, { "priority": "P0-05", "title": "AI Agent runtime write gate", "required_evidence": "allowlist、check-mode、rollback、verifier、KM / PlayBook trust;付費 provider / replacement 另需 replay / shadow / canary。", }, { "priority": "P0-06", "title": "agent-bounty-protocol 安全納管", "required_evidence": "repo refs truth、data classification、MCP / A2A abuse boundary、deployment boundary、owner acceptance。", }, ] def _summary_int(snapshot: dict[str, Any], key: str) -> int: return _as_int((snapshot.get("summary") or {}).get(key)) def _as_int(value: Any) -> int: return value if isinstance(value, int) else 0