Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 47a6e4933a | |||
| 70af2b12db | |||
| 800afbfefb | |||
| 085624b287 | |||
| ccb3dc6f75 |
@@ -135,7 +135,6 @@ shannon <URL> <REPO> --pipeline-testing
|
||||
|-------------------|---------|------------|
|
||||
| `config` | Configuration file issues | No |
|
||||
| `network` | Connection/timeout issues | Yes |
|
||||
| `tool` | External tool (nmap, etc.) failed | Yes |
|
||||
| `prompt` | Claude SDK/API issues | Sometimes |
|
||||
| `filesystem` | File read/write errors | Sometimes |
|
||||
| `validation` | Deliverable validation failed | Yes (via retry) |
|
||||
|
||||
+6
-3
@@ -4,6 +4,9 @@
|
||||
# Recommended output token configuration for larger tool outputs
|
||||
CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000
|
||||
|
||||
# Adaptive thinking is enabled automatically on Opus 4.6/4.7. Set to false to disable.
|
||||
# CLAUDE_ADAPTIVE_THINKING=false
|
||||
|
||||
# =============================================================================
|
||||
# OPTION 1: Direct Anthropic
|
||||
# =============================================================================
|
||||
@@ -26,7 +29,7 @@ ANTHROPIC_API_KEY=your-api-key-here
|
||||
# Optional for direct Anthropic and custom base URL modes. Required for Bedrock/Vertex.
|
||||
# ANTHROPIC_SMALL_MODEL=... # Small tier (default: claude-haiku-4-5-20251001)
|
||||
# ANTHROPIC_MEDIUM_MODEL=... # Medium tier (default: claude-sonnet-4-6)
|
||||
# ANTHROPIC_LARGE_MODEL=... # Large tier (default: claude-opus-4-6)
|
||||
# ANTHROPIC_LARGE_MODEL=... # Large tier (default: claude-opus-4-7)
|
||||
|
||||
# =============================================================================
|
||||
# OPTION 3: AWS Bedrock
|
||||
@@ -36,7 +39,7 @@ ANTHROPIC_API_KEY=your-api-key-here
|
||||
# Example Bedrock model IDs for us-east-1:
|
||||
# ANTHROPIC_SMALL_MODEL=us.anthropic.claude-haiku-4-5-20251001-v1:0
|
||||
# ANTHROPIC_MEDIUM_MODEL=us.anthropic.claude-sonnet-4-6
|
||||
# ANTHROPIC_LARGE_MODEL=us.anthropic.claude-opus-4-6
|
||||
# ANTHROPIC_LARGE_MODEL=us.anthropic.claude-opus-4-7
|
||||
|
||||
# CLAUDE_CODE_USE_BEDROCK=1
|
||||
# AWS_REGION=us-east-1
|
||||
@@ -52,7 +55,7 @@ ANTHROPIC_API_KEY=your-api-key-here
|
||||
# Example Vertex AI model IDs:
|
||||
# ANTHROPIC_SMALL_MODEL=claude-haiku-4-5@20251001
|
||||
# ANTHROPIC_MEDIUM_MODEL=claude-sonnet-4-6
|
||||
# ANTHROPIC_LARGE_MODEL=claude-opus-4-6
|
||||
# ANTHROPIC_LARGE_MODEL=claude-opus-4-7
|
||||
|
||||
# CLAUDE_CODE_USE_VERTEX=1
|
||||
# CLOUD_ML_REGION=us-east5
|
||||
|
||||
@@ -57,7 +57,7 @@ Durable workflow orchestration with crash recovery, queryable progress, intellig
|
||||
|
||||
### Five-Phase Pipeline
|
||||
|
||||
1. **Pre-Recon** (`pre-recon`) — External scans (nmap, subfinder, whatweb) + source code analysis
|
||||
1. **Pre-Recon** (`pre-recon`) — Source code analysis to build the architectural baseline
|
||||
2. **Recon** (`recon`) — Attack surface mapping from initial findings
|
||||
3. **Vulnerability Analysis** (5 parallel agents) — injection, xss, auth, authz, ssrf
|
||||
4. **Exploitation** (5 parallel agents, conditional) — Exploits confirmed vulnerabilities
|
||||
|
||||
+2
-49
@@ -13,44 +13,12 @@ RUN apk update && apk add --no-cache \
|
||||
curl \
|
||||
wget \
|
||||
ca-certificates \
|
||||
# Network libraries for Go tools
|
||||
libpcap-dev \
|
||||
linux-headers \
|
||||
# Language runtimes
|
||||
go \
|
||||
nodejs-22 \
|
||||
npm \
|
||||
python3 \
|
||||
py3-pip \
|
||||
ruby \
|
||||
ruby-dev \
|
||||
# Security tools available in Wolfi
|
||||
nmap \
|
||||
# Additional utilities
|
||||
bash
|
||||
|
||||
# Set environment variables for Go
|
||||
ENV GOPATH=/go
|
||||
ENV PATH=$GOPATH/bin:/usr/local/go/bin:$PATH
|
||||
ENV CGO_ENABLED=1
|
||||
|
||||
# Create directories
|
||||
RUN mkdir -p $GOPATH/bin
|
||||
|
||||
# Install Go-based security tools
|
||||
RUN go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@v2.13.0
|
||||
# Install WhatWeb from release tarball (Ruby-based tool)
|
||||
RUN curl -sL https://github.com/urbanadventurer/WhatWeb/archive/refs/tags/v0.6.3.tar.gz | tar xz -C /opt && \
|
||||
mv /opt/WhatWeb-0.6.3 /opt/whatweb && \
|
||||
chmod +x /opt/whatweb/whatweb && \
|
||||
gem install addressable -v 2.8.9 && \
|
||||
echo '#!/bin/bash' > /usr/local/bin/whatweb && \
|
||||
echo 'cd /opt/whatweb && exec ./whatweb "$@"' >> /usr/local/bin/whatweb && \
|
||||
chmod +x /usr/local/bin/whatweb
|
||||
|
||||
# Install Python-based tools
|
||||
RUN pip3 install --no-cache-dir schemathesis==4.13.0
|
||||
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm@10.33.0
|
||||
|
||||
@@ -84,12 +52,11 @@ RUN apk add --no-cache \
|
||||
curl \
|
||||
ca-certificates \
|
||||
shadow \
|
||||
libpcap \
|
||||
nmap \
|
||||
# Language runtimes (minimal)
|
||||
nodejs-22 \
|
||||
npm \
|
||||
python3 \
|
||||
ruby \
|
||||
# Chromium browser and dependencies for Playwright
|
||||
chromium \
|
||||
nss \
|
||||
freetype \
|
||||
@@ -104,20 +71,6 @@ RUN apk add --no-cache \
|
||||
fontconfig \
|
||||
|| true
|
||||
|
||||
# Copy Go binaries from builder
|
||||
COPY --from=builder /go/bin/subfinder /usr/local/bin/
|
||||
|
||||
# Copy WhatWeb from builder
|
||||
COPY --from=builder /opt/whatweb /opt/whatweb
|
||||
COPY --from=builder /usr/local/bin/whatweb /usr/local/bin/whatweb
|
||||
|
||||
# Install WhatWeb Ruby dependencies in runtime stage
|
||||
RUN gem install addressable -v 2.8.9
|
||||
|
||||
# Copy Python packages from builder
|
||||
COPY --from=builder /usr/lib/python3.*/site-packages /usr/lib/python3.12/site-packages
|
||||
COPY --from=builder /usr/bin/schemathesis /usr/bin/
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 pentest && \
|
||||
adduser -u 1001 -G pentest -s /bin/bash -D pentest
|
||||
|
||||
+1
-2
@@ -147,7 +147,7 @@ This phase informs everything downstream. If the codebase uses an ORM with param
|
||||
|
||||
## Phase 2: Reconnaissance
|
||||
|
||||
Bridges static and dynamic analysis using browser automation. The recon agent correlates code findings with the live application, validating that endpoints actually exist, mapping authentication flows, inventorying input vectors (URL parameters, POST fields, headers, cookies), and documenting the real authorization architecture. This phase may also integrate with infrastructure discovery tools including Nmap, Subfinder, and WhatWeb for network perimeter mapping.
|
||||
Bridges static and dynamic analysis using browser automation. The recon agent correlates code findings with the live application, validating that endpoints actually exist, mapping authentication flows, inventorying input vectors (URL parameters, POST fields, headers, cookies), and documenting the real authorization architecture.
|
||||
|
||||
## Phase 3: Vulnerability Analysis
|
||||
|
||||
@@ -194,7 +194,6 @@ This correlation means that a data flow vulnerability identified in static analy
|
||||
- **Fully Autonomous Operation:** Shannon Pro handles complex workflows including 2FA/TOTP logins and SSO (e.g., Sign in with Google) without human intervention. TOTP is handled via a dedicated MCP server tool.
|
||||
- **White-Box Awareness:** Unlike black-box scanners, Shannon Pro reads the source code to intelligently guide its attack strategy, combining code-level insight with runtime validation.
|
||||
- **Parallel Processing:** Vulnerability analysis and exploitation phases run concurrently across attack domains, with pipelined parallelism minimizing total execution time.
|
||||
- **Tool Orchestration:** Shannon Pro orchestrates existing security tools (e.g., Schemathesis for API testing, Nmap for network discovery) while adding LLM reasoning to interpret results.
|
||||
- **Configurable Login Flows:** Authentication configuration specifies login procedures and credentials, which are interpolated into agent prompts for authenticated testing.
|
||||
|
||||
---
|
||||
|
||||
@@ -65,7 +65,7 @@ export async function start(args: StartArgs): Promise<void> {
|
||||
const workspacePath = path.join(workspacesDir, workspace);
|
||||
fs.mkdirSync(workspacePath, { recursive: true });
|
||||
fs.chmodSync(workspacePath, 0o777);
|
||||
for (const dir of ['deliverables', 'scratchpad', '.playwright-cli']) {
|
||||
for (const dir of ['deliverables', 'scratchpad', '.playwright-cli', '.playwright']) {
|
||||
const dirPath = path.join(workspacePath, dir);
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
fs.chmodSync(dirPath, 0o777);
|
||||
@@ -76,6 +76,7 @@ export async function start(args: StartArgs): Promise<void> {
|
||||
for (const dir of ['deliverables', 'scratchpad', '.playwright-cli']) {
|
||||
fs.mkdirSync(path.join(shannonDir, dir), { recursive: true });
|
||||
}
|
||||
fs.mkdirSync(path.join(repo.hostPath, '.playwright'), { recursive: true });
|
||||
|
||||
const credentialsPath = getCredentialsPath();
|
||||
const hasCredentials = fs.existsSync(credentialsPath);
|
||||
|
||||
@@ -186,11 +186,12 @@ export function spawnWorker(opts: WorkerOptions): ChildProcess {
|
||||
args.push('-v', `${opts.workspacesDir}:/app/workspaces`);
|
||||
args.push('-v', `${opts.repo.hostPath}:${opts.repo.containerPath}:ro`);
|
||||
|
||||
// Writable overlays: shadow .shannon/ inside the :ro repo with workspace-backed dirs
|
||||
// Writable overlays: shadow .shannon/ and .playwright/ 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`);
|
||||
args.push('-v', `${path.join(workspacePath, '.playwright')}:${opts.repo.containerPath}/.playwright`);
|
||||
|
||||
// Local mode: mount prompts for live editing
|
||||
if (opts.promptsDir) {
|
||||
|
||||
@@ -26,6 +26,25 @@ import { displaySplash } from './splash.js';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
function blockSudo(): void {
|
||||
const isSudo = !!process.env.SUDO_USER;
|
||||
const isRoot = process.geteuid?.() === 0;
|
||||
if (!isSudo && !isRoot) return;
|
||||
|
||||
if (isSudo) {
|
||||
console.error('ERROR: Shannon must not be run with sudo.');
|
||||
console.error('Re-run this command as your normal user.');
|
||||
} else {
|
||||
console.error('ERROR: Shannon must not be run as the root user.');
|
||||
console.error('Switch to a regular user account and re-run this command.');
|
||||
}
|
||||
if (process.platform === 'linux') {
|
||||
console.error('Configure Docker to run without sudo first:');
|
||||
console.error('https://docs.docker.com/engine/install/linux-postinstall');
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function getVersion(): string {
|
||||
try {
|
||||
const pkgPath = path.join(__dirname, '..', 'package.json');
|
||||
@@ -179,6 +198,8 @@ function parseStartArgs(argv: string[]): ParsedStartArgs {
|
||||
|
||||
// === Main Dispatch ===
|
||||
|
||||
blockSudo();
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// Parse --backend flag before command dispatch
|
||||
|
||||
@@ -39,9 +39,33 @@
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z2-7]+=*$",
|
||||
"description": "TOTP secret for two-factor authentication (Base32 encoded, case insensitive)"
|
||||
},
|
||||
"email_login": {
|
||||
"type": "object",
|
||||
"description": "Email-based login credentials for magic-link and email-OTP flows",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"description": "Email address for authentication"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 255,
|
||||
"description": "Password for the email account"
|
||||
},
|
||||
"totp_secret": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z2-7]+=*$",
|
||||
"description": "TOTP secret for email 2FA (Base32 encoded)"
|
||||
}
|
||||
},
|
||||
"required": ["address", "password"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["username", "password"],
|
||||
"required": ["username"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"login_flow": {
|
||||
|
||||
@@ -47,6 +47,25 @@ rules:
|
||||
type: path
|
||||
url_path: "/api/v2/user-profile"
|
||||
|
||||
# Email-based login (for magic-link / email-OTP flows)
|
||||
# authentication:
|
||||
# login_type: form
|
||||
# login_url: "https://example.com/login"
|
||||
# credentials:
|
||||
# username: "testuser"
|
||||
# email_login:
|
||||
# address: "testuser@example.com"
|
||||
# password: "email-password"
|
||||
# totp_secret: "JBSWY3DPEHPK3PXP" # Optional TOTP for email 2FA
|
||||
# login_flow:
|
||||
# - "Type $username into the username field"
|
||||
# - "Type $email_address into the email field"
|
||||
# - "Type $email_password into the email password field"
|
||||
# - "Enter $email_totp in the verification code field"
|
||||
# success_condition:
|
||||
# type: url_contains
|
||||
# value: "/dashboard"
|
||||
|
||||
# Pipeline execution settings (optional)
|
||||
# pipeline:
|
||||
# retry_preset: subscription # 'default' or 'subscription' (6h max retry for rate limit recovery)
|
||||
|
||||
@@ -180,17 +180,16 @@ For each root vulnerability in your plan, you will follow this systematic, four-
|
||||
## **Strategic Tool Usage**
|
||||
Use the right tool for the job to ensure thoroughness.
|
||||
- **Use `curl` (Manual Probing) for:** Initial confirmation, simple UNION/Error-based injections, and crafting specific WAF bypasses.
|
||||
- **Use `sqlmap` (Automation) for:** Time-consuming blind injections, automating enumeration **after** manual confirmation, and as a final step to try a wide range of payloads when manual techniques are failing.
|
||||
|
||||
## **Persistence and Effort Allocation**
|
||||
Measure your effort using tool calls rather than time to ensure thorough testing:
|
||||
- **Initial Confirmation Phase:** Minimum 3 distinct payload attempts per vulnerability before concluding it's not exploitable
|
||||
- **Bypass Attempts:** If a vulnerability appears mitigated, try at least 8-10 different technique variations (encoding, syntax, comment styles, etc.) before concluding it's properly defended
|
||||
- **Escalation Trigger:** If manual testing exceeds 10-12 tool calls without progress on a single vulnerability, escalate to automated tools (`sqlmap`) or Task Agent scripting
|
||||
- **Escalation Trigger:** If manual testing exceeds 10-12 tool calls without progress on a single vulnerability, escalate to Task Agent scripting
|
||||
- **Termination Criteria:** After systematic attempts with multiple different techniques → classify as appropriate level
|
||||
|
||||
## **Using the Task Agent for Custom Scripting**
|
||||
You must delegate every injection automation task to the Task Agent. Use manual `curl` or `sqlmap` runs for spot checks, then escalate to scripted payload loops handled by the Task Agent.
|
||||
You must delegate every injection automation task to the Task Agent. Use manual `curl` runs for spot checks, then escalate to scripted payload loops handled by the Task Agent.
|
||||
|
||||
**TEMPLATE FOR SCRIPTING TASKS (REQUIRED):**
|
||||
"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Return the structured verdict `{ "login_success": true }` and stop.
|
||||
@@ -36,7 +36,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
|
||||
|
||||
**Your Input:** External scan results from pre-recon tools (nmap, subfinder, whatweb)
|
||||
**Your Input:** Target application source code in the working directory
|
||||
**Your Output:** `.shannon/deliverables/pre_recon_deliverable.md` (feeds all subsequent analysis phases)
|
||||
**Shared Intelligence:** You create the foundational intelligence baseline that all other agents depend on
|
||||
|
||||
@@ -71,7 +71,6 @@ You are the **Code Intelligence Gatherer** and **Architectural Foundation Builde
|
||||
|
||||
<starting_context>
|
||||
- You are the **ENTRY POINT** of the comprehensive security assessment - no prior deliverables exist to read
|
||||
- External reconnaissance tools have completed and their results are available in the working environment
|
||||
- The target application source code has been cloned and is ready for analysis in the current directory
|
||||
- You must create the **foundational intelligence baseline** that all subsequent agents depend on
|
||||
- **CRITICAL:** This is the ONLY agent with full source code access - your completeness determines whether vulnerabilities are found
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<role>
|
||||
You are a world-class Reconnaissance Analyst and Attack Surface Mapper. Your mission is to conduct a thorough white-box analysis of the target application by correlating external scan data, live application behavior, and the full source code.
|
||||
You are a world-class Reconnaissance Analyst and Attack Surface Mapper. Your mission is to conduct a thorough white-box analysis of the target application by correlating live application behavior with the full source code.
|
||||
</role>
|
||||
|
||||
<objective>
|
||||
@@ -7,7 +7,7 @@ Your goal is to create a comprehensive, structured map of the application's atta
|
||||
</objective>
|
||||
|
||||
<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 `.shannon/deliverables/pre_recon_deliverable.md`. This file contains the initial source code analysis. This is your only starting information.
|
||||
</starting_context>
|
||||
|
||||
<target>
|
||||
@@ -80,13 +80,13 @@ 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)
|
||||
|
||||
**Your Input:** `.shannon/deliverables/pre_recon_deliverable.md` (external scan data, initial code analysis)
|
||||
**Your Input:** `.shannon/deliverables/pre_recon_deliverable.md` (initial code analysis)
|
||||
**Your Output:** `.shannon/deliverables/recon_deliverable.md` (comprehensive attack surface map)
|
||||
**Shared Intelligence:** None (you are the first analysis specialist)
|
||||
|
||||
**WHAT HAPPENED BEFORE YOU:**
|
||||
- Pre-reconnaissance agent performed external scans (nmap, subfinder, whatweb) and initial code analysis
|
||||
- All attack surfaces, technologies, and entry points were catalogued from external perspective
|
||||
- Pre-reconnaissance agent performed initial source code analysis
|
||||
- Attack surfaces, technologies, and entry points were catalogued from the codebase
|
||||
|
||||
**WHAT HAPPENS AFTER YOU:**
|
||||
- Injection Analysis specialist will analyze SQL injection and command injection vulnerabilities using your attack surface map
|
||||
@@ -112,7 +112,7 @@ You must follow this methodical four-step process:
|
||||
|
||||
1. **Synthesize Initial Data:**
|
||||
- Read the entire `.shannon/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 and key code modules.
|
||||
|
||||
2. **Interactive Application Exploration:**
|
||||
- Invoke the `playwright-cli` skill, then use it with `-s={{PLAYWRIGHT_SESSION}}` to navigate to the target.
|
||||
@@ -166,8 +166,6 @@ A brief overview of the application's purpose, core technology stack (e.g., Next
|
||||
- **Frontend:** [Framework, key libraries, authentication libraries]
|
||||
- **Backend:** [Language, framework, key dependencies]
|
||||
- **Infrastructure:** [Hosting provider, CDN, database type]
|
||||
- **Identified Subdomains:** [List from subfinder and any others discovered]
|
||||
- **Open Ports & Services:** [List from nmap and their purpose]
|
||||
|
||||
## 3. Authentication & Session Management Flow
|
||||
- **Entry Points:** [e.g., /login, /register, /auth/sso]
|
||||
|
||||
@@ -12,7 +12,7 @@ Technical leadership (CTOs, CISOs, Engineering VPs) who need both technical accu
|
||||
The orchestrator has already concatenated all specialist reports into `comprehensive_security_assessment_report.md`.
|
||||
Your task is to:
|
||||
1. Read this existing concatenated report
|
||||
2. Add Executive Summary (vulnerability overview) and Network Reconnaissance (security-relevant scan findings) sections at the top
|
||||
2. Add an Executive Summary (vulnerability overview) at the top
|
||||
3. Clean up ALL exploitation evidence sections by removing hallucinated content
|
||||
4. Save the modified version back to the same file
|
||||
|
||||
@@ -72,19 +72,10 @@ MODIFY the existing file `.shannon/deliverables/comprehensive_security_assessmen
|
||||
**Server-Side Request Forgery (SSRF) Vulnerabilities:**
|
||||
{Check for "SSRF Exploitation Evidence" section. Include actually exploited vulnerabilities and those blocked by security controls. Exclude theoretical vulnerabilities requiring internal network access. If vulnerabilities exist, summarize their impact and severity. If section is missing or empty, state: "No SSRF vulnerabilities were found."}
|
||||
|
||||
## Network Reconnaissance
|
||||
{Extract security-relevant findings from automated scanning tools:
|
||||
- Open ports and exposed services from nmap
|
||||
- Subdomain discoveries from subfinder that could expand attack surface
|
||||
- Security headers or misconfigurations detected by whatweb
|
||||
- Any other security-relevant findings from the automated tools
|
||||
SKIP stack details - technical leaders know their infrastructure}
|
||||
|
||||
2. KEEPING the existing exploitation evidence sections but CLEANING them according to the rules below
|
||||
|
||||
3. The final structure should be:
|
||||
- Executive Summary (new)
|
||||
- Network Reconnaissance (new)
|
||||
- All existing exploitation evidence sections (cleaned)
|
||||
|
||||
IMPORTANT: Do NOT reorder the existing exploitation evidence sections. Maintain the exact order they appear in the concatenated report. Only remove sections that do not match the defined criteria above.
|
||||
@@ -93,15 +84,12 @@ IMPORTANT: Do NOT reorder the existing exploitation evidence sections. Maintain
|
||||
|
||||
<instructions>
|
||||
1. Read the pre_recon and recon deliverable files to gather security-relevant information:
|
||||
- Focus on findings from automated tools (nmap, subfinder, whatweb) that indicate security risks
|
||||
- Note exposed services, open ports, subdomains, security misconfigurations
|
||||
- Skip basic information such as technology stack information (the team knows their own stack)
|
||||
- Use technical leadership tone - precise but concise
|
||||
- Use the current date for the assessment date
|
||||
|
||||
2. Create the Executive Summary and Network Reconnaissance content:
|
||||
2. Create the Executive Summary content:
|
||||
- Executive Summary: Technical overview with actionable findings for engineering leaders
|
||||
- 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:
|
||||
- KEEP these specific section headings:
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<role>
|
||||
You are a credential validation agent. Your sole job is to verify that the provided login credentials work against the target application.
|
||||
</role>
|
||||
|
||||
<objective>
|
||||
Attempt to log in to the target application using the provided credentials. Report whether the login succeeded or failed.
|
||||
</objective>
|
||||
|
||||
<target_authentication>
|
||||
{{AUTH_CONTEXT}}
|
||||
</target_authentication>
|
||||
|
||||
<cli_tools>
|
||||
Use playwright-cli with session flag: `-s={{PLAYWRIGHT_SESSION}}`
|
||||
</cli_tools>
|
||||
|
||||
<login_instructions>
|
||||
{{LOGIN_INSTRUCTIONS}}
|
||||
</login_instructions>
|
||||
|
||||
<critical>
|
||||
- Do NOT explore the application beyond verifying the login.
|
||||
- Do NOT modify any data or settings.
|
||||
- After verifying, return your structured verdict immediately and stop.
|
||||
- If login fails, include which step failed and a brief detail (mask sensitive values like passwords).
|
||||
</critical>
|
||||
@@ -18,7 +18,7 @@ import { formatTimestamp } from '../utils/formatting.js';
|
||||
import { Timer } from '../utils/metrics.js';
|
||||
import { createAuditLogger } from './audit-logger.js';
|
||||
import { dispatchMessage } from './message-handlers.js';
|
||||
import { type ModelTier, resolveModel } from './models.js';
|
||||
import { type ModelTier, resolveModel, supportsAdaptiveThinking } from './models.js';
|
||||
import { detectExecutionContext, formatCompletionMessage, formatErrorOutput } from './output-formatters.js';
|
||||
import { createProgressManager } from './progress-manager.js';
|
||||
|
||||
@@ -218,6 +218,7 @@ export async function runClaudePrompt(
|
||||
// 4. Configure SDK options
|
||||
// Model override from providerConfig takes precedence over env-based resolveModel
|
||||
const model = providerConfig?.modelOverrides?.[modelTier] ?? resolveModel(modelTier);
|
||||
const adaptiveThinking = supportsAdaptiveThinking(model) && process.env.CLAUDE_ADAPTIVE_THINKING !== 'false';
|
||||
const options = {
|
||||
model,
|
||||
maxTurns: 10_000,
|
||||
@@ -226,6 +227,7 @@ export async function runClaudePrompt(
|
||||
allowDangerouslySkipPermissions: true,
|
||||
settingSources: ['user'] as ('user' | 'project' | 'local')[],
|
||||
env: sdkEnv,
|
||||
...(adaptiveThinking && { thinking: { type: 'adaptive' as const } }),
|
||||
...(outputFormat && { outputFormat }),
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,10 @@ function extractMessageContent(message: AssistantMessage): string {
|
||||
const messageContent = message.message;
|
||||
|
||||
if (Array.isArray(messageContent.content)) {
|
||||
return messageContent.content.map((c: ContentBlock) => c.text || JSON.stringify(c)).join('\n');
|
||||
return messageContent.content
|
||||
.filter((c: ContentBlock) => c.type !== 'thinking' && c.type !== 'redacted_thinking')
|
||||
.map((c: ContentBlock) => c.text || JSON.stringify(c))
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
return String(messageContent.content);
|
||||
|
||||
@@ -21,7 +21,7 @@ export type ModelTier = 'small' | 'medium' | 'large';
|
||||
const DEFAULT_MODELS: Readonly<Record<ModelTier, string>> = {
|
||||
small: 'claude-haiku-4-5-20251001',
|
||||
medium: 'claude-sonnet-4-6',
|
||||
large: 'claude-opus-4-6',
|
||||
large: 'claude-opus-4-7',
|
||||
};
|
||||
|
||||
/** Resolve a model tier to a concrete model ID. */
|
||||
@@ -35,3 +35,8 @@ export function resolveModel(tier: ModelTier = 'medium'): string {
|
||||
return process.env.ANTHROPIC_MEDIUM_MODEL || DEFAULT_MODELS.medium;
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether a model supports adaptive thinking. Opus 4.6 and 4.7 only. */
|
||||
export function supportsAdaptiveThinking(model: string): boolean {
|
||||
return /opus-4-[67]/.test(model);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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.
|
||||
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
export type StealthConfigWriteResult = 'wrote' | 'skipped-existing';
|
||||
|
||||
const STEALTH_INIT_SCRIPT = `
|
||||
// Remove webdriver flag
|
||||
Object.defineProperty(navigator, 'webdriver', { get: () => false });
|
||||
|
||||
// Fake plugins array (Chrome PDF Plugin, PDF Viewer, Native Client)
|
||||
Object.defineProperty(navigator, 'plugins', {
|
||||
get: () => {
|
||||
const plugins = [
|
||||
{ name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
|
||||
{ name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
|
||||
{ name: 'Native Client', filename: 'internal-nacl-plugin', description: '' },
|
||||
];
|
||||
plugins.refresh = () => {};
|
||||
return plugins;
|
||||
},
|
||||
});
|
||||
|
||||
// Stub window.chrome.runtime
|
||||
if (!window.chrome) window.chrome = {};
|
||||
if (!window.chrome.runtime) window.chrome.runtime = {};
|
||||
`.trim();
|
||||
|
||||
function buildStealthConfig(): object {
|
||||
return {
|
||||
browser: 'chromium',
|
||||
launchOptions: {
|
||||
headless: true,
|
||||
args: ['--disable-blink-features=AutomationControlled', '--no-first-run', '--no-default-browser-check'],
|
||||
ignoreDefaultArgs: ['--enable-automation'],
|
||||
},
|
||||
contextOptions: {
|
||||
viewport: { width: 1920, height: 1080 },
|
||||
locale: 'en-US',
|
||||
userAgent:
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Playwright stealth configuration to the source directory.
|
||||
* No-ops if the config file already exists.
|
||||
*/
|
||||
export async function writePlaywrightStealthConfig(sourceDir: string): Promise<StealthConfigWriteResult> {
|
||||
const playwrightDir = path.join(sourceDir, '.playwright');
|
||||
const configPath = path.join(playwrightDir, 'cli.config.json');
|
||||
|
||||
// Skip if config already exists
|
||||
try {
|
||||
await fs.stat(configPath);
|
||||
return 'skipped-existing';
|
||||
} catch {
|
||||
// File doesn't exist, proceed with writing
|
||||
}
|
||||
|
||||
await fs.mkdir(playwrightDir, { recursive: true });
|
||||
|
||||
const config = buildStealthConfig();
|
||||
const initScriptPath = path.join(playwrightDir, 'stealth-init.js');
|
||||
|
||||
await Promise.all([
|
||||
fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf8'),
|
||||
fs.writeFile(initScriptPath, STEALTH_INIT_SCRIPT, 'utf8'),
|
||||
]);
|
||||
|
||||
return 'wrote';
|
||||
}
|
||||
@@ -52,6 +52,8 @@ export interface ToolResultData {
|
||||
export interface ContentBlock {
|
||||
type?: string;
|
||||
text?: string;
|
||||
thinking?: string;
|
||||
data?: string;
|
||||
}
|
||||
|
||||
export interface AssistantMessage {
|
||||
|
||||
@@ -381,15 +381,6 @@ const performSecurityValidation = (config: Config): void => {
|
||||
ErrorCode.CONFIG_VALIDATION_FAILED,
|
||||
);
|
||||
}
|
||||
if (pattern.test(auth.credentials.password)) {
|
||||
throw new PentestError(
|
||||
`authentication.credentials.password contains potentially dangerous pattern: ${pattern.source}`,
|
||||
'config',
|
||||
false,
|
||||
{ field: 'credentials.password', pattern: pattern.source },
|
||||
ErrorCode.CONFIG_VALIDATION_FAILED,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,8 +596,17 @@ const sanitizeAuthentication = (auth: Authentication): Authentication => {
|
||||
login_url: auth.login_url.trim(),
|
||||
credentials: {
|
||||
username: auth.credentials.username.trim(),
|
||||
password: auth.credentials.password,
|
||||
...(auth.credentials.password !== undefined && { password: auth.credentials.password }),
|
||||
...(auth.credentials.totp_secret && { totp_secret: auth.credentials.totp_secret.trim() }),
|
||||
...(auth.credentials.email_login && {
|
||||
email_login: {
|
||||
address: auth.credentials.email_login.address.trim(),
|
||||
password: auth.credentials.email_login.password,
|
||||
...(auth.credentials.email_login.totp_secret && {
|
||||
totp_secret: auth.credentials.email_login.totp_secret.trim(),
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
...(auth.login_flow && { login_flow: auth.login_flow.map((step) => step.trim()) }),
|
||||
success_condition: {
|
||||
|
||||
@@ -82,6 +82,26 @@ function generateTOTP(secret: string, timeStep: number = 30, digits: number = 6)
|
||||
return generateHOTP(secret, counter, digits);
|
||||
}
|
||||
|
||||
// === Help ===
|
||||
|
||||
function printHelp(): void {
|
||||
console.log(
|
||||
`generate-totp - emit a current 6-digit TOTP code for a base32-encoded secret.
|
||||
|
||||
Usage:
|
||||
generate-totp --secret <BASE32>
|
||||
generate-totp --help
|
||||
|
||||
Options:
|
||||
--secret Base32-encoded TOTP shared secret (characters A-Z, 2-7).
|
||||
-h, --help Show this help and exit.
|
||||
|
||||
Output:
|
||||
JSON to stdout. On success: {"status":"success","totpCode":"123456","expiresIn":<sec>}.
|
||||
On error: {"status":"error","message":"...","retryable":false} (exit 1).`,
|
||||
);
|
||||
}
|
||||
|
||||
// === Argument Parsing ===
|
||||
|
||||
function parseSecret(argv: string[]): string {
|
||||
@@ -97,6 +117,11 @@ function parseSecret(argv: string[]): string {
|
||||
// === Main ===
|
||||
|
||||
function main(): void {
|
||||
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
const secret = parseSecret(process.argv);
|
||||
|
||||
if (!secret) {
|
||||
|
||||
@@ -19,6 +19,31 @@ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { DELIVERABLE_FILENAMES, type DeliverableType } from '../types/deliverables.js';
|
||||
|
||||
// === Help ===
|
||||
|
||||
function printHelp(): void {
|
||||
const types = Object.keys(DELIVERABLE_FILENAMES).join(', ');
|
||||
console.log(
|
||||
`save-deliverable - save a Shannon pentest deliverable under its canonical filename.
|
||||
|
||||
Usage:
|
||||
save-deliverable --type <TYPE> --file-path <path>
|
||||
save-deliverable --type <TYPE> --content '<text>'
|
||||
save-deliverable --help
|
||||
|
||||
Options:
|
||||
--type Deliverable type (required). One of:
|
||||
${types}
|
||||
--file-path Path of a file whose contents to save (preferred for large content).
|
||||
--content Inline content string to save.
|
||||
-h, --help Show this help and exit.
|
||||
|
||||
Output:
|
||||
JSON to stdout. On success: {"status":"success","filepath":"..."}.
|
||||
On error: {"status":"error","message":"...","retryable":true|false} (exit 1).`,
|
||||
);
|
||||
}
|
||||
|
||||
// === Argument Parsing ===
|
||||
|
||||
interface ParsedArgs {
|
||||
@@ -69,6 +94,11 @@ function saveDeliverableFile(targetDir: string, filename: string, content: strin
|
||||
// === Main ===
|
||||
|
||||
function main(): void {
|
||||
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
const args = parseArgs(process.argv);
|
||||
|
||||
// 1. Validate --type
|
||||
|
||||
@@ -138,6 +138,9 @@ function classifyByErrorCode(code: ErrorCode, retryableFromError: boolean): { ty
|
||||
case ErrorCode.AUTH_FAILED:
|
||||
return { type: 'AuthenticationError', retryable: false };
|
||||
|
||||
case ErrorCode.AUTH_LOGIN_FAILED:
|
||||
return { type: 'AuthLoginFailedError', retryable: false };
|
||||
|
||||
case ErrorCode.BILLING_ERROR:
|
||||
return { type: 'BillingError', retryable: true };
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ async function validateCredentials(
|
||||
// 1. Custom base URL — validate endpoint is reachable via SDK query
|
||||
if (process.env.ANTHROPIC_BASE_URL && process.env.ANTHROPIC_AUTH_TOKEN) {
|
||||
const baseUrl = process.env.ANTHROPIC_BASE_URL;
|
||||
logger.info(`Validating custom base URL: ${baseUrl}`);
|
||||
logger.info('Validating custom base URL');
|
||||
|
||||
try {
|
||||
for await (const message of query({ prompt: 'hi', options: { model: resolveModel('small'), maxTurns: 1 } })) {
|
||||
@@ -394,7 +394,7 @@ function httpHead(url: string, timeoutMs: number): Promise<number> {
|
||||
|
||||
/** Check that the target URL is reachable from inside the container. */
|
||||
async function validateTargetUrl(targetUrl: string, logger: ActivityLogger): Promise<Result<void, PentestError>> {
|
||||
logger.info('Checking target URL reachability...', { targetUrl });
|
||||
logger.info('Checking target URL reachability...');
|
||||
|
||||
// 1. Parse URL
|
||||
let parsed: URL;
|
||||
|
||||
@@ -76,6 +76,17 @@ async function buildLoginInstructions(
|
||||
`generated TOTP code using secret "${authentication.credentials.totp_secret}"`,
|
||||
);
|
||||
}
|
||||
if (authentication.credentials.email_login) {
|
||||
const emailLogin = authentication.credentials.email_login;
|
||||
userInstructions = userInstructions.replace(/\$email_address/g, emailLogin.address);
|
||||
userInstructions = userInstructions.replace(/\$email_password/g, emailLogin.password);
|
||||
if (emailLogin.totp_secret) {
|
||||
userInstructions = userInstructions.replace(
|
||||
/\$email_totp/g,
|
||||
`generated TOTP code using secret "${emailLogin.totp_secret}"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loginInstructions = loginInstructions.replace(/{{user_instructions}}/g, userInstructions);
|
||||
@@ -221,6 +232,16 @@ async function interpolateVariables(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a prompt directory override to an absolute path.
|
||||
* Falls back to the compiled-in PROMPTS_DIR when no override is given.
|
||||
*/
|
||||
export function resolvePromptDir(promptDir: string | undefined): string {
|
||||
if (!promptDir) return PROMPTS_DIR;
|
||||
if (path.isAbsolute(promptDir)) return promptDir;
|
||||
return path.resolve(process.env.SHANNON_WORKER_ROOT ?? process.cwd(), promptDir);
|
||||
}
|
||||
|
||||
// Pure function: Load and interpolate prompt template
|
||||
export async function loadPrompt(
|
||||
promptName: string,
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* Auth-validation preflight service.
|
||||
*
|
||||
* Drives a real browser login before the full pipeline runs,
|
||||
* catching bad credentials early and saving API budget.
|
||||
*/
|
||||
|
||||
import type { JsonSchemaOutputFormat } from '@anthropic-ai/claude-agent-sdk';
|
||||
import { z } from 'zod';
|
||||
import { runClaudePrompt } from '../ai/claude-executor.js';
|
||||
import type { AuditSession } from '../audit/index.js';
|
||||
import type { ActivityLogger } from '../types/activity-logger.js';
|
||||
import type { DistributedConfig, ProviderConfig } from '../types/config.js';
|
||||
import { ErrorCode } from '../types/errors.js';
|
||||
import type { Result } from '../types/result.js';
|
||||
import { err, ok } from '../types/result.js';
|
||||
import { PentestError } from './error-handling.js';
|
||||
import { loadPrompt } from './prompt-manager.js';
|
||||
|
||||
type FailurePoint = 'username_or_password' | 'totp_secret' | 'out_of_band';
|
||||
|
||||
const AuthValidationSchema = z.object({
|
||||
login_success: z.boolean(),
|
||||
failure_point: z.enum(['username_or_password', 'totp_secret', 'out_of_band']).optional(),
|
||||
failure_detail: z.string().max(250).optional(),
|
||||
});
|
||||
|
||||
const AUTH_VALIDATION_OUTPUT_FORMAT: JsonSchemaOutputFormat = {
|
||||
type: 'json_schema',
|
||||
schema: z.toJSONSchema(AuthValidationSchema, { target: 'draft-07' }) as Record<string, unknown>,
|
||||
};
|
||||
|
||||
export interface AuthValidationInput {
|
||||
webUrl: string;
|
||||
repoPath: string;
|
||||
config: DistributedConfig;
|
||||
pipelineTestingMode: boolean;
|
||||
auditSession: AuditSession;
|
||||
logger: ActivityLogger;
|
||||
promptDir?: string;
|
||||
apiKey?: string;
|
||||
providerConfig?: ProviderConfig;
|
||||
}
|
||||
|
||||
function classifyResult(parsed: z.infer<typeof AuthValidationSchema>): Result<void, PentestError> {
|
||||
if (parsed.login_success) {
|
||||
return ok(undefined);
|
||||
}
|
||||
|
||||
const failurePoint: FailurePoint = parsed.failure_point ?? 'username_or_password';
|
||||
const detail = parsed.failure_detail ?? 'Login failed';
|
||||
|
||||
return err(
|
||||
new PentestError(
|
||||
`Authentication validation failed at "${failurePoint}": ${detail}`,
|
||||
'config',
|
||||
false,
|
||||
{ failurePoint, failureDetail: detail },
|
||||
ErrorCode.AUTH_LOGIN_FAILED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export async function validateAuthentication(input: AuthValidationInput): Promise<Result<void, PentestError>> {
|
||||
const { webUrl, repoPath, config, pipelineTestingMode, auditSession, logger, promptDir, apiKey, providerConfig } =
|
||||
input;
|
||||
|
||||
// 1. Load the validation prompt
|
||||
const prompt = await loadPrompt(
|
||||
'validate-authentication',
|
||||
{ webUrl, repoPath },
|
||||
config,
|
||||
pipelineTestingMode,
|
||||
logger,
|
||||
promptDir,
|
||||
);
|
||||
|
||||
// 2. Run the agent with structured output
|
||||
const result = await runClaudePrompt(
|
||||
prompt,
|
||||
repoPath,
|
||||
'',
|
||||
'Auth validation',
|
||||
'validate-authentication',
|
||||
auditSession,
|
||||
logger,
|
||||
'medium',
|
||||
AUTH_VALIDATION_OUTPUT_FORMAT,
|
||||
apiKey,
|
||||
undefined,
|
||||
providerConfig,
|
||||
);
|
||||
|
||||
// 3. Parse structured output
|
||||
if (!result.success || !result.structuredOutput) {
|
||||
return err(
|
||||
new PentestError(
|
||||
`Auth validation agent did not return a structured verdict: ${result.error ?? 'unknown error'}`,
|
||||
'validation',
|
||||
true,
|
||||
{ agentError: result.error },
|
||||
ErrorCode.AGENT_EXECUTION_FAILED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const parseResult = AuthValidationSchema.safeParse(result.structuredOutput);
|
||||
if (!parseResult.success) {
|
||||
return err(
|
||||
new PentestError(
|
||||
`Auth validation output failed schema validation: ${parseResult.error.message}`,
|
||||
'validation',
|
||||
true,
|
||||
{ zodErrors: parseResult.error.issues },
|
||||
ErrorCode.OUTPUT_VALIDATION_FAILED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Classify the verdict
|
||||
return classifyResult(parseResult.data);
|
||||
}
|
||||
@@ -151,6 +151,9 @@ function createExploitValidator(vulnType: VulnType): AgentValidator {
|
||||
// Playwright session mapping - assigns each agent to a specific session for browser isolation
|
||||
// Keys are promptTemplate values from AGENTS registry
|
||||
export const PLAYWRIGHT_SESSION_MAPPING: Record<string, PlaywrightSession> = Object.freeze({
|
||||
// Runs before any agent — non-concurrent, so agent1 is safe to share
|
||||
'validate-authentication': 'agent1',
|
||||
|
||||
// Phase 1: Pre-reconnaissance
|
||||
'pre-recon-code': 'agent1',
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { ApplicationFailure, Context, heartbeat } from '@temporalio/activity';
|
||||
import { type StealthConfigWriteResult, writePlaywrightStealthConfig } from '../ai/playwright-config-writer.js';
|
||||
import { AuditSession } from '../audit/index.js';
|
||||
import type { ResumeAttempt } from '../audit/metrics-tracker.js';
|
||||
import type { SessionMetadata } from '../audit/utils.js';
|
||||
import type { WorkflowSummary } from '../audit/workflow-logger.js';
|
||||
import { distributeConfig, parseConfig, parseConfigYAML } from '../config-parser.js';
|
||||
import type { CheckpointContext } from '../interfaces/checkpoint-provider.js';
|
||||
import { DEFAULT_DELIVERABLES_SUBDIR, deliverablesDir } from '../paths.js';
|
||||
import { getContainer, getOrCreateContainer, removeContainer } from '../services/container.js';
|
||||
@@ -29,12 +31,14 @@ import { classifyErrorForTemporal, PentestError } from '../services/error-handli
|
||||
import { ExploitationCheckerService } from '../services/exploitation-checker.js';
|
||||
import { executeGitCommandWithRetry } from '../services/git-manager.js';
|
||||
import { runPreflightChecks } from '../services/preflight.js';
|
||||
import { resolvePromptDir } from '../services/prompt-manager.js';
|
||||
import type { ExploitationDecision, VulnType } from '../services/queue-validation.js';
|
||||
import { assembleFinalReport, injectModelIntoReport } from '../services/reporting.js';
|
||||
import { validateAuthentication } from '../services/validate-authentication.js';
|
||||
import { AGENTS } from '../session-manager.js';
|
||||
import type { AgentName } from '../types/agents.js';
|
||||
import { ALL_AGENTS } from '../types/agents.js';
|
||||
import type { ContainerConfig, ProviderConfig } from '../types/config.js';
|
||||
import type { Config, ContainerConfig, ProviderConfig } from '../types/config.js';
|
||||
import { ErrorCode } from '../types/errors.js';
|
||||
import { isErr } from '../types/result.js';
|
||||
import { fileExists, readJson } from '../utils/file-io.js';
|
||||
@@ -182,11 +186,7 @@ async function runAgentActivity(agentName: AgentName, input: ActivityInput): Pro
|
||||
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.promptDir !== undefined && { promptDir: resolvePromptDir(input.promptDir) }),
|
||||
...(input.configYAML !== undefined && { configYAML: input.configYAML }),
|
||||
},
|
||||
auditSession,
|
||||
@@ -373,6 +373,131 @@ export async function runPreflightValidation(input: ActivityInput): Promise<void
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Playwright stealth configuration to the source directory.
|
||||
* Thin activity wrapper — delegates to writePlaywrightStealthConfig.
|
||||
*/
|
||||
export async function syncPlaywrightStealthConfig(input: ActivityInput): Promise<StealthConfigWriteResult> {
|
||||
const logger = createActivityLogger();
|
||||
const result = await writePlaywrightStealthConfig(input.repoPath);
|
||||
logger.info(`Playwright stealth config: ${result}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth-validation preflight activity.
|
||||
*
|
||||
* Runs a real browser login attempt to confirm credentials work
|
||||
* before committing to the full pipeline.
|
||||
*
|
||||
* NOT using runAgentActivity — auth validation has its own structured output
|
||||
* flow and retry semantics.
|
||||
*/
|
||||
export async function runAuthenticationValidation(input: ActivityInput): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
const attemptNumber = Context.current().info.attempt;
|
||||
|
||||
const heartbeatInterval = setInterval(() => {
|
||||
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
||||
heartbeat({ phase: 'auth-validation', elapsedSeconds: elapsed, attempt: attemptNumber });
|
||||
}, HEARTBEAT_INTERVAL_MS);
|
||||
|
||||
try {
|
||||
const logger = createActivityLogger();
|
||||
logger.info('Running authentication validation...', { attempt: attemptNumber });
|
||||
|
||||
// 1. Load config to get authentication details
|
||||
let config: Config;
|
||||
if (input.configYAML) {
|
||||
config = parseConfigYAML(input.configYAML);
|
||||
} else if (input.configPath) {
|
||||
config = await parseConfig(input.configPath);
|
||||
} else {
|
||||
logger.info('No config provided, skipping auth validation');
|
||||
return;
|
||||
}
|
||||
|
||||
const distributed = distributeConfig(config);
|
||||
if (!distributed.authentication) {
|
||||
logger.info('No authentication configured, skipping auth validation');
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Create audit session
|
||||
const sessionMetadata = buildSessionMetadata(input);
|
||||
const auditSession = new AuditSession(sessionMetadata);
|
||||
await auditSession.initialize(input.workflowId);
|
||||
|
||||
// 3. Run validation
|
||||
const result = await validateAuthentication({
|
||||
webUrl: input.webUrl,
|
||||
repoPath: input.repoPath,
|
||||
config: distributed,
|
||||
pipelineTestingMode: input.pipelineTestingMode ?? false,
|
||||
auditSession,
|
||||
logger,
|
||||
promptDir: resolvePromptDir(input.promptDir),
|
||||
...(input.apiKey !== undefined && { apiKey: input.apiKey }),
|
||||
...(input.providerConfig !== undefined && { providerConfig: input.providerConfig }),
|
||||
});
|
||||
|
||||
if (isErr(result)) {
|
||||
const classified = classifyErrorForTemporal(result.error);
|
||||
const message = truncateErrorMessage(result.error.message);
|
||||
|
||||
const details: Record<string, unknown>[] = [
|
||||
{ phase: 'auth-validation', attemptNumber, elapsed: Date.now() - startTime },
|
||||
];
|
||||
|
||||
// Include failure point and detail for consumer error handling
|
||||
if (result.error.context.failurePoint) {
|
||||
details.push({
|
||||
failurePoint: result.error.context.failurePoint,
|
||||
failureDetail: result.error.context.failureDetail,
|
||||
});
|
||||
}
|
||||
|
||||
if (classified.retryable) {
|
||||
const failure = ApplicationFailure.create({
|
||||
message,
|
||||
type: classified.type,
|
||||
details,
|
||||
});
|
||||
truncateStackTrace(failure);
|
||||
throw failure;
|
||||
} else {
|
||||
const failure = ApplicationFailure.nonRetryable(message, classified.type, details);
|
||||
truncateStackTrace(failure);
|
||||
throw failure;
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('Authentication validation passed');
|
||||
} catch (error) {
|
||||
if (error instanceof ApplicationFailure) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const classified = classifyErrorForTemporal(error);
|
||||
const rawMessage = error instanceof Error ? error.message : String(error);
|
||||
const message = truncateErrorMessage(rawMessage);
|
||||
|
||||
const failure = classified.retryable
|
||||
? ApplicationFailure.create({
|
||||
message,
|
||||
type: classified.type,
|
||||
details: [{ phase: 'auth-validation', attemptNumber, elapsed: Date.now() - startTime }],
|
||||
})
|
||||
: ApplicationFailure.nonRetryable(message, classified.type, [
|
||||
{ phase: 'auth-validation', attemptNumber, elapsed: Date.now() - startTime },
|
||||
]);
|
||||
truncateStackTrace(failure);
|
||||
throw failure;
|
||||
} finally {
|
||||
clearInterval(heartbeatInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a private git repository inside the workspace deliverables directory.
|
||||
* Idempotent — skips if .git already exists (resume case).
|
||||
@@ -799,11 +924,7 @@ export async function generateReportOutputActivity(input: ActivityInput): Promis
|
||||
// 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),
|
||||
}),
|
||||
...(input.promptDir !== undefined && { promptDir: resolvePromptDir(input.promptDir) }),
|
||||
};
|
||||
|
||||
const result = await container.reportOutputProvider.generate(resolvedInput, logger);
|
||||
|
||||
@@ -56,6 +56,7 @@ const PRODUCTION_RETRY = {
|
||||
maximumAttempts: 50,
|
||||
nonRetryableErrorTypes: [
|
||||
'AuthenticationError',
|
||||
'AuthLoginFailedError',
|
||||
'PermissionError',
|
||||
'InvalidRequestError',
|
||||
'RequestTooLargeError',
|
||||
@@ -120,6 +121,22 @@ const preflightActs = proxyActivities<typeof activities>({
|
||||
retry: PREFLIGHT_RETRY,
|
||||
});
|
||||
|
||||
// Retry configuration for auth validation (browser-based, longer timeout)
|
||||
const AUTH_VALIDATION_RETRY = {
|
||||
initialInterval: '10 seconds',
|
||||
maximumInterval: '1 minute',
|
||||
backoffCoefficient: 2,
|
||||
maximumAttempts: 3,
|
||||
nonRetryableErrorTypes: PRODUCTION_RETRY.nonRetryableErrorTypes,
|
||||
};
|
||||
|
||||
// Activity proxy for auth validation (10-minute timeout for browser login)
|
||||
const authValidationActs = proxyActivities<typeof activities>({
|
||||
startToCloseTimeout: '10 minutes',
|
||||
heartbeatTimeout: '10 minutes',
|
||||
retry: AUTH_VALIDATION_RETRY,
|
||||
});
|
||||
|
||||
/**
|
||||
* Compute aggregated metrics from the current pipeline state.
|
||||
* Called on both success and failure to provide partial metrics.
|
||||
@@ -396,6 +413,16 @@ export async function pentestPipeline(input: PipelineInput): Promise<PipelineSta
|
||||
await preflightActs.runPreflightValidation(activityInput);
|
||||
log.info('Preflight validation passed');
|
||||
|
||||
// === Playwright Stealth Config ===
|
||||
await a.syncPlaywrightStealthConfig(activityInput);
|
||||
|
||||
// === Auth Validation ===
|
||||
// Browser-based credential check before committing to the full pipeline.
|
||||
state.currentPhase = 'auth-validation';
|
||||
state.currentAgent = 'validate-authentication';
|
||||
await authValidationActs.runAuthenticationValidation(activityInput);
|
||||
log.info('Auth validation passed');
|
||||
|
||||
// === Initialize Deliverables Git ===
|
||||
await a.initDeliverableGit(activityInput);
|
||||
|
||||
|
||||
@@ -28,12 +28,19 @@ export interface SuccessCondition {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Credentials {
|
||||
username: string;
|
||||
export interface EmailLogin {
|
||||
address: string;
|
||||
password: string;
|
||||
totp_secret?: string;
|
||||
}
|
||||
|
||||
export interface Credentials {
|
||||
username: string;
|
||||
password?: string;
|
||||
totp_secret?: string;
|
||||
email_login?: EmailLogin;
|
||||
}
|
||||
|
||||
export interface Authentication {
|
||||
login_type: LoginType;
|
||||
login_url: string;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/**
|
||||
* Specific error codes for reliable classification.
|
||||
*
|
||||
* ErrorCode provides precision within the coarse 8-category PentestErrorType.
|
||||
* ErrorCode provides precision within the coarse 7-category PentestErrorType.
|
||||
* Used by classifyErrorForTemporal for code-based classification (preferred)
|
||||
* with string matching as fallback for external errors.
|
||||
*/
|
||||
@@ -45,17 +45,12 @@ export enum ErrorCode {
|
||||
TARGET_UNREACHABLE = 'TARGET_UNREACHABLE',
|
||||
AUTH_FAILED = 'AUTH_FAILED',
|
||||
BILLING_ERROR = 'BILLING_ERROR',
|
||||
|
||||
// Auth validation errors
|
||||
AUTH_LOGIN_FAILED = 'AUTH_LOGIN_FAILED',
|
||||
}
|
||||
|
||||
export type PentestErrorType =
|
||||
| 'config'
|
||||
| 'network'
|
||||
| 'tool'
|
||||
| 'prompt'
|
||||
| 'filesystem'
|
||||
| 'validation'
|
||||
| 'billing'
|
||||
| 'unknown';
|
||||
export type PentestErrorType = 'config' | 'network' | 'prompt' | 'filesystem' | 'validation' | 'billing' | 'unknown';
|
||||
|
||||
export interface PentestErrorContext {
|
||||
[key: string]: unknown;
|
||||
|
||||
Generated
+122
-176
@@ -7,8 +7,8 @@ settings:
|
||||
catalogs:
|
||||
default:
|
||||
'@anthropic-ai/claude-agent-sdk':
|
||||
specifier: ^0.2.38
|
||||
version: 0.2.76
|
||||
specifier: ^0.2.114
|
||||
version: 0.2.140
|
||||
|
||||
importers:
|
||||
|
||||
@@ -77,7 +77,7 @@ importers:
|
||||
dependencies:
|
||||
'@anthropic-ai/claude-agent-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.2.76(zod@4.3.6)
|
||||
version: 0.2.140(zod@4.3.6)
|
||||
'@temporalio/activity':
|
||||
specifier: ^1.11.0
|
||||
version: 1.15.0
|
||||
@@ -115,12 +115,65 @@ importers:
|
||||
|
||||
packages:
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk@0.2.76':
|
||||
resolution: {integrity: sha512-HZxvnT8ZWkzCnQygaYCA0dl8RSUzuVbxE1YG4ecy6vh4nQbTT36CxUxBy+QVdR12pPQluncC0mCOLhI2918Eaw==}
|
||||
'@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.140':
|
||||
resolution: {integrity: sha512-zEbDsDKeoDO4DzbyX6wBVlcPhLy/gYiCrKzKnxmkOhyNtJBeshgiOTdr+M7WX1xcuI/M/UhEY+B9U6oo884lAQ==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.140':
|
||||
resolution: {integrity: sha512-BFJGeZEksvERy7mMJ0mkNAWoMrZOgl6XN/mKPaunGnaC/i+1ykx7xih7e58bRhsrzKzo2mnUrwtjiFyF3MFNRQ==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.140':
|
||||
resolution: {integrity: sha512-nG7xLL0nKb4ymFVnX0QhSGLoyhh9fuuDpBR+TYz5O4ZQc2RVUMSMqGusqcCNEIGxAKQSVKWCf0WgpCG/edAO9Q==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.140':
|
||||
resolution: {integrity: sha512-FauGGg3zikxrjAUnu+Pso6zD9Qv4Z2+QBiTiZqc12U+x4uoikNsplymUnsJ7MYD9VaTGmLJuZ9pCch0IiKrseQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.140':
|
||||
resolution: {integrity: sha512-EZ7VzOGmvft/1ymh2rwts5v3yPnsGGlGrTJlY2Dqnr1ABF43JIhEm1NFYrLnXQWSN74s5Pj8tgkPbYS9x4BhFA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-linux-x64@0.2.140':
|
||||
resolution: {integrity: sha512-7f627Tq2mIiwFoBYfCKTdEeZSP90r8UOWu/I5DezudTtwtoVl2zRaRCnJ8c4rW+Tzw+xWSfP/pHvR9bTQGXaOw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.140':
|
||||
resolution: {integrity: sha512-9EOozRF+LTt3UedeJtjJXC8pj9VTAFtPBuB+/YUmcpmDAEH9qcWWknWhf7NDKTapKtBWkNP/387x+18L15MLqg==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-win32-x64@0.2.140':
|
||||
resolution: {integrity: sha512-puQyWoYiqosjDEYULWAS/lBJse1vzib0NmQj/bYTirWCbtiUcu6ixKMd4NmLbE+Si/DKTB8XNz6hVZ/KckqeoQ==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk@0.2.140':
|
||||
resolution: {integrity: sha512-Zq2L7YCoTdbxTUi3/soN1axrTqbG7GoKuc6Im8EpkBRdwaY0D1W9+Ux3vAbV/cX8Qk31Vck7DQLZz1lGEArdoQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
zod: ^4.0.0
|
||||
|
||||
'@anthropic-ai/sdk@0.81.0':
|
||||
resolution: {integrity: sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
zod: ^3.25.0 || ^4.0.0
|
||||
peerDependenciesMeta:
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
'@babel/generator@8.0.0-rc.2':
|
||||
resolution: {integrity: sha512-oCQ1IKPwkzCeJzAPb7Fv8rQ9k5+1sG8mf2uoHiMInPYvkRfrDJxbTIbH51U+jstlkghus0vAi3EBvkfvEsYNLQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -138,6 +191,10 @@ packages:
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
|
||||
'@babel/runtime@7.29.2':
|
||||
resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/types@8.0.0-rc.2':
|
||||
resolution: {integrity: sha512-91gAaWRznDwSX4E2tZ1YjBuIfnQVOFDCQ2r0Toby0gu4XEbyF623kXLMA8d4ZbCu+fINcrudkmEcwSUHgDDkNw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -229,105 +286,6 @@ packages:
|
||||
peerDependencies:
|
||||
hono: ^4
|
||||
|
||||
'@img/sharp-darwin-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-darwin-x64@0.34.5':
|
||||
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-win32-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@img/sharp-win32-x64@0.34.5':
|
||||
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
|
||||
@@ -1345,6 +1303,10 @@ packages:
|
||||
json-parse-even-better-errors@2.3.1:
|
||||
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
|
||||
|
||||
json-schema-to-ts@3.1.1:
|
||||
resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
json-schema-traverse@1.0.0:
|
||||
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||
|
||||
@@ -1744,6 +1706,9 @@ packages:
|
||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||
hasBin: true
|
||||
|
||||
ts-algebra@2.0.0:
|
||||
resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==}
|
||||
|
||||
tsdown@0.21.5:
|
||||
resolution: {integrity: sha512-TlgNhfPioAD6ECCUnZsxcUsXXuPPR4Rrxz3az741kL/M3oGIET4a9GajSNRNRx+jIva73TYUKQybrEPkDYN+fQ==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
@@ -1933,19 +1898,53 @@ packages:
|
||||
|
||||
snapshots:
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk@0.2.76(zod@4.3.6)':
|
||||
'@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.140':
|
||||
optional: true
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.140':
|
||||
optional: true
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.140':
|
||||
optional: true
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.140':
|
||||
optional: true
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.140':
|
||||
optional: true
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-linux-x64@0.2.140':
|
||||
optional: true
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.140':
|
||||
optional: true
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk-win32-x64@0.2.140':
|
||||
optional: true
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk@0.2.140(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@anthropic-ai/sdk': 0.81.0(zod@4.3.6)
|
||||
'@modelcontextprotocol/sdk': 1.29.0(zod@4.3.6)
|
||||
zod: 4.3.6
|
||||
optionalDependencies:
|
||||
'@img/sharp-darwin-arm64': 0.34.5
|
||||
'@img/sharp-darwin-x64': 0.34.5
|
||||
'@img/sharp-linux-arm': 0.34.5
|
||||
'@img/sharp-linux-arm64': 0.34.5
|
||||
'@img/sharp-linux-x64': 0.34.5
|
||||
'@img/sharp-linuxmusl-arm64': 0.34.5
|
||||
'@img/sharp-linuxmusl-x64': 0.34.5
|
||||
'@img/sharp-win32-arm64': 0.34.5
|
||||
'@img/sharp-win32-x64': 0.34.5
|
||||
'@anthropic-ai/claude-agent-sdk-darwin-arm64': 0.2.140
|
||||
'@anthropic-ai/claude-agent-sdk-darwin-x64': 0.2.140
|
||||
'@anthropic-ai/claude-agent-sdk-linux-arm64': 0.2.140
|
||||
'@anthropic-ai/claude-agent-sdk-linux-arm64-musl': 0.2.140
|
||||
'@anthropic-ai/claude-agent-sdk-linux-x64': 0.2.140
|
||||
'@anthropic-ai/claude-agent-sdk-linux-x64-musl': 0.2.140
|
||||
'@anthropic-ai/claude-agent-sdk-win32-arm64': 0.2.140
|
||||
'@anthropic-ai/claude-agent-sdk-win32-x64': 0.2.140
|
||||
transitivePeerDependencies:
|
||||
- '@cfworker/json-schema'
|
||||
- supports-color
|
||||
|
||||
'@anthropic-ai/sdk@0.81.0(zod@4.3.6)':
|
||||
dependencies:
|
||||
json-schema-to-ts: 3.1.1
|
||||
optionalDependencies:
|
||||
zod: 4.3.6
|
||||
|
||||
'@babel/generator@8.0.0-rc.2':
|
||||
dependencies:
|
||||
@@ -1964,6 +1963,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/types': 8.0.0-rc.2
|
||||
|
||||
'@babel/runtime@7.29.2': {}
|
||||
|
||||
'@babel/types@8.0.0-rc.2':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 8.0.0-rc.2
|
||||
@@ -2045,68 +2046,6 @@ snapshots:
|
||||
dependencies:
|
||||
hono: 4.12.12
|
||||
|
||||
'@img/sharp-darwin-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-darwin-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-arm64@0.34.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-x64@0.34.5':
|
||||
optional: true
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@@ -3103,6 +3042,11 @@ snapshots:
|
||||
|
||||
json-parse-even-better-errors@2.3.1: {}
|
||||
|
||||
json-schema-to-ts@3.1.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.2
|
||||
ts-algebra: 2.0.0
|
||||
|
||||
json-schema-traverse@1.0.0: {}
|
||||
|
||||
json-schema-typed@8.0.2: {}
|
||||
@@ -3530,6 +3474,8 @@ snapshots:
|
||||
|
||||
tree-kill@1.2.2: {}
|
||||
|
||||
ts-algebra@2.0.0: {}
|
||||
|
||||
tsdown@0.21.5(typescript@5.9.3):
|
||||
dependencies:
|
||||
ansis: 4.2.0
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ packages:
|
||||
- "apps/*"
|
||||
|
||||
catalog:
|
||||
"@anthropic-ai/claude-agent-sdk": ^0.2.38
|
||||
"@anthropic-ai/claude-agent-sdk": ^0.2.114
|
||||
|
||||
Reference in New Issue
Block a user