feat(iwooos): expose wazuh managed host coverage readback
Some checks failed
Code Review / ai-code-review (push) Successful in 17s
CD Pipeline / tests (push) Successful in 1m39s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
Some checks failed
Code Review / ai-code-review (push) Successful in 17s
CD Pipeline / tests (push) Successful in 1m39s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
This commit is contained in:
@@ -44,6 +44,7 @@ import {
|
||||
type IwoooSSecurityControlCoverageResponse,
|
||||
type IwoooSWazuhLiveMetadataGateItem,
|
||||
type IwoooSWazuhLiveMetadataGateResponse,
|
||||
type IwoooSWazuhManagedHostCoverageResponse,
|
||||
type IwoooSWazuhOwnerEvidencePreflightItem,
|
||||
type IwoooSWazuhOwnerEvidencePreflightResponse,
|
||||
} from '@/lib/api-client'
|
||||
@@ -2433,13 +2434,6 @@ const wazuhOwnerEvidencePreflightBoundaries = [
|
||||
'not_authorization=true',
|
||||
] as const
|
||||
|
||||
const wazuhManagedHostCoverageSummary = [
|
||||
{ key: 'scope', value: '6', icon: Server, tone: 'warn' },
|
||||
{ key: 'directActive', value: '2', icon: Activity, tone: 'warn' },
|
||||
{ key: 'coverageGap', value: '4', icon: FileWarning, tone: 'locked' },
|
||||
{ key: 'registry', value: '0', icon: Lock, tone: 'locked' },
|
||||
] as const
|
||||
|
||||
const wazuhManagedHostCoverageItems: WazuhManagedHostCoverageItem[] = [
|
||||
{ key: 'registryTruth', check: 'HC-1', state: '接受 0', icon: Lock, tone: 'locked' },
|
||||
{ key: 'coreTransport', check: 'HC-2', state: '2 有連線', icon: Activity, tone: 'warn' },
|
||||
@@ -9481,6 +9475,74 @@ function IwoooSWazuhOwnerEvidencePreflightBoard() {
|
||||
function IwoooSWazuhManagedHostCoverageBoard() {
|
||||
const t = useTranslations('iwooos.wazuhManagedHostCoverage')
|
||||
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
|
||||
const [data, setData] = useState<IwoooSWazuhManagedHostCoverageResponse | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [failed, setFailed] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
|
||||
async function loadCoverage() {
|
||||
setLoading(true)
|
||||
setFailed(false)
|
||||
try {
|
||||
const payload = await apiClient.getIwoooSWazuhManagedHostCoverage()
|
||||
if (mounted) {
|
||||
setData(payload)
|
||||
}
|
||||
} catch {
|
||||
if (mounted) {
|
||||
setData(null)
|
||||
setFailed(true)
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadCoverage()
|
||||
return () => {
|
||||
mounted = false
|
||||
}
|
||||
}, [])
|
||||
|
||||
const summary = data?.summary
|
||||
const summaryItems = [
|
||||
{
|
||||
key: 'scope',
|
||||
value: summary ? String(summary.expected_host_scope_count) : loading ? '...' : '6',
|
||||
icon: Server,
|
||||
tone: 'warn',
|
||||
},
|
||||
{
|
||||
key: 'directActive',
|
||||
value: summary ? String(summary.direct_agent_active_observed_count) : loading ? '...' : '2',
|
||||
icon: Activity,
|
||||
tone: 'warn',
|
||||
},
|
||||
{
|
||||
key: 'coverageGap',
|
||||
value: summary ? String(summary.manager_registry_gap_count) : loading ? '...' : '6',
|
||||
icon: FileWarning,
|
||||
tone: 'locked',
|
||||
},
|
||||
{
|
||||
key: 'registry',
|
||||
value: summary ? String(summary.manager_registry_accepted_count) : loading ? '...' : '0',
|
||||
icon: Lock,
|
||||
tone: 'locked',
|
||||
},
|
||||
] as const
|
||||
const boundaryMarkers = data?.boundary_markers?.length
|
||||
? data.boundary_markers
|
||||
: loading
|
||||
? [t('loadingBoundary')]
|
||||
: wazuhManagedHostCoverageBoundaries
|
||||
const hostMatrix = data?.host_scope_matrix ?? []
|
||||
const statusText = loading ? t('status.loading') : failed ? t('status.failed') : t('status.ready')
|
||||
const statusTone: 'steady' | 'warn' | 'locked' = loading || failed ? 'warn' : 'locked'
|
||||
|
||||
return (
|
||||
<section
|
||||
@@ -9498,10 +9560,14 @@ function IwoooSWazuhManagedHostCoverageBoard() {
|
||||
<p style={{ fontSize: 12, color: '#655638', margin: '6px 0 0', lineHeight: 1.55, ...textWrap }}>
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
<div style={{ marginTop: 8, display: 'inline-flex', alignItems: 'center', gap: 8, color: toneColors[statusTone], fontSize: 12, fontWeight: 700, ...textWrap }}>
|
||||
<ToneDot tone={statusTone} />
|
||||
{statusText}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(126px, 1fr))', gap: 8 }}>
|
||||
{wazuhManagedHostCoverageSummary.map(item => {
|
||||
{summaryItems.map(item => {
|
||||
const Icon = item.icon
|
||||
return (
|
||||
<div key={item.key} style={{ border: '0.5px solid #ded6c5', borderRadius: 8, padding: 12, background: '#fff' }}>
|
||||
@@ -9568,6 +9634,69 @@ function IwoooSWazuhManagedHostCoverageBoard() {
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-testid="iwooos-wazuh-managed-host-coverage-matrix"
|
||||
style={{
|
||||
marginTop: 14,
|
||||
border: '0.5px solid #ded6c5',
|
||||
borderRadius: 8,
|
||||
background: '#fff',
|
||||
padding: 12,
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, flexWrap: 'wrap' }}>
|
||||
<div style={{ color: '#6b5631', fontSize: 12, fontWeight: 700 }}>{t('matrixTitle')}</div>
|
||||
<div style={{ color: '#655638', fontSize: 11, fontWeight: 700, ...textWrap }}>
|
||||
{t('matrixSubtitle')}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 10,
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 230px), 1fr))',
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
{(hostMatrix.length ? hostMatrix : []).map(item => (
|
||||
<div
|
||||
key={item.node_id}
|
||||
style={{
|
||||
border: '0.5px solid #ded6c5',
|
||||
borderRadius: 8,
|
||||
background: item.manager_registry_accepted ? '#f7faf5' : '#fbfaf6',
|
||||
padding: 10,
|
||||
minWidth: 0,
|
||||
...textWrap,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
|
||||
<code style={{ fontSize: 11, color: '#6b5631', fontWeight: 700, overflowWrap: 'anywhere' }}>{item.node_id}</code>
|
||||
<span style={{ color: toneColors.locked, fontSize: 11, fontWeight: 700 }}>
|
||||
{t('matrixAcceptedLabel')}:0
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: '#141413', fontWeight: 700, marginTop: 8 }}>{item.role}</div>
|
||||
<p style={{ fontSize: 11, color: '#655638', lineHeight: 1.45, margin: '6px 0 0', ...textWrap }}>
|
||||
{item.readback_status_label}
|
||||
</p>
|
||||
<p style={{ fontSize: 11, color: '#7a5d2f', lineHeight: 1.45, margin: '6px 0 0', ...textWrap }}>
|
||||
{t('matrixNextGateLabel')}:{item.next_gate_label}
|
||||
</p>
|
||||
<code style={{ display: 'block', marginTop: 6, fontSize: 10.5, color: '#655638', overflowWrap: 'anywhere' }}>
|
||||
{item.next_gate}
|
||||
</code>
|
||||
</div>
|
||||
))}
|
||||
{!hostMatrix.length && (
|
||||
<div style={{ border: '0.5px solid #ded6c5', borderRadius: 8, background: '#fbfaf6', padding: 10, color: '#655638', fontSize: 12 }}>
|
||||
{loading ? t('matrixLoading') : t('matrixFallback')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details
|
||||
data-testid="iwooos-wazuh-managed-host-coverage-boundaries"
|
||||
style={{
|
||||
@@ -9585,7 +9714,7 @@ function IwoooSWazuhManagedHostCoverageBoard() {
|
||||
{t('boundaryIntro')}
|
||||
</p>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))', gap: 6 }}>
|
||||
{wazuhManagedHostCoverageBoundaries.map(item => (
|
||||
{boundaryMarkers.map(item => (
|
||||
<code
|
||||
key={item}
|
||||
style={{
|
||||
|
||||
@@ -268,6 +268,56 @@ export interface IwoooSWazuhOwnerEvidencePreflightResponse {
|
||||
no_false_green_rules: string[]
|
||||
}
|
||||
|
||||
export interface IwoooSWazuhManagedHostCoverageHost {
|
||||
node_id: string
|
||||
role: string
|
||||
readback_status: string
|
||||
readback_status_label: string
|
||||
next_gate: string
|
||||
next_gate_label: string
|
||||
manager_registry_accepted: boolean
|
||||
}
|
||||
|
||||
export interface IwoooSWazuhManagedHostCoverageResponse {
|
||||
schema_version: 'iwooos_wazuh_managed_host_coverage_readback_v1'
|
||||
status: string
|
||||
mode: string
|
||||
source_refs: string[]
|
||||
summary: {
|
||||
expected_host_scope_count: number
|
||||
manager_service_active_observed_count: number
|
||||
manager_api_unauthenticated_response_count: number
|
||||
manager_transport_established_connection_count: number
|
||||
direct_agent_active_observed_count: number
|
||||
direct_agent_transport_observed_count: number
|
||||
direct_agent_missing_or_no_transport_count: number
|
||||
ssh_readback_blocked_count: number
|
||||
manager_registry_accepted_count: number
|
||||
manager_registry_gap_count: number
|
||||
dashboard_api_degraded_observed_count: number
|
||||
live_metadata_env_enabled_count: number
|
||||
active_response_authorized_count: number
|
||||
host_write_authorized_count: number
|
||||
agent_reenroll_authorized_count: number
|
||||
agent_restart_authorized_count: number
|
||||
runtime_gate_count: number
|
||||
host_scope_matrix_count: number
|
||||
required_evidence_before_green_count: number
|
||||
required_evidence_accepted_count: number
|
||||
}
|
||||
host_scope_matrix: IwoooSWazuhManagedHostCoverageHost[]
|
||||
required_evidence_before_green: Array<{
|
||||
evidence_id: string
|
||||
accepted: boolean
|
||||
}>
|
||||
operator_interpretation: string[]
|
||||
forbidden_completion_claims: string[]
|
||||
forbidden_actions: string[]
|
||||
boundary_markers: string[]
|
||||
boundaries: Record<string, boolean>
|
||||
no_false_green_rules: string[]
|
||||
}
|
||||
|
||||
export interface IwoooSSecurityControlCoverageDomain {
|
||||
domain_id:
|
||||
| 'high_value_asset_control'
|
||||
@@ -508,6 +558,11 @@ export const apiClient = {
|
||||
return handleResponse<IwoooSWazuhOwnerEvidencePreflightResponse>(res)
|
||||
},
|
||||
|
||||
async getIwoooSWazuhManagedHostCoverage() {
|
||||
const res = await fetch(`${API_BASE_URL}/iwooos/wazuh-managed-host-coverage`, { cache: 'no-store' })
|
||||
return handleResponse<IwoooSWazuhManagedHostCoverageResponse>(res)
|
||||
},
|
||||
|
||||
async getIwoooSSecurityControlCoverage() {
|
||||
const res = await fetch(`${API_BASE_URL}/iwooos/security-control-coverage`, { cache: 'no-store' })
|
||||
return handleResponse<IwoooSSecurityControlCoverageResponse>(res)
|
||||
|
||||
Reference in New Issue
Block a user