#!/usr/bin/env bash set -euo pipefail # Script Name: dvmcp # Description: Damn Vulnerable MCP Server launcher # Usage: dvmcp start|stop|status|logs|build VERSION="1.1.0" # Colors readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly CYAN='\033[0;36m' readonly BOLD='\033[1m' readonly NC='\033[0m' # Container settings CONTAINER_NAME="dvmcp" IMAGE="dvmcp:latest" PORT_RANGE="9001-9010" # Config file paths SETTINGS_DIR="${HOME}/.claude" PROD_CONFIG="${SETTINGS_DIR}/settings.json" CTF_CONFIG="${SETTINGS_DIR}/settings-ctf.json" BACKUP_CONFIG="${SETTINGS_DIR}/settings-prod-backup.json" CONFIG_STATE_FILE="${SETTINGS_DIR}/.dvmcp-config-state" show_help() { echo -e "${BOLD}dvmcp${NC} - Damn Vulnerable MCP Server Launcher v${VERSION}" echo echo -e "${BOLD}USAGE:${NC}" echo " dvmcp " echo echo -e "${BOLD}COMMANDS:${NC}" echo -e " ${CYAN}build${NC} Build Docker image from Dockerfile" echo -e " ${CYAN}start${NC} Start DVMCP server" echo -e " ${CYAN}stop${NC} Stop DVMCP server" echo -e " ${CYAN}restart${NC} Restart DVMCP server" echo -e " ${CYAN}status${NC} Check if running" echo -e " ${CYAN}logs${NC} Show container logs" echo -e " ${CYAN}shell${NC} Open shell in container" echo echo -e "${BOLD}EXAMPLES:${NC}" echo " dvmcp build # Build image (first time setup)" echo " dvmcp start # Launch DVMCP server" echo " dvmcp stop # Stop DVMCP server" echo " dvmcp logs # View logs" echo echo -e "${BOLD}ACCESS:${NC}" echo " Ports: ${BOLD}9001-9010${NC} (10 challenge instances)" echo " Test: ${BOLD}curl http://localhost:9001${NC}" echo echo -e "${BOLD}ABOUT:${NC}" echo " Damn Vulnerable MCP Server - Intentionally vulnerable MCP implementation" echo " Perfect for testing MCP security vulnerabilities" echo " GitHub: https://github.com/harishsg993010/damn-vulnerable-MCP-server" echo echo -e "${BOLD}SECURITY:${NC}" echo " This script automatically manages Claude Code config isolation:" echo " - ${GREEN}start${NC}: Backs up production config, loads CTF-only config" echo " - ${GREEN}stop${NC}: Restores production config automatically" echo " - ${YELLOW}Your production MCP servers are protected${NC}" echo echo -e "${BOLD}FIRST TIME SETUP:${NC}" echo " 1. Clone repo: git clone https://github.com/harishsg993010/damn-vulnerable-MCP-server.git" echo " 2. cd damn-vulnerable-MCP-server/" echo " 3. Build: dvmcp build" echo " 4. Start: dvmcp start" echo " 5. ${BOLD}Restart Claude Code${NC} to load CTF config" } check_docker() { if ! command -v docker &>/dev/null; then echo -e "${RED}Error:${NC} Docker not installed" exit 1 fi } check_image_exists() { if ! docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${IMAGE}$"; then return 1 fi return 0 } create_ctf_config_template() { if [[ ! -f "$CTF_CONFIG" ]]; then echo -e "${CYAN}[*]${NC} Creating CTF config template at ${CTF_CONFIG}..." cat > "$CTF_CONFIG" <<'EOF' { "mcpServers": { "Challenge 1": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9001/sse"] }, "Challenge 2": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9002/sse"] }, "Challenge 3": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9003/sse"] }, "Challenge 4": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9004/sse"] }, "Challenge 5": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9005/sse"] }, "Challenge 6": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9006/sse"] }, "Challenge 7": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9007/sse"] }, "Challenge 8": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9008/sse"] }, "Challenge 9": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9009/sse"] }, "Challenge 10": { "command": "npx", "args": ["mcp-remote", "http://127.0.0.1:9010/sse"] } } } EOF echo -e "${GREEN}✓${NC} CTF config created" fi } swap_to_ctf_config() { if [[ ! -f "$PROD_CONFIG" ]]; then echo -e "${RED}Error:${NC} Production config not found at ${PROD_CONFIG}" exit 1 fi # Check if we're already in CTF mode if [[ -f "$CONFIG_STATE_FILE" ]]; then echo -e "${YELLOW}⚠${NC} Already in CTF mode" return 0 fi # Create CTF config if it doesn't exist create_ctf_config_template echo -e "${CYAN}[*]${NC} Backing up production config..." cp "$PROD_CONFIG" "$BACKUP_CONFIG" echo -e "${CYAN}[*]${NC} Switching to CTF config..." cp "$CTF_CONFIG" "$PROD_CONFIG" # Mark that we're in CTF mode echo "CTF_MODE_ACTIVE" > "$CONFIG_STATE_FILE" echo -e "${GREEN}✓${NC} Switched to CTF config" echo -e "${YELLOW}Note:${NC} Production MCP servers are disabled until you stop DVMCP" } restore_prod_config() { if [[ ! -f "$CONFIG_STATE_FILE" ]]; then echo -e "${YELLOW}⚠${NC} Already using production config" return 0 fi if [[ ! -f "$BACKUP_CONFIG" ]]; then echo -e "${RED}Error:${NC} Backup config not found at ${BACKUP_CONFIG}" echo -e "${YELLOW}Warning:${NC} Cannot restore production config!" exit 1 fi echo -e "${CYAN}[*]${NC} Restoring production config..." cp "$BACKUP_CONFIG" "$PROD_CONFIG" # Clean up backup and state file rm -f "$BACKUP_CONFIG" "$CONFIG_STATE_FILE" echo -e "${GREEN}✓${NC} Production config restored" } cleanup_on_exit() { # If script exits unexpectedly while in CTF mode, restore production config if [[ -f "$CONFIG_STATE_FILE" ]]; then echo echo -e "${YELLOW}[!]${NC} Script interrupted, restoring production config..." restore_prod_config fi } # Register cleanup trap trap cleanup_on_exit EXIT INT TERM build_dvmcp() { # Check if Dockerfile exists in current directory if [[ ! -f "Dockerfile" ]]; then echo -e "${RED}Error:${NC} Dockerfile not found in current directory" echo echo -e "${YELLOW}Expected location:${NC} ./Dockerfile" echo echo -e "${CYAN}To build the image:${NC}" echo " 1. cd ~/path/to/damn-vulnerable-MCP-server/" echo " 2. Run: dvmcp build" echo echo -e "${CYAN}Or clone the repo:${NC}" echo " git clone https://github.com/harishsg993010/damn-vulnerable-MCP-server.git" exit 1 fi echo -e "${CYAN}[*]${NC} Building DVMCP Docker image..." echo -e "${YELLOW}Note:${NC} This may take several minutes..." if docker build -t "$IMAGE" . ; then echo -e "${GREEN}✓${NC} DVMCP image built successfully" echo -e "${CYAN}[*]${NC} You can now run: ${BOLD}dvmcp start${NC}" else echo -e "${RED}✗${NC} Build failed" exit 1 fi } start_dvmcp() { # Check if image exists if ! check_image_exists; then echo -e "${RED}Error:${NC} DVMCP image not found" echo -e "${YELLOW}Run:${NC} ${BOLD}dvmcp build${NC} first" exit 1 fi # Swap to CTF config BEFORE starting container swap_to_ctf_config echo if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then echo -e "${YELLOW}⚠${NC} DVMCP already running" echo -e "${CYAN}[*]${NC} Access at: ${BOLD}http://localhost:9001-9010${NC}" return 0 else echo -e "${CYAN}[*]${NC} Starting existing container..." docker start "$CONTAINER_NAME" fi else echo -e "${CYAN}[*]${NC} Starting DVMCP server..." echo -e "${YELLOW}Note:${NC} Mapping ports 9001-9010 for challenge instances" docker run -d --name "$CONTAINER_NAME" \ -p 9001:9001 \ -p 9002:9002 \ -p 9003:9003 \ -p 9004:9004 \ -p 9005:9005 \ -p 9006:9006 \ -p 9007:9007 \ -p 9008:9008 \ -p 9009:9009 \ -p 9010:9010 \ "$IMAGE" fi sleep 3 echo -e "${GREEN}✓${NC} DVMCP server started" echo echo -e "${BOLD}Challenge Instances:${NC}" echo -e " ${CYAN}Port 9001:${NC} http://localhost:9001" echo -e " ${CYAN}Port 9002:${NC} http://localhost:9002" echo -e " ${CYAN}Port 9003:${NC} http://localhost:9003" echo -e " ${CYAN}Port 9004:${NC} http://localhost:9004" echo -e " ${CYAN}Port 9005:${NC} http://localhost:9005" echo -e " ${CYAN}Port 9006:${NC} http://localhost:9006" echo -e " ${CYAN}Port 9007:${NC} http://localhost:9007" echo -e " ${CYAN}Port 9008:${NC} http://localhost:9008" echo -e " ${CYAN}Port 9009:${NC} http://localhost:9009" echo -e " ${CYAN}Port 9010:${NC} http://localhost:9010" echo echo -e "${YELLOW}Tip:${NC} Test with: ${BOLD}curl http://localhost:9001${NC}" echo -e "${YELLOW}Security:${NC} ${GREEN}Production MCP servers isolated${NC}" echo echo -e "${BOLD}⚠ IMPORTANT:${NC} Restart Claude Code to load CTF config" } stop_dvmcp() { if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then echo -e "${CYAN}[*]${NC} Stopping DVMCP server..." docker stop "$CONTAINER_NAME" echo -e "${GREEN}✓${NC} DVMCP server stopped" else echo -e "${YELLOW}⚠${NC} DVMCP not running" fi # Restore production config AFTER stopping container echo restore_prod_config echo echo -e "${BOLD}⚠ IMPORTANT:${NC} Restart Claude Code to load production config" } restart_dvmcp() { stop_dvmcp sleep 2 start_dvmcp } show_status() { if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then echo -e "${GREEN}●${NC} DVMCP server is ${GREEN}running${NC}" echo echo -e "${BOLD}Active Ports:${NC}" docker port "$CONTAINER_NAME" 2>/dev/null | while IFS= read -r line; do echo -e " ${CYAN}${line}${NC}" done echo docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "(NAMES|${CONTAINER_NAME})" else echo -e "${RED}●${NC} DVMCP server is ${RED}stopped${NC}" if ! check_image_exists; then echo echo -e "${YELLOW}Note:${NC} Image not built yet. Run: ${BOLD}dvmcp build${NC}" fi fi echo echo -e "${BOLD}Config Status:${NC}" if [[ -f "$CONFIG_STATE_FILE" ]]; then echo -e " ${CYAN}Mode:${NC} CTF (isolated)" echo -e " ${CYAN}Active:${NC} ${CTF_CONFIG}" echo -e " ${CYAN}Backup:${NC} ${BACKUP_CONFIG}" else echo -e " ${CYAN}Mode:${NC} Production" echo -e " ${CYAN}Active:${NC} ${PROD_CONFIG}" fi } show_logs() { if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then docker logs -f "$CONTAINER_NAME" else echo -e "${RED}Error:${NC} DVMCP server not running" exit 1 fi } open_shell() { if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then echo -e "${CYAN}[*]${NC} Opening shell in DVMCP container..." docker exec -it "$CONTAINER_NAME" /bin/bash || docker exec -it "$CONTAINER_NAME" /bin/sh else echo -e "${RED}Error:${NC} DVMCP server not running" exit 1 fi } # Main check_docker if [[ $# -eq 0 ]] || [[ "$1" =~ ^(-h|--help|help)$ ]]; then show_help exit 0 fi case "$1" in build) build_dvmcp ;; start|up) start_dvmcp ;; stop|down) stop_dvmcp ;; restart) restart_dvmcp ;; status) show_status ;; logs) show_logs ;; shell|sh|bash) open_shell ;; *) echo -e "${RED}Error:${NC} Unknown command: $1" echo "Run 'dvmcp --help' for usage" exit 1 ;; esac