Merge remote-tracking branch 'origin/main' into codex/security-governance-review-20260710

# Conflicts:
#	apps/web/messages/en.json
#	apps/web/messages/zh-TW.json
#	apps/web/src/app/[locale]/iwooos/page.tsx
#	apps/web/src/lib/api-client.ts
This commit is contained in:
ogt
2026-07-10 18:45:37 +08:00
4 changed files with 480 additions and 71 deletions

View File

@@ -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"
},

View File

@@ -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 收據推進"
},

View File

@@ -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<IwoooSSecurityToolClosureReadbackResponse | null>(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',
}}
>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 260px), 1fr))',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 240px), 1fr))',
gap: 10,
alignItems: 'stretch',
}}
>
<div style={{ minWidth: 0 }}>
<div
style={{
border: '0.5px solid #d6e1d9',
borderRadius: 8,
background: '#fff',
padding: 12,
minWidth: 0,
display: 'grid',
alignContent: 'space-between',
gap: 10,
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, color: '#315f43', fontSize: 11, fontWeight: 800 }}>
<SearchCheck size={16} />
<span>{t('eyebrow')}</span>
</div>
<h2 style={{ margin: '6px 0 0', color: '#101513', fontSize: 20, lineHeight: 1.1, ...textWrap }}>
<h2 style={{ margin: 0, color: '#101513', fontSize: 18, lineHeight: 1.1, ...textWrap }}>
{t('title')}
</h2>
<p style={{ margin: '6px 0 0', color: '#4e5c52', fontSize: 12, lineHeight: 1.45, ...textWrap }}>
{t('subtitle')}
</p>
<div style={{ color: toneColors.warn, fontSize: 46, fontWeight: 950, letterSpacing: 0, lineHeight: 0.95 }}>
{summary.tool_runtime_closure_percent}%
</div>
<div style={{ color: '#526058', fontSize: 10.5, fontWeight: 800, ...textWrap }}>
{t('statusDetail', {
ready: summary.total_ready_signal_count,
required: summary.total_required_signal_count,
})}
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 6, color: '#526058', fontSize: 10.5, fontWeight: 800 }}>
<ToneDot tone={statusTone} />
<span>{t(`source.${readbackFresh ? 'live' : 'bootstrap'}` as never)}</span>
</div>
</div>
<div style={{ border: '0.5px solid #d8e5db', borderRadius: 8, background: '#fff', padding: 11, minWidth: 0 }}>
<div style={{ border: '0.5px solid #d8e5db', borderRadius: 8, background: '#fff', padding: 12, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
<span style={{ color: '#64746a', fontSize: 10, fontWeight: 800 }}>{t('statusLabel')}</span>
<ToneDot tone={statusKey === 'ready' ? 'warn' : 'locked'} />
<ToneDot tone={statusTone} />
</div>
<div style={{ marginTop: 7, color: statusKey === 'ready' ? '#9a5d1d' : '#7b2b2b', fontSize: 15, fontWeight: 800, lineHeight: 1.2, ...textWrap }}>
<div style={{ marginTop: 8, color: statusTone === 'steady' ? '#315f43' : '#9a5d1d', fontSize: 15, fontWeight: 900, lineHeight: 1.2, ...textWrap }}>
{t(`status.${statusKey}` as never)}
</div>
<div style={{ marginTop: 7, color: '#64746a', fontSize: 11, lineHeight: 1.35, ...textWrap }}>
{summary ? t('statusDetail', { ready: summary.total_ready_signal_count, required: summary.total_required_signal_count }) : t('statusDetailFallback')}
<div
style={{
marginTop: 10,
display: 'grid',
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
gap: 7,
}}
>
{[
{ 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 => (
<div key={item.label} style={{ border: '0.5px solid #e3ece6', borderRadius: 8, background: '#f9fcfa', padding: 8, minWidth: 0 }}>
<div style={{ color: '#66706a', fontSize: 9.5, fontWeight: 800, ...textWrap }}>{item.label}</div>
<div style={{ marginTop: 4, color: '#18211b', fontSize: 18, lineHeight: 1, fontWeight: 950 }}>{item.value}</div>
</div>
))}
</div>
</div>
<div style={{ border: '0.5px solid #e2cbb4', borderRadius: 8, background: '#fffaf3', padding: 12, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 7, color: '#9a5d1d', fontSize: 10, fontWeight: 900 }}>
<ListChecks size={14} />
<span>{t('queueLabel')}</span>
</div>
<div style={{ marginTop: 8, color: '#9a5d1d', fontSize: 18, fontWeight: 950, lineHeight: 1 }}>
{primaryQueue.queue_id}
</div>
<div style={{ marginTop: 7, color: '#141413', fontSize: 13, fontWeight: 900, lineHeight: 1.2, ...textWrap }}>
{t(`queue.${primaryQueue.queue_id}.title` as never)}
</div>
<div style={{ marginTop: 8, display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<span style={{ border: '0.5px solid #e2cbb4', borderRadius: 999, background: '#fff', color: '#9a5d1d', padding: '4px 7px', fontSize: 10, fontWeight: 900 }}>
{primaryQueue.ready_signal_count ?? 0}/6
</span>
<span style={{ border: '0.5px solid #d6e1d9', borderRadius: 999, background: '#fff', color: '#315f43', padding: '4px 7px', fontSize: 10, fontWeight: 900 }}>
{t('compact.controlled')}
</span>
</div>
</div>
</div>
@@ -16419,11 +16791,34 @@ function IwoooSSecurityToolClosureBoard() {
})}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
{guardrails.map(item => (
<div
key={item.key}
style={{
border: `0.5px solid ${item.tone === 'steady' ? '#cddfd5' : item.tone === 'warn' ? '#e2cbb4' : '#d9d6ce'}`,
borderRadius: 999,
background: item.tone === 'steady' ? '#f4fbf6' : item.tone === 'warn' ? '#fff8f1' : '#f8f7f2',
color: toneColors[item.tone],
padding: '6px 9px',
display: 'flex',
alignItems: 'center',
gap: 6,
minWidth: 0,
}}
>
<Lock size={13} />
<span style={{ fontSize: 10, fontWeight: 900, ...textWrap }}>{item.label}</span>
<span style={{ fontSize: 12, fontWeight: 950 }}>{item.value}</span>
</div>
))}
</div>
<div
data-testid="iwooos-security-tool-closure-tracks"
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 210px), 1fr))',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 180px), 1fr))',
gap: 8,
}}
>
@@ -16462,50 +16857,32 @@ function IwoooSSecurityToolClosureBoard() {
<span>{t(`states.${track.runtime_closure_state}` as never)}</span>
</div>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
{track.tool_ids.slice(0, 4).map(tool => (
<code
key={tool}
style={{
border: '0.5px solid #dce7df',
borderRadius: 999,
background: '#f6faf7',
color: '#355f43',
padding: '3px 6px',
fontSize: 9.5,
fontWeight: 800,
...textWrap,
}}
>
{tool}
</code>
))}
</div>
<div style={{ color: '#526058', fontSize: 10.5, lineHeight: 1.35, ...textWrap }}>
{t(`tracks.${track.track_id}.next` as never)}
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 7, alignItems: 'center' }}>
<span style={{ color: '#66706a', fontSize: 9.5, fontWeight: 800, ...textWrap }}>
{track.tool_ids.slice(0, 2).join(' / ')}
</span>
<span style={{ color: toneColors[tone], fontSize: 11, fontWeight: 950 }}>{track.closure_percent}%</span>
</div>
</div>
)
})}
</div>
<div
<details
data-testid="iwooos-security-tool-closure-next-queue"
style={{
border: '0.5px solid #d6e1d9',
borderRadius: 8,
background: '#fff',
padding: 10,
display: 'grid',
gap: 8,
padding: '8px 10px',
minWidth: 0,
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 7, color: '#315f43', fontSize: 10, fontWeight: 900 }}>
<summary style={{ cursor: 'pointer', color: '#315f43', fontSize: 10.5, fontWeight: 900 }}>
<ListChecks size={14} />
<span>{t('queueLabel')}</span>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 190px), 1fr))', gap: 8 }}>
<span style={{ marginLeft: 6 }}>{t('detailsTitle')}</span>
</summary>
<div style={{ marginTop: 9, display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 190px), 1fr))', gap: 8 }}>
{queue.map(item => (
<div
key={`${item.queue_id}-${item.track_id}`}
@@ -16530,14 +16907,8 @@ function IwoooSSecurityToolClosureBoard() {
</div>
))}
</div>
</div>
<details style={{ border: '0.5px solid #d6e1d9', borderRadius: 8, background: '#fff', padding: '8px 10px' }}>
<summary style={{ cursor: 'pointer', color: '#64746a', fontSize: 10.5, fontWeight: 900 }}>
{t('boundaryTitle')}
</summary>
<div style={{ marginTop: 8, display: 'flex', flexWrap: 'wrap', gap: 6 }}>
{(data?.boundary_markers ?? []).slice(0, 10).map(marker => (
{boundaryMarkers.slice(0, 10).map(marker => (
<code
key={marker}
style={{
@@ -24510,9 +24881,9 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
</div>
</section>
<IwoooSSecurityToolClosureBoard />
<IwoooSWorkspaceSwitcher active={workspaceView} onChange={setWorkspaceView} />
<IwoooSManagerCockpit />
<IwoooSSecurityToolClosureBoard />
<IwoooSWorkspacePanel active={workspaceView === 'overview'}>
<IwoooSCommandRail />

View File

@@ -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