feat(iwooos): flag empty Wazuh agent registry

This commit is contained in:
ogt
2026-06-25 09:55:19 +08:00
parent d4f3953847
commit 8698f8311e
8 changed files with 241 additions and 14 deletions

View File

@@ -8,6 +8,9 @@ const READONLY_ENABLED = process.env.IWOOOS_WAZUH_READONLY_ENABLED === 'true';
const WAZUH_API_BASE_URL = process.env.WAZUH_API_BASE_URL?.trim() ?? '';
const WAZUH_API_USERNAME = process.env.WAZUH_API_USERNAME?.trim() ?? '';
const WAZUH_API_PASSWORD = process.env.WAZUH_API_PASSWORD?.trim() ?? '';
const EXPECTED_MIN_AGENT_COUNT = parseExpectedMinAgentCount(
process.env.IWOOOS_WAZUH_EXPECTED_MIN_AGENT_COUNT?.trim() ?? '',
);
const REQUEST_TIMEOUT_MS = 5000;
type WazuhConnectionSummary = {
@@ -48,6 +51,10 @@ function boundaryResponse(status: string, httpStatus = 200) {
active_response_authorized_count: 0,
host_write_authorized_count: 0,
runtime_gate_count: 0,
expected_min_agent_count: EXPECTED_MIN_AGENT_COUNT,
agent_registry_empty_count: 0,
agent_below_expected_minimum_count: 0,
agent_visibility_no_false_green_count: 1,
},
boundaries: {
active_response_authorized: false,
@@ -86,6 +93,25 @@ function requireHttpsBaseUrl(value: string): URL | null {
}
}
function parseExpectedMinAgentCount(value: string) {
const parsed = Number.parseInt(value, 10);
return Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
}
function numberOrDefault(value: unknown, fallback: number) {
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
}
function agentVisibilityStatus(agentTotal: number) {
if (agentTotal <= 0) {
return 'wazuh_agent_registry_empty';
}
if (EXPECTED_MIN_AGENT_COUNT > 0 && agentTotal < EXPECTED_MIN_AGENT_COUNT) {
return 'wazuh_agent_registry_below_expected';
}
return 'readonly_metadata_available';
}
function redactedAgent(agent: WazuhAgent, index: number) {
return {
alias: `agent-${String(index + 1).padStart(2, '0')}`,
@@ -141,19 +167,29 @@ export async function GET() {
const affectedItems = agents.data?.affected_items ?? [];
const connection = status.data?.connection ?? {};
const agentTotal = numberOrDefault(connection.total, affectedItems.length);
const agentActive = numberOrDefault(connection.active, 0);
const agentDisconnected = numberOrDefault(connection.disconnected, 0);
const agentPending = numberOrDefault(connection.pending, 0);
const agentRegistryEmpty = agentTotal <= 0;
const agentBelowExpected = EXPECTED_MIN_AGENT_COUNT > 0 && agentTotal < EXPECTED_MIN_AGENT_COUNT;
return NextResponse.json({
schema_version: 'iwooos_wazuh_readonly_status_v1',
status: 'readonly_metadata_available',
status: agentVisibilityStatus(agentTotal),
mode: 'metadata_only_no_active_response_no_raw_payload',
configured: true,
summary: {
wazuh_platform_reported_count: 1,
readonly_api_enabled_count: 1,
agent_total: connection.total ?? affectedItems.length,
agent_active: connection.active ?? 0,
agent_disconnected: connection.disconnected ?? 0,
agent_pending: connection.pending ?? 0,
agent_total: agentTotal,
agent_active: agentActive,
agent_disconnected: agentDisconnected,
agent_pending: agentPending,
expected_min_agent_count: EXPECTED_MIN_AGENT_COUNT,
agent_registry_empty_count: agentRegistryEmpty ? 1 : 0,
agent_below_expected_minimum_count: agentBelowExpected ? 1 : 0,
agent_visibility_no_false_green_count: 1,
wazuh_manager_query_accepted_count: 0,
wazuh_event_accepted_count: 0,
host_forensics_accepted_count: 0,