All files / src/store securityStore.ts

0% Statements 0/67
0% Branches 0/1
0% Functions 0/1
0% Lines 0/67

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142                                                                                                                                                                                                                                                                                           
/**
 * securityStore.ts - Security Status and Audit Log Management
 *
 * Extracted from gatewayStore.ts for Store Refactoring.
 * Manages OpenFang security layers, security status, and audit logs.
 */
import { create } from 'zustand';
import type { GatewayClient } from '../lib/gateway-client';
 
// === Types ===
 
export interface SecurityLayer {
  name: string;
  enabled: boolean;
  description?: string;
}
 
export interface SecurityStatus {
  layers: SecurityLayer[];
  enabledCount: number;
  totalCount: number;
  securityLevel: 'critical' | 'high' | 'medium' | 'low';
}
 
export interface AuditLogEntry {
  id: string;
  timestamp: string;
  action: string;
  actor?: string;
  result?: 'success' | 'failure';
  details?: Record<string, unknown>;
  // Merkle hash chain fields (OpenFang)
  hash?: string;
  previousHash?: string;
}
 
// === Helpers ===
 
function calculateSecurityLevel(enabledCount: number, totalCount: number): 'critical' | 'high' | 'medium' | 'low' {
  if (totalCount === 0) return 'low';
  const ratio = enabledCount / totalCount;
  if (ratio >= 0.875) return 'critical'; // 14-16 layers
  if (ratio >= 0.625) return 'high';     // 10-13 layers
  if (ratio >= 0.375) return 'medium';   // 6-9 layers
  return 'low';                          // 0-5 layers
}
 
// === Client Interface ===
 
interface SecurityClient {
  getSecurityStatus(): Promise<{ layers?: SecurityLayer[] } | null>;
  getAuditLogs(opts?: { limit?: number; offset?: number }): Promise<{ logs?: AuditLogEntry[] } | null>;
}
 
// === Store Interface ===
 
export interface SecurityStateSlice {
  securityStatus: SecurityStatus | null;
  securityStatusLoading: boolean;
  securityStatusError: string | null;
  auditLogs: AuditLogEntry[];
  auditLogsLoading: boolean;
}
 
export interface SecurityActionsSlice {
  loadSecurityStatus: () => Promise<void>;
  loadAuditLogs: (opts?: { limit?: number; offset?: number }) => Promise<void>;
}
 
export type SecurityStore = SecurityStateSlice & SecurityActionsSlice & { client: SecurityClient | null };
 
// === Store Implementation ===
 
export const useSecurityStore = create<SecurityStore>((set, get) => ({
  // Initial state
  securityStatus: null,
  securityStatusLoading: false,
  securityStatusError: null,
  auditLogs: [],
  auditLogsLoading: false,
  client: null,
 
  loadSecurityStatus: async () => {
    const client = get().client;
    if (!client) return;
 
    set({ securityStatusLoading: true, securityStatusError: null });
    try {
      const result = await client.getSecurityStatus();
      if (result?.layers) {
        const layers = result.layers as SecurityLayer[];
        const enabledCount = layers.filter(l => l.enabled).length;
        const totalCount = layers.length;
        const securityLevel = calculateSecurityLevel(enabledCount, totalCount);
        set({
          securityStatus: { layers, enabledCount, totalCount, securityLevel },
          securityStatusLoading: false,
          securityStatusError: null,
        });
      } else {
        set({
          securityStatusLoading: false,
          securityStatusError: 'API returned no data',
        });
      }
    } catch (err: unknown) {
      set({
        securityStatusLoading: false,
        securityStatusError: (err instanceof Error ? err.message : String(err)) || 'Security API not available',
      });
    }
  },
 
  loadAuditLogs: async (opts?: { limit?: number; offset?: number }) => {
    const client = get().client;
    if (!client) return;
 
    set({ auditLogsLoading: true });
    try {
      const result = await client.getAuditLogs(opts);
      set({ auditLogs: (result?.logs || []) as AuditLogEntry[], auditLogsLoading: false });
    } catch {
      set({ auditLogsLoading: false });
      /* ignore if audit API not available */
    }
  },
}));
 
// === Client Injection ===
 
function createSecurityClientFromGateway(client: GatewayClient): SecurityClient {
  return {
    getSecurityStatus: () => client.getSecurityStatus() as Promise<{ layers?: SecurityLayer[] } | null>,
    getAuditLogs: (opts) => client.getAuditLogs(opts) as Promise<{ logs?: AuditLogEntry[] } | null>,
  };
}
 
export function setSecurityStoreClient(client: unknown): void {
  const securityClient = createSecurityClientFromGateway(client as GatewayClient);
  useSecurityStore.setState({ client: securityClient });
}