/** * Static description of the Shannon scan pipeline, plus the worker types the CLI * reads back from Temporal. * * The CLI cannot import from the worker package, so this mirrors it. Keep in sync with: * - apps/worker/src/types/agents.ts (agent names / ordering) * - apps/worker/src/session-manager.ts (phase membership) * - apps/worker/src/temporal/activities.ts (the run*Agent activity names → `activityType`) * - apps/worker/src/temporal/shared.ts (PipelineState / PipelineSummary) * - apps/worker/src/types/metrics.ts (AgentMetrics) */ export interface AgentSpec { /** Canonical agent name as it appears in PipelineState.completedAgents / agentMetrics. */ readonly name: string; /** Short label for the progress tree. */ readonly label: string; /** Temporal activity type name — how a running agent shows up in pendingActivities. */ readonly activityType: string; } export interface PhaseSpec { readonly key: string; readonly label: string; readonly parallel: boolean; readonly agents: readonly AgentSpec[]; } /** The pipeline phases in execution order, each with its agents. */ export const PIPELINE: readonly PhaseSpec[] = [ { // Preflight login check. Only authenticated scans record metrics here; a non-auth scan // records none, so it renders as skipped — like Exploitation when nothing is exploitable. key: 'auth-validation', label: 'Authentication', parallel: false, agents: [{ name: 'validate-authentication', label: 'auth', activityType: 'runAuthenticationValidation' }], }, { key: 'pre-recon', label: 'Pre-Recon', parallel: false, agents: [{ name: 'pre-recon', label: 'pre-recon', activityType: 'runPreReconAgent' }], }, { key: 'recon', label: 'Recon', parallel: false, agents: [{ name: 'recon', label: 'recon', activityType: 'runReconAgent' }], }, { key: 'vulnerability-analysis', label: 'Vulnerability Analysis', parallel: true, agents: [ { name: 'injection-vuln', label: 'injection', activityType: 'runInjectionVulnAgent' }, { name: 'xss-vuln', label: 'xss', activityType: 'runXssVulnAgent' }, { name: 'auth-vuln', label: 'auth', activityType: 'runAuthVulnAgent' }, { name: 'ssrf-vuln', label: 'ssrf', activityType: 'runSsrfVulnAgent' }, { name: 'authz-vuln', label: 'authz', activityType: 'runAuthzVulnAgent' }, ], }, { key: 'exploitation', label: 'Exploitation', parallel: true, agents: [ { name: 'injection-exploit', label: 'injection', activityType: 'runInjectionExploitAgent' }, { name: 'xss-exploit', label: 'xss', activityType: 'runXssExploitAgent' }, { name: 'auth-exploit', label: 'auth', activityType: 'runAuthExploitAgent' }, { name: 'ssrf-exploit', label: 'ssrf', activityType: 'runSsrfExploitAgent' }, { name: 'authz-exploit', label: 'authz', activityType: 'runAuthzExploitAgent' }, ], }, { key: 'reporting', label: 'Reporting', parallel: false, agents: [{ name: 'report', label: 'report', activityType: 'runReportAgent' }], }, ]; /** Temporal activity type name → canonical agent name, for mapping pendingActivities. */ export const ACTIVITY_TO_AGENT: Readonly> = Object.fromEntries( PIPELINE.flatMap((phase) => phase.agents.map((agent) => [agent.activityType, agent.name])), ); /** The vuln/exploit class of an agent (e.g. "authz-vuln" → "authz"), for failedPipelines matching. */ export function agentClass(name: string): string { return name.replace(/-(vuln|exploit)$/, ''); } // === Worker types read back from Temporal (mirror of shared.ts / metrics.ts) === export interface AgentMetrics { readonly durationMs: number; readonly costUsd: number | null; readonly numTurns: number | null; readonly model?: string; readonly skipped?: boolean; } export interface PipelineSummary { readonly totalCostUsd: number; readonly totalDurationMs: number; // Wall-clock (end - start) readonly totalTurns: number; readonly agentCount: number; } export type PipelineStatus = 'running' | 'completed' | 'failed' | 'cancelled' | 'partial'; export interface PipelineState { readonly status: PipelineStatus; readonly currentPhase: string | null; readonly currentAgent: string | null; readonly completedAgents: string[]; readonly failedPipelines: { vulnType: string; error: string }[]; readonly failedAgent: string | null; readonly error: string | null; readonly startTime: number; readonly agentMetrics: Record; readonly summary: PipelineSummary | null; }