From 80fff1efa4b6bf06ac416827493cb68fd0f456c4 Mon Sep 17 00:00:00 2001 From: anti Date: Sun, 10 May 2026 22:27:38 -0400 Subject: [PATCH] fix(web): coerce fingerprint_type to string; sync frontend types and tests --- decnet_web/src/components/AttackerDetail.tsx | 2 +- decnet_web/src/components/Credentials/helpers.test.ts | 4 ++-- decnet_web/src/components/Layout.css | 4 +++- decnet_web/src/components/Login.css | 6 ++++++ .../src/components/MazeNET/useMazeContextMenu.test.ts | 11 ++++++----- .../src/components/PersonaGeneration/helpers.test.ts | 2 +- .../src/components/SwarmHosts/useSwarmHosts.test.ts | 2 +- decnet_web/src/components/SwarmHosts/useSwarmHosts.ts | 8 +++----- 8 files changed, 23 insertions(+), 16 deletions(-) diff --git a/decnet_web/src/components/AttackerDetail.tsx b/decnet_web/src/components/AttackerDetail.tsx index b161e93b..760b7b65 100644 --- a/decnet_web/src/components/AttackerDetail.tsx +++ b/decnet_web/src/components/AttackerDetail.tsx @@ -224,7 +224,7 @@ const AttackerDetail: React.FC = () => { const groups: Record = {}; filteredFps.forEach((fp) => { const p = getPayload(fp); - let fpType: string = p.fingerprint_type || 'unknown'; + let fpType: string = String(p.fingerprint_type || 'unknown'); if (fpType === 'tls_certificate') { fpType = p.target_ip ? 'tls_certificate_active' : 'tls_certificate_passive'; } diff --git a/decnet_web/src/components/Credentials/helpers.test.ts b/decnet_web/src/components/Credentials/helpers.test.ts index bff63887..d48178ba 100644 --- a/decnet_web/src/components/Credentials/helpers.test.ts +++ b/decnet_web/src/components/Credentials/helpers.test.ts @@ -34,7 +34,7 @@ describe('nextSortState', () => { }); const cred = (over: Partial = {}): CredentialEntry => ({ - id: '1', last_seen: '2026-05-01T00:00:00Z', decky_name: 'd1', service: 'ssh', + id: 1, last_seen: '2026-05-01T00:00:00Z', decky_name: 'd1', service: 'ssh', attacker_ip: '1.2.3.4', principal: 'root', secret_sha256: 'a', secret_kind: 'plaintext', secret_printable: 'p', attempt_count: 5, ...over, @@ -42,7 +42,7 @@ const cred = (over: Partial = {}): CredentialEntry => ({ describe('sortCreds', () => { it('returns the input untouched for empty col', () => { - const rows = [cred({ id: 'A' }), cred({ id: 'B' })]; + const rows = [cred({ id: 1 }), cred({ id: 2 })]; expect(sortCreds(rows, '', 'asc')).toBe(rows); }); it('sorts numbers numerically (asc/desc)', () => { diff --git a/decnet_web/src/components/Layout.css b/decnet_web/src/components/Layout.css index c83bd566..3fc22598 100644 --- a/decnet_web/src/components/Layout.css +++ b/decnet_web/src/components/Layout.css @@ -26,7 +26,9 @@ } .sidebar-header { - padding: 20px; + height: 64px; + flex-shrink: 0; + padding: 0 20px; display: flex; align-items: center; justify-content: space-between; diff --git a/decnet_web/src/components/Login.css b/decnet_web/src/components/Login.css index fa946947..1ccf4e94 100644 --- a/decnet_web/src/components/Login.css +++ b/decnet_web/src/components/Login.css @@ -66,6 +66,11 @@ background-color: rgba(0, 0, 0, 0.5); } +html[data-theme="light"] .login-form input { + background-color: rgba(255, 255, 255, 0.7); + color: var(--ink, #1a1a1a); +} + .error-msg { color: #ff4141; font-size: 0.8rem; @@ -80,6 +85,7 @@ margin-top: 8px; font-weight: bold; letter-spacing: 2px; + justify-content: center; } .login-footer { diff --git a/decnet_web/src/components/MazeNET/useMazeContextMenu.test.ts b/decnet_web/src/components/MazeNET/useMazeContextMenu.test.ts index 416051da..b7f2d0a4 100644 --- a/decnet_web/src/components/MazeNET/useMazeContextMenu.test.ts +++ b/decnet_web/src/components/MazeNET/useMazeContextMenu.test.ts @@ -7,9 +7,6 @@ import { useMazeContextMenu } from './useMazeContextMenu'; import type { Net, MazeNode } from './types'; import type { UseTopologyEditor } from './useTopologyEditor'; -const noop = () => {}; -const noopAsync = async () => {}; - const stubEditor = (): UseTopologyEditor => ({ inFlight: 0, addLan: vi.fn(), @@ -43,7 +40,8 @@ const decky: MazeNode = { archetype: 'workstation', services: ['ssh'], status: 'idle', x: 0, y: 0, }; const observed: MazeNode = { - kind: 'observed', id: 'obs-1', label: '1.2.3.4', netId: 'lan-www', + kind: 'observed', id: 'obs-1', netId: 'lan-www', name: '1.2.3.4', + archetype: 'attacker-pool', services: ['*'], status: 'idle', x: 0, y: 0, }; @@ -51,7 +49,10 @@ const baseArgs = () => ({ nets: [subnet, internet], nodes: [decky, observed], services: [ - { slug: 'http', name: 'HTTP', proto: 'tcp', port: 80, icon: 'globe', risk: 'medium' as const }, + { + slug: 'http', name: 'HTTP', proto: 'tcp' as const, port: 80, + icon: 'globe', risk: 'med' as const, group: 'Web' as const, + }, ], archetypes: [ { slug: 'workstation', name: 'Workstation', services: ['ssh'], icon: 'monitor' }, diff --git a/decnet_web/src/components/PersonaGeneration/helpers.test.ts b/decnet_web/src/components/PersonaGeneration/helpers.test.ts index 29db6c7a..3bd8dcb5 100644 --- a/decnet_web/src/components/PersonaGeneration/helpers.test.ts +++ b/decnet_web/src/components/PersonaGeneration/helpers.test.ts @@ -47,7 +47,7 @@ describe('coercePersona', () => { const r = coercePersona({ name: 'a', email: 'a@b.c', role: 'r', tone: 'custom', tone_custom: long, - mannerisms: Array.from({ length: 20 }, (_, i) => `m${i}`).concat([42, null]), + mannerisms: Array.from({ length: 20 }, (_, i) => `m${i}`).concat(['42', ''] as string[]), }); expect('ok' in r).toBe(true); if ('ok' in r) { diff --git a/decnet_web/src/components/SwarmHosts/useSwarmHosts.test.ts b/decnet_web/src/components/SwarmHosts/useSwarmHosts.test.ts index 87aada84..268923ff 100644 --- a/decnet_web/src/components/SwarmHosts/useSwarmHosts.test.ts +++ b/decnet_web/src/components/SwarmHosts/useSwarmHosts.test.ts @@ -100,7 +100,7 @@ describe('useSwarmHosts', () => { }); }); expect(r?.ok).toBe(true); - expect(r?.data?.host_uuid).toBe('h-9'); + expect(r?.ok && r.data?.host_uuid).toBe('h-9'); }); it('polls /swarm/hosts every 10s', async () => { diff --git a/decnet_web/src/components/SwarmHosts/useSwarmHosts.ts b/decnet_web/src/components/SwarmHosts/useSwarmHosts.ts index 2fdf993a..19d16a32 100644 --- a/decnet_web/src/components/SwarmHosts/useSwarmHosts.ts +++ b/decnet_web/src/components/SwarmHosts/useSwarmHosts.ts @@ -3,11 +3,9 @@ import api from '../../utils/api'; import { extractErrorDetail } from './helpers'; import type { BundleRequest, BundleResult, SwarmHost } from './types'; -export interface MutationResult { - ok: boolean; - reason?: string; - data?: T; -} +export type MutationResult = T extends void + ? { ok: true } | { ok: false; reason: string } + : { ok: true; data: T } | { ok: false; reason: string }; export interface UseSwarmHosts { hosts: SwarmHost[];