feat: T1016 dns_config probe + server-side drift detection + Crucible DNS DRIFT badge
This commit is contained in:
@@ -13,6 +13,10 @@ vi.mock('../hooks/useWebSocket', () => ({
|
||||
useWebSocket: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../components/Fleet/AgentRemoteActions', () => ({
|
||||
default: () => <div data-testid="agent-remote-actions-mock" />,
|
||||
}));
|
||||
|
||||
const useWebSocketMock = vi.mocked(useWebSocket);
|
||||
|
||||
function wsValue(overrides: Partial<ReturnType<typeof useWebSocket>> = {}) {
|
||||
|
||||
@@ -226,6 +226,38 @@
|
||||
50% { opacity: 0.45; }
|
||||
}
|
||||
|
||||
/* ── DNS row (T1016) ──────────────────────────────────────────────────────── */
|
||||
.cn-dns-row {
|
||||
font-size: 0.6rem;
|
||||
font-family: var(--font-tech);
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
padding: 2px 0;
|
||||
letter-spacing: 0.04em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.cn-dns-row.dns-drifted { color: var(--neon-amber); }
|
||||
.cn-dns-icon { opacity: 0.4; font-size: 0.55rem; }
|
||||
.dns-drift-flag { color: var(--neon-amber); font-weight: 700; margin-left: 4px; }
|
||||
|
||||
/* ── DNS DRIFT badge ──────────────────────────────────────────────────────── */
|
||||
.cn-dns {
|
||||
font-size: 0.62rem;
|
||||
font-family: var(--font-tech);
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
letter-spacing: 0.04em;
|
||||
cursor: default;
|
||||
}
|
||||
.cn-dns.dns-drift {
|
||||
color: var(--neon-amber);
|
||||
background: rgba(255,176,32,0.14);
|
||||
font-weight: 700;
|
||||
animation: rb-blink 1.2s step-end infinite;
|
||||
}
|
||||
|
||||
/* ── Resource pressure badges ─────────────────────────────────────────────── */
|
||||
.cn-thermal, .cn-disk, .cn-throttle {
|
||||
font-size: 0.62rem;
|
||||
|
||||
@@ -78,6 +78,14 @@ function postureTooltip(agent: Agent): string {
|
||||
if (agent.reboot_pending !== undefined) {
|
||||
lines.push(`Reboot required: ${agent.reboot_pending ? 'YES ⚠' : 'no ✓'}`);
|
||||
}
|
||||
// DNS config
|
||||
if (agent.dns_servers?.length) {
|
||||
lines.push('──────────────────────');
|
||||
lines.push(`DNS (T1016): ${agent.dns_servers.join(', ')}`);
|
||||
if (agent.dns_search_domains?.length) lines.push(`Search: ${agent.dns_search_domains.join(', ')}`);
|
||||
if (agent.dns_drifted) lines.push('⚠ DNS changed since last heartbeat!');
|
||||
}
|
||||
|
||||
// Resource pressure
|
||||
const tempLabel = agent.gpu_temp_c !== undefined ? `GPU ${agent.gpu_temp_c}°C` : agent.cpu_temp_c !== undefined ? `CPU ${agent.cpu_temp_c}°C` : null;
|
||||
if (tempLabel || agent.disk_free_pct !== undefined || agent.cpu_throttle !== undefined) {
|
||||
@@ -146,6 +154,13 @@ function throttleBadge(agent: Agent): { label: string; cls: string } | null {
|
||||
return { label, cls: 'therm-warm' };
|
||||
}
|
||||
|
||||
// ── DNS helpers (T1016) ────────────────────────────────────────────────────
|
||||
|
||||
function dnsBadge(agent: Agent): { label: string; cls: string } | null {
|
||||
if (agent.dns_drifted) return { label: 'DNS DRIFT', cls: 'dns-drift' };
|
||||
return null;
|
||||
}
|
||||
|
||||
// ── Service helpers (T1007) ────────────────────────────────────────────────
|
||||
|
||||
// Human-readable label for well-known service names
|
||||
@@ -575,7 +590,21 @@ export default function CruciblePage() {
|
||||
title={`CPU running at ${a.cpu_freq_mhz ?? '?'} MHz (max ${a.cpu_max_mhz ?? '?'} MHz)`}
|
||||
>{trb.label}</div>
|
||||
); })()}
|
||||
{(() => { const db2 = dnsBadge(a); return db2 && (
|
||||
<div
|
||||
className={`cn-dns ${db2.cls}`}
|
||||
title={`DNS changed since last heartbeat!\nCurrent: ${a.dns_servers?.join(', ') ?? '?'}`}
|
||||
>{db2.label}</div>
|
||||
); })()}
|
||||
</div>
|
||||
{a.dns_servers && a.dns_servers.length > 0 && (
|
||||
<div className={`cn-dns-row${a.dns_drifted ? ' dns-drifted' : ''}`}
|
||||
title={`DNS: ${a.dns_servers.join(', ')}${a.dns_search_domains?.length ? ' Search: ' + a.dns_search_domains.join(', ') : ''}`}>
|
||||
<span className="cn-dns-icon">⬡</span>
|
||||
{a.dns_servers.slice(0, 2).join(' · ')}
|
||||
{a.dns_drifted && <span className="dns-drift-flag"> ⚠ DRIFT</span>}
|
||||
</div>
|
||||
)}
|
||||
{a.services && a.services.length > 0 && (
|
||||
<div className="cn-services">
|
||||
{importantServices(a.services).map(svc => (
|
||||
|
||||
Reference in New Issue
Block a user