feat: remove Google Vertex AI provider support

This commit is contained in:
ezl-keygraph
2026-06-15 12:49:40 +05:30
parent 1908156525
commit a9e966026c
16 changed files with 25 additions and 280 deletions
+1 -18
View File
@@ -29,7 +29,7 @@ ANTHROPIC_API_KEY=your-api-key-here
# Model Tier Overrides (Anthropic API / OAuth / Custom Base URL / Bedrock)
# =============================================================================
# Override which model is used for each tier. Defaults are used if not set.
# Optional for direct Anthropic and custom base URL modes. Required for Bedrock/Vertex.
# Optional for direct Anthropic and custom base URL modes. Required for Bedrock.
# ANTHROPIC_SMALL_MODEL=... # Small tier (default: claude-haiku-4-5-20251001)
# ANTHROPIC_MEDIUM_MODEL=... # Medium tier (default: claude-sonnet-4-6)
# ANTHROPIC_LARGE_MODEL=... # Large tier (default: claude-opus-4-8)
@@ -47,20 +47,3 @@ ANTHROPIC_API_KEY=your-api-key-here
# CLAUDE_CODE_USE_BEDROCK=1
# AWS_REGION=us-east-1
# AWS_BEARER_TOKEN_BEDROCK=your-bearer-token
# =============================================================================
# OPTION 4: Google Vertex AI
# =============================================================================
# https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models
# Requires a GCP service account with roles/aiplatform.user.
# Download the SA key JSON from GCP Console (IAM > Service Accounts > Keys).
# Requires the model tier overrides above to be set with Vertex AI model IDs.
# Example Vertex AI model IDs:
# ANTHROPIC_SMALL_MODEL=claude-haiku-4-5@20251001
# ANTHROPIC_MEDIUM_MODEL=claude-sonnet-4-6
# ANTHROPIC_LARGE_MODEL=claude-opus-4-8
# CLAUDE_CODE_USE_VERTEX=1
# CLOUD_ML_REGION=us-east5
# ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
# GOOGLE_APPLICATION_CREDENTIALS=./credentials/google-sa-key.json
+1 -1
View File
@@ -147,7 +147,7 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig
### Supporting Systems
- **Configuration** — YAML configs in `apps/worker/configs/` with JSON Schema validation (`config-schema.json`). Supports auth settings (MFA/TOTP), URL/code rule scoping (`rules.avoid`/`rules.focus`), run-scope steering (`vuln_classes`, `exploit`), free-form `rules_of_engagement`, and post-hoc `report` filters (`min_severity`, `min_confidence`, `guidance`). `code_path` avoid rules are enforced via the `@gotgenes/pi-permission-system` extension: `apps/worker/src/temporal/activities.ts:syncCodePathDenyRules` writes a global `path` deny config once per workflow (`apps/worker/src/ai/settings-writer.ts:writeCodePathPermissionConfig`), and the executor loads the extension when that config is present (`apps/worker/src/ai/claude-executor.ts`), so denies fire across every tool and child `task` session. `vuln_classes`/`exploit` scope is locked into `session.json` on first run; resumes with a different scope fail fast (`persistOrValidateRunScope`). Credential resolution — local mode: env vars → `./.env`; npx mode: env vars → `~/.shannon/config.toml` (via `shn setup`)
- **Prompts** — Per-phase templates in `apps/worker/prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `apps/worker/prompts/shared/` via `apps/worker/src/services/prompt-manager.ts`, including `_code-path-rules.txt` (focus/avoid `[FILE]`/`[GLOB]` routing) and `_rules-of-engagement.txt` (free-text engagement rules). When `exploit: false`, `apps/worker/src/services/findings-renderer.ts` deterministically converts each `*_exploitation_queue.json` into a `*_findings.md` for report assembly — no LLM in the loop
- **Agent Harness (pi)** — Uses the **pi harness** (`@earendil-works/pi-coding-agent`, requires Node ≥ 22.19) via `apps/worker/src/ai/claude-executor.ts` (`runClaudePrompt``createAgentSession`, retry disabled so Temporal owns retry). Models resolve through pi-ai in `apps/worker/src/ai/models.ts` (Anthropic / Bedrock / Vertex / custom base URL via `ModelRegistry`+`AuthStorage`). pi ships no JSON-schema output or `Task`/`TodoWrite` built-ins, so structured queues are captured via a `submit_exploitation_queue` custom tool (`apps/worker/src/ai/queue-schemas.ts`), and `task` (read-only child sessions) + `todo_write` are provided as custom tools (`apps/worker/src/ai/tools.ts`); the per-phase MCP collectors are pi custom tools (TypeBox `defineTool` in `apps/worker/src/mcp-server/`). Thinking level defaults to `medium`; disable per-scan via `CLAUDE_ADAPTIVE_THINKING=false` (→ `off`) or set `CLAUDE_THINKING_LEVEL` (env) / `core.adaptive_thinking = false` (npx TOML). Browser automation via `playwright-cli` with session isolation (`-s=<session>`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth. On authenticated whitebox scans, the `validate-authentication` preflight performs the single real login and saves the browser session to `auth-state.json` in the per-session audit directory (path from `authStateFile()` in `apps/worker/src/audit/utils.ts`, derived from `generateAuditPath()`). The validation activity (`apps/worker/src/services/validate-authentication.ts`) removes any stale file from a prior run before the agent runs and verifies the file parses and contains cookies or storage before the preflight is marked complete; `logWorkflowComplete` deletes it when the workflow ends so authenticated cookies don't sit on disk between scans. Agent prompts opt in to session reuse by `@include(shared/_shared-session.txt)` before their `<login_instructions>` block — the partial restores the session and falls through to the full login flow if verification fails. `vuln-auth`/`exploit-auth` omit the include and own their own login
- **Agent Harness (pi)** — Uses the **pi harness** (`@earendil-works/pi-coding-agent`, requires Node ≥ 22.19) via `apps/worker/src/ai/claude-executor.ts` (`runClaudePrompt``createAgentSession`, retry disabled so Temporal owns retry). Models resolve through pi-ai in `apps/worker/src/ai/models.ts` (Anthropic / Bedrock / custom base URL via `ModelRegistry`+`AuthStorage`). pi ships no JSON-schema output or `Task`/`TodoWrite` built-ins, so structured queues are captured via a `submit_exploitation_queue` custom tool (`apps/worker/src/ai/queue-schemas.ts`), and `task` (read-only child sessions) + `todo_write` are provided as custom tools (`apps/worker/src/ai/tools.ts`); the per-phase MCP collectors are pi custom tools (TypeBox `defineTool` in `apps/worker/src/mcp-server/`). Thinking level defaults to `medium`; disable per-scan via `CLAUDE_ADAPTIVE_THINKING=false` (→ `off`) or set `CLAUDE_THINKING_LEVEL` (env) / `core.adaptive_thinking = false` (npx TOML). Browser automation via `playwright-cli` with session isolation (`-s=<session>`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth. On authenticated whitebox scans, the `validate-authentication` preflight performs the single real login and saves the browser session to `auth-state.json` in the per-session audit directory (path from `authStateFile()` in `apps/worker/src/audit/utils.ts`, derived from `generateAuditPath()`). The validation activity (`apps/worker/src/services/validate-authentication.ts`) removes any stale file from a prior run before the agent runs and verifies the file parses and contains cookies or storage before the preflight is marked complete; `logWorkflowComplete` deletes it when the workflow ends so authenticated cookies don't sit on disk between scans. Agent prompts opt in to session reuse by `@include(shared/_shared-session.txt)` before their `<login_instructions>` block — the partial restores the session and falls through to the full login flow if verification fails. `vuln-auth`/`exploit-auth` omit the include and own their own login
- **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. Tracks session metrics, per-agent logs, prompts, and deliverables. WorkflowLogger (`apps/worker/src/audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`apps/worker/src/audit/log-stream.ts`) shared stream primitive
- **Deliverables** — Saved to `deliverables/` in the target repo via the `save-deliverable` CLI script (`apps/worker/src/scripts/save-deliverable.ts`)
- **Workspaces & Resume** — Named workspaces via `-w <name>` or auto-named from URL+timestamp. Resume detects completed agents via `session.json`. `loadResumeState()` in `apps/worker/src/temporal/activities.ts` validates deliverable existence, restores git checkpoints, and cleans up incomplete deliverables. Workspace listing via `apps/worker/src/temporal/workspaces.ts`
+2 -2
View File
@@ -78,7 +78,7 @@ Sample Shannon Lite penetration test reports from intentionally vulnerable appli
- **Docker** - required for the worker container.
- **Node.js 18+** - required for the recommended `npx` workflow.
- **AI provider credentials** - Anthropic is recommended; AWS Bedrock, Google Vertex AI, and compatible proxy setups are documented separately.
- **AI provider credentials** - Anthropic is recommended; AWS Bedrock and compatible proxy setups are documented separately.
### Run Shannon Lite
@@ -194,7 +194,7 @@ Use these guides for operational detail:
| --- | --- |
| [Source build and CLI commands](docs/development.md) | Cloning, building, common commands, output paths, and local development. |
| [Configuration](docs/configuration.md) | Authenticated testing, login flows, rules of engagement, report filters, and rate-limit settings. |
| [AI providers](docs/ai-providers.md) | Anthropic, AWS Bedrock, Google Vertex AI, and custom Anthropic-compatible endpoints. |
| [AI providers](docs/ai-providers.md) | Anthropic, AWS Bedrock, and custom Anthropic-compatible endpoints. |
| [Platforms and networking](docs/platforms.md) | Windows/WSL2, Linux, macOS, Docker networking, local apps, and custom hostnames. |
| [Workspaces and resuming](docs/workspaces.md) | Naming workspaces, resuming interrupted scans, and workspace storage. |
| [Safety and limitations](docs/safety.md) | Authorized-use requirements, non-production guidance, mutative effects, cost, and model caveats. |
+1 -74
View File
@@ -5,7 +5,6 @@
* then persists everything to ~/.shannon/config.toml with 0o600 permissions.
*/
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import * as p from '@clack/prompts';
@@ -13,7 +12,7 @@ import { type ShannonConfig, saveConfig } from '../config/writer.js';
const SHANNON_HOME = path.join(os.homedir(), '.shannon');
type Provider = 'anthropic' | 'custom_base_url' | 'bedrock' | 'vertex';
type Provider = 'anthropic' | 'custom_base_url' | 'bedrock';
export async function setup(): Promise<void> {
p.intro('Shannon Setup');
@@ -25,7 +24,6 @@ export async function setup(): Promise<void> {
{ value: 'anthropic' as const, label: 'Claude Direct', hint: 'recommended' },
{ value: 'custom_base_url' as const, label: 'Custom Base URL', hint: 'proxies, gateways' },
{ value: 'bedrock' as const, label: 'Claude via AWS Bedrock' },
{ value: 'vertex' as const, label: 'Claude via Google Vertex AI' },
],
});
if (p.isCancel(provider)) return cancelAndExit();
@@ -51,8 +49,6 @@ async function setupProvider(provider: Provider): Promise<ShannonConfig> {
return setupCustomBaseUrl();
case 'bedrock':
return setupBedrock();
case 'vertex':
return setupVertex();
}
}
@@ -213,75 +209,6 @@ async function setupBedrock(): Promise<ShannonConfig> {
};
}
async function setupVertex(): Promise<ShannonConfig> {
// 1. Collect region and project ID
const region = await p.text({
message: 'Google Cloud region',
placeholder: 'us-east5',
validate: required('Region is required'),
});
if (p.isCancel(region)) return cancelAndExit();
const projectId = await p.text({
message: 'GCP Project ID',
validate: required('Project ID is required'),
});
if (p.isCancel(projectId)) return cancelAndExit();
// 2. File picker for service account key
p.log.info('Select the path to your GCP Service Account JSON key file.');
const keySourcePath = await p.path({
message: 'Service Account JSON key file',
validate: (value) => {
if (!value) return 'Path is required';
if (!fs.existsSync(value)) return 'File not found';
if (!value.endsWith('.json')) return 'Must be a .json file';
return undefined;
},
});
if (p.isCancel(keySourcePath)) return cancelAndExit();
// 3. Copy key to ~/.shannon/ and lock permissions
const destPath = path.join(SHANNON_HOME, 'google-sa-key.json');
fs.mkdirSync(SHANNON_HOME, { recursive: true });
fs.copyFileSync(keySourcePath, destPath);
fs.chmodSync(destPath, 0o600);
p.log.success(`Key copied to ${destPath} (permissions: 0600)`);
// 4. Model tiers
const models = await p.group({
small: () =>
p.text({
message: 'Small model ID',
placeholder: 'claude-haiku-4-5@20251001',
validate: required('Small model ID is required'),
}),
medium: () =>
p.text({
message: 'Medium model ID',
placeholder: 'claude-sonnet-4-6',
validate: required('Medium model ID is required'),
}),
large: () =>
p.text({
message: 'Large model ID',
placeholder: 'claude-opus-4-8',
validate: required('Large model ID is required'),
}),
});
if (p.isCancel(models)) return cancelAndExit();
return {
vertex: {
use: true,
region,
project_id: projectId,
key_path: destPath,
},
models: { small: models.small, medium: models.medium, large: models.large },
};
}
// === Helpers ===
async function maybePromptAdaptiveThinking(config: ShannonConfig): Promise<void> {
+1 -9
View File
@@ -10,7 +10,7 @@ import fs from 'node:fs';
import path from 'node:path';
import { ensureImage, ensureInfra, randomSuffix, spawnWorker } from '../docker.js';
import { buildEnvFlags, loadEnv, validateCredentials } from '../env.js';
import { getCredentialsPath, getWorkspacesDir, initHome } from '../home.js';
import { getWorkspacesDir, initHome } from '../home.js';
import { isLocal } from '../mode.js';
import { resolveConfig, resolveRepo } from '../paths.js';
import { displaySplash } from '../splash.js';
@@ -78,13 +78,6 @@ export async function start(args: StartArgs): Promise<void> {
}
fs.mkdirSync(path.join(repo.hostPath, '.playwright'), { recursive: true });
const credentialsPath = getCredentialsPath();
const hasCredentials = fs.existsSync(credentialsPath);
if (hasCredentials) {
process.env.GOOGLE_APPLICATION_CREDENTIALS = '/app/credentials/google-sa-key.json';
}
// 10. Resolve output directory
const outputDir = args.output ? path.resolve(args.output) : undefined;
if (outputDir) {
@@ -107,7 +100,6 @@ export async function start(args: StartArgs): Promise<void> {
containerName,
envFlags: buildEnvFlags(),
...(config && { config }),
...(hasCredentials && { credentials: credentialsPath }),
...(promptsDir && { promptsDir }),
...(outputDir && { outputDir }),
workspace,
+2 -18
View File
@@ -36,12 +36,6 @@ const CONFIG_MAP: readonly ConfigMapping[] = [
{ env: 'AWS_REGION', toml: 'bedrock.region', type: 'string' },
{ env: 'AWS_BEARER_TOKEN_BEDROCK', toml: 'bedrock.token', type: 'string' },
// Vertex
{ env: 'CLAUDE_CODE_USE_VERTEX', toml: 'vertex.use', type: 'boolean' },
{ env: 'CLOUD_ML_REGION', toml: 'vertex.region', type: 'string' },
{ env: 'ANTHROPIC_VERTEX_PROJECT_ID', toml: 'vertex.project_id', type: 'string' },
{ env: 'GOOGLE_APPLICATION_CREDENTIALS', toml: 'vertex.key_path', type: 'string' },
// Custom Base URL
{ env: 'ANTHROPIC_BASE_URL', toml: 'custom_base_url.base_url', type: 'string' },
{ env: 'ANTHROPIC_AUTH_TOKEN', toml: 'custom_base_url.auth_token', type: 'string' },
@@ -154,20 +148,10 @@ function validateProviderFields(config: TOMLConfig, provider: string, errors: st
validateModelTiers(config, 'bedrock', errors);
break;
}
case 'vertex': {
const required = ['use', 'region', 'project_id', 'key_path'];
const missing = required.filter((k) => !keys.includes(k));
if (missing.length > 0) {
errors.push(`[vertex] missing required keys: ${missing.join(', ')}`);
}
validateModelTiers(config, 'vertex', errors);
break;
}
}
}
/** Bedrock and Vertex require a [models] section with all three tiers. */
/** Bedrock requires a [models] section with all three tiers. */
function validateModelTiers(config: TOMLConfig, provider: string, errors: string[]): void {
const models = config.models as Record<string, unknown> | undefined;
if (!models || typeof models !== 'object') {
@@ -227,7 +211,7 @@ function validateConfig(config: TOMLConfig): string[] {
}
// 4. Only one provider section allowed (ignore empty sections)
const PROVIDER_SECTIONS = ['anthropic', 'custom_base_url', 'bedrock', 'vertex'] as const;
const PROVIDER_SECTIONS = ['anthropic', 'custom_base_url', 'bedrock'] as const;
const present = PROVIDER_SECTIONS.filter((s) => {
const section = config[s];
return section && typeof section === 'object' && Object.keys(section).length > 0;
-1
View File
@@ -12,7 +12,6 @@ export interface ShannonConfig {
anthropic?: { api_key?: string; oauth_token?: string };
custom_base_url?: { base_url?: string; auth_token?: string };
bedrock?: { use?: boolean; region?: string; token?: string };
vertex?: { use?: boolean; region?: string; project_id?: string; key_path?: string };
models?: { small?: string; medium?: string; large?: string };
}
-6
View File
@@ -236,7 +236,6 @@ export interface WorkerOptions {
containerName: string;
envFlags: string[];
config?: { hostPath: string; containerPath: string };
credentials?: string;
promptsDir?: string;
outputDir?: string;
workspace: string;
@@ -291,11 +290,6 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess {
args.push('-v', `${opts.outputDir}:/app/output`);
}
// Mount credentials file to fixed container path
if (opts.credentials) {
args.push('-v', `${opts.credentials}:/app/credentials/google-sa-key.json:ro`);
}
// Environment
args.push(...opts.envFlags);
+1 -29
View File
@@ -18,10 +18,6 @@ const FORWARD_VARS = [
'CLAUDE_CODE_USE_BEDROCK',
'AWS_REGION',
'AWS_BEARER_TOKEN_BEDROCK',
'CLAUDE_CODE_USE_VERTEX',
'CLOUD_ML_REGION',
'ANTHROPIC_VERTEX_PROJECT_ID',
'GOOGLE_APPLICATION_CREDENTIALS',
'ANTHROPIC_SMALL_MODEL',
'ANTHROPIC_MEDIUM_MODEL',
'ANTHROPIC_LARGE_MODEL',
@@ -62,7 +58,7 @@ export function buildEnvFlags(): string[] {
interface CredentialValidation {
valid: boolean;
error?: string;
mode: 'api-key' | 'oauth' | 'custom-base-url' | 'bedrock' | 'vertex';
mode: 'api-key' | 'oauth' | 'custom-base-url' | 'bedrock';
}
/** Check if a custom Anthropic-compatible base URL is configured. */
@@ -77,7 +73,6 @@ function detectProviders(): string[] {
if (process.env.CLAUDE_CODE_OAUTH_TOKEN) providers.push('Anthropic OAuth');
if (isCustomBaseUrlConfigured()) providers.push('Custom Base URL');
if (process.env.CLAUDE_CODE_USE_BEDROCK === '1') providers.push('AWS Bedrock');
if (process.env.CLAUDE_CODE_USE_VERTEX === '1') providers.push('Google Vertex');
return providers;
}
@@ -120,29 +115,6 @@ export function validateCredentials(): CredentialValidation {
}
return { valid: true, mode: 'bedrock' };
}
if (process.env.CLAUDE_CODE_USE_VERTEX === '1') {
const missing: string[] = [];
if (!process.env.CLOUD_ML_REGION) missing.push('CLOUD_ML_REGION');
if (!process.env.ANTHROPIC_VERTEX_PROJECT_ID) missing.push('ANTHROPIC_VERTEX_PROJECT_ID');
if (!process.env.ANTHROPIC_SMALL_MODEL) missing.push('ANTHROPIC_SMALL_MODEL');
if (!process.env.ANTHROPIC_MEDIUM_MODEL) missing.push('ANTHROPIC_MEDIUM_MODEL');
if (!process.env.ANTHROPIC_LARGE_MODEL) missing.push('ANTHROPIC_LARGE_MODEL');
if (missing.length > 0) {
return {
valid: false,
mode: 'vertex',
error: `Vertex AI mode requires: ${missing.join(', ')}`,
};
}
if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {
return {
valid: false,
mode: 'vertex',
error: 'Vertex AI mode requires GOOGLE_APPLICATION_CREDENTIALS',
};
}
return { valid: true, mode: 'vertex' };
}
const hint =
getMode() === 'local'
+2 -20
View File
@@ -1,7 +1,7 @@
/**
* Shannon state directory management.
*
* Local mode (cloned repo): uses ./workspaces/, ./credentials/
* Local mode (cloned repo): uses ./workspaces/
* NPX mode: uses ~/.shannon/workspaces/, ~/.shannon/
*/
@@ -20,32 +20,14 @@ export function getWorkspacesDir(): string {
return getMode() === 'local' ? path.resolve('workspaces') : path.join(SHANNON_HOME, 'workspaces');
}
/**
* Resolve the Vertex credentials file path.
*
* Checks GOOGLE_APPLICATION_CREDENTIALS env var first (may be set by TOML resolver),
* then falls back to mode-appropriate default location.
*/
export function getCredentialsPath(): string {
const envPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
if (envPath && fs.existsSync(envPath)) return path.resolve(envPath);
if (getMode() === 'local') {
return path.resolve('credentials', 'google-sa-key.json');
}
return path.join(SHANNON_HOME, 'google-sa-key.json');
}
/**
* Initialize state directories.
* Local mode: creates ./workspaces/ and ./credentials/
* Local mode: creates ./workspaces/
* NPX mode: creates ~/.shannon/workspaces/
*/
export function initHome(): void {
if (getMode() === 'local') {
fs.mkdirSync(path.resolve('workspaces'), { recursive: true });
fs.mkdirSync(path.resolve('credentials'), { recursive: true });
} else {
fs.mkdirSync(path.join(SHANNON_HOME, 'workspaces'), { recursive: true });
}
+5 -7
View File
@@ -14,12 +14,12 @@
*
* Users override per tier via ANTHROPIC_SMALL_MODEL / ANTHROPIC_MEDIUM_MODEL /
* ANTHROPIC_LARGE_MODEL, which works across all providers (Anthropic, Bedrock,
* Vertex, custom base URL).
* custom base URL).
*
* Resolution returns a pi `Model` object via `ModelRegistry.find`, plus the
* `thinkingLevel` and an `AuthStorage` primed with runtime credentials. Bedrock
* and Vertex authenticate from the process environment (the AWS_ and GOOGLE_ vars
* the CLI forwards), so they need no runtime API key.
* authenticates from the process environment (the AWS_ vars the CLI forwards), so
* it needs no runtime API key.
*/
import type { ThinkingLevel } from '@earendil-works/pi-agent-core';
@@ -40,8 +40,6 @@ function piProviderId(providerConfig?: ProviderConfig): string {
switch (providerConfig?.providerType) {
case 'bedrock':
return 'amazon-bedrock';
case 'vertex':
return 'google-vertex';
default:
// 'anthropic_api', 'custom_base_url', or unset all resolve to the anthropic
// provider; custom_base_url overrides baseUrl/auth below.
@@ -94,8 +92,8 @@ export interface ModelSelection {
* → ANTHROPIC_API_KEY env. OAuth (CLAUDE_CODE_OAUTH_TOKEN) is read from env by pi.
* - Custom base URL (custom_base_url): the auth token is set as the anthropic
* runtime key and the model's baseUrl is overridden.
* - Bedrock / Vertex: authenticate from the process environment (the AWS_ and
* GOOGLE_ vars), no runtime key needed.
* - Bedrock: authenticates from the process environment (the AWS_ vars), no
* runtime key needed.
*/
export function resolveModelSelection(
registryFactory: (authStorage: AuthStorage) => ModelRegistry,
+5 -56
View File
@@ -15,7 +15,7 @@
* 1. Repository path exists and contains .git
* 2. Config file parses and validates (if provided)
* 3. code_path rules match real entries in the repo (filesystem only)
* 4. Credentials validate via Claude Agent SDK query (API key, OAuth, Bedrock, or Vertex AI)
* 4. Credentials validate via a minimal pi session (API key, OAuth, or Bedrock)
* 5. Target URL resolves, is not link-local (cloud metadata), and is reachable (DNS + HTTP)
*/
@@ -405,62 +405,11 @@ async function validateCredentials(
return ok(undefined);
}
// 3. Vertex AI mode — validate required GCP credentials are present
if (process.env.CLAUDE_CODE_USE_VERTEX === '1') {
const required = [
'CLOUD_ML_REGION',
'ANTHROPIC_VERTEX_PROJECT_ID',
'ANTHROPIC_SMALL_MODEL',
'ANTHROPIC_MEDIUM_MODEL',
'ANTHROPIC_LARGE_MODEL',
];
const missing = required.filter((v) => !process.env[v]);
if (missing.length > 0) {
return err(
new PentestError(
`Vertex AI mode requires the following env vars in .env: ${missing.join(', ')}`,
'config',
false,
{ missing },
ErrorCode.AUTH_FAILED,
),
);
}
// Validate service account credentials file is accessible
const credPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
if (!credPath) {
return err(
new PentestError(
'Vertex AI mode requires GOOGLE_APPLICATION_CREDENTIALS pointing to a service account key JSON file',
'config',
false,
{},
ErrorCode.AUTH_FAILED,
),
);
}
try {
await fs.access(credPath);
} catch {
return err(
new PentestError(
`Service account key file not found at: ${credPath}`,
'config',
false,
{ credPath },
ErrorCode.AUTH_FAILED,
),
);
}
logger.info('Vertex AI credentials OK');
return ok(undefined);
}
// 4. Check that at least one credential is present
// 3. Check that at least one credential is present
if (!process.env.ANTHROPIC_API_KEY && !process.env.CLAUDE_CODE_OAUTH_TOKEN && !process.env.ANTHROPIC_AUTH_TOKEN) {
return err(
new PentestError(
'No API credentials found. Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env (or use CLAUDE_CODE_USE_BEDROCK=1 for AWS Bedrock, or CLAUDE_CODE_USE_VERTEX=1 for Google Vertex AI)',
'No API credentials found. Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env (or use CLAUDE_CODE_USE_BEDROCK=1 for AWS Bedrock)',
'config',
false,
{},
@@ -469,7 +418,7 @@ async function validateCredentials(
);
}
// 5. Validate via a minimal pi session
// 4. Validate via a minimal pi session
const authType = process.env.CLAUDE_CODE_OAUTH_TOKEN ? 'OAuth token' : 'API key';
logger.info(`Validating ${authType} via pi...`);
const probe = await probeCredentialsWithPi(authType);
@@ -606,7 +555,7 @@ async function validateTargetUrl(targetUrl: string, logger: ActivityLogger): Pro
* 1. Repository path exists and contains .git
* 2. Config file parses and validates (if configPath provided)
* 3. code_path rules match at least one entry in the repo (skipped without config)
* 4. Credentials validate (API key, OAuth, Bedrock, or Vertex AI)
* 4. Credentials validate (API key, OAuth, or Bedrock)
* 5. Target URL is reachable from the container
*
* Returns on first failure.
+1 -1
View File
@@ -459,7 +459,7 @@ export async function runReportAgent(input: ActivityInput): Promise<AgentMetrics
* Runs cheap checks before any agent execution:
* 1. Repository path exists with .git
* 2. Config file validates (if provided)
* 3. Credential validation (API key, OAuth, Bedrock, or Vertex AI)
* 3. Credential validation (API key, OAuth, or Bedrock)
* 4. Target URL reachable from the container
*
* NOT using runAgentActivity — preflight doesn't run an agent via the SDK.
+1 -1
View File
@@ -28,7 +28,7 @@ export interface PipelineInput {
sastSarifPath?: string; // Optional path for consumer-supplied findings input
checkpointsEnabled?: boolean; // Enable checkpoint activities (default: false)
skipGitCheck?: boolean; // Skip .git directory validation in preflight (e.g. when .git is removed after clone)
providerConfig?: ProviderConfig; // LLM provider configuration (Bedrock, Vertex, etc.)
providerConfig?: ProviderConfig; // LLM provider configuration (Bedrock, custom base URL, etc.)
vulnClasses?: VulnClass[]; // omitted = all five
exploit?: boolean; // false skips the exploitation phase
}
+1 -4
View File
@@ -95,7 +95,7 @@ export interface DistributedConfig {
* LLM provider configuration for multi-provider support.
*
* Resolved by the pi model/provider layer at execution time. Recognized
* providerType values: 'bedrock', 'vertex', 'custom_base_url', 'anthropic_api'.
* providerType values: 'bedrock', 'custom_base_url', 'anthropic_api'.
* When omitted or 'anthropic_api', falls back to apiKey + ANTHROPIC_API_KEY.
*/
export interface ProviderConfig {
@@ -104,9 +104,6 @@ export interface ProviderConfig {
readonly awsRegion?: string;
readonly awsAccessKeyId?: string;
readonly awsSecretAccessKey?: string;
readonly gcpRegion?: string;
readonly gcpProjectId?: string;
readonly gcpCredentialsPath?: string;
readonly baseUrl?: string;
readonly authToken?: string;
readonly modelOverrides?: Record<string, string>;
+1 -33
View File
@@ -1,6 +1,6 @@
# AI Providers
Shannon Lite works best with Claude models. Anthropic API keys are recommended for most users, and Shannon Lite also supports AWS Bedrock, Google Vertex AI, and custom Anthropic-compatible endpoints.
Shannon Lite works best with Claude models. Anthropic API keys are recommended for most users, and Shannon Lite also supports AWS Bedrock and custom Anthropic-compatible endpoints.
## Anthropic
@@ -59,38 +59,6 @@ Shannon Lite uses three model tiers:
Set `ANTHROPIC_SMALL_MODEL`, `ANTHROPIC_MEDIUM_MODEL`, and `ANTHROPIC_LARGE_MODEL` to Bedrock model IDs available in your region.
## Google Vertex AI
Create a service account with the `roles/aiplatform.user` role in the GCP Console, then download a JSON key file.
Run `npx @keygraph/shannon setup` and select **Google Vertex AI**. The wizard prompts for region, project ID, service account key file path, and model IDs. The key file is copied to `~/.shannon/google-sa-key.json`.
Or export environment variables directly:
```bash
export CLAUDE_CODE_USE_VERTEX=1
export CLOUD_ML_REGION=us-east5
export ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/your-sa-key.json
export ANTHROPIC_SMALL_MODEL=claude-haiku-4-5@20251001
export ANTHROPIC_MEDIUM_MODEL=claude-sonnet-4-6
export ANTHROPIC_LARGE_MODEL=claude-opus-4-8
```
Source-build `.env` equivalent:
```bash
CLAUDE_CODE_USE_VERTEX=1
CLOUD_ML_REGION=us-east5
ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
GOOGLE_APPLICATION_CREDENTIALS=./credentials/google-sa-key.json
ANTHROPIC_SMALL_MODEL=claude-haiku-4-5@20251001
ANTHROPIC_MEDIUM_MODEL=claude-sonnet-4-6
ANTHROPIC_LARGE_MODEL=claude-opus-4-8
```
Set `CLOUD_ML_REGION=global` for global endpoints, or use a specific region like `us-east5`. Some models may not be available on global endpoints.
## Custom Base URL
Shannon Lite supports pointing the SDK at an Anthropic-compatible endpoint with `ANTHROPIC_BASE_URL`. For proxy-based routing, use an LLM proxy such as LiteLLM configured to expose an Anthropic-compatible endpoint.