diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index fa14c5a73..13d5dda0e 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 先行" }, @@ -13972,9 +13972,27 @@ "statusDetail": "{ready}/{required} source signals are readable; production closure is calculated separately from same-run execution and verification receipts.", "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": "Source evidence is readable; production closure advances only from runtime receipts" }, diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index ea5d4cc7f..1b4b77654 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 先行" }, @@ -13972,9 +13972,27 @@ "statusDetail": "已讀回 {ready}/{required} 個來源訊號;正式環境閉環另以同一 run 的執行與驗證收據計算。", "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": "來源證據可讀,正式環境閉環仍依 runtime 收據推進" }, diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index 4ac2240de..85d338848 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -527,6 +527,285 @@ 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, + source_ready_tool_track_count: 1, + closed_tool_track_count: 0, + partial_tool_track_count: 1, + missing_tool_track_count: 6, + total_ready_signal_count: 14, + total_required_signal_count: 42, + tool_source_readiness_percent: 33, + tool_runtime_closure_percent: 0, + tool_closure_percent: 0, + wazuh_expected_host_scope_count: 6, + wazuh_manager_registry_accepted_count: 6, + wazuh_registry_complete_count: 1, + wazuh_fim_vulnerability_alert_closed_count: 0, + wazuh_fim_vulnerability_alert_source_verifier_ready_count: 0, + wazuh_fim_vulnerability_alert_runtime_closed_count: 0, + wazuh_fim_vulnerability_alert_verifier_ready_count: 0, + wazuh_fim_vulnerability_alert_verifier_ready_signal_count: 5, + alert_receipt_observed_count: 0, + alert_receipt_accepted_count: 0, + alert_receipt_source_status: 'not_connected', + 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: 1, + 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: 'partial_source_readiness_runtime_open', + closure_percent: 0, + source_readiness_state: 'source_readiness_incomplete', + source_readiness_percent: 83, + runtime_closure_state: 'partial_source_readiness_runtime_open', + runtime_closed: false, + ready_signal_count: 5, + required_signal_count: 6, + missing_signal_count: 1, + 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_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: 'source_missing_runtime_open', + closure_percent: 0, + source_readiness_state: 'source_readiness_incomplete', + source_readiness_percent: 0, + runtime_closure_state: 'source_missing_runtime_open', + runtime_closed: false, + 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: 'source_missing_runtime_open', + closure_percent: 0, + source_readiness_state: 'source_readiness_incomplete', + source_readiness_percent: 0, + runtime_closure_state: 'source_missing_runtime_open', + runtime_closed: false, + 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: 'source_missing_runtime_open', + closure_percent: 0, + source_readiness_state: 'source_readiness_incomplete', + source_readiness_percent: 0, + runtime_closure_state: 'source_missing_runtime_open', + runtime_closed: false, + 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: 'source_missing_runtime_open', + closure_percent: 0, + source_readiness_state: 'source_readiness_incomplete', + source_readiness_percent: 0, + runtime_closure_state: 'source_missing_runtime_open', + runtime_closed: false, + 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: 'source_missing_runtime_open', + closure_percent: 0, + source_readiness_state: 'source_readiness_incomplete', + source_readiness_percent: 0, + runtime_closure_state: 'source_missing_runtime_open', + runtime_closed: false, + 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: 'source_missing_runtime_open', + closure_percent: 0, + source_readiness_state: 'source_readiness_incomplete', + source_readiness_percent: 0, + runtime_closure_state: 'source_missing_runtime_open', + runtime_closed: false, + 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: '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: 'source_ready_runtime_open', + closure_percent: 0, + source_readiness_state: 'source_ready', + source_readiness_percent: 100, + runtime_closure_state: 'source_ready_runtime_open', + runtime_closed: false, + 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_registry_complete_move_to_fim_vulnerability_alert', + next_action: 'attach_fim_vulnerability_alert_receipt_post_verifier', + ready_signal_count: 5, + 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_source_readiness_percent=33', + 'iwooos_security_tool_runtime_closure_percent=0', + 'iwooos_security_tool_closure_percent=0', + 'iwooos_security_tool_closure_wazuh_manager_registry_accepted_count=6', + 'iwooos_security_tool_closure_alert_receipt_accepted_count=0', + 'iwooos_security_tool_closure_controlled_apply_candidate_ready_count=1', + 'owner_review_required_for_low_medium_high=false', + 'runtime_execution_performed=false', + 'secret_value_collection_allowed=false', + 'readback_endpoint_is_executor=false', +] + type IwoooSImmediateVisualMeshNode = { key: string metric: string @@ -16275,8 +16554,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 +16566,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 +16585,72 @@ 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_runtime_closure_percent}%` : '...', + value: `${summary.tool_runtime_closure_percent}%`, icon: ShieldCheck, - tone: summary && summary.tool_runtime_closure_percent > 0 ? 'steady' : 'locked', + tone: summary.tool_runtime_closure_percent >= 100 ? 'steady' : summary.tool_runtime_closure_percent > 0 ? 'warn' : 'locked', }, { key: 'tracks', - value: summary ? `${summary.source_ready_tool_track_count}/${summary.tool_track_count}` : '...', + value: `${summary.source_ready_tool_track_count}/${summary.tool_track_count}`, icon: SearchCheck, - tone: summary && summary.partial_tool_track_count > 0 ? 'warn' : 'locked', + tone: summary.source_ready_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_runtime_closed_count > 0 + ? 'steady' + : summary.wazuh_fim_vulnerability_alert_verifier_ready_signal_count > 0 + ? 'warn' + : 'locked', }, { 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 ? 'warn' : 'locked', + }, + ] + 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 +16666,98 @@ function IwoooSSecurityToolClosureBoard() { display: 'grid', gap: 12, background: '#f7faf8', - borderColor: '#cdded2', + borderColor: '#c9d8d0', }} >
-
+
{t('eyebrow')}
-

+

{t('title')}

-

- {t('subtitle')} -

+
+ {summary.tool_runtime_closure_percent}% +
+
+ {t('statusDetail', { + ready: summary.total_ready_signal_count, + required: summary.total_required_signal_count, + })} +
+
+ + {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 +16791,34 @@ function IwoooSSecurityToolClosureBoard() { })}
+
+ {guardrails.map(item => ( +
+ + {item.label} + {item.value} +
+ ))} +
+
@@ -16462,50 +16857,32 @@ function IwoooSSecurityToolClosureBoard() { {t(`states.${track.runtime_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 5763c6786..7ca3d3e90 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -811,6 +811,8 @@ export interface IwoooSSecurityToolClosureReadbackResponse { wazuh_fim_vulnerability_alert_closed_count: number wazuh_fim_vulnerability_alert_source_verifier_ready_count: number wazuh_fim_vulnerability_alert_runtime_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