mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-10-03 23:06:51 +02:00
* refactor(cli): list workspaces natively instead of via the worker image * feat(cli): preflight that Docker is installed and running * feat(cli): stop scans by workspace or --all, terminating their Temporal workflows * fix(worker): abort the running agent on cancellation so Temporal cancel takes effect * refactor(cli): split destructive teardown out of stop into a reset command * refactor(cli): centralise flag parsing and confirmation across commands * fix(cli): pass provider credentials to docker by name to keep secrets out of argv * feat(cli): add per-command help via <command> --help/-h and help <command> * feat(cli): replace raw docker output with clack spinners for infra and scan teardown * fix(cli): verify scan stop by re-querying container and workflow state instead of assuming success * fix(cli): resolve running state before prompting on stop and report no-op stops honestly * refactor(cli): show splash first and drive start with one spinner resolving to a clean line * fix(cli): validate --url up front so a bad value fails cleanly instead of a late crash * refactor(cli): centralize error reporting with fail() for expected errors and a crash handler that logs the stack and links the issue tracker * feat(cli): add --json/--plain machine-readable output to workspaces and status * refactor(cli): remove the workspaces command * refactor(cli): remove the status command * feat(cli): add 'progress <workspace>' — live scan progress from Temporal * fix(cli): mark metric-less agents as skipped in progress, not done * feat(cli): animate running agents in progress with a clack-style spinner * feat(cli): rename progress->status, reveal agents as they run, show live per-agent elapsed * fix(cli): mark passed-over phases as skipped live, not pending * style(cli): rename status footer 'Wall-clock' to 'Time Taken', drop the parenthetical * style(cli): drop '(sum of agents)' from status total cost line * style(cli): green filled circle for completed, Shannon gold for running * style(cli): use Shannon gold in place of green in status * feat(cli): suggest closest command or flag on typo * refactor(cli): single-source start help and drop ./repos bare-name shortcut * feat(cli): name providers and fix in multi-provider credential error * feat(cli): support --flag=value syntax and expand leading ~ in paths * refactor(cli): centralize ANSI color codes in colors.ts * feat(cli): add scans command listing completed scans with cost and duration * fix(cli): keep stdout clean off-TTY for logs and start * feat(cli): add repo link to top-level help * feat(worker): record auth-validation metrics and register resume attempts early * refactor(cli): share resume-aware workflow-id resolution and surface root-cause failures * feat(cli): add status --json, auth phase, dashboard link, and stable live redraw * refactor(cli): drop cost from status and scans output * feat(worker): surface both PDF and markdown report at run root * refactor(cli): normalize error/warning prefixing through fail and warn * feat(cli): add version --json for machine-readable output * refactor(cli): rename start --debug to --keep-container * refactor(cli): point start's progress hint at status instead of the Temporal dashboard * refactor(cli): centralize the mode-aware command prefix * refactor(cli): trim start and logs output to durable facts off-TTY * feat(cli): require typed confirmation for reset instead of --yes reset permanently wipes all Temporal data and volumes — a severe, irreversible action. Replace its default y/N confirm (bypassable with --yes) with a typed-word confirmation that has no bypass, so the wipe can only be triggered by a deliberate interactive answer. * feat(cli): surface logs and status hints after start on a TTY * feat(cli): exit 2 on usage errors, distinct from operational failures * feat(cli): add start --follow to stream logs and exit on scan outcome * refactor(cli): redesign splash with sunset-gradient wordmark and truecolor * refactor(cli): remove the uninstall command * docs: sync CLI docs with removed uninstall/workspaces, new scans and --follow * docs: fix reset confirmation — typed confirm, not --yes/-y * style(cli): restructure status footer with divider, aligned Logs/Temporal rows * feat(cli): show splash in the status command * fix(worker): validate auth-state shape, not entry count * docs: correct reset confirmation and add markdown report to run-root docs
124 lines
4.5 KiB
TypeScript
124 lines
4.5 KiB
TypeScript
/**
|
|
* 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<Record<string, string>> = 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<string, AgentMetrics>;
|
|
readonly summary: PipelineSummary | null;
|
|
}
|