Expand test coverage across server, agent, and web; fix bugs found during audit.

Adds hundreds of unit/integration/e2e tests, fixes WS bcrypt auth, config merge, fleet analytics, agent schedule/log tail, and documents stale PROBLEMS items. Updates PROBLEMS.md, README, and test scripts; ignores local spread-kits and coverage dirs.
This commit is contained in:
AetherForge
2026-05-31 01:13:49 -07:00
parent 159747877c
commit ea6f54ad03
89 changed files with 5307 additions and 322 deletions

View File

@@ -18,8 +18,53 @@ interface TermLine {
text: string;
ts: Date;
success?: boolean;
// Structured data for rich terminal renderers
richData?: RichTermData;
}
// ── Rich terminal data types ────────────────────────────────────────────────
interface RichListenPort {
port: number;
addr: string;
proto: string;
process?: string;
pid?: number;
}
interface RichListenPorts {
type: 'listen_ports';
ports: RichListenPort[];
count: number;
}
interface RichPatchStatus {
type: 'patch_status';
pending_updates?: number;
last_patch?: string;
last_patch_days?: number;
reboot_pending?: boolean;
}
interface RichPostureSummary {
type: 'posture';
posture_score?: number;
defender_enabled?: boolean;
defender_rtp?: boolean;
av_products?: string[];
firewall_domain?: boolean;
firewall_private?: boolean;
firewall_public?: boolean;
ssh_listening?: boolean;
agent_elevated?: boolean;
last_patch_days?: number;
pending_updates?: number;
reboot_pending?: boolean;
services?: Array<{ name: string; display_name?: string; status: string; start_type: string }>;
}
type RichTermData = RichListenPorts | RichPatchStatus | RichPostureSummary;
interface NodeGroup {
id: string;
name: string;
@@ -34,35 +79,35 @@ const AGENT_COLORS = [
'#7209b7', '#3a86ff', '#06d6a0', '#ffd60a',
];
function agentColor(agentId: string, allIds: string[]): string {
export function agentColor(agentId: string, allIds: string[]): string {
const idx = allIds.indexOf(agentId);
return AGENT_COLORS[idx % AGENT_COLORS.length] ?? '#00f5ff';
}
function sshBadge(agent: Agent) {
export function sshBadge(agent: Agent) {
if (agent.ssh_available === true) return { label: 'SSH ON', cls: 'ssh-on' };
if (agent.ssh_available === false) return { label: 'SSH OFF', cls: 'ssh-off' };
return { label: 'SSH ?', cls: 'ssh-unk' };
}
function postureBadge(score?: number) {
export function postureBadge(score?: number) {
if (score === undefined) return { label: 'POSTURE ?', cls: 'posture-unk' };
if (score >= 80) return { label: `POSTURE ${score}`, cls: 'posture-good' };
if (score >= 40) return { label: `POSTURE ${score}`, cls: 'posture-warn' };
return { label: `POSTURE ${score}`, cls: 'posture-bad' };
}
function patchLabel(days?: number) {
export function patchLabel(days?: number) {
if (days === undefined) return null;
return { label: `PATCH ${days}d`, cls: days <= 30 ? 'patch-ok' : 'patch-stale' };
}
function portsBadge(count?: number): { label: string; cls: string } | null {
export function portsBadge(count?: number): { label: string; cls: string } | null {
if (count === undefined) return null;
return { label: `PORTS ${count}`, cls: count > 20 ? 'ports-many' : 'ports-ok' };
}
function postureTooltip(agent: Agent): string {
export function postureTooltip(agent: Agent): string {
const lines: string[] = [];
const yn = (v?: boolean) => v === true ? '✓' : v === false ? '✗' : '?';
const na = (v: unknown) => v !== undefined && v !== null ? String(v) : '?';
@@ -118,7 +163,7 @@ function postureTooltip(agent: Agent): string {
return lines.join('\n');
}
function pendingBadge(agent: Agent): { label: string; cls: string } | null {
export function pendingBadge(agent: Agent): { label: string; cls: string } | null {
const u = agent.pending_updates;
if (u === undefined) return null;
if (u < 0) return { label: 'UPD ?', cls: 'upd-unk' };
@@ -135,7 +180,7 @@ function rebootBadge(agent: Agent): { label: string; cls: string } | null {
// ── Resource pressure badges ───────────────────────────────────────────────
function thermalBadge(agent: Agent): { label: string; cls: string } | null {
export function thermalBadge(agent: Agent): { label: string; cls: string } | null {
const t = agent.gpu_temp_c ?? agent.cpu_temp_c;
if (t === undefined) return null;
if (t > 80) return { label: `${t}°`, cls: 'therm-hot' };
@@ -289,44 +334,68 @@ export default function CruciblePage() {
if (!aid) continue;
const msg = r.message ?? '';
// Always update badges from probe / heartbeat command responses
// ── SSH badge updates ───────────────────────────────────────────────
if (msg.includes('SSH_PROBE:ONLINE')) {
setSshOverride((prev) => ({ ...prev, [aid]: true }));
} else if (msg.includes('SSH_PROBE:OFFLINE')) {
setSshOverride((prev) => ({ ...prev, [aid]: false }));
}
if (r.action === 'posture' || msg.includes('{')) {
// ── Parse structured JSON for known actions ────────────────────────
let richData: RichTermData | undefined;
const jsonStart = msg.indexOf('{');
if (jsonStart >= 0) {
try {
const start = msg.indexOf('{');
if (start >= 0) {
const p = JSON.parse(msg.slice(start)) as { posture_score?: number; last_patch_days?: number; ssh_listening?: boolean };
if (typeof p.posture_score === 'number') {
const parsed = JSON.parse(msg.slice(jsonStart));
if (r.action === 'listen_ports' && Array.isArray(parsed.ports)) {
richData = { type: 'listen_ports', ports: parsed.ports, count: parsed.count ?? parsed.ports.length };
} else if (r.action === 'patch_status') {
richData = { type: 'patch_status', ...parsed };
} else if (r.action === 'posture' && typeof parsed.posture_score === 'number') {
richData = { type: 'posture', ...parsed };
// Update badge state
setPostureOverride((prev) => ({
...prev,
[aid]: { score: parsed.posture_score, patchDays: parsed.last_patch_days },
}));
if (parsed.ssh_listening === true) setSshOverride((prev) => ({ ...prev, [aid]: true }));
if (parsed.ssh_listening === false) setSshOverride((prev) => ({ ...prev, [aid]: false }));
} else {
// Generic JSON with posture fields (legacy path)
if (typeof parsed.posture_score === 'number') {
setPostureOverride((prev) => ({
...prev,
[aid]: { score: p.posture_score!, patchDays: p.last_patch_days },
[aid]: { score: parsed.posture_score, patchDays: parsed.last_patch_days },
}));
}
if (p.ssh_listening === true) setSshOverride((prev) => ({ ...prev, [aid]: true }));
if (p.ssh_listening === false) setSshOverride((prev) => ({ ...prev, [aid]: false }));
if (parsed.ssh_listening === true) setSshOverride((prev) => ({ ...prev, [aid]: true }));
if (parsed.ssh_listening === false) setSshOverride((prev) => ({ ...prev, [aid]: false }));
}
} catch { /* ignore malformed JSON */ }
} catch { /* malformed JSON — fall through to plain text */ }
}
if (selectedIds.size > 0 && !selectedIds.has(aid)) continue;
const agent = agents.find((a) => a.id === aid);
const name = agent?.name ?? aid.slice(0, 8);
const msgLines = msg.split('\n').filter(Boolean);
for (const line of msgLines) {
if (richData) {
// Single rich-rendered line (table/block replaces raw JSON)
lines.push({
id: mkId(),
agentId: aid,
agentName: name,
isCmd: false,
text: line,
ts: new Date(),
success: r.success,
id: mkId(), agentId: aid, agentName: name,
isCmd: false, text: '', ts: new Date(),
success: r.success, richData,
});
} else {
const msgLines = msg.split('\n').filter(Boolean);
for (const line of msgLines) {
lines.push({
id: mkId(), agentId: aid, agentName: name,
isCmd: false, text: line, ts: new Date(), success: r.success,
});
}
}
}
if (lines.length > 0) {