Shannon is an AI pentester for web applications and APIs. It analyzes your source code, identifies attack vectors, and executes real exploits to prove vulnerabilities before they reach production.
This report summarizes scanner evidence for KeygraphHQ/shannon, a TypeScript project with 48,731 GitHub stars. SynthScan examined 69,726 lines of code across 310 source files, recording 238 pattern matches distributed across 7 syntactic categories. The legacy adjusted pattern signal is 4.8 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 221 finding(s).
Are imports declared and package/API names verified? 17 finding(s).
Are wrappers, guards, and repeated structures necessary? 176 finding(s).
Which changes need focused human review? 238 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 238 distinct pattern matches across 7 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | apps/cli/src/docker.ts | 195 | function readCurrentRetentionSeconds(): number | null { | CODE |
| LOW | apps/cli/src/docker.ts | 210 | function convergeNamespaceRetention(): void { | CODE |
| LOW | apps/cli/src/docker.ts | 291 | function ensureWorkerImageProtocol(image: string): void { | CODE |
| LOW | apps/cli/src/docker.ts | 582 | export function runningScanContainersChecked( | CODE |
| LOW | apps/cli/src/docker.ts | 621 | export function runningScanWorkspacesChecked(): CommandQueryResult<string[]> { | CODE |
| LOW | apps/cli/src/pending-workflow.ts | 40 | export function writePendingWorkflowIdentity(workspacePath: string, workflowId: string, taskQueue: string): void { | CODE |
| LOW | apps/cli/src/pending-workflow.ts | 73 | export function clearPendingWorkflowIdentity(workspacePath: string, taskQueue: string): void { | CODE |
| LOW | apps/cli/src/pending-workflow.ts | 83 | function isPendingWorkflowIdentity( | CODE |
| LOW | apps/cli/src/pending-workflow.ts | 113 | export function readPendingWorkflowIdentities(workspacePath: string): PendingWorkflowReadResult { | CODE |
| LOW | apps/cli/src/env.ts | 157 | function describeMissingCredential(providerId: string): string { | CODE |
| LOW | apps/cli/src/temporal-client.ts | 126 | export async function refreshWorkflowLifecycleConnection(): Promise<void> { | CODE |
| LOW | apps/cli/src/temporal-client.ts | 157 | export async function describeWorkflowLifecycle(workflowId: string): Promise<WorkflowLifecycleState> { | CODE |
| LOW | apps/cli/src/temporal-client.ts | 172 | export async function requestWorkflowCancellation(workflowId: string): Promise<'requested' | 'not-found'> { | CODE |
| LOW | apps/cli/src/temporal-client.ts | 183 | export async function requestWorkflowTermination( | CODE |
| LOW | apps/cli/src/config/resolver.ts | 235 | function assertNoCredentialConflict(toml: TOMLConfig): void { | CODE |
| LOW | apps/cli/src/scan/safe-fields.ts | 73 | function isProviderFailureCategory(value: unknown): value is string { | CODE |
| LOW | apps/cli/src/commands/logs.ts | 74 | export function appendCancellationFallback(logFile: string): void { | CODE |
| LOW | apps/cli/src/commands/setup.ts | 288 | function conflictingProviderPrefix(provider: string, value: string): string | undefined { | CODE |
| LOW | apps/cli/src/commands/start.ts | 230 | export function writeLaunchStateAtomically( | CODE |
| LOW | apps/cli/src/commands/start.ts | 741 | function printPreservedContainerHint(containerName: string): void { | CODE |
| LOW | apps/cli/src/commands/stop.ts | 289 | function verifiedContainerWorkflowId( | CODE |
| LOW | apps/cli/src/commands/stop.ts | 509 | function visibleWorkflowsForWorkspace( | CODE |
| LOW | apps/cli/src/commands/stop.ts | 680 | export async function stopVisibleWorkflowsUntilSettled( | CODE |
| LOW | apps/worker/src/temporal/workflows.ts | 222 | function hasCancellationInCauseChain(error: unknown): boolean { | CODE |
| LOW | apps/worker/src/temporal/workflows.ts | 226 | function applicationFailureInChain(error: unknown): ApplicationFailure | undefined { | CODE |
| LOW | apps/worker/src/temporal/workflows.ts | 242 | function shouldUseSingletonFallback(error: unknown): boolean { | CODE |
| LOW | apps/worker/src/temporal/workflows.ts | 273 | function reconciliationActivityOptions( | CODE |
| LOW | apps/worker/src/temporal/workflows.ts | 880 | function projectCapellaWorkflowFailure(message: string, startedAt: number): void { | CODE |
| LOW | apps/worker/src/temporal/workflows.ts | 898 | function projectCapellaInfrastructureFailure(startedAt: number): void { | CODE |
| LOW | apps/worker/src/temporal/workflows.ts | 1129 | async function runMiscellaneousExploitLane(capella: Promise<CapellaSettlement>): Promise<void> { | CODE |
| LOW | apps/worker/src/temporal/reconcile-activity-types.ts | 48 | export function isAcceptedTaskFormationFallbackReason(value: unknown): value is TaskFormationFallbackReason { | CODE |
| LOW | apps/worker/src/temporal/reconcile-activity-types.ts | 223 | export function reconciliationClassDeadlineFrom(startedAtMs: number): number { | CODE |
| LOW | apps/worker/src/temporal/reconcile-activity-types.ts | 234 | export function resolveReconciliationActivityBudget( | CODE |
| LOW | apps/worker/src/temporal/reconcile-activities.ts | 251 | function chainContainsRealCancellation(error: unknown, signal: AbortSignal): boolean { | CODE |
| LOW | apps/worker/src/temporal/reconcile-activities.ts | 476 | export function createReconciliationActivityRegistry( | CODE |
| LOW | apps/worker/src/temporal/reconcile-activities.ts | 500 | async function prepareClassReconciliation(input: PrepareClassReconciliationActivityInput) { | CODE |
| LOW | apps/worker/src/temporal/reconcile-activities.ts | 528 | async function enrichClassSastObservations(input: EnrichClassSastObservationsActivityInput) { | CODE |
| LOW | apps/worker/src/temporal/reconcile-activities.ts | 577 | async function materializeClassExploitTasks(input: MaterializeClassExploitTasksActivityInput) { | CODE |
| LOW | apps/worker/src/temporal/reconcile-activities.ts | 599 | async function publishClassReconciliationOss(input: PublishClassReconciliationActivityInput) { | CODE |
| LOW | apps/worker/src/temporal/worker.ts | 233 | export function createProductionActivityRegistry(bindings: ProductionActivityBindings): Readonly<object> { | CODE |
| LOW | apps/worker/src/temporal/worker.ts | 405 | async function terminateExistingWorkflows(client: Client, workspaceName: string): Promise<string[]> { | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 234 | function deterministicActivityFailure(error: unknown, failureType: string): ApplicationFailure { | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 312 | async function rollbackInvalidReportDraft(input: ActivityInput, progress: ReportProgress): Promise<void> { | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 540 | async function runVulnAgentWithCollector( | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 622 | async function runExploitAgentWithCollector( | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 691 | export async function runMiscellaneousExploitAgent(input: ActivityInput): Promise<AgentMetrics> { | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 885 | export async function runCyberAccessVerification(_input: ActivityInput): Promise<{ gated: boolean }> { | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 952 | export async function runAuthenticationValidation(input: ActivityInput): Promise<AgentMetrics | null> { | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 1070 | export async function syncPlaywrightStealthConfig(input: ActivityInput): Promise<void> { | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 1110 | export async function initializeDurableScanState( | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 1125 | export async function persistMiscellaneousOutcome( | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 1275 | export async function persistCanonicalReportProgress( | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 1379 | export async function persistFinalizedReportProgress( | CODE |
| LOW | apps/worker/src/temporal/activities.ts | 1753 | export async function persistOrValidateRunScope( | CODE |
| LOW | apps/worker/src/types/errors.ts | 64 | export function isProviderFailureCategory(value: unknown): value is ProviderFailureCategory { | CODE |
| LOW | apps/worker/src/types/run-state.ts | 386 | function isBoundedPartialReasonCount(value: unknown): value is number { | CODE |
| LOW | apps/worker/src/types/run-state.ts | 472 | function hasConflictingClassReasons(reasons: readonly PartialReason[]): boolean { | CODE |
| LOW | apps/worker/src/types/run-state.ts | 486 | export function isOrderedPartialReasonSet(value: unknown): value is readonly PartialReason[] { | CODE |
| LOW | apps/worker/src/types/run-state.ts | 641 | function renderIncompleteResearchReduction( | CODE |
| LOW | apps/worker/src/types/run-state.ts | 695 | export function partialReasonFromReduction(reduction: AgenticSastReduction): PartialReason { | CODE |
| 116 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | llms-full.txt | 453 | ANTHROPIC_API_KEY=your-api-key | CODE |
| HIGH | llms-full.txt | 459 | export ANTHROPIC_API_KEY="your-api-key" | CODE |
| HIGH | llms-full.txt | 820 | export SHANNON_AI_API_KEY=your-api-key # the provider's key — or the gateway's when a base URL is se | CODE |
| HIGH | llms-full.txt | 954 | "apiKey": "YOUR_API_KEY", | CODE |
| HIGH | llms-full.txt | 996 | export SHANNON_AI_API_KEY=your-api-key | CODE |
| HIGH | llms-full.txt | 1000 | Leave `YOUR_API_KEY` exactly as it is. Shannon sends the credential from your environment, and that takes precedence ove | CODE |
| HIGH | llms-full.txt | 1139 | api_key = "your-api-key" | CODE |
| HIGH | docs/ai-providers.md | 31 | export SHANNON_AI_API_KEY=your-api-key # the provider's key — or the gateway's when a base URL is se | CODE |
| HIGH | docs/ai-providers.md | 165 | "apiKey": "YOUR_API_KEY", | CODE |
| HIGH | docs/ai-providers.md | 207 | export SHANNON_AI_API_KEY=your-api-key | CODE |
| HIGH | docs/ai-providers.md | 211 | Leave `YOUR_API_KEY` exactly as it is. Shannon sends the credential from your environment, and that takes precedence ove | CODE |
| HIGH | docs/ai-providers.md | 350 | api_key = "your-api-key" | CODE |
| HIGH | docs/development.md | 35 | ANTHROPIC_API_KEY=your-api-key | CODE |
| HIGH | docs/development.md | 41 | export ANTHROPIC_API_KEY="your-api-key" | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .claude/commands/debug.md | 7 | ## Step 1: Capture Error Context | COMMENT |
| LOW | .claude/commands/debug.md | 20 | ## Step 2: Check Relevant Logs | COMMENT |
| LOW | .claude/commands/debug.md | 35 | ## Step 3: Trace the Call Path | COMMENT |
| LOW | .claude/commands/debug.md | 50 | ## Step 4: Identify Root Cause | COMMENT |
| LOW | .claude/commands/debug.md | 90 | ## Step 5: Apply Fix with Retry Limit | COMMENT |
| LOW | .claude/commands/debug.md | 99 | ## Step 6: Validate the Fix | COMMENT |
| LOW | .claude/commands/review.md | 7 | ## Step 1: Gather Changes | COMMENT |
| LOW | .claude/commands/review.md | 14 | ## Step 2: Check Shannon-Specific Patterns | COMMENT |
| LOW | .claude/commands/review.md | 66 | ## Step 3: TypeScript Safety | COMMENT |
| LOW | .claude/commands/review.md | 83 | ## Step 4: Security Review | COMMENT |
| LOW | .claude/commands/review.md | 95 | ## Step 5: Common Mistakes to Avoid | COMMENT |
| LOW | .claude/commands/review.md | 112 | ## Step 6: Provide Feedback | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-authz.txt | 7 | ### Step 1: Test Browser Isolation | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-authz.txt | 23 | ### Step 2: Create Required Deliverable | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-authz.txt | 31 | ### Step 3: Verify Session Isolation | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-ssrf.txt | 7 | ### Step 1: Test Browser Isolation | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-ssrf.txt | 23 | ### Step 2: Create Required Deliverable | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-ssrf.txt | 31 | ### Step 3: Verify Session Isolation | COMMENT |
| LOW | …/worker/prompts/pipeline-testing/exploit-injection.txt | 7 | ### Step 1: Test Browser Isolation | COMMENT |
| LOW | …/worker/prompts/pipeline-testing/exploit-injection.txt | 23 | ### Step 2: Create Required Deliverable | COMMENT |
| LOW | …/worker/prompts/pipeline-testing/exploit-injection.txt | 31 | ### Step 3: Verify Session Isolation | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-xss.txt | 7 | ### Step 1: Test Browser Isolation | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-xss.txt | 23 | ### Step 2: Create Required Deliverable | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-xss.txt | 31 | ### Step 3: Verify Session Isolation | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-auth.txt | 7 | ### Step 1: Test Browser Isolation | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-auth.txt | 23 | ### Step 2: Create Required Deliverable | COMMENT |
| LOW | apps/worker/prompts/pipeline-testing/exploit-auth.txt | 31 | ### Step 3: Verify Session Isolation | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | README.md | 255 | No source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester). | CODE |
| MEDIUM | llms-full.txt | 265 | No source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester). | CODE |
| MEDIUM | llms-full.txt | 1386 | No source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester). | CODE |
| MEDIUM | docs/keygraph-platform.md | 11 | No source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester). | CODE |
| MEDIUM | apps/cli/src/scan/status-json.ts | 92 | // Present only when agentic SAST actually ran; a disabled scan omits the key entirely. | COMMENT |
| MEDIUM | apps/cli/src/scan/render.ts | 276 | // The safe message names what degraded; these three name the agentic-SAST failure | COMMENT |
| MEDIUM | apps/worker/src/session-manager.ts | 107 | // or agentic SAST). It has no analysis-phase counterpart: there is no 'miscellaneous-vuln' | COMMENT |
| MEDIUM | apps/worker/src/temporal/worker.ts | 645 | // agentic-SAST failure behind it, under the same labels every other surface uses. | COMMENT |
| MEDIUM | apps/worker/src/audit/metrics-tracker.ts | 900 | // Operational work (agentic SAST, reconciliation) runs concurrently with the agent phases and is | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | llms-full.txt | 1107 | 3. Select **OAuth Token** and enter the token generated by Claude Code. | CODE |
| HIGH | docs/ai-providers.md | 318 | 3. Select **OAuth Token** and enter the token generated by Claude Code. | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | llms-full.txt | 681 | # code_path values are repo-relative file paths or globs. | COMMENT |
| LOW | docs/configuration.md | 41 | ```yaml | COMMENT |
| LOW | apps/worker/configs/example-config.yaml | 1 | # Example configuration file for pentest-agent | COMMENT |
| LOW | apps/worker/configs/example-config.yaml | 21 | # rules_of_engagement: | | COMMENT |
| LOW | apps/worker/configs/example-config.yaml | 101 | COMMENT | |
| LOW | apps/worker/src/session-manager.ts | 1 | // Copyright (C) 2026 Keygraph, Inc. | COMMENT |
| LOW | apps/worker/src/services/error-handling.ts | 21 | // status/headers mirrors pi's request-layer policy, and every other (flattened) failure | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | sample-reports/shannon-report-crapi.md | 57 | -d '{"email": "admin@example.com", "password": "Admin!123"}' | CODE |
| LOW | sample-reports/shannon-report-crapi.md | 139 | -d '{"email": "admin@example.com", "password": "Admin!123"}' | CODE |
| LOW | sample-reports/shannon-report-crapi.md | 242 | - Payload: `{"sub": "admin@example.com", "iat": 1758655244, "exp": 1759260044, "role": "admin"}` | CODE |