← back to Watches
scripts/security-audit.sh
535 lines
#!/bin/bash
###############################################################################
# COMPREHENSIVE SECURITY AUDIT SCRIPT
# Omega Watch Price History Platform
###############################################################################
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
PROJECT_DIR="/root/Projects/watches"
PORT=7600
HOST="localhost"
BASE_URL="http://$HOST:$PORT"
REPORT_DIR="$PROJECT_DIR/logs/security-audits"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
REPORT_FILE="$REPORT_DIR/audit_$TIMESTAMP.txt"
# Create report directory
mkdir -p "$REPORT_DIR"
# Initialize report
echo "╔════════════════════════════════════════════════════════════════╗" | tee "$REPORT_FILE"
echo "║ SECURITY AUDIT REPORT - OMEGA WATCHES PLATFORM ║" | tee -a "$REPORT_FILE"
echo "╚════════════════════════════════════════════════════════════════╝" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
echo "Date: $(date)" | tee -a "$REPORT_FILE"
echo "Target: $BASE_URL" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
###############################################################################
# Helper Functions
###############################################################################
print_section() {
echo "" | tee -a "$REPORT_FILE"
echo "═══════════════════════════════════════════════════════════════" | tee -a "$REPORT_FILE"
echo "$1" | tee -a "$REPORT_FILE"
echo "═══════════════════════════════════════════════════════════════" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
}
print_test() {
echo -e "${BLUE}[TEST]${NC} $1" | tee -a "$REPORT_FILE"
}
print_pass() {
echo -e "${GREEN}[PASS]${NC} $1" | tee -a "$REPORT_FILE"
}
print_fail() {
echo -e "${RED}[FAIL]${NC} $1" | tee -a "$REPORT_FILE"
}
print_warn() {
echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$REPORT_FILE"
}
print_info() {
echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$REPORT_FILE"
}
###############################################################################
# Test 1: Server Accessibility
###############################################################################
print_section "1. SERVER ACCESSIBILITY TEST"
print_test "Checking if server is running on port $PORT..."
if netstat -tulnp 2>/dev/null | grep -q ":$PORT "; then
print_pass "Server is running on port $PORT"
else
print_fail "Server is NOT running on port $PORT"
echo "Please start the server first: pm2 start server-enterprise-security.js"
exit 1
fi
print_test "Testing HTTP connectivity..."
if curl -s -f -o /dev/null "$BASE_URL/api/health"; then
print_pass "HTTP connectivity successful"
else
print_fail "Cannot connect to $BASE_URL"
exit 1
fi
###############################################################################
# Test 2: Security Headers
###############################################################################
print_section "2. SECURITY HEADERS TEST"
print_test "Fetching security headers..."
HEADERS=$(curl -s -I "$BASE_URL/api/health")
# Check for required headers
check_header() {
local header=$1
local description=$2
if echo "$HEADERS" | grep -qi "$header:"; then
local value=$(echo "$HEADERS" | grep -i "$header:" | cut -d: -f2- | xargs)
print_pass "$description: $value"
return 0
else
print_fail "$description is MISSING"
return 1
fi
}
check_header "Strict-Transport-Security" "HSTS Header"
check_header "X-Frame-Options" "X-Frame-Options Header"
check_header "X-Content-Type-Options" "X-Content-Type-Options Header"
check_header "X-XSS-Protection" "X-XSS-Protection Header"
check_header "Content-Security-Policy" "CSP Header"
check_header "Referrer-Policy" "Referrer-Policy Header"
# Check for headers that should NOT be present
if echo "$HEADERS" | grep -qi "X-Powered-By:"; then
print_warn "X-Powered-By header present (information disclosure)"
else
print_pass "X-Powered-By header not present"
fi
###############################################################################
# Test 3: SQL Injection Protection
###############################################################################
print_section "3. SQL INJECTION PROTECTION TEST"
print_test "Testing SQL injection in search query..."
# Test various SQL injection patterns
sql_payloads=(
"' OR '1'='1"
"'; DROP TABLE watches; --"
"' UNION SELECT * FROM users --"
"1' AND '1'='1"
"admin'--"
)
for payload in "${sql_payloads[@]}"; do
response=$(curl -s "$BASE_URL/api/search?q=$(echo "$payload" | jq -sRr @uri)" || echo "ERROR")
if echo "$response" | grep -qi "error\|syntax"; then
if echo "$response" | grep -qi "validation"; then
print_pass "Payload blocked: $payload"
else
print_warn "Payload may have caused error: $payload"
fi
else
print_pass "Payload sanitized: $payload"
fi
done
###############################################################################
# Test 4: XSS Protection
###############################################################################
print_section "4. XSS PROTECTION TEST"
print_test "Testing XSS protection..."
xss_payloads=(
"<script>alert(1)</script>"
"<img src=x onerror=alert(1)>"
"javascript:alert(1)"
"<svg onload=alert(1)>"
)
for payload in "${xss_payloads[@]}"; do
response=$(curl -s -X POST "$BASE_URL/api/watchlist" \
-H "Content-Type: application/json" \
-d "{\"userId\":\"$payload\",\"watchId\":\"test\",\"action\":\"add\"}" 2>/dev/null || echo "ERROR")
if echo "$response" | grep -q "<script>\|javascript:\|onerror"; then
print_fail "XSS payload NOT sanitized: $payload"
else
print_pass "XSS payload sanitized: $payload"
fi
done
###############################################################################
# Test 5: Rate Limiting
###############################################################################
print_section "5. RATE LIMITING TEST"
print_test "Testing API rate limiting (100 requests / 15 min)..."
success_count=0
rate_limited=false
for i in {1..120}; do
response=$(curl -s -w "%{http_code}" -o /dev/null "$BASE_URL/api/health" 2>/dev/null)
if [ "$response" = "200" ]; then
((success_count++))
elif [ "$response" = "429" ]; then
rate_limited=true
break
fi
# Small delay to avoid overwhelming the server
sleep 0.05
done
if [ "$rate_limited" = true ]; then
print_pass "Rate limiting active (blocked after $success_count requests)"
else
if [ $success_count -gt 100 ]; then
print_warn "Rate limiting may not be working correctly ($success_count requests succeeded)"
else
print_info "Rate limiting not triggered ($success_count < 100 requests)"
fi
fi
###############################################################################
# Test 6: Authentication
###############################################################################
print_section "6. AUTHENTICATION TEST"
print_test "Testing unauthenticated admin access..."
response=$(curl -s -w "%{http_code}" -o /dev/null "$BASE_URL/api/admin/security-logs")
if [ "$response" = "401" ] || [ "$response" = "403" ]; then
print_pass "Admin endpoint requires authentication (HTTP $response)"
else
print_fail "Admin endpoint accessible without authentication (HTTP $response)"
fi
print_test "Testing authentication with invalid credentials..."
response=$(curl -s -X POST "$BASE_URL/api/admin/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"wrongpassword"}')
if echo "$response" | grep -qi "unauthorized\|invalid"; then
print_pass "Invalid credentials rejected"
else
print_fail "Authentication may be compromised"
fi
###############################################################################
# Test 7: CORS Protection
###############################################################################
print_section "7. CORS PROTECTION TEST"
print_test "Testing CORS headers..."
cors_headers=$(curl -s -I -H "Origin: http://malicious-site.com" "$BASE_URL/api/health")
if echo "$cors_headers" | grep -qi "Access-Control-Allow-Origin"; then
allowed_origin=$(echo "$cors_headers" | grep -i "Access-Control-Allow-Origin:" | cut -d: -f2- | xargs)
if [ "$allowed_origin" = "*" ]; then
print_warn "CORS allows all origins (*)"
else
print_pass "CORS is restricted: $allowed_origin"
fi
else
print_info "CORS headers not present (may be configured elsewhere)"
fi
###############################################################################
# Test 8: Input Validation
###############################################################################
print_section "8. INPUT VALIDATION TEST"
print_test "Testing input validation on search endpoint..."
# Test invalid page number
response=$(curl -s "$BASE_URL/api/search?page=999999")
if echo "$response" | grep -qi "error\|validation"; then
print_pass "Large page number handled correctly"
else
print_info "Large page number accepted (may be valid)"
fi
# Test negative numbers
response=$(curl -s "$BASE_URL/api/search?limit=-100")
if echo "$response" | grep -qi "error\|validation\|invalid"; then
print_pass "Negative limit rejected"
else
print_warn "Negative limit may be accepted"
fi
# Test extremely long query
long_query=$(python3 -c "print('A' * 1000)")
response=$(curl -s "$BASE_URL/api/search?q=$long_query")
if echo "$response" | grep -qi "error\|validation\|too long"; then
print_pass "Excessive input length handled"
else
print_warn "Long input may not be validated"
fi
###############################################################################
# Test 9: Dependency Vulnerabilities
###############################################################################
print_section "9. DEPENDENCY VULNERABILITY SCAN"
print_test "Running npm audit..."
cd "$PROJECT_DIR"
audit_output=$(npm audit --json 2>/dev/null || echo '{"error":"audit failed"}')
vulnerabilities=$(echo "$audit_output" | jq -r '.metadata.vulnerabilities // empty' 2>/dev/null || echo "{}")
if [ "$vulnerabilities" = "{}" ]; then
print_pass "No dependency vulnerabilities found"
else
critical=$(echo "$vulnerabilities" | jq -r '.critical // 0')
high=$(echo "$vulnerabilities" | jq -r '.high // 0')
moderate=$(echo "$vulnerabilities" | jq -r '.moderate // 0')
low=$(echo "$vulnerabilities" | jq -r '.low // 0')
if [ "$critical" -gt 0 ]; then
print_fail "CRITICAL vulnerabilities found: $critical"
fi
if [ "$high" -gt 0 ]; then
print_fail "HIGH vulnerabilities found: $high"
fi
if [ "$moderate" -gt 0 ]; then
print_warn "MODERATE vulnerabilities found: $moderate"
fi
if [ "$low" -gt 0 ]; then
print_info "LOW vulnerabilities found: $low"
fi
fi
###############################################################################
# Test 10: File Permissions
###############################################################################
print_section "10. FILE PERMISSIONS TEST"
print_test "Checking sensitive file permissions..."
check_file_perms() {
local file=$1
local max_perms=$2
if [ -f "$file" ]; then
perms=$(stat -c "%a" "$file" 2>/dev/null || stat -f "%OLp" "$file" 2>/dev/null)
if [ "$perms" -le "$max_perms" ]; then
print_pass "$file permissions: $perms (secure)"
else
print_warn "$file permissions: $perms (should be <= $max_perms)"
fi
else
print_info "$file not found"
fi
}
check_file_perms "$PROJECT_DIR/.env" 600
check_file_perms "$PROJECT_DIR/package.json" 644
check_file_perms "$PROJECT_DIR/server-enterprise-security.js" 644
###############################################################################
# Test 11: Port Exposure
###############################################################################
print_section "11. PORT EXPOSURE TEST"
print_test "Checking for exposed ports..."
listening_ports=$(netstat -tulnp 2>/dev/null | grep LISTEN || ss -tulnp 2>/dev/null | grep LISTEN)
if echo "$listening_ports" | grep -q ":$PORT "; then
print_pass "Application port $PORT is listening"
fi
# Check for common dangerous ports
dangerous_ports=(3306 5432 27017 6379 9200)
for port in "${dangerous_ports[@]}"; do
if echo "$listening_ports" | grep -q ":$port "; then
print_warn "Database/service port $port is exposed"
fi
done
###############################################################################
# Test 12: Security Configuration
###############################################################################
print_section "12. SECURITY CONFIGURATION TEST"
print_test "Checking security status endpoint..."
security_status=$(curl -s "$BASE_URL/api/security/status")
if echo "$security_status" | jq -e '.securityFeatures' > /dev/null 2>&1; then
print_pass "Security status endpoint accessible"
# Check individual features
features=$(echo "$security_status" | jq -r '.securityFeatures | to_entries[] | "\(.key): \(.value)"')
echo "$features" | while read -r line; do
feature=$(echo "$line" | cut -d: -f1)
status=$(echo "$line" | cut -d: -f2 | xargs)
if [ "$status" = "true" ]; then
print_pass " $feature: enabled"
else
print_info " $feature: $status"
fi
done
else
print_warn "Security status endpoint not responding correctly"
fi
###############################################################################
# Test 13: Memory Leaks
###############################################################################
print_section "13. MEMORY USAGE TEST"
print_test "Checking memory usage..."
if command -v pm2 > /dev/null; then
pm2_info=$(pm2 jlist 2>/dev/null | jq -r '.[] | select(.name=="omega-watches-secure" or .name=="omega-watches") | {name: .name, memory: .monit.memory, cpu: .monit.cpu}' 2>/dev/null)
if [ -n "$pm2_info" ]; then
memory=$(echo "$pm2_info" | jq -r '.memory')
cpu=$(echo "$pm2_info" | jq -r '.cpu')
# Convert memory to MB
memory_mb=$((memory / 1024 / 1024))
print_info "Current memory usage: ${memory_mb}MB"
print_info "Current CPU usage: ${cpu}%"
if [ $memory_mb -lt 500 ]; then
print_pass "Memory usage is within acceptable range"
else
print_warn "High memory usage detected: ${memory_mb}MB"
fi
else
print_info "PM2 process information not available"
fi
else
print_info "PM2 not installed, skipping memory check"
fi
###############################################################################
# Summary
###############################################################################
print_section "AUDIT SUMMARY"
total_tests=13
echo "Total test categories: $total_tests" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
# Count results
pass_count=$(grep -c "\[PASS\]" "$REPORT_FILE" || echo 0)
fail_count=$(grep -c "\[FAIL\]" "$REPORT_FILE" || echo 0)
warn_count=$(grep -c "\[WARN\]" "$REPORT_FILE" || echo 0)
echo "Results:" | tee -a "$REPORT_FILE"
echo " Passed: $pass_count" | tee -a "$REPORT_FILE"
echo " Failed: $fail_count" | tee -a "$REPORT_FILE"
echo " Warnings: $warn_count" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
# Calculate score
total_checks=$((pass_count + fail_count + warn_count))
if [ $total_checks -gt 0 ]; then
score=$((pass_count * 100 / total_checks))
if [ $fail_count -eq 0 ]; then
if [ $warn_count -eq 0 ]; then
grade="A+"
print_pass "Security Grade: $grade (Score: $score/100)"
else
grade="A"
print_pass "Security Grade: $grade (Score: $score/100)"
fi
elif [ $fail_count -le 2 ]; then
grade="B"
print_warn "Security Grade: $grade (Score: $score/100)"
else
grade="C"
print_fail "Security Grade: $grade (Score: $score/100)"
fi
else
print_info "Unable to calculate security score"
fi
echo "" | tee -a "$REPORT_FILE"
echo "Report saved to: $REPORT_FILE" | tee -a "$REPORT_FILE"
# Create latest symlink
ln -sf "$REPORT_FILE" "$REPORT_DIR/latest.txt"
echo "" | tee -a "$REPORT_FILE"
echo "View latest report: cat $REPORT_DIR/latest.txt" | tee -a "$REPORT_FILE"
###############################################################################
# Recommendations
###############################################################################
if [ $fail_count -gt 0 ] || [ $warn_count -gt 0 ]; then
print_section "RECOMMENDATIONS"
echo "Based on the audit results, consider the following actions:" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
if [ $fail_count -gt 0 ]; then
echo "CRITICAL:" | tee -a "$REPORT_FILE"
grep "\[FAIL\]" "$REPORT_FILE" | sed 's/\x1b\[[0-9;]*m//g' | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
fi
if [ $warn_count -gt 0 ]; then
echo "WARNINGS:" | tee -a "$REPORT_FILE"
grep "\[WARN\]" "$REPORT_FILE" | sed 's/\x1b\[[0-9;]*m//g' | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
fi
echo "Review the full report for detailed information." | tee -a "$REPORT_FILE"
fi
echo "" | tee -a "$REPORT_FILE"
echo "═══════════════════════════════════════════════════════════════" | tee -a "$REPORT_FILE"
echo "END OF SECURITY AUDIT" | tee -a "$REPORT_FILE"
echo "═══════════════════════════════════════════════════════════════" | tee -a "$REPORT_FILE"
exit 0