Files
awoooi/apps/web/src/lib/security-asset-control-plane.ts

206 lines
6.4 KiB
TypeScript

export type SecurityControlStatus = "ready" | "degraded";
export type SecurityControlPlaneSummary = {
managed_asset_count: number;
expected_asset_type_count: number;
present_asset_type_count: number;
asset_type_coverage_percent: number;
scope_count: number;
controlled_scope_count: number;
unowned_asset_count: number;
stale_asset_count: number;
orphan_asset_count: number;
relationship_count: number;
automation_coverage_green_percent: number;
automation_coverage_unknown_count: number;
compliance_violation_count: number;
compliance_unknown_count: number;
nist_controlled_percent: number;
security_program_domain_count: number;
security_program_controlled_domain_count: number;
security_program_controlled_percent: number;
siem_stage_count: number;
siem_observed_stage_count: number;
siem_stage_coverage_percent: number;
ai_loop_stage_count: number;
ai_loop_observed_stage_count: number;
ai_loop_stage_coverage_percent: number;
verified_remediation_receipt_count_24h: number;
active_work_item_count: number;
};
export type SecurityControlPlaneFunction = {
function_id: string;
label: string;
controlled_percent: number;
status: string;
};
export type SecurityControlPlaneStage = {
stage_id: string;
label: string;
status: "observed" | "missing";
evidence_count_24h?: number;
receipt_count_24h?: number;
};
export type SecurityControlPlaneScope = {
scope_id: string;
label: string;
managed_asset_count: number;
expected_type_count: number;
present_type_count: number;
missing_type_count: number;
type_coverage_percent: number;
status: string;
};
export type SecurityControlPlaneWorkItem = {
work_item_id: string;
priority: string;
control_id: string;
title: string;
gap_count: number;
status: string;
next_action: string;
};
export type SecurityAssetControlPlanePayload = {
schema_version: "iwooos_security_asset_control_plane_v1";
generated_at: string;
status: SecurityControlStatus;
source_status: string;
reason_code?: string;
summary: SecurityControlPlaneSummary;
discovery: {
latest_status: string;
latest_success_at: string | null;
age_seconds: number | null;
freshness_slo_seconds: number;
fresh: boolean;
};
asset_scopes: SecurityControlPlaneScope[];
nist_csf_functions: SecurityControlPlaneFunction[];
security_program_domains: Array<{
domain_id: string;
label: string;
controlled_percent: number;
status: string;
evidence_count: number;
gap_code: string | null;
}>;
siem: {
pipeline: SecurityControlPlaneStage[];
incident_total_24h?: number;
open_incident_count_24h?: number;
resolved_incident_count_24h?: number;
noisy_rule_count?: number;
mean_time_to_resolve_minutes_24h?: number | null;
};
ai_automation: {
stages: SecurityControlPlaneStage[];
aggregate_stage_coverage_percent: number;
accepted_ai_trace_count: number;
verified_remediation_receipt_count: number;
rollback_receipt_count: number;
failed_operation_count: number;
same_run_closed_loop_proven: boolean;
same_run_closed_loop_count: number;
};
security_audit: {
k8s_execution_audit_count_24h: number;
k8s_execution_failure_count_24h: number;
mcp_tool_audit_count_24h: number;
mcp_tool_failure_count_24h: number;
asset_change_event_count_24h: number;
automation_operation_count_24h: number;
automation_failure_count_24h: number;
ai_trace_count_24h: number;
accepted_ai_trace_count_24h: number;
immutable_external_audit_store_evidenced: boolean;
};
work_items: SecurityControlPlaneWorkItem[];
boundaries: {
aggregate_only: boolean;
raw_asset_identity_returned: boolean;
raw_event_payload_returned: boolean;
secret_value_collection_allowed: boolean;
live_scan_triggered: boolean;
runtime_action_triggered: boolean;
completion_requires_same_run_receipts: boolean;
};
};
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
export function isSecurityAssetControlPlanePayload(
value: unknown
): value is SecurityAssetControlPlanePayload {
if (!isRecord(value)) return false;
if (value.schema_version !== "iwooos_security_asset_control_plane_v1") return false;
if (!isRecord(value.summary) || !isRecord(value.boundaries)) return false;
if (
!Array.isArray(value.asset_scopes) ||
!Array.isArray(value.nist_csf_functions) ||
!Array.isArray(value.security_program_domains)
) {
return false;
}
if (!isRecord(value.siem) || !Array.isArray(value.siem.pipeline)) return false;
if (!isRecord(value.ai_automation) || !Array.isArray(value.ai_automation.stages)) {
return false;
}
return (
typeof value.summary.managed_asset_count === "number" &&
typeof value.summary.nist_controlled_percent === "number" &&
value.boundaries.aggregate_only === true &&
value.boundaries.raw_asset_identity_returned === false &&
value.boundaries.raw_event_payload_returned === false &&
value.boundaries.secret_value_collection_allowed === false &&
value.boundaries.live_scan_triggered === false &&
value.boundaries.runtime_action_triggered === false
);
}
export type SecurityControlPlaneTone = "healthy" | "warning" | "critical";
export function securityControlPlaneTone(
payload: SecurityAssetControlPlanePayload
): SecurityControlPlaneTone {
if (payload.status !== "ready" || !payload.discovery.fresh) return "critical";
if (
payload.summary.compliance_violation_count > 0 ||
payload.summary.nist_controlled_percent < 50 ||
payload.summary.siem_stage_coverage_percent < 50 ||
payload.summary.ai_loop_stage_coverage_percent < 50
) {
return "critical";
}
if (
payload.summary.unowned_asset_count > 0 ||
payload.summary.orphan_asset_count > 0 ||
payload.summary.automation_coverage_unknown_count > 0 ||
payload.summary.compliance_unknown_count > 0 ||
!payload.ai_automation.same_run_closed_loop_proven
) {
return "warning";
}
return "healthy";
}
export function topSecurityControlPlaneWorkItems(
payload: SecurityAssetControlPlanePayload,
limit = 3
): SecurityControlPlaneWorkItem[] {
const rank: Record<string, number> = { P0: 0, P1: 1, P2: 2, P3: 3 };
return [...payload.work_items]
.sort(
(left, right) =>
(rank[left.priority] ?? 99) - (rank[right.priority] ?? 99) ||
left.work_item_id.localeCompare(right.work_item_id)
)
.slice(0, Math.max(0, limit));
}