feat: T1007 System Service Discovery - fixed allowlist probe in posture heartbeat

This commit is contained in:
AetherForge
2026-05-30 23:16:50 -07:00
parent 4207f6c21b
commit d010292333
26 changed files with 2499 additions and 134 deletions

View File

@@ -1,7 +1,7 @@
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
import { useWebSocket } from '../hooks/useWebSocket';
import { api } from '../api/client';
import type { Agent } from '../types';
import type { Agent, AgentService } from '../types';
import NeonCard from '../components/NeonCard/NeonCard';
import { formatHashrate } from '../help/fleetFilters';
import './CruciblePage.css';
@@ -78,6 +78,15 @@ function postureTooltip(agent: Agent): string {
if (agent.reboot_pending !== undefined) {
lines.push(`Reboot required: ${agent.reboot_pending ? 'YES ⚠' : 'no ✓'}`);
}
if (agent.services?.length) {
lines.push('──────────────────────');
lines.push('Services (T1007):');
for (const svc of agent.services) {
const icon = svc.status === 'running' ? '●' : svc.status === 'stopped' ? '○' : '—';
const st = svc.start_type !== 'unknown' ? ` [${svc.start_type}]` : '';
lines.push(` ${icon} ${svc.display_name ?? svc.name}${st}`);
}
}
return lines.join('\n');
}
@@ -96,6 +105,42 @@ function rebootBadge(agent: Agent): { label: string; cls: string } | null {
return null; // no badge when not pending — cleaner UI
}
// ── Service helpers (T1007) ────────────────────────────────────────────────
// Human-readable label for well-known service names
const SVC_LABELS: Record<string, string> = {
sshd: 'SSH',
ssh: 'SSH',
'openssh ssh server': 'SSH',
cloudflared: 'CF Tunnel',
wuauserv: 'WU',
windefend: 'Defender',
ufw: 'UFW',
fail2ban: 'Fail2Ban',
};
function svcLabel(svc: AgentService): string {
return SVC_LABELS[svc.name.toLowerCase()] ?? svc.display_name ?? svc.name;
}
function svcDot(status: string): string {
if (status === 'running') return '●';
if (status === 'stopped') return '○';
return '—';
}
function svcDotClass(status: string): string {
if (status === 'running') return 'svc-run';
if (status === 'stopped') return 'svc-stop';
return 'svc-unk';
}
// Only surface services that are interesting to show (skip self-service clutter)
const IMPORTANT_SVCS = new Set(['sshd', 'ssh', 'openssh ssh server', 'cloudflared', 'wuauserv', 'windefend']);
function importantServices(svcs: AgentService[]): AgentService[] {
return svcs.filter(s => IMPORTANT_SVCS.has(s.name.toLowerCase()) || s.status === 'running');
}
function platformIcon(platform?: string): string {
if (!platform) return '⬡';
const p = platform.toLowerCase();
@@ -472,6 +517,20 @@ export default function CruciblePage() {
<div className="cn-elevated" title="Running as Administrator / root">ADMIN</div>
)}
</div>
{a.services && a.services.length > 0 && (
<div className="cn-services">
{importantServices(a.services).map(svc => (
<span
key={svc.name}
className={`cn-svc ${svcDotClass(svc.status)}`}
title={`${svc.display_name ?? svc.name} status: ${svc.status} start: ${svc.start_type}`}
>
<span className="svc-dot">{svcDot(svc.status)}</span>
{svcLabel(svc)}
</span>
))}
</div>
)}
</div>
</div>
);