diff --git a/apps/api/src/services/iwooos_security_control_coverage.py b/apps/api/src/services/iwooos_security_control_coverage.py index 9bee0d25e..d98f81253 100644 --- a/apps/api/src/services/iwooos_security_control_coverage.py +++ b/apps/api/src/services/iwooos_security_control_coverage.py @@ -50,6 +50,32 @@ def load_latest_iwooos_security_control_coverage( domains = _build_domains(snapshots) summary = _build_summary(snapshots, domains) + p0_next_actions = _build_p0_next_actions() + professional_review = _build_professional_security_governance_review( + summary, + domains, + p0_next_actions, + ) + summary.update( + { + "professional_review_finding_count": len( + professional_review["root_cause_summary"] + ), + "professional_review_p0_work_item_count": len( + professional_review["priority_work_items"] + ), + "external_security_tool_track_count": len( + professional_review["security_tool_integration_matrix"] + ), + "ai_security_automation_stage_count": len( + professional_review["ai_automation_closure_loop"] + ), + "full_scope_contract_count": len(professional_review["scope_contract"]), + "ui_ux_control_room_requirement_count": len( + professional_review["ui_ux_control_room_requirements"] + ), + } + ) return { "schema_version": _SCHEMA_VERSION, @@ -57,7 +83,8 @@ def load_latest_iwooos_security_control_coverage( "mode": "committed_snapshot_rollup_only_no_live_runtime_query", "summary": summary, "domains": domains, - "p0_next_actions": _build_p0_next_actions(), + "professional_review": professional_review, + "p0_next_actions": p0_next_actions, "no_false_green_rules": [ "納管覆蓋總表只代表 committed snapshot 可讀,不代表所有主機已被 Wazuh manager registry 驗收。", "route 200、transport observed、UI 可見、一般工作批准都不能當成 runtime 授權。", @@ -361,37 +388,259 @@ 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。", + "title": "全域資安完成口徑與 owner closure", + "required_evidence": "每個主機、服務、產品、網站、工具、套件與 AI Agent 都要有 scope、owner、risk、verifier、runtime acceptance 欄位;UI / API / CD 綠燈不得算完成。", }, { "priority": "P0-02", - "title": "Host / Docker / systemd live hash 與維護窗口", - "required_evidence": "live config hash、restart window、rollback owner、post-check 指標、drift disposition。", + "title": "全資產 / 全主機 / 全服務 scope truth", + "required_evidence": "Gitea source truth、runtime surface inventory、host/service live hash、公開入口、runner、backup、AI tool 權限與缺口清單;不得用單一 dashboard 代表全域。", }, { "priority": "P0-03", - "title": "Nginx / 公開入口 / Firewall 變更控管", - "required_evidence": "脫敏 live conf export、rendered diff、nginx -t、route smoke、actor before / after。", + "title": "Wazuh manager registry、FIM、弱點與告警 receipt", + "required_evidence": "manager registry parity、agent active/disconnected rollup、FIM baseline、vulnerability detection rollup、alert sample receipt、TLS/RBAC readback、缺席主機 decision。", }, { "priority": "P0-04", - "title": "監控告警卡片化與 receipt", - "required_evidence": "可讀卡片格式、receiver route、Telegram / webhook receipt、silence / dedup / inhibit review。", + "title": "Host / Docker / systemd 入侵與鑑識基線", + "required_evidence": "auth、sudo、process、network、persistence、package、service、Docker event 的脫敏 readback;所有 remediation 先進 check-mode、rollback、post-verifier。", }, { "priority": "P0-05", - "title": "AI Agent runtime write gate", - "required_evidence": "allowlist、check-mode、rollback、verifier、KM / PlayBook trust;付費 provider / replacement 另需 replay / shadow / canary。", + "title": "Nginx / Gateway / SSH / Firewall / K8s network", + "required_evidence": "source-to-live diff、nginx -t 或等價檢核、route smoke、before/after actor、NetworkPolicy / firewall diff、rollback owner。", }, { "priority": "P0-06", - "title": "agent-bounty-protocol 安全納管", - "required_evidence": "repo refs truth、data classification、MCP / A2A abuse boundary、deployment boundary、owner acceptance。", + "title": "Web / API / AppSec / webhook 防護", + "required_evidence": "auth、authorization、session、rate limit、CORS、security headers、webhook abuse case、ASVS mapping、desktop/mobile smoke 與 no sensitive copy。", + }, + { + "priority": "P0-07", + "title": "套件 / container / workflow supply-chain", + "required_evidence": "SBOM、lockfile diff、image scan、KEV/EPSS priority、runner/workflow diff、artifact provenance、signature verification、package SLA。", + }, + { + "priority": "P0-08", + "title": "SIEM / SOAR / Monitoring 告警閉環", + "required_evidence": "Wazuh / Alertmanager / Telegram / OCSF / Sigma normalized event、dedupe、noise budget、case id、receiver receipt、operator acknowledgment。", + }, + { + "priority": "P0-09", + "title": "Backup / restore / forensic retention", + "required_evidence": "restore drill、offsite/escrow、chain of custody、retention policy、backup freshness、rollback proof、post-restore smoke。", + }, + { + "priority": "P0-10", + "title": "AI Agent / MCP / RAG permission gate", + "required_evidence": "tool allowlist、prompt redaction、secret boundary、RAG source trust、MCP connector scope、cost cap、replay/shadow/canary、KM / PlayBook writeback。", + }, + { + "priority": "P0-11", + "title": "AISOC 管理者 UI / UX 控制室", + "required_evidence": "單一風險地圖、目前 P0、blocked reason、next executable action、tool coverage、host/service/product/package drilldown;避免以大段文字取代狀態。", + }, + { + "priority": "P0-12", + "title": "Controlled apply deployment 與 production readback", + "required_evidence": "target selector、source-of-truth diff、check-mode/dry-run、rollback、post-apply verifier、runtime readback、KM / PlayBook trust、deploy marker。", }, ] +def _build_professional_security_governance_review( + summary: dict[str, Any], + domains: list[dict[str, Any]], + priority_work_items: list[dict[str, str]], +) -> dict[str, Any]: + runtime_acceptance = _as_int(summary.get("actual_runtime_acceptance_percent")) + owner_accepted = _as_int(summary.get("owner_response_accepted_count")) + runtime_gate_count = _as_int(summary.get("runtime_gate_count")) + visible_scope_count = _as_int(summary.get("visible_scope_unit_count")) + domain_count = len(domains) + + root_causes = [ + { + "finding_id": "F01", + "severity": "P0", + "title": "控制面可視不等於 runtime 防護生效", + "current_evidence": ( + f"已可讀 {visible_scope_count} 個納管單位與 {domain_count} 個控制域;" + f"runtime acceptance 仍為 {runtime_acceptance}%。" + ), + "required_closure": "每個 control domain 必須補 live/public-safe readback、post-verifier 與 accepted runtime gate。", + }, + { + "finding_id": "F02", + "severity": "P0", + "title": "完成定義混用了 UI / API / CD 成功與資安完成", + "current_evidence": "Dashboard 200、frontend visible、CD success、route smoke 只能算 delivery evidence。", + "required_closure": "把 delivery evidence 與 security runtime acceptance 拆開顯示,所有綠燈必須附 source、runtime、verifier 三段證據。", + }, + { + "finding_id": "F03", + "severity": "P0", + "title": "Wazuh 已有 registry snapshot,但尚未形成偵測與回應閉環", + "current_evidence": "Manager registry accepted 可以計數,但 live metadata、FIM、弱點 rollup、alert receipt 與 active-response gate 仍是不同閉環。", + "required_closure": "先完成 Wazuh registry parity、FIM / vulnerability readback、alert receipt,再讓 active response 保持 gated candidate。", + }, + { + "finding_id": "F04", + "severity": "P0", + "title": "主機、服務、產品、網站、工具與套件缺少同一張優先序", + "current_evidence": "既有 domain 涵蓋資產、主機、監控、網路、runtime、Wazuh、產品與 AI Agent,但套件 / 網站 / 工具閉環沒有集中成同一條順序。", + "required_closure": "用同一個 P0 list 管理 hosts, services, products, websites, tools, packages and AI automation,不再讓支線互相搶主線。", + }, + { + "finding_id": "F05", + "severity": "P0", + "title": "AI Agent 自動化還沒有變成可執行的安全閉環", + "current_evidence": "AI assets are inventoried, while runtime write gates and action buttons remain closed.", + "required_closure": "每個 AI action 必須有 tool allowlist、source diff、check-mode、rollback、post-verifier、cost/privacy gate 與 learning writeback。", + }, + { + "finding_id": "F06", + "severity": "P1", + "title": "管理者 UI 仍偏文字報告,不是 AISOC 控制台", + "current_evidence": "The current board exposes correct boundaries but does not yet compress risk, blockers, next action and tool coverage into one glance.", + "required_closure": "新增風險地圖、P0 queue、tool coverage、blocked reason、next executable action and drilldown,讓文字退到 details。", + }, + ] + + tool_matrix = [ + { + "tool_id": "wazuh", + "priority": "P0", + "role": "XDR / SIEM, agent registry, FIM, vulnerability detection, alert normalization", + "integration_state": "partial_registry_readback_runtime_gate_closed", + "required_closure": "registry parity、FIM baseline、vulnerability rollup、alert receipt、SOAR candidate gate。", + }, + { + "tool_id": "trivy", + "priority": "P0", + "role": "container image, filesystem, IaC, dependency and SBOM vulnerability scanning", + "integration_state": "required_not_closed", + "required_closure": "CI/check-mode scan、KEV/EPSS priority、artifact report、no-secret output guard。", + }, + { + "tool_id": "syft_grype", + "priority": "P0", + "role": "SBOM generation and vulnerability matching for packages and images", + "integration_state": "required_not_closed", + "required_closure": "SPDX/CycloneDX SBOM、lockfile diff、package SLA、VEX / accepted-risk register。", + }, + { + "tool_id": "openssf_scorecard", + "priority": "P1", + "role": "source, build, dependency, testing and maintenance risk scoring", + "integration_state": "required_not_closed", + "required_closure": "Gitea/source-side hygiene mapping、branch protection equivalent、dependency/update policy readback。", + }, + { + "tool_id": "kyverno_cosign", + "priority": "P1", + "role": "Kubernetes policy, image signature and attestation verification", + "integration_state": "required_when_k8s_runtime_gate_opens", + "required_closure": "policy check-mode、image verification、exception register、rollback and post-apply verifier。", + }, + { + "tool_id": "falco", + "priority": "P1", + "role": "runtime threat detection for Linux and Kubernetes workloads", + "integration_state": "required_not_closed", + "required_closure": "read-only rule dry-run、event redaction、Wazuh/OCSF correlation、no auto-block until reviewer gate。", + }, + { + "tool_id": "owasp_zap_or_equivalent_dast", + "priority": "P1", + "role": "web/API dynamic testing with bounded scope", + "integration_state": "controlled_scan_required", + "required_closure": "explicit target selector、rate limit、test window、baseline scan, no credentialed attack unless break-glass approved。", + }, + { + "tool_id": "sigma_ocsf", + "priority": "P1", + "role": "portable detection rules and normalized event schema", + "integration_state": "required_for_ai_triage_quality", + "required_closure": "map Wazuh / Alertmanager / app events into normalized cards and regression-test false positives。", + }, + ] + + scope_contract = [ + {"scope_id": "hosts", "included": True, "closure_signal": "agent / process / service / network readback"}, + {"scope_id": "services", "included": True, "closure_signal": "systemd / Docker / API health and config hash"}, + {"scope_id": "products", "included": True, "closure_signal": "owner, data classification, route and deploy boundary"}, + {"scope_id": "websites", "included": True, "closure_signal": "desktop/mobile smoke, headers, auth and webhook abuse review"}, + {"scope_id": "tools", "included": True, "closure_signal": "security tool health, version, rule pack and output redaction"}, + {"scope_id": "packages", "included": True, "closure_signal": "SBOM, lockfile diff, vulnerability SLA and provenance"}, + {"scope_id": "ai_agents", "included": True, "closure_signal": "tool allowlist, MCP/RAG trust, replay/shadow/canary"}, + {"scope_id": "runtime_actions", "included": True, "closure_signal": "target selector, check-mode, rollback and verifier"}, + ] + + ai_loop = [ + {"stage_id": "collect", "gate": "public_safe_readback_only", "runtime_write": False}, + {"stage_id": "normalize", "gate": "redaction_and_schema_guard", "runtime_write": False}, + {"stage_id": "triage", "gate": "severity_scope_confidence_scoring", "runtime_write": False}, + {"stage_id": "plan", "gate": "target_selector_source_diff_check_mode_rollback", "runtime_write": False}, + {"stage_id": "apply", "gate": "allowlisted_controlled_apply_only", "runtime_write": runtime_gate_count > 0}, + {"stage_id": "verify", "gate": "post_apply_readback_and_no_false_green", "runtime_write": False}, + {"stage_id": "learn", "gate": "KM_PlayBook_trust_writeback", "runtime_write": False}, + ] + + ui_requirements = [ + "one_glance_current_p0_blocker_next_action", + "host_service_product_package_tool_coverage_map", + "wazuh_registry_fim_vulnerability_alert_receipt_status", + "runtime_acceptance_not_confused_with_deploy_success", + "ai_agent_action_queue_with_check_mode_verifier_state", + "details_only_for_long_boundary_text", + ] + + acceptance_definition = [ + "all_scope_items_have_owner_scope_risk_and_verifier", + "wazuh_registry_fim_vulnerability_and_alert_receipt_readback_green", + "critical_tools_have_health_version_rule_and_redaction_readback", + "packages_and_images_have_sbom_vulnerability_and_provenance_receipts", + "ai_agent_actions_have_allowlist_check_mode_rollback_post_verifier", + "production_dashboard_shows_runtime_acceptance_separate_from_delivery", + ] + + return { + "schema_version": "iwooos_security_governance_professional_review_v1", + "status": "review_updated_runtime_closure_required", + "review_scope_statement": ( + "Covers hosts, services, products, websites, tools, packages, security tools, " + "runtime actions, AI Agents, MCP/RAG and production readback." + ), + "completion_diagnosis": { + "control_plane_visibility_percent": _as_int( + summary.get("control_plane_visibility_percent") + ), + "actual_runtime_acceptance_percent": runtime_acceptance, + "owner_response_accepted_count": owner_accepted, + "runtime_gate_count": runtime_gate_count, + "visible_scope_unit_count": visible_scope_count, + "control_domain_count": domain_count, + "diagnosis": "control_plane_exists_runtime_security_closure_not_complete", + }, + "root_cause_summary": root_causes, + "scope_contract": scope_contract, + "priority_work_items": priority_work_items, + "security_tool_integration_matrix": tool_matrix, + "ai_automation_closure_loop": ai_loop, + "ui_ux_control_room_requirements": ui_requirements, + "acceptance_definition": acceptance_definition, + "boundaries": { + "secret_value_collection_allowed": False, + "raw_session_or_sqlite_read_allowed": False, + "active_scan_without_target_selector_allowed": False, + "auto_block_without_reviewer_allowed": False, + "runtime_write_without_post_verifier_allowed": False, + }, + } + + def _summary_int(snapshot: dict[str, Any], key: str) -> int: return _as_int((snapshot.get("summary") or {}).get(key)) diff --git a/apps/api/tests/test_iwooos_security_control_coverage.py b/apps/api/tests/test_iwooos_security_control_coverage.py index 6d1a8b383..d897f5e56 100644 --- a/apps/api/tests/test_iwooos_security_control_coverage.py +++ b/apps/api/tests/test_iwooos_security_control_coverage.py @@ -31,6 +31,12 @@ def test_iwooos_security_control_coverage_rolls_up_core_scopes() -> None: assert payload["summary"]["wazuh_expected_host_scope_count"] == 6 assert payload["summary"]["agent_bounty_product_surface_count"] == 7 assert payload["summary"]["ai_agent_asset_count"] == 24 + assert payload["summary"]["professional_review_finding_count"] == 6 + assert payload["summary"]["professional_review_p0_work_item_count"] == 12 + assert payload["summary"]["external_security_tool_track_count"] == 8 + assert payload["summary"]["ai_security_automation_stage_count"] == 7 + assert payload["summary"]["full_scope_contract_count"] == 8 + assert payload["summary"]["ui_ux_control_room_requirement_count"] == 6 domain_ids = {domain["domain_id"] for domain in payload["domains"]} assert domain_ids == { @@ -45,6 +51,44 @@ def test_iwooos_security_control_coverage_rolls_up_core_scopes() -> None: } +def test_iwooos_security_control_coverage_exposes_professional_review() -> None: + payload = load_latest_iwooos_security_control_coverage() + review = payload["professional_review"] + + assert review["schema_version"] == "iwooos_security_governance_professional_review_v1" + assert review["completion_diagnosis"]["diagnosis"] == ( + "control_plane_exists_runtime_security_closure_not_complete" + ) + assert review["completion_diagnosis"]["actual_runtime_acceptance_percent"] == 0 + assert review["root_cause_summary"][0]["finding_id"] == "F01" + assert review["root_cause_summary"][0]["severity"] == "P0" + assert {scope["scope_id"] for scope in review["scope_contract"]} == { + "hosts", + "services", + "products", + "websites", + "tools", + "packages", + "ai_agents", + "runtime_actions", + } + assert [item["priority"] for item in review["priority_work_items"][:3]] == [ + "P0-01", + "P0-02", + "P0-03", + ] + assert {tool["tool_id"] for tool in review["security_tool_integration_matrix"]} >= { + "wazuh", + "trivy", + "syft_grype", + "openssf_scorecard", + "kyverno_cosign", + "falco", + } + assert review["ai_automation_closure_loop"][0]["stage_id"] == "collect" + assert all(stage["runtime_write"] is False for stage in review["ai_automation_closure_loop"]) + + def test_iwooos_security_control_coverage_keeps_runtime_gates_closed() -> None: payload = load_latest_iwooos_security_control_coverage() summary = payload["summary"] @@ -83,6 +127,9 @@ def test_iwooos_security_control_coverage_api_is_public_safe() -> None: assert data["schema_version"] == "iwooos_security_control_coverage_v1" assert data["summary"]["runtime_gate_count"] == 0 assert data["summary"]["visible_scope_unit_count"] == 160 + assert data["professional_review"]["schema_version"] == ( + "iwooos_security_governance_professional_review_v1" + ) assert any(action["priority"] == "P0-01" for action in data["p0_next_actions"]) assert "192.168.0." not in response.text assert "runtime_control_blocked" not in response.text diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 43e382e6e..9afb899fa 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -21828,6 +21828,25 @@ "wazuhAccepted": { "label": "Wazuh accepted", "detail": "Manager registry accepted 已讀回 6;runtime gate 仍為 0。" + }, + "reviewFindings": { + "label": "Review 根因", + "detail": "專業 review 已把未完成原因集中成可追的根因。" + }, + "toolTracks": { + "label": "工具軌", + "detail": "Wazuh、SBOM、runtime detection、policy 與掃描工具進入同一矩陣。" + }, + "aiStages": { + "label": "AI 閉環", + "detail": "AI Agent 從收集、分流、計畫、驗證到學習回寫都需受控。" + } + }, + "review": { + "title": "為什麼還沒有完成資安管理閉環", + "diagnosis": { + "controlPlaneRuntimeGap": "控制面已建立,runtime 防護閉環未完成", + "unknown": "需要重新讀回診斷狀態" } }, "domainMetric": { diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 8ed5bdf57..2f022a08d 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -21828,6 +21828,25 @@ "wazuhAccepted": { "label": "Wazuh accepted", "detail": "Manager registry accepted 已讀回 6;runtime gate 仍為 0。" + }, + "reviewFindings": { + "label": "Review 根因", + "detail": "專業 review 已把未完成原因集中成可追的根因。" + }, + "toolTracks": { + "label": "工具軌", + "detail": "Wazuh、SBOM、runtime detection、policy 與掃描工具進入同一矩陣。" + }, + "aiStages": { + "label": "AI 閉環", + "detail": "AI Agent 從收集、分流、計畫、驗證到學習回寫都需受控。" + } + }, + "review": { + "title": "為什麼還沒有完成資安管理閉環", + "diagnosis": { + "controlPlaneRuntimeGap": "控制面已建立,runtime 防護閉環未完成", + "unknown": "需要重新讀回診斷狀態" } }, "domainMetric": { diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index 48e905199..dd141ba42 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -362,7 +362,16 @@ type RuntimeSecurityReadbackSummaryItem = { } type SecurityControlCoverageSummaryItem = { - key: 'visibleScopes' | 'controlDomains' | 'controlPlane' | 'runtimeAcceptance' | 'ownerAccepted' | 'wazuhAccepted' + key: + | 'visibleScopes' + | 'controlDomains' + | 'controlPlane' + | 'runtimeAcceptance' + | 'ownerAccepted' + | 'wazuhAccepted' + | 'reviewFindings' + | 'toolTracks' + | 'aiStages' value: string icon: typeof ShieldCheck tone: 'steady' | 'warn' | 'locked' @@ -8657,6 +8666,11 @@ function coverageTone(domain: IwoooSSecurityControlCoverageDomain): 'steady' | ' return 'locked' } +function securityReviewDiagnosisKey(value?: string | null) { + if (value === 'control_plane_exists_runtime_security_closure_not_complete') return 'controlPlaneRuntimeGap' + return 'unknown' +} + function IwoooSSecurityControlCoverageBoard() { const t = useTranslations('iwooos.securityControlCoverage') const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } @@ -8694,6 +8708,8 @@ function IwoooSSecurityControlCoverageBoard() { }, []) const summary = data?.summary + const review = data?.professional_review + const reviewFindings = review?.root_cause_summary.slice(0, 3) ?? [] const statusKey = loading ? 'loading' : failed || !data ? 'failed' : 'ready' const statusTone: 'steady' | 'warn' | 'locked' = loading ? 'warn' : failed || !data ? 'warn' : 'locked' const summaryItems: SecurityControlCoverageSummaryItem[] = [ @@ -8733,6 +8749,24 @@ function IwoooSSecurityControlCoverageBoard() { icon: Radar, tone: summary && summary.wazuh_manager_registry_accepted_count > 0 ? 'steady' : 'locked', }, + { + key: 'reviewFindings', + value: summary ? String(summary.professional_review_finding_count ?? reviewFindings.length) : '...', + icon: FileWarning, + tone: 'warn', + }, + { + key: 'toolTracks', + value: summary ? String(summary.external_security_tool_track_count ?? review?.security_tool_integration_matrix.length ?? 0) : '...', + icon: SearchCheck, + tone: summary && summary.external_security_tool_track_count > 0 ? 'steady' : 'warn', + }, + { + key: 'aiStages', + value: summary ? String(summary.ai_security_automation_stage_count ?? review?.ai_automation_closure_loop.length ?? 0) : '...', + icon: Workflow, + tone: summary && summary.ai_security_automation_stage_count > 0 ? 'steady' : 'warn', + }, ] const domains = data?.domains ?? [] const p0Actions = data?.p0_next_actions ?? [] @@ -8796,6 +8830,56 @@ function IwoooSSecurityControlCoverageBoard() { })} + {review ? ( +
+ {finding.required_closure} +
+