fix(web): coerce fingerprint_type to string; sync frontend types and tests

This commit is contained in:
2026-05-10 22:27:38 -04:00
parent a009746dd1
commit 80fff1efa4
8 changed files with 23 additions and 16 deletions

View File

@@ -224,7 +224,7 @@ const AttackerDetail: React.FC = () => {
const groups: Record<string, any[]> = {};
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';
}

View File

@@ -34,7 +34,7 @@ describe('nextSortState', () => {
});
const cred = (over: Partial<CredentialEntry> = {}): 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> = {}): 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)', () => {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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' },

View File

@@ -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) {

View File

@@ -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 () => {

View File

@@ -3,11 +3,9 @@ import api from '../../utils/api';
import { extractErrorDetail } from './helpers';
import type { BundleRequest, BundleResult, SwarmHost } from './types';
export interface MutationResult<T = void> {
ok: boolean;
reason?: string;
data?: T;
}
export type MutationResult<T = void> = T extends void
? { ok: true } | { ok: false; reason: string }
: { ok: true; data: T } | { ok: false; reason: string };
export interface UseSwarmHosts {
hosts: SwarmHost[];