From c7b017d4fc49d06e103397d153f3e141c442551e Mon Sep 17 00:00:00 2001 From: cschantz Date: Tue, 11 Nov 2025 19:16:16 -0500 Subject: [PATCH] Major refactor: Toolkit as monitor, standalone for all scans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture Changes: - ALL scans now use standalone scanner (/opt deployment) - Toolkit serves as monitor/manager, not executor - Removed direct scanning from toolkit entirely New Features: - Bulk scanner installation (install all 3 at once) - Scan status checker with live progress - Session manager (delete individual or all completed scans) - Enhanced menu structure with clear separation Menu Organization: 1. Create New Scan (server/user/domain/custom) → generates standalone 2. Monitor & Manage (status/results/delete) 3. Configuration (install all/settings) Removed Functions: - scan_entire_server() - now via standalone - scan_user_account() - now via standalone - scan_domain() - now via standalone - scan_custom_path() - now via standalone - run_all_scanners() - embedded in standalone - scan_imunify/clamav/maldet() - embedded in standalone Benefits: - Cleaner separation of concerns - Consistent scan execution (all via standalone) - Better resource management - Toolkit can be deleted during scan - Centralized scan monitoring --- modules/security/malware-scanner.sh | 792 ++++++++++++---------------- 1 file changed, 329 insertions(+), 463 deletions(-) diff --git a/modules/security/malware-scanner.sh b/modules/security/malware-scanner.sh index 793fe23..d00c1b7 100755 --- a/modules/security/malware-scanner.sh +++ b/modules/security/malware-scanner.sh @@ -102,6 +102,128 @@ show_scanner_installation_guide() { echo "" } +# Install all scanners at once +install_all_scanners() { + echo "" + print_header "Install All Malware Scanners" + + echo "This will install:" + echo " • ClamAV (free, open source)" + echo " • Maldet (free, Linux-specific)" + echo " • ImunifyAV (commercial, requires license)" + echo "" + echo -e "${YELLOW}Note: ImunifyAV requires a paid license to function.${NC}" + echo "" + + read -p "Proceed with installation? (yes/no): " confirm + + if [ "$confirm" != "yes" ]; then + echo "Cancelled." + read -p "Press Enter to continue..." + return 0 + fi + + echo "" + echo "==========================================" + echo "Installing Scanners" + echo "==========================================" + echo "" + + # Install ClamAV + if ! command -v clamscan &>/dev/null; then + echo -e "${CYAN}[1/3] Installing ClamAV...${NC}" + + if [ -f "/usr/local/cpanel/cpanel" ]; then + # cPanel method + /scripts/update_local_rpm_versions --edit target_settings.clamav installed + /scripts/check_cpanel_rpms --fix --targets=clamav + elif command -v yum &>/dev/null; then + yum install -y clamav clamav-update + elif command -v apt-get &>/dev/null; then + apt-get update && apt-get install -y clamav clamav-daemon + fi + + if command -v clamscan &>/dev/null; then + echo -e "${GREEN}✓ ClamAV installed${NC}" + # Update signatures + if command -v freshclam &>/dev/null; then + echo " Updating virus signatures..." + freshclam &>/dev/null || true + fi + else + echo -e "${RED}✗ ClamAV installation failed${NC}" + fi + else + echo -e "${GREEN}✓ ClamAV already installed${NC}" + fi + + echo "" + + # Install Maldet + if ! command -v maldet &>/dev/null; then + echo -e "${CYAN}[2/3] Installing Maldet...${NC}" + + cd /tmp + wget -q http://www.rfxn.com/downloads/maldetect-current.tar.gz + + if [ -f maldetect-current.tar.gz ]; then + tar -xzf maldetect-current.tar.gz + cd maldetect-* 2>/dev/null + ./install.sh &>/dev/null + cd /tmp + rm -rf maldetect-* + fi + + if command -v maldet &>/dev/null; then + echo -e "${GREEN}✓ Maldet installed${NC}" + # Update signatures + echo " Updating signatures..." + maldet -u &>/dev/null || true + else + echo -e "${RED}✗ Maldet installation failed${NC}" + fi + else + echo -e "${GREEN}✓ Maldet already installed${NC}" + fi + + echo "" + + # Install ImunifyAV + if ! command -v imunify-antivirus &>/dev/null; then + echo -e "${CYAN}[3/3] Installing ImunifyAV...${NC}" + echo -e "${YELLOW}Note: Requires license key to activate${NC}" + + cd /tmp + wget -q https://repo.imunify360.cloudlinux.com/defence360/imav-deploy.sh + + if [ -f imav-deploy.sh ]; then + bash imav-deploy.sh &>/dev/null + rm -f imav-deploy.sh + fi + + if command -v imunify-antivirus &>/dev/null; then + echo -e "${GREEN}✓ ImunifyAV installed${NC}" + echo -e "${YELLOW} Register with: imunify-antivirus register ${NC}" + else + echo -e "${RED}✗ ImunifyAV installation failed${NC}" + fi + else + echo -e "${GREEN}✓ ImunifyAV already installed${NC}" + fi + + echo "" + echo "==========================================" + echo "Installation Complete" + echo "==========================================" + echo "" + + # Re-detect scanners + detect_scanners + + echo "" + read -p "Press Enter to continue..." +} + # Detect control panel and gather docroots detect_control_panel() { docroot_array=() @@ -241,217 +363,6 @@ check_memory() { } # ImunifyAV scanner -scan_imunify() { - local scan_paths=("$@") - - if ! command -v imunify-antivirus &>/dev/null; then - echo -e "${RED}ImunifyAV not installed${NC}" - return 1 - fi - - echo -e "${CYAN}Starting ImunifyAV scan...${NC}" - echo "" - - # Update signatures - echo "→ Updating signatures..." - imunify-antivirus update 2>/dev/null - - # Queue scan paths - for path in "${scan_paths[@]}"; do - if [ -d "$path" ]; then - echo "→ Queuing: $path" - imunify-antivirus malware on-demand queue put "$path" 2>/dev/null - fi - done - - echo "" - echo -e "${GREEN}✓ Scan queued${NC}" - echo "" - echo "Monitor progress:" - echo " imunify-antivirus malware on-demand list" -} - -# ClamAV scanner -scan_clamav() { - local scan_paths=("$@") - - if ! command -v clamscan &>/dev/null; then - echo -e "${RED}ClamAV not installed${NC}" - return 1 - fi - - # Create log directory - local log_dir="$SCRIPT_DIR/logs/malware-scans" - mkdir -p "$log_dir" - - local log_file="$log_dir/clamav_$(date +%Y%m%d_%H%M%S).log" - - echo -e "${CYAN}Starting ClamAV scan...${NC}" - echo "" - - # Update signatures - if command -v freshclam &>/dev/null; then - echo "→ Updating signatures..." - freshclam 2>/dev/null || true - fi - - echo "→ Scanning paths..." - echo "" - - # Log scan details - { - echo "ClamAV Malware Scan" - echo "Date: $(date)" - echo "Paths:" - printf '%s\n' "${scan_paths[@]}" - echo "" - echo "Results:" - echo "========================================" - } > "$log_file" - - # Run scan - clamscan --infected --recursive "${scan_paths[@]}" >> "$log_file" 2>&1 & - local scan_pid=$! - - echo "Scan running in background (PID: $scan_pid)" - echo "Log file: $log_file" - echo "" - echo "Monitor with: tail -f $log_file" - - # Store scan info in reference DB - store_reference "malware_scan_clamav_latest" "$log_file" -} - -# Maldet scanner -scan_maldet() { - local scan_paths=("$@") - - if ! command -v maldet &>/dev/null; then - echo -e "${RED}Maldet not installed${NC}" - return 1 - fi - - # Create temp file with paths - local path_file="/tmp/maldet_paths_$$.txt" - printf '%s\n' "${scan_paths[@]}" > "$path_file" - - echo -e "${CYAN}Starting Maldet scan...${NC}" - echo "" - - # Update signatures - echo "→ Updating signatures..." - maldet -u 2>/dev/null || true - - echo "→ Starting scan..." - maldet -b -f "$path_file" - - rm -f "$path_file" - - echo "" - echo "View results:" - echo " maldet -l" -} - -# Run all available scanners sequentially -run_all_scanners() { - local scan_paths=("$@") - - if [ ${#scan_paths[@]} -eq 0 ]; then - echo -e "${RED}No paths to scan${NC}" - return 1 - fi - - # Create session ID for this multi-scanner run - local session_id="multiscan_$(date +%Y%m%d_%H%M%S)" - local report_file="$SCRIPT_DIR/logs/malware-scans/${session_id}_summary.txt" - mkdir -p "$SCRIPT_DIR/logs/malware-scans" - - echo "" - print_header "Multi-Scanner Session: $session_id" - - echo "Running ${#available_scanners[@]} scanner(s) on ${#scan_paths[@]} path(s)" - echo "Session report: $report_file" - echo "" - - # Initialize report - { - echo "==========================================" - echo "Multi-Scanner Malware Detection Report" - echo "==========================================" - echo "Session ID: $session_id" - echo "Date: $(date)" - echo "Scanners: ${available_scanners[*]}" - echo "Paths: ${#scan_paths[@]}" - echo "" - printf '%s\n' "${scan_paths[@]}" - echo "" - echo "==========================================" - echo "" - } > "$report_file" - - local scanner_num=1 - local total_scanners=${#available_scanners[@]} - - # Run each scanner - for scanner in "${available_scanners[@]}"; do - echo -e "${CYAN}[$scanner_num/$total_scanners] Starting ${scanner^} scan...${NC}" - echo "" - - { - echo "Scanner: ${scanner^}" - echo "Started: $(date)" - echo "---" - } >> "$report_file" - - case "$scanner" in - imunify) - scan_imunify "${scan_paths[@]}" | tee -a "$report_file" - ;; - clamav) - scan_clamav "${scan_paths[@]}" | tee -a "$report_file" - ;; - maldet) - scan_maldet "${scan_paths[@]}" | tee -a "$report_file" - ;; - esac - - echo "" | tee -a "$report_file" - echo "---" >> "$report_file" - echo "" >> "$report_file" - - ((scanner_num++)) - - # Wait a moment between scanners - if [ $scanner_num -le $total_scanners ]; then - echo "" - echo "Waiting 3 seconds before next scanner..." - sleep 3 - echo "" - fi - done - - # Finalize report - { - echo "==========================================" - echo "Multi-Scanner Session Complete" - echo "Completed: $(date)" - echo "==========================================" - } >> "$report_file" - - echo "" - echo -e "${GREEN}✓ All scanners completed${NC}" - echo "" - echo "Session report saved: $report_file" - echo "" - echo "View individual scanner results using option 5 from main menu" - - # Store in reference database - store_reference "malware_multiscan_latest" "$session_id" - store_reference "malware_multiscan_${session_id}" "$report_file" - - echo "" - read -p "Press Enter to continue..." -} # Generate standalone malware scan script generate_standalone_scanner() { @@ -891,12 +802,13 @@ compare_scan_results() { # Launch standalone scanner menu launch_standalone_scanner_menu() { + local preset_scope="$1" # Optional: server, user, domain, custom + echo "" print_header "Launch Standalone Scanner" echo "This will create a self-contained scanner in /opt/ that runs" - echo "independently in a screen session. You can safely delete the" - echo "toolkit after launching - the scan will continue running." + echo "independently. You can safely delete the toolkit after launching." echo "" if ! detect_control_panel; then @@ -908,19 +820,30 @@ launch_standalone_scanner_menu() { echo "Available Scanners: ${available_scanners[*]}" echo "" - echo "Select scan scope:" - echo " 1. Entire server (all docroots)" - echo " 2. Specific user account" - echo " 3. Specific domain" - echo " 4. Custom path" - echo " 0. Cancel" - echo "" - - read -p "Select option: " scope_choice - + local scope_choice local scan_paths=() local scan_description="" + # If preset scope provided, use it; otherwise show menu + if [ -n "$preset_scope" ]; then + case "$preset_scope" in + server) scope_choice=1 ;; + user) scope_choice=2 ;; + domain) scope_choice=3 ;; + custom) scope_choice=4 ;; + *) scope_choice=0 ;; + esac + else + echo "Select scan scope:" + echo " 1. Entire server (all docroots)" + echo " 2. Specific user account" + echo " 3. Specific domain" + echo " 4. Custom path" + echo " 0. Cancel" + echo "" + read -p "Select option: " scope_choice + fi + case $scope_choice in 1) # Entire server @@ -1045,6 +968,173 @@ launch_standalone_scanner_menu() { generate_standalone_scanner "${scan_paths[@]}" } +# Check status of all standalone scanners +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)) + + if [ ${#standalone_dirs[@]} -eq 0 ]; then + echo "No standalone scanner sessions found." + echo "" + read -p "Press Enter to continue..." + return 0 + fi + + echo "Active Sessions:" + echo "" + + local running_count=0 + local completed_count=0 + local error_count=0 + + for dir in "${standalone_dirs[@]}"; do + local session_name=$(basename "$dir") + + # Check if still running + if pgrep -f "$dir/scan.sh" > /dev/null 2>&1; then + echo -e " ${GREEN}●${NC} $session_name [RUNNING]" + ((running_count++)) + + # Show progress if available + if [ -f "$dir/logs/session.log" ]; then + local last_log=$(tail -1 "$dir/logs/session.log" 2>/dev/null) + echo " Latest: $last_log" + fi + elif [ -f "$dir/results/summary.txt" ]; then + # Check if completed successfully + if grep -q "Multi-Scanner Session Complete\|Scan session ended" "$dir/results/summary.txt" 2>/dev/null; then + echo -e " ${CYAN}✓${NC} $session_name [COMPLETED]" + ((completed_count++)) + + # Show infected count if available + if [ -f "$dir/results/infected_files.txt" ] && [ -s "$dir/results/infected_files.txt" ]; then + local infected_count=$(wc -l < "$dir/results/infected_files.txt") + echo -e " Found: ${RED}$infected_count infected files${NC}" + fi + else + echo -e " ${RED}✗${NC} $session_name [ERROR/INCOMPLETE]" + ((error_count++)) + fi + else + echo -e " ${YELLOW}?${NC} $session_name [UNKNOWN - no results yet]" + fi + + echo "" + done + + echo "Summary:" + echo " Running: $running_count" + echo " Completed: $completed_count" + echo " Errors: $error_count" + echo " Total: ${#standalone_dirs[@]}" + + echo "" + read -p "Press Enter to continue..." +} + +# Delete standalone scanner sessions +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)) + + if [ ${#standalone_dirs[@]} -eq 0 ]; then + echo "No standalone scanner sessions found." + echo "" + read -p "Press Enter to continue..." + return 0 + fi + + echo "Available sessions:" + echo "" + + # List sessions with status + local i=1 + for dir in "${standalone_dirs[@]}"; do + local session_name=$(basename "$dir") + local status="completed" + + if pgrep -f "$dir/scan.sh" > /dev/null 2>&1; then + status="${GREEN}running${NC}" + fi + + echo -e " $i. $session_name [$status]" + ((i++)) + done + + echo "" + echo " A. Delete all completed sessions" + echo " 0. Cancel" + echo "" + + read -p "Select session to delete (or A for all completed): " delete_choice + + case "$delete_choice" in + 0) + return 0 + ;; + [Aa]) + # Delete all completed sessions + echo "" + local deleted=0 + for dir in "${standalone_dirs[@]}"; do + if ! pgrep -f "$dir/scan.sh" > /dev/null 2>&1; then + echo "Deleting: $(basename $dir)" + rm -rf "$dir" + ((deleted++)) + fi + done + echo "" + echo -e "${GREEN}✓ Deleted $deleted completed session(s)${NC}" + ;; + *) + # Delete specific session + if [ "$delete_choice" -lt 1 ] || [ "$delete_choice" -gt ${#standalone_dirs[@]} ]; then + echo -e "${RED}Invalid choice${NC}" + read -p "Press Enter to continue..." + return 1 + fi + + local selected_dir="${standalone_dirs[$((delete_choice-1))]}" + local session_name=$(basename "$selected_dir") + + # Check if running + if pgrep -f "$selected_dir/scan.sh" > /dev/null 2>&1; then + echo "" + echo -e "${YELLOW}Warning: This scan is currently running!${NC}" + read -p "Stop scan and delete? (yes/no): " confirm_running + + if [ "$confirm_running" = "yes" ]; then + pkill -f "$selected_dir/scan.sh" + sleep 1 + rm -rf "$selected_dir" + echo -e "${GREEN}✓ Stopped and deleted: $session_name${NC}" + else + echo "Cancelled." + fi + else + echo "" + read -p "Delete $session_name? (yes/no): " confirm_delete + + if [ "$confirm_delete" = "yes" ]; then + rm -rf "$selected_dir" + echo -e "${GREEN}✓ Deleted: $session_name${NC}" + else + echo "Cancelled." + fi + fi + ;; + esac + + echo "" + read -p "Press Enter to continue..." +} + # Main scan menu show_scan_menu() { while true; do @@ -1056,19 +1146,20 @@ show_scan_menu() { done echo "" - echo "Scan Scope:" + echo -e "${CYAN}Create New Scan:${NC}" echo " 1. Scan entire server" echo " 2. Scan specific user" echo " 3. Scan specific domain" echo " 4. Scan custom path" echo "" - echo -e "${GREEN}Standalone Scanner:${NC}" - echo " 5. Launch standalone scanner (runs in background, toolkit-independent)" - echo "" - echo "Results & Management:" + echo -e "${CYAN}Monitor & Manage:${NC}" + echo " 5. Check scan status" echo " 6. View scan results" - echo " 7. Compare scanner results" - echo " 8. Scanner settings" + echo " 7. Delete scan sessions" + echo "" + echo -e "${CYAN}Configuration:${NC}" + echo " 8. Install all scanners" + echo " 9. Scanner settings" echo "" echo " 0. Back to main menu" echo "" @@ -1076,246 +1167,21 @@ show_scan_menu() { read -p "Select option: " choice case $choice in - 1) scan_entire_server ;; - 2) scan_user_account ;; - 3) scan_domain ;; - 4) scan_custom_path ;; - 5) launch_standalone_scanner_menu ;; + 1) launch_standalone_scanner_menu "server" ;; + 2) launch_standalone_scanner_menu "user" ;; + 3) launch_standalone_scanner_menu "domain" ;; + 4) launch_standalone_scanner_menu "custom" ;; + 5) check_standalone_status ;; 6) view_scan_results ;; - 7) compare_scan_results ;; - 8) scanner_settings ;; + 7) delete_standalone_sessions ;; + 8) install_all_scanners ;; + 9) scanner_settings ;; 0) return 0 ;; *) echo -e "${RED}Invalid option${NC}"; sleep 1 ;; esac done } -# Scan entire server -scan_entire_server() { - echo "" - print_header "Full Server Scan" - - if ! detect_control_panel; then - read -p "Press Enter to continue..." - return 1 - fi - - echo "Control Panel: ${CONTROL_PANEL^}" - echo "Docroots found: ${#sanitized_docroot[@]}" - echo "" - - if ! check_memory; then - read -p "Press Enter to continue..." - return 1 - fi - - echo "Select scanner:" - local i=1 - for scanner in "${available_scanners[@]}"; do - echo " $i. ${scanner^}" - ((i++)) - done - echo " $i. All Available Scanners (run sequentially)" - echo "" - - read -p "Scanner: " scanner_choice - - # Check for "All Scanners" option - if [ "$scanner_choice" -eq "$i" ]; then - run_all_scanners "${sanitized_docroot[@]}" - elif [ "$scanner_choice" -lt 1 ] || [ "$scanner_choice" -gt ${#available_scanners[@]} ]; then - echo -e "${RED}Invalid choice${NC}" - read -p "Press Enter to continue..." - return 1 - else - local selected_scanner="${available_scanners[$((scanner_choice-1))]}" - - case "$selected_scanner" in - imunify) scan_imunify "${sanitized_docroot[@]}" ;; - clamav) scan_clamav "${sanitized_docroot[@]}" ;; - maldet) scan_maldet "${sanitized_docroot[@]}" ;; - esac - fi - - echo "" - read -p "Press Enter to continue..." -} - -# Scan user account -scan_user_account() { - echo "" - print_header "Scan User Account" - - if ! detect_control_panel; then - read -p "Press Enter to continue..." - return 1 - fi - - # Use user manager to select user - select_user_interactive "Select user to scan" - - if [ -z "$SELECTED_USER" ]; then - echo "No user selected" - read -p "Press Enter to continue..." - return 1 - fi - - echo "" - echo "Getting docroots for: $SELECTED_USER" - - local user_paths=($(get_user_docroots "$SELECTED_USER")) - - if [ ${#user_paths[@]} -eq 0 ]; then - echo -e "${RED}No docroots found for user${NC}" - read -p "Press Enter to continue..." - return 1 - fi - - echo "Paths to scan: ${#user_paths[@]}" - printf ' %s\n' "${user_paths[@]}" - echo "" - - echo "Select scanner:" - local i=1 - for scanner in "${available_scanners[@]}"; do - echo " $i. ${scanner^}" - ((i++)) - done - echo " $i. All Available Scanners (run sequentially)" - echo "" - - read -p "Scanner: " scanner_choice - - # Check for "All Scanners" option - if [ "$scanner_choice" -eq "$i" ]; then - run_all_scanners "${user_paths[@]}" - elif [ "$scanner_choice" -lt 1 ] || [ "$scanner_choice" -gt ${#available_scanners[@]} ]; then - echo -e "${RED}Invalid choice${NC}" - read -p "Press Enter to continue..." - return 1 - else - local selected_scanner="${available_scanners[$((scanner_choice-1))]}" - - case "$selected_scanner" in - imunify) scan_imunify "${user_paths[@]}" ;; - clamav) scan_clamav "${user_paths[@]}" ;; - maldet) scan_maldet "${user_paths[@]}" ;; - esac - fi - - echo "" - read -p "Press Enter to continue..." -} - -# Scan domain -scan_domain() { - echo "" - print_header "Scan Domain" - - if ! detect_control_panel; then - read -p "Press Enter to continue..." - return 1 - fi - - read -p "Enter domain name: " domain - - if [ -z "$domain" ]; then - echo "No domain entered" - read -p "Press Enter to continue..." - return 1 - fi - - local domain_path=$(get_domain_docroot "$domain") - - if [ -z "$domain_path" ] || [ ! -d "$domain_path" ]; then - echo -e "${RED}Domain not found or docroot doesn't exist${NC}" - read -p "Press Enter to continue..." - return 1 - fi - - echo "Docroot: $domain_path" - echo "" - - echo "Select scanner:" - local i=1 - for scanner in "${available_scanners[@]}"; do - echo " $i. ${scanner^}" - ((i++)) - done - echo " $i. All Available Scanners (run sequentially)" - echo "" - - read -p "Scanner: " scanner_choice - - # Check for "All Scanners" option - if [ "$scanner_choice" -eq "$i" ]; then - run_all_scanners "$domain_path" - elif [ "$scanner_choice" -lt 1 ] || [ "$scanner_choice" -gt ${#available_scanners[@]} ]; then - echo -e "${RED}Invalid choice${NC}" - read -p "Press Enter to continue..." - return 1 - else - local selected_scanner="${available_scanners[$((scanner_choice-1))]}" - - case "$selected_scanner" in - imunify) scan_imunify "$domain_path" ;; - clamav) scan_clamav "$domain_path" ;; - maldet) scan_maldet "$domain_path" ;; - esac - fi - - echo "" - read -p "Press Enter to continue..." -} - -# Scan custom path -scan_custom_path() { - echo "" - print_header "Scan Custom Path" - - read -p "Enter path to scan: " custom_path - - if [ -z "$custom_path" ] || [ ! -d "$custom_path" ]; then - echo -e "${RED}Path doesn't exist${NC}" - read -p "Press Enter to continue..." - return 1 - fi - - echo "Path: $custom_path" - echo "" - - echo "Select scanner:" - local i=1 - for scanner in "${available_scanners[@]}"; do - echo " $i. ${scanner^}" - ((i++)) - done - echo " $i. All Available Scanners (run sequentially)" - echo "" - - read -p "Scanner: " scanner_choice - - # Check for "All Scanners" option - if [ "$scanner_choice" -eq "$i" ]; then - run_all_scanners "$custom_path" - elif [ "$scanner_choice" -lt 1 ] || [ "$scanner_choice" -gt ${#available_scanners[@]} ]; then - echo -e "${RED}Invalid choice${NC}" - read -p "Press Enter to continue..." - return 1 - else - local selected_scanner="${available_scanners[$((scanner_choice-1))]}" - - case "$selected_scanner" in - imunify) scan_imunify "$custom_path" ;; - clamav) scan_clamav "$custom_path" ;; - maldet) scan_maldet "$custom_path" ;; - esac - fi - - echo "" - read -p "Press Enter to continue..." -} - # View scan results view_scan_results() { echo ""