diff --git a/apps/web/src/app/[locale]/awooop/work-items/page.tsx b/apps/web/src/app/[locale]/awooop/work-items/page.tsx index 4ca4e1382..d9c4e33c8 100644 --- a/apps/web/src/app/[locale]/awooop/work-items/page.tsx +++ b/apps/web/src/app/[locale]/awooop/work-items/page.tsx @@ -21,6 +21,7 @@ import { GitBranch, GitPullRequest, ListChecks, + Lock, Network, Radar, RefreshCw, @@ -1434,6 +1435,39 @@ type PriorityWorkOrderResponse = { } | null; }; +type SecurityCockpitRuntimeReadback = { + status?: string | null; + summary?: { + control_plane_visibility_percent?: number | null; + actual_runtime_acceptance_percent?: number | null; + wazuh_manager_registry_accepted_count?: number | null; + wazuh_expected_host_scope_count?: number | null; + wazuh_owner_evidence_registry_export_accepted_count?: number | null; + wazuh_owner_evidence_reviewer_check_count?: number | null; + wazuh_runtime_apply_preflight_ready_count?: number | null; + wazuh_runtime_apply_check_mode_plan_count?: number | null; + wazuh_runtime_apply_rollback_plan_count?: number | null; + wazuh_runtime_apply_post_apply_verifier_count?: number | null; + runtime_gate_count?: number | null; + host_write_authorized_count?: number | null; + active_response_authorized_count?: number | null; + } | null; +}; + +type SecurityCockpitRegistryReadback = { + status?: string | null; + summary?: { + expected_scope_alias_count?: number | null; + manager_registry_accepted_count?: number | null; + owner_registry_export_accepted_count?: number | null; + reviewer_validation_passed_count?: number | null; + runtime_gate_count?: number | null; + host_write_authorized_count?: number | null; + active_response_authorized_count?: number | null; + secret_value_collection_allowed_count?: number | null; + } | null; +}; + const COMMANDER_INSERTED_REQUIREMENT_FALLBACK: PriorityWorkOrderResponse = { summary: { active_p0_state: "blocked_reboot_auto_recovery_slo_not_ready", @@ -9276,6 +9310,312 @@ function stockplatformManagerStateKey(value: string | null | undefined) { return "review"; } +function SecurityMainlineCockpit({ + priority, + loading, + locale, +}: { + priority: PriorityWorkOrderResponse | null; + loading: boolean; + locale: string; +}) { + const [runtime, setRuntime] = useState(null); + const [registry, setRegistry] = useState(null); + const [liveLoading, setLiveLoading] = useState(true); + const isZh = locale === "zh-TW"; + const copy = { + eyebrow: isZh ? "資安主線" : "Security mainline", + title: isZh ? "管理者作戰盤" : "Operator cockpit", + subtitle: isZh + ? "正式環境讀回:Wazuh manager registry、Windows99 locator、P0 blocker 與高風險動作邊界集中顯示。" + : "Production readback for Wazuh registry, Windows99 locator, P0 blockers, and high-risk guardrails.", + live: isZh ? "正式讀回" : "production", + loading: isZh ? "讀取中" : "loading", + p0: isZh ? "目前 P0" : "Current P0", + registry: isZh ? "Wazuh 納管" : "Wazuh registry", + locator: isZh ? "Windows99 locator" : "Windows99 locator", + guard: isZh ? "高風險邊界" : "Risk boundary", + next: isZh ? "下一步" : "Next action", + blockers: isZh ? "阻擋項" : "blockers", + readiness: isZh ? "準備度" : "readiness", + owner: isZh ? "owner export" : "owner export", + reviewer: isZh ? "review passed" : "review passed", + identity: isZh ? "單一身分候選" : "identity candidate", + confirmation: isZh ? "需確認" : "confirmation", + noRaw: isZh ? "不顯示 raw path" : "no raw path", + noWrite: isZh ? "未做主機寫入" : "no host write", + noPower: isZh ? "未改 VM power" : "no VM power change", + locked: isZh ? "runtime gate 鎖定" : "runtime gate locked", + deployLabel: isZh ? "部署" : "deploy", + locatorLabel: isZh ? "定位器" : "locator", + }; + + useEffect(() => { + let mounted = true; + async function loadSecurityReadback() { + setLiveLoading(true); + const [runtimePayload, registryPayload] = await Promise.all([ + fetchJson( + `${API_BASE}/api/v1/iwooos/runtime-security-readback`, + 12000 + ), + fetchJson( + `${API_BASE}/api/v1/iwooos/wazuh-manager-registry-reviewer-validation`, + 12000 + ), + ]); + if (!mounted) return; + setRuntime(runtimePayload); + setRegistry(registryPayload); + setLiveLoading(false); + } + loadSecurityReadback(); + return () => { + mounted = false; + }; + }, []); + + const summary = priority?.summary; + const activeItem = priority?.in_progress_or_blocked_in_priority_order?.[0] ?? null; + const evidence = activeItem?.evidence; + const runtimeSummary = runtime?.summary; + const registrySummary = registry?.summary; + const activeBlockers = + summary?.active_p0_live_active_blockers ?? + evidence?.active_blockers ?? + []; + const readiness = summary?.active_p0_readiness_percent ?? 0; + const registryAccepted = + registrySummary?.manager_registry_accepted_count ?? + runtimeSummary?.wazuh_manager_registry_accepted_count ?? + 0; + const registryExpected = + registrySummary?.expected_scope_alias_count ?? + runtimeSummary?.wazuh_expected_host_scope_count ?? + 0; + const ownerAccepted = + registrySummary?.owner_registry_export_accepted_count ?? + runtimeSummary?.wazuh_owner_evidence_registry_export_accepted_count ?? + 0; + const reviewerPassed = registrySummary?.reviewer_validation_passed_count ?? 0; + const runtimeGateCount = + registrySummary?.runtime_gate_count ?? + runtimeSummary?.runtime_gate_count ?? + 0; + const hostWriteCount = + registrySummary?.host_write_authorized_count ?? + runtimeSummary?.host_write_authorized_count ?? + 0; + const activeResponseCount = + registrySummary?.active_response_authorized_count ?? + runtimeSummary?.active_response_authorized_count ?? + 0; + const secretValueAllowedCount = + registrySummary?.secret_value_collection_allowed_count ?? + 0; + const locatorStatus = + summary?.windows99_vmx_source_locator_status ?? + evidence?.windows99_vmx_source_locator_status ?? + "waiting_readback"; + const locatorCandidates = + summary?.windows99_vmx_source_locator_candidate_source_count ?? + evidence?.windows99_vmx_source_locator_candidate_source_count ?? + 0; + const locatorIdentityCandidates = + summary?.windows99_vmx_source_locator_identity_unassigned_ubuntu_candidate_count ?? + evidence?.windows99_vmx_source_locator_identity_unassigned_ubuntu_candidate_count ?? + 0; + const locatorConfirmationRequired = + summary?.windows99_vmx_source_locator_candidate_confirmation_required ?? + evidence?.windows99_vmx_source_locator_candidate_confirmation_required ?? + false; + const locatorRawPathOutput = + summary?.windows99_vmx_source_locator_raw_path_output ?? + evidence?.windows99_vmx_source_locator_raw_path_output ?? + false; + const locatorRemoteWrite = + summary?.windows99_vmx_source_locator_remote_write_performed ?? + evidence?.windows99_vmx_source_locator_remote_write_performed ?? + false; + const locatorVmPower = + summary?.windows99_vmx_source_locator_vm_power_change_performed ?? + evidence?.windows99_vmx_source_locator_vm_power_change_performed ?? + false; + const nextAction = + evidence?.windows99_vmx_source_locator_candidate_confirmation_safe_next_step ?? + evidence?.windows99_vmx_source_locator_next_executable_step ?? + summary?.ai_loop_current_blocker_safe_next_action ?? + activeItem?.next_action ?? + summary?.commander_inserted_requirement_next_action ?? + "--"; + const guardClear = + runtimeGateCount === 0 && + hostWriteCount === 0 && + activeResponseCount === 0 && + secretValueAllowedCount === 0 && + !locatorRawPathOutput && + !locatorRemoteWrite && + !locatorVmPower; + const mainSha = + priority?.current_head?.latest_successful_deployed_source_sha ?? + priority?.current_head?.gitea_main_sha ?? + null; + const shortSha = mainSha ? mainSha.slice(0, 10) : "--"; + const cards = [ + { + key: "p0", + label: copy.p0, + value: summary?.active_p0_workplan_id ?? activeItem?.id ?? "P0", + detail: `${copy.readiness} ${readiness}% · ${activeBlockers.length} ${copy.blockers}`, + icon: Radar, + tone: activeBlockers.length > 0 ? "blocked" : "live", + }, + { + key: "registry", + label: copy.registry, + value: `${registryAccepted}/${registryExpected || "?"}`, + detail: `${copy.owner} ${ownerAccepted} · ${copy.reviewer} ${reviewerPassed}`, + icon: Network, + tone: registryExpected > 0 && registryAccepted >= registryExpected ? "live" : "in_progress", + }, + { + key: "locator", + label: copy.locator, + value: `${locatorCandidates}`, + detail: `${copy.identity} ${locatorIdentityCandidates} · ${copy.confirmation} ${locatorConfirmationRequired ? "yes" : "no"}`, + icon: SearchCheck, + tone: locatorCandidates > 0 && locatorConfirmationRequired ? "in_progress" : locatorCandidates > 0 ? "live" : "blocked", + }, + { + key: "guard", + label: copy.guard, + value: guardClear ? "0" : "!", + detail: `${copy.noRaw} · ${copy.noWrite} · ${copy.noPower}`, + icon: Lock, + tone: guardClear ? "live" : "blocked", + }, + ] as const; + + return ( +
+
+
+
+
+
+

+ {copy.title} +

+

+ {copy.subtitle} +

+
+
+
+

{copy.deployLabel}

+

+ {shortSha} +

+
+
+

{copy.locatorLabel}

+

+ {locatorStatus} +

+
+
+
+ +
+
+ {cards.map((card) => { + const Icon = card.icon; + return ( +
+
+

+ {card.label} +

+
+

+ {card.value} +

+

+ {card.detail} +

+
+ ); + })} +
+ +
+
+

+ {copy.next} +

+

+ {nextAction} +

+
+
+ {[ + [copy.noRaw, !locatorRawPathOutput], + [copy.noWrite, hostWriteCount === 0 && !locatorRemoteWrite], + [copy.noPower, !locatorVmPower], + [copy.locked, runtimeGateCount === 0 && activeResponseCount === 0], + ].map(([label, ok]) => ( + + {String(label)} + + ))} +
+
+
+
+
+ ); +} + function ManagerSituationBoard({ priority, loading, @@ -11226,6 +11566,12 @@ export default function AwoooPWorkItemsPage() { active={workspaceView === "overview"} view="overview" > + +