diff --git a/CLAUDE.md b/CLAUDE.md index 13d6c82f..e736f3f2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,12 +10,13 @@ AI-powered penetration testing agent for defensive security analysis. Automates # Setup cp .env.example .env && edit .env # Set ANTHROPIC_API_KEY -# Prepare repo (REPO is a folder name inside ./repos/, not an absolute path) +# Prepare repo (clone into ./repos/, or point to any path) git clone https://github.com/org/repo.git ./repos/my-repo -# or symlink: ln -s /path/to/existing/repo ./repos/my-repo # Run -./shannon start URL= REPO=my-repo +./shannon start URL= REPO=my-repo # Option A: folder name in ./repos/ +./shannon start URL= REPO=/path/to/existing/repo # Option B: absolute path +./shannon start URL= REPO=../other-project/repo # Option C: relative path ./shannon start URL= REPO=my-repo CONFIG=./configs/my-config.yaml # Workspaces & Resume @@ -150,7 +151,7 @@ Comments must be **timeless** — no references to this conversation, refactorin ## Troubleshooting -- **"Repository not found"** — `REPO` must be a folder name inside `./repos/`, not an absolute path. Clone or symlink your repo there first: `ln -s /path/to/repo ./repos/my-repo` +- **"Repository not found"** — `REPO` can be a folder name inside `./repos/`, an absolute path, or a relative path. Verify the path exists and contains a git repository. - **"Temporal not ready"** — Wait for health check or `docker compose logs temporal` - **Worker not processing** — Check `docker compose ps` - **Reset state** — `./shannon stop CLEAN=true` diff --git a/Dockerfile b/Dockerfile index a78a2108..e278220c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -130,9 +130,9 @@ RUN npm prune --production && \ RUN npm install -g @anthropic-ai/claude-code # Create directories for session data and ensure proper permissions -RUN mkdir -p /app/sessions /app/deliverables /app/repos /app/configs && \ +RUN mkdir -p /app/sessions /app/deliverables /app/repos /app/configs /target-repo && \ mkdir -p /tmp/.cache /tmp/.config /tmp/.npm && \ - chmod 777 /app && \ + chmod 777 /app /target-repo && \ chmod 777 /tmp/.cache && \ chmod 777 /tmp/.config && \ chmod 777 /tmp/.npm && \ diff --git a/README.md b/README.md index c1f7ec92..95ab3080 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,11 @@ CLAUDE_CODE_MAX_OUTPUT_TOKENS=64000 EOF # 3. Run a pentest +# Using ./repos/ directory ./shannon start URL=https://your-app.com REPO=your-repo + +# Or point to any directory on your filesystem +./shannon start URL=https://your-app.com REPO=/path/to/your-repo ``` Shannon will build the containers, start the workflow, and return a workflow ID. The pentest runs in the background. @@ -202,6 +206,9 @@ open http://localhost:8233 # Basic pentest ./shannon start URL=https://example.com REPO=repo-name +# Pentest with arbitrary repo path +./shannon start URL=https://example.com REPO=/home/user/projects/my-app + # With a configuration file ./shannon start URL=https://example.com REPO=repo-name CONFIG=./configs/my-config.yaml @@ -244,10 +251,23 @@ Shannon supports **workspaces** that allow you to resume interrupted or failed r ### Prepare Your Repository -Shannon expects target repositories to be placed under the `./repos/` directory at the project root. The `REPO` flag refers to a folder name inside `./repos/`. Copy the repository you want to scan into `./repos/`, or clone it directly there: +Shannon can analyze a repository from any location on your host filesystem. Pass the path to your repo via the `REPO` flag: + +```bash +# Point to a repo anywhere on your filesystem +./shannon start URL=https://example.com REPO=/home/user/projects/my-app + +# Or use a relative path +./shannon start URL=https://example.com REPO=../other-project/my-app +``` + +**Using the `./repos/` directory (optional):** + +You can also clone repos into the `./repos/` directory and reference them by folder name: ```bash git clone https://github.com/your-org/your-repo.git ./repos/your-repo +./shannon start URL=https://example.com REPO=your-repo ``` **For monorepos:** diff --git a/docker-compose.yml b/docker-compose.yml index 843e7522..1df7a3ac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,7 +44,7 @@ services: - ./audit-logs:/app/audit-logs - ${OUTPUT_DIR:-./audit-logs}:/app/output - ./credentials:/app/credentials:ro - - ./repos:/repos + - ${REPO_MOUNT_SOURCE:-./repos}:${REPO_MOUNT_TARGET:-/repos} - ${BENCHMARKS_BASE:-.}:/benchmarks shm_size: 2gb ipc: host diff --git a/shannon b/shannon index 0a96eba3..e59cad80 100755 --- a/shannon +++ b/shannon @@ -40,14 +40,14 @@ show_help() { AI Penetration Testing Framework Usage: - ./shannon start URL= REPO= Start a pentest workflow + ./shannon start URL= REPO= Start a pentest workflow ./shannon workspaces List all workspaces ./shannon logs ID= Tail logs for a specific workflow ./shannon stop Stop all containers ./shannon help Show this help message Options for 'start': - REPO= Folder name under ./repos/ (e.g. REPO=repo-name) + REPO= Repository path: folder name under ./repos/, absolute, or relative path CONFIG= Configuration file (YAML) OUTPUT= Output directory for reports (default: ./audit-logs/) WORKSPACE= Named workspace (auto-resumes if exists, creates if new) @@ -59,6 +59,8 @@ Options for 'stop': Examples: ./shannon start URL=https://example.com REPO=repo-name + ./shannon start URL=https://example.com REPO=/home/user/projects/my-app + ./shannon start URL=https://example.com REPO=../other-project/my-app ./shannon start URL=https://example.com REPO=repo-name WORKSPACE=q1-audit ./shannon start URL=https://example.com REPO=repo-name CONFIG=./config.yaml ./shannon start URL=https://example.com REPO=repo-name OUTPUT=./my-reports @@ -96,10 +98,10 @@ is_temporal_ready() { # Ensure containers are running with correct mounts ensure_containers() { - # If custom OUTPUT_DIR is set, always refresh worker to ensure correct volume mount + # If custom mounts are set, always refresh worker to ensure correct volume mounts # Docker compose will only recreate if the mount actually changed - if [ -n "$OUTPUT_DIR" ]; then - echo "Ensuring worker has correct output mount..." + if [ -n "$OUTPUT_DIR" ] || [ -n "$REPO_MOUNT_SOURCE" ]; then + echo "Ensuring worker has correct volume mounts..." docker compose -f "$COMPOSE_FILE" $COMPOSE_OVERRIDE up -d worker 2>/dev/null || true fi @@ -138,7 +140,7 @@ cmd_start() { # Validate required vars if [ -z "$URL" ] || [ -z "$REPO" ]; then echo "ERROR: URL and REPO are required" - echo "Usage: ./shannon start URL= REPO=" + echo "Usage: ./shannon start URL= REPO=" exit 1 fi @@ -193,17 +195,36 @@ cmd_start() { fi # Determine container path for REPO - # - If REPO is already a container path (/benchmarks/*, /repos/*), use as-is - # - Otherwise, treat as a folder name under ./repos/ (mounted at /repos in container) + # Mode 1: Already a container path (/benchmarks/*, /repos/*) — use as-is + # Mode 2: Host filesystem path (contains / or starts with .) — resolve and mount + # Mode 3: Simple name — legacy behavior, folder under ./repos/ case "$REPO" in /benchmarks/*|/repos/*) + # Container path — use as-is (e.g., internal benchmarks) CONTAINER_REPO="$REPO" ;; + /*|./*|../*|..|.) + # Absolute or relative path — resolve to absolute and mount into container + REPO_HOST_PATH="$(cd "$REPO" 2>/dev/null && pwd -P)" || { + echo "ERROR: Repository not found at $REPO" + echo "" + echo "Provide an absolute path, a relative path, or a folder name under ./repos/" + exit 1 + } + export REPO_MOUNT_SOURCE="$REPO_HOST_PATH" + export REPO_MOUNT_TARGET="/target-repo" + CONTAINER_REPO="/target-repo" + ;; *) + # Simple name — legacy behavior: folder under ./repos/ + unset REPO_MOUNT_SOURCE REPO_MOUNT_TARGET if [ ! -d "./repos/$REPO" ]; then echo "ERROR: Repository not found at ./repos/$REPO" echo "" - echo "Place your target repository under the ./repos/ directory" + echo "Provide a folder name under ./repos/, or use an absolute/relative path:" + echo " REPO=my-repo (looks in ./repos/my-repo)" + echo " REPO=/home/user/projects/repo (absolute path)" + echo " REPO=../other-project/repo (relative path)" exit 1 fi CONTAINER_REPO="/repos/$REPO" @@ -251,7 +272,12 @@ cmd_start() { chmod 777 ./audit-logs # Ensure repo deliverables directory is writable by container user (UID 1001) - if [ -d "./repos/$REPO" ]; then + if [ -n "$REPO_HOST_PATH" ]; then + # Arbitrary path mode — use resolved host path + mkdir -p "$REPO_HOST_PATH/deliverables" + chmod 777 "$REPO_HOST_PATH/deliverables" + elif [ -d "./repos/$REPO" ]; then + # Legacy mode — folder under ./repos/ mkdir -p "./repos/$REPO/deliverables" chmod 777 "./repos/$REPO/deliverables" fi