diff --git a/.sysref-test b/.sysref-test deleted file mode 100644 index 9ea9fb5..0000000 --- a/.sysref-test +++ /dev/null @@ -1,16 +0,0 @@ -# Test System Reference Database -# Platform: cpanel -# Generated: Wed Dec 24 03:16:31 PM EST 2025 - -[USERS] -USER|pickledperil - -[DOMAINS] -DOMAIN|pickledperil.com|pickledperil|/home/pickledperil/public_html|/etc/apache2/logs/domlogs/pickledperil.com|ea-php81|yes|primary|www.pickledperil.com|200|200|200_OK -DOMAIN|www.pickledperil.com|pickledperil|/home/pickledperil/public_html|/etc/apache2/logs/domlogs/pickledperil.com|ea-php81|no|alias|pickledperil.com|200|200|alias_of_200_OK -DOMAIN|67-227-141-132.cprapid.com|unknown||/var/log/apache2/domlogs/67-227-141-132.cprapid.com||unknown|local||timeout|timeout|TIMEOUT -DOMAIN|cloudvpstemplate.host.pickledperil.com|unknown||/var/log/apache2/domlogs/cloudvpstemplate.host.pickledperil.com||unknown|local||200|200|200_OK - -[DATABASES] -DB|pickledperil_wp_wt6lz|pickledperil - diff --git a/.sysref-test.timestamp b/.sysref-test.timestamp deleted file mode 100644 index a62939c..0000000 --- a/.sysref-test.timestamp +++ /dev/null @@ -1 +0,0 @@ -1766607398 diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a029019..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,113 +0,0 @@ -# Changelog - -All notable changes to the Linux Server Management Toolkit will be documented in this file. - -## [2.2.1] - 2026-01-11 - -### Added - Nginx + Varnish Cache Manager -- **New Module**: Complete Varnish cache installation and management system for cPanel - - Location: `modules/performance/nginx-varnish-manager.sh` - - Interactive menu with 8 options (setup, status, health check, auto-fix, statistics, flush, revert, backups) - - Automated audit script with 44 tests (`/root/audit-varnish-setup.sh`) - - Comprehensive documentation (`modules/performance/README-nginx-varnish.md`) - -#### Key Features -- **99.5% Stock Compliance**: Only modifies settings.json (RPM config file) -- **Update Survival**: Proven to survive ea-nginx package updates and rebuilds -- **93 Static File Types**: Images, fonts, CSS/JS, videos, documents, archives, packages -- **Smart Bypasses**: AutoSSL (.well-known/acme-challenge/), cPanel services, 13 admin page patterns -- **Self-Healing**: 7 automatic fixes for any configuration issues -- **Complete Backup/Revert**: Full restoration to pre-installation state in 2-5 minutes - -#### Architecture -``` -Client → Nginx (80/443) → Varnish (6081) → Apache (81/444) -``` - -#### Technical Implementation -- **Primary Persistence**: settings.json preservation via RPM config file handling -- **Safety Net**: ea-nginx config-script auto-fixes if settings.json fails -- **Tertiary Recovery**: Auto-fix function detects and repairs 7 failure scenarios -- **Multi-Layer Protection**: 3-layer strategy ensures configuration never stays broken - -#### Performance Impact -- Cache hit rate: 60-80% after 24 hours -- Page load time: 30-50% faster for cached content -- Server load: 20-40% reduction -- TTFB: Significantly improved for static files - -#### Testing & Validation -- 44 automated tests across 6 phases -- Manual verification: 100% pass rate -- Comprehensive documentation with examples -- Production-ready with rollback capability - -### Changed -- Updated main README.md to include nginx-varnish-manager -- Added module to Performance Analysis section -- Updated module count: 41 → 42 working modules -- Updated Recent Updates section with Varnish cache manager highlights - -### Documentation -- Created comprehensive module README (`README-nginx-varnish.md`) -- Created automated audit script with color-coded output -- Created audit plan with 10 testing phases -- Created verification documents (3 comprehensive audit reports) - -## [2.2.0] - 2026-01-08 - -### Added - Security Enhancements -- **Auto-Mitigation Engine**: Automatic IP blocking at Score >= 80/100 via IPset (kernel-level) -- **Distributed Attack Blocking**: Detects and blocks coordinated botnet attacks (5+ IPs) -- **Subnet-Level Blocking**: Blocks entire /24 subnets when 25+ IPs attack from same range - -### Fixed -- **Attack Signature Improvements**: Fixed false positives in HTTP_SMUGGLING and SUSPICIOUS_UA detection -- **Function Exports**: Fixed critical bug preventing HTTP attack auto-blocking in subshells - -### Changed -- **No System Pollution**: Moved all persistent data from /var/lib/ to /tmp/ for clean removal -- **Maldet Auto-Installation**: Enhanced Plesk support with improved directory detection - -## [2.1.0] - 2025-12-15 - -### Added -- **MySQL Restore Tool**: Advanced database recovery with intelligent Force Recovery detection -- Multi-control panel support (cPanel, InterWorx, Plesk, standalone) - -### Changed -- **Launcher Cleanup**: Removed 90+ phantom menu items -- Reduced launcher size from 1,576 to 574 lines (64% reduction) -- **Performance**: Cached domain status checks save ~5 minutes on 50-domain servers - -## [2.0.0] - 2025-11-01 - -### Added -- Modular architecture with organized directory structure -- 41 working modules across 5 categories -- Reference database for cross-module intelligence -- Session-based tracking (no historical data) - -### Changed -- Complete restructuring of toolkit -- Zero hardcoded paths with automatic control panel detection -- Self-contained design (delete = full cleanup) - -## [1.0.0] - 2025-01-01 - -### Added -- Initial release -- Basic server management scripts -- cPanel-focused utilities - ---- - -**Version Format**: [Major.Minor.Patch] -- **Major**: Breaking changes or major feature additions -- **Minor**: New features, non-breaking changes -- **Patch**: Bug fixes, small improvements - -**Links**: -- Repository: https://git.mull.lol/cschantz/Linux-Server-Management-Toolkit -- Documentation: README.md -- License: MIT (see LICENSE file) diff --git a/manifest.txt.example b/manifest.txt.example deleted file mode 100644 index 623b3a1..0000000 --- a/manifest.txt.example +++ /dev/null @@ -1,85 +0,0 @@ -# Server Management Toolkit - Module Manifest -# Format: category:module-name.sh -# Upload this to your Nextcloud folder as manifest.txt - -# Security & Threat Analysis -security:bot-analyzer.sh -security:live-monitor.sh -security:ip-lookup.sh -security:threat-blocker.sh -security:whitelist-manager.sh -security:attack-pattern-analyzer.sh -security:ddos-detector.sh -security:firewall-manager.sh -security:ssl-security-audit.sh - -# WordPress Management -wordpress:wp-health-check.sh -wordpress:wp-cron-status.sh -wordpress:wp-cron-mass-fix.sh -wordpress:wp-cron-mass-create.sh -wordpress:wp-plugin-audit.sh -wordpress:wp-theme-audit.sh -wordpress:wp-db-optimizer.sh -wordpress:wp-cache-clear.sh -wordpress:wp-mass-update-core.sh -wordpress:wp-mass-update-plugins.sh -wordpress:wp-login-security.sh -wordpress:wp-malware-scanner.sh -wordpress:wp-permission-fixer.sh -wordpress:wp-debug-log-analyzer.sh - -# Performance & Diagnostics -performance:resource-monitor.sh -performance:top-processes.sh -performance:slow-query-analyzer.sh -performance:bandwidth-analyzer.sh -performance:apache-performance.sh -performance:php-fpm-monitor.sh -performance:disk-io-analyzer.sh -performance:disk-usage-report.sh -performance:email-queue-monitor.sh -performance:inode-usage-checker.sh -performance:network-performance.sh - -# Backup & Recovery -backup:auto-backup.sh -backup:selective-backup.sh -backup:restore-helper.sh -backup:database-backup.sh -backup:config-backup.sh -backup:log-archive.sh -backup:backup-verification.sh -backup:offsite-sync.sh - -# Monitoring & Alerts -monitoring:service-status-monitor.sh -monitoring:uptime-tracker.sh -monitoring:error-log-watcher.sh -monitoring:disk-space-alerts.sh -monitoring:ssl-expiration-monitor.sh -monitoring:security-alert-dashboard.sh -monitoring:email-delivery-monitor.sh -monitoring:dns-monitor.sh - -# Troubleshooting & Diagnostics -troubleshooting:oom-killer-plotter.sh -troubleshooting:hard-drive-error-tracker.sh -troubleshooting:kernel-log-analyzer.sh -troubleshooting:mysql-error-analyzer.sh -troubleshooting:apache-error-deep-dive.sh -troubleshooting:php-error-tracker.sh -troubleshooting:connection-issues.sh -troubleshooting:zombie-process-hunter.sh -troubleshooting:file-system-checker.sh -troubleshooting:port-scanner.sh -troubleshooting:service-restart-helper.sh - -# Reporting & Analytics -reporting:security-report-viewer.sh -reporting:performance-summary.sh -reporting:traffic-analytics.sh -reporting:account-usage-report.sh -reporting:system-health-dashboard.sh -reporting:custom-report-builder.sh -reporting:export-to-pdf.sh diff --git a/modules/backup/mysql-restore-to-sql.sh b/modules/backup/mysql-restore-to-sql.sh index e9cfc1f..ad706a7 100755 --- a/modules/backup/mysql-restore-to-sql.sh +++ b/modules/backup/mysql-restore-to-sql.sh @@ -293,10 +293,66 @@ show_step_menu() { echo " [3] Go to Step 3 (Select database)" echo " [4] Go to Step 4 (Configure restore options)" echo " [5] Go to Step 5 (Create SQL dump)" + echo " [G] Guided process (walks through all steps automatically)" echo " [C] Compare original vs recovered database" echo " [R] Review current state" + echo " [0] Back to main menu" echo "" - echo -n "Select action (1-5, C, R): " + echo -n "Select action (1-5, G, C, R, 0): " + return 0 +} + +# Guided Process Mode: Walks user through all 5 steps automatically +# Returns 0 on success, 1 if user cancels at any step +run_guided_process() { + echo "" + echo "════════════════════════════════════════════════════════════════" + print_banner "GUIDED PROCESS - Automatic Workflow" + echo "════════════════════════════════════════════════════════════════" + echo "" + echo "This will walk you through all 5 steps automatically." + echo "You can cancel at any step by typing '0' when prompted." + echo "" + press_enter + + # Step 1 + print_banner "Step 1 of 5: Detect Live MySQL Data Directory" + if ! step1_detect_datadir; then + print_warning "Step 1 cancelled or failed. Returning to menu." + return 1 + fi + + # Step 2 + print_banner "Step 2 of 5: Set Restore Data Location" + if ! step2_set_restore_location; then + print_warning "Step 2 cancelled or failed. Returning to menu." + return 1 + fi + + # Step 3 + print_banner "Step 3 of 5: Select Database to Restore" + if ! step3_select_database; then + print_warning "Step 3 cancelled or failed. Returning to menu." + return 1 + fi + + # Step 4 + print_banner "Step 4 of 5: Configure Restore Options" + if ! step4_configure_options; then + print_warning "Step 4 cancelled or failed. Returning to menu." + return 1 + fi + + # Step 5 + print_banner "Step 5 of 5: Create SQL Dump" + if ! step5_create_dump; then + print_warning "Step 5 failed. Returning to menu to retry with different options." + return 1 + fi + + print_success "GUIDED PROCESS COMPLETED SUCCESSFULLY!" + echo "" + press_enter return 0 } @@ -3156,6 +3212,17 @@ main() { show_current_state press_enter ;; + G|g) + # Guided process mode - walks through all steps automatically + run_guided_process + ;; + 0) + # Exit to main menu + echo "" + print_info "Returning to main menu..." + sleep 1 + return 0 + ;; *) print_error "Invalid option: $menu_choice" press_enter diff --git a/modules/security/malware-scanner.sh b/modules/security/malware-scanner.sh index cc26780..58d7d99 100755 --- a/modules/security/malware-scanner.sh +++ b/modules/security/malware-scanner.sh @@ -9,11 +9,16 @@ ################################################################################ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -source "$SCRIPT_DIR/lib/common-functions.sh" 2>/dev/null || true -source "$SCRIPT_DIR/lib/system-detect.sh" 2>/dev/null || true -source "$SCRIPT_DIR/lib/user-manager.sh" 2>/dev/null || true -source "$SCRIPT_DIR/lib/reference-db.sh" 2>/dev/null || true -source "$SCRIPT_DIR/lib/ip-reputation.sh" 2>/dev/null || true + +# Source required libraries (warn if missing, but allow graceful degradation) +source "$SCRIPT_DIR/lib/common-functions.sh" 2>/dev/null || \ + { echo "WARNING: common-functions.sh not found - some features may not work" >&2; } +source "$SCRIPT_DIR/lib/system-detect.sh" 2>/dev/null || \ + { echo "WARNING: system-detect.sh not found - control panel detection may fail" >&2; } +source "$SCRIPT_DIR/lib/user-manager.sh" 2>/dev/null || \ + { echo "WARNING: user-manager.sh not found - user selection may not work" >&2; } +source "$SCRIPT_DIR/lib/reference-db.sh" 2>/dev/null || true # Optional +source "$SCRIPT_DIR/lib/ip-reputation.sh" 2>/dev/null || true # Optional # Arrays for docroots and scanners declare -a docroot_array @@ -21,6 +26,32 @@ declare -a sanitized_docroot declare -a remove_docroot declare -a available_scanners +# Validate that required functions were sourced from libraries +# These functions must exist for the script to work properly +validate_required_functions() { + local required_functions=( + "confirm" + "print_header" + "select_user_interactive" + "get_user_domains" + ) + + for func in "${required_functions[@]}"; do + if ! declare -f "$func" &>/dev/null; then + echo "ERROR: Required function '$func' not found." >&2 + echo " Check that library files exist in: $SCRIPT_DIR/lib/" >&2 + return 1 + fi + done + + return 0 +} + +# Validate functions early +if ! validate_required_functions; then + exit 1 +fi + # Individual scanner detection functions is_imunify_installed() { command -v imunify-antivirus &>/dev/null || [ -f "/usr/bin/imunify-antivirus" ] @@ -436,8 +467,8 @@ detect_control_panel() { while IFS= read -r line; do # Format: domain: user==owner==main==domain==docroot==... - # Extract docroot (field 5, 0-indexed field 4) - docroot=$(echo "$line" | awk -F'==' '{print $5}') + # Extract docroot (field 5, 0-indexed field 4) using awk directly + docroot=$(awk -F'==' '{print $5}' <<< "$line") [ -n "$docroot" ] && [ -d "$docroot" ] && docroot_array+=("$docroot") done < <(cut -d: -f2- /etc/userdatadomains | sort -u) @@ -512,7 +543,7 @@ get_user_docroots() { if [ "$CONTROL_PANEL" = "cpanel" ]; then while IFS= read -r line; do - docroot=$(echo "$line" | awk -F'==' '{print $5}') + docroot=$(awk -F'==' '{print $5}' <<< "$line") [ -n "$docroot" ] && [ -d "$docroot" ] && user_docroots+=("$docroot") done < <(grep ":.*${username}==" /etc/userdatadomains | cut -d: -f2- | sort -u) elif [ "$CONTROL_PANEL" = "interworx" ]; then @@ -687,9 +718,24 @@ cleanup_on_exit() { fi echo "→ Cleaning up: Removing Rootkit Hunter..." if command -v yum &>/dev/null; then - yum remove -y rkhunter &>/dev/null 2>&1 - if [ -f "$SESSION_LOG" ]; then - log_message "RKHunter removed" + if yum remove -y rkhunter &>/dev/null 2>&1; then + if [ -f "$SESSION_LOG" ]; then + log_message "RKHunter removed successfully" + fi + else + if [ -f "$SESSION_LOG" ]; then + log_message "WARNING: Failed to remove RKHunter (yum command failed)" + fi + fi + elif command -v apt-get &>/dev/null; then + if apt-get remove -y rkhunter &>/dev/null 2>&1; then + if [ -f "$SESSION_LOG" ]; then + log_message "RKHunter removed successfully" + fi + else + if [ -f "$SESSION_LOG" ]; then + log_message "WARNING: Failed to remove RKHunter (apt-get command failed)" + fi fi fi fi @@ -716,7 +762,7 @@ clear echo "========================================" echo "Standalone Malware Scanner" echo "========================================" -echo "Session: $(basename $SCAN_DIR)" +echo "Session: $(basename "$SCAN_DIR")" echo "Started: $(date)" echo "========================================" echo "" @@ -843,7 +889,7 @@ fi echo "==========================================" echo "Malware Scan Summary Report" echo "==========================================" - echo "Session: $(basename $SCAN_DIR)" + echo "Session: $(basename "$SCAN_DIR")" echo "Started: $(date)" echo "Scanners: ${AVAILABLE_SCANNERS[*]}" echo "Paths: ${#SCAN_PATHS[@]}" @@ -896,117 +942,63 @@ for scanner in "${AVAILABLE_SCANNERS[@]}"; do SCAN_START=$(date +%s) log_message "ImunifyAV: Updating signatures" - if ! imunify-antivirus update &>> "$LOG_DIR/imunify.log"; then + if ! timeout 300 imunify-antivirus update &>> "$LOG_DIR/imunify.log"; then log_message "WARNING: ImunifyAV update failed (continuing with existing signatures)" echo "⚠️ WARNING: Signature update failed, using existing signatures" fi log_message "ImunifyAV: Starting on-demand scan" + echo "" + echo " 📁 Scanning paths: ${SCAN_PATHS[@]}" + echo " ⏳ Scanner: ImunifyAV" + echo "" - # Use on-demand start with background monitoring for progress - LAST_SCAN="" - TOTAL_FILES_SCANNED=0 + IMUNIFY_INFECTED=0 + FILES_SCANNED=0 - # For user-focused scans, use paths as-is - IMUNIFY_SCAN_PATHS=("${SCAN_PATHS[@]}") + # Run ImunifyAV scan with timeout (2 hours max) + # Use --output-format to make parsing easier, with --timeout to prevent hanging + for path in "${SCAN_PATHS[@]}"; do + if [ ! -d "$path" ]; then + log_message "ImunifyAV: Skipping non-existent path: $path" + continue + fi - for path in "${IMUNIFY_SCAN_PATHS[@]}"; do - if [ -d "$path" ]; then - log_message "ImunifyAV: Scanning $path" - echo "" - echo " 📁 Scanning path: $path" - echo " ⏳ Scanner: ImunifyAV (monitoring progress...)" - echo "" + log_message "ImunifyAV: Scanning $path" + echo " Scanning: $path" - # Start scan (ImunifyAV runs async, command returns immediately) - imunify-antivirus malware on-demand start --path="$path" &>> "$LOG_DIR/imunify.log" - START_EXIT=$? + # Run scan with timeout to prevent hanging on status checks + # ImunifyAV scan - output to log file + timeout 7200 imunify-antivirus malware on-demand scan --path="$path" &>> "$LOG_DIR/imunify.log" & + IMUNIFY_PID=$! - if [ "${START_EXIT:-1}" -ne 0 ]; then - log_message "ERROR: ImunifyAV scan failed to start for $path (exit code: $START_EXIT)" - echo " ✗ Scan failed to start for $path (check logs)" - continue - fi + # Monitor with simple timeout (don't try to parse imunify status which hangs) + wait $IMUNIFY_PID + IMUNIFY_EXIT=$? - # Monitor progress by polling scan status - # ImunifyAV runs scans asynchronously, we poll the status - sleep 3 # Give scan time to initialize - last_count=0 - timeout_counter=0 - max_timeout=7200 # 2 hour timeout - scan_running=true - - while [ "$scan_running" = true ]; do - # Get current scan status from most recent scan - scan_info=$(imunify-antivirus malware on-demand list 2>/dev/null | tail -n +2 | head -1) - - if [ -n "$scan_info" ]; then - completed_time=$(echo "$scan_info" | awk '{print $1}') # Field 1 is COMPLETED timestamp - created_time=$(echo "$scan_info" | awk '{print $2}') # Field 2 is CREATED - current_files=$(echo "$scan_info" | awk '{print $11}') # Field 11 is TOTAL - current_status=$(echo "$scan_info" | awk '{print $7}') # Field 7 is SCAN_STATUS - - # Check if this is our scan (created after we started) - if [[ "$created_time" =~ ^[0-9]+$ ]] && [ "$created_time" -ge "$SCAN_START" ]; then - # Check if scan is complete (COMPLETED field has timestamp) - if [ -n "$completed_time" ] && [ "$completed_time" != "COMPLETED" ] && [[ "$completed_time" =~ ^[0-9]+$ ]] && [ "$completed_time" -gt 0 ]; then - scan_running=false - echo "" # New line after progress - log_message "ImunifyAV scan finished for $path (status: $current_status)" - break - fi - - # Update progress if file count changed - if [[ "$current_files" =~ ^[0-9]+$ ]]; then - if [ "$current_files" != "$last_count" ]; then - elapsed=$(($(date +%s) - SCAN_START)) - printf "\r Files scanned: %s | Elapsed: %s " \ - "$current_files" "$(format_time $elapsed)" - last_count=$current_files - timeout_counter=0 - fi - fi - fi - fi - - sleep 3 - timeout_counter=$((timeout_counter + 3)) - if [ $timeout_counter -ge $max_timeout ]; then - log_message "ERROR: ImunifyAV scan timed out after 2 hours for $path" - echo -e "\n ⏱️ Scan timed out (exceeded 2 hour limit)" - # Try to stop the scan - imunify-antivirus malware on-demand stop --path="$path" &>/dev/null - continue 2 - fi - done - - # Get final scan results - LAST_SCAN=$(imunify-antivirus malware on-demand list 2>/dev/null | tail -n +2 | head -1) - FILES_SCANNED=$(echo "$LAST_SCAN" | awk '{print $11}') - if ! [[ "$FILES_SCANNED" =~ ^[0-9]+$ ]]; then - FILES_SCANNED=0 - fi - TOTAL_FILES_SCANNED=$((TOTAL_FILES_SCANNED + FILES_SCANNED)) - echo " ✓ Scanned $FILES_SCANNED files in this path" + if [ "$IMUNIFY_EXIT" -eq 124 ]; then + log_message "ERROR: ImunifyAV scan timed out after 2 hours for $path" + echo " ⏱️ Scan timed out (2 hour limit)" + elif [ "$IMUNIFY_EXIT" -ne 0 ]; then + log_message "WARNING: ImunifyAV scan exited with code $IMUNIFY_EXIT for $path" + echo " ⚠️ Scan completed with code $IMUNIFY_EXIT" fi done - FILES_SCANNED=$TOTAL_FILES_SCANNED + # Try to get count of malicious files from malicious list (if available) + # Run once with timeout (60s) - capture output and exit code together + IMUNIFY_INFECTED=$(timeout 60 imunify-antivirus malware malicious list 2>/dev/null | tail -n +2 | wc -l) + IMUNIFY_MALICIOUS_EXIT=$? - # Extract malicious file count - # Skip header line and count data rows, or use TOTAL_MALICIOUS from most recent scan - if [ -n "$LAST_SCAN" ]; then - IMUNIFY_INFECTED=$(echo "$LAST_SCAN" | awk '{print $12}') - else + # Validate the count + if [ "$IMUNIFY_MALICIOUS_EXIT" -ne 0 ] || ! [[ "$IMUNIFY_INFECTED" =~ ^[0-9]+$ ]]; then IMUNIFY_INFECTED=0 - fi - # Verify we got a valid number, otherwise try malicious list - if ! [[ "$IMUNIFY_INFECTED" =~ ^[0-9]+$ ]]; then - IMUNIFY_INFECTED=$(imunify-antivirus malware malicious list 2>/dev/null | tail -n +2 | wc -l || echo 0) + log_message "WARNING: Failed to get ImunifyAV malicious count (exit: $IMUNIFY_MALICIOUS_EXIT)" fi SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) + echo " ✓ ImunifyAV scan complete" echo " ⏱️ Duration: ${DURATION}s" echo "" echo "✓ ImunifyAV scan complete - Found: $IMUNIFY_INFECTED | Duration: ${DURATION}s" | tee -a "$SUMMARY_FILE" @@ -1182,8 +1174,9 @@ for scanner in "${AVAILABLE_SCANNERS[@]}"; do # Extract scan results from event log (more reliable than parsing output) # Maldet logs to /usr/local/maldetect/logs/event_log - FILES_SCANNED=$(grep "scan completed" /usr/local/maldetect/logs/event_log | tail -1 | grep -oP 'files \K[0-9]+' || echo 0) - MALDET_HITS=$(grep "scan completed" /usr/local/maldetect/logs/event_log | tail -1 | grep -oP 'malware hits \K[0-9]+' || echo 0) + # Use proper fallback: assign first, then check if empty and fallback to 0 + FILES_SCANNED=$(grep "scan completed" /usr/local/maldetect/logs/event_log 2>/dev/null | tail -1 | grep -oP 'files \K[0-9]+') || FILES_SCANNED="0" + MALDET_HITS=$(grep "scan completed" /usr/local/maldetect/logs/event_log 2>/dev/null | tail -1 | grep -oP 'malware hits \K[0-9]+') || MALDET_HITS="0" # Validate numbers if ! [[ "$FILES_SCANNED" =~ ^[0-9]+$ ]]; then @@ -1341,7 +1334,7 @@ done # Check if this log corresponds to the domain/user grep -h "POST.*${filepath}" "$logfile" 2>/dev/null | tail -20 | while read -r logline; do # Extract IP from Apache log line - ip=$(echo "$logline" | awk '{print $1}') + ip=$(awk '{print $1}' <<< "$logline") if [ -n "$ip" ] && [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then # Flag this IP in reputation database if type flag_ip_attack &>/dev/null; then @@ -1359,7 +1352,7 @@ done # Check if this log corresponds to the domain/user grep -h "POST.*${filepath}" "$logfile" 2>/dev/null | tail -20 | while read -r logline; do # Extract IP from Apache log line - ip=$(echo "$logline" | awk '{print $1}') + ip=$(awk '{print $1}' <<< "$logline") if [ -n "$ip" ] && [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then # Flag this IP in reputation database if type flag_ip_attack &>/dev/null; then @@ -1378,7 +1371,7 @@ done # Check if this log corresponds to the domain/user grep -h "POST.*${filepath}" "$logfile" 2>/dev/null | tail -20 | while read -r logline; do # Extract IP from Apache log line - ip=$(echo "$logline" | awk '{print $1}') + ip=$(awk '{print $1}' <<< "$logline") if [ -n "$ip" ] && [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then # Flag this IP in reputation database if type flag_ip_attack &>/dev/null; then @@ -1528,7 +1521,7 @@ else fi echo "" - echo "Scan ID: $(basename $SCAN_DIR)" + echo "Scan ID: $(basename "$SCAN_DIR")" } > "$client_report_file" fi @@ -1537,7 +1530,7 @@ clear echo "==========================================" echo -e "${GREEN}Malware Scan Complete!${NC}" echo "==========================================" -echo "Session: $(basename $SCAN_DIR)" +echo "Session: $(basename "$SCAN_DIR")" echo "Completed: $(date)" echo "" echo "Results saved to:" @@ -1604,7 +1597,14 @@ STANDALONE_EOF done paths_declaration+=")" - sed -i "s|PLACEHOLDER_SCAN_PATHS|$paths_declaration|" "$session_dir/scan.sh" + # Escape special characters for sed (handle /, \, &, |, $) + # CRITICAL FIX: Must escape the delimiter (|) as well since we use it in the sed command + local escaped_paths=$(printf '%s\n' "$paths_declaration" | sed -e 's/[\/&|]/\\&/g') + + if ! sed -i "s|PLACEHOLDER_SCAN_PATHS|$escaped_paths|" "$session_dir/scan.sh"; then + echo -e "${RED}ERROR: Failed to generate standalone scanner script${NC}" + return 1 + fi # Make executable chmod +x "$session_dir/scan.sh" @@ -1785,8 +1785,17 @@ launch_standalone_scanner_menu() { return 1 fi + # Validate that docroots were found + if [ ${#sanitized_docroot[@]} -eq 0 ]; then + echo -e "${YELLOW}WARNING: No docroots found on this system${NC}" + echo "You can still:" + echo " 1. Scan specific paths manually (custom path option)" + echo " 2. Scan from root (entire server)" + echo "" + fi + echo "Control Panel: ${CONTROL_PANEL^}" - echo "Available Scanners: ${available_scanners[*]}" + echo "Available Scanners: ${available_scanners[@]}" echo "" local scope_choice @@ -1963,7 +1972,7 @@ launch_standalone_scanner_menu() { echo -e "${YELLOW}Ready to generate standalone scanner${NC}" echo "Scope: $scan_description" echo "Paths: ${#scan_paths[@]}" - echo "Scanners: ${available_scanners[*]}" + echo "Scanners: ${available_scanners[@]}" echo "" read -p "Generate and launch? (yes/no): " confirm @@ -1982,8 +1991,17 @@ check_standalone_status() { echo "" print_header "Standalone Scanner Status" - # Find all malware-* directories in /opt - local standalone_dirs=($(find /opt -maxdepth 1 -type d -name "malware-*" 2>/dev/null | sort -r)) + # Find all malware-* directories in /opt (proper array initialization to handle spaces in names) + if [ ! -d /opt ]; then + echo "ERROR: /opt directory not found or not accessible" + read -p "Press Enter to continue..." + return 1 + fi + + local standalone_dirs=() + while IFS= read -r dir; do + [ -n "$dir" ] && standalone_dirs+=("$dir") + done < <(find /opt -maxdepth 1 -type d -name "malware-*" 2>/dev/null | sort -r) if [ ${#standalone_dirs[@]} -eq 0 ]; then echo "No standalone scanner sessions found." @@ -2050,8 +2068,17 @@ delete_standalone_sessions() { echo "" print_header "Delete Standalone Scanner Sessions" - # Find all malware-* directories in /opt - local standalone_dirs=($(find /opt -maxdepth 1 -type d -name "malware-*" 2>/dev/null | sort -r)) + # Find all malware-* directories in /opt (proper array initialization to handle spaces in names) + if [ ! -d /opt ]; then + echo "ERROR: /opt directory not found or not accessible" + read -p "Press Enter to continue..." + return 1 + fi + + local standalone_dirs=() + while IFS= read -r dir; do + [ -n "$dir" ] && standalone_dirs+=("$dir") + done < <(find /opt -maxdepth 1 -type d -name "malware-*" 2>/dev/null | sort -r) if [ ${#standalone_dirs[@]} -eq 0 ]; then echo "No standalone scanner sessions found." @@ -2094,7 +2121,7 @@ delete_standalone_sessions() { local deleted=0 for dir in "${standalone_dirs[@]}"; do if ! pgrep -f "$dir/scan.sh" > /dev/null 2>&1; then - echo "Deleting: $(basename $dir)" + echo "Deleting: $(basename "$dir")" rm -rf "$dir" ((deleted++)) fi diff --git a/modules/website/website-slowness-diagnostics.sh b/modules/website/website-slowness-diagnostics.sh index bf1342b..f0a4d27 100755 --- a/modules/website/website-slowness-diagnostics.sh +++ b/modules/website/website-slowness-diagnostics.sh @@ -573,8 +573,8 @@ analyze_images() { print_section "Image Format Analysis" print_info "Scanning for unoptimized images..." - # Count image types - local jpg_count=$(find "$docroot" -maxdepth 5 -iname "*.jpg" -o -iname "*.jpeg" 2>/dev/null | wc -l) + # Count image types (use parentheses to ensure -maxdepth applies to all -o branches) + local jpg_count=$(find "$docroot" -maxdepth 5 \( -iname "*.jpg" -o -iname "*.jpeg" \) 2>/dev/null | wc -l) local png_count=$(find "$docroot" -maxdepth 5 -iname "*.png" 2>/dev/null | wc -l) local gif_count=$(find "$docroot" -maxdepth 5 -iname "*.gif" 2>/dev/null | wc -l) local webp_count=$(find "$docroot" -maxdepth 5 -iname "*.webp" 2>/dev/null | wc -l) diff --git a/modules/website/wordpress/wordpress-cron-manager.sh b/modules/website/wordpress/wordpress-cron-manager.sh index 3d8ca0b..24f673b 100755 --- a/modules/website/wordpress/wordpress-cron-manager.sh +++ b/modules/website/wordpress/wordpress-cron-manager.sh @@ -38,7 +38,8 @@ if ! flock -n 9; then print_error "Another instance of this script is already running" exit 1 fi -# NOTE: Trap is set later at line ~373, MUST include flock unlock! + +# Note: Trap is set later at line ~469 to handle flock, fd closure, and lock file cleanup # OPTIMIZATION: Parse command-line flags for script behavior # Support: --dry-run, --parallel, --log, --help @@ -456,7 +457,7 @@ get_wp_sites_cached() { # Cleanup on exit (keep cache file for next invocation, only remove lock file) # CRITICAL: Must unlock flock (fd 9) before removing lock file! -trap 'flock -u 9 2>/dev/null; exec 9>&-; rm -f "$LOCK_FILE"; rollback_cleanup' EXIT INT TERM +trap 'flock -u 9 2>/dev/null; exec 9>&-; rm -f "$LOCK_FILE"' EXIT INT TERM # OPTIMIZATION: User extraction caching (memoization) # extract_user_from_path() called 10 times, often for same path @@ -505,8 +506,14 @@ safe_add_cron_job() { # Add the job to crontab # CRITICAL: crontab -l already verified to have succeeded above - (echo "$current_crontab"; echo "$cron_time $cron_cmd") | crontab -u "$user" - 2>/dev/null - return $? + # Use temporary file instead of pipe to avoid pipefail issues and ensure proper error reporting + local temp_crontab + temp_crontab=$(mktemp) || return 1 + (echo "$current_crontab"; echo "$cron_time $cron_cmd") > "$temp_crontab" + crontab -u "$user" "$temp_crontab" 2>/dev/null + local result=$? + rm -f "$temp_crontab" + return $result } # Function to safely remove cron jobs from user's crontab @@ -526,9 +533,17 @@ safe_remove_cron_jobs() { fi # Remove jobs matching pattern - # CRITICAL: crontab -l already verified to have succeeded above - echo "$current_crontab" | grep -v "$pattern" | crontab -u "$user" - 2>/dev/null - return $? + # CRITICAL FIX: grep -v returns 1 when ALL lines are filtered (nothing matches the NOT pattern) + # With set -o pipefail, this makes the pipe fail even though crontab should succeed + # Solution: Use temporary file to break the pipe and avoid pipefail issues + local temp_crontab + temp_crontab=$(mktemp) || return 1 + echo "$current_crontab" | grep -v "$pattern" > "$temp_crontab" 2>/dev/null + # Note: grep -v can return 1 if output is empty - this is not an error for crontab + crontab -u "$user" "$temp_crontab" 2>/dev/null + local result=$? + rm -f "$temp_crontab" + return $result } # Function to validate wp-config.php syntax before and after modification diff --git a/test-launcher.sh b/test-launcher.sh deleted file mode 100755 index f4402fa..0000000 --- a/test-launcher.sh +++ /dev/null @@ -1,421 +0,0 @@ -#!/bin/bash - -############################################################################### -# TEST LAUNCHER - Cross-Platform Verification -# Tests multi-platform reference database building without modifying launcher.sh -############################################################################### - -# Get script directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -export TOOLKIT_BASE_DIR="$SCRIPT_DIR" - -# Source libraries -LIB_DIR="$SCRIPT_DIR/lib" -source "$LIB_DIR/common-functions.sh" -source "$LIB_DIR/system-detect.sh" -source "$LIB_DIR/domain-discovery.sh" -source "$LIB_DIR/user-manager.sh" - -# Test database location -TEST_SYSREF_DB="${TOOLKIT_BASE_DIR}/.sysref-test" -TEST_SYSREF_TIMESTAMP="${TOOLKIT_BASE_DIR}/.sysref-test.timestamp" - -############################################################################### -# DOMAIN STATUS CHECKING (from reference-db.sh) -############################################################################### - -# Returns: http_code|https_code|status_summary -check_domain_status() { - local domain="$1" - local http_code="000" - local https_code="000" - local status_summary="unchecked" - - # Skip if curl not available - if ! command -v curl &>/dev/null; then - echo "000|000|no_curl" - return 0 - fi - - # Skip obviously invalid domains - if [ -z "$domain" ] || [[ ! "$domain" =~ \. ]]; then - echo "000|000|invalid_domain" - return 0 - fi - - # Try HTTP (timeout 3 seconds, max 2 redirects, check for valid response) - http_code=$(timeout 3 curl -s -o /dev/null -w "%{http_code}" --max-redirs 2 -m 3 "http://$domain" 2>/dev/null) - if [ $? -ne 0 ] || [ -z "$http_code" ]; then - http_code="timeout" - fi - - # Try HTTPS (timeout 3 seconds, max 2 redirects, ignore cert errors) - https_code=$(timeout 3 curl -s -o /dev/null -w "%{http_code}" --max-redirs 2 -m 3 -k "https://$domain" 2>/dev/null) - if [ $? -ne 0 ] || [ -z "$https_code" ]; then - https_code="timeout" - fi - - # Determine overall status - if [ "$http_code" = "200" ] || [ "$https_code" = "200" ]; then - status_summary="200_OK" - elif [ "$http_code" = "403" ] || [ "$https_code" = "403" ]; then - status_summary="403_FORBIDDEN" - elif [ "$http_code" = "404" ] || [ "$https_code" = "404" ]; then - status_summary="404_NOT_FOUND" - elif [ "$http_code" = "500" ] || [ "$https_code" = "500" ]; then - status_summary="500_ERROR" - elif [ "$http_code" = "502" ] || [ "$https_code" = "502" ]; then - status_summary="502_BAD_GATEWAY" - elif [ "$http_code" = "503" ] || [ "$https_code" = "503" ]; then - status_summary="503_UNAVAILABLE" - elif [[ "$http_code" =~ ^30[0-9]$ ]] || [[ "$https_code" =~ ^30[0-9]$ ]]; then - status_summary="REDIRECT" - elif [ "$http_code" = "timeout" ] && [ "$https_code" = "timeout" ]; then - status_summary="TIMEOUT" - elif [ "$http_code" = "000" ] && [ "$https_code" = "000" ]; then - status_summary="UNREACHABLE" - else - status_summary="OTHER" - fi - - echo "${http_code}|${https_code}|${status_summary}" -} - -############################################################################### -# PLATFORM-SPECIFIC DOMAIN BUILDERS -############################################################################### - -build_domains_cpanel_test() { - print_info "Using cPanel-optimized domain discovery..." - - local users=($(list_all_users)) - local current=0 - local total=0 - - # Count domains - for user in "${users[@]}"; do - local userdata_dir="${SYS_CPANEL_USERDATA_DIR:-/var/cpanel/userdata}/${user}" - if [ -d "$userdata_dir" ]; then - total=$((total + $(find "$userdata_dir" -type f ! -name "*.cache" ! -name "*.yaml" ! -name "*.json" ! -name "main*" ! -name "cache" ! -name "*_SSL" 2>/dev/null | wc -l))) - fi - done - - # Process domains - declare -A seen_domains - for user in "${users[@]}"; do - local userdata_dir="${SYS_CPANEL_USERDATA_DIR:-/var/cpanel/userdata}/${user}" - - if [ -d "$userdata_dir" ]; then - for config_file in "$userdata_dir"/*; do - [ ! -f "$config_file" ] && continue - local basename=$(basename "$config_file") - - # Skip cache files - [[ "$basename" =~ \.cache$|\.yaml$|\.json$|^main|^cache$|_SSL$ ]] && continue - - local domain="$basename" - local doc_root=$(grep "^documentroot:" "$config_file" | awk '{print $2}' || true) - local log_path=$(grep "target:.*domlogs" "$config_file" | head -1 | awk '{print $2}' || true) - local server_alias=$(grep "^serveralias:" "$config_file" | awk '{print $2}' || true) - local php_version=$(grep "^phpversion:" "$config_file" | awk '{print $2}' || true) - - # Determine if primary domain - local is_primary="no" - local primary_domain=$(get_user_domains "$user" | head -1) - [ "$domain" = "$primary_domain" ] && is_primary="yes" - - # Determine domain type (addon, parked, subdomain, primary) - local domain_type="addon" - if [ "$is_primary" = "yes" ]; then - domain_type="primary" - elif [[ "$domain" =~ \. ]] && [[ "$domain" =~ ^[^.]+\. ]]; then - # Check if it's a subdomain of the primary - local base_domain=$(echo "$domain" | rev | cut -d. -f1-2 | rev) - if [ "$base_domain" = "$primary_domain" ]; then - domain_type="subdomain" - fi - fi - - # Check HTTP/HTTPS status codes (only for primary and addon domains) - current=$((current + 1)) - local http_code="000" - local https_code="000" - local status_summary="skipped" - - if [ "$domain_type" = "primary" ] || [ "$domain_type" = "addon" ]; then - show_progress $current $total "Checking domain status codes..." - local status_result=$(check_domain_status "$domain") - IFS='|' read -r http_code https_code status_summary <<< "$status_result" - fi - - # Format: DOMAIN|domain|owner|doc_root|log_path|php_version|is_primary|type|aliases|http_code|https_code|status_summary - echo "DOMAIN|$domain|$user|$doc_root|$log_path|$php_version|$is_primary|$domain_type|$server_alias|$http_code|$https_code|$status_summary" >> "$TEST_SYSREF_DB" - seen_domains["$domain"]=1 - - # Also add aliases as separate entries - if [ -n "$server_alias" ]; then - for alias in $server_alias; do - [ -z "$alias" ] && continue - [ -n "${seen_domains[$alias]:-}" ] && continue - - # Alias points to same document root and logs (inherit status from parent) - echo "DOMAIN|$alias|$user|$doc_root|$log_path|$php_version|no|alias|$domain|$http_code|$https_code|alias_of_$status_summary" >> "$TEST_SYSREF_DB" - seen_domains["$alias"]=1 - done - fi - done - fi - done - - finish_progress - - # Check /etc/localdomains (cPanel local domains not yet added) - if [ "$SYS_CONTROL_PANEL" = "cpanel" ] && [ -f "/etc/localdomains" ]; then - while read -r domain; do - [ -z "$domain" ] && continue - [ -n "${seen_domains[$domain]:-}" ] && continue - - local owner=$(grep "^${domain}:" /etc/trueuserdomains 2>/dev/null | cut -d: -f2 | xargs || true) - [ -z "$owner" ] && owner="unknown" - - local log_path="${SYS_LOG_DIR}/${domain}" - - # Check status - local status_result=$(check_domain_status "$domain") - IFS='|' read -r http_code https_code status_summary <<< "$status_result" - - echo "DOMAIN|$domain|$owner||$log_path||unknown|local||$http_code|$https_code|$status_summary" >> "$TEST_SYSREF_DB" - seen_domains["$domain"]=1 - done < /etc/localdomains - fi - - # Check /etc/remotedomains (cPanel remote MX domains) - if [ "$SYS_CONTROL_PANEL" = "cpanel" ] && [ -f "/etc/remotedomains" ]; then - while read -r domain; do - [ -z "$domain" ] && continue - [ -n "${seen_domains[$domain]:-}" ] && continue - - local owner=$(grep "^${domain}:" /etc/trueuserdomains 2>/dev/null | cut -d: -f2 | xargs || true) - [ -z "$owner" ] && owner="unknown" - - echo "DOMAIN|$domain|$owner||||unknown|remote||000|000|remote_mx" >> "$TEST_SYSREF_DB" - seen_domains["$domain"]=1 - done < /etc/remotedomains - fi -} - -build_domains_plesk_test() { - print_info "Using Plesk-specific domain discovery..." - - local all_domains=$(list_all_domains) - local domain_count=$(echo "$all_domains" | wc -w) - local current=0 - - for domain in $all_domains; do - [ -z "$domain" ] && continue - ((current++)) - - show_progress $current $domain_count "Checking domain status codes..." - - # Use panel-agnostic functions that call Plesk helpers - local owner=$(get_domain_owner "$domain" || echo "unknown") - local docroot=$(get_domain_docroot "$domain" || echo "") - local logdir=$(get_domain_logdir "$domain" || echo "") - local access_log=$(get_domain_access_log "$domain" || echo "") - - # Try to get PHP version if plesk helper exists - local php_version="" - if type plesk_get_php_version >/dev/null 2>&1; then - php_version=$(plesk_get_php_version "$domain" || echo "") - fi - - # Check domain status - local status_result=$(check_domain_status "$domain") - IFS='|' read -r http_code https_code status_summary <<< "$status_result" - - # Format to match production - echo "DOMAIN|$domain|$owner|$docroot|$logdir|$php_version|unknown|local||$http_code|$https_code|$status_summary" >> "$TEST_SYSREF_DB" - done - - finish_progress -} - -build_domains_standalone_test() { - print_info "Using standalone domain discovery..." - - local all_domains=$(list_all_domains) - local domain_count=$(echo "$all_domains" | wc -w) - local current=0 - - if [ -z "$all_domains" ]; then - print_warning "No domains found via directory scanning" - return - fi - - for domain in $all_domains; do - [ -z "$domain" ] && continue - ((current++)) - - show_progress $current $domain_count "Checking domain status codes..." - - local docroot=$(get_domain_docroot "$domain" || echo "") - local owner=$(get_domain_owner "$domain" || echo "unknown") - local logdir=$(get_domain_logdir "$domain" || echo "") - local access_log=$(get_domain_access_log "$domain" || echo "") - - # Check domain status - local status_result=$(check_domain_status "$domain") - IFS='|' read -r http_code https_code status_summary <<< "$status_result" - - # Format to match production - echo "DOMAIN|$domain|$owner|$docroot|$logdir||unknown|local||$http_code|$https_code|$status_summary" >> "$TEST_SYSREF_DB" - done - - finish_progress -} - -############################################################################### -# MAIN TEST FUNCTION -############################################################################### - -test_reference_database() { - local start_time=$(date +%s) - - print_header "Cross-Platform Reference Database Test" - echo "" - - # Show detected platform - print_info "Detected Platform: $SYS_CONTROL_PANEL" - print_info "OS: $SYS_OS_TYPE $SYS_OS_VERSION" - print_info "Web Server: $SYS_WEB_SERVER $SYS_WEB_SERVER_VERSION" - print_info "Database: $SYS_DB_TYPE $SYS_DB_VERSION" - print_info "User Home Base: $SYS_USER_HOME_BASE" - print_info "Log Directory: $SYS_LOG_DIR" - echo "" - - # Initialize test database - print_info "Building test reference database..." - echo "# Test System Reference Database" > "$TEST_SYSREF_DB" - echo "# Platform: $SYS_CONTROL_PANEL" >> "$TEST_SYSREF_DB" - echo "# Generated: $(date)" >> "$TEST_SYSREF_DB" - echo "" >> "$TEST_SYSREF_DB" - - # Test users - echo "[USERS]" >> "$TEST_SYSREF_DB" - local users=($(list_all_users)) - print_info "Found ${#users[@]} users" - for user in "${users[@]}"; do - echo "USER|$user" >> "$TEST_SYSREF_DB" - done - echo "" >> "$TEST_SYSREF_DB" - - # Test domains - platform-specific - echo "[DOMAINS]" >> "$TEST_SYSREF_DB" - case "$SYS_CONTROL_PANEL" in - cpanel) - build_domains_cpanel_test - ;; - plesk) - build_domains_plesk_test - ;; - interworx) - print_warning "InterWorx support not yet implemented in test" - build_domains_standalone_test - ;; - *) - build_domains_standalone_test - ;; - esac - echo "" >> "$TEST_SYSREF_DB" - - # Test databases - echo "[DATABASES]" >> "$TEST_SYSREF_DB" - if [ "$SYS_DB_TYPE" != "none" ]; then - local all_dbs=$(mysql -Ns -e "SHOW DATABASES" 2>/dev/null | grep -v "^information_schema$\|^mysql$\|^performance_schema$\|^sys$" || true) - local db_count=$(echo "$all_dbs" | wc -l) - print_info "Found $db_count databases" - for db in $all_dbs; do - local owner=$(get_database_owner "$db" || echo "unknown") - echo "DB|$db|$owner" >> "$TEST_SYSREF_DB" - done - fi - echo "" >> "$TEST_SYSREF_DB" - - # Save timestamp - date +%s > "$TEST_SYSREF_TIMESTAMP" - - local end_time=$(date +%s) - local duration=$((end_time - start_time)) - - echo "" - print_success "Test database built in ${duration}s" - print_info "Test database location: $TEST_SYSREF_DB" - echo "" - - # Show statistics - local user_count=$(grep -c "^USER|" "$TEST_SYSREF_DB" 2>/dev/null || echo 0) - local domain_count=$(grep -c "^DOMAIN|" "$TEST_SYSREF_DB" 2>/dev/null || echo 0) - local db_count=$(grep -c "^DB|" "$TEST_SYSREF_DB" 2>/dev/null || echo 0) - - print_header "Test Results" - echo " Users: $user_count" - echo " Domains: $domain_count" - echo " Databases: $db_count" - echo "" - - # Show sample domains - if [ "$domain_count" -gt 0 ]; then - print_header "Sample Domain Entries (first 5)" - grep "^DOMAIN|" "$TEST_SYSREF_DB" | head -5 | while IFS='|' read -r type domain owner docroot logdir php_version is_primary domain_type server_alias http_code https_code status_summary; do - echo " Domain: $domain" - echo " Owner: $owner" - echo " Docroot: $docroot" - echo " Type: $domain_type" - echo " Status: HTTP=$http_code HTTPS=$https_code ($status_summary)" - echo "" - done - fi - - # Compare with production database if it exists - if [ -f "$TOOLKIT_BASE_DIR/.sysref" ]; then - echo "" - print_header "Comparison with Production Database" - - local prod_users=$(grep -c "^USER|" "$TOOLKIT_BASE_DIR/.sysref" 2>/dev/null || echo 0) - local prod_domains=$(grep -c "^DOMAIN|" "$TOOLKIT_BASE_DIR/.sysref" 2>/dev/null || echo 0) - local prod_dbs=$(grep -c "^DB|" "$TOOLKIT_BASE_DIR/.sysref" 2>/dev/null || echo 0) - - echo " Production: $prod_users users, $prod_domains domains, $prod_dbs databases" - echo " Test: $user_count users, $domain_count domains, $db_count databases" - echo "" - - if [ "$user_count" -eq "$prod_users" ] && [ "$domain_count" -eq "$prod_domains" ]; then - print_success "✅ Counts match! Test successful." - else - print_warning "⚠️ Counts differ - this may be expected for cross-platform changes" - fi - fi - - echo "" - print_info "Test database saved to: $TEST_SYSREF_DB" - print_info "You can inspect it with: cat $TEST_SYSREF_DB" -} - -############################################################################### -# RUN TEST -############################################################################### - -# Clear screen and run -clear -test_reference_database - -# Instructions -echo "" -print_header "Next Steps" -echo "1. Review the test database: cat $TEST_SYSREF_DB" -echo "2. If results look good on this cPanel server, test on Plesk:" -echo " - git pull on Plesk server" -echo " - bash test-launcher.sh" -echo " - Verify domains/users/databases are detected" -echo "3. If Plesk test succeeds, we can integrate into main launcher" -echo "" diff --git a/test-wordpress-cron-manager.sh b/test-wordpress-cron-manager.sh deleted file mode 100755 index 767779f..0000000 --- a/test-wordpress-cron-manager.sh +++ /dev/null @@ -1,296 +0,0 @@ -#!/bin/bash -################################################################################ -# Integration Test Suite for WordPress Cron Manager -# Purpose: Validate script functionality and catch regressions -################################################################################ - -SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/modules/website/wordpress/wordpress-cron-manager.sh" -TEST_COUNT=0 -PASSED_COUNT=0 -FAILED_COUNT=0 - -# Color codes -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -test_start() { - TEST_COUNT=$((TEST_COUNT + 1)) - echo -e "\n${BLUE}[TEST $TEST_COUNT]${NC} $1" -} - -test_pass() { - PASSED_COUNT=$((PASSED_COUNT + 1)) - echo -e "${GREEN}✓ PASS${NC}: $1" -} - -test_fail() { - FAILED_COUNT=$((FAILED_COUNT + 1)) - echo -e "${RED}✗ FAIL${NC}: $1" -} - -test_info() { - echo -e "${YELLOW}ℹ${NC} $1" -} - -# Test 1: Script exists -test_script_exists() { - test_start "Script file exists" - if [ -f "$SCRIPT" ]; then - test_pass "Script found at: $SCRIPT" - else - test_fail "Script not found: $SCRIPT" - fi -} - -# Test 2: Script is executable -test_script_executable() { - test_start "Script is executable" - if [ -x "$SCRIPT" ]; then - test_pass "Script has execute permission" - else - test_fail "Script is not executable" - return 1 - fi -} - -# Test 3: Bash syntax validation -test_bash_syntax() { - test_start "Bash syntax validation" - if bash -n "$SCRIPT" 2>/dev/null; then - test_pass "No syntax errors" - else - test_fail "Syntax errors detected" - bash -n "$SCRIPT" 2>&1 | head -5 - return 1 - fi -} - -# Test 4: Help flag works -test_help_flag() { - test_start "Help flag functionality (--help)" - output=$($SCRIPT --help 2>&1 | head -5) - if echo "$output" | grep -q "Usage"; then - test_pass "Help output generated" - else - test_fail "Help flag not working properly" - return 1 - fi -} - -# Test 5: Grep for key functions (non-execution test) -test_functions_defined() { - test_start "Key functions defined in script" - local functions="initialize_wp_cache get_wp_search_paths validate_wordpress_site is_empty is_set" - local missing=0 - - for func in $functions; do - if grep -q "^$func()" "$SCRIPT"; then - test_info "✓ Found function: $func" - else - test_fail "Missing function: $func" - missing=$((missing + 1)) - fi - done - - if [ $missing -eq 0 ]; then - test_pass "All key functions defined" - else - test_fail "$missing functions missing" - return 1 - fi -} - -# Test 6: Check for helper functions -test_helper_functions_defined() { - test_start "Helper functions defined" - local helpers="is_file_valid is_user_valid is_wp_configured is_cron_job_exists has_sufficient_disk_space" - local count=0 - - for helper in $helpers; do - if grep -q "^$helper()" "$SCRIPT"; then - count=$((count + 1)) - fi - done - - if [ $count -ge 3 ]; then - test_pass "Helper functions defined ($count/5)" - else - test_fail "Insufficient helper functions ($count/5)" - fi -} - -# Test 7: Error code constants -test_error_codes_defined() { - test_start "Error code constants defined" - if grep -q "ERR_SUCCESS\|ERR_INVALID_USER\|ERR_FILE_NOT_FOUND" "$SCRIPT"; then - test_pass "Error code constants found" - else - test_fail "Error code constants not found" - return 1 - fi -} - -# Test 8: Report generation functions -test_report_functions() { - test_start "Report generation functions" - local funcs="generate_json_report generate_csv_report report_save" - local count=0 - - for func in $funcs; do - if grep -q "^$func()" "$SCRIPT"; then - count=$((count + 1)) - fi - done - - if [ $count -eq 3 ]; then - test_pass "Report functions defined ($count/3)" - else - test_fail "Report functions incomplete ($count/3)" - fi -} - -# Test 9: Rollback functions -test_rollback_functions() { - test_start "Rollback support functions" - local funcs="rollback_init rollback_create_checkpoint rollback_all" - local count=0 - - for func in $funcs; do - if grep -q "^$func()" "$SCRIPT"; then - count=$((count + 1)) - fi - done - - if [ $count -eq 3 ]; then - test_pass "Rollback functions defined ($count/3)" - else - test_fail "Rollback functions incomplete ($count/3)" - fi -} - -# Test 10: Configuration support -test_config_support() { - test_start "Configuration file support" - if grep -q "load_config_file\|/etc/wordpress-cron-manager.conf" "$SCRIPT"; then - test_pass "Configuration file support implemented" - else - test_fail "Configuration file support not found" - return 1 - fi -} - -# Test 11: Progress tracking -test_progress_tracking() { - test_start "Progress tracking functions" - local funcs="show_progress show_progress_bar show_spinner" - local count=0 - - for func in $funcs; do - if grep -q "^$func()" "$SCRIPT"; then - count=$((count + 1)) - fi - done - - if [ $count -ge 2 ]; then - test_pass "Progress tracking defined ($count/3)" - else - test_fail "Progress tracking incomplete ($count/3)" - fi -} - -# Test 12: Regex helpers -test_regex_helpers() { - test_start "Regex pattern helpers" - local helpers="grep_wp_config_define grep_disabled_wp_cron grep_in_crontab" - local count=0 - - for helper in $helpers; do - if grep -q "^$helper()" "$SCRIPT"; then - count=$((count + 1)) - fi - done - - if [ $count -eq 3 ]; then - test_pass "Regex helpers defined ($count/3)" - else - test_fail "Regex helpers incomplete ($count/3)" - fi -} - -# Test 13: Function registry -test_function_registry() { - test_start "Function registry metadata" - if grep -q "FUNCTION_REGISTRY" "$SCRIPT"; then - test_pass "Function registry implemented" - else - test_fail "Function registry not found" - return 1 - fi -} - -# Test 14: Line count (sanity check) -test_script_size() { - test_start "Script size sanity check" - local lines=$(wc -l < "$SCRIPT") - if [ "$lines" -gt 2000 ]; then - test_pass "Script size reasonable: $lines lines" - else - test_fail "Script size too small: $lines lines (expected >2000)" - fi -} - -# Test 15: Git commits -test_git_history() { - test_start "Optimization commits in git history" - local opt_commits=$(cd "$(dirname $SCRIPT)/../../../" && git log --oneline | grep -c "OPTIMIZE\|ADVANCED") - if [ $opt_commits -gt 5 ]; then - test_pass "Multiple optimization commits found: $opt_commits" - else - test_fail "Insufficient optimization commits: $opt_commits" - fi -} - -# Summary -print_summary() { - echo "" - echo "================================================================================" - echo "INTEGRATION TEST SUMMARY" - echo "================================================================================" - echo "Total Tests: $TEST_COUNT" - echo -e "${GREEN}Passed: $PASSED_COUNT${NC}" - echo -e "${RED}Failed: $FAILED_COUNT${NC}" - - if [ $FAILED_COUNT -eq 0 ]; then - echo -e "\n${GREEN}✓ ALL TESTS PASSED${NC}" - return 0 - else - echo -e "\n${RED}⚠ SOME TESTS FAILED (but script may still work)${NC}" - return 1 - fi -} - -# Main execution -echo "================================================================================" -echo "WordPress Cron Manager - Integration Test Suite" -echo "================================================================================" - -test_script_exists -test_script_executable -test_bash_syntax -test_help_flag -test_functions_defined -test_helper_functions_defined -test_error_codes_defined -test_report_functions -test_rollback_functions -test_config_support -test_progress_tracking -test_regex_helpers -test_function_registry -test_script_size -test_git_history - -print_summary