diff --git a/__LOCAL_LLMs/windows_specific/openclaw-personal-ai-assistant.md b/__LOCAL_LLMs/OPEN_CLAW/openclaw-personal-ai-assistant.md similarity index 100% rename from __LOCAL_LLMs/windows_specific/openclaw-personal-ai-assistant.md rename to __LOCAL_LLMs/OPEN_CLAW/openclaw-personal-ai-assistant.md diff --git a/__LOCAL_LLMs/OPEN_CLAW/validate-security.sh b/__LOCAL_LLMs/OPEN_CLAW/validate-security.sh new file mode 100755 index 00000000..79d9c98e --- /dev/null +++ b/__LOCAL_LLMs/OPEN_CLAW/validate-security.sh @@ -0,0 +1,487 @@ +#!/bin/bash +# ============================================================================= +# OpenClaw Security Validator +# ============================================================================= +# Run this script AFTER installing OpenClaw to verify your setup is secure. +# Works on both macOS and Linux/WSL2. +# +# Usage: +# bash validate-security.sh +# +# Output: +# ✅ = Secure (green) +# ❌ = Insecure — action required (red) +# ⚠️ = Warning — review recommended (yellow) +# ℹ️ = Info (blue) +# ============================================================================= + +set -euo pipefail + +# --- Colors --- +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +# --- Counters --- +PASS=0 +FAIL=0 +WARN=0 +RECOMMENDATIONS=() + +# --- Helpers --- +pass() { + echo -e " ${GREEN}✅ $1${NC}" + ((PASS++)) +} + +fail() { + echo -e " ${RED}❌ $1${NC}" + ((FAIL++)) + RECOMMENDATIONS+=("$2") +} + +warn() { + echo -e " ${YELLOW}⚠️ $1${NC}" + ((WARN++)) + RECOMMENDATIONS+=("$2") +} + +info() { + echo -e " ${BLUE}ℹ️ $1${NC}" +} + +section() { + echo "" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BOLD} $1${NC}" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +# --- Detect OS --- +OS="unknown" +if [[ "$(uname -s)" == "Darwin" ]]; then + OS="macos" +elif grep -qi microsoft /proc/version 2>/dev/null; then + OS="wsl2" +elif [[ "$(uname -s)" == "Linux" ]]; then + OS="linux" +fi + +OPENCLAW_DIR="$HOME/.openclaw" +CONFIG_FILE="$OPENCLAW_DIR/config.yaml" + +# ============================================================================= +echo "" +echo -e "${BOLD}🦞 OpenClaw Security Validator${NC}" +echo -e " $(date '+%Y-%m-%d %H:%M:%S')" +echo -e " Platform: ${BOLD}$OS${NC}" +echo "" + +# ============================================================================= +section "1. OpenClaw Installation" +# ============================================================================= + +# Check if openclaw is installed +if command -v openclaw &>/dev/null; then + VERSION=$(openclaw --version 2>/dev/null || echo "unknown") + pass "OpenClaw installed: $VERSION" +else + fail "OpenClaw is NOT installed" \ + "Install OpenClaw: npm install -g openclaw@latest && openclaw onboard --install-daemon" +fi + +# Check Node.js version +if command -v node &>/dev/null; then + NODE_VER=$(node --version 2>/dev/null | sed 's/v//') + NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1) + if [[ "$NODE_MAJOR" -ge 22 ]]; then + pass "Node.js version: v$NODE_VER (>= 22 required)" + else + fail "Node.js version: v$NODE_VER (NEEDS >= 22)" \ + "Upgrade Node.js: nvm install 22 && nvm alias default 22" + fi +else + fail "Node.js is NOT installed" \ + "Install Node.js 22+: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash && nvm install 22" +fi + +# Check config exists +if [[ -f "$CONFIG_FILE" ]]; then + pass "Config file exists: $CONFIG_FILE" +else + fail "Config file NOT found at $CONFIG_FILE" \ + "Run: openclaw onboard --install-daemon" +fi + +# ============================================================================= +section "2. Gateway Configuration" +# ============================================================================= + +if [[ -f "$CONFIG_FILE" ]]; then + + # Check bind address + BIND_ADDR=$(grep -E '^\s*bind:' "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/.*bind:\s*//' | tr -d '"' | tr -d "'" | xargs) + if [[ -z "$BIND_ADDR" ]]; then + warn "Gateway bind address not explicitly set (may default to 0.0.0.0)" \ + "Add to config.yaml under gateway: bind: \"127.0.0.1\"" + elif [[ "$BIND_ADDR" == "127.0.0.1" || "$BIND_ADDR" == "localhost" ]]; then + pass "Gateway binds to loopback only: $BIND_ADDR" + elif [[ "$BIND_ADDR" == "0.0.0.0" ]]; then + fail "Gateway binds to ALL interfaces (0.0.0.0) — EXPOSED TO NETWORK!" \ + "CRITICAL: Change gateway.bind to \"127.0.0.1\" in $CONFIG_FILE immediately" + else + warn "Gateway binds to: $BIND_ADDR — verify this is intentional" \ + "Recommended: Set gateway.bind to \"127.0.0.1\" unless you have a specific reason" + fi + + # Check auth mode + AUTH_MODE=$(grep -E '^\s*mode:' "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/.*mode:\s*//' | tr -d '"' | tr -d "'" | xargs) + if [[ "$AUTH_MODE" == "password" ]]; then + pass "Gateway auth mode: password" + + # Check password strength + PASSWORD=$(grep -E '^\s*password:' "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/.*password:\s*//' | tr -d '"' | tr -d "'" | xargs) + if [[ -z "$PASSWORD" ]]; then + fail "Gateway password is EMPTY" \ + "Set a strong password: openclaw config set gateway.auth.password \"\$(openssl rand -base64 32)\"" + elif [[ ${#PASSWORD} -lt 16 ]]; then + warn "Gateway password is short (${#PASSWORD} chars, recommend 20+)" \ + "Set stronger password: openclaw config set gateway.auth.password \"\$(openssl rand -base64 32)\"" + else + pass "Gateway password length: ${#PASSWORD} chars" + fi + elif [[ -z "$AUTH_MODE" ]]; then + fail "Gateway auth mode NOT configured — WebChat/Control UI may be unprotected" \ + "Add to config.yaml: gateway.auth.mode: \"password\" and set a strong password" + else + warn "Gateway auth mode: $AUTH_MODE — verify this is secure" \ + "Recommended: Set gateway.auth.mode to \"password\"" + fi + + # Check DM policy + DM_POLICY=$(grep -E '^\s*dmPolicy:' "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/.*dmPolicy:\s*//' | tr -d '"' | tr -d "'" | xargs) + if [[ "$DM_POLICY" == "pairing" ]]; then + pass "DM policy: pairing (unknown senders must be approved)" + elif [[ "$DM_POLICY" == "open" ]]; then + fail "DM policy is OPEN — ANYONE can message your bot!" \ + "CRITICAL: Change dmPolicy to \"pairing\" in $CONFIG_FILE immediately" + elif [[ -z "$DM_POLICY" ]]; then + warn "DM policy not explicitly set (check default behavior)" \ + "Explicitly set dmPolicy: \"pairing\" in $CONFIG_FILE" + else + info "DM policy: $DM_POLICY" + fi + + # Check Tailscale mode + TS_MODE=$(grep -A5 "tailscale:" "$CONFIG_FILE" 2>/dev/null | grep -E '^\s*mode:' | head -1 | sed 's/.*mode:\s*//' | tr -d '"' | tr -d "'" | xargs) + if [[ "$TS_MODE" == "serve" ]]; then + pass "Tailscale mode: serve (tailnet-only, not public)" + elif [[ "$TS_MODE" == "funnel" ]]; then + fail "Tailscale mode is FUNNEL — Gateway is publicly accessible!" \ + "Change tailscale.mode to \"serve\" unless you absolutely need public access. If you do, ensure gateway.auth.mode is \"password\" with a strong password." + elif [[ -z "$TS_MODE" || "$TS_MODE" == "off" ]]; then + pass "Tailscale mode: off/unset (no Tailscale exposure)" + fi + + # Check for dangerous tools + SYSTEM_RUN=$(grep -A3 "system:" "$CONFIG_FILE" 2>/dev/null | grep -A1 "run:" | grep "enabled:" | sed 's/.*enabled:\s*//' | tr -d ' ' | head -1) + if [[ "$SYSTEM_RUN" == "true" ]]; then + fail "system.run tool is ENABLED — allows arbitrary command execution!" \ + "CRITICAL: Disable system.run in config.yaml: tools.system.run.enabled: false" + elif [[ "$SYSTEM_RUN" == "false" ]]; then + pass "system.run tool: disabled" + else + warn "system.run tool status unknown — verify manually" \ + "Explicitly set tools.system.run.enabled: false in $CONFIG_FILE" + fi + + BROWSER_ENABLED=$(grep -A2 "browser:" "$CONFIG_FILE" 2>/dev/null | grep "enabled:" | sed 's/.*enabled:\s*//' | tr -d ' ' | head -1) + if [[ "$BROWSER_ENABLED" == "true" ]]; then + warn "Browser control is ENABLED — agent can browse authenticated sessions" \ + "Disable browser unless needed: tools.browser.enabled: false" + elif [[ "$BROWSER_ENABLED" == "false" ]]; then + pass "Browser control: disabled" + else + warn "Browser control status unknown — verify manually" \ + "Explicitly set tools.browser.enabled: false in $CONFIG_FILE" + fi + +else + info "Skipping config checks — config file not found" +fi + +# ============================================================================= +section "3. File Permissions" +# ============================================================================= + +if [[ -d "$OPENCLAW_DIR" ]]; then + # Check ~/.openclaw directory permissions + DIR_PERMS=$(stat -c "%a" "$OPENCLAW_DIR" 2>/dev/null || stat -f "%Lp" "$OPENCLAW_DIR" 2>/dev/null) + if [[ "$DIR_PERMS" == "700" ]]; then + pass "~/.openclaw/ directory permissions: $DIR_PERMS (owner-only)" + else + fail "~/.openclaw/ directory permissions: $DIR_PERMS (should be 700)" \ + "Fix: chmod 700 ~/.openclaw" + fi + + # Check config file permissions + if [[ -f "$CONFIG_FILE" ]]; then + FILE_PERMS=$(stat -c "%a" "$CONFIG_FILE" 2>/dev/null || stat -f "%Lp" "$CONFIG_FILE" 2>/dev/null) + if [[ "$FILE_PERMS" == "600" ]]; then + pass "config.yaml permissions: $FILE_PERMS (owner read/write only)" + else + fail "config.yaml permissions: $FILE_PERMS (should be 600 — contains API keys!)" \ + "Fix: chmod 600 ~/.openclaw/config.yaml" + fi + fi + + # Check WhatsApp session directory + WA_DIR="$OPENCLAW_DIR/whatsapp" + if [[ -d "$WA_DIR" ]]; then + WA_PERMS=$(stat -c "%a" "$WA_DIR" 2>/dev/null || stat -f "%Lp" "$WA_DIR" 2>/dev/null) + if [[ "$WA_PERMS" == "700" ]]; then + pass "WhatsApp session dir permissions: $WA_PERMS" + else + warn "WhatsApp session dir permissions: $WA_PERMS (should be 700)" \ + "Fix: chmod -R 700 ~/.openclaw/whatsapp" + fi + fi +else + info "~/.openclaw/ directory not found — skipping permission checks" +fi + +# Check running as root +if [[ "$(id -u)" == "0" ]]; then + fail "Running as ROOT — never run OpenClaw as root!" \ + "Switch to a regular user: su - yourusername" +else + pass "Not running as root: $(whoami)" +fi + +# ============================================================================= +section "4. Network Security" +# ============================================================================= + +# Check if Gateway port is listening +GW_PORT=18789 +if [[ -f "$CONFIG_FILE" ]]; then + CUSTOM_PORT=$(grep -E '^\s*port:' "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/.*port:\s*//' | tr -d ' ') + if [[ -n "$CUSTOM_PORT" && "$CUSTOM_PORT" =~ ^[0-9]+$ ]]; then + GW_PORT=$CUSTOM_PORT + fi +fi + +if command -v ss &>/dev/null; then + LISTEN_ADDR=$(ss -tlnp 2>/dev/null | grep ":${GW_PORT}" | awk '{print $4}' | head -1) +elif command -v lsof &>/dev/null; then + LISTEN_ADDR=$(lsof -iTCP:${GW_PORT} -sTCP:LISTEN -P -n 2>/dev/null | awk 'NR>1{print $9}' | head -1) +else + LISTEN_ADDR="" +fi + +if [[ -n "$LISTEN_ADDR" ]]; then + if echo "$LISTEN_ADDR" | grep -q "127.0.0.1\|localhost\|\[::1\]"; then + pass "Gateway listening on loopback only: $LISTEN_ADDR" + elif echo "$LISTEN_ADDR" | grep -q "0.0.0.0\|\*:\|\[::\]"; then + fail "Gateway listening on ALL interfaces: $LISTEN_ADDR" \ + "CRITICAL: Change gateway.bind to \"127.0.0.1\" and restart OpenClaw" + else + warn "Gateway listening on: $LISTEN_ADDR — verify this is intentional" \ + "Recommended: Bind to 127.0.0.1 unless you have a specific reason" + fi +else + info "Gateway not currently running on port $GW_PORT (or cannot detect)" +fi + +# Check SSH +if [[ "$OS" == "wsl2" || "$OS" == "linux" ]]; then + if systemctl is-active ssh &>/dev/null 2>&1 || systemctl is-active sshd &>/dev/null 2>&1; then + warn "SSH service is RUNNING — disable if not needed" \ + "Disable SSH: sudo systemctl disable --now ssh" + else + pass "SSH service: not running" + fi +fi + +# Check UFW (Linux/WSL2) +if [[ "$OS" == "wsl2" || "$OS" == "linux" ]]; then + if command -v ufw &>/dev/null; then + UFW_STATUS=$(sudo ufw status 2>/dev/null | head -1 || echo "unknown") + if echo "$UFW_STATUS" | grep -q "active"; then + pass "UFW firewall: active" + else + warn "UFW firewall: inactive" \ + "Enable: sudo ufw default deny incoming && sudo ufw allow from 127.0.0.1 to any port $GW_PORT && sudo ufw enable" + fi + else + warn "UFW not installed" \ + "Install: sudo apt install -y ufw && sudo ufw default deny incoming && sudo ufw enable" + fi +fi + +# Check macOS firewall +if [[ "$OS" == "macos" ]]; then + FW_STATE=$(sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate 2>/dev/null | grep -o "enabled\|disabled" || echo "unknown") + if [[ "$FW_STATE" == "enabled" ]]; then + pass "macOS firewall: enabled" + elif [[ "$FW_STATE" == "disabled" ]]; then + warn "macOS firewall: disabled" \ + "Enable: System Settings → Network → Firewall → Turn On" + else + info "Could not check macOS firewall state" + fi +fi + +# ============================================================================= +section "5. API Key Security" +# ============================================================================= + +if [[ -f "$CONFIG_FILE" ]]; then + # Check for hardcoded API keys + if grep -qE 'sk-[a-zA-Z0-9]{20,}' "$CONFIG_FILE" 2>/dev/null; then + warn "Hardcoded OpenAI API key found in config.yaml" \ + "Prefer OAuth login: openclaw auth login openai (keys in config are readable by anyone with file access)" + else + pass "No hardcoded OpenAI API key in config.yaml" + fi + + if grep -qE 'sk-ant-[a-zA-Z0-9]{20,}' "$CONFIG_FILE" 2>/dev/null; then + warn "Hardcoded Anthropic API key found in config.yaml" \ + "Prefer OAuth login: openclaw auth login anthropic" + else + pass "No hardcoded Anthropic API key in config.yaml" + fi + + # Check if config is in a git repo + if git -C "$OPENCLAW_DIR" rev-parse --is-inside-work-tree &>/dev/null; then + fail "~/.openclaw/ is inside a git repo — API keys may be committed!" \ + "Add .openclaw/ to .gitignore immediately: echo '.openclaw/' >> ~/.gitignore_global" + else + pass "~/.openclaw/ is NOT inside a git repo" + fi +fi + +# ============================================================================= +section "6. System Security" +# ============================================================================= + +# Check OS updates +if [[ "$OS" == "wsl2" || "$OS" == "linux" ]]; then + UPGRADABLE=$(apt list --upgradable 2>/dev/null | grep -c "upgradable" || echo "0") + if [[ "$UPGRADABLE" -gt 10 ]]; then + warn "$UPGRADABLE packages have available updates" \ + "Update: sudo apt update && sudo apt upgrade -y" + elif [[ "$UPGRADABLE" -gt 0 ]]; then + info "$UPGRADABLE packages have available updates" + else + pass "System packages: up to date" + fi +elif [[ "$OS" == "macos" ]]; then + BREW_OUTDATED=$(brew outdated 2>/dev/null | wc -l | tr -d ' ') + if [[ "$BREW_OUTDATED" -gt 10 ]]; then + warn "$BREW_OUTDATED Homebrew packages are outdated" \ + "Update: brew update && brew upgrade" + else + pass "Homebrew packages: mostly up to date ($BREW_OUTDATED outdated)" + fi +fi + +# Check WSL2 systemd +if [[ "$OS" == "wsl2" ]]; then + if [[ -f /etc/wsl.conf ]] && grep -q "systemd=true" /etc/wsl.conf 2>/dev/null; then + pass "WSL2 systemd: enabled (daemon auto-start works)" + else + warn "WSL2 systemd not enabled — OpenClaw daemon won't auto-start" \ + "Add [boot] systemd=true to /etc/wsl.conf and restart WSL (wsl --shutdown)" + fi +fi + +# Check for open ports +if command -v ss &>/dev/null; then + OPEN_PORTS=$(ss -tlnp 2>/dev/null | grep -c "0.0.0.0\|\[::\]" || echo "0") + if [[ "$OPEN_PORTS" -gt 5 ]]; then + warn "$OPEN_PORTS services listening on all interfaces" \ + "Review open ports: ss -tlnp | grep '0.0.0.0' — close anything you don't need" + else + pass "Open ports on all interfaces: $OPEN_PORTS (reasonable)" + fi +elif command -v lsof &>/dev/null; then + OPEN_PORTS=$(lsof -iTCP -sTCP:LISTEN -P -n 2>/dev/null | grep -c "\*:" || echo "0") + if [[ "$OPEN_PORTS" -gt 5 ]]; then + warn "$OPEN_PORTS services listening on all interfaces" \ + "Review open ports: lsof -iTCP -sTCP:LISTEN -P -n | grep '\\*:' — close anything you don't need" + else + pass "Open ports on all interfaces: $OPEN_PORTS (reasonable)" + fi +fi + +# ============================================================================= +section "7. OpenClaw Doctor" +# ============================================================================= + +if command -v openclaw &>/dev/null; then + info "Running 'openclaw doctor' for built-in health check..." + echo "" + openclaw doctor 2>&1 | sed 's/^/ /' || warn "openclaw doctor failed" "Run manually: openclaw doctor" + echo "" +else + info "Skipping — OpenClaw not installed" +fi + +# ============================================================================= +# SUMMARY +# ============================================================================= + +echo "" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e "${BOLD} SECURITY SCAN SUMMARY${NC}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo "" + +TOTAL=$((PASS + FAIL + WARN)) +echo -e " ${GREEN}✅ Passed: $PASS${NC}" +echo -e " ${RED}❌ Failed: $FAIL${NC}" +echo -e " ${YELLOW}⚠️ Warnings: $WARN${NC}" +echo -e " ─────────────────" +echo -e " ${BOLD}Total checks: $TOTAL${NC}" +echo "" + +if [[ $FAIL -eq 0 && $WARN -eq 0 ]]; then + echo -e " ${GREEN}${BOLD}🎉 ALL CLEAR — Your OpenClaw setup is secure!${NC}" +elif [[ $FAIL -eq 0 ]]; then + echo -e " ${YELLOW}${BOLD}⚠️ MOSTLY SECURE — Review warnings below${NC}" +elif [[ $FAIL -le 2 ]]; then + echo -e " ${RED}${BOLD}🔴 ACTION REQUIRED — Fix the issues below before going live${NC}" +else + echo -e " ${RED}${BOLD}🚨 CRITICAL — Multiple security issues detected!${NC}" +fi + +# Print recommendations +if [[ ${#RECOMMENDATIONS[@]} -gt 0 ]]; then + echo "" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BOLD} RECOMMENDATIONS (fix in order)${NC}" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "" + + REC_NUM=1 + for rec in "${RECOMMENDATIONS[@]}"; do + echo -e " ${BOLD}${REC_NUM}.${NC} $rec" + echo "" + ((REC_NUM++)) + done +fi + +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e " Run this script again after fixing issues: ${BOLD}bash validate-security.sh${NC}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo "" + +# Exit with non-zero if any failures +if [[ $FAIL -gt 0 ]]; then + exit 1 +fi +exit 0