#!/bin/bash # # Security Test Suite for Contact Form # Tests all security features against a live server # # Usage: # ./security_tests.sh [SERVER_URL] # # Example: # ./security_tests.sh http://localhost:8080 # ./security_tests.sh https://cv.example.com # set -e # Exit on error # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Server URL (default to localhost) SERVER_URL="${1:-http://localhost:8080}" API_URL="${SERVER_URL}/api/contact" # Test counters TOTAL_TESTS=0 PASSED_TESTS=0 FAILED_TESTS=0 # Helper functions print_header() { echo "" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${BLUE}$1${NC}" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" } print_test() { echo -e "${YELLOW}→ $1${NC}" } print_pass() { echo -e "${GREEN}✓ $1${NC}" ((PASSED_TESTS++)) } print_fail() { echo -e "${RED}✗ $1${NC}" ((FAILED_TESTS++)) } test_result() { local test_name="$1" local expected_code="$2" local actual_code="$3" local response="$4" ((TOTAL_TESTS++)) if [ "$actual_code" -eq "$expected_code" ]; then print_pass "$test_name (expected $expected_code, got $actual_code)" return 0 else print_fail "$test_name (expected $expected_code, got $actual_code)" echo " Response: ${response:0:200}" return 1 fi } # Calculate submit time (5 seconds ago) get_submit_time() { echo $(( $(date +%s) * 1000 - 5000 )) } # Get submit time that's too fast (500ms ago) get_fast_submit_time() { echo $(( $(date +%s) * 1000 - 500 )) } # Check if server is running check_server() { print_header "Checking Server Availability" if curl -s -o /dev/null -w "%{http_code}" "$SERVER_URL" > /dev/null 2>&1; then print_pass "Server is running at $SERVER_URL" return 0 else print_fail "Server is not accessible at $SERVER_URL" echo "Please start the server first: go run main.go" exit 1 fi } # Test 1: Browser-Only Middleware - Block curl test_block_curl() { print_header "Test 1: Browser-Only Middleware - Block HTTP Clients" # Test curl (default user agent) print_test "Testing curl blocking" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Block curl" 403 "$http_code" "$body" # Test wget user agent print_test "Testing wget blocking" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Wget/1.20.3" \ -d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Block wget" 403 "$http_code" "$body" # Test postman print_test "Testing Postman blocking" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: PostmanRuntime/7.26.8" \ -d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Block Postman" 403 "$http_code" "$body" # Test python requests print_test "Testing Python requests blocking" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: python-requests/2.25.1" \ -d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Block Python requests" 403 "$http_code" "$body" # Test empty user agent print_test "Testing empty User-Agent blocking" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent:" \ -d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Block empty User-Agent" 403 "$http_code" "$body" } # Test 2: Browser-Only Middleware - Require Referer/Origin test_require_referer_origin() { print_header "Test 2: Browser-Only Middleware - Referer/Origin Headers" # Test without Referer or Origin print_test "Testing request without Referer/Origin (should block)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \ -H "HX-Request: true" \ -d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Block without Referer/Origin" 403 "$http_code" "$body" # Test with Referer print_test "Testing request with Referer (should allow)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=test@example.com&name=Test&subject=Test&message=Test message here&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Allow with Referer" 200 "$http_code" "$body" # Test with Origin print_test "Testing request with Origin (should allow)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \ -H "Origin: ${SERVER_URL}" \ -H "HX-Request: true" \ -d "email=test2@example.com&name=Test2&subject=Test&message=Another test message&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Allow with Origin" 200 "$http_code" "$body" } # Test 3: Browser-Only Middleware - Require Browser Headers test_require_browser_headers() { print_header "Test 3: Browser-Only Middleware - Browser Headers" # Test without browser headers print_test "Testing request without browser headers (should block)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \ -H "Referer: ${SERVER_URL}/" \ -d "email=test@example.com&name=Test&subject=Test&message=Test message&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Block without browser headers" 403 "$http_code" "$body" # Test with HX-Request header print_test "Testing request with HX-Request header (should allow)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=test3@example.com&name=Test3&subject=Test&message=Message with HX header&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Allow with HX-Request" 200 "$http_code" "$body" # Test with X-Requested-With header print_test "Testing request with X-Requested-With header (should allow)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \ -H "Referer: ${SERVER_URL}/" \ -H "X-Requested-With: XMLHttpRequest" \ -d "email=test4@example.com&name=Test4&subject=Test&message=Message with XMLHttpRequest header&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Allow with X-Requested-With" 200 "$http_code" "$body" # Test with X-Browser-Request header print_test "Testing request with X-Browser-Request header (should allow)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" \ -H "Referer: ${SERVER_URL}/" \ -H "X-Browser-Request: true" \ -d "email=test5@example.com&name=Test5&subject=Test&message=Message with browser request header&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Allow with X-Browser-Request" 200 "$http_code" "$body" } # Test 4: Input Validation - Email Format test_email_validation() { print_header "Test 4: Input Validation - Email Format" # Valid email print_test "Testing valid email" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=valid@example.com&name=Test&subject=Test&message=Valid email test message&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Valid email accepted" 200 "$http_code" "$body" # Invalid email - no @ print_test "Testing invalid email (no @)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=notanemail&name=Test&subject=Test&message=Invalid email test&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Invalid email rejected (no @)" 400 "$http_code" "$body" # Invalid email - no domain print_test "Testing invalid email (no domain)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=test@&name=Test&subject=Test&message=Invalid email test&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Invalid email rejected (no domain)" 400 "$http_code" "$body" # Empty email print_test "Testing empty email" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=&name=Test&subject=Test&message=Empty email test&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Empty email rejected" 400 "$http_code" "$body" } # Test 5: Input Validation - Message Length test_message_validation() { print_header "Test 5: Input Validation - Message Length" # Valid message print_test "Testing valid message length" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=test@example.com&name=Test&subject=Test&message=This is a valid message with sufficient length.&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Valid message accepted" 200 "$http_code" "$body" # Empty message print_test "Testing empty message" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=test@example.com&name=Test&subject=Test&message=&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Empty message rejected" 400 "$http_code" "$body" # Too long message (> 5000 chars) print_test "Testing message too long (>5000 chars)" long_message=$(printf 'a%.0s' {1..5001}) response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=test@example.com&name=Test&subject=Test&message=${long_message}&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Too long message rejected" 400 "$http_code" "$body" } # Test 6: Bot Protection - Honeypot test_honeypot() { print_header "Test 6: Bot Protection - Honeypot Field" # Honeypot empty (human) print_test "Testing honeypot empty (human behavior)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=human@example.com&name=Human&subject=Test&message=I am a real human user.&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Honeypot empty - accepted" 200 "$http_code" "$body" # Honeypot filled (bot) print_test "Testing honeypot filled (bot behavior)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=bot@example.com&name=Bot&subject=Spam&message=This is spam!&website=http://spam-site.com&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) # Honeypot returns 200 to fool bots, but doesn't actually send email test_result "Honeypot filled - fake success (bot fooled)" 200 "$http_code" "$body" } # Test 7: Bot Protection - Timing test_timing_validation() { print_header "Test 7: Bot Protection - Form Submission Timing" # Valid timing (5 seconds) print_test "Testing normal submission timing (5 seconds)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=slow@example.com&name=Slow&subject=Test&message=I took my time filling this out.&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Normal timing accepted" 200 "$http_code" "$body" # Too fast (< 2 seconds) print_test "Testing fast submission (< 2 seconds)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=fast@example.com&name=Fast&subject=Test&message=I submitted instantly!&website=&submit_time=$(get_fast_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Too fast submission rejected" 400 "$http_code" "$body" } # Test 8: Rate Limiting test_rate_limiting() { print_header "Test 8: Rate Limiting (5 requests per hour)" echo "Sending 6 requests rapidly..." for i in {1..6}; do print_test "Request #$i" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=rate${i}@example.com&name=RateTest${i}&subject=Test&message=Rate limit test message number ${i}.&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) if [ "$i" -le 5 ]; then # First 5 should succeed test_result "Request $i allowed" 200 "$http_code" "$body" else # 6th should be rate limited test_result "Request $i rate limited" 429 "$http_code" "$body" fi # Small delay between requests sleep 0.1 done echo "" echo -e "${YELLOW}Note: Rate limit resets after 1 hour${NC}" } # Test 9: Real-World Attack Scenarios test_attack_scenarios() { print_header "Test 9: Real-World Attack Scenarios" # Scenario 1: Sophisticated bot with browser-like headers but honeypot filled print_test "Scenario 1: Sophisticated bot (browser headers + honeypot)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=smartbot@example.com&name=SmartBot&subject=Offer&message=Check out our amazing product!&website=http://smart-bot.com&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Smart bot caught by honeypot" 200 "$http_code" "$body" # Scenario 2: Script kiddie with curl print_test "Scenario 2: Script kiddie using curl" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "email=hacker@example.com&name=Hacker&subject=Pwned&message=You got hacked!&website=&submit_time=$(get_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Script kiddie blocked by BrowserOnly" 403 "$http_code" "$body" # Scenario 3: Automated form filler (fast submission) print_test "Scenario 3: Automated form filler (too fast)" response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: ${SERVER_URL}/" \ -H "HX-Request: true" \ -d "email=autobot@example.com&name=AutoBot&subject=Auto&message=Automatically filled form!&website=&submit_time=$(get_fast_submit_time)") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | head -n-1) test_result "Auto-filler caught by timing check" 400 "$http_code" "$body" } # Summary print_summary() { print_header "Test Summary" echo "" echo -e "Total Tests: ${BLUE}${TOTAL_TESTS}${NC}" echo -e "Passed: ${GREEN}${PASSED_TESTS}${NC}" echo -e "Failed: ${RED}${FAILED_TESTS}${NC}" echo "" if [ "$FAILED_TESTS" -eq 0 ]; then echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${GREEN}✓ ALL SECURITY TESTS PASSED!${NC}" echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" exit 0 else echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${RED}✗ SOME TESTS FAILED${NC}" echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" exit 1 fi } # Main execution main() { clear echo -e "${BLUE}" echo "╔══════════════════════════════════════════════════════════════════════╗" echo "║ ║" echo "║ CONTACT FORM SECURITY TEST SUITE ║" echo "║ ║" echo "╚══════════════════════════════════════════════════════════════════════╝" echo -e "${NC}" echo "" echo "Server: $SERVER_URL" echo "Endpoint: $API_URL" echo "" check_server test_block_curl test_require_referer_origin test_require_browser_headers test_email_validation test_message_validation test_honeypot test_timing_validation test_rate_limiting test_attack_scenarios print_summary } # Run main main