From 19bdef5470d6f1e1c8dcc3a59178c6a29dae098b Mon Sep 17 00:00:00 2001 From: ogt Date: Fri, 10 Jul 2026 18:29:56 +0800 Subject: [PATCH] fix(iwooos): surface security closure readback --- apps/web/messages/en.json | 40 +- apps/web/messages/zh-TW.json | 40 +- apps/web/src/app/[locale]/iwooos/page.tsx | 454 ++++++++++++++++++---- apps/web/src/lib/api-client.ts | 2 + 4 files changed, 447 insertions(+), 89 deletions(-) diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index b249212f4..4df958c4f 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -13941,7 +13941,7 @@ "nextValue": "進 runtime controlled apply、target selector、dry-run、rollback、postcheck", "runtimeStatus": "readback: {status}", "chips": { - "noSecret": "不讀 secret", + "noSecret": "no sensitive-value read", "noHostWrite": "不寫主機", "postcheck": "postcheck 先行" }, @@ -13965,28 +13965,46 @@ } }, "securityToolClosure": { - "eyebrow": "Tool closure readback", - "title": "Wazuh, SBOM, runtime detection, DAST and AI apply in one panel", - "subtitle": "This panel reads the controlled-execution conditions for each security tool track; it creates no side effects, while low, medium, and high risk actions move to the single executor once evidence is complete.", + "eyebrow": "Security closure", + "title": "IWOOOS security closure", + "subtitle": "Tool closure readback for Wazuh, SBOM, runtime detection, DAST, and AI apply.", "statusLabel": "Tool closure state", "statusDetail": "{ready}/{required} closure signals are readable; missing evidence is queued for AI repair.", "statusDetailFallback": "Waiting for tool-closure and controlled-candidate readback.", "queueLabel": "Next executable P0", - "boundaryTitle": "Show controlled execution and critical boundaries", + "detailsTitle": "Tool-track next steps and hard boundaries", + "boundaryTitle": "Hard boundaries", + "source": { + "live": "live readback", + "bootstrap": "latest deploy readback" + }, + "compact": { + "signals": "signals", + "sources": "sources", + "p0": "P0 tracks", + "controlled": "controlled" + }, + "guards": { + "runtimeActions": "runtime action", + "criticalSecrets": "sensitive value read", + "ownerReview": "owner gate" + }, "status": { "loading": "Reading tool closure", + "syncing": "Syncing live readback", + "bootstrap": "Showing latest deployed readback", "failed": "Tool closure readback is not deployed or failed to load", - "ready": "Controlled execution policy is active and advances by tool-track evidence" + "ready": "Live readback synced" }, "metrics": { "closure": "Closure", - "tracks": "Partial", - "wazuh": "Wazuh registry", - "alerts": "Alert receipt", - "runtime": "Controlled candidates" + "tracks": "Closed", + "wazuh": "Wazuh", + "alerts": "Alert", + "runtime": "AI candidates" }, "states": { - "ready_for_controlled_apply_candidate": "controlled candidate ready", + "ready_for_controlled_apply_candidate": "candidate ready", "partial_readback_missing_verifier": "missing verifier", "required_not_closed": "not closed" }, diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index c5192c0a4..161504bcf 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -13941,7 +13941,7 @@ "nextValue": "進 runtime controlled apply、target selector、dry-run、rollback、postcheck", "runtimeStatus": "readback: {status}", "chips": { - "noSecret": "不讀 secret", + "noSecret": "不讀敏感值", "noHostWrite": "不寫主機", "postcheck": "postcheck 先行" }, @@ -13965,28 +13965,46 @@ } }, "securityToolClosure": { - "eyebrow": "工具閉環 readback", - "title": "Wazuh、SBOM、runtime detection、DAST 與 AI apply 一屏收斂", - "subtitle": "這張面板讀回每條安全工具軌的受控執行條件;面板本身不產生 side effect,低中高風險在證據完整後交由單一 executor 執行。", + "eyebrow": "Security closure", + "title": "IWOOOS 資安閉環", + "subtitle": "Wazuh、SBOM、runtime detection、DAST 與 AI apply 的工具閉環 readback。", "statusLabel": "工具閉環狀態", "statusDetail": "已讀回 {ready}/{required} 個 closure 訊號;缺口會排入 AI 修復隊列。", "statusDetailFallback": "等待工具閉環 readback 與受控候選狀態。", "queueLabel": "下一個可執行 P0", - "boundaryTitle": "展開受控執行與 critical 邊界", + "detailsTitle": "工具軌下一步與硬邊界", + "boundaryTitle": "硬邊界", + "source": { + "live": "live readback", + "bootstrap": "latest deploy readback" + }, + "compact": { + "signals": "signals", + "sources": "sources", + "p0": "P0 軌", + "controlled": "controlled" + }, + "guards": { + "runtimeActions": "runtime action", + "criticalSecrets": "敏感值讀取", + "ownerReview": "owner gate" + }, "status": { "loading": "正在讀取工具閉環", + "syncing": "同步 live readback", + "bootstrap": "顯示最近部署驗收值", "failed": "工具閉環 readback 尚未部署或讀取失敗", - "ready": "受控執行政策已啟用,依各工具軌證據推進" + "ready": "live readback 已同步" }, "metrics": { "closure": "閉環率", - "tracks": "部分可讀", - "wazuh": "Wazuh registry", - "alerts": "Alert receipt", - "runtime": "受控候選" + "tracks": "已閉環", + "wazuh": "Wazuh", + "alerts": "Alert", + "runtime": "AI 候選" }, "states": { - "ready_for_controlled_apply_candidate": "可進受控候選", + "ready_for_controlled_apply_candidate": "候選就緒", "partial_readback_missing_verifier": "缺 verifier", "required_not_closed": "未閉環" }, diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index 081c481b0..e758a8e9f 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -527,6 +527,244 @@ type IwoooSSecurityToolClosureMetric = { tone: 'steady' | 'warn' | 'locked' } +type IwoooSSecurityToolClosureSummary = IwoooSSecurityToolClosureReadbackResponse['summary'] +type IwoooSSecurityToolClosureQueueEntry = IwoooSSecurityToolClosureReadbackResponse['next_executable_queue'][number] + +const SECURITY_TOOL_CLOSURE_BOOTSTRAP_SUMMARY: IwoooSSecurityToolClosureSummary = { + source_readback_count: 8, + tool_track_count: 8, + p0_tool_track_count: 3, + closed_tool_track_count: 2, + partial_tool_track_count: 1, + missing_tool_track_count: 5, + total_ready_signal_count: 16, + total_required_signal_count: 42, + tool_closure_percent: 38, + wazuh_expected_host_scope_count: 6, + wazuh_manager_registry_accepted_count: 6, + wazuh_registry_complete_count: 1, + wazuh_fim_vulnerability_alert_closed_count: 1, + wazuh_fim_vulnerability_alert_verifier_ready_count: 1, + wazuh_fim_vulnerability_alert_verifier_ready_signal_count: 6, + alert_receipt_observed_count: 1, + alert_receipt_accepted_count: 1, + alert_receipt_source_status: 'ready', + controlled_apply_preflight_ready_count: 1, + owner_review_packet_accepted_count: 1, + allowlisted_dry_run_result_ref_count: 1, + supply_chain_scan_closed_count: 0, + runtime_detection_closed_count: 0, + web_api_dast_closed_count: 0, + normalized_detection_schema_closed_count: 0, + controlled_apply_policy_active_count: 1, + controlled_apply_candidate_ready_count: 2, + owner_review_required_for_low_medium_high_count: 0, + runtime_execution_performed_count: 0, + secret_value_collection_allowed_count: 0, +} + +const SECURITY_TOOL_CLOSURE_BOOTSTRAP_TRACKS: IwoooSSecurityToolClosureTrack[] = [ + { + track_id: 'wazuh_detection_response', + priority: 'P0', + label: 'Wazuh detection and response', + tool_ids: ['wazuh'], + closure_state: 'ready_for_controlled_apply_candidate', + closure_percent: 100, + ready_signal_count: 6, + required_signal_count: 6, + missing_signal_count: 0, + controlled_apply_policy_allowed: true, + execution_candidate_ready: true, + runtime_execution_performed: false, + owner_review_required: false, + critical_break_glass_required: false, + secret_value_collection_allowed: false, + next_executable_action: 'attach_incident_case_and_active_response_dry_run_receipts_without_runtime_gate', + required_verifier: 'wazuh_registry_fim_vulnerability_alert_receipt_post_verifier', + source_refs: ['apps/api/src/services/iwooos_wazuh_fim_vulnerability_alert_verifier.py'], + }, + { + track_id: 'supply_chain_sbom_scan', + priority: 'P0', + label: 'Supply-chain SBOM and vulnerability scan', + tool_ids: ['trivy', 'syft', 'grype'], + closure_state: 'required_not_closed', + closure_percent: 0, + ready_signal_count: 0, + required_signal_count: 5, + missing_signal_count: 5, + controlled_apply_policy_allowed: true, + execution_candidate_ready: false, + runtime_execution_performed: false, + owner_review_required: false, + critical_break_glass_required: false, + secret_value_collection_allowed: false, + next_executable_action: 'attach_sbom_vulnerability_report_and_no_secret_output_guard', + required_verifier: 'sbom_vulnerability_artifact_post_verifier', + source_refs: ['docs/security/security-asset-control-ledger.snapshot.json'], + }, + { + track_id: 'source_control_scorecard', + priority: 'P1', + label: 'Source-control hygiene scorecard', + tool_ids: ['openssf_scorecard', 'gitea_source_policy'], + closure_state: 'required_not_closed', + closure_percent: 0, + ready_signal_count: 0, + required_signal_count: 4, + missing_signal_count: 4, + controlled_apply_policy_allowed: true, + execution_candidate_ready: false, + runtime_execution_performed: false, + owner_review_required: false, + critical_break_glass_required: false, + secret_value_collection_allowed: false, + next_executable_action: 'map_branch_protection_dependency_update_and_build_policy_readback', + required_verifier: 'source_control_policy_scorecard_verifier', + source_refs: ['docs/evaluations/runtime_surface_inventory_latest'], + }, + { + track_id: 'k8s_policy_attestation', + priority: 'P1', + label: 'Kubernetes policy and image attestation', + tool_ids: ['kyverno', 'cosign'], + closure_state: 'required_not_closed', + closure_percent: 0, + ready_signal_count: 0, + required_signal_count: 5, + missing_signal_count: 5, + controlled_apply_policy_allowed: true, + execution_candidate_ready: false, + runtime_execution_performed: false, + owner_review_required: false, + critical_break_glass_required: false, + secret_value_collection_allowed: false, + next_executable_action: 'stage_policy_check_mode_image_signature_and_exception_register', + required_verifier: 'k8s_policy_attestation_post_verifier', + source_refs: ['docs/security/host-service-config-inventory.snapshot.json'], + }, + { + track_id: 'runtime_threat_detection', + priority: 'P1', + label: 'Runtime threat detection', + tool_ids: ['falco'], + closure_state: 'required_not_closed', + closure_percent: 0, + ready_signal_count: 0, + required_signal_count: 4, + missing_signal_count: 4, + controlled_apply_policy_allowed: true, + execution_candidate_ready: false, + runtime_execution_performed: false, + owner_review_required: false, + critical_break_glass_required: false, + secret_value_collection_allowed: false, + next_executable_action: 'stage_rule_dry_run_event_redaction_and_wazuh_correlation', + required_verifier: 'runtime_detection_event_post_verifier', + source_refs: ['docs/security/monitoring-alerting-observability-inventory.snapshot.json'], + }, + { + track_id: 'web_api_dast', + priority: 'P1', + label: 'Web and API DAST', + tool_ids: ['owasp_zap_or_equivalent_dast'], + closure_state: 'required_not_closed', + closure_percent: 0, + ready_signal_count: 0, + required_signal_count: 5, + missing_signal_count: 5, + controlled_apply_policy_allowed: true, + execution_candidate_ready: false, + runtime_execution_performed: false, + owner_review_required: false, + critical_break_glass_required: false, + secret_value_collection_allowed: false, + next_executable_action: 'prepare_bounded_target_selector_rate_limit_and_baseline_report', + required_verifier: 'web_api_dast_scope_and_report_verifier', + source_refs: ['docs/security/ssh-network-access-inventory.snapshot.json'], + }, + { + track_id: 'normalized_detection_schema', + priority: 'P1', + label: 'Normalized detection schema', + tool_ids: ['sigma', 'ocsf'], + closure_state: 'partial_readback_missing_verifier', + closure_percent: 25, + ready_signal_count: 1, + required_signal_count: 4, + missing_signal_count: 3, + controlled_apply_policy_allowed: true, + execution_candidate_ready: false, + runtime_execution_performed: false, + owner_review_required: false, + critical_break_glass_required: false, + secret_value_collection_allowed: false, + next_executable_action: 'normalize_wazuh_alertmanager_app_events_into_review_cards', + required_verifier: 'sigma_ocsf_event_mapping_regression_verifier', + source_refs: ['docs/security/iwooos-security-operating-system.snapshot.json'], + }, + { + track_id: 'ai_agent_controlled_apply', + priority: 'P0', + label: 'AI Agent controlled apply loop', + tool_ids: ['ai_agent', 'mcp', 'rag', 'km_playbook'], + closure_state: 'ready_for_controlled_apply_candidate', + closure_percent: 100, + ready_signal_count: 9, + required_signal_count: 9, + missing_signal_count: 0, + controlled_apply_policy_allowed: true, + execution_candidate_ready: true, + runtime_execution_performed: false, + owner_review_required: false, + critical_break_glass_required: false, + secret_value_collection_allowed: false, + next_executable_action: 'run_allowlisted_check_mode_with_rollback_post_verifier_and_km_writeback', + required_verifier: 'ai_controlled_apply_post_verifier_and_learning_writeback', + source_refs: [ + 'docs/security/wazuh-runtime-controlled-apply-preflight.snapshot.json', + 'docs/security/wazuh-runtime-gate-owner-review-readback.snapshot.json', + 'docs/security/wazuh-allowlisted-check-mode-dry-run.snapshot.json', + ], + }, +] + +const SECURITY_TOOL_CLOSURE_BOOTSTRAP_QUEUE: IwoooSSecurityToolClosureQueueEntry[] = [ + { + queue_id: 'P0-03', + track_id: 'wazuh_detection_response', + state: 'wazuh_fim_vulnerability_alert_verifier_ready_keep_runtime_gated', + next_action: 'attach_incident_case_and_active_response_dry_run_receipts_without_runtime_gate', + ready_signal_count: 6, + controlled_apply_policy_allowed: true, + }, + { + queue_id: 'P0-07', + track_id: 'supply_chain_sbom_scan', + state: 'sbom_and_scan_artifacts_missing', + next_action: 'stage_trivy_syft_grype_check_mode_reports', + controlled_apply_policy_allowed: true, + }, + { + queue_id: 'P0-08', + track_id: 'ai_agent_controlled_apply', + state: 'preflight_ready_for_controlled_policy_check', + next_action: 'connect_target_selector_diff_check_mode_rollback_post_verifier_km', + ready_signal_count: 2, + controlled_apply_policy_allowed: true, + }, +] + +const SECURITY_TOOL_CLOSURE_BOOTSTRAP_BOUNDARY_MARKERS = [ + 'iwooos_security_tool_closure_percent=38', + 'iwooos_security_tool_closure_wazuh_manager_registry_accepted_count=6', + 'iwooos_security_tool_closure_alert_receipt_accepted_count=1', + 'iwooos_security_tool_closure_controlled_apply_candidate_ready_count=2', + 'runtime_execution_performed=false', + 'redacted_secret_value_collection_allowed=false', +] + type IwoooSImmediateVisualMeshNode = { key: string metric: string @@ -16275,8 +16513,9 @@ function IwoooSSecurityToolClosureBoard() { const t = useTranslations('iwooos.securityToolClosure') const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } const [data, setData] = useState(null) - const [loading, setLoading] = useState(true) + const [loading, setLoading] = useState(false) const [failed, setFailed] = useState(false) + const [readbackFresh, setReadbackFresh] = useState(false) useEffect(() => { let mounted = true @@ -16286,10 +16525,12 @@ function IwoooSSecurityToolClosureBoard() { setFailed(false) try { const payload = await apiClient.getIwoooSSecurityToolClosureReadback() - if (mounted) setData(payload) + if (mounted) { + setData(payload) + setReadbackFresh(true) + } } catch { if (mounted) { - setData(null) setFailed(true) } } finally { @@ -16303,40 +16544,68 @@ function IwoooSSecurityToolClosureBoard() { } }, []) - const summary = data?.summary - const statusKey = loading ? 'loading' : failed || !data ? 'failed' : 'ready' - const tracks = data?.tool_tracks ?? [] - const queue = data?.next_executable_queue ?? [] + const summary = data?.summary ?? SECURITY_TOOL_CLOSURE_BOOTSTRAP_SUMMARY + const statusKey = readbackFresh ? 'ready' : failed ? 'bootstrap' : loading ? 'syncing' : 'bootstrap' + const statusTone: 'steady' | 'warn' = readbackFresh ? 'steady' : 'warn' + const tracks = data?.tool_tracks?.length ? data.tool_tracks : SECURITY_TOOL_CLOSURE_BOOTSTRAP_TRACKS + const queue = data?.next_executable_queue?.length ? data.next_executable_queue : SECURITY_TOOL_CLOSURE_BOOTSTRAP_QUEUE + const boundaryMarkers = data?.boundary_markers?.length ? data.boundary_markers : SECURITY_TOOL_CLOSURE_BOOTSTRAP_BOUNDARY_MARKERS + const primaryQueue = queue[0] const metrics: IwoooSSecurityToolClosureMetric[] = [ { key: 'closure', - value: summary ? `${summary.tool_closure_percent}%` : '...', + value: `${summary.tool_closure_percent}%`, icon: ShieldCheck, - tone: summary && summary.tool_closure_percent > 0 ? 'warn' : 'locked', + tone: summary.tool_closure_percent >= 80 ? 'steady' : summary.tool_closure_percent > 0 ? 'warn' : 'locked', }, { key: 'tracks', - value: summary ? `${summary.partial_tool_track_count}/${summary.tool_track_count}` : '...', + value: `${summary.closed_tool_track_count}/${summary.tool_track_count}`, icon: SearchCheck, - tone: summary && summary.partial_tool_track_count > 0 ? 'warn' : 'locked', + tone: summary.closed_tool_track_count > 0 ? 'warn' : 'locked', }, { key: 'wazuh', - value: summary ? `${summary.wazuh_manager_registry_accepted_count}/${summary.wazuh_expected_host_scope_count}` : '...', + value: `${summary.wazuh_fim_vulnerability_alert_verifier_ready_signal_count}/${summary.wazuh_expected_host_scope_count}`, icon: Radar, - tone: summary && summary.wazuh_registry_complete_count > 0 ? 'steady' : 'warn', + tone: summary.wazuh_fim_vulnerability_alert_verifier_ready_count > 0 ? 'steady' : 'warn', }, { key: 'alerts', - value: summary ? `${summary.alert_receipt_accepted_count}/${summary.alert_receipt_observed_count}` : '...', + value: `${summary.alert_receipt_accepted_count}/${summary.alert_receipt_observed_count}`, icon: Bell, - tone: summary && summary.alert_receipt_accepted_count > 0 ? 'steady' : 'warn', + tone: summary.alert_receipt_accepted_count > 0 ? 'steady' : 'warn', }, { key: 'runtime', - value: summary ? `${summary.controlled_apply_candidate_ready_count}/${summary.tool_track_count}` : '...', + value: `${summary.controlled_apply_candidate_ready_count}/${summary.tool_track_count}`, icon: Activity, - tone: summary && summary.controlled_apply_policy_active_count > 0 ? 'steady' : 'warn', + tone: summary.controlled_apply_candidate_ready_count > 0 ? 'steady' : 'warn', + }, + ] + const guardrails: Array<{ + key: string + label: string + value: string + tone: 'steady' | 'warn' | 'locked' + }> = [ + { + key: 'runtimeActions', + label: t('guards.runtimeActions'), + value: String(summary.runtime_execution_performed_count), + tone: summary.runtime_execution_performed_count === 0 ? 'locked' : 'warn', + }, + { + key: 'criticalSecrets', + label: t('guards.criticalSecrets'), + value: String(summary.secret_value_collection_allowed_count), + tone: summary.secret_value_collection_allowed_count === 0 ? 'locked' : 'warn', + }, + { + key: 'ownerReview', + label: t('guards.ownerReview'), + value: String(summary.owner_review_required_for_low_medium_high_count), + tone: summary.owner_review_required_for_low_medium_high_count === 0 ? 'steady' : 'warn', }, ] @@ -16352,40 +16621,92 @@ function IwoooSSecurityToolClosureBoard() { display: 'grid', gap: 12, background: '#f7faf8', - borderColor: '#cdded2', + borderColor: '#c9d8d0', }} >
-
+
{t('eyebrow')}
-

+

{t('title')}

-

- {t('subtitle')} -

+
+ {summary.tool_closure_percent}% +
+
+ + {t(`source.${readbackFresh ? 'live' : 'bootstrap'}` as never)} +
-
+
{t('statusLabel')} - +
-
+
{t(`status.${statusKey}` as never)}
-
- {summary ? t('statusDetail', { ready: summary.total_ready_signal_count, required: summary.total_required_signal_count }) : t('statusDetailFallback')} +
+ {[ + { label: t('compact.signals'), value: `${summary.total_ready_signal_count}/${summary.total_required_signal_count}` }, + { label: t('compact.sources'), value: String(summary.source_readback_count) }, + { label: t('compact.p0'), value: `${summary.p0_tool_track_count}` }, + ].map(item => ( +
+
{item.label}
+
{item.value}
+
+ ))} +
+
+ +
+
+ + {t('queueLabel')} +
+
+ {primaryQueue.queue_id} +
+
+ {t(`queue.${primaryQueue.queue_id}.title` as never)} +
+
+ + {primaryQueue.ready_signal_count ?? 0}/6 + + + {t('compact.controlled')} +
@@ -16419,11 +16740,34 @@ function IwoooSSecurityToolClosureBoard() { })}
+
+ {guardrails.map(item => ( +
+ + {item.label} + {item.value} +
+ ))} +
+
@@ -16462,50 +16806,32 @@ function IwoooSSecurityToolClosureBoard() { {t(`states.${track.closure_state}` as never)}
-
- {track.tool_ids.slice(0, 4).map(tool => ( - - {tool} - - ))} -
-
- {t(`tracks.${track.track_id}.next` as never)} +
+ + {track.tool_ids.slice(0, 2).join(' / ')} + + {track.closure_percent}%
) })}
-
-
+ - {t('queueLabel')} -
-
+ {t('detailsTitle')} + +
{queue.map(item => (
))}
-
- -
- - {t('boundaryTitle')} -
- {(data?.boundary_markers ?? []).slice(0, 10).map(marker => ( + {boundaryMarkers.slice(0, 10).map(marker => ( + - diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 3d1cfe086..9d185a0cc 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -801,6 +801,8 @@ export interface IwoooSSecurityToolClosureReadbackResponse { wazuh_manager_registry_accepted_count: number wazuh_registry_complete_count: number wazuh_fim_vulnerability_alert_closed_count: number + wazuh_fim_vulnerability_alert_verifier_ready_count: number + wazuh_fim_vulnerability_alert_verifier_ready_signal_count: number alert_receipt_observed_count: number alert_receipt_accepted_count: number alert_receipt_source_status: string