#!/bin/sh # ═══════════════════════════════════════════════════════════════════════ # Ghaymah Pre-Deploy Scanner — Exhaustive QA Test Suite # 16 scenarios covering baseline + deep edge cases # ═══════════════════════════════════════════════════════════════════════ SCANNER="/root/ghaymah-v2-test/Scripts/gy-scanner.py" TB="/tmp/gy-scanner-tests" PASS=0 FAIL=0 TOTAL=0 RED='\033[1;31m' GRN='\033[1;32m' YLW='\033[1;33m' CYN='\033[1;36m' RST='\033[0m' rm -rf "$TB"; mkdir -p "$TB" echo "" echo "═══════════════════════════════════════════════════════════════════" echo " 🛡️ Ghaymah Pre-Deploy Scanner — Exhaustive QA Test Suite" echo " 📅 $(date -u)" echo "═══════════════════════════════════════════════════════════════════" echo "" run_test() { name="$1"; dir="$2"; expect="$3" TOTAL=$((TOTAL+1)) printf " 🧪 TEST #%02d: %-55s " "$TOTAL" "$name" python3 "$SCANNER" --strict "$dir" >/dev/null 2>&1 got=$? if [ "$got" -eq "$expect" ]; then printf "${GRN}PASSED${RST} (exit=%d)\n" "$got" PASS=$((PASS+1)) else printf "${RED}FAILED${RST} (expected=%d got=%d)\n" "$expect" "$got" FAIL=$((FAIL+1)) fi } verbose() { name="$1"; dir="$2"; mode="$3" echo "" echo " ┌─────────────────────────────────────────────────────────────" echo " │ 🔬 VERBOSE: $name" echo " │ 📁 $dir" echo " └─────────────────────────────────────────────────────────────" flags="" [ "$mode" = "json" ] && flags="--json" python3 "$SCANNER" $flags "$dir" 2>&1 echo " Exit Code: $?" echo " ─────────────────────────────────────────────────────────────" } # ══════════════ PART 1: BASELINE ══════════════ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " 📋 PART 1: BASELINE AUTOMATED TESTS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # 1: Secure App D="$TB/secure"; mkdir -p "$D" cat > "$D/Dockerfile" <<'E' FROM alpine:3.18 RUN addgroup --system nonroot && adduser --system nonroot USER nonroot COPY src/ /app/ EXPOSE 8080 E printf ".env\n.git\n*.pem\n*.key\n" > "$D/.dockerignore" run_test "Secure App (all pass)" "$D" 0 # 2: Env Leak D="$TB/envleak"; mkdir -p "$D" echo "SECRET=123" > "$D/.env" cat > "$D/Dockerfile" <<'E' FROM alpine:3.18 COPY . /app USER nonroot E run_test "Env Leak (COPY . + no .dockerignore)" "$D" 1 # 3: Env Poisoning D="$TB/poison"; mkdir -p "$D" printf 'MAL=$(id)\nBAD=`whoami`\n' > "$D/.env" cat > "$D/Dockerfile" <<'E' FROM alpine:3.18 USER nonroot EXPOSE 8080 E run_test "Env Poisoning (shell substitution)" "$D" 1 # 4: Unclosed Quotes D="$TB/unclosed"; mkdir -p "$D" echo 'BROKEN="quote' > "$D/.env" cat > "$D/Dockerfile" <<'E' FROM alpine:3.18 USER nonroot E run_test "Unclosed Quotes (DoS vector)" "$D" 1 # ══════════════ PART 2: DEEP EDGE CASES ══════════════ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " 📋 PART 2: DEEP EDGE-CASE SCENARIOS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # A: Sneaky Dev (path traversal + string port) D="$TB/sneaky"; mkdir -p "$D" cat > "$D/.gy.json" <<'E' {"app":"../../../etc/passwd","port":"8080","project":"legit","tier":"t1","env":{"NODE_ENV":"production"}} E cat > "$D/Dockerfile" <<'E' FROM node:20-alpine COPY src/ /app/ USER nonroot EXPOSE 3000 E run_test "Scenario A: Sneaky Dev (path traversal)" "$D" 1 # B: DoS Attempt (10k string + unclosed quote) D="$TB/dos"; mkdir -p "$D" python3 -c "print('PAYLOAD=\"' + 'A'*10000)" > "$D/.env" echo 'OK=fine' >> "$D/.env" cat > "$D/Dockerfile" <<'E' FROM python:3.12-slim USER nonroot EXPOSE 8000 E run_test "Scenario B: DoS (10k char + unclosed quote)" "$D" 1 # C: Symlink Attack (Dockerfile → /etc/shadow) D="$TB/symlink"; mkdir -p "$D" ln -sf /etc/shadow "$D/Dockerfile" echo "NORMAL=val" > "$D/.env" run_test "Scenario C: Symlink Attack (Dockerfile→/etc/shadow)" "$D" 1 # D: False Positive (legit complex .env) D="$TB/falsep"; mkdir -p "$D" cat > "$D/.env" <<'E' DATABASE_URL=postgres://user:p@ssw0rd@db.example.com:5432/mydb?sslmode=require&connect_timeout=10 REDIS_URL=redis://default:abc123@redis.internal:6379/0 API_ENDPOINT=https://api.example.com/v2/webhooks?token=abc123&format=json FEATURE_FLAGS={"darkMode":true,"beta":false} NORMAL_STRING=hello world PORT=8080 E cat > "$D/Dockerfile" <<'E' FROM node:20-alpine COPY package.json yarn.lock ./ RUN yarn --frozen-lockfile COPY src/ ./src/ RUN addgroup --system nonroot && adduser --system nonroot USER nonroot EXPOSE 3000 E printf ".env\n.git\n*.pem\n*.key\n" > "$D/.dockerignore" run_test "Scenario D: False Positive (legit complex .env)" "$D" 0 # E: Missing FROM D="$TB/nofrom"; mkdir -p "$D" cat > "$D/Dockerfile" <<'E' COPY . /app RUN echo "no FROM" E run_test "Scenario E: Missing FROM Instruction" "$D" 1 # F: Hardcoded Secrets D="$TB/secrets"; mkdir -p "$D" cat > "$D/Dockerfile" <<'E' FROM alpine:3.18 ENV API_KEY=sk-live-abc123def456 ENV DB_PASSWORD=SuperSecret123 USER nonroot EXPOSE 8080 E run_test "Scenario F: Hardcoded Secrets in ENV" "$D" 1 # G: curl piped to bash D="$TB/curlpipe"; mkdir -p "$D" cat > "$D/Dockerfile" <<'E' FROM ubuntu:22.04 RUN curl -fsSL https://example.com/install.sh | bash - USER nonroot EXPOSE 3000 E run_test "Scenario G: curl piped to bash" "$D" 1 # H: chmod 777 D="$TB/chmod"; mkdir -p "$D" cat > "$D/Dockerfile" <<'E' FROM alpine:3.18 COPY . /app RUN chmod 777 /app USER nonroot EXPOSE 8080 E run_test "Scenario H: chmod 777" "$D" 1 # I: Invalid EXPOSE port D="$TB/badport"; mkdir -p "$D" cat > "$D/Dockerfile" <<'E' FROM alpine:3.18 USER nonroot EXPOSE 99999 E run_test "Scenario I: EXPOSE port 99999 (out of range)" "$D" 1 # J: Control characters D="$TB/ctrl"; mkdir -p "$D" printf 'INJECT=value\x01\x02hidden' > "$D/.env" cat > "$D/Dockerfile" <<'E' FROM alpine:3.18 USER nonroot EXPOSE 8080 E run_test "Scenario J: Control chars in env value" "$D" 1 # K: No Dockerfile (auto-gen) D="$TB/nodf"; mkdir -p "$D" echo '{"app":"my-app","port":3000}' > "$D/.gy.json" run_test "Scenario K: No Dockerfile (auto-gen, pass)" "$D" 0 # L: Weak .dockerignore D="$TB/weakdi"; mkdir -p "$D" echo "node_modules" > "$D/.dockerignore" echo "DB_SECRET=hunter2" > "$D/.env" cat > "$D/Dockerfile" <<'E' FROM node:20-alpine COPY . /app USER nonroot EXPOSE 3000 E run_test "Scenario L: .dockerignore missing .env" "$D" 1 # ══════════════ PART 3: VERBOSE OUTPUT ══════════════ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " 📋 PART 3: VERBOSE OUTPUTS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" verbose "Scenario 1: Secure App" "$TB/secure" "human" verbose "Scenario A: Sneaky Dev" "$TB/sneaky" "json" verbose "Scenario B: DoS Attempt" "$TB/dos" "human" verbose "Scenario C: Symlink Attack" "$TB/symlink" "human" verbose "Scenario D: False Positive" "$TB/falsep" "human" # ══════════════ FINAL SUMMARY ══════════════ echo "" echo "═══════════════════════════════════════════════════════════════════" echo " 📊 FINAL RESULTS" echo "═══════════════════════════════════════════════════════════════════" echo "" echo " Total Tests: $TOTAL" printf " ${GRN}Passed: %d${RST}\n" "$PASS" printf " ${RED}Failed: %d${RST}\n" "$FAIL" echo "" if [ "$FAIL" -eq 0 ]; then printf " ${GRN}✅ ALL %d TESTS PASSED — Scanner is production-ready.${RST}\n" "$TOTAL" else printf " ${RED}⛔ %d TEST(S) FAILED — Review required.${RST}\n" "$FAIL" fi echo "" echo "═══════════════════════════════════════════════════════════════════" rm -rf "$TB" exit $FAIL