mirror of
https://github.com/KeygraphHQ/shannon.git
synced 2026-10-09 17:43:58 +02:00
Compare commits
39
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef2b254030 | ||
|
|
147bc3f5f4 | ||
|
|
e220f4862c | ||
|
|
ce935d42d8 | ||
|
|
76c32a458e | ||
|
|
b7af20b479 | ||
|
|
05c2c1048e | ||
|
|
3a1a91e07a | ||
|
|
4e703ef183 | ||
|
|
8f795f6dca | ||
|
|
c689ef0de0 | ||
|
|
c408eabc62 | ||
|
|
01dc49bbd6 | ||
|
|
a6fbb4832e | ||
|
|
4436459310 | ||
|
|
45581a7882 | ||
|
|
35b5192837 | ||
|
|
48225a077b | ||
|
|
167f3c3ccd | ||
|
|
a513aad161 | ||
|
|
762795c111 | ||
|
|
916a085d79 | ||
|
|
6860c56f42 | ||
|
|
955eae5d65 | ||
|
|
ea7c74f33b | ||
|
|
b27fdac0f9 | ||
|
|
92204adbaa | ||
|
|
12ce802770 | ||
|
|
96732306a8 | ||
|
|
2e7c6b4cb7 | ||
|
|
f720b7d752 | ||
|
|
117a9d859d | ||
|
|
de8b7c368d | ||
|
|
d89dbcd58b | ||
|
|
a8ab9d8b1c | ||
|
|
ade31455b7 | ||
|
|
53b4c6b83f | ||
|
|
181f24cfcc | ||
|
|
9b1abd9ec0 |
No files matched your search
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
# Node.js
|
# Node.js
|
||||||
**/node_modules/
|
node_modules/
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
@@ -49,7 +49,7 @@ Thumbs.db
|
|||||||
# CLI package (runs on host, not in container)
|
# CLI package (runs on host, not in container)
|
||||||
# Keep apps/cli/package.json so pnpm workspaces resolve
|
# Keep apps/cli/package.json so pnpm workspaces resolve
|
||||||
apps/cli/src/
|
apps/cli/src/
|
||||||
**/dist/
|
apps/cli/dist/
|
||||||
apps/cli/infra/
|
apps/cli/infra/
|
||||||
apps/cli/tsconfig.json
|
apps/cli/tsconfig.json
|
||||||
apps/cli/tsdown.config.ts
|
apps/cli/tsdown.config.ts
|
||||||
|
|||||||
+23
-3
@@ -5,7 +5,7 @@
|
|||||||
CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000
|
CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# OPTION 1: Direct Anthropic
|
# OPTION 1: Direct Anthropic (default, no router)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
ANTHROPIC_API_KEY=your-api-key-here
|
ANTHROPIC_API_KEY=your-api-key-here
|
||||||
|
|
||||||
@@ -19,6 +19,20 @@ ANTHROPIC_API_KEY=your-api-key-here
|
|||||||
# ANTHROPIC_BASE_URL=https://your-proxy.example.com
|
# ANTHROPIC_BASE_URL=https://your-proxy.example.com
|
||||||
# ANTHROPIC_AUTH_TOKEN=your-auth-token # Auth token for the custom endpoint
|
# ANTHROPIC_AUTH_TOKEN=your-auth-token # Auth token for the custom endpoint
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTION 3: Router Mode (use alternative providers)
|
||||||
|
# =============================================================================
|
||||||
|
# Enable router mode by running: ./shannon start ... ROUTER=true
|
||||||
|
# Then configure ONE of the providers below:
|
||||||
|
|
||||||
|
# --- OpenAI ---
|
||||||
|
# OPENAI_API_KEY=sk-your-openai-key
|
||||||
|
# ROUTER_DEFAULT=openai,gpt-5.2
|
||||||
|
|
||||||
|
# --- OpenRouter (access Gemini 3 models via single API) ---
|
||||||
|
# OPENROUTER_API_KEY=sk-or-your-openrouter-key
|
||||||
|
# ROUTER_DEFAULT=openrouter,google/gemini-3-flash-preview
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Model Tier Overrides (Anthropic API / OAuth / Custom Base URL / Bedrock)
|
# Model Tier Overrides (Anthropic API / OAuth / Custom Base URL / Bedrock)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -29,7 +43,7 @@ ANTHROPIC_API_KEY=your-api-key-here
|
|||||||
# ANTHROPIC_LARGE_MODEL=... # Large tier (default: claude-opus-4-6)
|
# ANTHROPIC_LARGE_MODEL=... # Large tier (default: claude-opus-4-6)
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# OPTION 3: AWS Bedrock
|
# OPTION 4: AWS Bedrock
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# https://aws.amazon.com/blogs/machine-learning/accelerate-ai-development-with-amazon-bedrock-api-keys/
|
# https://aws.amazon.com/blogs/machine-learning/accelerate-ai-development-with-amazon-bedrock-api-keys/
|
||||||
# Requires the model tier overrides above to be set with Bedrock-specific model IDs.
|
# Requires the model tier overrides above to be set with Bedrock-specific model IDs.
|
||||||
@@ -43,7 +57,7 @@ ANTHROPIC_API_KEY=your-api-key-here
|
|||||||
# AWS_BEARER_TOKEN_BEDROCK=your-bearer-token
|
# AWS_BEARER_TOKEN_BEDROCK=your-bearer-token
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# OPTION 4: Google Vertex AI
|
# OPTION 5: Google Vertex AI
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models
|
# https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models
|
||||||
# Requires a GCP service account with roles/aiplatform.user.
|
# Requires a GCP service account with roles/aiplatform.user.
|
||||||
@@ -58,3 +72,9 @@ ANTHROPIC_API_KEY=your-api-key-here
|
|||||||
# CLOUD_ML_REGION=us-east5
|
# CLOUD_ML_REGION=us-east5
|
||||||
# ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
|
# ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
|
||||||
# GOOGLE_APPLICATION_CREDENTIALS=./credentials/google-sa-key.json
|
# GOOGLE_APPLICATION_CREDENTIALS=./credentials/google-sa-key.json
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Available Models
|
||||||
|
# =============================================================================
|
||||||
|
# OpenAI: gpt-5.2, gpt-5-mini
|
||||||
|
# OpenRouter: google/gemini-3-flash-preview
|
||||||
@@ -1 +0,0 @@
|
|||||||
*.sh text eol=lf
|
|
||||||
@@ -55,7 +55,7 @@ body:
|
|||||||
label: If applicable
|
label: If applicable
|
||||||
options:
|
options:
|
||||||
- label: I have included relevant error messages, stack traces, or failure details.
|
- label: I have included relevant error messages, stack traces, or failure details.
|
||||||
- label: I have checked the workspaces folder for logs and pasted the relevant errors.
|
- label: I have checked the audit logs and pasted the relevant errors.
|
||||||
- label: I have inspected the failed Temporal workflow run and included the failure reason.
|
- label: I have inspected the failed Temporal workflow run and included the failure reason.
|
||||||
- label: I have included clear steps to reproduce the issue.
|
- label: I have included clear steps to reproduce the issue.
|
||||||
- label: I have redacted any sensitive information (tokens, URLs, repo names).
|
- label: I have redacted any sensitive information (tokens, URLs, repo names).
|
||||||
@@ -69,9 +69,7 @@ body:
|
|||||||
|
|
||||||
Issues without this information may be difficult to triage.
|
Issues without this information may be difficult to triage.
|
||||||
|
|
||||||
- Check the workflow log:
|
- Check the logs at: `./workspaces/target_url_shannon-123/workflow.log`
|
||||||
- **npx mode:** `~/.shannon/workspaces/<workspace>/workflow.log`
|
|
||||||
- **Local mode:** `./workspaces/<workspace>/workflow.log`
|
|
||||||
Use `grep` or search to identify errors.
|
Use `grep` or search to identify errors.
|
||||||
Paste the relevant error output below.
|
Paste the relevant error output below.
|
||||||
- Temporal:
|
- Temporal:
|
||||||
@@ -85,13 +83,13 @@ body:
|
|||||||
id: debugging-details
|
id: debugging-details
|
||||||
attributes:
|
attributes:
|
||||||
label: Debugging details
|
label: Debugging details
|
||||||
description: Paste any error messages, stack traces, or failure details from the workspace logs or Temporal UI.
|
description: Paste any error messages, stack traces, or failure details from the audit logs or Temporal UI.
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: screenshots
|
id: screenshots
|
||||||
attributes:
|
attributes:
|
||||||
label: Screenshots
|
label: Screenshots
|
||||||
description: If applicable, add screenshots of the workspace logs or Temporal failure details.
|
description: If applicable, add screenshots of the audit logs or Temporal failure details.
|
||||||
|
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
@@ -101,36 +99,35 @@ body:
|
|||||||
Provide the following information (redact sensitive data such as repository names, URLs, and tokens):
|
Provide the following information (redact sensitive data such as repository names, URLs, and tokens):
|
||||||
|
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
id: cli-mode
|
id: auth-method
|
||||||
attributes:
|
attributes:
|
||||||
label: CLI mode
|
label: Authentication method used
|
||||||
options:
|
options:
|
||||||
- "npx (@keygraph/shannon)"
|
- CLAUDE_CODE_OAUTH_TOKEN
|
||||||
- "Local (./shannon)"
|
- ANTHROPIC_API_KEY
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: provider
|
|
||||||
attributes:
|
|
||||||
label: Provider
|
|
||||||
options:
|
|
||||||
- "Anthropic (API key)"
|
|
||||||
- "Anthropic (OAuth token)"
|
|
||||||
- "Custom base URL (proxy/gateway)"
|
|
||||||
- "AWS Bedrock"
|
|
||||||
- "Google Vertex AI"
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: shannon-command
|
id: shannon-command
|
||||||
attributes:
|
attributes:
|
||||||
label: Full command with all flags used (with redactions)
|
label: Full ./shannon command with all flags used (with redactions)
|
||||||
placeholder: "e.g. npx @keygraph/shannon start -u <url> -r my-repo OR ./shannon start -u <url> -r my-repo"
|
|
||||||
|
- type: dropdown
|
||||||
|
id: experimental-models
|
||||||
|
attributes:
|
||||||
|
label: Are you using any experimental models or providers other than default Anthropic models?
|
||||||
|
options:
|
||||||
|
- "No"
|
||||||
|
- "Yes"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: experimental-model-details
|
||||||
|
attributes:
|
||||||
|
label: If Yes, which one (model/provider)?
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: os-version
|
id: os-version
|
||||||
attributes:
|
attributes:
|
||||||
@@ -139,14 +136,6 @@ body:
|
|||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: node-version
|
|
||||||
attributes:
|
|
||||||
label: "Node.js version ('node -v')"
|
|
||||||
placeholder: "e.g. 22.12.0"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: docker-version
|
id: docker-version
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -20,15 +20,6 @@ body:
|
|||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: cli-mode
|
|
||||||
attributes:
|
|
||||||
label: Which CLI mode does this apply to?
|
|
||||||
options:
|
|
||||||
- Both
|
|
||||||
- "npx (@keygraph/shannon)"
|
|
||||||
- "Local (./shannon)"
|
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: alternatives-considered
|
id: alternatives-considered
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
auto-install-peers=true
|
auto-install-peers=true
|
||||||
strict-peer-dependencies=false
|
strict-peer-dependencies=false
|
||||||
minimum-release-age=10080
|
|
||||||
ignore-scripts=true
|
|
||||||
@@ -82,7 +82,7 @@ pnpm biome:fix # Auto-fix lint, format, and import sorting
|
|||||||
|
|
||||||
**Monorepo tooling:** pnpm workspaces, Turborepo for task orchestration, Biome for linting/formatting. TypeScript compiler options shared via `tsconfig.base.json` at the root. All packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`, `turbo`, `@biomejs/biome`) are hoisted to the root workspace.
|
**Monorepo tooling:** pnpm workspaces, Turborepo for task orchestration, Biome for linting/formatting. TypeScript compiler options shared via `tsconfig.base.json` at the root. All packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`, `turbo`, `@biomejs/biome`) are hoisted to the root workspace.
|
||||||
|
|
||||||
**Options:** `-c <file>` (YAML config), `-o <path>` (output directory), `-w <name>` (named workspace; auto-resumes if exists), `--pipeline-testing` (minimal prompts, 10s retries), `--debug` (preserve worker container after exit for log inspection)
|
**Options:** `-c <file>` (YAML config), `-o <path>` (output directory), `-w <name>` (named workspace; auto-resumes if exists), `--pipeline-testing` (minimal prompts, 10s retries), `--router` (multi-model routing via [claude-code-router](https://github.com/musistudio/claude-code-router))
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
@@ -106,14 +106,14 @@ Published as `@keygraph/shannon` on npm. Contains only Docker orchestration logi
|
|||||||
- `apps/cli/src/commands/setup.ts` — Interactive TUI wizard (`@clack/prompts`) for provider credential setup (npx only)
|
- `apps/cli/src/commands/setup.ts` — Interactive TUI wizard (`@clack/prompts`) for provider credential setup (npx only)
|
||||||
- `apps/cli/src/paths.ts` — Repo/config path resolution (bare name → `./repos/<name>`, or any absolute/relative path)
|
- `apps/cli/src/paths.ts` — Repo/config path resolution (bare name → `./repos/<name>`, or any absolute/relative path)
|
||||||
- `apps/cli/src/commands/` — Command handlers
|
- `apps/cli/src/commands/` — Command handlers
|
||||||
- `apps/cli/infra/compose.yml` — Bundled Temporal compose file for npx mode
|
- `apps/cli/infra/compose.yml` — Bundled Temporal + router compose file for npx mode
|
||||||
- `apps/cli/tsdown.config.ts` — tsdown bundler config
|
- `apps/cli/tsdown.config.ts` — tsdown bundler config
|
||||||
- `shannon` — Node.js entry point (`#!/usr/bin/env node`) that delegates to `apps/cli/dist/index.mjs`
|
- `shannon` — Node.js entry point (`#!/usr/bin/env node`) that delegates to `apps/cli/dist/index.mjs`
|
||||||
|
|
||||||
### Docker Architecture
|
### Docker Architecture
|
||||||
Infra (Temporal) runs via `docker-compose.yml`. Workers are ephemeral `docker run --rm` containers, one per scan, each with a unique task queue and isolated volume mounts.
|
Infra (Temporal + router) runs via `docker-compose.yml`. Workers are ephemeral `docker run --rm` containers, one per scan, each with a unique task queue and isolated volume mounts.
|
||||||
|
|
||||||
- `docker-compose.yml` — Infra only: `shannon-temporal` (port 7233/8233). Network: `shannon-net`
|
- `docker-compose.yml` — Infra only: `shannon-temporal` (port 7233/8233) and `shannon-router` (port 3456, optional via profile). Network: `shannon-net`
|
||||||
- `Dockerfile` — 2-stage build (builder + Chainguard Wolfi runtime). Uses pnpm. Entrypoint: `CMD ["node", "apps/worker/dist/temporal/worker.js"]`
|
- `Dockerfile` — 2-stage build (builder + Chainguard Wolfi runtime). Uses pnpm. Entrypoint: `CMD ["node", "apps/worker/dist/temporal/worker.js"]`
|
||||||
- No `docker-compose.docker.yml` — host gateway handled via `--add-host` flag in CLI
|
- No `docker-compose.docker.yml` — host gateway handled via `--add-host` flag in CLI
|
||||||
|
|
||||||
@@ -233,10 +233,6 @@ Comments must be **timeless** — no references to this conversation, refactorin
|
|||||||
|
|
||||||
**CI/CD:** `.github/workflows/release.yml` (Docker Hub push + npm publish + GitHub release, manual dispatch)
|
**CI/CD:** `.github/workflows/release.yml` (Docker Hub push + npm publish + GitHub release, manual dispatch)
|
||||||
|
|
||||||
## Package Installation
|
|
||||||
|
|
||||||
Package managers are configured with a minimum release age (7 days). Requires pnpm >= 10.16.0. If `pnpm install` fails due to a package being too new, **do not attempt to bypass it** — report the blocked package to the user and stop.
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
- **"Repository not found"** — Pass a bare name (`-r my-repo`) for `./repos/my-repo`, or a path (`-r /path/to/repo`) for any directory
|
- **"Repository not found"** — Pass a bare name (`-r my-repo`) for `./repos/my-repo`, or a path (`-r /path/to/repo`) for any directory
|
||||||
|
|||||||
+3
-5
@@ -52,7 +52,7 @@ RUN curl -sL https://github.com/urbanadventurer/WhatWeb/archive/refs/tags/v0.6.3
|
|||||||
RUN pip3 install --no-cache-dir schemathesis==4.13.0
|
RUN pip3 install --no-cache-dir schemathesis==4.13.0
|
||||||
|
|
||||||
# Install pnpm
|
# Install pnpm
|
||||||
RUN npm install -g pnpm@10.33.0
|
RUN npm install -g pnpm@10.12.1
|
||||||
|
|
||||||
# Build Node.js application in builder to avoid QEMU emulation failures in CI
|
# Build Node.js application in builder to avoid QEMU emulation failures in CI
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -69,8 +69,7 @@ COPY . .
|
|||||||
# Build worker. CLI not needed in Docker
|
# Build worker. CLI not needed in Docker
|
||||||
RUN pnpm --filter @shannon/worker run build
|
RUN pnpm --filter @shannon/worker run build
|
||||||
|
|
||||||
# Production-only deps (pnpm recommends install --prod over prune in monorepos)
|
RUN pnpm prune --prod
|
||||||
RUN rm -rf node_modules apps/*/node_modules && pnpm install --frozen-lockfile --prod
|
|
||||||
|
|
||||||
# Runtime stage - Minimal production image
|
# Runtime stage - Minimal production image
|
||||||
FROM cgr.dev/chainguard/wolfi-base:latest AS runtime
|
FROM cgr.dev/chainguard/wolfi-base:latest AS runtime
|
||||||
@@ -83,7 +82,6 @@ RUN apk update && apk add --no-cache \
|
|||||||
bash \
|
bash \
|
||||||
curl \
|
curl \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
shadow \
|
|
||||||
# Network libraries (runtime)
|
# Network libraries (runtime)
|
||||||
libpcap \
|
libpcap \
|
||||||
# Security tools
|
# Security tools
|
||||||
@@ -155,7 +153,7 @@ RUN ln -s /app/apps/worker/dist/scripts/save-deliverable.js /usr/local/bin/save-
|
|||||||
chmod +x /app/apps/worker/dist/scripts/generate-totp.js
|
chmod +x /app/apps/worker/dist/scripts/generate-totp.js
|
||||||
|
|
||||||
# Create directories for session data and ensure proper permissions
|
# Create directories for session data and ensure proper permissions
|
||||||
RUN mkdir -p /app/sessions /app/repos /app/workspaces && \
|
RUN mkdir -p /app/sessions /app/deliverables /app/repos /app/workspaces && \
|
||||||
mkdir -p /tmp/.cache /tmp/.config /tmp/.npm && \
|
mkdir -p /tmp/.cache /tmp/.config /tmp/.npm && \
|
||||||
chmod 777 /app && \
|
chmod 777 /app && \
|
||||||
chmod 777 /tmp/.cache && \
|
chmod 777 /tmp/.cache && \
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
>[!NOTE]
|
>[!NOTE]
|
||||||
> **[📢 Sunsetting Router Mode (claude-code-router)`. →](https://github.com/KeygraphHQ/shannon/discussions/301)**
|
> **[📢 New: Shannon is now available via `npx @keygraph/shannon`. →](https://github.com/KeygraphHQ/shannon/discussions/249)**
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
@@ -118,6 +118,7 @@ Shannon Pro supports a self-hosted runner model (similar to GitHub Actions self-
|
|||||||
- [AWS Bedrock](#aws-bedrock)
|
- [AWS Bedrock](#aws-bedrock)
|
||||||
- [Google Vertex AI](#google-vertex-ai)
|
- [Google Vertex AI](#google-vertex-ai)
|
||||||
- [Custom Base URL](#custom-base-url)
|
- [Custom Base URL](#custom-base-url)
|
||||||
|
- [Router Mode](#experimental---unsupported-router-mode-alternative-providers)
|
||||||
- [Platform-Specific Instructions](#platform-specific-instructions)
|
- [Platform-Specific Instructions](#platform-specific-instructions)
|
||||||
- [Output and Results](#output-and-results)
|
- [Output and Results](#output-and-results)
|
||||||
- [Sample Reports](#sample-reports)
|
- [Sample Reports](#sample-reports)
|
||||||
@@ -143,15 +144,13 @@ Shannon Pro supports a self-hosted runner model (similar to GitHub Actions self-
|
|||||||
- **Claude Code OAuth token**
|
- **Claude Code OAuth token**
|
||||||
- **AWS Bedrock** - Route through Amazon Bedrock with AWS credentials (see [AWS Bedrock](#aws-bedrock))
|
- **AWS Bedrock** - Route through Amazon Bedrock with AWS credentials (see [AWS Bedrock](#aws-bedrock))
|
||||||
- **Google Vertex AI** - Route through Google Cloud Vertex AI (see [Google Vertex AI](#google-vertex-ai))
|
- **Google Vertex AI** - Route through Google Cloud Vertex AI (see [Google Vertex AI](#google-vertex-ai))
|
||||||
|
- **[EXPERIMENTAL - UNSUPPORTED] Alternative providers via Router Mode** - OpenAI or Google Gemini via OpenRouter (see [Router Mode](#experimental---unsupported-router-mode-alternative-providers))
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Docker is still required to use the `npx` workflow. Under the hood, the CLI pulls and runs a prebuilt Shannon worker image from Docker Hub, which is approximately 1 GB and contains Shannon plus all required dependencies. Shannon mounts the target repository as read-only inside the worker container to protect against accidental modifications during analysis. Run Shannon via `npx @keygraph/shannon` for the latest released version, or pull the latest `main` if building from source.
|
> Docker is still required to use the `npx` workflow. Under the hood, the CLI pulls and runs a prebuilt Shannon worker image from Docker Hub, which is approximately 1 GB and contains Shannon plus all required dependencies.
|
||||||
|
|
||||||
### Quick Start (Recommended: npx)
|
### Quick Start (Recommended: npx)
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> **Please read the [Disclaimers](#disclaimers) before running Shannon.** Shannon is **not** a passive scanner — it actively executes exploits against the target. You must have **explicit, written authorization** from the system owner.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Configure credentials (interactive wizard — one-time setup)
|
# 1. Configure credentials (interactive wizard — one-time setup)
|
||||||
npx @keygraph/shannon setup
|
npx @keygraph/shannon setup
|
||||||
@@ -513,12 +512,7 @@ Set `CLOUD_ML_REGION=global` for global endpoints, or a specific region like `us
|
|||||||
|
|
||||||
### Custom Base URL
|
### Custom Base URL
|
||||||
|
|
||||||
Shannon supports pointing the SDK at any Anthropic-compatible endpoint via `ANTHROPIC_BASE_URL`. For users who need proxy-based routing, the supported path is to use an LLM proxy such as [LiteLLM](https://github.com/BerriAI/litellm) configured to expose an Anthropic-compatible endpoint.
|
Shannon supports pointing the SDK at any Anthropic-compatible endpoint (proxies, gateways, etc.) via `ANTHROPIC_BASE_URL`.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> **Only Claude models are officially supported.** Shannon's evaluations, internal testing, and agent harness are all optimized for Claude. Smaller or alternative models — including non-Claude models routed through a proxy — may not reliably follow Shannon's instructions or tool-use constraints, and are not officially supported. Use them at your own risk; results may be incomplete, inaccurate, or unstable.
|
|
||||||
>
|
|
||||||
> The previously experimental `claude-code-router` integration is being removed in an upcoming release. If you currently rely on it, migrate to an Anthropic-compatible proxy such as LiteLLM before upgrading.
|
|
||||||
|
|
||||||
Run `npx @keygraph/shannon setup` and select **Custom Base URL**. The wizard will prompt for your endpoint URL, auth token, and optionally let you override the default model tiers.
|
Run `npx @keygraph/shannon setup` and select **Custom Base URL**. The wizard will prompt for your endpoint URL, auth token, and optionally let you override the default model tiers.
|
||||||
|
|
||||||
@@ -547,11 +541,63 @@ ANTHROPIC_LARGE_MODEL=claude-opus-4-6
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### [EXPERIMENTAL - UNSUPPORTED] Router Mode (Alternative Providers)
|
||||||
|
|
||||||
|
Shannon can experimentally route requests through alternative AI providers using claude-code-router. This mode is not officially supported and is intended primarily for:
|
||||||
|
|
||||||
|
- **Model experimentation** — try Shannon with GPT-5.2 or Gemini 3-family models
|
||||||
|
|
||||||
|
#### Quick Setup
|
||||||
|
|
||||||
|
Run `npx @keygraph/shannon setup` and select **Router**. The wizard will prompt you to choose a provider (OpenAI or OpenRouter), enter your API key, and select a default model.
|
||||||
|
|
||||||
|
Or export env vars directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export OPENAI_API_KEY=sk-... # or OPENROUTER_API_KEY=sk-or-...
|
||||||
|
export ROUTER_DEFAULT=openai,gpt-5.2 # provider,model format
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @keygraph/shannon start -u https://example.com -r /path/to/repo --router
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Clone and Build: add to .env and run with --router</summary>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OPENAI_API_KEY=sk-...
|
||||||
|
# OR
|
||||||
|
OPENROUTER_API_KEY=sk-or-...
|
||||||
|
ROUTER_DEFAULT=openai,gpt-5.2
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./shannon start -u https://example.com -r /path/to/repo --router
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
#### Experimental Models
|
||||||
|
|
||||||
|
| Provider | Models |
|
||||||
|
|----------|--------|
|
||||||
|
| OpenAI | gpt-5.2, gpt-5-mini |
|
||||||
|
| OpenRouter | google/gemini-3-flash-preview |
|
||||||
|
|
||||||
|
#### Disclaimer
|
||||||
|
|
||||||
|
This feature is experimental and unsupported. Output quality depends heavily on the model. Shannon is built on top of the Anthropic Agent SDK and is optimized and primarily tested with Anthropic Claude models. Alternative providers may produce inconsistent results (including failing early phases like Recon) depending on the model and routing setup.
|
||||||
|
|
||||||
### Platform-Specific Instructions
|
### Platform-Specific Instructions
|
||||||
|
|
||||||
**For Windows:**
|
**For Windows:**
|
||||||
|
|
||||||
Shannon on Windows is only supported via **WSL2**. Native Windows (including Git Bash) is not supported.
|
*Native (Git Bash):*
|
||||||
|
|
||||||
|
Install [Git for Windows](https://git-scm.com/install/windows) and run Shannon from **Git Bash** with Docker Desktop installed. Both `npx @keygraph/shannon` and local clone mode are supported.
|
||||||
|
|
||||||
|
*WSL2 (Recommended):*
|
||||||
|
|
||||||
**Step 1: Ensure WSL 2**
|
**Step 1: Ensure WSL 2**
|
||||||
|
|
||||||
@@ -794,7 +840,6 @@ This is not a passive scanner. The exploitation agents are designed to **activel
|
|||||||
>
|
>
|
||||||
> - It is intended exclusively for use on sandboxed, staging, or local development environments where data integrity is not a concern.
|
> - It is intended exclusively for use on sandboxed, staging, or local development environments where data integrity is not a concern.
|
||||||
> - Potential mutative effects include, but are not limited to: creating new users, modifying or deleting data, compromising test accounts, and triggering unintended side effects from injection attacks.
|
> - Potential mutative effects include, but are not limited to: creating new users, modifying or deleting data, compromising test accounts, and triggering unintended side effects from injection attacks.
|
||||||
> - **For maximum security and isolation, run Shannon inside a virtual machine (VM).** This confines any side effects from exploitation — including unexpected outbound traffic, file writes from agent tooling, or interactions with local services — to a disposable environment.
|
|
||||||
|
|
||||||
#### **2. Legal & Ethical Use**
|
#### **2. Legal & Ethical Use**
|
||||||
|
|
||||||
@@ -808,7 +853,6 @@ Shannon is designed for legitimate security auditing purposes only.
|
|||||||
#### **3. LLM & Automation Caveats**
|
#### **3. LLM & Automation Caveats**
|
||||||
|
|
||||||
- **Verification is Required**: While significant engineering has gone into our "proof-by-exploitation" methodology to eliminate false positives, the underlying LLMs can still generate hallucinated or weakly-supported content in the final report. **Human oversight is essential** to validate the legitimacy and severity of all reported findings.
|
- **Verification is Required**: While significant engineering has gone into our "proof-by-exploitation" methodology to eliminate false positives, the underlying LLMs can still generate hallucinated or weakly-supported content in the final report. **Human oversight is essential** to validate the legitimacy and severity of all reported findings.
|
||||||
- **Model Support**: Shannon is officially supported only with **Claude models**. Our evaluations, internal testing, and agent harness are all optimized for Claude. Smaller or alternative models — including non-Claude models routed through a proxy — may not reliably follow Shannon's instructions or tool-use constraints, and are not officially supported.
|
|
||||||
- **Comprehensiveness**: The analysis in Shannon Lite may not be exhaustive due to the inherent limitations of LLM context windows. For a more comprehensive, graph-based analysis of your entire codebase, **Shannon Pro** leverages its advanced data flow analysis engine to ensure deeper and more thorough coverage.
|
- **Comprehensiveness**: The analysis in Shannon Lite may not be exhaustive due to the inherent limitations of LLM context windows. For a more comprehensive, graph-based analysis of your entire codebase, **Shannon Pro** leverages its advanced data flow analysis engine to ensure deeper and more thorough coverage.
|
||||||
|
|
||||||
#### **4. Scope of Analysis**
|
#### **4. Scope of Analysis**
|
||||||
@@ -877,8 +921,8 @@ Issues are welcome for bug reports and feature requests.
|
|||||||
Shannon Pro is Keygraph's all-in-one AppSec platform. For organizations that need unified SAST, SCA, and autonomous pentesting with static-dynamic correlation, CI/CD integration, or self-hosted deployment, see the [Shannon Pro technical overview](./SHANNON-PRO.md).
|
Shannon Pro is Keygraph's all-in-one AppSec platform. For organizations that need unified SAST, SCA, and autonomous pentesting with static-dynamic correlation, CI/CD integration, or self-hosted deployment, see the [Shannon Pro technical overview](./SHANNON-PRO.md).
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://cal.com/team/keygraph/shannon-pro" target="_blank">
|
<a href="https://docs.google.com/forms/d/e/1FAIpQLSf-cPZcWjlfBJ3TCT8AaWpf8ztsw3FaHzJE4urr55KdlQs6cQ/viewform?usp=header" target="_blank">
|
||||||
<img src="./assets/Demo_Button.png" height="40" alt="Shannon Pro Inquiry">
|
<img src="https://img.shields.io/badge/Shannon%20Pro%20Inquiry-4285F4?style=for-the-badge&logo=google&logoColor=white" alt="Shannon Pro Inquiry">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -19,5 +19,32 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
||||||
|
router:
|
||||||
|
image: node:20-slim
|
||||||
|
container_name: shannon-router
|
||||||
|
profiles: ["router"]
|
||||||
|
command: >
|
||||||
|
sh -c "apt-get update && apt-get install -y gettext-base &&
|
||||||
|
npm install -g @musistudio/claude-code-router &&
|
||||||
|
mkdir -p /root/.claude-code-router &&
|
||||||
|
envsubst < /config/router-config.json > /root/.claude-code-router/config.json &&
|
||||||
|
ccr start"
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3456:3456"
|
||||||
|
volumes:
|
||||||
|
- ./router-config.json:/config/router-config.json:ro
|
||||||
|
environment:
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
|
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||||
|
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
||||||
|
- ROUTER_DEFAULT=${ROUTER_DEFAULT:-openai,gpt-4o}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3456/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
temporal-data:
|
temporal-data:
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"HOST": "0.0.0.0",
|
||||||
|
"APIKEY": "shannon-router-key",
|
||||||
|
"LOG": true,
|
||||||
|
"LOG_LEVEL": "info",
|
||||||
|
"NON_INTERACTIVE_MODE": true,
|
||||||
|
"API_TIMEOUT_MS": 600000,
|
||||||
|
"Providers": [
|
||||||
|
{
|
||||||
|
"name": "openai",
|
||||||
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
|
"api_key": "$OPENAI_API_KEY",
|
||||||
|
"models": ["gpt-5.2", "gpt-5-mini"],
|
||||||
|
"transformer": {
|
||||||
|
"use": [["maxcompletiontokens", { "max_completion_tokens": 16384 }]]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "openrouter",
|
||||||
|
"api_base_url": "https://openrouter.ai/api/v1/chat/completions",
|
||||||
|
"api_key": "$OPENROUTER_API_KEY",
|
||||||
|
"models": ["google/gemini-3-flash-preview"],
|
||||||
|
"transformer": {
|
||||||
|
"use": ["openrouter"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Router": {
|
||||||
|
"default": "$ROUTER_DEFAULT"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ import { type ShannonConfig, saveConfig } from '../config/writer.js';
|
|||||||
|
|
||||||
const SHANNON_HOME = path.join(os.homedir(), '.shannon');
|
const SHANNON_HOME = path.join(os.homedir(), '.shannon');
|
||||||
|
|
||||||
type Provider = 'anthropic' | 'custom_base_url' | 'bedrock' | 'vertex';
|
type Provider = 'anthropic' | 'custom_base_url' | 'bedrock' | 'vertex' | 'router';
|
||||||
|
|
||||||
export async function setup(): Promise<void> {
|
export async function setup(): Promise<void> {
|
||||||
p.intro('Shannon Setup');
|
p.intro('Shannon Setup');
|
||||||
@@ -26,6 +26,7 @@ export async function setup(): Promise<void> {
|
|||||||
{ value: 'custom_base_url' as const, label: 'Custom Base URL', hint: 'proxies, gateways' },
|
{ value: 'custom_base_url' as const, label: 'Custom Base URL', hint: 'proxies, gateways' },
|
||||||
{ value: 'bedrock' as const, label: 'Claude via AWS Bedrock' },
|
{ value: 'bedrock' as const, label: 'Claude via AWS Bedrock' },
|
||||||
{ value: 'vertex' as const, label: 'Claude via Google Vertex AI' },
|
{ value: 'vertex' as const, label: 'Claude via Google Vertex AI' },
|
||||||
|
{ value: 'router' as const, label: 'Router', hint: 'experimental' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
if (p.isCancel(provider)) return cancelAndExit();
|
if (p.isCancel(provider)) return cancelAndExit();
|
||||||
@@ -50,6 +51,8 @@ async function setupProvider(provider: Provider): Promise<ShannonConfig> {
|
|||||||
return setupBedrock();
|
return setupBedrock();
|
||||||
case 'vertex':
|
case 'vertex':
|
||||||
return setupVertex();
|
return setupVertex();
|
||||||
|
case 'router':
|
||||||
|
return setupRouter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +282,50 @@ async function setupVertex(): Promise<ShannonConfig> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setupRouter(): Promise<ShannonConfig> {
|
||||||
|
const routerProvider = await p.select({
|
||||||
|
message: 'Router provider',
|
||||||
|
options: [
|
||||||
|
{ value: 'openai' as const, label: 'OpenAI' },
|
||||||
|
{ value: 'openrouter' as const, label: 'OpenRouter' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (p.isCancel(routerProvider)) return cancelAndExit();
|
||||||
|
|
||||||
|
const apiKey = await promptSecret(
|
||||||
|
routerProvider === 'openai' ? 'Enter your OpenAI API key' : 'Enter your OpenRouter API key',
|
||||||
|
);
|
||||||
|
|
||||||
|
let defaultModel: string;
|
||||||
|
if (routerProvider === 'openai') {
|
||||||
|
const model = await p.select({
|
||||||
|
message: 'Default model',
|
||||||
|
options: [
|
||||||
|
{ value: 'gpt-5.2' as const, label: 'GPT-5.2' },
|
||||||
|
{ value: 'gpt-5-mini' as const, label: 'GPT-5 Mini' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (p.isCancel(model)) return cancelAndExit();
|
||||||
|
defaultModel = `openai,${model}`;
|
||||||
|
} else {
|
||||||
|
const model = await p.select({
|
||||||
|
message: 'Default model',
|
||||||
|
options: [{ value: 'google/gemini-3-flash-preview' as const, label: 'Google Gemini 3 Flash Preview' }],
|
||||||
|
});
|
||||||
|
if (p.isCancel(model)) return cancelAndExit();
|
||||||
|
defaultModel = `openrouter,${model}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const router: ShannonConfig['router'] = { default: defaultModel };
|
||||||
|
if (routerProvider === 'openai') {
|
||||||
|
router.openai_key = apiKey;
|
||||||
|
} else {
|
||||||
|
router.openrouter_key = apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { router };
|
||||||
|
}
|
||||||
|
|
||||||
// === Helpers ===
|
// === Helpers ===
|
||||||
|
|
||||||
async function promptSecret(message: string): Promise<string> {
|
async function promptSecret(message: string): Promise<string> {
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import { execFileSync } from 'node:child_process';
|
|||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { ensureImage, ensureInfra, randomSuffix, spawnWorker } from '../docker.js';
|
import { ensureImage, ensureInfra, randomSuffix, spawnWorker } from '../docker.js';
|
||||||
import { buildEnvFlags, loadEnv, validateCredentials } from '../env.js';
|
import { buildEnvFlags, isRouterConfigured, loadEnv, validateCredentials } from '../env.js';
|
||||||
import { getCredentialsPath, getWorkspacesDir, initHome } from '../home.js';
|
import { getCredentialsPath, getWorkspacesDir, initHome } from '../home.js';
|
||||||
import { isLocal } from '../mode.js';
|
import { isLocal } from '../mode.js';
|
||||||
import { resolveConfig, resolveRepo } from '../paths.js';
|
import { ensureDeliverables, resolveConfig, resolveRepo } from '../paths.js';
|
||||||
import { displaySplash } from '../splash.js';
|
import { displaySplash } from '../splash.js';
|
||||||
|
|
||||||
export interface StartArgs {
|
export interface StartArgs {
|
||||||
@@ -22,7 +22,7 @@ export interface StartArgs {
|
|||||||
workspace?: string;
|
workspace?: string;
|
||||||
output?: string;
|
output?: string;
|
||||||
pipelineTesting: boolean;
|
pipelineTesting: boolean;
|
||||||
debug: boolean;
|
router: boolean;
|
||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,52 +31,44 @@ export async function start(args: StartArgs): Promise<void> {
|
|||||||
initHome();
|
initHome();
|
||||||
loadEnv();
|
loadEnv();
|
||||||
|
|
||||||
// 2. Validate credentials
|
// 2. Validate credentials and auto-detect router mode
|
||||||
const creds = validateCredentials();
|
const creds = validateCredentials();
|
||||||
if (!creds.valid) {
|
if (!creds.valid) {
|
||||||
console.error(`ERROR: ${creds.error}`);
|
console.error(`ERROR: ${creds.error}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
const useRouter = args.router || isRouterConfigured();
|
||||||
|
|
||||||
// 3. Resolve paths
|
// 3. Resolve paths
|
||||||
const repo = resolveRepo(args.repo);
|
const repo = resolveRepo(args.repo);
|
||||||
const config = args.config ? resolveConfig(args.config) : undefined;
|
const config = args.config ? resolveConfig(args.config) : undefined;
|
||||||
|
ensureDeliverables(repo.hostPath);
|
||||||
|
|
||||||
// 4. Ensure workspaces dir is writable by container user (UID 1001)
|
// 4. Ensure workspaces dir is writable by container user (UID 1001)
|
||||||
const workspacesDir = getWorkspacesDir();
|
const workspacesDir = getWorkspacesDir();
|
||||||
fs.mkdirSync(workspacesDir, { recursive: true });
|
fs.mkdirSync(workspacesDir, { recursive: true });
|
||||||
fs.chmodSync(workspacesDir, 0o777);
|
fs.chmodSync(workspacesDir, 0o777);
|
||||||
|
|
||||||
// 5. Ensure image (auto-build in dev, pull in npx) and start infra
|
// 5. Handle router env
|
||||||
ensureImage(args.version);
|
if (useRouter) {
|
||||||
await ensureInfra();
|
process.env.ANTHROPIC_BASE_URL = 'http://shannon-router:3456';
|
||||||
|
process.env.ANTHROPIC_AUTH_TOKEN = 'shannon-router-key';
|
||||||
|
}
|
||||||
|
|
||||||
// 6. Generate unique task queue and container name
|
// 6. Ensure image (auto-build in dev, pull in npx) and start infra
|
||||||
|
ensureImage(args.version);
|
||||||
|
await ensureInfra(useRouter);
|
||||||
|
|
||||||
|
// 7. Generate unique task queue and container name
|
||||||
const suffix = randomSuffix();
|
const suffix = randomSuffix();
|
||||||
const taskQueue = `shannon-${suffix}`;
|
const taskQueue = `shannon-${suffix}`;
|
||||||
const containerName = `shannon-worker-${suffix}`;
|
const containerName = `shannon-worker-${suffix}`;
|
||||||
|
|
||||||
// 7. Generate workspace name if not provided
|
// 8. Generate workspace name if not provided
|
||||||
const workspace =
|
const workspace =
|
||||||
args.workspace ?? `${new URL(args.url).hostname.replace(/[^a-zA-Z0-9-]/g, '-')}_shannon-${Date.now()}`;
|
args.workspace ?? `${new URL(args.url).hostname.replace(/[^a-zA-Z0-9-]/g, '-')}_shannon-${Date.now()}`;
|
||||||
|
|
||||||
// 8. Create writable overlay directories (mounted over :ro repo paths inside container)
|
// 9. Resolve credentials — mount single file to fixed container path
|
||||||
// Workspace dir must be 0o777 so the container user (UID 1001) can create audit subdirs
|
|
||||||
const workspacePath = path.join(workspacesDir, workspace);
|
|
||||||
fs.mkdirSync(workspacePath, { recursive: true });
|
|
||||||
fs.chmodSync(workspacePath, 0o777);
|
|
||||||
for (const dir of ['deliverables', 'scratchpad', '.playwright-cli']) {
|
|
||||||
const dirPath = path.join(workspacePath, dir);
|
|
||||||
fs.mkdirSync(dirPath, { recursive: true });
|
|
||||||
fs.chmodSync(dirPath, 0o777);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 9. Pre-create overlay mount points (:ro mounts can't auto-create them)
|
|
||||||
const shannonDir = path.join(repo.hostPath, '.shannon');
|
|
||||||
for (const dir of ['deliverables', 'scratchpad', '.playwright-cli']) {
|
|
||||||
fs.mkdirSync(path.join(shannonDir, dir), { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const credentialsPath = getCredentialsPath();
|
const credentialsPath = getCredentialsPath();
|
||||||
const hasCredentials = fs.existsSync(credentialsPath);
|
const hasCredentials = fs.existsSync(credentialsPath);
|
||||||
|
|
||||||
@@ -109,23 +101,15 @@ export async function start(args: StartArgs): Promise<void> {
|
|||||||
...(hasCredentials && { credentials: credentialsPath }),
|
...(hasCredentials && { credentials: credentialsPath }),
|
||||||
...(promptsDir && { promptsDir }),
|
...(promptsDir && { promptsDir }),
|
||||||
...(outputDir && { outputDir }),
|
...(outputDir && { outputDir }),
|
||||||
workspace,
|
...(workspace && { workspace }),
|
||||||
...(args.pipelineTesting && { pipelineTesting: true }),
|
...(args.pipelineTesting && { pipelineTesting: true }),
|
||||||
...(args.debug && { debug: true }),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 14. Bail if `docker run -d` itself fails (mount error, image missing, etc.)
|
// 14. Wait for workflow to register, then display info
|
||||||
const dockerExitCode = await new Promise<number>((resolve) => {
|
proc.on('error', (err) => {
|
||||||
proc.once('exit', (code) => resolve(code ?? 1));
|
console.error(`Failed to start worker: ${err.message}`);
|
||||||
proc.once('error', (err) => {
|
|
||||||
console.error(`Failed to start worker: ${err.message}`);
|
|
||||||
resolve(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (dockerExitCode !== 0) {
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
});
|
||||||
|
|
||||||
// Detect whether this is a fresh workspace or a resume by checking session.json existence
|
// Detect whether this is a fresh workspace or a resume by checking session.json existence
|
||||||
const sessionJson = path.join(workspacesDir, workspace, 'session.json');
|
const sessionJson = path.join(workspacesDir, workspace, 'session.json');
|
||||||
@@ -170,7 +154,7 @@ export async function start(args: StartArgs): Promise<void> {
|
|||||||
|
|
||||||
// Clear waiting line and show info
|
// Clear waiting line and show info
|
||||||
process.stdout.write('\r\x1b[K');
|
process.stdout.write('\r\x1b[K');
|
||||||
printInfo(args, workspace, workflowId, repo.hostPath, workspacesDir);
|
printInfo(args, useRouter, workspace, workflowId, repo.hostPath, workspacesDir);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -191,9 +175,6 @@ export async function start(args: StartArgs): Promise<void> {
|
|||||||
} catch {
|
} catch {
|
||||||
// Container may have already exited
|
// Container may have already exited
|
||||||
}
|
}
|
||||||
if (args.debug) {
|
|
||||||
printDebugHint(containerName);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
@@ -207,16 +188,9 @@ export async function start(args: StartArgs): Promise<void> {
|
|||||||
process.on('exit', cleanup);
|
process.on('exit', cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
function printDebugHint(containerName: string): void {
|
|
||||||
console.log('');
|
|
||||||
console.log(` Worker container preserved: ${containerName}`);
|
|
||||||
console.log(` Inspect logs: docker logs ${containerName}`);
|
|
||||||
console.log(` Remove: docker rm ${containerName}`);
|
|
||||||
console.log('');
|
|
||||||
}
|
|
||||||
|
|
||||||
function printInfo(
|
function printInfo(
|
||||||
args: StartArgs,
|
args: StartArgs,
|
||||||
|
routerActive: boolean,
|
||||||
workspace: string,
|
workspace: string,
|
||||||
workflowId: string,
|
workflowId: string,
|
||||||
repoPath: string,
|
repoPath: string,
|
||||||
@@ -234,6 +208,9 @@ function printInfo(
|
|||||||
if (args.pipelineTesting) {
|
if (args.pipelineTesting) {
|
||||||
console.log(' Mode: Pipeline Testing');
|
console.log(' Mode: Pipeline Testing');
|
||||||
}
|
}
|
||||||
|
if (routerActive) {
|
||||||
|
console.log(' Router: Enabled');
|
||||||
|
}
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log(' Monitor:');
|
console.log(' Monitor:');
|
||||||
if (workflowId) {
|
if (workflowId) {
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ const CONFIG_MAP: readonly ConfigMapping[] = [
|
|||||||
{ env: 'ANTHROPIC_BASE_URL', toml: 'custom_base_url.base_url', type: 'string' },
|
{ env: 'ANTHROPIC_BASE_URL', toml: 'custom_base_url.base_url', type: 'string' },
|
||||||
{ env: 'ANTHROPIC_AUTH_TOKEN', toml: 'custom_base_url.auth_token', type: 'string' },
|
{ env: 'ANTHROPIC_AUTH_TOKEN', toml: 'custom_base_url.auth_token', type: 'string' },
|
||||||
|
|
||||||
|
// Router
|
||||||
|
{ env: 'ROUTER_DEFAULT', toml: 'router.default', type: 'string' },
|
||||||
|
{ env: 'OPENAI_API_KEY', toml: 'router.openai_key', type: 'string' },
|
||||||
|
{ env: 'OPENROUTER_API_KEY', toml: 'router.openrouter_key', type: 'string' },
|
||||||
|
|
||||||
// Model tiers
|
// Model tiers
|
||||||
{ env: 'ANTHROPIC_SMALL_MODEL', toml: 'models.small', type: 'string' },
|
{ env: 'ANTHROPIC_SMALL_MODEL', toml: 'models.small', type: 'string' },
|
||||||
{ env: 'ANTHROPIC_MEDIUM_MODEL', toml: 'models.medium', type: 'string' },
|
{ env: 'ANTHROPIC_MEDIUM_MODEL', toml: 'models.medium', type: 'string' },
|
||||||
@@ -160,6 +165,20 @@ function validateProviderFields(config: TOMLConfig, provider: string, errors: st
|
|||||||
validateModelTiers(config, 'vertex', errors);
|
validateModelTiers(config, 'vertex', errors);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'router': {
|
||||||
|
if (!keys.includes('default')) {
|
||||||
|
errors.push('[router] missing required key: default');
|
||||||
|
}
|
||||||
|
if (!keys.includes('openai_key') && !keys.includes('openrouter_key')) {
|
||||||
|
errors.push('[router] requires either openai_key or openrouter_key');
|
||||||
|
}
|
||||||
|
const models = config.models as Record<string, unknown> | undefined;
|
||||||
|
if (models && typeof models === 'object' && Object.keys(models).length > 0) {
|
||||||
|
errors.push('[models] is not supported with [router]');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +242,7 @@ function validateConfig(config: TOMLConfig): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Only one provider section allowed (ignore empty sections)
|
// 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', 'vertex', 'router'] as const;
|
||||||
const present = PROVIDER_SECTIONS.filter((s) => {
|
const present = PROVIDER_SECTIONS.filter((s) => {
|
||||||
const section = config[s];
|
const section = config[s];
|
||||||
return section && typeof section === 'object' && Object.keys(section).length > 0;
|
return section && typeof section === 'object' && Object.keys(section).length > 0;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface ShannonConfig {
|
|||||||
custom_base_url?: { base_url?: string; auth_token?: string };
|
custom_base_url?: { base_url?: string; auth_token?: string };
|
||||||
bedrock?: { use?: boolean; region?: string; token?: string };
|
bedrock?: { use?: boolean; region?: string; token?: string };
|
||||||
vertex?: { use?: boolean; region?: string; project_id?: string; key_path?: string };
|
vertex?: { use?: boolean; region?: string; project_id?: string; key_path?: string };
|
||||||
|
router?: { default?: string; openai_key?: string; openrouter_key?: string };
|
||||||
models?: { small?: string; medium?: string; large?: string };
|
models?: { small?: string; medium?: string; large?: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+59
-34
@@ -69,28 +69,65 @@ export function isTemporalReady(): boolean {
|
|||||||
return output.includes('SERVING');
|
return output.includes('SERVING');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check if the router container is running and healthy. */
|
||||||
|
function isRouterReady(): boolean {
|
||||||
|
const status = runOutput('docker', ['inspect', '--format', '{{.State.Health.Status}}', 'shannon-router']);
|
||||||
|
return status === 'healthy';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure Temporal is running via compose.
|
* Ensure Temporal (and optionally router) are running via compose.
|
||||||
|
* If Temporal is already up but router is needed and missing, starts router only.
|
||||||
*/
|
*/
|
||||||
export async function ensureInfra(): Promise<void> {
|
export async function ensureInfra(useRouter: boolean): Promise<void> {
|
||||||
if (isTemporalReady()) {
|
const temporalReady = isTemporalReady();
|
||||||
|
const routerNeeded = useRouter && !isRouterReady();
|
||||||
|
|
||||||
|
if (temporalReady && !routerNeeded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const composeFile = getComposeFile();
|
const composeFile = getComposeFile();
|
||||||
console.log('Starting Shannon infrastructure...');
|
const composeArgs = ['compose', '-f', composeFile];
|
||||||
execFileSync('docker', ['compose', '-f', composeFile, 'up', '-d'], { stdio: 'inherit' });
|
if (useRouter) composeArgs.push('--profile', 'router');
|
||||||
|
composeArgs.push('up', '-d');
|
||||||
|
|
||||||
console.log('Waiting for Temporal to be ready...');
|
if (temporalReady && routerNeeded) {
|
||||||
for (let i = 0; i < 30; i++) {
|
console.log('Starting router...');
|
||||||
if (isTemporalReady()) {
|
} else {
|
||||||
console.log('Temporal is ready!');
|
console.log('Starting Shannon infrastructure...');
|
||||||
return;
|
}
|
||||||
|
execFileSync('docker', composeArgs, { stdio: 'inherit' });
|
||||||
|
|
||||||
|
// Wait for Temporal if it wasn't already running
|
||||||
|
if (!temporalReady) {
|
||||||
|
console.log('Waiting for Temporal to be ready...');
|
||||||
|
for (let i = 0; i < 30; i++) {
|
||||||
|
if (isTemporalReady()) {
|
||||||
|
console.log('Temporal is ready!');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i === 29) {
|
||||||
|
console.error('Timeout waiting for Temporal');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
await sleep(2000);
|
||||||
}
|
}
|
||||||
await sleep(2000);
|
}
|
||||||
|
|
||||||
|
// Wait for router if needed
|
||||||
|
if (routerNeeded) {
|
||||||
|
console.log('Waiting for router to be ready...');
|
||||||
|
for (let i = 0; i < 15; i++) {
|
||||||
|
if (isRouterReady()) {
|
||||||
|
console.log('Router is ready!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await sleep(2000);
|
||||||
|
}
|
||||||
|
console.error('Timeout waiting for router');
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.error('Timeout waiting for Temporal');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -157,21 +194,15 @@ export interface WorkerOptions {
|
|||||||
credentials?: string;
|
credentials?: string;
|
||||||
promptsDir?: string;
|
promptsDir?: string;
|
||||||
outputDir?: string;
|
outputDir?: string;
|
||||||
workspace: string;
|
workspace?: string;
|
||||||
pipelineTesting?: boolean;
|
pipelineTesting?: boolean;
|
||||||
debug?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spawn the worker container in detached mode and return the process.
|
* Spawn the worker container in detached mode and return the process.
|
||||||
* When `opts.debug` is true, omits `--rm` so the container persists for log inspection.
|
|
||||||
*/
|
*/
|
||||||
export function spawnWorker(opts: WorkerOptions): ChildProcess {
|
export function spawnWorker(opts: WorkerOptions): ChildProcess {
|
||||||
const args = ['run', '-d'];
|
const args = ['run', '-d', '--rm', '--name', opts.containerName, '--network', 'shannon-net'];
|
||||||
if (!opts.debug) {
|
|
||||||
args.push('--rm');
|
|
||||||
}
|
|
||||||
args.push('--name', opts.containerName, '--network', 'shannon-net');
|
|
||||||
|
|
||||||
// Add host flag for Linux
|
// Add host flag for Linux
|
||||||
args.push(...addHostFlag());
|
args.push(...addHostFlag());
|
||||||
@@ -183,13 +214,7 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess {
|
|||||||
|
|
||||||
// Volume mounts
|
// Volume mounts
|
||||||
args.push('-v', `${opts.workspacesDir}:/app/workspaces`);
|
args.push('-v', `${opts.workspacesDir}:/app/workspaces`);
|
||||||
args.push('-v', `${opts.repo.hostPath}:${opts.repo.containerPath}:ro`);
|
args.push('-v', `${opts.repo.hostPath}:${opts.repo.containerPath}`);
|
||||||
|
|
||||||
// Writable overlays: shadow .shannon/ inside the :ro repo with workspace-backed dirs
|
|
||||||
const workspacePath = path.join(opts.workspacesDir, opts.workspace);
|
|
||||||
args.push('-v', `${path.join(workspacePath, 'deliverables')}:${opts.repo.containerPath}/.shannon/deliverables`);
|
|
||||||
args.push('-v', `${path.join(workspacePath, 'scratchpad')}:${opts.repo.containerPath}/.shannon/scratchpad`);
|
|
||||||
args.push('-v', `${path.join(workspacePath, '.playwright-cli')}:${opts.repo.containerPath}/.shannon/.playwright-cli`);
|
|
||||||
|
|
||||||
// Local mode: mount prompts for live editing
|
// Local mode: mount prompts for live editing
|
||||||
if (opts.promptsDir) {
|
if (opts.promptsDir) {
|
||||||
@@ -228,16 +253,16 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess {
|
|||||||
if (opts.outputDir) {
|
if (opts.outputDir) {
|
||||||
args.push('--output', '/app/output');
|
args.push('--output', '/app/output');
|
||||||
}
|
}
|
||||||
args.push('--workspace', opts.workspace);
|
if (opts.workspace) {
|
||||||
|
args.push('--workspace', opts.workspace);
|
||||||
|
}
|
||||||
if (opts.pipelineTesting) {
|
if (opts.pipelineTesting) {
|
||||||
args.push('--pipeline-testing');
|
args.push('--pipeline-testing');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inherit stderr so `docker run` daemon errors surface to the user;
|
// Prevent MSYS/Git Bash from converting Unix paths (e.g. /repos/my-repo) to Windows paths
|
||||||
// ignore stdin/stdout (the container ID is noise).
|
|
||||||
return spawn('docker', args, {
|
return spawn('docker', args, {
|
||||||
stdio: ['ignore', 'ignore', 'inherit'],
|
stdio: 'pipe',
|
||||||
// Prevent MSYS/Git Bash from converting Unix paths on Windows
|
|
||||||
...(os.platform() === 'win32' && { env: { ...process.env, MSYS_NO_PATHCONV: '1' } }),
|
...(os.platform() === 'win32' && { env: { ...process.env, MSYS_NO_PATHCONV: '1' } }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -259,7 +284,7 @@ export function stopWorkers(): void {
|
|||||||
*/
|
*/
|
||||||
export function stopInfra(clean: boolean): void {
|
export function stopInfra(clean: boolean): void {
|
||||||
const composeFile = getComposeFile();
|
const composeFile = getComposeFile();
|
||||||
const args = ['compose', '-f', composeFile, 'down'];
|
const args = ['compose', '-f', composeFile, '--profile', 'router', 'down'];
|
||||||
if (clean) args.push('-v');
|
if (clean) args.push('-v');
|
||||||
execFileSync('docker', args, { stdio: 'inherit' });
|
execFileSync('docker', args, { stdio: 'inherit' });
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-1
@@ -14,6 +14,7 @@ const FORWARD_VARS = [
|
|||||||
'ANTHROPIC_API_KEY',
|
'ANTHROPIC_API_KEY',
|
||||||
'ANTHROPIC_BASE_URL',
|
'ANTHROPIC_BASE_URL',
|
||||||
'ANTHROPIC_AUTH_TOKEN',
|
'ANTHROPIC_AUTH_TOKEN',
|
||||||
|
'ROUTER_DEFAULT',
|
||||||
'CLAUDE_CODE_OAUTH_TOKEN',
|
'CLAUDE_CODE_OAUTH_TOKEN',
|
||||||
'CLAUDE_CODE_USE_BEDROCK',
|
'CLAUDE_CODE_USE_BEDROCK',
|
||||||
'AWS_REGION',
|
'AWS_REGION',
|
||||||
@@ -26,6 +27,8 @@ const FORWARD_VARS = [
|
|||||||
'ANTHROPIC_MEDIUM_MODEL',
|
'ANTHROPIC_MEDIUM_MODEL',
|
||||||
'ANTHROPIC_LARGE_MODEL',
|
'ANTHROPIC_LARGE_MODEL',
|
||||||
'CLAUDE_CODE_MAX_OUTPUT_TOKENS',
|
'CLAUDE_CODE_MAX_OUTPUT_TOKENS',
|
||||||
|
'OPENAI_API_KEY',
|
||||||
|
'OPENROUTER_API_KEY',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,7 +64,12 @@ export function buildEnvFlags(): string[] {
|
|||||||
interface CredentialValidation {
|
interface CredentialValidation {
|
||||||
valid: boolean;
|
valid: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
mode: 'api-key' | 'oauth' | 'custom-base-url' | 'bedrock' | 'vertex';
|
mode: 'api-key' | 'oauth' | 'custom-base-url' | 'bedrock' | 'vertex' | 'router';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if router credentials are present in the environment. */
|
||||||
|
export function isRouterConfigured(): boolean {
|
||||||
|
return !!(process.env.ROUTER_DEFAULT && (process.env.OPENAI_API_KEY || process.env.OPENROUTER_API_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if a custom Anthropic-compatible base URL is configured. */
|
/** Check if a custom Anthropic-compatible base URL is configured. */
|
||||||
@@ -77,6 +85,7 @@ function detectProviders(): string[] {
|
|||||||
if (isCustomBaseUrlConfigured()) providers.push('Custom Base URL');
|
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_BEDROCK === '1') providers.push('AWS Bedrock');
|
||||||
if (process.env.CLAUDE_CODE_USE_VERTEX === '1') providers.push('Google Vertex');
|
if (process.env.CLAUDE_CODE_USE_VERTEX === '1') providers.push('Google Vertex');
|
||||||
|
if (isRouterConfigured()) providers.push('Router');
|
||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +110,8 @@ export function validateCredentials(): CredentialValidation {
|
|||||||
return { valid: true, mode: 'oauth' };
|
return { valid: true, mode: 'oauth' };
|
||||||
}
|
}
|
||||||
if (isCustomBaseUrlConfigured()) {
|
if (isCustomBaseUrlConfigured()) {
|
||||||
|
// Set auth token as API key so the SDK can initialize
|
||||||
|
process.env.ANTHROPIC_API_KEY = process.env.ANTHROPIC_AUTH_TOKEN;
|
||||||
return { valid: true, mode: 'custom-base-url' };
|
return { valid: true, mode: 'custom-base-url' };
|
||||||
}
|
}
|
||||||
if (process.env.CLAUDE_CODE_USE_BEDROCK === '1') {
|
if (process.env.CLAUDE_CODE_USE_BEDROCK === '1') {
|
||||||
@@ -142,6 +153,11 @@ export function validateCredentials(): CredentialValidation {
|
|||||||
}
|
}
|
||||||
return { valid: true, mode: 'vertex' };
|
return { valid: true, mode: 'vertex' };
|
||||||
}
|
}
|
||||||
|
if (isRouterConfigured()) {
|
||||||
|
// Set a placeholder so the worker doesn't reject the missing key
|
||||||
|
process.env.ANTHROPIC_API_KEY = 'router-mode';
|
||||||
|
return { valid: true, mode: 'router' };
|
||||||
|
}
|
||||||
|
|
||||||
const hint =
|
const hint =
|
||||||
getMode() === 'local'
|
getMode() === 'local'
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Options for 'start':
|
|||||||
-o, --output <path> Copy deliverables to this directory after run
|
-o, --output <path> Copy deliverables to this directory after run
|
||||||
-w, --workspace <name> Named workspace (auto-resumes if exists)
|
-w, --workspace <name> Named workspace (auto-resumes if exists)
|
||||||
--pipeline-testing Use minimal prompts for fast testing
|
--pipeline-testing Use minimal prompts for fast testing
|
||||||
--debug Preserve worker container after exit for log inspection
|
--router Route requests through claude-code-router
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
${prefix} start -u https://example.com -r ${mode === 'local' ? 'my-repo' : './my-repo'}
|
${prefix} start -u https://example.com -r ${mode === 'local' ? 'my-repo' : './my-repo'}
|
||||||
@@ -94,7 +94,7 @@ interface ParsedStartArgs {
|
|||||||
workspace?: string;
|
workspace?: string;
|
||||||
output?: string;
|
output?: string;
|
||||||
pipelineTesting: boolean;
|
pipelineTesting: boolean;
|
||||||
debug: boolean;
|
router: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStartArgs(argv: string[]): ParsedStartArgs {
|
function parseStartArgs(argv: string[]): ParsedStartArgs {
|
||||||
@@ -104,7 +104,7 @@ function parseStartArgs(argv: string[]): ParsedStartArgs {
|
|||||||
let workspace: string | undefined;
|
let workspace: string | undefined;
|
||||||
let output: string | undefined;
|
let output: string | undefined;
|
||||||
let pipelineTesting = false;
|
let pipelineTesting = false;
|
||||||
let debug = false;
|
let router = false;
|
||||||
|
|
||||||
for (let i = 0; i < argv.length; i++) {
|
for (let i = 0; i < argv.length; i++) {
|
||||||
const arg = argv[i];
|
const arg = argv[i];
|
||||||
@@ -149,8 +149,8 @@ function parseStartArgs(argv: string[]): ParsedStartArgs {
|
|||||||
case '--pipeline-testing':
|
case '--pipeline-testing':
|
||||||
pipelineTesting = true;
|
pipelineTesting = true;
|
||||||
break;
|
break;
|
||||||
case '--debug':
|
case '--router':
|
||||||
debug = true;
|
router = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error(`Unknown option: ${arg}`);
|
console.error(`Unknown option: ${arg}`);
|
||||||
@@ -169,7 +169,7 @@ function parseStartArgs(argv: string[]): ParsedStartArgs {
|
|||||||
url,
|
url,
|
||||||
repo,
|
repo,
|
||||||
pipelineTesting,
|
pipelineTesting,
|
||||||
debug,
|
router,
|
||||||
...(config && { config }),
|
...(config && { config }),
|
||||||
...(workspace && { workspace }),
|
...(workspace && { workspace }),
|
||||||
...(output && { output }),
|
...(output && { output }),
|
||||||
|
|||||||
@@ -76,3 +76,12 @@ export function resolveConfig(configArg: string): MountPair {
|
|||||||
containerPath: `/app/configs/${basename}`,
|
containerPath: `/app/configs/${basename}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the deliverables directory exists and is writable by the container user.
|
||||||
|
*/
|
||||||
|
export function ensureDeliverables(repoHostPath: string): void {
|
||||||
|
const deliverables = path.join(repoHostPath, 'deliverables');
|
||||||
|
fs.mkdirSync(deliverables, { recursive: true });
|
||||||
|
fs.chmodSync(deliverables, 0o777);
|
||||||
|
}
|
||||||
@@ -3,16 +3,6 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
|
||||||
"./interfaces": "./dist/interfaces/index.js",
|
|
||||||
"./types": "./dist/types/index.js",
|
|
||||||
"./types/config": "./dist/types/config.js",
|
|
||||||
"./types/agents": "./dist/types/agents.js",
|
|
||||||
"./pipeline": "./dist/temporal/pipeline.js",
|
|
||||||
"./activities": "./dist/temporal/activities.js",
|
|
||||||
"./services": "./dist/services/index.js",
|
|
||||||
"./config": "./dist/config-parser.js"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"check": "tsc --noEmit",
|
"check": "tsc --noEmit",
|
||||||
@@ -28,7 +18,6 @@
|
|||||||
"ajv-formats": "^2.1.1",
|
"ajv-formats": "^2.1.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"zod": "^4.3.6",
|
|
||||||
"zx": "^8.0.0"
|
"zx": "^8.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -106,13 +106,13 @@ If you only confirmed a vulnerability without attempting to weaponize it, you ha
|
|||||||
</login_instructions>
|
</login_instructions>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your **actionable exploitation queue** is located at `.shannon/deliverables/auth_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
- Your **actionable exploitation queue** is located at `deliverables/auth_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
||||||
|
|
||||||
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
||||||
Before beginning exploitation, read these strategic intelligence files in order:
|
Before beginning exploitation, read these strategic intelligence files in order:
|
||||||
1. `.shannon/deliverables/pre_recon_deliverable.md` - Architecture overview, technology stack, and authentication code locations.
|
1. `deliverables/pre_recon_deliverable.md` - Architecture overview, technology stack, and authentication code locations.
|
||||||
2. `.shannon/deliverables/recon_deliverable.md` - Complete API inventory, user roles, and data flow maps.
|
2. `deliverables/recon_deliverable.md` - Complete API inventory, user roles, and data flow maps.
|
||||||
3. `.shannon/deliverables/auth_analysis_deliverable.md` - Strategic context from the Auth analysis specialist, including notes on session mechanisms, password policies, and flawed logic paths.
|
3. `deliverables/auth_analysis_deliverable.md` - Strategic context from the Auth analysis specialist, including notes on session mechanisms, password policies, and flawed logic paths.
|
||||||
|
|
||||||
- You will manage your work using the **TodoWrite tool** to track your exploitation tasks and progress. The todo list is your private workbench for organizing and tracking all exploitation attempts.
|
- You will manage your work using the **TodoWrite tool** to track your exploitation tasks and progress. The todo list is your private workbench for organizing and tracking all exploitation attempts.
|
||||||
</starting_context>
|
</starting_context>
|
||||||
@@ -122,13 +122,13 @@ Before beginning exploitation, read these strategic intelligence files in order:
|
|||||||
|
|
||||||
**Phase Sequence:** VULN ANALYSIS (Complete) → **AUTH EXPLOITATION (You)** → FINAL REPORT (next phase)
|
**Phase Sequence:** VULN ANALYSIS (Complete) → **AUTH EXPLOITATION (You)** → FINAL REPORT (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/auth_exploitation_queue.json` (primary exploitation queue)
|
**Your Input:** `deliverables/auth_exploitation_queue.json` (primary exploitation queue)
|
||||||
**Your Output:** `.shannon/deliverables/auth_exploitation_evidence.md` (exploitation evidence)
|
**Your Output:** `deliverables/auth_exploitation_evidence.md` (exploitation evidence)
|
||||||
|
|
||||||
**Supporting Intelligence:**
|
**Supporting Intelligence:**
|
||||||
- `.shannon/deliverables/pre_recon_deliverable.md` (architecture & code context)
|
- `deliverables/pre_recon_deliverable.md` (architecture & code context)
|
||||||
- `.shannon/deliverables/recon_deliverable.md` (API inventory & user roles)
|
- `deliverables/recon_deliverable.md` (API inventory & user roles)
|
||||||
- `.shannon/deliverables/auth_analysis_deliverable.md` (strategic intel)
|
- `deliverables/auth_analysis_deliverable.md` (strategic intel)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture and attack surfaces
|
- Reconnaissance agent mapped application architecture and attack surfaces
|
||||||
@@ -144,10 +144,10 @@ You are the **Identity Compromise Specialist** - proving tangible impact of brok
|
|||||||
<cli_tools>
|
<cli_tools>
|
||||||
- **Browser Automation (playwright-cli skill):** Essential for interacting with multi-step authentication flows, injecting stolen session cookies, and verifying account takeover in a real browser context. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** Essential for interacting with multi-step authentication flows, injecting stolen session cookies, and verifying account takeover in a real browser context. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
- **Task Agent:** Mandatory coder-executor for brute force batches, credential stuffing, token replay automation, and any scripted workflow.
|
- **Task Agent:** Mandatory coder-executor for brute force batches, credential stuffing, token replay automation, and any scripted workflow.
|
||||||
- **TodoWrite tool:** Essential for managing your exploitation task list. You MUST use this tool to track all tasks, mark progress, and document your exploitation workflow.
|
- **TodoWrite tool:** Essential for managing your exploitation task list. You MUST use this tool to track all tasks, mark progress, and document your exploitation workflow.
|
||||||
@@ -189,7 +189,7 @@ Your primary goal is to achieve a definitive, evidence-backed conclusion for **e
|
|||||||
Your entire exploitation process will be managed through the TodoWrite tool. You MUST adhere to this process.
|
Your entire exploitation process will be managed through the TodoWrite tool. You MUST adhere to this process.
|
||||||
|
|
||||||
**1. Initialization:**
|
**1. Initialization:**
|
||||||
- Read `.shannon/deliverables/auth_exploitation_queue.json`.
|
- Read `deliverables/auth_exploitation_queue.json`.
|
||||||
- Use the TodoWrite tool to create tasks for each vulnerability in the queue.
|
- Use the TodoWrite tool to create tasks for each vulnerability in the queue.
|
||||||
- Each task should include the vulnerability ID and the hypothesis to test.
|
- Each task should include the vulnerability ID and the hypothesis to test.
|
||||||
Example tasks:
|
Example tasks:
|
||||||
@@ -315,7 +315,7 @@ When your attack plan is fully explored (all checkboxes are marked `[x]`), your
|
|||||||
First, synthesize all evidence into your specialist section and save it using the save-deliverable CLI with --type AUTH_EVIDENCE. This section will be combined with other specialist reports to form the complete security assessment.
|
First, synthesize all evidence into your specialist section and save it using the save-deliverable CLI with --type AUTH_EVIDENCE. This section will be combined with other specialist reports to form the complete security assessment.
|
||||||
|
|
||||||
**Section Ordering & Format Requirements:**
|
**Section Ordering & Format Requirements:**
|
||||||
- Use the exact vulnerability IDs from `.shannon/deliverables/auth_exploitation_queue.json`
|
- Use the exact vulnerability IDs from `deliverables/auth_exploitation_queue.json`
|
||||||
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
||||||
- Within "Confirmed Vulnerabilities (Attempted Exploitation)" section: Order by severity (most critical first)
|
- Within "Confirmed Vulnerabilities (Attempted Exploitation)" section: Order by severity (most critical first)
|
||||||
- Within "Potential" section: Order by confidence level (highest confidence first)
|
- Within "Potential" section: Order by confidence level (highest confidence first)
|
||||||
@@ -407,13 +407,13 @@ COMPLETION REQUIREMENTS (ALL must be satisfied):
|
|||||||
1. Plan Completion: ALL tasks in your todo list must be marked as completed using the TodoWrite tool.
|
1. Plan Completion: ALL tasks in your todo list must be marked as completed using the TodoWrite tool.
|
||||||
2. Deliverable Generation: The required deliverable must be successfully saved using the save-deliverable CLI:
|
2. Deliverable Generation: The required deliverable must be successfully saved using the save-deliverable CLI:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/auth_exploitation_evidence.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/auth_exploitation_evidence.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type AUTH_EVIDENCE --file-path ".shannon/deliverables/auth_exploitation_evidence.md"`
|
4. Run `save-deliverable` with `--type AUTH_EVIDENCE --file-path "deliverables/auth_exploitation_evidence.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
|
||||||
CRITICAL WARNING: Announcing completion before every item in .shannon/deliverables/auth_exploitation_queue.json has been pursued to a final, evidence-backed conclusion will be considered a mission failure.
|
CRITICAL WARNING: Announcing completion before every item in deliverables/auth_exploitation_queue.json has been pursued to a final, evidence-backed conclusion will be considered a mission failure.
|
||||||
|
|
||||||
ONLY AFTER fulfilling these exhaustive requirements, announce "AUTH EXPLOITATION COMPLETE" and stop.
|
ONLY AFTER fulfilling these exhaustive requirements, announce "AUTH EXPLOITATION COMPLETE" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -93,13 +93,13 @@ After exhaustive bypass attempts, determine:
|
|||||||
</login_instructions>
|
</login_instructions>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your **actionable exploitation queue** is located at `.shannon/deliverables/authz_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
- Your **actionable exploitation queue** is located at `deliverables/authz_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
||||||
|
|
||||||
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
||||||
Before beginning exploitation, read these strategic intelligence files in order:
|
Before beginning exploitation, read these strategic intelligence files in order:
|
||||||
1. `.shannon/deliverables/pre_recon_deliverable.md` - Architecture overview, technology stack, and authorization code locations.
|
1. `deliverables/pre_recon_deliverable.md` - Architecture overview, technology stack, and authorization code locations.
|
||||||
2. `.shannon/deliverables/recon_deliverable.md` - Complete API inventory, user roles, and permission models.
|
2. `deliverables/recon_deliverable.md` - Complete API inventory, user roles, and permission models.
|
||||||
3. `.shannon/deliverables/authz_analysis_deliverable.md` - Strategic context from the Authz analysis specialist, including notes on access control patterns, role hierarchies, and flawed logic paths.
|
3. `deliverables/authz_analysis_deliverable.md` - Strategic context from the Authz analysis specialist, including notes on access control patterns, role hierarchies, and flawed logic paths.
|
||||||
|
|
||||||
- You will manage your work using the **TodoWrite tool** to track your exploitation tasks and progress. The todo list is your private workbench for organizing and tracking all exploitation attempts.
|
- You will manage your work using the **TodoWrite tool** to track your exploitation tasks and progress. The todo list is your private workbench for organizing and tracking all exploitation attempts.
|
||||||
</starting_context>
|
</starting_context>
|
||||||
@@ -109,13 +109,13 @@ Before beginning exploitation, read these strategic intelligence files in order:
|
|||||||
|
|
||||||
**Phase Sequence:** VULN ANALYSIS (Complete) → **AUTHZ EXPLOITATION (You)** → FINAL REPORT (next phase)
|
**Phase Sequence:** VULN ANALYSIS (Complete) → **AUTHZ EXPLOITATION (You)** → FINAL REPORT (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/authz_exploitation_queue.json` (primary exploitation queue)
|
**Your Input:** `deliverables/authz_exploitation_queue.json` (primary exploitation queue)
|
||||||
**Your Output:** `.shannon/deliverables/authz_exploitation_evidence.md` (exploitation evidence)
|
**Your Output:** `deliverables/authz_exploitation_evidence.md` (exploitation evidence)
|
||||||
|
|
||||||
**Supporting Intelligence:**
|
**Supporting Intelligence:**
|
||||||
- `.shannon/deliverables/pre_recon_deliverable.md` (architecture & code context)
|
- `deliverables/pre_recon_deliverable.md` (architecture & code context)
|
||||||
- `.shannon/deliverables/recon_deliverable.md` (API inventory & user roles)
|
- `deliverables/recon_deliverable.md` (API inventory & user roles)
|
||||||
- `.shannon/deliverables/authz_analysis_deliverable.md` (strategic intel)
|
- `deliverables/authz_analysis_deliverable.md` (strategic intel)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture and attack surfaces
|
- Reconnaissance agent mapped application architecture and attack surfaces
|
||||||
@@ -131,10 +131,10 @@ You are the **Privilege Escalation Specialist** - proving tangible impact of bro
|
|||||||
<cli_tools>
|
<cli_tools>
|
||||||
- **Browser Automation (playwright-cli skill):** Essential for interacting with complex authorization flows, testing role-based access controls in browser contexts, and verifying privilege escalation through UI elements. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** Essential for interacting with complex authorization flows, testing role-based access controls in browser contexts, and verifying privilege escalation through UI elements. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
- **Task Agent:** Mandatory coder-executor for IDOR sweeps, role escalation loops, and workflow bypass automation.
|
- **Task Agent:** Mandatory coder-executor for IDOR sweeps, role escalation loops, and workflow bypass automation.
|
||||||
- **TodoWrite tool:** Essential for managing your exploitation task list. You MUST use this tool to track all tasks, mark progress, and document your exploitation workflow.
|
- **TodoWrite tool:** Essential for managing your exploitation task list. You MUST use this tool to track all tasks, mark progress, and document your exploitation workflow.
|
||||||
@@ -176,7 +176,7 @@ Your primary goal is to achieve a definitive, evidence-backed conclusion for **e
|
|||||||
Your entire exploitation process will be managed through the TodoWrite tool. You MUST adhere to this process.
|
Your entire exploitation process will be managed through the TodoWrite tool. You MUST adhere to this process.
|
||||||
|
|
||||||
**1. Initialization:**
|
**1. Initialization:**
|
||||||
- Read `.shannon/deliverables/authz_exploitation_queue.json`.
|
- Read `deliverables/authz_exploitation_queue.json`.
|
||||||
- Use the TodoWrite tool to create tasks for each vulnerability in the queue.
|
- Use the TodoWrite tool to create tasks for each vulnerability in the queue.
|
||||||
- Each task should include the vulnerability ID, type, and the hypothesis to test.
|
- Each task should include the vulnerability ID, type, and the hypothesis to test.
|
||||||
Example tasks:
|
Example tasks:
|
||||||
@@ -321,7 +321,7 @@ When your attack plan is fully explored (all checkboxes are marked `[x]`), your
|
|||||||
First, synthesize all evidence into your specialist section and save it using the save-deliverable CLI with --type AUTHZ_EVIDENCE. This section will be combined with other specialist reports to form the complete security assessment and MUST use the following structure:
|
First, synthesize all evidence into your specialist section and save it using the save-deliverable CLI with --type AUTHZ_EVIDENCE. This section will be combined with other specialist reports to form the complete security assessment and MUST use the following structure:
|
||||||
|
|
||||||
**Section Ordering & Format Requirements:**
|
**Section Ordering & Format Requirements:**
|
||||||
- Use the exact vulnerability IDs from `.shannon/deliverables/authz_exploitation_queue.json`
|
- Use the exact vulnerability IDs from `deliverables/authz_exploitation_queue.json`
|
||||||
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
||||||
- Within "Potential" section: Order by confidence level (highest confidence first)
|
- Within "Potential" section: Order by confidence level (highest confidence first)
|
||||||
|
|
||||||
@@ -409,13 +409,13 @@ COMPLETION REQUIREMENTS (ALL must be satisfied):
|
|||||||
1. Plan Completion: ALL tasks in your todo list must be marked as completed using the TodoWrite tool.
|
1. Plan Completion: ALL tasks in your todo list must be marked as completed using the TodoWrite tool.
|
||||||
2. Deliverable Generation: The required deliverable must be successfully saved using the save-deliverable CLI:
|
2. Deliverable Generation: The required deliverable must be successfully saved using the save-deliverable CLI:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/authz_exploitation_evidence.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/authz_exploitation_evidence.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type AUTHZ_EVIDENCE --file-path ".shannon/deliverables/authz_exploitation_evidence.md"`
|
4. Run `save-deliverable` with `--type AUTHZ_EVIDENCE --file-path "deliverables/authz_exploitation_evidence.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
|
||||||
CRITICAL WARNING: Announcing completion before every item in .shannon/deliverables/authz_exploitation_queue.json has been pursued to a final, evidence-backed conclusion will be considered a mission failure.
|
CRITICAL WARNING: Announcing completion before every item in deliverables/authz_exploitation_queue.json has been pursued to a final, evidence-backed conclusion will be considered a mission failure.
|
||||||
|
|
||||||
ONLY AFTER fulfilling these exhaustive requirements, announce "AUTHZ EXPLOITATION COMPLETE" and stop.
|
ONLY AFTER fulfilling these exhaustive requirements, announce "AUTHZ EXPLOITATION COMPLETE" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -70,13 +70,13 @@ Remember: An unproven vulnerability is worse than no finding at all - it wastes
|
|||||||
</login_instructions>
|
</login_instructions>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your **actionable exploitation queue** is located at `.shannon/deliverables/injection_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
- Your **actionable exploitation queue** is located at `deliverables/injection_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
||||||
|
|
||||||
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
||||||
Before beginning exploitation, read these strategic intelligence files in order:
|
Before beginning exploitation, read these strategic intelligence files in order:
|
||||||
1. `.shannon/deliverables/pre_recon_deliverable.md` - Architecture overview, exact vulnerable code snippets, database details, authentication mechanisms
|
1. `deliverables/pre_recon_deliverable.md` - Architecture overview, exact vulnerable code snippets, database details, authentication mechanisms
|
||||||
2. `.shannon/deliverables/recon_deliverable.md` - Complete API inventory, input vectors, code pointers with file:line references
|
2. `deliverables/recon_deliverable.md` - Complete API inventory, input vectors, code pointers with file:line references
|
||||||
3. `.shannon/deliverables/injection_analysis_deliverable.md` - Strategic context from analysis specialist, WAF behavior, database technologies
|
3. `deliverables/injection_analysis_deliverable.md` - Strategic context from analysis specialist, WAF behavior, database technologies
|
||||||
|
|
||||||
**INTELLIGENCE-INFORMED EXPLOITATION:**
|
**INTELLIGENCE-INFORMED EXPLOITATION:**
|
||||||
- Use exact query or command structures provided in intelligence reports to construct precise payloads.
|
- Use exact query or command structures provided in intelligence reports to construct precise payloads.
|
||||||
@@ -91,13 +91,13 @@ Before beginning exploitation, read these strategic intelligence files in order:
|
|||||||
|
|
||||||
**Phase Sequence:** VULN ANALYSIS (Complete) → **INJECTION EXPLOITATION (You)** → FINAL REPORT (next phase)
|
**Phase Sequence:** VULN ANALYSIS (Complete) → **INJECTION EXPLOITATION (You)** → FINAL REPORT (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/injection_exploitation_queue.json` (primary exploitation queue)
|
**Your Input:** `deliverables/injection_exploitation_queue.json` (primary exploitation queue)
|
||||||
**Your Output:** `.shannon/deliverables/injection_exploitation_evidence.md` (exploitation evidence)
|
**Your Output:** `deliverables/injection_exploitation_evidence.md` (exploitation evidence)
|
||||||
|
|
||||||
**Supporting Intelligence:**
|
**Supporting Intelligence:**
|
||||||
- `.shannon/deliverables/pre_recon_deliverable.md` (architecture & code context)
|
- `deliverables/pre_recon_deliverable.md` (architecture & code context)
|
||||||
- `.shannon/deliverables/recon_deliverable.md` (API inventory & input vectors)
|
- `deliverables/recon_deliverable.md` (API inventory & input vectors)
|
||||||
- `.shannon/deliverables/injection_analysis_deliverable.md` (strategic intel)
|
- `deliverables/injection_analysis_deliverable.md` (strategic intel)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture and attack surfaces
|
- Reconnaissance agent mapped application architecture and attack surfaces
|
||||||
@@ -113,10 +113,10 @@ You are the **Proof-of-Impact Generator** - converting theoretical injection fla
|
|||||||
<cli_tools>
|
<cli_tools>
|
||||||
- **Browser Automation (playwright-cli skill):** For testing injection vulnerabilities through browser interactions when needed. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** For testing injection vulnerabilities through browser interactions when needed. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
- **Task Agent:** Mandatory coder-executor for any custom scripting beyond single ad-hoc commands.
|
- **Task Agent:** Mandatory coder-executor for any custom scripting beyond single ad-hoc commands.
|
||||||
- **TodoWrite tool:** Essential for managing your exploitation task list. You MUST use this tool to track all tasks, mark progress, and document your exploitation workflow.
|
- **TodoWrite tool:** Essential for managing your exploitation task list. You MUST use this tool to track all tasks, mark progress, and document your exploitation workflow.
|
||||||
@@ -139,7 +139,7 @@ Your primary goal is to achieve a definitive, evidence-backed conclusion for **e
|
|||||||
Your entire exploitation process will be managed through the TodoWrite tool. You MUST adhere to this process.
|
Your entire exploitation process will be managed through the TodoWrite tool. You MUST adhere to this process.
|
||||||
|
|
||||||
**1. Initialization:**
|
**1. Initialization:**
|
||||||
- Read the `.shannon/deliverables/injection_exploitation_queue.json` file.
|
- Read the `deliverables/injection_exploitation_queue.json` file.
|
||||||
- Use the TodoWrite tool to create tasks for each vulnerability in the queue.
|
- Use the TodoWrite tool to create tasks for each vulnerability in the queue.
|
||||||
- Each task should include the vulnerability ID and the hypothesis to test.
|
- Each task should include the vulnerability ID and the hypothesis to test.
|
||||||
Example tasks:
|
Example tasks:
|
||||||
@@ -347,7 +347,7 @@ First, synthesize all of your evidence into your specialist section and save it
|
|||||||
Your section MUST use the following structure precisely:
|
Your section MUST use the following structure precisely:
|
||||||
|
|
||||||
**Section Ordering & Format Requirements:**
|
**Section Ordering & Format Requirements:**
|
||||||
- Use the exact vulnerability IDs from `.shannon/deliverables/injection_exploitation_queue.json`
|
- Use the exact vulnerability IDs from `deliverables/injection_exploitation_queue.json`
|
||||||
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
||||||
- Within "Potential" section: Order by confidence level (highest confidence first)
|
- Within "Potential" section: Order by confidence level (highest confidence first)
|
||||||
|
|
||||||
@@ -436,13 +436,13 @@ COMPLETION REQUIREMENTS (ALL must be satisfied):
|
|||||||
1. **Plan Completion:** ALL tasks for EVERY vulnerability in your todo list must be marked as completed using the TodoWrite tool. **No vulnerability or task can be left unaddressed.**
|
1. **Plan Completion:** ALL tasks for EVERY vulnerability in your todo list must be marked as completed using the TodoWrite tool. **No vulnerability or task can be left unaddressed.**
|
||||||
2. **Deliverable Generation:** The required deliverable must be successfully saved using the save-deliverable CLI tool:
|
2. **Deliverable Generation:** The required deliverable must be successfully saved using the save-deliverable CLI tool:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/injection_exploitation_evidence.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/injection_exploitation_evidence.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type INJECTION_EVIDENCE --file-path ".shannon/deliverables/injection_exploitation_evidence.md"`
|
4. Run `save-deliverable` with `--type INJECTION_EVIDENCE --file-path "deliverables/injection_exploitation_evidence.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
|
||||||
**CRITICAL WARNING:** Announcing completion before every item in `.shannon/deliverables/injection_exploitation_queue.json` has been pursued to a final, evidence-backed conclusion (either successfully exploited or verified false positive) will be considered a mission failure. Superficial testing is not acceptable.
|
**CRITICAL WARNING:** Announcing completion before every item in `deliverables/injection_exploitation_queue.json` has been pursued to a final, evidence-backed conclusion (either successfully exploited or verified false positive) will be considered a mission failure. Superficial testing is not acceptable.
|
||||||
|
|
||||||
ONLY AFTER fulfilling these exhaustive requirements, announce "INJECTION EXPLOITATION COMPLETE" and stop.
|
ONLY AFTER fulfilling these exhaustive requirements, announce "INJECTION EXPLOITATION COMPLETE" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -93,13 +93,13 @@ After exhaustive bypass attempts, determine:
|
|||||||
</login_instructions>
|
</login_instructions>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your **actionable exploitation queue** is located at `.shannon/deliverables/ssrf_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
- Your **actionable exploitation queue** is located at `deliverables/ssrf_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
||||||
|
|
||||||
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
||||||
Before beginning exploitation, read these strategic intelligence files in order:
|
Before beginning exploitation, read these strategic intelligence files in order:
|
||||||
1. `.shannon/deliverables/pre_recon_deliverable.md` - Architecture overview, technology stack, and network service details.
|
1. `deliverables/pre_recon_deliverable.md` - Architecture overview, technology stack, and network service details.
|
||||||
2. `.shannon/deliverables/recon_deliverable.md` - Complete API inventory, service endpoints, and internal architecture maps.
|
2. `deliverables/recon_deliverable.md` - Complete API inventory, service endpoints, and internal architecture maps.
|
||||||
3. `.shannon/deliverables/ssrf_analysis_deliverable.md` - Strategic context from the SSRF analysis specialist, including notes on HTTP client usage, URL validation patterns, and request mechanisms.
|
3. `deliverables/ssrf_analysis_deliverable.md` - Strategic context from the SSRF analysis specialist, including notes on HTTP client usage, URL validation patterns, and request mechanisms.
|
||||||
|
|
||||||
- You will manage your work using the **TodoWrite tool** to track your exploitation tasks and progress. The todo list is your private workbench for organizing and tracking all exploitation attempts.
|
- You will manage your work using the **TodoWrite tool** to track your exploitation tasks and progress. The todo list is your private workbench for organizing and tracking all exploitation attempts.
|
||||||
</starting_context>
|
</starting_context>
|
||||||
@@ -109,13 +109,13 @@ Before beginning exploitation, read these strategic intelligence files in order:
|
|||||||
|
|
||||||
**Phase Sequence:** VULN ANALYSIS (Complete) → **SSRF EXPLOITATION (You)** → FINAL REPORT (next phase)
|
**Phase Sequence:** VULN ANALYSIS (Complete) → **SSRF EXPLOITATION (You)** → FINAL REPORT (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/ssrf_exploitation_queue.json` (primary exploitation queue)
|
**Your Input:** `deliverables/ssrf_exploitation_queue.json` (primary exploitation queue)
|
||||||
**Your Output:** `.shannon/deliverables/ssrf_exploitation_evidence.md` (exploitation evidence)
|
**Your Output:** `deliverables/ssrf_exploitation_evidence.md` (exploitation evidence)
|
||||||
|
|
||||||
**Supporting Intelligence:**
|
**Supporting Intelligence:**
|
||||||
- `.shannon/deliverables/pre_recon_deliverable.md` (architecture & network context)
|
- `deliverables/pre_recon_deliverable.md` (architecture & network context)
|
||||||
- `.shannon/deliverables/recon_deliverable.md` (API inventory & service endpoints)
|
- `deliverables/recon_deliverable.md` (API inventory & service endpoints)
|
||||||
- `.shannon/deliverables/ssrf_analysis_deliverable.md` (strategic intel)
|
- `deliverables/ssrf_analysis_deliverable.md` (strategic intel)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture and attack surfaces
|
- Reconnaissance agent mapped application architecture and attack surfaces
|
||||||
@@ -130,10 +130,10 @@ You are the **Network Boundary Breaker** - proving tangible impact of SSRF vulne
|
|||||||
|
|
||||||
<cli_tools>
|
<cli_tools>
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
- **Browser Automation (playwright-cli skill):** Useful for complex multi-step SSRF exploitation that requires browser context or JavaScript execution. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** Useful for complex multi-step SSRF exploitation that requires browser context or JavaScript execution. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **Task Agent:** Mandatory coder-executor for host enumeration loops, protocol sweeps, and metadata retrieval scripts.
|
- **Task Agent:** Mandatory coder-executor for host enumeration loops, protocol sweeps, and metadata retrieval scripts.
|
||||||
@@ -176,7 +176,7 @@ Your primary goal is to achieve a definitive, evidence-backed conclusion for **e
|
|||||||
Your entire exploitation process will be managed through the TodoWrite tool. You MUST adhere to this process.
|
Your entire exploitation process will be managed through the TodoWrite tool. You MUST adhere to this process.
|
||||||
|
|
||||||
**1. Initialization:**
|
**1. Initialization:**
|
||||||
- Read `.shannon/deliverables/ssrf_exploitation_queue.json`.
|
- Read `deliverables/ssrf_exploitation_queue.json`.
|
||||||
- Use the TodoWrite tool to create tasks for each vulnerability in the queue.
|
- Use the TodoWrite tool to create tasks for each vulnerability in the queue.
|
||||||
- Each task should include the vulnerability ID and the hypothesis to test.
|
- Each task should include the vulnerability ID and the hypothesis to test.
|
||||||
Example tasks:
|
Example tasks:
|
||||||
@@ -398,7 +398,7 @@ When your attack plan is fully explored (all checkboxes are marked `[x]`), your
|
|||||||
First, synthesize all evidence into your specialist section and save it using the save-deliverable CLI with --type SSRF_EVIDENCE. This section will be combined with other specialist reports to form the complete security assessment and MUST use the following structure:
|
First, synthesize all evidence into your specialist section and save it using the save-deliverable CLI with --type SSRF_EVIDENCE. This section will be combined with other specialist reports to form the complete security assessment and MUST use the following structure:
|
||||||
|
|
||||||
**Section Ordering & Format Requirements:**
|
**Section Ordering & Format Requirements:**
|
||||||
- Use the exact vulnerability IDs from `.shannon/deliverables/ssrf_exploitation_queue.json`
|
- Use the exact vulnerability IDs from `deliverables/ssrf_exploitation_queue.json`
|
||||||
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
||||||
- Within "Potential" section: Order by confidence level (highest confidence first)
|
- Within "Potential" section: Order by confidence level (highest confidence first)
|
||||||
|
|
||||||
@@ -486,13 +486,13 @@ COMPLETION REQUIREMENTS (ALL must be satisfied):
|
|||||||
1. Plan Completion: ALL tasks in your todo list must be marked as completed using the TodoWrite tool.
|
1. Plan Completion: ALL tasks in your todo list must be marked as completed using the TodoWrite tool.
|
||||||
2. Deliverable Generation: The required deliverable must be successfully saved using the save-deliverable CLI:
|
2. Deliverable Generation: The required deliverable must be successfully saved using the save-deliverable CLI:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/ssrf_exploitation_evidence.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/ssrf_exploitation_evidence.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type SSRF_EVIDENCE --file-path ".shannon/deliverables/ssrf_exploitation_evidence.md"`
|
4. Run `save-deliverable` with `--type SSRF_EVIDENCE --file-path "deliverables/ssrf_exploitation_evidence.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
|
||||||
CRITICAL WARNING: Announcing completion before every item in .shannon/deliverables/ssrf_exploitation_queue.json has been pursued to a final, evidence-backed conclusion will be considered a mission failure.
|
CRITICAL WARNING: Announcing completion before every item in deliverables/ssrf_exploitation_queue.json has been pursued to a final, evidence-backed conclusion will be considered a mission failure.
|
||||||
|
|
||||||
ONLY AFTER fulfilling these exhaustive requirements, announce "SSRF EXPLOITATION COMPLETE" and stop.
|
ONLY AFTER fulfilling these exhaustive requirements, announce "SSRF EXPLOITATION COMPLETE" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -91,13 +91,13 @@ After exhaustive bypass attempts, determine:
|
|||||||
</login_instructions>
|
</login_instructions>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your **actionable exploitation queue** is located at `.shannon/deliverables/xss_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
- Your **actionable exploitation queue** is located at `deliverables/xss_exploitation_queue.json`. This is your to-do list, containing the precise targets identified by the analysis phase. You MUST process every vulnerability in this queue.
|
||||||
|
|
||||||
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
**INTELLIGENCE GATHERING PHASE (MANDATORY):**
|
||||||
Before beginning exploitation, read these strategic intelligence files in order:
|
Before beginning exploitation, read these strategic intelligence files in order:
|
||||||
1. `.shannon/deliverables/pre_recon_deliverable.md` - Architecture overview, technology stack, and critical file paths.
|
1. `deliverables/pre_recon_deliverable.md` - Architecture overview, technology stack, and critical file paths.
|
||||||
2. `.shannon/deliverables/recon_deliverable.md` - Complete API inventory, input vectors, and data flow maps.
|
2. `deliverables/recon_deliverable.md` - Complete API inventory, input vectors, and data flow maps.
|
||||||
3. `.shannon/deliverables/xss_analysis_deliverable.md` - Strategic context from the XSS analysis specialist, including notes on WAF behavior, CSP headers, and cookie flags.
|
3. `deliverables/xss_analysis_deliverable.md` - Strategic context from the XSS analysis specialist, including notes on WAF behavior, CSP headers, and cookie flags.
|
||||||
|
|
||||||
**INTELLIGENCE-INFORMED EXPLOITATION:**
|
**INTELLIGENCE-INFORMED EXPLOITATION:**
|
||||||
- Use the `render_context` and `mismatch_reason` from the analysis queue to craft precise initial payloads.
|
- Use the `render_context` and `mismatch_reason` from the analysis queue to craft precise initial payloads.
|
||||||
@@ -111,13 +111,13 @@ Before beginning exploitation, read these strategic intelligence files in order:
|
|||||||
|
|
||||||
**Phase Sequence:** VULN ANALYSIS (Complete) → **XSS EXPLOITATION (You)** → FINAL REPORT (next phase)
|
**Phase Sequence:** VULN ANALYSIS (Complete) → **XSS EXPLOITATION (You)** → FINAL REPORT (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/xss_exploitation_queue.json` (primary exploitation queue)
|
**Your Input:** `deliverables/xss_exploitation_queue.json` (primary exploitation queue)
|
||||||
**Your Output:** `.shannon/deliverables/xss_exploitation_evidence.md` (exploitation evidence)
|
**Your Output:** `deliverables/xss_exploitation_evidence.md` (exploitation evidence)
|
||||||
|
|
||||||
**Supporting Intelligence:**
|
**Supporting Intelligence:**
|
||||||
- `.shannon/deliverables/pre_recon_deliverable.md` (architecture & code context)
|
- `deliverables/pre_recon_deliverable.md` (architecture & code context)
|
||||||
- `.shannon/deliverables/recon_deliverable.md` (API inventory & input vectors)
|
- `deliverables/recon_deliverable.md` (API inventory & input vectors)
|
||||||
- `.shannon/deliverables/xss_analysis_deliverable.md` (strategic intel)
|
- `deliverables/xss_analysis_deliverable.md` (strategic intel)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture and attack surfaces
|
- Reconnaissance agent mapped application architecture and attack surfaces
|
||||||
@@ -133,10 +133,10 @@ You are the **Client-Side Impact Demonstrator** - converting theoretical XSS fla
|
|||||||
<cli_tools>
|
<cli_tools>
|
||||||
- **Browser Automation (playwright-cli skill):** Your primary tool for testing DOM-based and Stored XSS, confirming script execution in a real browser context, and interacting with the application post-exploitation. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** Your primary tool for testing DOM-based and Stored XSS, confirming script execution in a real browser context, and interacting with the application post-exploitation. Invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
- **Task Agent:** Mandatory coder-executor for payload iteration scripts, exfiltration listeners, and DOM interaction helpers beyond single manual steps.
|
- **Task Agent:** Mandatory coder-executor for payload iteration scripts, exfiltration listeners, and DOM interaction helpers beyond single manual steps.
|
||||||
- **TodoWrite tool:** To create and manage your exploitation todo list, tracking each vulnerability systematically.
|
- **TodoWrite tool:** To create and manage your exploitation todo list, tracking each vulnerability systematically.
|
||||||
@@ -183,7 +183,7 @@ For **every single vulnerability** in your queue, systematically work through th
|
|||||||
**Goal:** Set up tracking and understand the pre-analyzed vulnerabilities.
|
**Goal:** Set up tracking and understand the pre-analyzed vulnerabilities.
|
||||||
|
|
||||||
**Actions:**
|
**Actions:**
|
||||||
- Read `.shannon/deliverables/xss_exploitation_queue.json` to get your targets with their complete graph analysis
|
- Read `deliverables/xss_exploitation_queue.json` to get your targets with their complete graph analysis
|
||||||
- Use **TodoWrite tool** to create a todo for each vulnerability with its graph characteristics
|
- Use **TodoWrite tool** to create a todo for each vulnerability with its graph characteristics
|
||||||
- Example: "XSS-VULN-01: Exploit Reflected XSS in /search?q= (source: URL param → no sanitization → innerHTML sink)"
|
- Example: "XSS-VULN-01: Exploit Reflected XSS in /search?q= (source: URL param → no sanitization → innerHTML sink)"
|
||||||
- Study the provided intelligence for each vulnerability:
|
- Study the provided intelligence for each vulnerability:
|
||||||
@@ -331,7 +331,7 @@ First, synthesize all of your evidence into your specialist section and save it
|
|||||||
Your section MUST use the following structure precisely:
|
Your section MUST use the following structure precisely:
|
||||||
|
|
||||||
**Section Ordering & Format Requirements:**
|
**Section Ordering & Format Requirements:**
|
||||||
- Use the exact vulnerability IDs from `.shannon/deliverables/xss_exploitation_queue.json`
|
- Use the exact vulnerability IDs from `deliverables/xss_exploitation_queue.json`
|
||||||
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
- Within "Successfully Exploited" section: Order by severity of impact (most critical first)
|
||||||
- Within "Potential" section: Order by confidence level (highest confidence first)
|
- Within "Potential" section: Order by confidence level (highest confidence first)
|
||||||
|
|
||||||
@@ -426,13 +426,13 @@ COMPLETION REQUIREMENTS (ALL must be satisfied):
|
|||||||
- Todo List Completion: ALL vulnerabilities from the exploitation queue must have been processed and marked as completed in your todo list.
|
- Todo List Completion: ALL vulnerabilities from the exploitation queue must have been processed and marked as completed in your todo list.
|
||||||
- Deliverable Generation: The required deliverable must be successfully saved using the save-deliverable CLI tool:
|
- Deliverable Generation: The required deliverable must be successfully saved using the save-deliverable CLI tool:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/xss_exploitation_evidence.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/xss_exploitation_evidence.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type XSS_EVIDENCE --file-path ".shannon/deliverables/xss_exploitation_evidence.md"`
|
4. Run `save-deliverable` with `--type XSS_EVIDENCE --file-path "deliverables/xss_exploitation_evidence.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
|
||||||
**CRITICAL WARNING:** Announcing completion before every item in `.shannon/deliverables/xss_exploitation_queue.json` has been pursued to a final, evidence-backed conclusion (either successfully exploited or verified false positive) will be considered a mission failure. Superficial testing is not acceptable.
|
**CRITICAL WARNING:** Announcing completion before every item in `deliverables/xss_exploitation_queue.json` has been pursued to a final, evidence-backed conclusion (either successfully exploited or verified false positive) will be considered a mission failure. Superficial testing is not acceptable.
|
||||||
|
|
||||||
ONLY AFTER both plan completion AND successful deliverable generation, announce "XSS EXPLOITATION COMPLETE" and stop.
|
ONLY AFTER both plan completion AND successful deliverable generation, announce "XSS EXPLOITATION COMPLETE" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
## 🧪 Pipeline Testing: Session Isolation Test for Authentication Exploitation Agent
|
## 🧪 Pipeline Testing: Session Isolation Test for Authentication Exploitation Agent
|
||||||
|
|
||||||
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
||||||
@@ -12,7 +10,7 @@
|
|||||||
|
|
||||||
2. Take a screenshot to prove isolation:
|
2. Take a screenshot to prove isolation:
|
||||||
```
|
```
|
||||||
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "{{REPO_PATH}}/.shannon/scratchpad/auth-exploit-test.png"
|
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "auth-exploit-test.png"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Log the test results:
|
3. Log the test results:
|
||||||
@@ -26,7 +24,7 @@ Run the save-deliverable CLI:
|
|||||||
save-deliverable --type AUTH_EVIDENCE --content '# Authentication Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** auth-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test authentication vulnerability\n- **Method:** Authentication bypass via parameter manipulation\n- **Evidence:** Successfully bypassed login using admin=true parameter\n- **Impact:** Demonstrated unauthorized access to protected resources\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** AUTH-VULN-01\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}\n- **Attack Vector:** Parameter-based authentication bypass\n- **Bypass Method:** URL parameter manipulation'
|
save-deliverable --type AUTH_EVIDENCE --content '# Authentication Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** auth-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test authentication vulnerability\n- **Method:** Authentication bypass via parameter manipulation\n- **Evidence:** Successfully bypassed login using admin=true parameter\n- **Impact:** Demonstrated unauthorized access to protected resources\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** AUTH-VULN-01\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}\n- **Attack Vector:** Parameter-based authentication bypass\n- **Bypass Method:** URL parameter manipulation'
|
||||||
```
|
```
|
||||||
|
|
||||||
This will automatically create `.shannon/deliverables/auth_exploitation_evidence.md`.
|
This will automatically create `deliverables/auth_exploitation_evidence.md`.
|
||||||
|
|
||||||
### Step 3: Verify Session Isolation
|
### Step 3: Verify Session Isolation
|
||||||
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
## 🧪 Pipeline Testing: Session Isolation Test for Authorization Exploitation Agent
|
## 🧪 Pipeline Testing: Session Isolation Test for Authorization Exploitation Agent
|
||||||
|
|
||||||
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
||||||
@@ -12,7 +10,7 @@
|
|||||||
|
|
||||||
2. Take a screenshot to prove isolation:
|
2. Take a screenshot to prove isolation:
|
||||||
```
|
```
|
||||||
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "{{REPO_PATH}}/.shannon/scratchpad/authz-exploit-test.png"
|
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "authz-exploit-test.png"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Log the test results:
|
3. Log the test results:
|
||||||
@@ -26,7 +24,7 @@ Run the save-deliverable CLI:
|
|||||||
save-deliverable --type AUTHZ_EVIDENCE --content '# Authorization Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** authz-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test authorization vulnerability\n- **Method:** Privilege escalation via direct object reference\n- **Evidence:** Successfully accessed admin panel with user-level privileges\n- **Impact:** Demonstrated unauthorized access to privileged functionality\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** AUTHZ-VULN-01\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}\n- **Attack Vector:** Insecure Direct Object Reference (IDOR)\n- **Escalation Method:** User ID manipulation in API calls'
|
save-deliverable --type AUTHZ_EVIDENCE --content '# Authorization Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** authz-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test authorization vulnerability\n- **Method:** Privilege escalation via direct object reference\n- **Evidence:** Successfully accessed admin panel with user-level privileges\n- **Impact:** Demonstrated unauthorized access to privileged functionality\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** AUTHZ-VULN-01\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}\n- **Attack Vector:** Insecure Direct Object Reference (IDOR)\n- **Escalation Method:** User ID manipulation in API calls'
|
||||||
```
|
```
|
||||||
|
|
||||||
This will automatically create `.shannon/deliverables/authz_exploitation_evidence.md`.
|
This will automatically create `deliverables/authz_exploitation_evidence.md`.
|
||||||
|
|
||||||
### Step 3: Verify Session Isolation
|
### Step 3: Verify Session Isolation
|
||||||
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
## 🧪 Pipeline Testing: Session Isolation Test for Injection Exploitation Agent
|
## 🧪 Pipeline Testing: Session Isolation Test for Injection Exploitation Agent
|
||||||
|
|
||||||
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
||||||
@@ -12,7 +10,7 @@
|
|||||||
|
|
||||||
2. Take a screenshot to prove isolation:
|
2. Take a screenshot to prove isolation:
|
||||||
```
|
```
|
||||||
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "{{REPO_PATH}}/.shannon/scratchpad/injection-exploit-test.png"
|
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "injection-exploit-test.png"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Log the test results:
|
3. Log the test results:
|
||||||
@@ -26,7 +24,7 @@ Run the save-deliverable CLI:
|
|||||||
save-deliverable --type INJECTION_EVIDENCE --content '# Injection Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** injection-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test injection vulnerability\n- **Vulnerability Type:** SQLi | CommandInjection | LFI | RFI | SSTI | PathTraversal | InsecureDeserialization\n- **Method:** [Type-specific exploitation method]\n- **Evidence:** Successfully executed test payload\n- **Impact:** Demonstrated ability to manipulate [database queries | system commands | file system | template engine | deserialization]\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** INJ-VULN-XX\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}'
|
save-deliverable --type INJECTION_EVIDENCE --content '# Injection Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** injection-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test injection vulnerability\n- **Vulnerability Type:** SQLi | CommandInjection | LFI | RFI | SSTI | PathTraversal | InsecureDeserialization\n- **Method:** [Type-specific exploitation method]\n- **Evidence:** Successfully executed test payload\n- **Impact:** Demonstrated ability to manipulate [database queries | system commands | file system | template engine | deserialization]\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** INJ-VULN-XX\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}'
|
||||||
```
|
```
|
||||||
|
|
||||||
This will automatically create `.shannon/deliverables/injection_exploitation_evidence.md`.
|
This will automatically create `deliverables/injection_exploitation_evidence.md`.
|
||||||
|
|
||||||
### Step 3: Verify Session Isolation
|
### Step 3: Verify Session Isolation
|
||||||
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
## 🧪 Pipeline Testing: Session Isolation Test for SSRF Exploitation Agent
|
## 🧪 Pipeline Testing: Session Isolation Test for SSRF Exploitation Agent
|
||||||
|
|
||||||
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
||||||
@@ -12,7 +10,7 @@
|
|||||||
|
|
||||||
2. Take a screenshot to prove isolation:
|
2. Take a screenshot to prove isolation:
|
||||||
```
|
```
|
||||||
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "{{REPO_PATH}}/.shannon/scratchpad/ssrf-exploit-test.png"
|
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "ssrf-exploit-test.png"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Log the test results:
|
3. Log the test results:
|
||||||
@@ -26,7 +24,7 @@ Run the save-deliverable CLI:
|
|||||||
save-deliverable --type SSRF_EVIDENCE --content '# SSRF Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** ssrf-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test SSRF vulnerability\n- **Method:** Server-Side Request Forgery via URL parameter\n- **Evidence:** Successfully forced server to make request to internal network\n- **Impact:** Demonstrated access to internal services and potential data exfiltration\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** SSRF-VULN-01\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}\n- **Attack Vector:** URL parameter manipulation\n- **Target:** Internal network services (localhost:8080)'
|
save-deliverable --type SSRF_EVIDENCE --content '# SSRF Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** ssrf-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test SSRF vulnerability\n- **Method:** Server-Side Request Forgery via URL parameter\n- **Evidence:** Successfully forced server to make request to internal network\n- **Impact:** Demonstrated access to internal services and potential data exfiltration\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** SSRF-VULN-01\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}\n- **Attack Vector:** URL parameter manipulation\n- **Target:** Internal network services (localhost:8080)'
|
||||||
```
|
```
|
||||||
|
|
||||||
This will automatically create `.shannon/deliverables/ssrf_exploitation_evidence.md`.
|
This will automatically create `deliverables/ssrf_exploitation_evidence.md`.
|
||||||
|
|
||||||
### Step 3: Verify Session Isolation
|
### Step 3: Verify Session Isolation
|
||||||
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
## 🧪 Pipeline Testing: Session Isolation Test for XSS Exploitation Agent
|
## 🧪 Pipeline Testing: Session Isolation Test for XSS Exploitation Agent
|
||||||
|
|
||||||
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
**Playwright Session:** Using session `{{PLAYWRIGHT_SESSION}}` for browser automation testing.
|
||||||
@@ -12,7 +10,7 @@
|
|||||||
|
|
||||||
2. Take a screenshot to prove isolation:
|
2. Take a screenshot to prove isolation:
|
||||||
```
|
```
|
||||||
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "{{REPO_PATH}}/.shannon/scratchpad/xss-exploit-test.png"
|
playwright-cli -s={{PLAYWRIGHT_SESSION}} screenshot --filename "xss-exploit-test.png"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Log the test results:
|
3. Log the test results:
|
||||||
@@ -26,7 +24,7 @@ Run the save-deliverable CLI:
|
|||||||
save-deliverable --type XSS_EVIDENCE --content '# XSS Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** xss-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test XSS vulnerability\n- **Method:** Reflected XSS via search parameter\n- **Evidence:** Successfully executed payload `<script>alert('\''XSS'\'')</script>`\n- **Impact:** Demonstrated JavaScript code execution in user context\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** XSS-VULN-01\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}\n- **Attack Vector:** Reflected XSS in search functionality'
|
save-deliverable --type XSS_EVIDENCE --content '# XSS Exploitation Evidence (Pipeline Test)\n\n**Playwright Session:** {{PLAYWRIGHT_SESSION}}\n**Test Site:** https://example.com\n**Screenshot:** xss-exploit-test.png\n**Status:** Browser isolation test successful\n\n## Exploitation Results\n- **Target:** Test XSS vulnerability\n- **Method:** Reflected XSS via search parameter\n- **Evidence:** Successfully executed payload `<script>alert('\''XSS'\'')</script>`\n- **Impact:** Demonstrated JavaScript code execution in user context\n- **Proof:** Pipeline testing mode - simulated successful exploitation\n\n## Technical Details\n- **Vulnerability ID:** XSS-VULN-01\n- **Exploitation Status:** Simulated Success (Pipeline Test)\n- **Session:** {{PLAYWRIGHT_SESSION}}\n- **Attack Vector:** Reflected XSS in search functionality'
|
||||||
```
|
```
|
||||||
|
|
||||||
This will automatically create `.shannon/deliverables/xss_exploitation_evidence.md`.
|
This will automatically create `deliverables/xss_exploitation_evidence.md`.
|
||||||
|
|
||||||
### Step 3: Verify Session Isolation
|
### Step 3: Verify Session Isolation
|
||||||
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
This agent should be using {{PLAYWRIGHT_SESSION}} and navigating to example.com independently of other parallel exploitation agents.
|
||||||
@@ -1,3 +1 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
Run: `save-deliverable --type CODE_ANALYSIS --content 'Pre-recon analysis complete'`. Then say "Done".
|
Run: `save-deliverable --type CODE_ANALYSIS --content 'Pre-recon analysis complete'`. Then say "Done".
|
||||||
@@ -1,3 +1 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
Run: `save-deliverable --type RECON --content 'Reconnaissance analysis complete'`. Then say "Done".
|
Run: `save-deliverable --type RECON --content 'Reconnaissance analysis complete'`. Then say "Done".
|
||||||
@@ -1,3 +1 @@
|
|||||||
@include(shared/_filesystem.txt)
|
Read `deliverables/comprehensive_security_assessment_report.md`, prepend "# Security Assessment Report\n\n**Target:** {{WEB_URL}}\n\n" to the content, and save it back. Say "Done".
|
||||||
|
|
||||||
Read `.shannon/deliverables/comprehensive_security_assessment_report.md`, prepend "# Security Assessment Report\n\n**Target:** {{WEB_URL}}\n\n" to the content, and save it back. Say "Done".
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
Filesystem:
|
|
||||||
- {{REPO_PATH}}/ (read only)
|
|
||||||
- {{REPO_PATH}}/.shannon/deliverables/ (read-write)
|
|
||||||
- {{REPO_PATH}}/.shannon/scratchpad/ (read-write) - screenshots, scripts, scratch work, etc.
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
Please complete these tasks using your CLI tools:
|
Please complete these tasks using your CLI tools:
|
||||||
|
|
||||||
1. Navigate to https://example.net and take a screenshot:
|
1. Navigate to https://example.net and take a screenshot:
|
||||||
@@ -10,4 +8,5 @@ Please complete these tasks using your CLI tools:
|
|||||||
2. Save an analysis deliverable:
|
2. Save an analysis deliverable:
|
||||||
- Run: `save-deliverable --type AUTH_ANALYSIS --content '# Auth Analysis Report\n\nAnalysis complete. No authentication vulnerabilities identified.'`
|
- Run: `save-deliverable --type AUTH_ANALYSIS --content '# Auth Analysis Report\n\nAnalysis complete. No authentication vulnerabilities identified.'`
|
||||||
|
|
||||||
As a final step, return an empty array for vulnerabilities.
|
3. Save a queue deliverable:
|
||||||
|
- Run: `save-deliverable --type AUTH_QUEUE --content '{"vulnerabilities": []}'`
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
Please complete these tasks using your CLI tools:
|
Please complete these tasks using your CLI tools:
|
||||||
|
|
||||||
1. Navigate to https://jsonplaceholder.typicode.com and take a screenshot:
|
1. Navigate to https://jsonplaceholder.typicode.com and take a screenshot:
|
||||||
@@ -10,4 +8,5 @@ Please complete these tasks using your CLI tools:
|
|||||||
2. Save an analysis deliverable:
|
2. Save an analysis deliverable:
|
||||||
- Run: `save-deliverable --type AUTHZ_ANALYSIS --content '# Authorization Analysis Report\n\nAnalysis complete. No authorization vulnerabilities identified.'`
|
- Run: `save-deliverable --type AUTHZ_ANALYSIS --content '# Authorization Analysis Report\n\nAnalysis complete. No authorization vulnerabilities identified.'`
|
||||||
|
|
||||||
As a final step, return an empty array for vulnerabilities.
|
3. Save a queue deliverable:
|
||||||
|
- Run: `save-deliverable --type AUTHZ_QUEUE --content '{"vulnerabilities": []}'`
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
Please complete these tasks using your CLI tools:
|
Please complete these tasks using your CLI tools:
|
||||||
|
|
||||||
1. Navigate to https://example.com and take a screenshot:
|
1. Navigate to https://example.com and take a screenshot:
|
||||||
@@ -10,4 +8,5 @@ Please complete these tasks using your CLI tools:
|
|||||||
2. Save an analysis deliverable:
|
2. Save an analysis deliverable:
|
||||||
- Run: `save-deliverable --type INJECTION_ANALYSIS --content '# Injection Analysis Report\n\nAnalysis complete. No injection vulnerabilities identified.'`
|
- Run: `save-deliverable --type INJECTION_ANALYSIS --content '# Injection Analysis Report\n\nAnalysis complete. No injection vulnerabilities identified.'`
|
||||||
|
|
||||||
As a final step, return an empty array for vulnerabilities.
|
3. Save a queue deliverable:
|
||||||
|
- Run: `save-deliverable --type INJECTION_QUEUE --content '{"vulnerabilities": []}'`
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
Please complete these tasks using your CLI tools:
|
Please complete these tasks using your CLI tools:
|
||||||
|
|
||||||
1. Navigate to https://httpbin.org and take a screenshot:
|
1. Navigate to https://httpbin.org and take a screenshot:
|
||||||
@@ -10,4 +8,5 @@ Please complete these tasks using your CLI tools:
|
|||||||
2. Save an analysis deliverable:
|
2. Save an analysis deliverable:
|
||||||
- Run: `save-deliverable --type SSRF_ANALYSIS --content '# SSRF Analysis Report\n\nAnalysis complete. No SSRF vulnerabilities identified.'`
|
- Run: `save-deliverable --type SSRF_ANALYSIS --content '# SSRF Analysis Report\n\nAnalysis complete. No SSRF vulnerabilities identified.'`
|
||||||
|
|
||||||
As a final step, return an empty array for vulnerabilities.
|
3. Save a queue deliverable:
|
||||||
|
- Run: `save-deliverable --type SSRF_QUEUE --content '{"vulnerabilities": []}'`
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@include(shared/_filesystem.txt)
|
|
||||||
|
|
||||||
Please complete these tasks using your CLI tools:
|
Please complete these tasks using your CLI tools:
|
||||||
|
|
||||||
1. Navigate to https://example.org and take a screenshot:
|
1. Navigate to https://example.org and take a screenshot:
|
||||||
@@ -10,4 +8,5 @@ Please complete these tasks using your CLI tools:
|
|||||||
2. Save an analysis deliverable:
|
2. Save an analysis deliverable:
|
||||||
- Run: `save-deliverable --type XSS_ANALYSIS --content '# XSS Analysis Report\n\nAnalysis complete. No XSS vulnerabilities identified.'`
|
- Run: `save-deliverable --type XSS_ANALYSIS --content '# XSS Analysis Report\n\nAnalysis complete. No XSS vulnerabilities identified.'`
|
||||||
|
|
||||||
As a final step, return an empty array for vulnerabilities.
|
3. Save a queue deliverable:
|
||||||
|
- Run: `save-deliverable --type XSS_QUEUE --content '{"vulnerabilities": []}'`
|
||||||
@@ -10,11 +10,6 @@ Objective: Your task is to analyze the provided source code to generate a securi
|
|||||||
|
|
||||||
**WORKING CONTEXT:** You are currently in the root directory of the target codebase to analyze.
|
**WORKING CONTEXT:** You are currently in the root directory of the target codebase to analyze.
|
||||||
|
|
||||||
Filesystem:
|
|
||||||
- {{REPO_PATH}}/ (read only)
|
|
||||||
- {{REPO_PATH}}/.shannon/deliverables/ (read-write)
|
|
||||||
- {{REPO_PATH}}/.shannon/scratchpad/ (read-write) - screenshots, scripts, scratch work, etc.
|
|
||||||
|
|
||||||
**CRITICAL INSTRUCTIONS:**
|
**CRITICAL INSTRUCTIONS:**
|
||||||
- Base your analysis SOLELY on the provided source code. Do not invent services or infer functionality that is not present.
|
- Base your analysis SOLELY on the provided source code. Do not invent services or infer functionality that is not present.
|
||||||
- The output MUST be in Markdown format.
|
- The output MUST be in Markdown format.
|
||||||
@@ -37,7 +32,7 @@ Read `.gitignore` and run `git ls-files --others --ignored --exclude-standard --
|
|||||||
**Phase Sequence:** **PRE-RECON (You)** → RECON → VULN ANALYSIS (5 agents) → EXPLOITATION (5 agents) → REPORTING
|
**Phase Sequence:** **PRE-RECON (You)** → RECON → VULN ANALYSIS (5 agents) → EXPLOITATION (5 agents) → REPORTING
|
||||||
|
|
||||||
**Your Input:** External scan results from pre-recon tools (nmap, subfinder, whatweb)
|
**Your Input:** External scan results from pre-recon tools (nmap, subfinder, whatweb)
|
||||||
**Your Output:** `.shannon/deliverables/pre_recon_deliverable.md` (feeds all subsequent analysis phases)
|
**Your Output:** `deliverables/code_analysis_deliverable.md` (feeds all subsequent analysis phases)
|
||||||
**Shared Intelligence:** You create the foundational intelligence baseline that all other agents depend on
|
**Shared Intelligence:** You create the foundational intelligence baseline that all other agents depend on
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
@@ -89,10 +84,10 @@ You are the **Code Intelligence Gatherer** and **Architectural Foundation Builde
|
|||||||
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, trace authentication mechanisms, map attack surfaces, and understand architectural patterns. MANDATORY for all source code analysis.
|
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, trace authentication mechanisms, map attack surfaces, and understand architectural patterns. MANDATORY for all source code analysis.
|
||||||
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create todo items for each phase and agent that needs execution. Mark items as "in_progress" when working on them and "completed" when done.
|
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create todo items for each phase and agent that needs execution. Mark items as "in_progress" when working on them and "completed" when done.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
</cli_tools>
|
</cli_tools>
|
||||||
|
|
||||||
@@ -133,14 +128,14 @@ After Phase 1 completes, launch all three vulnerability-focused agents in parall
|
|||||||
- Resolve conflicts and eliminate duplicates
|
- Resolve conflicts and eliminate duplicates
|
||||||
- Generate the final structured markdown report
|
- Generate the final structured markdown report
|
||||||
- **Schema Management**: Using schemas identified by the Entry Point Mapper Agent:
|
- **Schema Management**: Using schemas identified by the Entry Point Mapper Agent:
|
||||||
- Create the `.shannon/deliverables/schemas/` directory using mkdir -p
|
- Create the `outputs/schemas/` directory using mkdir -p
|
||||||
- Copy all discovered schema files to `.shannon/deliverables/schemas/` with descriptive names
|
- Copy all discovered schema files to `outputs/schemas/` with descriptive names
|
||||||
- Include schema locations in your attack surface analysis
|
- Include schema locations in your attack surface analysis
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/pre_recon_deliverable.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/code_analysis_deliverable.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type CODE_ANALYSIS --file-path ".shannon/deliverables/pre_recon_deliverable.md"`
|
4. Run `save-deliverable` with `--type CODE_ANALYSIS --file-path "deliverables/code_analysis_deliverable.md"`
|
||||||
- **WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
- **WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
|
||||||
**EXECUTION PATTERN:**
|
**EXECUTION PATTERN:**
|
||||||
@@ -399,8 +394,8 @@ A component is **out-of-scope** if it **cannot** be invoked through the running
|
|||||||
- Phase 3: Synthesis and report generation completed
|
- Phase 3: Synthesis and report generation completed
|
||||||
|
|
||||||
2. **Deliverable Generation:** The following files must be successfully created:
|
2. **Deliverable Generation:** The following files must be successfully created:
|
||||||
- `.shannon/deliverables/pre_recon_deliverable.md` (via `save-deliverable` with `--file-path`, not inline `--content`)
|
- `deliverables/code_analysis_deliverable.md` (via `save-deliverable` with `--file-path`, not inline `--content`)
|
||||||
- `.shannon/deliverables/schemas/` directory with all discovered schema files copied (if any schemas found)
|
- `outputs/schemas/` directory with all discovered schema files copied (if any schemas found)
|
||||||
|
|
||||||
3. **TodoWrite Completion:** All tasks in your todo list must be marked as completed
|
3. **TodoWrite Completion:** All tasks in your todo list must be marked as completed
|
||||||
|
|
||||||
|
|||||||
@@ -7,17 +7,12 @@ Your goal is to create a comprehensive, structured map of the application's atta
|
|||||||
</objective>
|
</objective>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
Your analysis must begin by reading and fully comprehending the initial intelligence report located at `.shannon/deliverables/pre_recon_deliverable.md`. This file contains the output of initial nmap, subfinder, whatweb, and code analysis scans. This is your only starting information.
|
Your analysis must begin by reading and fully comprehending the initial intelligence report located at `deliverables/pre_recon_deliverable.md`. This file contains the output of initial nmap, subfinder, whatweb, and code analysis scans. This is your only starting information.
|
||||||
</starting_context>
|
</starting_context>
|
||||||
|
|
||||||
<target>
|
<target>
|
||||||
URL: {{WEB_URL}}
|
URL: {{WEB_URL}}
|
||||||
{{DESCRIPTION}}
|
{{DESCRIPTION}}
|
||||||
|
|
||||||
Filesystem:
|
|
||||||
- {{REPO_PATH}}/ (read only)
|
|
||||||
- {{REPO_PATH}}/.shannon/deliverables/ (read-write)
|
|
||||||
- {{REPO_PATH}}/.shannon/scratchpad/ (read-write) - screenshots, scripts, scratch work, etc.
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<rules>
|
<rules>
|
||||||
@@ -66,10 +61,10 @@ Please use these tools for the following use cases:
|
|||||||
- Task tool: **MANDATORY for ALL source code analysis.** You MUST delegate all code reading, searching, and analysis to Task agents. DO NOT use Read, Glob, or Grep tools for source code.
|
- Task tool: **MANDATORY for ALL source code analysis.** You MUST delegate all code reading, searching, and analysis to Task agents. DO NOT use Read, Glob, or Grep tools for source code.
|
||||||
- **Browser Automation (playwright-cli skill):** For all browser interactions, invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** For all browser interactions, invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
|
|
||||||
**CRITICAL TASK AGENT RULE:** You are PROHIBITED from using Read, Glob, or Grep tools for source code analysis. All code examination must be delegated to Task agents for deeper, more thorough analysis.
|
**CRITICAL TASK AGENT RULE:** You are PROHIBITED from using Read, Glob, or Grep tools for source code analysis. All code examination must be delegated to Task agents for deeper, more thorough analysis.
|
||||||
@@ -80,8 +75,8 @@ Please use these tools for the following use cases:
|
|||||||
|
|
||||||
**Phase Sequence:** PRE-RECON (Complete) → **RECONNAISSANCE (You)** → VULN ANALYSIS (5 agents) → EXPLOITATION (5 agents) → FINAL REPORT (next phase)
|
**Phase Sequence:** PRE-RECON (Complete) → **RECONNAISSANCE (You)** → VULN ANALYSIS (5 agents) → EXPLOITATION (5 agents) → FINAL REPORT (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/pre_recon_deliverable.md` (external scan data, initial code analysis)
|
**Your Input:** `deliverables/pre_recon_deliverable.md` (external scan data, initial code analysis)
|
||||||
**Your Output:** `.shannon/deliverables/recon_deliverable.md` (comprehensive attack surface map)
|
**Your Output:** `deliverables/recon_deliverable.md` (comprehensive attack surface map)
|
||||||
**Shared Intelligence:** None (you are the first analysis specialist)
|
**Shared Intelligence:** None (you are the first analysis specialist)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
@@ -111,7 +106,7 @@ You are the **Attack Surface Architect** - building the foundational intelligenc
|
|||||||
You must follow this methodical four-step process:
|
You must follow this methodical four-step process:
|
||||||
|
|
||||||
1. **Synthesize Initial Data:**
|
1. **Synthesize Initial Data:**
|
||||||
- Read the entire `.shannon/deliverables/pre_recon_deliverable.md`.
|
- Read the entire `deliverables/pre_recon_deliverable.md`.
|
||||||
- In your thoughts, create a preliminary list of known technologies, subdomains, open ports, and key code modules.
|
- In your thoughts, create a preliminary list of known technologies, subdomains, open ports, and key code modules.
|
||||||
|
|
||||||
2. **Interactive Application Exploration:**
|
2. **Interactive Application Exploration:**
|
||||||
@@ -372,10 +367,10 @@ CRITICAL: Only include sources tracing to dangerous sinks (shell, DB, file ops,
|
|||||||
<conclusion_trigger>
|
<conclusion_trigger>
|
||||||
**DELIVERABLE SAVING:**
|
**DELIVERABLE SAVING:**
|
||||||
1. **CHUNKED WRITING (MANDATORY):**
|
1. **CHUNKED WRITING (MANDATORY):**
|
||||||
- Use the **Write** tool to create `.shannon/deliverables/recon_deliverable.md` with the title and first major section
|
- Use the **Write** tool to create `deliverables/recon_deliverable.md` with the title and first major section
|
||||||
- Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
- Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
- Repeat for all remaining sections
|
- Repeat for all remaining sections
|
||||||
2. Run `save-deliverable` with `--type RECON --file-path ".shannon/deliverables/recon_deliverable.md"`
|
2. Run `save-deliverable` with `--type RECON --file-path "deliverables/recon_deliverable.md"`
|
||||||
|
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations. Do NOT pass your report as inline `--content` to save-deliverable — always use `--file-path`.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations. Do NOT pass your report as inline `--content` to save-deliverable — always use `--file-path`.
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,6 @@ IMPORTANT: You are MODIFYING an existing file, not creating a new one.
|
|||||||
<target>
|
<target>
|
||||||
URL: {{WEB_URL}}
|
URL: {{WEB_URL}}
|
||||||
{{DESCRIPTION}}
|
{{DESCRIPTION}}
|
||||||
|
|
||||||
Filesystem:
|
|
||||||
- {{REPO_PATH}}/ (read only)
|
|
||||||
- {{REPO_PATH}}/.shannon/deliverables/ (read-write)
|
|
||||||
- {{REPO_PATH}}/.shannon/scratchpad/ (read-write) - screenshots, scripts, scratch work, etc.
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<context>
|
<context>
|
||||||
@@ -36,13 +31,13 @@ Authentication Context:
|
|||||||
|
|
||||||
<input_files>
|
<input_files>
|
||||||
You will analyze the following deliverable files:
|
You will analyze the following deliverable files:
|
||||||
- `.shannon/deliverables/pre_recon_deliverable.md` - Initial reconnaissance and technology stack
|
- `deliverables/pre_recon_deliverable.md` - Initial reconnaissance and technology stack
|
||||||
- `.shannon/deliverables/recon_deliverable.md` - Attack surface mapping and endpoint discovery
|
- `deliverables/recon_deliverable.md` - Attack surface mapping and endpoint discovery
|
||||||
- `.shannon/deliverables/comprehensive_security_assessment_report.md` - The already-concatenated report that you will modify
|
- `deliverables/comprehensive_security_assessment_report.md` - The already-concatenated report that you will modify
|
||||||
</input_files>
|
</input_files>
|
||||||
|
|
||||||
<deliverable_instructions>
|
<deliverable_instructions>
|
||||||
MODIFY the existing file `.shannon/deliverables/comprehensive_security_assessment_report.md` by:
|
MODIFY the existing file `deliverables/comprehensive_security_assessment_report.md` by:
|
||||||
|
|
||||||
1. ADDING these sections at the top:
|
1. ADDING these sections at the top:
|
||||||
|
|
||||||
@@ -103,7 +98,7 @@ IMPORTANT: Do NOT reorder the existing exploitation evidence sections. Maintain
|
|||||||
- Executive Summary: Technical overview with actionable findings for engineering leaders
|
- Executive Summary: Technical overview with actionable findings for engineering leaders
|
||||||
- Network Reconnaissance: Focus on security-relevant discoveries from automated scans
|
- Network Reconnaissance: Focus on security-relevant discoveries from automated scans
|
||||||
|
|
||||||
3. Clean the exploitation evidence sections from `.shannon/deliverables/comprehensive_security_assessment_report.md` by applying these rules:
|
3. Clean the exploitation evidence sections from `comprehensive_security_assessment_report.md` by applying these rules:
|
||||||
- KEEP these specific section headings:
|
- KEEP these specific section headings:
|
||||||
NOTE: these sections will contain vulnerability lists with IDs matching pattern `### [TYPE]-VULN-[NUMBER]`
|
NOTE: these sections will contain vulnerability lists with IDs matching pattern `### [TYPE]-VULN-[NUMBER]`
|
||||||
* `# [Type] Exploitation Evidence`
|
* `# [Type] Exploitation Evidence`
|
||||||
@@ -124,8 +119,8 @@ IMPORTANT: Do NOT reorder the existing exploitation evidence sections. Maintain
|
|||||||
4. Combine the content:
|
4. Combine the content:
|
||||||
- Place the Executive Summary and Network Reconnaissance sections at the top
|
- Place the Executive Summary and Network Reconnaissance sections at the top
|
||||||
- Follow with the cleaned exploitation evidence sections
|
- Follow with the cleaned exploitation evidence sections
|
||||||
- Save as the modified `.shannon/deliverables/comprehensive_security_assessment_report.md`
|
- Save as the modified `comprehensive_security_assessment_report.md`
|
||||||
|
|
||||||
CRITICAL: You are modifying the existing concatenated report at `.shannon/deliverables/comprehensive_security_assessment_report.md` IN-PLACE, not creating a separate file.
|
CRITICAL: You are modifying the existing concatenated report IN-PLACE, not creating a separate file.
|
||||||
</instructions>
|
</instructions>
|
||||||
|
|
||||||
@@ -1,6 +1 @@
|
|||||||
URL: {{WEB_URL}}
|
URL: {{WEB_URL}}
|
||||||
|
|
||||||
Filesystem:
|
|
||||||
- {{REPO_PATH}}/ (read only)
|
|
||||||
- {{REPO_PATH}}/.shannon/deliverables/ (read-write)
|
|
||||||
- {{REPO_PATH}}/.shannon/scratchpad/ (read-write) - screenshots, scripts, scratch work, etc.
|
|
||||||
@@ -33,7 +33,7 @@ Success criterion: A complete, code-backed analysis of every potential authentic
|
|||||||
</critical>
|
</critical>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your primary source of truth for the application's structure is the reconnaissance report located at `.shannon/deliverables/recon_deliverable.md`. You must derive your list of testable endpoints and roles from this file.
|
- Your primary source of truth for the application's structure is the reconnaissance report located at `deliverables/recon_deliverable.md`. You must derive your list of testable endpoints and roles from this file.
|
||||||
</starting_context>
|
</starting_context>
|
||||||
|
|
||||||
<system_architecture>
|
<system_architecture>
|
||||||
@@ -41,8 +41,8 @@ Success criterion: A complete, code-backed analysis of every potential authentic
|
|||||||
|
|
||||||
**Phase Sequence:** RECON (Complete) → **AUTH ANALYSIS (You)** → EXPLOITATION (next phase)
|
**Phase Sequence:** RECON (Complete) → **AUTH ANALYSIS (You)** → EXPLOITATION (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/recon_deliverable.md`, `.shannon/deliverables/pre_recon_deliverable.md` (reconnaissance findings)
|
**Your Input:** `deliverables/recon_deliverable.md`, `deliverables/pre_recon_deliverable.md` (reconnaissance findings)
|
||||||
**Your Output:** `.shannon/deliverables/auth_analysis_deliverable.md` (analysis report for exploitation phase)
|
**Your Output:** `deliverables/auth_exploitation_queue.json` (actionable vulnerabilities for exploitation)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture, endpoints, and data flows
|
- Reconnaissance agent mapped application architecture, endpoints, and data flows
|
||||||
@@ -78,10 +78,10 @@ An **exploitable vulnerability** is a logical flaw in the code that represents a
|
|||||||
**Available Tools:**
|
**Available Tools:**
|
||||||
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, trace authentication logic paths, and understand session/credential handling. MANDATORY for all source code analysis.
|
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, trace authentication logic paths, and understand session/credential handling. MANDATORY for all source code analysis.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
- **Browser Automation (playwright-cli skill):** For browser interactions with the live web application (multi-step flows like password reset or registration), invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** For browser interactions with the live web application (multi-step flows like password reset or registration), invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each endpoint/flow that needs analysis. Mark items as "in_progress" when working on them and "completed" when done.
|
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each endpoint/flow that needs analysis. Mark items as "in_progress" when working on them and "completed" when done.
|
||||||
@@ -90,7 +90,7 @@ An **exploitable vulnerability** is a logical flaw in the code that represents a
|
|||||||
<data_format_specifications>
|
<data_format_specifications>
|
||||||
|
|
||||||
<exploitation_queue_format>
|
<exploitation_queue_format>
|
||||||
**Purpose:** Defines the format of the exploitation queue JSON.
|
**Purpose:** Defines the structure for a "exploitation queue" saved via the save-deliverable CLI with --type AUTH_QUEUE.
|
||||||
**Structure:** The `vulnerability` JSON object MUST follow this exact format:
|
**Structure:** The `vulnerability` JSON object MUST follow this exact format:
|
||||||
{
|
{
|
||||||
"ID": "unique ID for each vulnerability json (AUTH-VULN-XX)",
|
"ID": "unique ID for each vulnerability json (AUTH-VULN-XX)",
|
||||||
@@ -114,7 +114,7 @@ An **exploitable vulnerability** is a logical flaw in the code that represents a
|
|||||||
# White-Box Vulnerability Analysis Procedure: Broken Authentication (AuthN-only)
|
# White-Box Vulnerability Analysis Procedure: Broken Authentication (AuthN-only)
|
||||||
|
|
||||||
NOTE ON "FOR ALL": whenever an item begins with "for all …", use the TodoWrite tool to create a separate task for each relevant endpoint/flow that needs to be analyzed.
|
NOTE ON "FOR ALL": whenever an item begins with "for all …", use the TodoWrite tool to create a separate task for each relevant endpoint/flow that needs to be analyzed.
|
||||||
From `.shannon/deliverables/pre_recon_deliverable.md`, use Section 3 and 6 to help guide your tasks.
|
From `deliverables/pre_recon_deliverable.md`, use Section 3 and 6 to help guide your tasks.
|
||||||
|
|
||||||
## 1) Transport & caching
|
## 1) Transport & caching
|
||||||
- For all auth endpoints, enforce HTTPS (no HTTP fallbacks/hops); verify HSTS at the edge. (for all: use TodoWrite tool to add each endpoint as a task)
|
- For all auth endpoints, enforce HTTPS (no HTTP fallbacks/hops); verify HSTS at the edge. (for all: use TodoWrite tool to add each endpoint as a task)
|
||||||
@@ -181,7 +181,7 @@ Rule: when uncertain, round down (favor Medium/Low) to minimize false positives.
|
|||||||
# Documenting Findings (MANDATORY)
|
# Documenting Findings (MANDATORY)
|
||||||
For each check you perform from the list above (Transport, Rate Limiting, Session Management, etc.), you must make a final **verdict**.
|
For each check you perform from the list above (Transport, Rate Limiting, Session Management, etc.), you must make a final **verdict**.
|
||||||
|
|
||||||
- If the verdict is **`vulnerable`**, you must include the finding in your exploitation queue.
|
- If the verdict is **`vulnerable`**, you must document the finding using the save-deliverable CLI to update the exploitation queue.
|
||||||
- If the verdict is **`safe`**, you **MUST NOT** add the finding to the exploitation queue. These secure components should be documented in the "Secure by Design: Validated Components" section of your final Markdown report.
|
- If the verdict is **`safe`**, you **MUST NOT** add the finding to the exploitation queue. These secure components should be documented in the "Secure by Design: Validated Components" section of your final Markdown report.
|
||||||
</methodology>
|
</methodology>
|
||||||
|
|
||||||
@@ -237,20 +237,27 @@ These components were analyzed and found to have robust defenses. They are low-p
|
|||||||
|
|
||||||
</deliverable_instructions>
|
</deliverable_instructions>
|
||||||
|
|
||||||
|
<exploitation_queue_requirements>
|
||||||
|
**Exploitation Queue (MANDATORY)**
|
||||||
|
Regardless of whether vulnerabilities are found, you MUST create the exploitation queue using the save-deliverable CLI tool:
|
||||||
|
- **If vulnerabilities found:** Run save-deliverable CLI with `--type AUTH_QUEUE --content '{"vulnerabilities": [...]}'` with each confirmed hypothesis following the `exploitation_queue_format`. Set `externally_exploitable` to `true` ONLY if exploitable via public internet without internal access.
|
||||||
|
- **If no vulnerabilities found:** Run save-deliverable CLI with `--type AUTH_QUEUE --content '{"vulnerabilities": []}'`.
|
||||||
|
- **QUEUE INCLUSION CRITERIA:** ONLY include vulnerabilities where `externally_exploitable = true`. Exclude any vulnerability requiring internal network access, VPN, or direct server access.
|
||||||
|
This file serves as the handoff mechanism and must always be created to signal completion.
|
||||||
|
</exploitation_queue_requirements>
|
||||||
|
|
||||||
<conclusion_trigger>
|
<conclusion_trigger>
|
||||||
**COMPLETION REQUIREMENTS (ALL must be satisfied):**
|
**COMPLETION REQUIREMENTS (ALL must be satisfied):**
|
||||||
|
|
||||||
1. **Systematic Analysis:** ALL relevant API endpoints and user-facing features identified in the reconnaissance deliverable must be analyzed for AuthN/AuthZ flaws.
|
1. **Systematic Analysis:** ALL relevant API endpoints and user-facing features identified in the reconnaissance deliverable must be analyzed for AuthN/AuthZ flaws.
|
||||||
2. **Deliverable Generation:** Your deliverable must be successfully saved using the save-deliverable CLI tool:
|
2. **Deliverable Generation:** Both required deliverables must be successfully saved using the save-deliverable CLI tool:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/auth_analysis_deliverable.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/auth_analysis_deliverable.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type AUTH_ANALYSIS --file-path ".shannon/deliverables/auth_analysis_deliverable.md"`
|
4. Run `save-deliverable` with `--type AUTH_ANALYSIS --file-path "deliverables/auth_analysis_deliverable.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
- Exploitation queue: Run save-deliverable CLI with `--type AUTH_QUEUE --content '{"vulnerabilities": [...]}'`
|
||||||
**Note:** Save your deliverable markdown via save-deliverable first. The exploitation queue is captured automatically at the end of your session.
|
|
||||||
|
|
||||||
**ONLY AFTER** both systematic analysis AND successful deliverable generation, announce "**AUTH ANALYSIS COMPLETE**" and stop.
|
**ONLY AFTER** both systematic analysis AND successful deliverable generation, announce "**AUTH ANALYSIS COMPLETE**" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Success criterion: A complete, code-backed analysis of every potential authoriza
|
|||||||
</critical>
|
</critical>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your **primary source of truth** for authorization test targets is the reconnaissance report at `.shannon/deliverables/recon_deliverable.md`. Look specifically for:
|
- Your **primary source of truth** for authorization test targets is the reconnaissance report at `deliverables/recon_deliverable.md`. Look specifically for:
|
||||||
- **"Horizontal" section:** Endpoints where users access resources by ID that might belong to other users
|
- **"Horizontal" section:** Endpoints where users access resources by ID that might belong to other users
|
||||||
- **"Vertical" section:** Admin/privileged endpoints that regular users shouldn't access
|
- **"Vertical" section:** Admin/privileged endpoints that regular users shouldn't access
|
||||||
- **"Context" section:** Multi-step workflows where order/state matters
|
- **"Context" section:** Multi-step workflows where order/state matters
|
||||||
@@ -44,8 +44,8 @@ Success criterion: A complete, code-backed analysis of every potential authoriza
|
|||||||
|
|
||||||
**Phase Sequence:** RECON (Complete) → **AUTHZ ANALYSIS (You)** → EXPLOITATION (next phase)
|
**Phase Sequence:** RECON (Complete) → **AUTHZ ANALYSIS (You)** → EXPLOITATION (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/recon_deliverable.md`, `.shannon/deliverables/pre_recon_deliverable.md` (reconnaissance findings)
|
**Your Input:** `deliverables/recon_deliverable.md`, `deliverables/pre_recon_deliverable.md` (reconnaissance findings)
|
||||||
**Your Output:** `.shannon/deliverables/authz_analysis_deliverable.md` (analysis report for exploitation phase)
|
**Your Output:** `deliverables/authz_exploitation_queue.json` (actionable vulnerabilities for exploitation)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture, endpoints, and data flows
|
- Reconnaissance agent mapped application architecture, endpoints, and data flows
|
||||||
@@ -81,10 +81,10 @@ An **exploitable vulnerability** is a logical flaw in the code that represents a
|
|||||||
**Available Tools:**
|
**Available Tools:**
|
||||||
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, trace authorization logic paths, and understand permission models. MANDATORY for all source code analysis.
|
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, trace authorization logic paths, and understand permission models. MANDATORY for all source code analysis.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
- **Browser Automation (playwright-cli skill):** For browser interactions with the live web application (multi-step flows and role-based access controls), invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** For browser interactions with the live web application (multi-step flows and role-based access controls), invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each endpoint that needs authorization analysis. Mark items as "in_progress" when working on them and "completed" when done.
|
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each endpoint that needs authorization analysis. Mark items as "in_progress" when working on them and "completed" when done.
|
||||||
@@ -93,7 +93,7 @@ An **exploitable vulnerability** is a logical flaw in the code that represents a
|
|||||||
<data_format_specifications>
|
<data_format_specifications>
|
||||||
|
|
||||||
<exploitation_queue_format>
|
<exploitation_queue_format>
|
||||||
**Purpose:** Defines the format of the exploitation queue JSON.
|
**Purpose:** Defines the structure for a "exploitation queue" saved via the save-deliverable CLI with --type AUTHZ_QUEUE.
|
||||||
**Structure:** The `vulnerability` JSON object MUST follow this exact format:
|
**Structure:** The `vulnerability` JSON object MUST follow this exact format:
|
||||||
{
|
{
|
||||||
"ID": "unique ID for each vulnerability (e.g., AUTHZ-VULN-01)",
|
"ID": "unique ID for each vulnerability (e.g., AUTHZ-VULN-01)",
|
||||||
@@ -125,7 +125,7 @@ An **exploitable vulnerability** is a logical flaw in the code that represents a
|
|||||||
### 1) Horizontal Authorization Analysis
|
### 1) Horizontal Authorization Analysis
|
||||||
|
|
||||||
- **Create To Dos:**
|
- **Create To Dos:**
|
||||||
For each item listed under **`.shannon/deliverables/recon_deliverable.md` → section 8 "Horizontal"*, use the TodoWrite tool to create a task entry.
|
For each item listed under **`deliverables/recon_deliverable.md` → section 8 "Horizontal"*, use the TodoWrite tool to create a task entry.
|
||||||
|
|
||||||
- **Process:**
|
- **Process:**
|
||||||
- Start at the identified endpoint.
|
- Start at the identified endpoint.
|
||||||
@@ -157,7 +157,7 @@ An **exploitable vulnerability** is a logical flaw in the code that represents a
|
|||||||
### 2) Vertical Authorization Analysis
|
### 2) Vertical Authorization Analysis
|
||||||
|
|
||||||
- **Create To Dos:**
|
- **Create To Dos:**
|
||||||
For each item listed under **`.shannon/deliverables/recon_deliverable.md` → section 8 "Vertical"**, use the TodoWrite tool to create a task entry.
|
For each item listed under **`deliverables/recon_deliverable.md` → section 8 "Vertical"**, use the TodoWrite tool to create a task entry.
|
||||||
|
|
||||||
- **Process:**
|
- **Process:**
|
||||||
- Start at the identified endpoint.
|
- Start at the identified endpoint.
|
||||||
@@ -183,7 +183,7 @@ An **exploitable vulnerability** is a logical flaw in the code that represents a
|
|||||||
### 3) Context / Workflow Authorization Analysis
|
### 3) Context / Workflow Authorization Analysis
|
||||||
|
|
||||||
- **Create To Dos:**
|
- **Create To Dos:**
|
||||||
For each item listed under **`.shannon/deliverables/recon_deliverable.md` → section 8 "Context"**, use the TodoWrite tool to create a task entry.
|
For each item listed under **`deliverables/recon_deliverable.md` → section 8 "Context"**, use the TodoWrite tool to create a task entry.
|
||||||
|
|
||||||
- **Process:**
|
- **Process:**
|
||||||
- Start at the endpoint that represents a step in a workflow.
|
- Start at the endpoint that represents a step in a workflow.
|
||||||
@@ -238,7 +238,7 @@ An **exploitable vulnerability** is a logical flaw in the code that represents a
|
|||||||
|
|
||||||
For each analysis you perform from the lists above, you must make a final **verdict**:
|
For each analysis you perform from the lists above, you must make a final **verdict**:
|
||||||
|
|
||||||
- If the verdict is **`vulnerable`**, you must include the finding in your exploitation queue.
|
- If the verdict is **`vulnerable`**, you must document the finding using the save-deliverable CLI to update the exploitation queue.
|
||||||
- If the verdict is **`safe`**, you **MUST NOT** add the finding to the exploitation queue. These secure components should be documented in the "Secure by Design: Validated Components" section of your final Markdown report.
|
- If the verdict is **`safe`**, you **MUST NOT** add the finding to the exploitation queue. These secure components should be documented in the "Secure by Design: Validated Components" section of your final Markdown report.
|
||||||
</methodology>
|
</methodology>
|
||||||
|
|
||||||
@@ -340,20 +340,27 @@ examples:
|
|||||||
|
|
||||||
</deliverable_instructions>
|
</deliverable_instructions>
|
||||||
|
|
||||||
|
<exploitation_queue_requirements>
|
||||||
|
**Exploitation Queue (MANDATORY)**
|
||||||
|
Regardless of whether vulnerabilities are found, you MUST create the exploitation queue using the save-deliverable CLI tool:
|
||||||
|
- **If vulnerabilities found:** Run save-deliverable CLI with `--type AUTHZ_QUEUE --content '{"vulnerabilities": [...]}'` with each confirmed hypothesis following the `exploitation_queue_format`. Set `externally_exploitable` to `true` ONLY if exploitable via public internet without internal access.
|
||||||
|
- **If no vulnerabilities found:** Run save-deliverable CLI with `--type AUTHZ_QUEUE --content '{"vulnerabilities": []}'`.
|
||||||
|
- **QUEUE INCLUSION CRITERIA:** ONLY include vulnerabilities where `externally_exploitable = true`. Exclude any vulnerability requiring internal network access, VPN, or direct server access.
|
||||||
|
This file serves as the handoff mechanism and must always be created to signal completion.
|
||||||
|
</exploitation_queue_requirements>
|
||||||
|
|
||||||
<conclusion_trigger>
|
<conclusion_trigger>
|
||||||
**COMPLETION REQUIREMENTS (ALL must be satisfied):**
|
**COMPLETION REQUIREMENTS (ALL must be satisfied):**
|
||||||
|
|
||||||
1. **Todo Completion:** ALL tasks in your TodoWrite list must be marked as "completed"
|
1. **Todo Completion:** ALL tasks in your TodoWrite list must be marked as "completed"
|
||||||
2. **Deliverable Generation:** Your deliverable must be successfully saved using the save-deliverable CLI tool:
|
2. **Deliverable Generation:** Both required deliverables must be successfully saved using the save-deliverable CLI tool:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/authz_analysis_deliverable.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/authz_analysis_deliverable.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type AUTHZ_ANALYSIS --file-path ".shannon/deliverables/authz_analysis_deliverable.md"`
|
4. Run `save-deliverable` with `--type AUTHZ_ANALYSIS --file-path "deliverables/authz_analysis_deliverable.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
- Exploitation queue: Run save-deliverable CLI with `--type AUTHZ_QUEUE --content '{"vulnerabilities": [...]}'`
|
||||||
**Note:** Save your deliverable markdown via save-deliverable first. The exploitation queue is captured automatically at the end of your session.
|
|
||||||
|
|
||||||
**ONLY AFTER** both todo completion AND successful deliverable generation, announce "**AUTHORIZATION ANALYSIS COMPLETE**" and stop.
|
**ONLY AFTER** both todo completion AND successful deliverable generation, announce "**AUTHORIZATION ANALYSIS COMPLETE**" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ Success criterion: Complete source-to-sink traces detailing path, sanitizers, si
|
|||||||
</critical>
|
</critical>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your **single source of truth** for the application's structure is the reconnaissance report located at `.shannon/deliverables/recon_deliverable.md`. You must derive your list of testable targets from this file.
|
- Your **single source of truth** for the application's structure is the reconnaissance report located at `deliverables/recon_deliverable.md`. You must derive your list of testable targets from this file.
|
||||||
|
|
||||||
</starting_context>
|
</starting_context>
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ Success criterion: Complete source-to-sink traces detailing path, sanitizers, si
|
|||||||
|
|
||||||
**Phase Sequence:** RECON (Complete) → **INJECTION ANALYSIS (You)** → EXPLOITATION (next phase)
|
**Phase Sequence:** RECON (Complete) → **INJECTION ANALYSIS (You)** → EXPLOITATION (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/recon_deliverable.md` (reconnaissance findings)
|
**Your Input:** `deliverables/recon_deliverable.md` (reconnaissance findings)
|
||||||
**Your Output:** `.shannon/deliverables/injection_analysis_deliverable.md` (analysis report for exploitation phase)
|
**Your Output:** `deliverables/injection_exploitation_queue.json` (actionable vulnerabilities for exploitation)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture, attack surfaces, endpoints, input vectors
|
- Reconnaissance agent mapped application architecture, attack surfaces, endpoints, input vectors
|
||||||
@@ -81,10 +81,10 @@ An **exploitable vulnerability** is a confirmed source-to-sink path where the en
|
|||||||
**Available Tools:**
|
**Available Tools:**
|
||||||
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, map query/command construction paths, and verify sanitization coverage. MANDATORY for all source code analysis.
|
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, map query/command construction paths, and verify sanitization coverage. MANDATORY for all source code analysis.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
- **Browser Automation (playwright-cli skill):** For browser interactions with the live web application (multi-step flows like password reset or registration), invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** For browser interactions with the live web application (multi-step flows like password reset or registration), invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each injection source that needs analysis. Mark items as "in_progress" when working on them and "completed" when done.
|
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each injection source that needs analysis. Mark items as "in_progress" when working on them and "completed" when done.
|
||||||
@@ -93,7 +93,7 @@ An **exploitable vulnerability** is a confirmed source-to-sink path where the en
|
|||||||
<data_format_specifications>
|
<data_format_specifications>
|
||||||
|
|
||||||
<exploitation_queue_format>
|
<exploitation_queue_format>
|
||||||
**Purpose:** Defines the format of the exploitation queue JSON.
|
**Purpose:** Defines the structure for a "exploitation queue" saved via the save-deliverable CLI with --type INJECTION_QUEUE.
|
||||||
|
|
||||||
**Structure:** The `vulnerability` JSON object MUST follow this exact format:
|
**Structure:** The `vulnerability` JSON object MUST follow this exact format:
|
||||||
{
|
{
|
||||||
@@ -124,7 +124,7 @@ An **exploitable vulnerability** is a confirmed source-to-sink path where the en
|
|||||||
|
|
||||||
- **Goal:** Prove whether untrusted input can influence the **structure** of a backend command (SQL or Shell) or reach sensitive **slots** without the correct defense. No live exploitation in this phase.
|
- **Goal:** Prove whether untrusted input can influence the **structure** of a backend command (SQL or Shell) or reach sensitive **slots** without the correct defense. No live exploitation in this phase.
|
||||||
- **1) Create a To Do for each Injection Source found in the Pre-Recon Deliverable
|
- **1) Create a To Do for each Injection Source found in the Pre-Recon Deliverable
|
||||||
- inside of .shannon/deliverables/pre_recon_deliverable.md under the section "7. Injection Sources (Command Injection and SQL Injection)" use the TodoWrite tool to create a task for each discovered Injection Source.
|
- inside of deliverables/pre_recon_deliverable.md under the section "7. Injection Sources (Command Injection and SQL Injection)" use the TodoWrite tool to create a task for each discovered Injection Source.
|
||||||
- Note: All sources are marked as Tainted until they Hit a Santiization that matches the sink context. normalizers (lowercasing, trimming, JSON parse, schema decode) — still **tainted**.
|
- Note: All sources are marked as Tainted until they Hit a Santiization that matches the sink context. normalizers (lowercasing, trimming, JSON parse, schema decode) — still **tainted**.
|
||||||
- **2) Trace Data Flow Paths from Source to Sink**
|
- **2) Trace Data Flow Paths from Source to Sink**
|
||||||
- For each source, your goal is to identify every unique "Data Flow Path" to a database sink. A path is a distinct route the data takes through the code.
|
- For each source, your goal is to identify every unique "Data Flow Path" to a database sink. A path is a distinct route the data takes through the code.
|
||||||
@@ -147,8 +147,8 @@ An **exploitable vulnerability** is a confirmed source-to-sink path where the en
|
|||||||
- Include a short rationale (e.g., "context mismatch: regex escape on ORDER BY keyword slot").
|
- Include a short rationale (e.g., "context mismatch: regex escape on ORDER BY keyword slot").
|
||||||
- If concat occurred **after** sanitization, treat that sanitization as **non-effective** for this path.
|
- If concat occurred **after** sanitization, treat that sanitization as **non-effective** for this path.
|
||||||
- **6) Append to findings list (consistent fields)**
|
- **6) Append to findings list (consistent fields)**
|
||||||
- **If the verdict is `vulnerable`:** Include the finding in your exploitation queue. Set `externally_exploitable` to `true` ONLY if exploitable via public internet without internal access. Ensure all fields in the `exploitation_queue_format`, including a minimal `witness_payload`, are populated.
|
- **If the verdict is `vulnerable`:** Use the Write tool to log the finding in `deliverables/injection_exploitation_queue.json`. Set `externally_exploitable` to `true` ONLY if exploitable via public internet without internal access. Ensure all fields in the `exploitation_queue_format`, including a minimal `witness_payload`, are populated.
|
||||||
- **If the verdict is `safe`:** DO NOT add the finding to the exploitation queue. These secure vectors must be documented later in the "Vectors Analyzed and Confirmed Secure" section of your final Markdown report (`.shannon/deliverables/injection_analysis_deliverable.md`).
|
- **If the verdict is `safe`:** DO NOT add the finding to the exploitation queue. These secure vectors must be documented later in the "Vectors Analyzed and Confirmed Secure" section of your final Markdown report (`deliverables/injection_analysis_deliverable.md`).
|
||||||
- **If a single source is found to be vulnerable via multiple, distinct paths to different sinks, you must create a separate vulnerability entry in the exploitation queue for each unique vulnerable path.**
|
- **If a single source is found to be vulnerable via multiple, distinct paths to different sinks, you must create a separate vulnerability entry in the exploitation queue for each unique vulnerable path.**
|
||||||
- **QUEUE INCLUSION CRITERIA:** ONLY include vulnerabilities where `externally_exploitable = true`. Exclude any vulnerability requiring internal network access, VPN, or direct server access.
|
- **QUEUE INCLUSION CRITERIA:** ONLY include vulnerabilities where `externally_exploitable = true`. Exclude any vulnerability requiring internal network access, VPN, or direct server access.
|
||||||
|
|
||||||
@@ -287,7 +287,7 @@ When you have systematically analyzed all input vectors, you MUST generate two f
|
|||||||
|
|
||||||
**1. Your Specialist Deliverable**
|
**1. Your Specialist Deliverable**
|
||||||
|
|
||||||
First, synthesize all of your findings into a single, detailed Markdown report located at `.shannon/deliverables/injection_analysis_deliverable.md`. This report is the official record of your work.
|
First, synthesize all of your findings into a single, detailed Markdown report located at `deliverables/injection_analysis_deliverable.md`. This report is the official record of your work.
|
||||||
|
|
||||||
Your report MUST use the following structure precisely:
|
Your report MUST use the following structure precisely:
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ Your report MUST use the following structure precisely:
|
|||||||
## 1. Executive Summary
|
## 1. Executive Summary
|
||||||
|
|
||||||
- **Analysis Status:** Complete
|
- **Analysis Status:** Complete
|
||||||
- **Key Outcome:** Several high-confidence SQL injection injection vulnerabilities (both SQLi and Command Injection) were identified. All findings have been passed to the exploitation phase.
|
- **Key Outcome:** Several high-confidence SQL injection injection vulnerabilities (both SQLi and Command Injection) were identified. All findings have been passed to the exploitation phase via the machine-readable queue at `deliverables/injection_exploitation_queue.json`.
|
||||||
- **Purpose of this Document:** This report provides the strategic context, dominant patterns, and environmental intelligence necessary to effectively exploit the vulnerabilities listed in the queue. It is intended to be read alongside the JSON deliverable.
|
- **Purpose of this Document:** This report provides the strategic context, dominant patterns, and environmental intelligence necessary to effectively exploit the vulnerabilities listed in the queue. It is intended to be read alongside the JSON deliverable.
|
||||||
|
|
||||||
|
|
||||||
@@ -345,20 +345,29 @@ These input vectors were traced and confirmed to have robust, context-appropriat
|
|||||||
|
|
||||||
</deliverable_instructions>
|
</deliverable_instructions>
|
||||||
|
|
||||||
|
<exploitation_queue_requirements>
|
||||||
|
**Exploitation Queue (MANDATORY)**
|
||||||
|
|
||||||
|
Regardless of whether vulnerabilities are found, you MUST create the exploitation queue using the save-deliverable CLI tool:
|
||||||
|
|
||||||
|
- **If vulnerabilities found:** Run save-deliverable CLI with `--type INJECTION_QUEUE --content '{"vulnerabilities": [...]}'` with each exploitable injection vulnerability (verdict: "vulnerable") following the exploitation_queue_format
|
||||||
|
- **If no vulnerabilities found:** Run save-deliverable CLI with `--type INJECTION_QUEUE --content '{"vulnerabilities": []}'`
|
||||||
|
|
||||||
|
This file serves as the handoff mechanism to the Exploitation phase and must always be created to signal completion of your analysis.
|
||||||
|
</exploitation_queue_requirements>
|
||||||
|
|
||||||
<conclusion_trigger>
|
<conclusion_trigger>
|
||||||
**COMPLETION REQUIREMENTS (ALL must be satisfied):**
|
**COMPLETION REQUIREMENTS (ALL must be satisfied):**
|
||||||
|
|
||||||
1. **Todo Completion:** ALL tasks in your TodoWrite list must be marked as "completed"
|
1. **Todo Completion:** ALL tasks in your TodoWrite list must be marked as "completed"
|
||||||
2. **Deliverable Generation:** Your deliverable must be successfully saved using the save-deliverable CLI tool:
|
2. **Deliverable Generation:** Both required deliverables must be successfully saved using the save-deliverable CLI tool:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/injection_analysis_deliverable.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/injection_analysis_deliverable.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type INJECTION_ANALYSIS --file-path ".shannon/deliverables/injection_analysis_deliverable.md"`
|
4. Run `save-deliverable` with `--type INJECTION_ANALYSIS --file-path "deliverables/injection_analysis_deliverable.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
- Exploitation queue: Run save-deliverable CLI with `--type INJECTION_QUEUE --content '{"vulnerabilities": [...]}'`
|
||||||
**Note:** Save your deliverable markdown via save-deliverable first. The exploitation queue is captured automatically at the end of your session.
|
|
||||||
|
|
||||||
**ONLY AFTER** both todo completion AND successful deliverable generation, announce "**INJECTION ANALYSIS COMPLETE**" and stop.
|
**ONLY AFTER** both todo completion AND successful deliverable generation, announce "**INJECTION ANALYSIS COMPLETE**" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Success criterion: A complete source-to-sink trace for every identified SSRF vul
|
|||||||
</critical>
|
</critical>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your primary source of truth for the application's structure is the reconnaissance report located at `.shannon/deliverables/recon_deliverable.md`. You must derive your list of testable endpoints from this file.
|
- Your primary source of truth for the application's structure is the reconnaissance report located at `deliverables/recon_deliverable.md`. You must derive your list of testable endpoints from this file.
|
||||||
</starting_context>
|
</starting_context>
|
||||||
|
|
||||||
<system_architecture>
|
<system_architecture>
|
||||||
@@ -40,8 +40,8 @@ Success criterion: A complete source-to-sink trace for every identified SSRF vul
|
|||||||
|
|
||||||
**Phase Sequence:** RECON (Complete) → **SSRF ANALYSIS (You)** → EXPLOITATION (next phase)
|
**Phase Sequence:** RECON (Complete) → **SSRF ANALYSIS (You)** → EXPLOITATION (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/recon_deliverable.md`, `.shannon/deliverables/pre_recon_deliverable.md` (reconnaissance findings)
|
**Your Input:** `deliverables/recon_deliverable.md`, `deliverables/pre_recon_deliverable.md` (reconnaissance findings)
|
||||||
**Your Output:** `.shannon/deliverables/ssrf_analysis_deliverable.md` (analysis report for exploitation phase)
|
**Your Output:** `deliverables/ssrf_exploitation_queue.json` (actionable vulnerabilities for exploitation)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture, endpoints, and data flows
|
- Reconnaissance agent mapped application architecture, endpoints, and data flows
|
||||||
@@ -77,10 +77,10 @@ An **exploitable vulnerability** is a data flow where user-controlled input infl
|
|||||||
**Available Tools:**
|
**Available Tools:**
|
||||||
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, trace data flows, and understand HTTP client usage. MANDATORY for all source code analysis.
|
- **Task Agent (Code Analysis):** Your primary tool. Use it to ask targeted questions about the source code, trace data flows, and understand HTTP client usage. MANDATORY for all source code analysis.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
- **Browser Automation (playwright-cli skill):** For browser interactions with the live web application (multi-step flows involving URL redirection or proxy functionality), invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** For browser interactions with the live web application (multi-step flows involving URL redirection or proxy functionality), invoke the `playwright-cli` skill to learn available commands. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each SSRF sink that needs analysis. Mark items as "in_progress" when working on them and "completed" when done.
|
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each SSRF sink that needs analysis. Mark items as "in_progress" when working on them and "completed" when done.
|
||||||
@@ -89,7 +89,7 @@ An **exploitable vulnerability** is a data flow where user-controlled input infl
|
|||||||
<data_format_specifications>
|
<data_format_specifications>
|
||||||
|
|
||||||
<exploitation_queue_format>
|
<exploitation_queue_format>
|
||||||
**Purpose:** Defines the format of the exploitation queue JSON.
|
**Purpose:** Defines the structure for a "exploitation queue" saved via the save-deliverable CLI with --type SSRF_QUEUE.
|
||||||
**Structure:** The `vulnerability` JSON object MUST follow this exact format:
|
**Structure:** The `vulnerability` JSON object MUST follow this exact format:
|
||||||
{
|
{
|
||||||
"ID": "unique ID for each vulnerability json (SSRF-VULN-XX)",
|
"ID": "unique ID for each vulnerability json (SSRF-VULN-XX)",
|
||||||
@@ -114,7 +114,7 @@ An **exploitable vulnerability** is a data flow where user-controlled input infl
|
|||||||
# White-Box Vulnerability Analysis Procedure: Server-Side Request Forgery (SSRF)
|
# White-Box Vulnerability Analysis Procedure: Server-Side Request Forgery (SSRF)
|
||||||
|
|
||||||
NOTE ON "FOR ALL": whenever an item begins with "for all …", use the TodoWrite tool to create a separate task for each relevant endpoint/flow that needs to be analyzed.
|
NOTE ON "FOR ALL": whenever an item begins with "for all …", use the TodoWrite tool to create a separate task for each relevant endpoint/flow that needs to be analyzed.
|
||||||
From `.shannon/deliverables/pre_recon_deliverable.md`, use Section 10 (SSRF Sinks) to guide your tasks.
|
From `deliverables/pre_recon_deliverable.md`, use Section 10 (SSRF Sinks) to guide your tasks.
|
||||||
|
|
||||||
## 1) Identify HTTP Client Usage Patterns
|
## 1) Identify HTTP Client Usage Patterns
|
||||||
- For all endpoints that accept URL parameters, callback URLs, webhook URLs, or file paths, trace how these inputs are processed.
|
- For all endpoints that accept URL parameters, callback URLs, webhook URLs, or file paths, trace how these inputs are processed.
|
||||||
@@ -166,7 +166,7 @@ From `.shannon/deliverables/pre_recon_deliverable.md`, use Section 10 (SSRF Sink
|
|||||||
|
|
||||||
### **1) Create a To-Do Item for Each SSRF Sink**
|
### **1) Create a To-Do Item for Each SSRF Sink**
|
||||||
|
|
||||||
Inside `.shannon/deliverables/pre_recon_deliverable.md` under section `##10. SSRF Sinks##`.
|
Inside `deliverables/pre_recon_deliverable.md` under section `##10. SSRF Sinks##`.
|
||||||
|
|
||||||
Use the TodoWrite tool to create a task for each discovered sink (any server-side request composed even partially from user input).
|
Use the TodoWrite tool to create a task for each discovered sink (any server-side request composed even partially from user input).
|
||||||
|
|
||||||
@@ -228,7 +228,7 @@ Rule: when uncertain, round down (favor Medium/Low) to minimize false positives.
|
|||||||
# Documenting Findings (MANDATORY)
|
# Documenting Findings (MANDATORY)
|
||||||
For each check you perform from the list above, you must make a final **verdict**.
|
For each check you perform from the list above, you must make a final **verdict**.
|
||||||
|
|
||||||
- If the verdict is **`vulnerable`**, you must include the finding in your exploitation queue.
|
- If the verdict is **`vulnerable`**, you must document the finding using the save-deliverable CLI to update the exploitation queue.
|
||||||
- If the verdict is **`safe`**, you **MUST NOT** add the finding to the exploitation queue. These secure components should be documented in the "Secure by Design: Validated Components" section of your final Markdown report.
|
- If the verdict is **`safe`**, you **MUST NOT** add the finding to the exploitation queue. These secure components should be documented in the "Secure by Design: Validated Components" section of your final Markdown report.
|
||||||
</methodology>
|
</methodology>
|
||||||
|
|
||||||
@@ -284,20 +284,27 @@ These components were analyzed and found to have robust defenses. They are low-p
|
|||||||
|
|
||||||
</deliverable_instructions>
|
</deliverable_instructions>
|
||||||
|
|
||||||
|
<exploitation_queue_requirements>
|
||||||
|
**Exploitation Queue (MANDATORY)**
|
||||||
|
Regardless of whether vulnerabilities are found, you MUST create the exploitation queue using the save-deliverable CLI tool:
|
||||||
|
- **If vulnerabilities found:** Run save-deliverable CLI with `--type SSRF_QUEUE --content '{"vulnerabilities": [...]}'` with each confirmed hypothesis following the `exploitation_queue_format`. Set `externally_exploitable` to `true` ONLY if exploitable via public internet without internal access.
|
||||||
|
- **If no vulnerabilities found:** Run save-deliverable CLI with `--type SSRF_QUEUE --content '{"vulnerabilities": []}'`.
|
||||||
|
- **QUEUE INCLUSION CRITERIA:** ONLY include vulnerabilities where `externally_exploitable = true`. Exclude any vulnerability requiring internal network access, VPN, or direct server access.
|
||||||
|
This file serves as the handoff mechanism and must always be created to signal completion.
|
||||||
|
</exploitation_queue_requirements>
|
||||||
|
|
||||||
<conclusion_trigger>
|
<conclusion_trigger>
|
||||||
**COMPLETION REQUIREMENTS (ALL must be satisfied):**
|
**COMPLETION REQUIREMENTS (ALL must be satisfied):**
|
||||||
|
|
||||||
1. **Systematic Analysis:** ALL relevant API endpoints and request-making features identified in the reconnaissance deliverable must be analyzed for SSRF vulnerabilities.
|
1. **Systematic Analysis:** ALL relevant API endpoints and request-making features identified in the reconnaissance deliverable must be analyzed for SSRF vulnerabilities.
|
||||||
2. **Deliverable Generation:** Your deliverable must be successfully saved using the save-deliverable CLI tool:
|
2. **Deliverable Generation:** Both required deliverables must be successfully saved using the save-deliverable CLI tool:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/ssrf_analysis_deliverable.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/ssrf_analysis_deliverable.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type SSRF_ANALYSIS --file-path ".shannon/deliverables/ssrf_analysis_deliverable.md"`
|
4. Run `save-deliverable` with `--type SSRF_ANALYSIS --file-path "deliverables/ssrf_analysis_deliverable.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
- Exploitation queue: Run save-deliverable CLI with `--type SSRF_QUEUE --content '{"vulnerabilities": [...]}'`
|
||||||
**Note:** Save your deliverable markdown via save-deliverable first. The exploitation queue is captured automatically at the end of your session.
|
|
||||||
|
|
||||||
**ONLY AFTER** both systematic analysis AND successful deliverable generation, announce "**SSRF ANALYSIS COMPLETE**" and stop.
|
**ONLY AFTER** both systematic analysis AND successful deliverable generation, announce "**SSRF ANALYSIS COMPLETE**" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Success criterion: Live confirmation of XSS execution for every vulnerability th
|
|||||||
</critical>
|
</critical>
|
||||||
|
|
||||||
<starting_context>
|
<starting_context>
|
||||||
- Your primary source of truth for the application's structure is the reconnaissance report located at .shannon/deliverables/recon_deliverable.md. You must derive your list of testable targets from this file.
|
- Your primary source of truth for the application's structure is the reconnaissance report located at deliverables/recon_deliverable.md. You must derive your list of testable targets from this file.
|
||||||
</starting_context>
|
</starting_context>
|
||||||
|
|
||||||
<system_architecture>
|
<system_architecture>
|
||||||
@@ -40,8 +40,8 @@ Success criterion: Live confirmation of XSS execution for every vulnerability th
|
|||||||
|
|
||||||
**Phase Sequence:** RECON (Complete) → **XSS ANALYSIS (You)** → EXPLOITATION (next phase)
|
**Phase Sequence:** RECON (Complete) → **XSS ANALYSIS (You)** → EXPLOITATION (next phase)
|
||||||
|
|
||||||
**Your Input:** `.shannon/deliverables/recon_deliverable.md`, `.shannon/deliverables/pre_recon_deliverable.md` (reconnaissance findings)
|
**Your Input:** `deliverables/recon_deliverable.md`, `deliverables/pre_recon_deliverable.md` (reconnaissance findings)
|
||||||
**Your Output:** `.shannon/deliverables/xss_analysis_deliverable.md` (analysis report for exploitation phase)
|
**Your Output:** `deliverables/xss_exploitation_queue.json` (actionable vulnerabilities for exploitation)
|
||||||
|
|
||||||
**WHAT HAPPENED BEFORE YOU:**
|
**WHAT HAPPENED BEFORE YOU:**
|
||||||
- Reconnaissance agent mapped application architecture
|
- Reconnaissance agent mapped application architecture
|
||||||
@@ -72,7 +72,7 @@ An **exploitable vulnerability** is a confirmed source-to-sink path where the en
|
|||||||
|
|
||||||
**CRITICAL TOOL USAGE RESTRICTIONS:**
|
**CRITICAL TOOL USAGE RESTRICTIONS:**
|
||||||
- NEVER use the Read tool for application source code analysis - ALWAYS delegate to Task agents for examining .js, .ts, .py, .php files and application logic. You MAY use Read
|
- NEVER use the Read tool for application source code analysis - ALWAYS delegate to Task agents for examining .js, .ts, .py, .php files and application logic. You MAY use Read
|
||||||
tool directly for these files: `.shannon/deliverables/pre_recon_deliverable.md`, `.shannon/deliverables/recon_deliverable.md`
|
tool directly for these files: `deliverables/pre_recon_deliverable.md`, `deliverables/recon_deliverable.md`
|
||||||
- Direct the Task Agent to trace render contexts, sanitization coverage, and template/component boundaries before deciding on exploitability.
|
- Direct the Task Agent to trace render contexts, sanitization coverage, and template/component boundaries before deciding on exploitability.
|
||||||
- **ALWAYS delegate code analysis to Task agents**
|
- **ALWAYS delegate code analysis to Task agents**
|
||||||
|
|
||||||
@@ -82,17 +82,17 @@ An **exploitable vulnerability** is a confirmed source-to-sink path where the en
|
|||||||
- **Browser Automation (playwright-cli skill):** MANDATORY for testing DOM-based XSS and form submission vectors. Invoke the `playwright-cli` skill to learn available commands. Use for stored XSS testing and client-side payload execution verification. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
- **Browser Automation (playwright-cli skill):** MANDATORY for testing DOM-based XSS and form submission vectors. Invoke the `playwright-cli` skill to learn available commands. Use for stored XSS testing and client-side payload execution verification. Always pass `-s={{PLAYWRIGHT_SESSION}}` to every command for session isolation.
|
||||||
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each sink you need to analyze.
|
- **TodoWrite Tool:** Use this to create and manage your analysis task list. Create a todo item for each sink you need to analyze.
|
||||||
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
- **save-deliverable (CLI Tool):** Saves your deliverable files with automatic validation.
|
||||||
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<text>'`
|
- **Usage:** `save-deliverable --type <TYPE> --file-path <path>` or `--content '<json>'`
|
||||||
- **Returns:** JSON to stdout: `{"status":"success","filepath":"..."}` or `{"status":"error","message":"...","retryable":true}`
|
- **Returns:** JSON to stdout: `{"status":"success","filepath":"...","validated":true}` or `{"status":"error","message":"...","retryable":true}`
|
||||||
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
- **For large reports:** Write to disk first, then use `--file-path`. Do NOT pass large reports via `--content`.
|
||||||
|
- **For JSON queues:** You may use `--content '{"vulnerabilities": [...]}'`. Queue files are validated automatically.
|
||||||
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
- **Bash tool:** Use for creating directories, copying files, and other shell commands as needed.
|
||||||
</cli_tools>
|
</cli_tools>
|
||||||
|
|
||||||
<data_format_specifications>
|
<data_format_specifications>
|
||||||
|
|
||||||
<exploitation_queue_format>
|
<exploitation_queue_format>
|
||||||
Purpose: Defines the structure of the agent's final structured response.
|
Purpose: Defines the structure for a "exploitation queue" saved via the save-deliverable CLI with --type XSS_QUEUE.
|
||||||
Structure: The vulnerability JSON object MUST follow this exact format:
|
Structure: The vulnerability JSON object MUST follow this exact format:
|
||||||
{
|
{
|
||||||
"ID": "unique ID for each vulnerability json (XSS-VULN-XX)",
|
"ID": "unique ID for each vulnerability json (XSS-VULN-XX)",
|
||||||
@@ -124,7 +124,7 @@ Structure: The vulnerability JSON object MUST follow this exact format:
|
|||||||
- **Core Principle:** Data is assumed to be tainted until a context-appropriate output encoder (sanitization) is encountered on its path to the sink.
|
- **Core Principle:** Data is assumed to be tainted until a context-appropriate output encoder (sanitization) is encountered on its path to the sink.
|
||||||
|
|
||||||
### **1) Create a todo item for each XSS sink using the TodoWrite tool**
|
### **1) Create a todo item for each XSS sink using the TodoWrite tool**
|
||||||
Read .shannon/deliverables/pre_recon_deliverable.md section ##9. XSS Sinks and Render Contexts## and use the **TodoWrite tool** to create a todo item for each discovered sink-context pair that needs analysis.
|
Read deliverables/pre_recon_deliverable.md section ##9. XSS Sinks and Render Contexts## and use the **TodoWrite tool** to create a todo item for each discovered sink-context pair that needs analysis.
|
||||||
|
|
||||||
### **2) Trace Each Sink Backward (Backward Taint Analysis)**
|
### **2) Trace Each Sink Backward (Backward Taint Analysis)**
|
||||||
For each pending item in your todo list (managed via TodoWrite tool), trace the origin of the data variable backward from the sink through the application logic. Your goal is to find either a valid sanitizer or an untrusted source. Mark each todo item as completed after you've fully analyzed that sink.
|
For each pending item in your todo list (managed via TodoWrite tool), trace the origin of the data variable backward from the sink through the application logic. Your goal is to find either a valid sanitizer or an untrusted source. Mark each todo item as completed after you've fully analyzed that sink.
|
||||||
@@ -177,7 +177,7 @@ This rulebook is used for the **Early Termination** check in Step 2.
|
|||||||
- Include both safe and vulnerable paths to demonstrate **full coverage**.
|
- Include both safe and vulnerable paths to demonstrate **full coverage**.
|
||||||
- Craft a minimal `witness_payload` that proves control over the render context.
|
- Craft a minimal `witness_payload` that proves control over the render context.
|
||||||
- For every path analyzed, you must document the outcome. The location of the documentation depends on the verdict:
|
- For every path analyzed, you must document the outcome. The location of the documentation depends on the verdict:
|
||||||
- If the verdict is 'vulnerable', you MUST include the finding in your final structured response's exploitation queue, including complete source-to-sink information.
|
- If the verdict is 'vulnerable', you MUST use the save-deliverable CLI to save the finding to the exploitation queue, including complete source-to-sink information.
|
||||||
- If the verdict is 'safe', you MUST NOT add it to the exploitation queue. Instead, you will document these secure paths in the "Vectors Analyzed and Confirmed Secure" table of your final analysis report.
|
- If the verdict is 'safe', you MUST NOT add it to the exploitation queue. Instead, you will document these secure paths in the "Vectors Analyzed and Confirmed Secure" table of your final analysis report.
|
||||||
- For vulnerable findings, craft a minimal witness_payload that proves control over the render context.
|
- For vulnerable findings, craft a minimal witness_payload that proves control over the render context.
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ When you have systematically analyzed all input vectors, you MUST generate two f
|
|||||||
## 1. Your Specialist Deliverable
|
## 1. Your Specialist Deliverable
|
||||||
|
|
||||||
- First, synthesize all of your findings into a single, detailed Markdown report located at
|
- First, synthesize all of your findings into a single, detailed Markdown report located at
|
||||||
`.shannon/deliverables/xss_analysis_deliverable.md`.
|
`deliverables/xss_analysis_deliverable.md`.
|
||||||
|
|
||||||
- Your report MUST use the following structure precisely:
|
- Your report MUST use the following structure precisely:
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ When you have systematically analyzed all input vectors, you MUST generate two f
|
|||||||
|
|
||||||
## 1. Executive Summary
|
## 1. Executive Summary
|
||||||
- **Analysis Status:** Complete
|
- **Analysis Status:** Complete
|
||||||
- **Key Outcome:** Several high-confidence XSS vulnerabilities were identified. All findings have been passed to the exploitation phase.
|
- **Key Outcome:** Several high-confidence XSS vulnerabilities were identified. All findings have been passed to the exploitation phase via `deliverables/xss_exploitation_queue.json`.
|
||||||
- **Purpose of this Document:** This report provides the strategic context, dominant patterns, and environmental intelligence necessary to effectively exploit the vulnerabilities.
|
- **Purpose of this Document:** This report provides the strategic context, dominant patterns, and environmental intelligence necessary to effectively exploit the vulnerabilities.
|
||||||
|
|
||||||
## 2. Dominant Vulnerability Patterns
|
## 2. Dominant Vulnerability Patterns
|
||||||
@@ -265,20 +265,35 @@ These input vectors were traced and confirmed to have robust, context-appropriat
|
|||||||
|
|
||||||
</deliverable_instructions>
|
</deliverable_instructions>
|
||||||
|
|
||||||
|
<exploitation_queue_requirements>
|
||||||
|
|
||||||
|
## Exploitation Queue (MANDATORY)
|
||||||
|
|
||||||
|
Regardless of whether vulnerabilities are found, you MUST create the exploitation queue using the save-deliverable CLI tool.
|
||||||
|
|
||||||
|
- **If exploitable vulnerabilities found:**
|
||||||
|
Run save-deliverable CLI with `--type XSS_QUEUE --content '{"vulnerabilities": [...]}'` with each exploitable XSS vulnerability (verdict: "vulnerable") following the `exploitation_queue_format`. Set `externally_exploitable` to `true` ONLY if exploitable via public internet without internal access.
|
||||||
|
|
||||||
|
- **If no exploitable vulnerabilities found:**
|
||||||
|
Run save-deliverable CLI with `--type XSS_QUEUE --content '{"vulnerabilities": []}'`
|
||||||
|
|
||||||
|
- **QUEUE INCLUSION CRITERIA:** ONLY include vulnerabilities where `externally_exploitable = true`. Exclude any vulnerability requiring internal network access, VPN, or direct server access.
|
||||||
|
|
||||||
|
This file is the mandatory handoff to the Exploitation phase.
|
||||||
|
</exploitation_queue_requirements>
|
||||||
|
|
||||||
<conclusion_trigger>
|
<conclusion_trigger>
|
||||||
COMPLETION REQUIREMENTS (ALL must be satisfied):
|
COMPLETION REQUIREMENTS (ALL must be satisfied):
|
||||||
|
|
||||||
1. Systematic Analysis: ALL input vectors identified from the reconnaissance deliverable must be analyzed.
|
1. Systematic Analysis: ALL input vectors identified from the reconnaissance deliverable must be analyzed.
|
||||||
2. Deliverable Generation: Your deliverable must be successfully saved using the save-deliverable CLI tool:
|
2. Deliverable Generation: Both required deliverables must be successfully saved using the save-deliverable CLI tool:
|
||||||
- **CHUNKED WRITING (MANDATORY):**
|
- **CHUNKED WRITING (MANDATORY):**
|
||||||
1. Use the **Write** tool to create `.shannon/deliverables/xss_analysis_deliverable.md` with the title and first major section
|
1. Use the **Write** tool to create `deliverables/xss_analysis_deliverable.md` with the title and first major section
|
||||||
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
2. Use the **Edit** tool to append each remaining section — match the last few lines of the file, then replace with those lines plus the new section content
|
||||||
3. Repeat step 2 for all remaining sections
|
3. Repeat step 2 for all remaining sections
|
||||||
4. Run `save-deliverable` with `--type XSS_ANALYSIS --file-path ".shannon/deliverables/xss_analysis_deliverable.md"`
|
4. Run `save-deliverable` with `--type XSS_ANALYSIS --file-path "deliverables/xss_analysis_deliverable.md"`
|
||||||
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
**WARNING:** Do NOT write the entire report in a single tool call — exceeds 32K output token limit. Split into multiple Write/Edit operations.
|
||||||
|
- Exploitation queue: Run save-deliverable CLI with `--type XSS_QUEUE --content '{"vulnerabilities": [...]}'`
|
||||||
**Note:** Save your deliverable markdown via save-deliverable first. The exploitation queue is captured automatically at the end of your session.
|
|
||||||
|
|
||||||
ONLY AFTER both systematic analysis AND successful deliverable generation, announce "XSS ANALYSIS COMPLETE" and stop.
|
ONLY AFTER both systematic analysis AND successful deliverable generation, announce "XSS ANALYSIS COMPLETE" and stop.
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@
|
|||||||
|
|
||||||
// Production Claude agent execution with retry, git checkpoints, and audit logging
|
// Production Claude agent execution with retry, git checkpoints, and audit logging
|
||||||
|
|
||||||
import { type JsonSchemaOutputFormat, query } from '@anthropic-ai/claude-agent-sdk';
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
||||||
import { fs, path } from 'zx';
|
import { fs, path } from 'zx';
|
||||||
import type { AuditSession } from '../audit/index.js';
|
import type { AuditSession } from '../audit/index.js';
|
||||||
import { deliverablesDir } from '../paths.js';
|
|
||||||
import { isRetryableError, PentestError } from '../services/error-handling.js';
|
import { isRetryableError, PentestError } from '../services/error-handling.js';
|
||||||
import { AGENT_VALIDATORS } from '../session-manager.js';
|
import { AGENT_VALIDATORS } from '../session-manager.js';
|
||||||
import type { ActivityLogger } from '../types/activity-logger.js';
|
import type { ActivityLogger } from '../types/activity-logger.js';
|
||||||
@@ -21,6 +20,7 @@ import { dispatchMessage } from './message-handlers.js';
|
|||||||
import { type ModelTier, resolveModel } from './models.js';
|
import { type ModelTier, resolveModel } from './models.js';
|
||||||
import { detectExecutionContext, formatCompletionMessage, formatErrorOutput } from './output-formatters.js';
|
import { detectExecutionContext, formatCompletionMessage, formatErrorOutput } from './output-formatters.js';
|
||||||
import { createProgressManager } from './progress-manager.js';
|
import { createProgressManager } from './progress-manager.js';
|
||||||
|
import { getActualModelName } from './router-utils.js';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
var SHANNON_DISABLE_LOADER: boolean | undefined;
|
var SHANNON_DISABLE_LOADER: boolean | undefined;
|
||||||
@@ -39,7 +39,6 @@ export interface ClaudePromptResult {
|
|||||||
errorType?: string | undefined;
|
errorType?: string | undefined;
|
||||||
prompt?: string | undefined;
|
prompt?: string | undefined;
|
||||||
retryable?: boolean | undefined;
|
retryable?: boolean | undefined;
|
||||||
structuredOutput?: unknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function outputLines(lines: string[]): void {
|
function outputLines(lines: string[]): void {
|
||||||
@@ -72,7 +71,7 @@ async function writeErrorLog(
|
|||||||
},
|
},
|
||||||
duration,
|
duration,
|
||||||
};
|
};
|
||||||
const logPath = path.join(deliverablesDir(sourceDir), 'error.log');
|
const logPath = path.join(sourceDir, 'error.log');
|
||||||
await fs.appendFile(logPath, `${JSON.stringify(errorLog)}\n`);
|
await fs.appendFile(logPath, `${JSON.stringify(errorLog)}\n`);
|
||||||
} catch {
|
} catch {
|
||||||
// Best-effort error log writing - don't propagate failures
|
// Best-effort error log writing - don't propagate failures
|
||||||
@@ -88,8 +87,8 @@ export async function validateAgentOutput(
|
|||||||
logger.info(`Validating ${agentName} agent output`);
|
logger.info(`Validating ${agentName} agent output`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if agent completed successfully (text result OR structured output)
|
// Check if agent completed successfully
|
||||||
if (!result.success || (!result.result && result.structuredOutput === undefined)) {
|
if (!result.success || !result.result) {
|
||||||
logger.error('Validation failed: Agent execution was unsuccessful');
|
logger.error('Validation failed: Agent execution was unsuccessful');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -133,10 +132,6 @@ export async function runClaudePrompt(
|
|||||||
auditSession: AuditSession | null = null,
|
auditSession: AuditSession | null = null,
|
||||||
logger: ActivityLogger,
|
logger: ActivityLogger,
|
||||||
modelTier: ModelTier = 'medium',
|
modelTier: ModelTier = 'medium',
|
||||||
outputFormat?: JsonSchemaOutputFormat,
|
|
||||||
apiKey?: string,
|
|
||||||
deliverablesSubdir?: string,
|
|
||||||
providerConfig?: import('../types/config.js').ProviderConfig,
|
|
||||||
): Promise<ClaudePromptResult> {
|
): Promise<ClaudePromptResult> {
|
||||||
// 1. Initialize timing and prompt
|
// 1. Initialize timing and prompt
|
||||||
const timer = new Timer(`agent-${description.toLowerCase().replace(/\s+/g, '-')}`);
|
const timer = new Timer(`agent-${description.toLowerCase().replace(/\s+/g, '-')}`);
|
||||||
@@ -155,54 +150,22 @@ export async function runClaudePrompt(
|
|||||||
// 3. Build env vars to pass to SDK subprocesses
|
// 3. Build env vars to pass to SDK subprocesses
|
||||||
const sdkEnv: Record<string, string> = {
|
const sdkEnv: Record<string, string> = {
|
||||||
CLAUDE_CODE_MAX_OUTPUT_TOKENS: process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS || '64000',
|
CLAUDE_CODE_MAX_OUTPUT_TOKENS: process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS || '64000',
|
||||||
PLAYWRIGHT_MCP_OUTPUT_DIR: deliverablesSubdir
|
|
||||||
? path.join(sourceDir, path.dirname(deliverablesSubdir), '.playwright-cli')
|
|
||||||
: path.join(sourceDir, '.shannon', '.playwright-cli'),
|
|
||||||
// apiKey from ContainerConfig takes precedence over process.env
|
|
||||||
...(apiKey && { ANTHROPIC_API_KEY: apiKey }),
|
|
||||||
// Deliverables subdir for save-deliverable CLI tool
|
|
||||||
...(deliverablesSubdir && { SHANNON_DELIVERABLES_SUBDIR: deliverablesSubdir }),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3a. Apply structured provider config directly to sdkEnv (no process.env mutation)
|
|
||||||
if (providerConfig) {
|
|
||||||
switch (providerConfig.providerType) {
|
|
||||||
case 'bedrock':
|
|
||||||
sdkEnv.CLAUDE_CODE_USE_BEDROCK = '1';
|
|
||||||
if (providerConfig.awsRegion) sdkEnv.AWS_REGION = providerConfig.awsRegion;
|
|
||||||
if (providerConfig.awsAccessKeyId) sdkEnv.AWS_ACCESS_KEY_ID = providerConfig.awsAccessKeyId;
|
|
||||||
if (providerConfig.awsSecretAccessKey) sdkEnv.AWS_SECRET_ACCESS_KEY = providerConfig.awsSecretAccessKey;
|
|
||||||
break;
|
|
||||||
case 'vertex':
|
|
||||||
sdkEnv.CLAUDE_CODE_USE_VERTEX = '1';
|
|
||||||
if (providerConfig.gcpRegion) sdkEnv.CLOUD_ML_REGION = providerConfig.gcpRegion;
|
|
||||||
if (providerConfig.gcpProjectId) sdkEnv.ANTHROPIC_VERTEX_PROJECT_ID = providerConfig.gcpProjectId;
|
|
||||||
if (providerConfig.gcpCredentialsPath) sdkEnv.GOOGLE_APPLICATION_CREDENTIALS = providerConfig.gcpCredentialsPath;
|
|
||||||
break;
|
|
||||||
case 'litellm_router':
|
|
||||||
if (providerConfig.baseUrl) sdkEnv.ANTHROPIC_BASE_URL = providerConfig.baseUrl;
|
|
||||||
if (providerConfig.authToken) sdkEnv.ANTHROPIC_AUTH_TOKEN = providerConfig.authToken;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// 'anthropic_api' or unset — apiKey already handled above
|
|
||||||
if (providerConfig.apiKey && !apiKey) sdkEnv.ANTHROPIC_API_KEY = providerConfig.apiKey;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3b. Passthrough env vars not already set by providerConfig or apiKey
|
|
||||||
const passthroughVars = [
|
const passthroughVars = [
|
||||||
...(!sdkEnv.ANTHROPIC_API_KEY ? ['ANTHROPIC_API_KEY'] : []),
|
'ANTHROPIC_API_KEY',
|
||||||
'CLAUDE_CODE_OAUTH_TOKEN',
|
'CLAUDE_CODE_OAUTH_TOKEN',
|
||||||
...(!sdkEnv.ANTHROPIC_BASE_URL ? ['ANTHROPIC_BASE_URL'] : []),
|
'ANTHROPIC_BASE_URL',
|
||||||
...(!sdkEnv.ANTHROPIC_AUTH_TOKEN ? ['ANTHROPIC_AUTH_TOKEN'] : []),
|
'ANTHROPIC_AUTH_TOKEN',
|
||||||
...(!sdkEnv.CLAUDE_CODE_USE_BEDROCK ? ['CLAUDE_CODE_USE_BEDROCK'] : []),
|
'CLAUDE_CODE_USE_BEDROCK',
|
||||||
...(!sdkEnv.AWS_REGION ? ['AWS_REGION'] : []),
|
'AWS_REGION',
|
||||||
'AWS_BEARER_TOKEN_BEDROCK',
|
'AWS_BEARER_TOKEN_BEDROCK',
|
||||||
...(!sdkEnv.CLAUDE_CODE_USE_VERTEX ? ['CLAUDE_CODE_USE_VERTEX'] : []),
|
'CLAUDE_CODE_USE_VERTEX',
|
||||||
...(!sdkEnv.CLOUD_ML_REGION ? ['CLOUD_ML_REGION'] : []),
|
'CLOUD_ML_REGION',
|
||||||
...(!sdkEnv.ANTHROPIC_VERTEX_PROJECT_ID ? ['ANTHROPIC_VERTEX_PROJECT_ID'] : []),
|
'ANTHROPIC_VERTEX_PROJECT_ID',
|
||||||
...(!sdkEnv.GOOGLE_APPLICATION_CREDENTIALS ? ['GOOGLE_APPLICATION_CREDENTIALS'] : []),
|
'GOOGLE_APPLICATION_CREDENTIALS',
|
||||||
|
'ANTHROPIC_SMALL_MODEL',
|
||||||
|
'ANTHROPIC_MEDIUM_MODEL',
|
||||||
|
'ANTHROPIC_LARGE_MODEL',
|
||||||
'HOME',
|
'HOME',
|
||||||
'PATH',
|
'PATH',
|
||||||
'PLAYWRIGHT_MCP_EXECUTABLE_PATH',
|
'PLAYWRIGHT_MCP_EXECUTABLE_PATH',
|
||||||
@@ -215,17 +178,14 @@ export async function runClaudePrompt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Configure SDK options
|
// 4. Configure SDK options
|
||||||
// Model override from providerConfig takes precedence over env-based resolveModel
|
|
||||||
const model = providerConfig?.modelOverrides?.[modelTier] ?? resolveModel(modelTier);
|
|
||||||
const options = {
|
const options = {
|
||||||
model,
|
model: resolveModel(modelTier),
|
||||||
maxTurns: 10_000,
|
maxTurns: 10_000,
|
||||||
cwd: sourceDir,
|
cwd: sourceDir,
|
||||||
permissionMode: 'bypassPermissions' as const,
|
permissionMode: 'bypassPermissions' as const,
|
||||||
allowDangerouslySkipPermissions: true,
|
allowDangerouslySkipPermissions: true,
|
||||||
settingSources: ['user'] as ('user' | 'project' | 'local')[],
|
settingSources: ['user'] as ('user' | 'project' | 'local')[],
|
||||||
env: sdkEnv,
|
env: sdkEnv,
|
||||||
...(outputFormat && { outputFormat }),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!execContext.useCleanOutput) {
|
if (!execContext.useCleanOutput) {
|
||||||
@@ -283,9 +243,6 @@ export async function runClaudePrompt(
|
|||||||
model,
|
model,
|
||||||
partialCost: totalCost,
|
partialCost: totalCost,
|
||||||
apiErrorDetected,
|
apiErrorDetected,
|
||||||
...(messageLoopResult.structuredOutput !== undefined && {
|
|
||||||
structuredOutput: messageLoopResult.structuredOutput,
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 9. Handle errors — log, write error file, return failure
|
// 9. Handle errors — log, write error file, return failure
|
||||||
@@ -316,7 +273,6 @@ interface MessageLoopResult {
|
|||||||
apiErrorDetected: boolean;
|
apiErrorDetected: boolean;
|
||||||
cost: number;
|
cost: number;
|
||||||
model?: string | undefined;
|
model?: string | undefined;
|
||||||
structuredOutput?: unknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MessageLoopDeps {
|
interface MessageLoopDeps {
|
||||||
@@ -341,7 +297,6 @@ async function processMessageStream(
|
|||||||
let apiErrorDetected = false;
|
let apiErrorDetected = false;
|
||||||
let cost = 0;
|
let cost = 0;
|
||||||
let model: string | undefined;
|
let model: string | undefined;
|
||||||
let structuredOutput: unknown | undefined;
|
|
||||||
let lastHeartbeat = Date.now();
|
let lastHeartbeat = Date.now();
|
||||||
|
|
||||||
for await (const message of query({ prompt: fullPrompt, options })) {
|
for await (const message of query({ prompt: fullPrompt, options })) {
|
||||||
@@ -372,9 +327,6 @@ async function processMessageStream(
|
|||||||
if (dispatchResult.type === 'complete') {
|
if (dispatchResult.type === 'complete') {
|
||||||
result = dispatchResult.result;
|
result = dispatchResult.result;
|
||||||
cost = dispatchResult.cost;
|
cost = dispatchResult.cost;
|
||||||
if (dispatchResult.structuredOutput !== undefined) {
|
|
||||||
structuredOutput = dispatchResult.structuredOutput;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,18 +334,12 @@ async function processMessageStream(
|
|||||||
if (dispatchResult.apiErrorDetected) {
|
if (dispatchResult.apiErrorDetected) {
|
||||||
apiErrorDetected = true;
|
apiErrorDetected = true;
|
||||||
}
|
}
|
||||||
|
// Capture model from SystemInitMessage, but override with router model if applicable
|
||||||
if (dispatchResult.model) {
|
if (dispatchResult.model) {
|
||||||
model = dispatchResult.model;
|
model = getActualModelName(dispatchResult.model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return { turnCount, result, apiErrorDetected, cost, model };
|
||||||
turnCount,
|
|
||||||
result,
|
|
||||||
apiErrorDetected,
|
|
||||||
cost,
|
|
||||||
model,
|
|
||||||
...(structuredOutput !== undefined && { structuredOutput }),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
formatToolUseOutput,
|
formatToolUseOutput,
|
||||||
} from './output-formatters.js';
|
} from './output-formatters.js';
|
||||||
import type { ProgressManager } from './progress-manager.js';
|
import type { ProgressManager } from './progress-manager.js';
|
||||||
|
import { getActualModelName } from './router-utils.js';
|
||||||
import type {
|
import type {
|
||||||
ApiErrorDetection,
|
ApiErrorDetection,
|
||||||
AssistantMessage,
|
AssistantMessage,
|
||||||
@@ -222,10 +223,6 @@ function handleResultMessage(message: ResultMessage): ResultData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.structured_output !== undefined) {
|
|
||||||
result.structuredOutput = message.structured_output;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +259,7 @@ function outputLines(lines: string[]): void {
|
|||||||
|
|
||||||
export type MessageDispatchAction =
|
export type MessageDispatchAction =
|
||||||
| { type: 'continue'; apiErrorDetected?: boolean | undefined; model?: string | undefined }
|
| { type: 'continue'; apiErrorDetected?: boolean | undefined; model?: string | undefined }
|
||||||
| { type: 'complete'; result: string | null; cost: number; structuredOutput?: unknown }
|
| { type: 'complete'; result: string | null; cost: number }
|
||||||
| { type: 'throw'; error: Error };
|
| { type: 'throw'; error: Error };
|
||||||
|
|
||||||
export interface MessageDispatchDeps {
|
export interface MessageDispatchDeps {
|
||||||
@@ -308,10 +305,12 @@ export async function dispatchMessage(
|
|||||||
case 'system': {
|
case 'system': {
|
||||||
if (message.subtype === 'init') {
|
if (message.subtype === 'init') {
|
||||||
const initMsg = message as SystemInitMessage;
|
const initMsg = message as SystemInitMessage;
|
||||||
|
const actualModel = getActualModelName(initMsg.model);
|
||||||
if (!execContext.useCleanOutput) {
|
if (!execContext.useCleanOutput) {
|
||||||
logger.info(`Model: ${initMsg.model}, Permission: ${initMsg.permissionMode}`);
|
logger.info(`Model: ${actualModel}, Permission: ${initMsg.permissionMode}`);
|
||||||
}
|
}
|
||||||
return { type: 'continue', model: initMsg.model };
|
// Return actual model for tracking in audit logs
|
||||||
|
return { type: 'continue', model: actualModel };
|
||||||
}
|
}
|
||||||
return { type: 'continue' };
|
return { type: 'continue' };
|
||||||
}
|
}
|
||||||
@@ -339,26 +338,7 @@ export async function dispatchMessage(
|
|||||||
case 'result': {
|
case 'result': {
|
||||||
const resultData = handleResultMessage(message as ResultMessage);
|
const resultData = handleResultMessage(message as ResultMessage);
|
||||||
outputLines(formatResultOutput(resultData, !execContext.useCleanOutput));
|
outputLines(formatResultOutput(resultData, !execContext.useCleanOutput));
|
||||||
|
return { type: 'complete', result: resultData.result, cost: resultData.cost };
|
||||||
if (resultData.subtype === 'error_max_structured_output_retries') {
|
|
||||||
return {
|
|
||||||
type: 'throw',
|
|
||||||
error: new PentestError(
|
|
||||||
'Structured output validation failed after max retries',
|
|
||||||
'validation',
|
|
||||||
true,
|
|
||||||
{},
|
|
||||||
ErrorCode.OUTPUT_VALIDATION_FAILED,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'complete' as const,
|
|
||||||
result: resultData.result,
|
|
||||||
cost: resultData.cost,
|
|
||||||
...(resultData.structuredOutput !== undefined && { structuredOutput: resultData.structuredOutput }),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
// Copyright (C) 2025 Keygraph, Inc.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License version 3
|
|
||||||
// as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zod schema definitions for vulnerability exploitation queue structured outputs.
|
|
||||||
*
|
|
||||||
* Each vuln agent returns a structured JSON response matching its schema.
|
|
||||||
* The SDK validates the output against the JSON Schema generated from these Zod definitions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { JsonSchemaOutputFormat } from '@anthropic-ai/claude-agent-sdk';
|
|
||||||
import { z } from 'zod';
|
|
||||||
import type { AgentName } from '../types/agents.js';
|
|
||||||
|
|
||||||
// === Common Fields ===
|
|
||||||
|
|
||||||
const baseVulnerability = z.object({
|
|
||||||
ID: z.string(),
|
|
||||||
vulnerability_type: z.string(),
|
|
||||||
externally_exploitable: z.boolean(),
|
|
||||||
confidence: z.string(),
|
|
||||||
notes: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// === Per-Vuln-Type Schemas ===
|
|
||||||
|
|
||||||
const InjectionVulnerability = baseVulnerability.extend({
|
|
||||||
source: z.string().optional(),
|
|
||||||
combined_sources: z.string().optional(),
|
|
||||||
path: z.string().optional(),
|
|
||||||
sink_call: z.string().optional(),
|
|
||||||
slot_type: z.string().optional(),
|
|
||||||
sanitization_observed: z.string().optional(),
|
|
||||||
concat_occurrences: z.string().optional(),
|
|
||||||
verdict: z.string().optional(),
|
|
||||||
mismatch_reason: z.string().optional(),
|
|
||||||
witness_payload: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const XssVulnerability = baseVulnerability.extend({
|
|
||||||
source: z.string().optional(),
|
|
||||||
source_detail: z.string().optional(),
|
|
||||||
path: z.string().optional(),
|
|
||||||
sink_function: z.string().optional(),
|
|
||||||
render_context: z.string().optional(),
|
|
||||||
encoding_observed: z.string().optional(),
|
|
||||||
verdict: z.string().optional(),
|
|
||||||
mismatch_reason: z.string().optional(),
|
|
||||||
witness_payload: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const AuthVulnerability = baseVulnerability.extend({
|
|
||||||
source_endpoint: z.string().optional(),
|
|
||||||
vulnerable_code_location: z.string().optional(),
|
|
||||||
missing_defense: z.string().optional(),
|
|
||||||
exploitation_hypothesis: z.string().optional(),
|
|
||||||
suggested_exploit_technique: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const SsrfVulnerability = baseVulnerability.extend({
|
|
||||||
source_endpoint: z.string().optional(),
|
|
||||||
vulnerable_parameter: z.string().optional(),
|
|
||||||
vulnerable_code_location: z.string().optional(),
|
|
||||||
missing_defense: z.string().optional(),
|
|
||||||
exploitation_hypothesis: z.string().optional(),
|
|
||||||
suggested_exploit_technique: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const AuthzVulnerability = baseVulnerability.extend({
|
|
||||||
endpoint: z.string().optional(),
|
|
||||||
vulnerable_code_location: z.string().optional(),
|
|
||||||
role_context: z.string().optional(),
|
|
||||||
guard_evidence: z.string().optional(),
|
|
||||||
side_effect: z.string().optional(),
|
|
||||||
reason: z.string().optional(),
|
|
||||||
minimal_witness: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// === Queue Wrapper Schemas ===
|
|
||||||
|
|
||||||
const InjectionQueueSchema = z.object({ vulnerabilities: z.array(InjectionVulnerability) });
|
|
||||||
const XssQueueSchema = z.object({ vulnerabilities: z.array(XssVulnerability) });
|
|
||||||
const AuthQueueSchema = z.object({ vulnerabilities: z.array(AuthVulnerability) });
|
|
||||||
const SsrfQueueSchema = z.object({ vulnerabilities: z.array(SsrfVulnerability) });
|
|
||||||
const AuthzQueueSchema = z.object({ vulnerabilities: z.array(AuthzVulnerability) });
|
|
||||||
|
|
||||||
// === Convert to JSON Schema for SDK ===
|
|
||||||
|
|
||||||
// NOTE: The SDK's AJV validator expects draft-07. Zod defaults to draft-2020-12 which
|
|
||||||
// causes the SDK to silently skip structured output.
|
|
||||||
function toOutputFormat(zodSchema: z.ZodType): JsonSchemaOutputFormat {
|
|
||||||
return { type: 'json_schema', schema: z.toJSONSchema(zodSchema, { target: 'draft-07' }) as Record<string, unknown> };
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Lookup Maps ===
|
|
||||||
|
|
||||||
const VULN_AGENT_OUTPUT_FORMAT: Partial<Record<AgentName, JsonSchemaOutputFormat>> = {
|
|
||||||
'injection-vuln': toOutputFormat(InjectionQueueSchema),
|
|
||||||
'xss-vuln': toOutputFormat(XssQueueSchema),
|
|
||||||
'auth-vuln': toOutputFormat(AuthQueueSchema),
|
|
||||||
'ssrf-vuln': toOutputFormat(SsrfQueueSchema),
|
|
||||||
'authz-vuln': toOutputFormat(AuthzQueueSchema),
|
|
||||||
};
|
|
||||||
|
|
||||||
const VULN_AGENT_QUEUE_FILENAMES: Partial<Record<AgentName, string>> = {
|
|
||||||
'injection-vuln': 'injection_exploitation_queue.json',
|
|
||||||
'xss-vuln': 'xss_exploitation_queue.json',
|
|
||||||
'auth-vuln': 'auth_exploitation_queue.json',
|
|
||||||
'ssrf-vuln': 'ssrf_exploitation_queue.json',
|
|
||||||
'authz-vuln': 'authz_exploitation_queue.json',
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Returns the structured output format for a vuln agent, or undefined for non-vuln agents. */
|
|
||||||
export function getOutputFormat(agentName: AgentName): JsonSchemaOutputFormat | undefined {
|
|
||||||
return VULN_AGENT_OUTPUT_FORMAT[agentName];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the queue filename for a vuln agent, or undefined for non-vuln agents. */
|
|
||||||
export function getQueueFilename(agentName: AgentName): string | undefined {
|
|
||||||
return VULN_AGENT_QUEUE_FILENAMES[agentName];
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (C) 2025 Keygraph, Inc.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License version 3
|
||||||
|
// as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the actual model name being used.
|
||||||
|
* When using claude-code-router, the SDK reports its configured model (claude-sonnet)
|
||||||
|
* but the actual model is determined by ROUTER_DEFAULT env var.
|
||||||
|
*/
|
||||||
|
export function getActualModelName(sdkReportedModel?: string): string | undefined {
|
||||||
|
const routerBaseUrl = process.env.ANTHROPIC_BASE_URL;
|
||||||
|
const routerDefault = process.env.ROUTER_DEFAULT;
|
||||||
|
|
||||||
|
// If router mode is active and ROUTER_DEFAULT is set, use that
|
||||||
|
if (routerBaseUrl && routerDefault) {
|
||||||
|
// ROUTER_DEFAULT format: "provider,model" (e.g., "gemini,gemini-2.5-pro")
|
||||||
|
const parts = routerDefault.split(',');
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
return parts.slice(1).join(','); // Handle model names with commas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to SDK-reported model
|
||||||
|
return sdkReportedModel;
|
||||||
|
}
|
||||||
@@ -34,7 +34,6 @@ export interface ResultData {
|
|||||||
subtype?: string;
|
subtype?: string;
|
||||||
stop_reason?: string | null;
|
stop_reason?: string | null;
|
||||||
permissionDenials: number;
|
permissionDenials: number;
|
||||||
structuredOutput?: unknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolUseData {
|
export interface ToolUseData {
|
||||||
@@ -70,7 +69,6 @@ export interface ResultMessage {
|
|||||||
subtype?: string;
|
subtype?: string;
|
||||||
stop_reason?: string | null;
|
stop_reason?: string | null;
|
||||||
permission_denials?: unknown[];
|
permission_denials?: unknown[];
|
||||||
structured_output?: unknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolUseMessage {
|
export interface ToolUseMessage {
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ export class AuditSession {
|
|||||||
/**
|
/**
|
||||||
* Update session status
|
* Update session status
|
||||||
*/
|
*/
|
||||||
async updateSessionStatus(status: 'in-progress' | 'completed' | 'failed' | 'cancelled'): Promise<void> {
|
async updateSessionStatus(status: 'in-progress' | 'completed' | 'failed'): Promise<void> {
|
||||||
await this.ensureInitialized();
|
await this.ensureInitialized();
|
||||||
|
|
||||||
const unlock = await sessionMutex.lock(this.sessionId);
|
const unlock = await sessionMutex.lock(this.sessionId);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ interface SessionData {
|
|||||||
id: string;
|
id: string;
|
||||||
webUrl: string;
|
webUrl: string;
|
||||||
repoPath?: string;
|
repoPath?: string;
|
||||||
status: 'in-progress' | 'completed' | 'failed' | 'cancelled';
|
status: 'in-progress' | 'completed' | 'failed';
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
completedAt?: string;
|
completedAt?: string;
|
||||||
originalWorkflowId?: string; // First workflow that created this workspace
|
originalWorkflowId?: string; // First workflow that created this workspace
|
||||||
@@ -232,12 +232,12 @@ export class MetricsTracker {
|
|||||||
/**
|
/**
|
||||||
* Update session status
|
* Update session status
|
||||||
*/
|
*/
|
||||||
async updateSessionStatus(status: 'in-progress' | 'completed' | 'failed' | 'cancelled'): Promise<void> {
|
async updateSessionStatus(status: 'in-progress' | 'completed' | 'failed'): Promise<void> {
|
||||||
if (!this.data) return;
|
if (!this.data) return;
|
||||||
|
|
||||||
this.data.session.status = status;
|
this.data.session.status = status;
|
||||||
|
|
||||||
if (status === 'completed' || status === 'failed' || status === 'cancelled') {
|
if (status === 'completed' || status === 'failed') {
|
||||||
this.data.session.completedAt = formatTimestamp();
|
this.data.session.completedAt = formatTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
* All functions are pure and crash-safe.
|
* All functions are pure and crash-safe.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { WORKSPACES_DIR } from '../paths.js';
|
import { WORKSPACES_DIR } from '../paths.js';
|
||||||
import { ensureDirectory } from '../utils/file-io.js';
|
import { ensureDirectory } from '../utils/file-io.js';
|
||||||
@@ -97,3 +98,33 @@ export async function initializeAuditStructure(sessionMetadata: SessionMetadata)
|
|||||||
await ensureDirectory(promptsPath);
|
await ensureDirectory(promptsPath);
|
||||||
await ensureDirectory(deliverablesPath);
|
await ensureDirectory(deliverablesPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy deliverable files from repo to workspaces for self-contained audit trail.
|
||||||
|
* No-ops if source directory doesn't exist. Idempotent and parallel-safe.
|
||||||
|
*/
|
||||||
|
export async function copyDeliverablesToAudit(sessionMetadata: SessionMetadata, repoPath: string): Promise<void> {
|
||||||
|
const sourceDir = path.join(repoPath, 'deliverables');
|
||||||
|
const destDir = path.join(generateAuditPath(sessionMetadata), 'deliverables');
|
||||||
|
|
||||||
|
let entries: string[];
|
||||||
|
try {
|
||||||
|
entries = await fs.readdir(sourceDir);
|
||||||
|
} catch {
|
||||||
|
// Source directory doesn't exist yet — nothing to copy
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ensureDirectory(destDir);
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const sourcePath = path.join(sourceDir, entry);
|
||||||
|
const destPath = path.join(destDir, entry);
|
||||||
|
|
||||||
|
// Only copy files, skip subdirectories
|
||||||
|
const stat = await fs.stat(sourcePath);
|
||||||
|
if (stat.isFile()) {
|
||||||
|
await fs.copyFile(sourcePath, destPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ export interface AgentMetricsSummary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkflowSummary {
|
export interface WorkflowSummary {
|
||||||
status: 'completed' | 'failed' | 'cancelled';
|
status: 'completed' | 'failed';
|
||||||
totalDurationMs: number;
|
totalDurationMs: number;
|
||||||
totalCostUsd: number;
|
totalCostUsd: number;
|
||||||
completedAgents: string[];
|
completedAgents: string[];
|
||||||
|
|||||||
@@ -258,54 +258,6 @@ export const parseConfig = async (configPath: string): Promise<Config> => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a raw YAML string into a validated Config object.
|
|
||||||
*
|
|
||||||
* Same validation as parseConfig but accepts a string instead of a file path.
|
|
||||||
* Used when config YAML is passed inline (e.g., from a parent workflow).
|
|
||||||
*/
|
|
||||||
export const parseConfigYAML = (yamlContent: string): Config => {
|
|
||||||
if (!yamlContent.trim()) {
|
|
||||||
throw new PentestError(
|
|
||||||
'Configuration YAML string is empty',
|
|
||||||
'config',
|
|
||||||
false,
|
|
||||||
{},
|
|
||||||
ErrorCode.CONFIG_VALIDATION_FAILED,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let config: unknown;
|
|
||||||
try {
|
|
||||||
config = yaml.load(yamlContent, {
|
|
||||||
schema: yaml.FAILSAFE_SCHEMA,
|
|
||||||
json: false,
|
|
||||||
});
|
|
||||||
} catch (yamlError) {
|
|
||||||
const errMsg = yamlError instanceof Error ? yamlError.message : String(yamlError);
|
|
||||||
throw new PentestError(
|
|
||||||
`YAML parsing failed: ${errMsg}`,
|
|
||||||
'config',
|
|
||||||
false,
|
|
||||||
{ originalError: errMsg },
|
|
||||||
ErrorCode.CONFIG_PARSE_ERROR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config === null || config === undefined) {
|
|
||||||
throw new PentestError(
|
|
||||||
'Configuration YAML resulted in null/undefined after parsing',
|
|
||||||
'config',
|
|
||||||
false,
|
|
||||||
{},
|
|
||||||
ErrorCode.CONFIG_PARSE_ERROR,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
validateConfig(config as Config);
|
|
||||||
return config as Config;
|
|
||||||
};
|
|
||||||
|
|
||||||
const validateConfig = (config: Config): void => {
|
const validateConfig = (config: Config): void => {
|
||||||
if (!config || typeof config !== 'object') {
|
if (!config || typeof config !== 'object') {
|
||||||
throw new PentestError(
|
throw new PentestError(
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
/**
|
|
||||||
* CheckpointProvider — injectable interface for external state persistence.
|
|
||||||
*
|
|
||||||
* Called before and after each agent to support skip-guard (resume) and
|
|
||||||
* post-agent artifact persistence. During the concurrent vulnerability-exploitation
|
|
||||||
* phase, 5 pipelines run in parallel — methods fire per-agent for granular control.
|
|
||||||
*
|
|
||||||
* Default: no-op (skip nothing, persist nothing).
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { AgentMetrics, PipelineState } from '../temporal/shared.js';
|
|
||||||
|
|
||||||
/** Result of a pre-agent skip check. */
|
|
||||||
export interface SkipDecision {
|
|
||||||
readonly skip: boolean;
|
|
||||||
readonly metrics?: AgentMetrics; // Required when skip=true
|
|
||||||
}
|
|
||||||
|
|
||||||
/** File-system context passed after agent completion for artifact persistence. */
|
|
||||||
export interface CheckpointContext {
|
|
||||||
readonly repoPath: string;
|
|
||||||
readonly sessionId: string;
|
|
||||||
readonly deliverablesSubdir: string;
|
|
||||||
readonly outputPath?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CheckpointProvider {
|
|
||||||
/**
|
|
||||||
* Called before an agent activity executes.
|
|
||||||
* Return { skip: true, metrics } to skip the agent (e.g., output files already exist).
|
|
||||||
* Return { skip: false } to run normally.
|
|
||||||
*/
|
|
||||||
shouldSkipAgent(
|
|
||||||
agentName: string,
|
|
||||||
repoPath: string,
|
|
||||||
deliverablesSubdir: string,
|
|
||||||
): Promise<SkipDecision>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after an agent activity succeeds.
|
|
||||||
* Receives pipeline state and optional file context for artifact persistence.
|
|
||||||
*/
|
|
||||||
onAgentComplete(
|
|
||||||
agentName: string,
|
|
||||||
phase: string,
|
|
||||||
state: PipelineState,
|
|
||||||
context?: CheckpointContext,
|
|
||||||
): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Default no-op implementation — no external checkpointing. */
|
|
||||||
export class NoOpCheckpointProvider implements CheckpointProvider {
|
|
||||||
async shouldSkipAgent(): Promise<SkipDecision> {
|
|
||||||
return { skip: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
async onAgentComplete(): Promise<void> {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* FindingsProvider — injectable interface for external findings integration.
|
|
||||||
*
|
|
||||||
* Allows external security data from consumer-supplied sources to be merged
|
|
||||||
* into the exploitation pipeline between vulnerability analysis and exploitation.
|
|
||||||
*
|
|
||||||
* Default: no-op returning { mergedCount: 0 }.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { ActivityInput } from '../temporal/activities.js';
|
|
||||||
import type { VulnType } from '../types/agents.js';
|
|
||||||
|
|
||||||
export interface FindingsProvider {
|
|
||||||
mergeFindingsIntoQueue(
|
|
||||||
repoPath: string,
|
|
||||||
vulnType: VulnType,
|
|
||||||
input: ActivityInput,
|
|
||||||
): Promise<{ mergedCount: number }>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Default no-op implementation — no external findings to merge. */
|
|
||||||
export class NoOpFindingsProvider implements FindingsProvider {
|
|
||||||
async mergeFindingsIntoQueue(): Promise<{ mergedCount: number }> {
|
|
||||||
return { mergedCount: 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* Injectable interfaces for extending the pentest pipeline.
|
|
||||||
*
|
|
||||||
* All interfaces have default no-op implementations.
|
|
||||||
* Consumers can provide alternate implementations via the DI container.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export type { CheckpointProvider, CheckpointContext, SkipDecision } from './checkpoint-provider.js';
|
|
||||||
export { NoOpCheckpointProvider } from './checkpoint-provider.js';
|
|
||||||
export type { FindingsProvider } from './findings-provider.js';
|
|
||||||
export { NoOpFindingsProvider } from './findings-provider.js';
|
|
||||||
export type { ReportOutputProvider } from './report-output-provider.js';
|
|
||||||
export { NoOpReportOutputProvider } from './report-output-provider.js';
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
/**
|
|
||||||
* ReportOutputProvider — injectable interface for emitting an optional
|
|
||||||
* additional artifact alongside the assembled markdown report.
|
|
||||||
*
|
|
||||||
* Runs after the report agent has finalized
|
|
||||||
* `comprehensive_security_assessment_report.md`. Consumers can override to
|
|
||||||
* produce derived outputs; the default no-op produces nothing.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { ActivityInput } from '../temporal/activities.js';
|
|
||||||
import type { ActivityLogger } from '../types/activity-logger.js';
|
|
||||||
|
|
||||||
export interface ReportOutputProvider {
|
|
||||||
generate(input: ActivityInput, logger: ActivityLogger): Promise<{ outputPath?: string }>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Default no-op implementation — no additional output produced. */
|
|
||||||
export class NoOpReportOutputProvider implements ReportOutputProvider {
|
|
||||||
async generate(): Promise<{ outputPath?: string }> {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,21 +9,6 @@ const WORKER_ROOT = path.resolve(import.meta.dirname, '..');
|
|||||||
export const PROMPTS_DIR = path.join(WORKER_ROOT, 'prompts');
|
export const PROMPTS_DIR = path.join(WORKER_ROOT, 'prompts');
|
||||||
export const CONFIGS_DIR = path.join(WORKER_ROOT, 'configs');
|
export const CONFIGS_DIR = path.join(WORKER_ROOT, 'configs');
|
||||||
|
|
||||||
/** Default deliverables subdirectory relative to repoPath */
|
|
||||||
export const DEFAULT_DELIVERABLES_SUBDIR = '.shannon/deliverables';
|
|
||||||
|
|
||||||
/** Default audit log directory */
|
|
||||||
export const DEFAULT_AUDIT_DIR = './workspaces';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve the deliverables directory for a given repoPath and optional subdir override.
|
|
||||||
* @param repoPath - Absolute path to the target repository
|
|
||||||
* @param subdir - Subdirectory relative to repoPath (default: '.shannon/deliverables')
|
|
||||||
*/
|
|
||||||
export function deliverablesDir(repoPath: string, subdir: string = DEFAULT_DELIVERABLES_SUBDIR): string {
|
|
||||||
return path.join(repoPath, ...subdir.split('/'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository root — walk up from WORKER_ROOT looking for pnpm-workspace.yaml.
|
* Repository root — walk up from WORKER_ROOT looking for pnpm-workspace.yaml.
|
||||||
* Falls back to two levels up (apps/worker/ → repo root) if not found.
|
* Falls back to two levels up (apps/worker/ → repo root) if not found.
|
||||||
|
|||||||
@@ -9,15 +9,17 @@
|
|||||||
/**
|
/**
|
||||||
* save-deliverable CLI
|
* save-deliverable CLI
|
||||||
*
|
*
|
||||||
* Standalone script to save deliverable files.
|
* Standalone script to save deliverable files with validation.
|
||||||
|
* Replaces the MCP save_deliverable tool.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
|
* node save-deliverable.js --type INJECTION_QUEUE --content '{"vulnerabilities": [...]}'
|
||||||
* node save-deliverable.js --type INJECTION_ANALYSIS --file-path deliverables/injection_analysis_deliverable.md
|
* node save-deliverable.js --type INJECTION_ANALYSIS --file-path deliverables/injection_analysis_deliverable.md
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||||||
import { join, resolve } from 'node:path';
|
import { join, resolve } from 'node:path';
|
||||||
import { DELIVERABLE_FILENAMES, type DeliverableType } from '../types/deliverables.js';
|
import { DELIVERABLE_FILENAMES, type DeliverableType, isQueueType } from '../types/deliverables.js';
|
||||||
|
|
||||||
// === Argument Parsing ===
|
// === Argument Parsing ===
|
||||||
|
|
||||||
@@ -49,11 +51,53 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Queue Validation ===
|
||||||
|
|
||||||
|
interface ValidationResult {
|
||||||
|
valid: boolean;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateQueueJson(content: string): ValidationResult {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(content) as unknown;
|
||||||
|
|
||||||
|
if (typeof parsed !== 'object' || parsed === null) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: `Invalid queue structure: Expected an object. Got: ${typeof parsed}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = parsed as Record<string, unknown>;
|
||||||
|
|
||||||
|
if (!('vulnerabilities' in obj)) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: `Invalid queue structure: Missing 'vulnerabilities' property. Expected: {"vulnerabilities": [...]}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(obj.vulnerabilities)) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: `Invalid queue structure: 'vulnerabilities' must be an array. Expected: {"vulnerabilities": [...]}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: true };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// === File Operations ===
|
// === File Operations ===
|
||||||
|
|
||||||
function saveDeliverableFile(targetDir: string, filename: string, content: string): string {
|
function saveDeliverableFile(targetDir: string, filename: string, content: string): string {
|
||||||
const subdir = process.env.SHANNON_DELIVERABLES_SUBDIR || '.shannon/deliverables';
|
const deliverablesDir = join(targetDir, 'deliverables');
|
||||||
const deliverablesDir = join(targetDir, ...subdir.split('/'));
|
|
||||||
const filepath = join(deliverablesDir, filename);
|
const filepath = join(deliverablesDir, filename);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -121,11 +165,22 @@ function main(): void {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Save the file
|
// 3. Validate queue types
|
||||||
|
let validated = false;
|
||||||
|
if (isQueueType(args.type)) {
|
||||||
|
const validation = validateQueueJson(content);
|
||||||
|
if (!validation.valid) {
|
||||||
|
console.log(JSON.stringify({ status: 'error', message: validation.message, retryable: true }));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
validated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Save the file
|
||||||
try {
|
try {
|
||||||
const targetDir = process.cwd();
|
const targetDir = process.cwd();
|
||||||
const filepath = saveDeliverableFile(targetDir, filename, content);
|
const filepath = saveDeliverableFile(targetDir, filename, content);
|
||||||
console.log(JSON.stringify({ status: 'success', filepath }));
|
console.log(JSON.stringify({ status: 'success', filepath, validated }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg = error instanceof Error ? error.message : String(error);
|
const msg = error instanceof Error ? error.message : String(error);
|
||||||
console.log(JSON.stringify({ status: 'error', message: `Failed to save: ${msg}`, retryable: true }));
|
console.log(JSON.stringify({ status: 'error', message: `Failed to save: ${msg}`, retryable: true }));
|
||||||
|
|||||||
@@ -21,9 +21,7 @@
|
|||||||
* No Temporal dependencies - pure domain logic.
|
* No Temporal dependencies - pure domain logic.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fs, path } from 'zx';
|
|
||||||
import { type ClaudePromptResult, runClaudePrompt, validateAgentOutput } from '../ai/claude-executor.js';
|
import { type ClaudePromptResult, runClaudePrompt, validateAgentOutput } from '../ai/claude-executor.js';
|
||||||
import { getOutputFormat, getQueueFilename } from '../ai/queue-schemas.js';
|
|
||||||
import type { AuditSession } from '../audit/index.js';
|
import type { AuditSession } from '../audit/index.js';
|
||||||
import { AGENTS } from '../session-manager.js';
|
import { AGENTS } from '../session-manager.js';
|
||||||
import type { ActivityLogger } from '../types/activity-logger.js';
|
import type { ActivityLogger } from '../types/activity-logger.js';
|
||||||
@@ -44,15 +42,9 @@ import { loadPrompt } from './prompt-manager.js';
|
|||||||
export interface AgentExecutionInput {
|
export interface AgentExecutionInput {
|
||||||
webUrl: string;
|
webUrl: string;
|
||||||
repoPath: string;
|
repoPath: string;
|
||||||
deliverablesPath: string;
|
|
||||||
configPath?: string | undefined;
|
configPath?: string | undefined;
|
||||||
configData?: import('../types/config.js').DistributedConfig | undefined;
|
|
||||||
configYAML?: string | undefined;
|
|
||||||
pipelineTestingMode?: boolean | undefined;
|
pipelineTestingMode?: boolean | undefined;
|
||||||
attemptNumber: number;
|
attemptNumber: number;
|
||||||
apiKey?: string | undefined;
|
|
||||||
promptDir?: string | undefined;
|
|
||||||
providerConfig?: import('../types/config.js').ProviderConfig | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FailAgentOpts {
|
interface FailAgentOpts {
|
||||||
@@ -95,10 +87,10 @@ export class AgentExecutionService {
|
|||||||
auditSession: AuditSession,
|
auditSession: AuditSession,
|
||||||
logger: ActivityLogger,
|
logger: ActivityLogger,
|
||||||
): Promise<Result<AgentEndResult, PentestError>> {
|
): Promise<Result<AgentEndResult, PentestError>> {
|
||||||
const { webUrl, repoPath, deliverablesPath, configPath, configData, configYAML, pipelineTestingMode = false, attemptNumber, apiKey, promptDir, providerConfig } = input;
|
const { webUrl, repoPath, configPath, pipelineTestingMode = false, attemptNumber } = input;
|
||||||
|
|
||||||
// 1. Load config (pre-parsed configData → raw YAML → file path)
|
// 1. Load config (if provided)
|
||||||
const configResult = await this.configLoader.loadOptional(configPath, configData, configYAML);
|
const configResult = await this.configLoader.loadOptional(configPath);
|
||||||
if (isErr(configResult)) {
|
if (isErr(configResult)) {
|
||||||
return configResult;
|
return configResult;
|
||||||
}
|
}
|
||||||
@@ -108,7 +100,7 @@ export class AgentExecutionService {
|
|||||||
const promptTemplate = AGENTS[agentName].promptTemplate;
|
const promptTemplate = AGENTS[agentName].promptTemplate;
|
||||||
let prompt: string;
|
let prompt: string;
|
||||||
try {
|
try {
|
||||||
prompt = await loadPrompt(promptTemplate, { webUrl, repoPath }, distributedConfig, pipelineTestingMode, logger, promptDir);
|
prompt = await loadPrompt(promptTemplate, { webUrl, repoPath }, distributedConfig, pipelineTestingMode, logger);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
return err(
|
return err(
|
||||||
@@ -124,7 +116,7 @@ export class AgentExecutionService {
|
|||||||
|
|
||||||
// 3. Create git checkpoint before execution
|
// 3. Create git checkpoint before execution
|
||||||
try {
|
try {
|
||||||
await createGitCheckpoint(deliverablesPath, agentName, attemptNumber, logger);
|
await createGitCheckpoint(repoPath, agentName, attemptNumber, logger);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
return err(
|
return err(
|
||||||
@@ -132,7 +124,7 @@ export class AgentExecutionService {
|
|||||||
`Failed to create git checkpoint for ${agentName}: ${errorMessage}`,
|
`Failed to create git checkpoint for ${agentName}: ${errorMessage}`,
|
||||||
'filesystem',
|
'filesystem',
|
||||||
false,
|
false,
|
||||||
{ agentName, deliverablesPath, originalError: errorMessage },
|
{ agentName, repoPath, originalError: errorMessage },
|
||||||
ErrorCode.GIT_CHECKPOINT_FAILED,
|
ErrorCode.GIT_CHECKPOINT_FAILED,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -142,7 +134,6 @@ export class AgentExecutionService {
|
|||||||
await auditSession.startAgent(agentName, prompt, attemptNumber);
|
await auditSession.startAgent(agentName, prompt, attemptNumber);
|
||||||
|
|
||||||
// 5. Execute agent
|
// 5. Execute agent
|
||||||
const outputFormat = getOutputFormat(agentName);
|
|
||||||
const result: ClaudePromptResult = await runClaudePrompt(
|
const result: ClaudePromptResult = await runClaudePrompt(
|
||||||
prompt,
|
prompt,
|
||||||
repoPath,
|
repoPath,
|
||||||
@@ -152,17 +143,13 @@ export class AgentExecutionService {
|
|||||||
auditSession,
|
auditSession,
|
||||||
logger,
|
logger,
|
||||||
AGENTS[agentName].modelTier,
|
AGENTS[agentName].modelTier,
|
||||||
outputFormat,
|
|
||||||
apiKey,
|
|
||||||
path.relative(repoPath, deliverablesPath),
|
|
||||||
providerConfig,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// 6. Spending cap check - defense-in-depth
|
// 6. Spending cap check - defense-in-depth
|
||||||
if (result.success && (result.turns ?? 0) <= 2 && (result.cost || 0) === 0) {
|
if (result.success && (result.turns ?? 0) <= 2 && (result.cost || 0) === 0) {
|
||||||
const resultText = result.result || '';
|
const resultText = result.result || '';
|
||||||
if (isSpendingCapBehavior(result.turns ?? 0, result.cost || 0, resultText)) {
|
if (isSpendingCapBehavior(result.turns ?? 0, result.cost || 0, resultText)) {
|
||||||
return this.failAgent(agentName, deliverablesPath, auditSession, logger, {
|
return this.failAgent(agentName, repoPath, auditSession, logger, {
|
||||||
attemptNumber,
|
attemptNumber,
|
||||||
result,
|
result,
|
||||||
rollbackReason: 'spending cap detected',
|
rollbackReason: 'spending cap detected',
|
||||||
@@ -177,7 +164,7 @@ export class AgentExecutionService {
|
|||||||
|
|
||||||
// 7. Handle execution failure
|
// 7. Handle execution failure
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return this.failAgent(agentName, deliverablesPath, auditSession, logger, {
|
return this.failAgent(agentName, repoPath, auditSession, logger, {
|
||||||
attemptNumber,
|
attemptNumber,
|
||||||
result,
|
result,
|
||||||
rollbackReason: 'execution failure',
|
rollbackReason: 'execution failure',
|
||||||
@@ -189,19 +176,10 @@ export class AgentExecutionService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Write structured output to disk (vuln agents only)
|
// 8. Validate output
|
||||||
const queueFilename = getQueueFilename(agentName);
|
const validationPassed = await validateAgentOutput(result, agentName, repoPath, logger);
|
||||||
if (result.structuredOutput !== undefined && queueFilename) {
|
|
||||||
await fs.ensureDir(deliverablesPath);
|
|
||||||
const queuePath = path.join(deliverablesPath, queueFilename);
|
|
||||||
await fs.writeFile(queuePath, JSON.stringify(result.structuredOutput, null, 2), 'utf8');
|
|
||||||
logger.info(`Wrote structured output queue to ${queueFilename}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 9. Validate output
|
|
||||||
const validationPassed = await validateAgentOutput(result, agentName, deliverablesPath, logger);
|
|
||||||
if (!validationPassed) {
|
if (!validationPassed) {
|
||||||
return this.failAgent(agentName, deliverablesPath, auditSession, logger, {
|
return this.failAgent(agentName, repoPath, auditSession, logger, {
|
||||||
attemptNumber,
|
attemptNumber,
|
||||||
result,
|
result,
|
||||||
rollbackReason: 'validation failure',
|
rollbackReason: 'validation failure',
|
||||||
@@ -213,9 +191,9 @@ export class AgentExecutionService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10. Success - commit deliverables, then capture checkpoint hash
|
// 9. Success - commit deliverables, then capture checkpoint hash
|
||||||
await commitGitSuccess(deliverablesPath, agentName, logger);
|
await commitGitSuccess(repoPath, agentName, logger);
|
||||||
const commitHash = await getGitCommitHash(deliverablesPath);
|
const commitHash = await getGitCommitHash(repoPath);
|
||||||
|
|
||||||
const endResult: AgentEndResult = {
|
const endResult: AgentEndResult = {
|
||||||
attemptNumber,
|
attemptNumber,
|
||||||
@@ -232,12 +210,12 @@ export class AgentExecutionService {
|
|||||||
|
|
||||||
private async failAgent(
|
private async failAgent(
|
||||||
agentName: AgentName,
|
agentName: AgentName,
|
||||||
deliverablesPath: string,
|
repoPath: string,
|
||||||
auditSession: AuditSession,
|
auditSession: AuditSession,
|
||||||
logger: ActivityLogger,
|
logger: ActivityLogger,
|
||||||
opts: FailAgentOpts,
|
opts: FailAgentOpts,
|
||||||
): Promise<Result<AgentEndResult, PentestError>> {
|
): Promise<Result<AgentEndResult, PentestError>> {
|
||||||
await rollbackGitWorkspace(deliverablesPath, opts.rollbackReason, logger);
|
await rollbackGitWorkspace(repoPath, opts.rollbackReason, logger);
|
||||||
|
|
||||||
const endResult: AgentEndResult = {
|
const endResult: AgentEndResult = {
|
||||||
attemptNumber: opts.attemptNumber,
|
attemptNumber: opts.attemptNumber,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
* Pure service with no Temporal dependencies.
|
* Pure service with no Temporal dependencies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { distributeConfig, parseConfig, parseConfigYAML } from '../config-parser.js';
|
import { distributeConfig, parseConfig } from '../config-parser.js';
|
||||||
import type { DistributedConfig } from '../types/config.js';
|
import type { DistributedConfig } from '../types/config.js';
|
||||||
import { ErrorCode } from '../types/errors.js';
|
import { ErrorCode } from '../types/errors.js';
|
||||||
import { err, ok, type Result } from '../types/result.js';
|
import { err, ok, type Result } from '../types/result.js';
|
||||||
@@ -60,31 +60,11 @@ export class ConfigLoaderService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Load config if path is provided, otherwise return null config.
|
* Load config if path is provided, otherwise return null config.
|
||||||
* If configData is provided (pre-parsed), returns it directly without file I/O.
|
|
||||||
*
|
*
|
||||||
* @param configPath - Optional path to the YAML configuration file
|
* @param configPath - Optional path to the YAML configuration file
|
||||||
* @param configData - Optional pre-parsed config (bypasses file loading)
|
|
||||||
* @returns Result containing DistributedConfig (or null) on success, PentestError on failure
|
* @returns Result containing DistributedConfig (or null) on success, PentestError on failure
|
||||||
*/
|
*/
|
||||||
async loadOptional(
|
async loadOptional(configPath: string | undefined): Promise<Result<DistributedConfig | null, PentestError>> {
|
||||||
configPath: string | undefined,
|
|
||||||
configData?: DistributedConfig,
|
|
||||||
configYAML?: string,
|
|
||||||
): Promise<Result<DistributedConfig | null, PentestError>> {
|
|
||||||
if (configData) {
|
|
||||||
return ok(configData);
|
|
||||||
}
|
|
||||||
if (configYAML) {
|
|
||||||
try {
|
|
||||||
const config = parseConfigYAML(configYAML);
|
|
||||||
return ok(distributeConfig(config));
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
||||||
return err(
|
|
||||||
new PentestError(`Failed to parse config YAML: ${errorMessage}`, 'config', false, { originalError: errorMessage }, ErrorCode.CONFIG_PARSE_ERROR),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!configPath) {
|
if (!configPath) {
|
||||||
return ok(null);
|
return ok(null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { SessionMetadata } from '../audit/utils.js';
|
import type { SessionMetadata } from '../audit/utils.js';
|
||||||
import type { CheckpointProvider } from '../interfaces/checkpoint-provider.js';
|
|
||||||
import { NoOpCheckpointProvider } from '../interfaces/checkpoint-provider.js';
|
|
||||||
import type { FindingsProvider } from '../interfaces/findings-provider.js';
|
|
||||||
import { NoOpFindingsProvider } from '../interfaces/findings-provider.js';
|
|
||||||
import type { ReportOutputProvider } from '../interfaces/report-output-provider.js';
|
|
||||||
import { NoOpReportOutputProvider } from '../interfaces/report-output-provider.js';
|
|
||||||
import type { ContainerConfig } from '../types/config.js';
|
|
||||||
import { AgentExecutionService } from './agent-execution.js';
|
import { AgentExecutionService } from './agent-execution.js';
|
||||||
import { ConfigLoaderService } from './config-loader.js';
|
import { ConfigLoaderService } from './config-loader.js';
|
||||||
import { ExploitationCheckerService } from './exploitation-checker.js';
|
import { ExploitationCheckerService } from './exploitation-checker.js';
|
||||||
@@ -39,10 +32,6 @@ import { ExploitationCheckerService } from './exploitation-checker.js';
|
|||||||
*/
|
*/
|
||||||
export interface ContainerDependencies {
|
export interface ContainerDependencies {
|
||||||
readonly sessionMetadata: SessionMetadata;
|
readonly sessionMetadata: SessionMetadata;
|
||||||
readonly config: ContainerConfig;
|
|
||||||
readonly findingsProvider?: FindingsProvider;
|
|
||||||
readonly checkpointProvider?: CheckpointProvider;
|
|
||||||
readonly reportOutputProvider?: ReportOutputProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,27 +45,17 @@ export interface ContainerDependencies {
|
|||||||
*/
|
*/
|
||||||
export class Container {
|
export class Container {
|
||||||
readonly sessionMetadata: SessionMetadata;
|
readonly sessionMetadata: SessionMetadata;
|
||||||
readonly config: ContainerConfig;
|
|
||||||
readonly agentExecution: AgentExecutionService;
|
readonly agentExecution: AgentExecutionService;
|
||||||
readonly configLoader: ConfigLoaderService;
|
readonly configLoader: ConfigLoaderService;
|
||||||
readonly exploitationChecker: ExploitationCheckerService;
|
readonly exploitationChecker: ExploitationCheckerService;
|
||||||
readonly findingsProvider: FindingsProvider;
|
|
||||||
readonly checkpointProvider: CheckpointProvider;
|
|
||||||
readonly reportOutputProvider: ReportOutputProvider;
|
|
||||||
|
|
||||||
constructor(deps: ContainerDependencies) {
|
constructor(deps: ContainerDependencies) {
|
||||||
this.sessionMetadata = deps.sessionMetadata;
|
this.sessionMetadata = deps.sessionMetadata;
|
||||||
this.config = deps.config;
|
|
||||||
|
|
||||||
// Wire services with explicit constructor injection
|
// Wire services with explicit constructor injection
|
||||||
this.configLoader = new ConfigLoaderService();
|
this.configLoader = new ConfigLoaderService();
|
||||||
this.exploitationChecker = new ExploitationCheckerService();
|
this.exploitationChecker = new ExploitationCheckerService();
|
||||||
this.agentExecution = new AgentExecutionService(this.configLoader);
|
this.agentExecution = new AgentExecutionService(this.configLoader);
|
||||||
|
|
||||||
// Wire providers with default no-ops when not provided
|
|
||||||
this.findingsProvider = deps.findingsProvider ?? new NoOpFindingsProvider();
|
|
||||||
this.checkpointProvider = deps.checkpointProvider ?? new NoOpCheckpointProvider();
|
|
||||||
this.reportOutputProvider = deps.reportOutputProvider ?? new NoOpReportOutputProvider();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,38 +65,6 @@ export class Container {
|
|||||||
*/
|
*/
|
||||||
const containers = new Map<string, Container>();
|
const containers = new Map<string, Container>();
|
||||||
|
|
||||||
/** Default container config — OSS standalone defaults */
|
|
||||||
const DEFAULT_CONFIG: ContainerConfig = {
|
|
||||||
deliverablesSubdir: '.shannon/deliverables',
|
|
||||||
auditDir: './workspaces',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory function for creating containers.
|
|
||||||
*
|
|
||||||
* Default: creates a plain Container with NoOp providers. Consumers can call
|
|
||||||
* setContainerFactory() at worker startup to inject custom provider
|
|
||||||
* implementations into every container.
|
|
||||||
*/
|
|
||||||
type ContainerFactory = (
|
|
||||||
workflowId: string,
|
|
||||||
sessionMetadata: SessionMetadata,
|
|
||||||
config: ContainerConfig,
|
|
||||||
) => Container;
|
|
||||||
|
|
||||||
let containerFactory: ContainerFactory = (_workflowId, sessionMetadata, config) =>
|
|
||||||
new Container({ sessionMetadata, config });
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override the default container factory.
|
|
||||||
*
|
|
||||||
* Call once at worker startup to inject providers into all containers
|
|
||||||
* created during the worker's lifetime.
|
|
||||||
*/
|
|
||||||
export function setContainerFactory(factory: ContainerFactory): void {
|
|
||||||
containerFactory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or create a Container for a workflow.
|
* Get or create a Container for a workflow.
|
||||||
*
|
*
|
||||||
@@ -126,18 +73,13 @@ export function setContainerFactory(factory: ContainerFactory): void {
|
|||||||
*
|
*
|
||||||
* @param workflowId - Unique workflow identifier
|
* @param workflowId - Unique workflow identifier
|
||||||
* @param sessionMetadata - Session metadata for audit paths
|
* @param sessionMetadata - Session metadata for audit paths
|
||||||
* @param config - Runtime configuration (defaults to OSS standalone config)
|
|
||||||
* @returns Container instance for the workflow
|
* @returns Container instance for the workflow
|
||||||
*/
|
*/
|
||||||
export function getOrCreateContainer(
|
export function getOrCreateContainer(workflowId: string, sessionMetadata: SessionMetadata): Container {
|
||||||
workflowId: string,
|
|
||||||
sessionMetadata: SessionMetadata,
|
|
||||||
config: ContainerConfig = DEFAULT_CONFIG,
|
|
||||||
): Container {
|
|
||||||
let container = containers.get(workflowId);
|
let container = containers.get(workflowId);
|
||||||
|
|
||||||
if (!container) {
|
if (!container) {
|
||||||
container = containerFactory(workflowId, sessionMetadata, config);
|
container = new Container({ sessionMetadata });
|
||||||
containers.set(workflowId, container);
|
containers.set(workflowId, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ export { AgentExecutionService } from './agent-execution.js';
|
|||||||
|
|
||||||
export { ConfigLoaderService } from './config-loader.js';
|
export { ConfigLoaderService } from './config-loader.js';
|
||||||
export type { ContainerDependencies } from './container.js';
|
export type { ContainerDependencies } from './container.js';
|
||||||
export { Container, getContainer, getOrCreateContainer, removeContainer, setContainerFactory } from './container.js';
|
export { Container, getOrCreateContainer, removeContainer } from './container.js';
|
||||||
export { ExploitationCheckerService } from './exploitation-checker.js';
|
export { ExploitationCheckerService } from './exploitation-checker.js';
|
||||||
export { loadPrompt } from './prompt-manager.js';
|
export { loadPrompt } from './prompt-manager.js';
|
||||||
export { assembleFinalReport, injectModelIntoReport } from './reporting.js';
|
export { assembleFinalReport, injectModelIntoReport } from './reporting.js';
|
||||||
export type { ClaudePromptResult } from '../ai/claude-executor.js';
|
|
||||||
export { runClaudePrompt } from '../ai/claude-executor.js';
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* Checks run sequentially, cheapest first:
|
* Checks run sequentially, cheapest first:
|
||||||
* 1. Repository path exists and contains .git
|
* 1. Repository path exists and contains .git
|
||||||
* 2. Config file parses and validates (if provided)
|
* 2. Config file parses and validates (if provided)
|
||||||
* 3. Credentials validate via Claude Agent SDK query (API key, OAuth, Bedrock, or Vertex AI)
|
* 3. Credentials validate via Claude Agent SDK query (API key, OAuth, Bedrock, Vertex AI, or router mode)
|
||||||
* 4. Target URL is reachable from the container (DNS + HTTP)
|
* 4. Target URL is reachable from the container (DNS + HTTP)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ function isLoopbackAddress(address: string): boolean {
|
|||||||
|
|
||||||
// === Repository Validation ===
|
// === Repository Validation ===
|
||||||
|
|
||||||
async function validateRepo(repoPath: string, logger: ActivityLogger, skipGitCheck?: boolean): Promise<Result<void, PentestError>> {
|
async function validateRepo(repoPath: string, logger: ActivityLogger): Promise<Result<void, PentestError>> {
|
||||||
logger.info('Checking repository path...', { repoPath });
|
logger.info('Checking repository path...', { repoPath });
|
||||||
|
|
||||||
// 1. Check repo directory exists
|
// 1. Check repo directory exists
|
||||||
@@ -68,22 +68,10 @@ async function validateRepo(repoPath: string, logger: ActivityLogger, skipGitChe
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Check .git directory exists (skipped when consumer removes .git after clone)
|
// 2. Check .git directory exists
|
||||||
if (!skipGitCheck) {
|
try {
|
||||||
try {
|
const gitStats = await fs.stat(`${repoPath}/.git`);
|
||||||
const gitStats = await fs.stat(`${repoPath}/.git`);
|
if (!gitStats.isDirectory()) {
|
||||||
if (!gitStats.isDirectory()) {
|
|
||||||
return err(
|
|
||||||
new PentestError(
|
|
||||||
`Not a git repository (no .git directory): ${repoPath}`,
|
|
||||||
'config',
|
|
||||||
false,
|
|
||||||
{ repoPath },
|
|
||||||
ErrorCode.REPO_NOT_FOUND,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return err(
|
return err(
|
||||||
new PentestError(
|
new PentestError(
|
||||||
`Not a git repository (no .git directory): ${repoPath}`,
|
`Not a git repository (no .git directory): ${repoPath}`,
|
||||||
@@ -94,8 +82,16 @@ async function validateRepo(repoPath: string, logger: ActivityLogger, skipGitChe
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} catch {
|
||||||
logger.info('Skipping .git check (skipGitCheck enabled)');
|
return err(
|
||||||
|
new PentestError(
|
||||||
|
`Not a git repository (no .git directory): ${repoPath}`,
|
||||||
|
'config',
|
||||||
|
false,
|
||||||
|
{ repoPath },
|
||||||
|
ErrorCode.REPO_NOT_FOUND,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Repository path OK');
|
logger.info('Repository path OK');
|
||||||
@@ -184,21 +180,9 @@ function classifySdkError(sdkError: SDKAssistantMessageError, authType: string):
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Validate credentials via a minimal Claude Agent SDK query. */
|
/** Validate credentials via a minimal Claude Agent SDK query. */
|
||||||
async function validateCredentials(logger: ActivityLogger, apiKey?: string, providerConfig?: import('../types/config.js').ProviderConfig): Promise<Result<void, PentestError>> {
|
async function validateCredentials(logger: ActivityLogger): Promise<Result<void, PentestError>> {
|
||||||
// 0. If providerConfig is present, credentials are managed by the caller.
|
|
||||||
// The executor will map providerConfig directly to sdkEnv — no process.env needed.
|
|
||||||
if (providerConfig) {
|
|
||||||
logger.info(`Provider config present (type: ${providerConfig.providerType || 'anthropic_api'}) — skipping env-based credential validation`);
|
|
||||||
return ok(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0b. If apiKey provided via config, set it in env for SDK validation
|
|
||||||
// This avoids requiring process.env.ANTHROPIC_API_KEY when key is threaded via input
|
|
||||||
if (apiKey) {
|
|
||||||
process.env.ANTHROPIC_API_KEY = apiKey;
|
|
||||||
}
|
|
||||||
// 1. Custom base URL — validate endpoint is reachable via SDK query
|
// 1. Custom base URL — validate endpoint is reachable via SDK query
|
||||||
if (process.env.ANTHROPIC_BASE_URL && process.env.ANTHROPIC_AUTH_TOKEN) {
|
if (process.env.ANTHROPIC_BASE_URL) {
|
||||||
const baseUrl = process.env.ANTHROPIC_BASE_URL;
|
const baseUrl = process.env.ANTHROPIC_BASE_URL;
|
||||||
logger.info(`Validating custom base URL: ${baseUrl}`);
|
logger.info(`Validating custom base URL: ${baseUrl}`);
|
||||||
|
|
||||||
@@ -305,7 +289,7 @@ async function validateCredentials(logger: ActivityLogger, apiKey?: string, prov
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Check that at least one credential is present
|
// 4. 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) {
|
if (!process.env.ANTHROPIC_API_KEY && !process.env.CLAUDE_CODE_OAUTH_TOKEN) {
|
||||||
return err(
|
return err(
|
||||||
new PentestError(
|
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, or CLAUDE_CODE_USE_VERTEX=1 for Google Vertex AI)',
|
||||||
@@ -463,7 +447,7 @@ async function validateTargetUrl(targetUrl: string, logger: ActivityLogger): Pro
|
|||||||
*
|
*
|
||||||
* 1. Repository path exists and contains .git
|
* 1. Repository path exists and contains .git
|
||||||
* 2. Config file parses and validates (if configPath provided)
|
* 2. Config file parses and validates (if configPath provided)
|
||||||
* 3. Credentials validate (API key, OAuth, Bedrock, or Vertex AI)
|
* 3. Credentials validate (API key, OAuth, or router mode)
|
||||||
* 4. Target URL is reachable from the container
|
* 4. Target URL is reachable from the container
|
||||||
*
|
*
|
||||||
* Returns on first failure.
|
* Returns on first failure.
|
||||||
@@ -473,12 +457,9 @@ export async function runPreflightChecks(
|
|||||||
repoPath: string,
|
repoPath: string,
|
||||||
configPath: string | undefined,
|
configPath: string | undefined,
|
||||||
logger: ActivityLogger,
|
logger: ActivityLogger,
|
||||||
skipGitCheck?: boolean,
|
|
||||||
apiKey?: string,
|
|
||||||
providerConfig?: import('../types/config.js').ProviderConfig,
|
|
||||||
): Promise<Result<void, PentestError>> {
|
): Promise<Result<void, PentestError>> {
|
||||||
// 1. Repository check (free — filesystem only)
|
// 1. Repository check (free — filesystem only)
|
||||||
const repoResult = await validateRepo(repoPath, logger, skipGitCheck);
|
const repoResult = await validateRepo(repoPath, logger);
|
||||||
if (!repoResult.ok) {
|
if (!repoResult.ok) {
|
||||||
return repoResult;
|
return repoResult;
|
||||||
}
|
}
|
||||||
@@ -491,8 +472,8 @@ export async function runPreflightChecks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Credential check (cheap — 1 SDK round-trip, skipped when providerConfig present)
|
// 3. Credential check (cheap — 1 SDK round-trip)
|
||||||
const credResult = await validateCredentials(logger, apiKey, providerConfig);
|
const credResult = await validateCredentials(logger);
|
||||||
if (!credResult.ok) {
|
if (!credResult.ok) {
|
||||||
return credResult;
|
return credResult;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,14 +23,10 @@ interface IncludeReplacement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pure function: Build complete login instructions from config
|
// Pure function: Build complete login instructions from config
|
||||||
async function buildLoginInstructions(
|
async function buildLoginInstructions(authentication: Authentication, logger: ActivityLogger): Promise<string> {
|
||||||
authentication: Authentication,
|
|
||||||
logger: ActivityLogger,
|
|
||||||
promptsBaseDir: string = PROMPTS_DIR,
|
|
||||||
): Promise<string> {
|
|
||||||
try {
|
try {
|
||||||
// 1. Load the login instructions template
|
// 1. Load the login instructions template
|
||||||
const loginInstructionsPath = path.join(promptsBaseDir, 'shared', 'login-instructions.txt');
|
const loginInstructionsPath = path.join(PROMPTS_DIR, 'shared', 'login-instructions.txt');
|
||||||
|
|
||||||
if (!(await fs.pathExists(loginInstructionsPath))) {
|
if (!(await fs.pathExists(loginInstructionsPath))) {
|
||||||
throw new PentestError('Login instructions template not found', 'filesystem', false, { loginInstructionsPath });
|
throw new PentestError('Login instructions template not found', 'filesystem', false, { loginInstructionsPath });
|
||||||
@@ -152,7 +148,6 @@ async function interpolateVariables(
|
|||||||
variables: PromptVariables,
|
variables: PromptVariables,
|
||||||
config: DistributedConfig | null = null,
|
config: DistributedConfig | null = null,
|
||||||
logger: ActivityLogger,
|
logger: ActivityLogger,
|
||||||
promptsBaseDir: string = PROMPTS_DIR,
|
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
if (!template || typeof template !== 'string') {
|
if (!template || typeof template !== 'string') {
|
||||||
@@ -193,7 +188,7 @@ async function interpolateVariables(
|
|||||||
|
|
||||||
// Extract and inject login instructions from config
|
// Extract and inject login instructions from config
|
||||||
if (config.authentication?.login_flow) {
|
if (config.authentication?.login_flow) {
|
||||||
const loginInstructions = await buildLoginInstructions(config.authentication, logger, promptsBaseDir);
|
const loginInstructions = await buildLoginInstructions(config.authentication, logger);
|
||||||
result = result.replace(/{{LOGIN_INSTRUCTIONS}}/g, loginInstructions);
|
result = result.replace(/{{LOGIN_INSTRUCTIONS}}/g, loginInstructions);
|
||||||
} else {
|
} else {
|
||||||
result = result.replace(/{{LOGIN_INSTRUCTIONS}}/g, '');
|
result = result.replace(/{{LOGIN_INSTRUCTIONS}}/g, '');
|
||||||
@@ -228,12 +223,10 @@ export async function loadPrompt(
|
|||||||
config: DistributedConfig | null = null,
|
config: DistributedConfig | null = null,
|
||||||
pipelineTestingMode: boolean = false,
|
pipelineTestingMode: boolean = false,
|
||||||
logger: ActivityLogger,
|
logger: ActivityLogger,
|
||||||
promptDir?: string,
|
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
// 1. Resolve prompt file path (promptDir override → default PROMPTS_DIR)
|
// 1. Resolve prompt file path
|
||||||
const basePromptsDir = promptDir ?? PROMPTS_DIR;
|
const promptsDir = pipelineTestingMode ? path.join(PROMPTS_DIR, 'pipeline-testing') : PROMPTS_DIR;
|
||||||
const promptsDir = pipelineTestingMode ? path.join(basePromptsDir, 'pipeline-testing') : basePromptsDir;
|
|
||||||
const promptPath = path.join(promptsDir, `${promptName}.txt`);
|
const promptPath = path.join(promptsDir, `${promptName}.txt`);
|
||||||
|
|
||||||
if (pipelineTestingMode) {
|
if (pipelineTestingMode) {
|
||||||
@@ -263,7 +256,7 @@ export async function loadPrompt(
|
|||||||
template = await processIncludes(template, promptsDir);
|
template = await processIncludes(template, promptsDir);
|
||||||
|
|
||||||
// 5. Interpolate variables and return final prompt
|
// 5. Interpolate variables and return final prompt
|
||||||
return await interpolateVariables(template, enhancedVariables, config, logger, basePromptsDir);
|
return await interpolateVariables(template, enhancedVariables, config, logger);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof PentestError) {
|
if (error instanceof PentestError) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// as published by the Free Software Foundation.
|
// as published by the Free Software Foundation.
|
||||||
|
|
||||||
import { fs, path } from 'zx';
|
import { fs, path } from 'zx';
|
||||||
|
|
||||||
import type { ExploitationDecision, VulnType } from '../types/agents.js';
|
import type { ExploitationDecision, VulnType } from '../types/agents.js';
|
||||||
import { ErrorCode } from '../types/errors.js';
|
import { ErrorCode } from '../types/errors.js';
|
||||||
import { err, ok, type Result } from '../types/result.js';
|
import { err, ok, type Result } from '../types/result.js';
|
||||||
@@ -115,12 +114,12 @@ function getExistenceErrorMessage(existence: FileExistence): string {
|
|||||||
const { deliverableExists, queueExists } = existence;
|
const { deliverableExists, queueExists } = existence;
|
||||||
|
|
||||||
if (!deliverableExists && !queueExists) {
|
if (!deliverableExists && !queueExists) {
|
||||||
return 'Analysis failed: Neither deliverable nor queue file exists. Both are required.';
|
return 'Analysis failed: Neither deliverable nor queue file exists. Analysis agent must create both files.';
|
||||||
}
|
}
|
||||||
if (!queueExists) {
|
if (!queueExists) {
|
||||||
return 'Analysis incomplete: Deliverable exists but queue file missing. Both are required.';
|
return 'Analysis incomplete: Deliverable exists but queue file missing. Analysis agent must create both files.';
|
||||||
}
|
}
|
||||||
return 'Analysis incomplete: Queue exists but deliverable file missing. Both are required.';
|
return 'Analysis incomplete: Queue exists but deliverable file missing. Analysis agent must create both files.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pure function to create file paths
|
// Pure function to create file paths
|
||||||
@@ -134,8 +133,8 @@ const createPaths = (vulnType: VulnType, sourceDir: string): PathsBase | PathsWi
|
|||||||
|
|
||||||
return Object.freeze({
|
return Object.freeze({
|
||||||
vulnType,
|
vulnType,
|
||||||
deliverable: path.join(sourceDir, config.deliverable),
|
deliverable: path.join(sourceDir, 'deliverables', config.deliverable),
|
||||||
queue: path.join(sourceDir, config.queue),
|
queue: path.join(sourceDir, 'deliverables', config.queue),
|
||||||
sourceDir,
|
sourceDir,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// as published by the Free Software Foundation.
|
// as published by the Free Software Foundation.
|
||||||
|
|
||||||
import { fs, path } from 'zx';
|
import { fs, path } from 'zx';
|
||||||
import { deliverablesDir } from '../paths.js';
|
|
||||||
import type { ActivityLogger } from '../types/activity-logger.js';
|
import type { ActivityLogger } from '../types/activity-logger.js';
|
||||||
import { ErrorCode } from '../types/errors.js';
|
import { ErrorCode } from '../types/errors.js';
|
||||||
import { PentestError } from './error-handling.js';
|
import { PentestError } from './error-handling.js';
|
||||||
@@ -17,11 +16,7 @@ interface DeliverableFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pure function: Assemble final report from specialist deliverables
|
// Pure function: Assemble final report from specialist deliverables
|
||||||
export async function assembleFinalReport(
|
export async function assembleFinalReport(sourceDir: string, logger: ActivityLogger): Promise<string> {
|
||||||
sourceDir: string,
|
|
||||||
deliverablesSubdir: string | undefined,
|
|
||||||
logger: ActivityLogger,
|
|
||||||
): Promise<string> {
|
|
||||||
const deliverableFiles: DeliverableFile[] = [
|
const deliverableFiles: DeliverableFile[] = [
|
||||||
{ name: 'Injection', path: 'injection_exploitation_evidence.md', required: false },
|
{ name: 'Injection', path: 'injection_exploitation_evidence.md', required: false },
|
||||||
{ name: 'XSS', path: 'xss_exploitation_evidence.md', required: false },
|
{ name: 'XSS', path: 'xss_exploitation_evidence.md', required: false },
|
||||||
@@ -33,7 +28,7 @@ export async function assembleFinalReport(
|
|||||||
const sections: string[] = [];
|
const sections: string[] = [];
|
||||||
|
|
||||||
for (const file of deliverableFiles) {
|
for (const file of deliverableFiles) {
|
||||||
const filePath = path.join(deliverablesDir(sourceDir, deliverablesSubdir), file.path);
|
const filePath = path.join(sourceDir, 'deliverables', file.path);
|
||||||
try {
|
try {
|
||||||
if (await fs.pathExists(filePath)) {
|
if (await fs.pathExists(filePath)) {
|
||||||
const content = await fs.readFile(filePath, 'utf8');
|
const content = await fs.readFile(filePath, 'utf8');
|
||||||
@@ -60,12 +55,12 @@ export async function assembleFinalReport(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const finalContent = sections.join('\n\n');
|
const finalContent = sections.join('\n\n');
|
||||||
const outputDir = deliverablesDir(sourceDir, deliverablesSubdir);
|
const deliverablesDir = path.join(sourceDir, 'deliverables');
|
||||||
const finalReportPath = path.join(outputDir, 'comprehensive_security_assessment_report.md');
|
const finalReportPath = path.join(deliverablesDir, 'comprehensive_security_assessment_report.md');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Ensure deliverables directory exists
|
// Ensure deliverables directory exists
|
||||||
await fs.ensureDir(outputDir);
|
await fs.ensureDir(deliverablesDir);
|
||||||
await fs.writeFile(finalReportPath, finalContent);
|
await fs.writeFile(finalReportPath, finalContent);
|
||||||
logger.info(`Final report assembled at ${finalReportPath}`);
|
logger.info(`Final report assembled at ${finalReportPath}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -86,7 +81,6 @@ export async function assembleFinalReport(
|
|||||||
*/
|
*/
|
||||||
export async function injectModelIntoReport(
|
export async function injectModelIntoReport(
|
||||||
repoPath: string,
|
repoPath: string,
|
||||||
deliverablesSubdir: string | undefined,
|
|
||||||
outputPath: string,
|
outputPath: string,
|
||||||
logger: ActivityLogger,
|
logger: ActivityLogger,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@@ -123,7 +117,7 @@ export async function injectModelIntoReport(
|
|||||||
logger.info(`Injecting model info into report: ${modelStr}`);
|
logger.info(`Injecting model info into report: ${modelStr}`);
|
||||||
|
|
||||||
// 3. Read the final report
|
// 3. Read the final report
|
||||||
const reportPath = path.join(deliverablesDir(repoPath, deliverablesSubdir), 'comprehensive_security_assessment_report.md');
|
const reportPath = path.join(repoPath, 'deliverables', 'comprehensive_security_assessment_report.md');
|
||||||
|
|
||||||
if (!(await fs.pathExists(reportPath))) {
|
if (!(await fs.pathExists(reportPath))) {
|
||||||
logger.warn('Final report not found, skipping model injection');
|
logger.warn('Final report not found, skipping model injection');
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// as published by the Free Software Foundation.
|
// as published by the Free Software Foundation.
|
||||||
|
|
||||||
import { fs, path } from 'zx';
|
import { fs, path } from 'zx';
|
||||||
|
|
||||||
import { validateQueueAndDeliverable } from './services/queue-validation.js';
|
import { validateQueueAndDeliverable } from './services/queue-validation.js';
|
||||||
import type { ActivityLogger } from './types/activity-logger.js';
|
import type { ActivityLogger } from './types/activity-logger.js';
|
||||||
import type { AgentDefinition, AgentName, AgentValidator, PlaywrightSession, VulnType } from './types/index.js';
|
import type { AgentDefinition, AgentName, AgentValidator, PlaywrightSession, VulnType } from './types/index.js';
|
||||||
@@ -17,7 +16,7 @@ export const AGENTS: Readonly<Record<AgentName, AgentDefinition>> = Object.freez
|
|||||||
displayName: 'Pre-recon agent',
|
displayName: 'Pre-recon agent',
|
||||||
prerequisites: [],
|
prerequisites: [],
|
||||||
promptTemplate: 'pre-recon-code',
|
promptTemplate: 'pre-recon-code',
|
||||||
deliverableFilename: 'pre_recon_deliverable.md',
|
deliverableFilename: 'code_analysis_deliverable.md',
|
||||||
modelTier: 'large',
|
modelTier: 'large',
|
||||||
},
|
},
|
||||||
recon: {
|
recon: {
|
||||||
@@ -103,6 +102,7 @@ export const AGENTS: Readonly<Record<AgentName, AgentDefinition>> = Object.freez
|
|||||||
prerequisites: ['injection-exploit', 'xss-exploit', 'auth-exploit', 'ssrf-exploit', 'authz-exploit'],
|
prerequisites: ['injection-exploit', 'xss-exploit', 'auth-exploit', 'ssrf-exploit', 'authz-exploit'],
|
||||||
promptTemplate: 'report-executive',
|
promptTemplate: 'report-executive',
|
||||||
deliverableFilename: 'comprehensive_security_assessment_report.md',
|
deliverableFilename: 'comprehensive_security_assessment_report.md',
|
||||||
|
modelTier: 'small',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ function createVulnValidator(vulnType: VulnType): AgentValidator {
|
|||||||
// Factory function for exploit deliverable validators
|
// Factory function for exploit deliverable validators
|
||||||
function createExploitValidator(vulnType: VulnType): AgentValidator {
|
function createExploitValidator(vulnType: VulnType): AgentValidator {
|
||||||
return async (sourceDir: string): Promise<boolean> => {
|
return async (sourceDir: string): Promise<boolean> => {
|
||||||
const evidenceFile = path.join(sourceDir, `${vulnType}_exploitation_evidence.md`);
|
const evidenceFile = path.join(sourceDir, 'deliverables', `${vulnType}_exploitation_evidence.md`);
|
||||||
return await fs.pathExists(evidenceFile);
|
return await fs.pathExists(evidenceFile);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -179,13 +179,13 @@ export const PLAYWRIGHT_SESSION_MAPPING: Record<string, PlaywrightSession> = Obj
|
|||||||
export const AGENT_VALIDATORS: Record<AgentName, AgentValidator> = Object.freeze({
|
export const AGENT_VALIDATORS: Record<AgentName, AgentValidator> = Object.freeze({
|
||||||
// Pre-reconnaissance agent - validates the code analysis deliverable created by the agent
|
// Pre-reconnaissance agent - validates the code analysis deliverable created by the agent
|
||||||
'pre-recon': async (sourceDir: string): Promise<boolean> => {
|
'pre-recon': async (sourceDir: string): Promise<boolean> => {
|
||||||
const codeAnalysisFile = path.join(sourceDir, 'pre_recon_deliverable.md');
|
const codeAnalysisFile = path.join(sourceDir, 'deliverables', 'code_analysis_deliverable.md');
|
||||||
return await fs.pathExists(codeAnalysisFile);
|
return await fs.pathExists(codeAnalysisFile);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Reconnaissance agent
|
// Reconnaissance agent
|
||||||
recon: async (sourceDir: string): Promise<boolean> => {
|
recon: async (sourceDir: string): Promise<boolean> => {
|
||||||
const reconFile = path.join(sourceDir, 'recon_deliverable.md');
|
const reconFile = path.join(sourceDir, 'deliverables', 'recon_deliverable.md');
|
||||||
return await fs.pathExists(reconFile);
|
return await fs.pathExists(reconFile);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ export const AGENT_VALIDATORS: Record<AgentName, AgentValidator> = Object.freeze
|
|||||||
|
|
||||||
// Executive report agent
|
// Executive report agent
|
||||||
report: async (sourceDir: string, logger: ActivityLogger): Promise<boolean> => {
|
report: async (sourceDir: string, logger: ActivityLogger): Promise<boolean> => {
|
||||||
const reportFile = path.join(sourceDir, 'comprehensive_security_assessment_report.md');
|
const reportFile = path.join(sourceDir, 'deliverables', 'comprehensive_security_assessment_report.md');
|
||||||
|
|
||||||
const reportExists = await fs.pathExists(reportFile);
|
const reportExists = await fs.pathExists(reportFile);
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,8 @@ import path from 'node:path';
|
|||||||
import { ApplicationFailure, Context, heartbeat } from '@temporalio/activity';
|
import { ApplicationFailure, Context, heartbeat } from '@temporalio/activity';
|
||||||
import { AuditSession } from '../audit/index.js';
|
import { AuditSession } from '../audit/index.js';
|
||||||
import type { ResumeAttempt } from '../audit/metrics-tracker.js';
|
import type { ResumeAttempt } from '../audit/metrics-tracker.js';
|
||||||
import type { SessionMetadata } from '../audit/utils.js';
|
import { copyDeliverablesToAudit, type SessionMetadata } from '../audit/utils.js';
|
||||||
import type { WorkflowSummary } from '../audit/workflow-logger.js';
|
import type { WorkflowSummary } from '../audit/workflow-logger.js';
|
||||||
import type { ContainerConfig, ProviderConfig } from '../types/config.js';
|
|
||||||
import type { CheckpointContext } from '../interfaces/checkpoint-provider.js';
|
|
||||||
import { getContainer, getOrCreateContainer, removeContainer } from '../services/container.js';
|
import { getContainer, getOrCreateContainer, removeContainer } from '../services/container.js';
|
||||||
import { classifyErrorForTemporal, PentestError } from '../services/error-handling.js';
|
import { classifyErrorForTemporal, PentestError } from '../services/error-handling.js';
|
||||||
import { ExploitationCheckerService } from '../services/exploitation-checker.js';
|
import { ExploitationCheckerService } from '../services/exploitation-checker.js';
|
||||||
@@ -36,10 +34,9 @@ import type { AgentName } from '../types/agents.js';
|
|||||||
import { ALL_AGENTS } from '../types/agents.js';
|
import { ALL_AGENTS } from '../types/agents.js';
|
||||||
import { ErrorCode } from '../types/errors.js';
|
import { ErrorCode } from '../types/errors.js';
|
||||||
import { isErr } from '../types/result.js';
|
import { isErr } from '../types/result.js';
|
||||||
import { DEFAULT_DELIVERABLES_SUBDIR, deliverablesDir } from '../paths.js';
|
|
||||||
import { fileExists, readJson } from '../utils/file-io.js';
|
import { fileExists, readJson } from '../utils/file-io.js';
|
||||||
import { createActivityLogger } from './activity-logger.js';
|
import { createActivityLogger } from './activity-logger.js';
|
||||||
import type { AgentMetrics, PipelineState, ResumeState } from './shared.js';
|
import type { AgentMetrics, ResumeState } from './shared.js';
|
||||||
|
|
||||||
// Max lengths to prevent Temporal protobuf buffer overflow
|
// Max lengths to prevent Temporal protobuf buffer overflow
|
||||||
const MAX_ERROR_MESSAGE_LENGTH = 2000;
|
const MAX_ERROR_MESSAGE_LENGTH = 2000;
|
||||||
@@ -52,9 +49,6 @@ const HEARTBEAT_INTERVAL_MS = 2000;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Input for all agent activities.
|
* Input for all agent activities.
|
||||||
*
|
|
||||||
* Config fields are optional with sensible defaults. When provided, they
|
|
||||||
* flow through to getOrCreateContainer() for path and credential configuration.
|
|
||||||
*/
|
*/
|
||||||
export interface ActivityInput {
|
export interface ActivityInput {
|
||||||
webUrl: string;
|
webUrl: string;
|
||||||
@@ -64,16 +58,6 @@ export interface ActivityInput {
|
|||||||
pipelineTestingMode?: boolean;
|
pipelineTestingMode?: boolean;
|
||||||
workflowId: string;
|
workflowId: string;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
|
|
||||||
// Config fields — serializable, read by getOrCreateContainer()
|
|
||||||
configYAML?: string;
|
|
||||||
apiKey?: string;
|
|
||||||
deliverablesSubdir?: string;
|
|
||||||
auditDir?: string;
|
|
||||||
promptDir?: string;
|
|
||||||
sastSarifPath?: string;
|
|
||||||
skipGitCheck?: boolean;
|
|
||||||
providerConfig?: ProviderConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -108,19 +92,6 @@ function buildSessionMetadata(input: ActivityInput): SessionMetadata {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build ContainerConfig from ActivityInput, falling back to defaults.
|
|
||||||
*/
|
|
||||||
function buildContainerConfig(input: ActivityInput): ContainerConfig {
|
|
||||||
return {
|
|
||||||
deliverablesSubdir: input.deliverablesSubdir ?? DEFAULT_DELIVERABLES_SUBDIR,
|
|
||||||
auditDir: input.auditDir ?? './workspaces',
|
|
||||||
...(input.apiKey !== undefined && { apiKey: input.apiKey }),
|
|
||||||
...(input.promptDir !== undefined && { promptDir: input.promptDir }),
|
|
||||||
...(input.providerConfig !== undefined && { providerConfig: input.providerConfig }),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core activity implementation using services.
|
* Core activity implementation using services.
|
||||||
*
|
*
|
||||||
@@ -132,20 +103,6 @@ function buildContainerConfig(input: ActivityInput): ContainerConfig {
|
|||||||
*/
|
*/
|
||||||
async function runAgentActivity(agentName: AgentName, input: ActivityInput): Promise<AgentMetrics> {
|
async function runAgentActivity(agentName: AgentName, input: ActivityInput): Promise<AgentMetrics> {
|
||||||
const { repoPath, configPath, pipelineTestingMode = false, workflowId, webUrl } = input;
|
const { repoPath, configPath, pipelineTestingMode = false, workflowId, webUrl } = input;
|
||||||
|
|
||||||
// Skip guard: the checkpoint provider decides whether to run the agent.
|
|
||||||
// The default NoOp provider always returns { skip: false }.
|
|
||||||
const skipContainer = getContainer(workflowId) ??
|
|
||||||
getOrCreateContainer(workflowId, buildSessionMetadata(input), buildContainerConfig(input));
|
|
||||||
const decision = await skipContainer.checkpointProvider.shouldSkipAgent(
|
|
||||||
agentName,
|
|
||||||
repoPath,
|
|
||||||
input.deliverablesSubdir ?? DEFAULT_DELIVERABLES_SUBDIR,
|
|
||||||
);
|
|
||||||
if (decision.skip && decision.metrics) {
|
|
||||||
return decision.metrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const attemptNumber = Context.current().info.attempt;
|
const attemptNumber = Context.current().info.attempt;
|
||||||
|
|
||||||
@@ -160,7 +117,7 @@ async function runAgentActivity(agentName: AgentName, input: ActivityInput): Pro
|
|||||||
|
|
||||||
// 1. Build session metadata and get/create container
|
// 1. Build session metadata and get/create container
|
||||||
const sessionMetadata = buildSessionMetadata(input);
|
const sessionMetadata = buildSessionMetadata(input);
|
||||||
const container = getOrCreateContainer(workflowId, sessionMetadata, buildContainerConfig(input));
|
const container = getOrCreateContainer(workflowId, sessionMetadata);
|
||||||
|
|
||||||
// 2. Create audit session for THIS agent execution
|
// 2. Create audit session for THIS agent execution
|
||||||
// NOTE: Each agent needs its own AuditSession because AuditSession uses
|
// NOTE: Each agent needs its own AuditSession because AuditSession uses
|
||||||
@@ -169,24 +126,14 @@ async function runAgentActivity(agentName: AgentName, input: ActivityInput): Pro
|
|||||||
await auditSession.initialize(workflowId);
|
await auditSession.initialize(workflowId);
|
||||||
|
|
||||||
// 3. Execute agent via service (throws PentestError on failure)
|
// 3. Execute agent via service (throws PentestError on failure)
|
||||||
const deliverablesPath = deliverablesDir(repoPath, container.config.deliverablesSubdir);
|
|
||||||
const endResult = await container.agentExecution.executeOrThrow(
|
const endResult = await container.agentExecution.executeOrThrow(
|
||||||
agentName,
|
agentName,
|
||||||
{
|
{
|
||||||
webUrl,
|
webUrl,
|
||||||
repoPath,
|
repoPath,
|
||||||
deliverablesPath,
|
|
||||||
configPath,
|
configPath,
|
||||||
pipelineTestingMode,
|
pipelineTestingMode,
|
||||||
attemptNumber,
|
attemptNumber,
|
||||||
...(input.apiKey !== undefined && { apiKey: input.apiKey }),
|
|
||||||
...(input.providerConfig !== undefined && { providerConfig: input.providerConfig }),
|
|
||||||
...(input.promptDir !== undefined && {
|
|
||||||
promptDir: path.isAbsolute(input.promptDir)
|
|
||||||
? input.promptDir
|
|
||||||
: path.resolve(process.env.SHANNON_WORKER_ROOT ?? process.cwd(), input.promptDir),
|
|
||||||
}),
|
|
||||||
...(input.configYAML !== undefined && { configYAML: input.configYAML }),
|
|
||||||
},
|
},
|
||||||
auditSession,
|
auditSession,
|
||||||
logger,
|
logger,
|
||||||
@@ -303,7 +250,7 @@ export async function runReportAgent(input: ActivityInput): Promise<AgentMetrics
|
|||||||
* Runs cheap checks before any agent execution:
|
* Runs cheap checks before any agent execution:
|
||||||
* 1. Repository path exists with .git
|
* 1. Repository path exists with .git
|
||||||
* 2. Config file validates (if provided)
|
* 2. Config file validates (if provided)
|
||||||
* 3. Credential validation (API key, OAuth, Bedrock, or Vertex AI)
|
* 3. Credential validation (API key, OAuth, or router mode)
|
||||||
* 4. Target URL reachable from the container
|
* 4. Target URL reachable from the container
|
||||||
*
|
*
|
||||||
* NOT using runAgentActivity — preflight doesn't run an agent via the SDK.
|
* NOT using runAgentActivity — preflight doesn't run an agent via the SDK.
|
||||||
@@ -321,7 +268,7 @@ export async function runPreflightValidation(input: ActivityInput): Promise<void
|
|||||||
const logger = createActivityLogger();
|
const logger = createActivityLogger();
|
||||||
logger.info('Running preflight validation...', { attempt: attemptNumber });
|
logger.info('Running preflight validation...', { attempt: attemptNumber });
|
||||||
|
|
||||||
const result = await runPreflightChecks(input.webUrl, input.repoPath, input.configPath, logger, input.skipGitCheck, input.apiKey, input.providerConfig);
|
const result = await runPreflightChecks(input.webUrl, input.repoPath, input.configPath, logger);
|
||||||
|
|
||||||
if (isErr(result)) {
|
if (isErr(result)) {
|
||||||
const classified = classifyErrorForTemporal(result.error);
|
const classified = classifyErrorForTemporal(result.error);
|
||||||
@@ -364,40 +311,15 @@ export async function runPreflightValidation(input: ActivityInput): Promise<void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a private git repository inside the workspace deliverables directory.
|
|
||||||
* Idempotent — skips if .git already exists (resume case).
|
|
||||||
*/
|
|
||||||
export async function initDeliverableGit(input: ActivityInput): Promise<void> {
|
|
||||||
const deliverablesPath = deliverablesDir(input.repoPath, input.deliverablesSubdir);
|
|
||||||
await fs.mkdir(deliverablesPath, { recursive: true });
|
|
||||||
|
|
||||||
// Check for .git directly inside deliverables, not parent repo's .git
|
|
||||||
const dotGitPath = path.join(deliverablesPath, '.git');
|
|
||||||
try {
|
|
||||||
await fs.stat(dotGitPath);
|
|
||||||
return;
|
|
||||||
} catch {
|
|
||||||
// .git doesn't exist, proceed with init
|
|
||||||
}
|
|
||||||
|
|
||||||
await executeGitCommandWithRetry(['git', 'init'], deliverablesPath, 'init deliverables repo');
|
|
||||||
await executeGitCommandWithRetry(
|
|
||||||
['git', 'commit', '--allow-empty', '-m', '📍 Initial deliverables checkpoint'],
|
|
||||||
deliverablesPath,
|
|
||||||
'initial checkpoint',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assemble the final report by concatenating exploitation evidence files.
|
* Assemble the final report by concatenating exploitation evidence files.
|
||||||
*/
|
*/
|
||||||
export async function assembleReportActivity(input: ActivityInput): Promise<void> {
|
export async function assembleReportActivity(input: ActivityInput): Promise<void> {
|
||||||
const { repoPath, deliverablesSubdir } = input;
|
const { repoPath } = input;
|
||||||
const logger = createActivityLogger();
|
const logger = createActivityLogger();
|
||||||
logger.info('Assembling deliverables from specialist agents...');
|
logger.info('Assembling deliverables from specialist agents...');
|
||||||
try {
|
try {
|
||||||
await assembleFinalReport(repoPath, deliverablesSubdir, logger);
|
await assembleFinalReport(repoPath, logger);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = error as Error;
|
const err = error as Error;
|
||||||
logger.warn(`Error assembling final report: ${err.message}`);
|
logger.warn(`Error assembling final report: ${err.message}`);
|
||||||
@@ -408,11 +330,11 @@ export async function assembleReportActivity(input: ActivityInput): Promise<void
|
|||||||
* Inject model metadata into the final report.
|
* Inject model metadata into the final report.
|
||||||
*/
|
*/
|
||||||
export async function injectReportMetadataActivity(input: ActivityInput): Promise<void> {
|
export async function injectReportMetadataActivity(input: ActivityInput): Promise<void> {
|
||||||
const { repoPath, sessionId, outputPath, deliverablesSubdir } = input;
|
const { repoPath, sessionId, outputPath } = input;
|
||||||
const logger = createActivityLogger();
|
const logger = createActivityLogger();
|
||||||
const effectiveOutputPath = outputPath ? path.join(outputPath, sessionId) : path.join('./workspaces', sessionId);
|
const effectiveOutputPath = outputPath ? path.join(outputPath, sessionId) : path.join('./workspaces', sessionId);
|
||||||
try {
|
try {
|
||||||
await injectModelIntoReport(repoPath, deliverablesSubdir, effectiveOutputPath, logger);
|
await injectModelIntoReport(repoPath, effectiveOutputPath, logger);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = error as Error;
|
const err = error as Error;
|
||||||
logger.warn(`Error injecting model into report: ${err.message}`);
|
logger.warn(`Error injecting model into report: ${err.message}`);
|
||||||
@@ -433,9 +355,7 @@ export async function checkExploitationQueue(input: ActivityInput, vulnType: Vul
|
|||||||
const existingContainer = getContainer(workflowId);
|
const existingContainer = getContainer(workflowId);
|
||||||
const checker = existingContainer?.exploitationChecker ?? new ExploitationCheckerService();
|
const checker = existingContainer?.exploitationChecker ?? new ExploitationCheckerService();
|
||||||
|
|
||||||
// Pass deliverablesPath (not repoPath) — validators expect the deliverables directory
|
return checker.checkQueue(vulnType, repoPath, logger);
|
||||||
const delivPath = deliverablesDir(repoPath, input.deliverablesSubdir);
|
|
||||||
return checker.checkQueue(vulnType, delivPath, logger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SessionJson {
|
interface SessionJson {
|
||||||
@@ -464,7 +384,6 @@ export async function loadResumeState(
|
|||||||
workspaceName: string,
|
workspaceName: string,
|
||||||
expectedUrl: string,
|
expectedUrl: string,
|
||||||
expectedRepoPath: string,
|
expectedRepoPath: string,
|
||||||
deliverablesSubdir?: string,
|
|
||||||
): Promise<ResumeState> {
|
): Promise<ResumeState> {
|
||||||
// 1. Validate workspace exists
|
// 1. Validate workspace exists
|
||||||
const sessionPath = path.join('./workspaces', workspaceName, 'session.json');
|
const sessionPath = path.join('./workspaces', workspaceName, 'session.json');
|
||||||
@@ -507,7 +426,7 @@ export async function loadResumeState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deliverableFilename = AGENTS[agentName].deliverableFilename;
|
const deliverableFilename = AGENTS[agentName].deliverableFilename;
|
||||||
const deliverablePath = path.join(deliverablesDir(expectedRepoPath, deliverablesSubdir), deliverableFilename);
|
const deliverablePath = `${expectedRepoPath}/deliverables/${deliverableFilename}`;
|
||||||
const deliverableExists = await fileExists(deliverablePath);
|
const deliverableExists = await fileExists(deliverablePath);
|
||||||
|
|
||||||
if (!deliverableExists) {
|
if (!deliverableExists) {
|
||||||
@@ -541,8 +460,7 @@ export async function loadResumeState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. Find the most recent checkpoint commit
|
// 5. Find the most recent checkpoint commit
|
||||||
const deliverablesPath = deliverablesDir(expectedRepoPath, deliverablesSubdir);
|
const checkpointHash = await findLatestCommit(expectedRepoPath, checkpoints);
|
||||||
const checkpointHash = await findLatestCommit(deliverablesPath, checkpoints);
|
|
||||||
const originalWorkflowId = session.session.originalWorkflowId || session.session.id;
|
const originalWorkflowId = session.session.originalWorkflowId || session.session.id;
|
||||||
|
|
||||||
// 6. Log summary and return resume state
|
// 6. Log summary and return resume state
|
||||||
@@ -562,7 +480,7 @@ export async function loadResumeState(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findLatestCommit(gitDir: string, commitHashes: string[]): Promise<string> {
|
async function findLatestCommit(repoPath: string, commitHashes: string[]): Promise<string> {
|
||||||
if (commitHashes.length === 1) {
|
if (commitHashes.length === 1) {
|
||||||
const hash = commitHashes[0];
|
const hash = commitHashes[0];
|
||||||
if (!hash) {
|
if (!hash) {
|
||||||
@@ -579,7 +497,7 @@ async function findLatestCommit(gitDir: string, commitHashes: string[]): Promise
|
|||||||
|
|
||||||
const result = await executeGitCommandWithRetry(
|
const result = await executeGitCommandWithRetry(
|
||||||
['git', 'rev-list', '--max-count=1', ...commitHashes],
|
['git', 'rev-list', '--max-count=1', ...commitHashes],
|
||||||
gitDir,
|
repoPath,
|
||||||
'find latest commit',
|
'find latest commit',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -587,42 +505,26 @@ async function findLatestCommit(gitDir: string, commitHashes: string[]): Promise
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore deliverables git to a checkpoint.
|
* Restore git workspace to a checkpoint and clean up partial deliverables.
|
||||||
* Operates on the private git inside workspace deliverables, not the user's repo.
|
|
||||||
*/
|
*/
|
||||||
export async function restoreGitCheckpoint(
|
export async function restoreGitCheckpoint(
|
||||||
repoPath: string,
|
repoPath: string,
|
||||||
checkpointHash: string,
|
checkpointHash: string,
|
||||||
incompleteAgents: AgentName[],
|
incompleteAgents: AgentName[],
|
||||||
deliverablesSubdir?: string,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const deliverablesPath = deliverablesDir(repoPath, deliverablesSubdir);
|
|
||||||
const logger = createActivityLogger();
|
const logger = createActivityLogger();
|
||||||
logger.info(`Restoring deliverables to ${checkpointHash}...`);
|
logger.info(`Restoring git workspace to ${checkpointHash}...`);
|
||||||
|
|
||||||
// Validate hash exists in this clone before attempting reset
|
|
||||||
try {
|
|
||||||
await executeGitCommandWithRetry(
|
|
||||||
['git', 'rev-parse', '--verify', checkpointHash],
|
|
||||||
repoPath,
|
|
||||||
'verify checkpoint hash exists'
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
logger.info(`Checkpoint hash not found in clone, skipping git reset: ${checkpointHash}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await executeGitCommandWithRetry(
|
await executeGitCommandWithRetry(
|
||||||
['git', 'reset', '--hard', checkpointHash],
|
['git', 'reset', '--hard', checkpointHash],
|
||||||
deliverablesPath,
|
repoPath,
|
||||||
'reset deliverables to checkpoint',
|
'reset to checkpoint for resume',
|
||||||
);
|
);
|
||||||
await executeGitCommandWithRetry(['git', 'clean', '-fd'], deliverablesPath, 'clean untracked deliverables');
|
await executeGitCommandWithRetry(['git', 'clean', '-fd'], repoPath, 'clean untracked files for resume');
|
||||||
|
|
||||||
// Explicitly delete partial deliverables for incomplete agents
|
|
||||||
for (const agentName of incompleteAgents) {
|
for (const agentName of incompleteAgents) {
|
||||||
const deliverableFilename = AGENTS[agentName].deliverableFilename;
|
const deliverableFilename = AGENTS[agentName].deliverableFilename;
|
||||||
const deliverablePath = path.join(deliverablesPath, deliverableFilename);
|
const deliverablePath = `${repoPath}/deliverables/${deliverableFilename}`;
|
||||||
try {
|
try {
|
||||||
const exists = await fileExists(deliverablePath);
|
const exists = await fileExists(deliverablePath);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
@@ -634,7 +536,7 @@ export async function restoreGitCheckpoint(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Deliverables restored to clean state');
|
logger.info('Workspace restored to clean state');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -687,7 +589,7 @@ export async function logPhaseTransition(
|
|||||||
* Cleans up container when done.
|
* Cleans up container when done.
|
||||||
*/
|
*/
|
||||||
export async function logWorkflowComplete(input: ActivityInput, summary: WorkflowSummary): Promise<void> {
|
export async function logWorkflowComplete(input: ActivityInput, summary: WorkflowSummary): Promise<void> {
|
||||||
const { workflowId } = input;
|
const { repoPath, workflowId } = input;
|
||||||
const sessionMetadata = buildSessionMetadata(input);
|
const sessionMetadata = buildSessionMetadata(input);
|
||||||
|
|
||||||
// 1. Initialize audit session and mark final status
|
// 1. Initialize audit session and mark final status
|
||||||
@@ -729,76 +631,16 @@ export async function logWorkflowComplete(input: ActivityInput, summary: Workflo
|
|||||||
// 5. Write completion entry to workflow.log
|
// 5. Write completion entry to workflow.log
|
||||||
await auditSession.logWorkflowComplete(cumulativeSummary);
|
await auditSession.logWorkflowComplete(cumulativeSummary);
|
||||||
|
|
||||||
// 6. Clean up container
|
// 6. Copy deliverables to workspaces
|
||||||
|
try {
|
||||||
|
await copyDeliverablesToAudit(sessionMetadata, repoPath);
|
||||||
|
} catch (copyErr) {
|
||||||
|
const logger = createActivityLogger();
|
||||||
|
logger.error('Failed to copy deliverables to workspaces', {
|
||||||
|
error: copyErr instanceof Error ? copyErr.message : String(copyErr),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Clean up container
|
||||||
removeContainer(workflowId);
|
removeContainer(workflowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge external findings into the exploitation queue for a vulnerability type.
|
|
||||||
*
|
|
||||||
* Delegates to the FindingsProvider registered in the DI container.
|
|
||||||
* Default: no-op returning { mergedCount: 0 }.
|
|
||||||
* Consumers can override this activity at the worker level with custom findings integration.
|
|
||||||
*/
|
|
||||||
export async function mergeFindingsIntoQueue(
|
|
||||||
input: ActivityInput,
|
|
||||||
vulnType: VulnType,
|
|
||||||
): Promise<{ mergedCount: number }> {
|
|
||||||
const container = getContainer(input.workflowId);
|
|
||||||
if (!container?.findingsProvider) return { mergedCount: 0 };
|
|
||||||
return container.findingsProvider.mergeFindingsIntoQueue(input.repoPath, vulnType, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Persist pipeline state after an agent completes.
|
|
||||||
*
|
|
||||||
* Delegates to the CheckpointProvider registered in the DI container.
|
|
||||||
* Default: no-op. Consumers can override this activity at the worker level with custom persistence.
|
|
||||||
*/
|
|
||||||
export async function saveCheckpoint(
|
|
||||||
input: ActivityInput,
|
|
||||||
agentName: string,
|
|
||||||
phase: string,
|
|
||||||
state: PipelineState,
|
|
||||||
): Promise<void> {
|
|
||||||
const container = getContainer(input.workflowId);
|
|
||||||
if (!container?.checkpointProvider) return;
|
|
||||||
|
|
||||||
const context: CheckpointContext = {
|
|
||||||
repoPath: input.repoPath,
|
|
||||||
sessionId: input.sessionId,
|
|
||||||
deliverablesSubdir: input.deliverablesSubdir ?? DEFAULT_DELIVERABLES_SUBDIR,
|
|
||||||
...(input.outputPath !== undefined && { outputPath: input.outputPath }),
|
|
||||||
};
|
|
||||||
|
|
||||||
return container.checkpointProvider.onAgentComplete(agentName, phase, state, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate an optional additional output alongside the assembled markdown report.
|
|
||||||
*
|
|
||||||
* Delegates to the ReportOutputProvider registered in the DI container.
|
|
||||||
* Default: no-op. Consumers can override this activity at the worker level
|
|
||||||
* to emit derived outputs from the final report.
|
|
||||||
*/
|
|
||||||
export async function generateReportOutputActivity(input: ActivityInput): Promise<void> {
|
|
||||||
const container = getContainer(input.workflowId);
|
|
||||||
if (!container?.reportOutputProvider) return;
|
|
||||||
|
|
||||||
const logger = createActivityLogger();
|
|
||||||
|
|
||||||
// Resolve promptDir against the worker root so providers are cwd-independent.
|
|
||||||
const resolvedInput: ActivityInput = {
|
|
||||||
...input,
|
|
||||||
...(input.promptDir !== undefined && {
|
|
||||||
promptDir: path.isAbsolute(input.promptDir)
|
|
||||||
? input.promptDir
|
|
||||||
: path.resolve(process.env.SHANNON_WORKER_ROOT ?? process.cwd(), input.promptDir),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await container.reportOutputProvider.generate(resolvedInput, logger);
|
|
||||||
if (result.outputPath) {
|
|
||||||
logger.info(`Report output written to ${result.outputPath}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* Pipeline entry point — re-exports the extracted pipeline function and shared types.
|
|
||||||
*
|
|
||||||
* Consumers import from this module to call the pipeline as a library function
|
|
||||||
* within their own workflow context.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export { pentestPipeline } from './workflows.js';
|
|
||||||
export type {
|
|
||||||
AgentMetrics,
|
|
||||||
PipelineInput,
|
|
||||||
PipelineState,
|
|
||||||
PipelineSummary,
|
|
||||||
ResumeState,
|
|
||||||
VulnExploitPipelineResult,
|
|
||||||
} from './shared.js';
|
|
||||||
export type { ActivityInput } from './activities.js';
|
|
||||||
@@ -2,8 +2,7 @@ import { defineQuery } from '@temporalio/workflow';
|
|||||||
|
|
||||||
export type { AgentMetrics } from '../types/metrics.js';
|
export type { AgentMetrics } from '../types/metrics.js';
|
||||||
|
|
||||||
import type { DistributedConfig, PipelineConfig, ProviderConfig } from '../types/config.js';
|
import type { PipelineConfig } from '../types/config.js';
|
||||||
import type { ErrorCode } from '../types/errors.js';
|
|
||||||
import type { AgentMetrics } from '../types/metrics.js';
|
import type { AgentMetrics } from '../types/metrics.js';
|
||||||
|
|
||||||
export interface PipelineInput {
|
export interface PipelineInput {
|
||||||
@@ -17,18 +16,6 @@ export interface PipelineInput {
|
|||||||
sessionId?: string; // Workspace directory name (distinct from workflowId for named workspaces)
|
sessionId?: string; // Workspace directory name (distinct from workflowId for named workspaces)
|
||||||
resumeFromWorkspace?: string; // Workspace name to resume from
|
resumeFromWorkspace?: string; // Workspace name to resume from
|
||||||
terminatedWorkflows?: string[]; // Workflows terminated during resume
|
terminatedWorkflows?: string[]; // Workflows terminated during resume
|
||||||
|
|
||||||
// Config fields — serializable, flow through to ActivityInput → getOrCreateContainer()
|
|
||||||
configYAML?: string; // Raw YAML string (parsed in activity, not workflow — workflow sandbox can't use Node.js)
|
|
||||||
configData?: DistributedConfig; // Pre-parsed config (bypasses file loading)
|
|
||||||
apiKey?: string; // API key override (avoids process.env mutation)
|
|
||||||
deliverablesSubdir?: string; // Override deliverables path (default: '.shannon/deliverables')
|
|
||||||
auditDir?: string; // Override audit log directory (default: './workspaces')
|
|
||||||
promptDir?: string; // Override prompt template directory
|
|
||||||
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.)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResumeState {
|
export interface ResumeState {
|
||||||
@@ -47,13 +34,12 @@ export interface PipelineSummary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PipelineState {
|
export interface PipelineState {
|
||||||
status: 'running' | 'completed' | 'failed' | 'cancelled';
|
status: 'running' | 'completed' | 'failed';
|
||||||
currentPhase: string | null;
|
currentPhase: string | null;
|
||||||
currentAgent: string | null;
|
currentAgent: string | null;
|
||||||
completedAgents: string[];
|
completedAgents: string[];
|
||||||
failedAgent: string | null;
|
failedAgent: string | null;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
errorCode?: ErrorCode;
|
|
||||||
startTime: number;
|
startTime: number;
|
||||||
agentMetrics: Record<string, AgentMetrics>;
|
agentMetrics: Record<string, AgentMetrics>;
|
||||||
summary: PipelineSummary | null;
|
summary: PipelineSummary | null;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import type { PipelineState } from './shared.js';
|
|||||||
* safely imported into Temporal workflows. The caller must ensure
|
* safely imported into Temporal workflows. The caller must ensure
|
||||||
* state.summary is set before calling (via computeSummary).
|
* state.summary is set before calling (via computeSummary).
|
||||||
*/
|
*/
|
||||||
export function toWorkflowSummary(state: PipelineState, status: 'completed' | 'failed' | 'cancelled'): WorkflowSummary {
|
export function toWorkflowSummary(state: PipelineState, status: 'completed' | 'failed'): WorkflowSummary {
|
||||||
// state.summary must be computed before calling this mapper
|
// state.summary must be computed before calling this mapper
|
||||||
const summary = state.summary;
|
const summary = state.summary;
|
||||||
if (!summary) {
|
if (!summary) {
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import { bundleWorkflowCode, NativeConnection, Worker } from '@temporalio/worker
|
|||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import { sanitizeHostname } from '../audit/utils.js';
|
import { sanitizeHostname } from '../audit/utils.js';
|
||||||
import { parseConfig } from '../config-parser.js';
|
import { parseConfig } from '../config-parser.js';
|
||||||
import { deliverablesDir } from '../paths.js';
|
|
||||||
import type { PipelineConfig } from '../types/config.js';
|
import type { PipelineConfig } from '../types/config.js';
|
||||||
import { fileExists, readJson } from '../utils/file-io.js';
|
import { fileExists, readJson } from '../utils/file-io.js';
|
||||||
import * as activities from './activities.js';
|
import * as activities from './activities.js';
|
||||||
@@ -361,13 +360,13 @@ async function waitForWorkflowResult(
|
|||||||
// === Deliverables Copy ===
|
// === Deliverables Copy ===
|
||||||
|
|
||||||
function copyDeliverables(repoPath: string, outputPath: string): void {
|
function copyDeliverables(repoPath: string, outputPath: string): void {
|
||||||
const outputDir = deliverablesDir(repoPath);
|
const deliverablesDir = path.join(repoPath, 'deliverables');
|
||||||
if (!fs.existsSync(outputDir)) {
|
if (!fs.existsSync(deliverablesDir)) {
|
||||||
console.log('No deliverables directory found, skipping copy');
|
console.log('No deliverables directory found, skipping copy');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = fs.readdirSync(outputDir);
|
const files = fs.readdirSync(deliverablesDir);
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
console.log('No deliverables to copy');
|
console.log('No deliverables to copy');
|
||||||
return;
|
return;
|
||||||
@@ -376,8 +375,7 @@ function copyDeliverables(repoPath: string, outputPath: string): void {
|
|||||||
fs.mkdirSync(outputPath, { recursive: true });
|
fs.mkdirSync(outputPath, { recursive: true });
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (file === '.git') continue;
|
const src = path.join(deliverablesDir, file);
|
||||||
const src = path.join(outputDir, file);
|
|
||||||
const dest = path.join(outputPath, file);
|
const dest = path.join(outputPath, file);
|
||||||
fs.cpSync(src, dest, { recursive: true });
|
fs.cpSync(src, dest, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,39 +9,6 @@
|
|||||||
* Pure functions with no side effects — safe for Temporal workflow sandbox.
|
* Pure functions with no side effects — safe for Temporal workflow sandbox.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ErrorCode } from '../types/errors.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps an ApplicationFailure type string to a structured ErrorCode.
|
|
||||||
*
|
|
||||||
* Activities classify errors via classifyErrorForTemporal() and throw
|
|
||||||
* ApplicationFailure with a type string. This function maps those strings
|
|
||||||
* to stable ErrorCode values so consumers can switch on codes instead of
|
|
||||||
* string-matching error messages.
|
|
||||||
*/
|
|
||||||
const ERROR_TYPE_TO_CODE: Record<string, ErrorCode> = {
|
|
||||||
AuthenticationError: ErrorCode.AUTH_FAILED,
|
|
||||||
BillingError: ErrorCode.BILLING_ERROR,
|
|
||||||
RateLimitError: ErrorCode.API_RATE_LIMITED,
|
|
||||||
ConfigurationError: ErrorCode.CONFIG_VALIDATION_FAILED,
|
|
||||||
OutputValidationError: ErrorCode.OUTPUT_VALIDATION_FAILED,
|
|
||||||
AgentExecutionError: ErrorCode.AGENT_EXECUTION_FAILED,
|
|
||||||
GitError: ErrorCode.GIT_CHECKPOINT_FAILED,
|
|
||||||
InvalidTargetError: ErrorCode.TARGET_UNREACHABLE,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function classifyErrorCode(error: unknown): ErrorCode | undefined {
|
|
||||||
let current: unknown = error;
|
|
||||||
while (current instanceof Error) {
|
|
||||||
if ('type' in current && typeof (current as { type: unknown }).type === 'string') {
|
|
||||||
const code = ERROR_TYPE_TO_CODE[(current as { type: string }).type];
|
|
||||||
if (code) return code;
|
|
||||||
}
|
|
||||||
current = (current as { cause?: unknown }).cause;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Maps Temporal error type strings to actionable remediation hints. */
|
/** Maps Temporal error type strings to actionable remediation hints. */
|
||||||
const REMEDIATION_HINTS: Record<string, string> = {
|
const REMEDIATION_HINTS: Record<string, string> = {
|
||||||
AuthenticationError: 'Verify ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env is valid and not expired.',
|
AuthenticationError: 'Verify ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN in .env is valid and not expired.',
|
||||||
|
|||||||
@@ -23,14 +23,7 @@
|
|||||||
* - Graceful failure handling: pipelines continue if one fails
|
* - Graceful failure handling: pipelines continue if one fails
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { log, proxyActivities, setHandler, workflowInfo } from '@temporalio/workflow';
|
||||||
ApplicationFailure,
|
|
||||||
isCancellation,
|
|
||||||
log,
|
|
||||||
proxyActivities,
|
|
||||||
setHandler,
|
|
||||||
workflowInfo,
|
|
||||||
} from '@temporalio/workflow';
|
|
||||||
import type { AgentName, VulnType } from '../types/agents.js';
|
import type { AgentName, VulnType } from '../types/agents.js';
|
||||||
import { ALL_AGENTS } from '../types/agents.js';
|
import { ALL_AGENTS } from '../types/agents.js';
|
||||||
import type * as activities from './activities.js';
|
import type * as activities from './activities.js';
|
||||||
@@ -46,7 +39,7 @@ import {
|
|||||||
type VulnExploitPipelineResult,
|
type VulnExploitPipelineResult,
|
||||||
} from './shared.js';
|
} from './shared.js';
|
||||||
import { toWorkflowSummary } from './summary-mapper.js';
|
import { toWorkflowSummary } from './summary-mapper.js';
|
||||||
import { classifyErrorCode, formatWorkflowError } from './workflow-errors.js';
|
import { formatWorkflowError } from './workflow-errors.js';
|
||||||
|
|
||||||
// Retry configuration for production (long intervals for billing recovery)
|
// Retry configuration for production (long intervals for billing recovery)
|
||||||
const PRODUCTION_RETRY = {
|
const PRODUCTION_RETRY = {
|
||||||
@@ -134,28 +127,7 @@ function computeSummary(state: PipelineState): PipelineSummary {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function pentestPipelineWorkflow(input: PipelineInput): Promise<PipelineState> {
|
||||||
* Core pipeline orchestration. Coordinates the pentest pipeline stages.
|
|
||||||
*
|
|
||||||
* IMPORTANT: This function uses Temporal workflow APIs internally (proxyActivities,
|
|
||||||
* queries). It can ONLY be called from within a Temporal workflow execution.
|
|
||||||
* Do not call from standalone scripts or activity code.
|
|
||||||
*/
|
|
||||||
export async function pentestPipeline(input: PipelineInput): Promise<PipelineState> {
|
|
||||||
// Validate repoPath: reject traversal attempts and require absolute path
|
|
||||||
if (!input.repoPath || input.repoPath.includes('..')) {
|
|
||||||
throw ApplicationFailure.nonRetryable(
|
|
||||||
`Invalid repoPath: path traversal not allowed (received: ${input.repoPath ?? '<empty>'})`,
|
|
||||||
'ConfigurationError',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!input.repoPath.startsWith('/')) {
|
|
||||||
throw ApplicationFailure.nonRetryable(
|
|
||||||
`Invalid repoPath: absolute path required (received: ${input.repoPath})`,
|
|
||||||
'ConfigurationError',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { workflowId } = workflowInfo();
|
const { workflowId } = workflowInfo();
|
||||||
|
|
||||||
// Select activity proxy based on mode: testing (fast), subscription (extended), or default
|
// Select activity proxy based on mode: testing (fast), subscription (extended), or default
|
||||||
@@ -204,29 +176,20 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
...(input.pipelineTestingMode !== undefined && {
|
...(input.pipelineTestingMode !== undefined && {
|
||||||
pipelineTestingMode: input.pipelineTestingMode,
|
pipelineTestingMode: input.pipelineTestingMode,
|
||||||
}),
|
}),
|
||||||
// Config fields — flow through to getOrCreateContainer()
|
|
||||||
...(input.configYAML !== undefined && { configYAML: input.configYAML }),
|
|
||||||
...(input.apiKey !== undefined && { apiKey: input.apiKey }),
|
|
||||||
...(input.deliverablesSubdir !== undefined && { deliverablesSubdir: input.deliverablesSubdir }),
|
|
||||||
...(input.auditDir !== undefined && { auditDir: input.auditDir }),
|
|
||||||
...(input.promptDir !== undefined && { promptDir: input.promptDir }),
|
|
||||||
...(input.sastSarifPath !== undefined && { sastSarifPath: input.sastSarifPath }),
|
|
||||||
...(input.skipGitCheck !== undefined && { skipGitCheck: input.skipGitCheck }),
|
|
||||||
...(input.providerConfig !== undefined && { providerConfig: input.providerConfig }),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let resumeState: ResumeState | null = null;
|
let resumeState: ResumeState | null = null;
|
||||||
|
|
||||||
if (input.resumeFromWorkspace) {
|
if (input.resumeFromWorkspace) {
|
||||||
// 1. Load resume state (validates workspace, cross-checks deliverables)
|
// 1. Load resume state (validates workspace, cross-checks deliverables)
|
||||||
resumeState = await a.loadResumeState(input.resumeFromWorkspace, input.webUrl, input.repoPath, input.deliverablesSubdir);
|
resumeState = await a.loadResumeState(input.resumeFromWorkspace, input.webUrl, input.repoPath);
|
||||||
|
|
||||||
// 2. Restore git workspace and clean up incomplete deliverables
|
// 2. Restore git workspace and clean up incomplete deliverables
|
||||||
const incompleteAgents = ALL_AGENTS.filter(
|
const incompleteAgents = ALL_AGENTS.filter(
|
||||||
(agentName) => !resumeState?.completedAgents.includes(agentName),
|
(agentName) => !resumeState?.completedAgents.includes(agentName),
|
||||||
) as AgentName[];
|
) as AgentName[];
|
||||||
|
|
||||||
await a.restoreGitCheckpoint(input.repoPath, resumeState.checkpointHash, incompleteAgents, input.deliverablesSubdir);
|
await a.restoreGitCheckpoint(input.repoPath, resumeState.checkpointHash, incompleteAgents);
|
||||||
|
|
||||||
// 3. Short-circuit if all agents already completed
|
// 3. Short-circuit if all agents already completed
|
||||||
if (resumeState.completedAgents.length === ALL_AGENTS.length) {
|
if (resumeState.completedAgents.length === ALL_AGENTS.length) {
|
||||||
@@ -265,9 +228,6 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
await a.logPhaseTransition(activityInput, phaseName, 'start');
|
await a.logPhaseTransition(activityInput, phaseName, 'start');
|
||||||
state.agentMetrics[agentName] = await runAgent(activityInput);
|
state.agentMetrics[agentName] = await runAgent(activityInput);
|
||||||
state.completedAgents.push(agentName);
|
state.completedAgents.push(agentName);
|
||||||
if (input.checkpointsEnabled) {
|
|
||||||
await a.saveCheckpoint(activityInput, agentName, phaseName, state);
|
|
||||||
}
|
|
||||||
await a.logPhaseTransition(activityInput, phaseName, 'complete');
|
await a.logPhaseTransition(activityInput, phaseName, 'complete');
|
||||||
} else {
|
} else {
|
||||||
log.info(`Skipping ${agentName} (already complete)`);
|
log.info(`Skipping ${agentName} (already complete)`);
|
||||||
@@ -322,14 +282,30 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate errors from settled pipeline promises.
|
// Aggregate results from settled pipeline promises into workflow state
|
||||||
// Metrics and completedAgents are updated incrementally inside runVulnExploitPipeline
|
|
||||||
// so that getProgress queries reflect real-time status during execution.
|
|
||||||
function aggregatePipelineResults(results: PromiseSettledResult<VulnExploitPipelineResult>[]): void {
|
function aggregatePipelineResults(results: PromiseSettledResult<VulnExploitPipelineResult>[]): void {
|
||||||
const failedPipelines: string[] = [];
|
const failedPipelines: string[] = [];
|
||||||
|
|
||||||
for (const result of results) {
|
for (const result of results) {
|
||||||
if (result.status === 'rejected') {
|
if (result.status === 'fulfilled') {
|
||||||
|
const { vulnType, vulnMetrics, exploitMetrics } = result.value;
|
||||||
|
|
||||||
|
const vulnAgentName = `${vulnType}-vuln`;
|
||||||
|
if (vulnMetrics) {
|
||||||
|
state.agentMetrics[vulnAgentName] = vulnMetrics;
|
||||||
|
state.completedAgents.push(vulnAgentName);
|
||||||
|
} else if (shouldSkip(vulnAgentName)) {
|
||||||
|
state.completedAgents.push(vulnAgentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const exploitAgentName = `${vulnType}-exploit`;
|
||||||
|
if (exploitMetrics) {
|
||||||
|
state.agentMetrics[exploitAgentName] = exploitMetrics;
|
||||||
|
state.completedAgents.push(exploitAgentName);
|
||||||
|
} else if (shouldSkip(exploitAgentName)) {
|
||||||
|
state.completedAgents.push(exploitAgentName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const errorMsg = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
const errorMsg = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
||||||
failedPipelines.push(errorMsg);
|
failedPipelines.push(errorMsg);
|
||||||
}
|
}
|
||||||
@@ -386,9 +362,6 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
await preflightActs.runPreflightValidation(activityInput);
|
await preflightActs.runPreflightValidation(activityInput);
|
||||||
log.info('Preflight validation passed');
|
log.info('Preflight validation passed');
|
||||||
|
|
||||||
// === Initialize Deliverables Git ===
|
|
||||||
await a.initDeliverableGit(activityInput);
|
|
||||||
|
|
||||||
// === Phase 1: Pre-Reconnaissance ===
|
// === Phase 1: Pre-Reconnaissance ===
|
||||||
await runSequentialPhase('pre-recon', 'pre-recon', a.runPreReconAgent);
|
await runSequentialPhase('pre-recon', 'pre-recon', a.runPreReconAgent);
|
||||||
|
|
||||||
@@ -416,19 +389,10 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
let vulnMetrics: AgentMetrics | null = null;
|
let vulnMetrics: AgentMetrics | null = null;
|
||||||
if (!shouldSkip(vulnAgentName)) {
|
if (!shouldSkip(vulnAgentName)) {
|
||||||
vulnMetrics = await runVulnAgent();
|
vulnMetrics = await runVulnAgent();
|
||||||
state.agentMetrics[vulnAgentName] = vulnMetrics;
|
|
||||||
state.completedAgents.push(vulnAgentName);
|
|
||||||
if (input.checkpointsEnabled) {
|
|
||||||
await a.saveCheckpoint(activityInput, vulnAgentName, 'vulnerability-analysis', state);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log.info(`Skipping ${vulnAgentName} (already complete)`);
|
log.info(`Skipping ${vulnAgentName} (already complete)`);
|
||||||
state.completedAgents.push(vulnAgentName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1.5. Merge external findings from consumer provider into exploitation queue
|
|
||||||
await a.mergeFindingsIntoQueue(activityInput, vulnType);
|
|
||||||
|
|
||||||
// 2. Check exploitation queue for actionable findings
|
// 2. Check exploitation queue for actionable findings
|
||||||
const decision = await a.checkExploitationQueue(activityInput, vulnType);
|
const decision = await a.checkExploitationQueue(activityInput, vulnType);
|
||||||
|
|
||||||
@@ -437,14 +401,8 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
if (decision.shouldExploit) {
|
if (decision.shouldExploit) {
|
||||||
if (!shouldSkip(exploitAgentName)) {
|
if (!shouldSkip(exploitAgentName)) {
|
||||||
exploitMetrics = await runExploitAgent();
|
exploitMetrics = await runExploitAgent();
|
||||||
state.agentMetrics[exploitAgentName] = exploitMetrics;
|
|
||||||
state.completedAgents.push(exploitAgentName);
|
|
||||||
if (input.checkpointsEnabled) {
|
|
||||||
await a.saveCheckpoint(activityInput, exploitAgentName, 'exploitation', state);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log.info(`Skipping ${exploitAgentName} (already complete)`);
|
log.info(`Skipping ${exploitAgentName} (already complete)`);
|
||||||
state.completedAgents.push(exploitAgentName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,9 +451,6 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
// Then run the report agent to add executive summary and clean up
|
// Then run the report agent to add executive summary and clean up
|
||||||
state.agentMetrics.report = await a.runReportAgent(activityInput);
|
state.agentMetrics.report = await a.runReportAgent(activityInput);
|
||||||
state.completedAgents.push('report');
|
state.completedAgents.push('report');
|
||||||
if (input.checkpointsEnabled) {
|
|
||||||
await a.saveCheckpoint(activityInput, 'report', 'reporting', state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject model metadata into the final report
|
// Inject model metadata into the final report
|
||||||
await a.injectReportMetadataActivity(activityInput);
|
await a.injectReportMetadataActivity(activityInput);
|
||||||
@@ -506,13 +461,6 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
state.completedAgents.push('report');
|
state.completedAgents.push('report');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs after the skip gate so consumer providers still execute on resume.
|
|
||||||
await a.generateReportOutputActivity(activityInput);
|
|
||||||
|
|
||||||
if (input.checkpointsEnabled) {
|
|
||||||
await a.saveCheckpoint(activityInput, 'report-output', 'reporting', state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.status = 'completed';
|
state.status = 'completed';
|
||||||
state.currentPhase = null;
|
state.currentPhase = null;
|
||||||
state.currentAgent = null;
|
state.currentAgent = null;
|
||||||
@@ -523,22 +471,9 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
|
|
||||||
return state;
|
return state;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Cancellation: return structured state instead of throwing
|
|
||||||
if (isCancellation(error)) {
|
|
||||||
state.status = 'cancelled';
|
|
||||||
state.error = `Cancelled during phase: ${state.currentPhase ?? 'unknown'}`;
|
|
||||||
state.summary = computeSummary(state);
|
|
||||||
await a.logWorkflowComplete(activityInput, toWorkflowSummary(state, 'cancelled'));
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.status = 'failed';
|
state.status = 'failed';
|
||||||
state.failedAgent = state.currentAgent;
|
state.failedAgent = state.currentAgent;
|
||||||
state.error = formatWorkflowError(error, state.currentPhase, state.currentAgent);
|
state.error = formatWorkflowError(error, state.currentPhase, state.currentAgent);
|
||||||
const errorCode = classifyErrorCode(error);
|
|
||||||
if (errorCode) {
|
|
||||||
state.errorCode = errorCode;
|
|
||||||
}
|
|
||||||
state.summary = computeSummary(state);
|
state.summary = computeSummary(state);
|
||||||
|
|
||||||
// Log workflow failure summary
|
// Log workflow failure summary
|
||||||
@@ -547,8 +482,3 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** OSS workflow entry point — thin shell around the extracted pipeline function. */
|
|
||||||
export async function pentestPipelineWorkflow(input: PipelineInput): Promise<PipelineState> {
|
|
||||||
return pentestPipeline(input);
|
|
||||||
}
|
|
||||||
@@ -62,43 +62,3 @@ export interface DistributedConfig {
|
|||||||
authentication: Authentication | null;
|
authentication: Authentication | null;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* LLM provider configuration for multi-provider support.
|
|
||||||
*
|
|
||||||
* Maps to SDK environment variables at execution time. When providerType
|
|
||||||
* is omitted or 'anthropic_api', falls back to apiKey + ANTHROPIC_API_KEY.
|
|
||||||
*/
|
|
||||||
export interface ProviderConfig {
|
|
||||||
readonly providerType?: string;
|
|
||||||
readonly apiKey?: string;
|
|
||||||
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>;
|
|
||||||
readonly supportsStructuredOutput?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runtime configuration for the DI container.
|
|
||||||
*
|
|
||||||
* Abstracts path conventions and credential threading so consumers
|
|
||||||
* can override OSS defaults without modifying source files.
|
|
||||||
*/
|
|
||||||
export interface ContainerConfig {
|
|
||||||
/** Subdirectory for deliverables relative to repoPath. Default: '.shannon/deliverables' */
|
|
||||||
readonly deliverablesSubdir: string;
|
|
||||||
/** Directory for audit logs. Default: './workspaces' */
|
|
||||||
readonly auditDir: string;
|
|
||||||
/** API key override — when set, executor reads from config instead of process.env */
|
|
||||||
readonly apiKey?: string;
|
|
||||||
/** Prompt directory override — when set, prompt manager loads from this path */
|
|
||||||
readonly promptDir?: string;
|
|
||||||
/** LLM provider configuration — when set, executor maps to SDK env vars directly */
|
|
||||||
readonly providerConfig?: ProviderConfig;
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
/**
|
/**
|
||||||
* Deliverable Type Definitions
|
* Deliverable Type Definitions
|
||||||
*
|
*
|
||||||
* Maps deliverable types to their filenames for the save-deliverable CLI.
|
* Maps deliverable types to their filenames and defines validation requirements.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export enum DeliverableType {
|
export enum DeliverableType {
|
||||||
@@ -19,10 +19,19 @@ export enum DeliverableType {
|
|||||||
|
|
||||||
// Vulnerability analysis agents
|
// Vulnerability analysis agents
|
||||||
INJECTION_ANALYSIS = 'INJECTION_ANALYSIS',
|
INJECTION_ANALYSIS = 'INJECTION_ANALYSIS',
|
||||||
|
INJECTION_QUEUE = 'INJECTION_QUEUE',
|
||||||
|
|
||||||
XSS_ANALYSIS = 'XSS_ANALYSIS',
|
XSS_ANALYSIS = 'XSS_ANALYSIS',
|
||||||
|
XSS_QUEUE = 'XSS_QUEUE',
|
||||||
|
|
||||||
AUTH_ANALYSIS = 'AUTH_ANALYSIS',
|
AUTH_ANALYSIS = 'AUTH_ANALYSIS',
|
||||||
|
AUTH_QUEUE = 'AUTH_QUEUE',
|
||||||
|
|
||||||
AUTHZ_ANALYSIS = 'AUTHZ_ANALYSIS',
|
AUTHZ_ANALYSIS = 'AUTHZ_ANALYSIS',
|
||||||
|
AUTHZ_QUEUE = 'AUTHZ_QUEUE',
|
||||||
|
|
||||||
SSRF_ANALYSIS = 'SSRF_ANALYSIS',
|
SSRF_ANALYSIS = 'SSRF_ANALYSIS',
|
||||||
|
SSRF_QUEUE = 'SSRF_QUEUE',
|
||||||
|
|
||||||
// Exploitation agents
|
// Exploitation agents
|
||||||
INJECTION_EVIDENCE = 'INJECTION_EVIDENCE',
|
INJECTION_EVIDENCE = 'INJECTION_EVIDENCE',
|
||||||
@@ -36,16 +45,50 @@ export enum DeliverableType {
|
|||||||
* Hard-coded filename mappings from agent prompts
|
* Hard-coded filename mappings from agent prompts
|
||||||
*/
|
*/
|
||||||
export const DELIVERABLE_FILENAMES: Record<DeliverableType, string> = {
|
export const DELIVERABLE_FILENAMES: Record<DeliverableType, string> = {
|
||||||
[DeliverableType.CODE_ANALYSIS]: 'pre_recon_deliverable.md',
|
[DeliverableType.CODE_ANALYSIS]: 'code_analysis_deliverable.md',
|
||||||
[DeliverableType.RECON]: 'recon_deliverable.md',
|
[DeliverableType.RECON]: 'recon_deliverable.md',
|
||||||
[DeliverableType.INJECTION_ANALYSIS]: 'injection_analysis_deliverable.md',
|
[DeliverableType.INJECTION_ANALYSIS]: 'injection_analysis_deliverable.md',
|
||||||
|
[DeliverableType.INJECTION_QUEUE]: 'injection_exploitation_queue.json',
|
||||||
[DeliverableType.XSS_ANALYSIS]: 'xss_analysis_deliverable.md',
|
[DeliverableType.XSS_ANALYSIS]: 'xss_analysis_deliverable.md',
|
||||||
|
[DeliverableType.XSS_QUEUE]: 'xss_exploitation_queue.json',
|
||||||
[DeliverableType.AUTH_ANALYSIS]: 'auth_analysis_deliverable.md',
|
[DeliverableType.AUTH_ANALYSIS]: 'auth_analysis_deliverable.md',
|
||||||
|
[DeliverableType.AUTH_QUEUE]: 'auth_exploitation_queue.json',
|
||||||
[DeliverableType.AUTHZ_ANALYSIS]: 'authz_analysis_deliverable.md',
|
[DeliverableType.AUTHZ_ANALYSIS]: 'authz_analysis_deliverable.md',
|
||||||
|
[DeliverableType.AUTHZ_QUEUE]: 'authz_exploitation_queue.json',
|
||||||
[DeliverableType.SSRF_ANALYSIS]: 'ssrf_analysis_deliverable.md',
|
[DeliverableType.SSRF_ANALYSIS]: 'ssrf_analysis_deliverable.md',
|
||||||
|
[DeliverableType.SSRF_QUEUE]: 'ssrf_exploitation_queue.json',
|
||||||
[DeliverableType.INJECTION_EVIDENCE]: 'injection_exploitation_evidence.md',
|
[DeliverableType.INJECTION_EVIDENCE]: 'injection_exploitation_evidence.md',
|
||||||
[DeliverableType.XSS_EVIDENCE]: 'xss_exploitation_evidence.md',
|
[DeliverableType.XSS_EVIDENCE]: 'xss_exploitation_evidence.md',
|
||||||
[DeliverableType.AUTH_EVIDENCE]: 'auth_exploitation_evidence.md',
|
[DeliverableType.AUTH_EVIDENCE]: 'auth_exploitation_evidence.md',
|
||||||
[DeliverableType.AUTHZ_EVIDENCE]: 'authz_exploitation_evidence.md',
|
[DeliverableType.AUTHZ_EVIDENCE]: 'authz_exploitation_evidence.md',
|
||||||
[DeliverableType.SSRF_EVIDENCE]: 'ssrf_exploitation_evidence.md',
|
[DeliverableType.SSRF_EVIDENCE]: 'ssrf_exploitation_evidence.md',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue types that require JSON validation
|
||||||
|
*/
|
||||||
|
export const QUEUE_TYPES: DeliverableType[] = [
|
||||||
|
DeliverableType.INJECTION_QUEUE,
|
||||||
|
DeliverableType.XSS_QUEUE,
|
||||||
|
DeliverableType.AUTH_QUEUE,
|
||||||
|
DeliverableType.AUTHZ_QUEUE,
|
||||||
|
DeliverableType.SSRF_QUEUE,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard to check if a deliverable type is a queue
|
||||||
|
*/
|
||||||
|
export function isQueueType(type: string): boolean {
|
||||||
|
return QUEUE_TYPES.includes(type as DeliverableType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vulnerability queue structure
|
||||||
|
*/
|
||||||
|
export interface VulnerabilityQueue {
|
||||||
|
vulnerabilities: VulnerabilityItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VulnerabilityItem {
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ export const BILLING_TEXT_PATTERNS = [
|
|||||||
'cap reached',
|
'cap reached',
|
||||||
'budget exceeded',
|
'budget exceeded',
|
||||||
'usage limit',
|
'usage limit',
|
||||||
|
'resets',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 51 KiB |
@@ -19,5 +19,34 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
||||||
|
# Optional: claude-code-router for multi-model support
|
||||||
|
# Start with: ROUTER=true ./shannon start ...
|
||||||
|
router:
|
||||||
|
image: node:20-slim
|
||||||
|
container_name: shannon-router
|
||||||
|
profiles: ["router"] # Only starts when explicitly requested
|
||||||
|
command: >
|
||||||
|
sh -c "apt-get update && apt-get install -y gettext-base &&
|
||||||
|
npm install -g @musistudio/claude-code-router &&
|
||||||
|
mkdir -p /root/.claude-code-router &&
|
||||||
|
envsubst < /config/router-config.json > /root/.claude-code-router/config.json &&
|
||||||
|
ccr start"
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3456:3456"
|
||||||
|
volumes:
|
||||||
|
- ./apps/cli/infra/router-config.json:/config/router-config.json:ro
|
||||||
|
environment:
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
|
||||||
|
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||||
|
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
|
||||||
|
- ROUTER_DEFAULT=${ROUTER_DEFAULT:-openai,gpt-4o}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3456/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
temporal-data:
|
temporal-data:
|
||||||
+5
-5
@@ -6,13 +6,13 @@ TARGET_GID="${SHANNON_HOST_GID:-}"
|
|||||||
CURRENT_UID=$(id -u pentest 2>/dev/null || echo "")
|
CURRENT_UID=$(id -u pentest 2>/dev/null || echo "")
|
||||||
|
|
||||||
if [ -n "$TARGET_UID" ] && [ "$TARGET_UID" != "$CURRENT_UID" ]; then
|
if [ -n "$TARGET_UID" ] && [ "$TARGET_UID" != "$CURRENT_UID" ]; then
|
||||||
userdel pentest 2>/dev/null || true
|
deluser pentest 2>/dev/null || true
|
||||||
groupdel pentest 2>/dev/null || true
|
delgroup pentest 2>/dev/null || true
|
||||||
|
|
||||||
groupadd -g "$TARGET_GID" pentest
|
addgroup -g "$TARGET_GID" pentest
|
||||||
useradd -u "$TARGET_UID" -g pentest -s /bin/bash -M pentest
|
adduser -u "$TARGET_UID" -G pentest -s /bin/bash -D pentest
|
||||||
|
|
||||||
chown -R pentest:pentest /app/sessions /app/workspaces /tmp/.claude
|
chown -R pentest:pentest /app/sessions /app/deliverables /app/workspaces /tmp/.claude
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec su -m pentest -c "exec $*"
|
exec su -m pentest -c "exec $*"
|
||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"packageManager": "pnpm@10.33.0",
|
"packageManager": "pnpm@10.12.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"check": "turbo run check",
|
"check": "turbo run check",
|
||||||
|
|||||||
Generated
-3
@@ -75,9 +75,6 @@ importers:
|
|||||||
js-yaml:
|
js-yaml:
|
||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.1.1
|
version: 4.1.1
|
||||||
zod:
|
|
||||||
specifier: ^4.3.6
|
|
||||||
version: 4.3.6
|
|
||||||
zx:
|
zx:
|
||||||
specifier: ^8.0.0
|
specifier: ^8.0.0
|
||||||
version: 8.8.5
|
version: 8.8.5
|
||||||
|
|||||||
Reference in new issue
Block a user