dotfiles/scripts/bin/dvmcp

397 lines
11 KiB
Bash
Executable file

#!/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 <command>"
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