diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 92be01a..9dc619a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,7 +7,6 @@ Please include a summary of the changes and the related issue. Describe the over ## Checklist - [ ] Ensure version updates in `composer.json` and the plugin main file. -- [ ] Verify the Yara rules integration in `src/rules/malware_rules.yar`. - [ ] Check the updates in the `README.md` file if applicable. - [ ] Attach the Jira link for tracking this PR. diff --git a/README.md b/README.md index 886505b..0311b8a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Overview -The Malware Scanner Plugin is a robust security solution designed for WordPress websites, helping site administrators detect, manage, and resolve threats effectively. The plugin utilizes YARA rules for advanced threat detection and provides an intuitive interface for managing scans and system security. +The Malware Scanner Plugin is a robust security solution designed for WordPress websites, helping site administrators detect, manage, and resolve threats effectively. The plugin utilizes advanced malware detection algorithms and provides an intuitive interface for managing scans and system security. ## Features @@ -41,7 +41,7 @@ Detailed user guide for setup, running scans, scheduling, and troubleshooting. ## How It Works -The plugin leverages YARA rules to identify malicious patterns in code. Administrators can configure scans to meet their needs, monitor real-time progress, and resolve threats with ease. The user-friendly interface ensures smooth navigation and efficient management. +The plugin leverages advanced pattern recognition to identify malicious code. Administrators can configure scans to meet their needs, monitor real-time progress, and resolve threats with ease. The user-friendly interface ensures smooth navigation and efficient management. ## License diff --git a/composer.json b/composer.json index ca6e3d0..83fe5f9 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "vplugins/malware-scanner", - "description": "A powerful malware scanner plugin for WordPress using the Yara framework.", + "description": "A powerful malware scanner plugin for WordPress using advanced malware detection algorithms.", "type": "wordpress-plugin", "license": "MIT", "authors": [ diff --git a/src/Admin/AdminPage.php b/src/Admin/AdminPage.php index ffab96d..3e75933 100644 --- a/src/Admin/AdminPage.php +++ b/src/Admin/AdminPage.php @@ -75,20 +75,20 @@ public function add_admin_menu() { * Enqueue CSS, JS, and Dashicons for the admin pages. */ function enqueue_admin_assets() { - // Enqueue Admin CSS + // Enqueue Admin CSS with cache busting wp_enqueue_style( 'malware-scanner-admin-css', // Handle plugin_dir_url(__FILE__) . 'assets/css/admin-styles.css', // Path to CSS file [], // Dependencies - '1.0' // Version + '2.6.0' // Version (comprehensive scanning improvements and diagnostics) ); - // Enqueue Admin JS + // Enqueue Admin JS with cache busting wp_enqueue_script( 'malware-scanner-admin-js', // Handle plugin_dir_url(__FILE__) . 'assets/js/admin-scripts.js', // Path to JS file ['jquery', 'chart-js'], // Dependencies (jQuery, Chart.js) - '1.0', // Version + '2.6.0', // Version (comprehensive scanning improvements and diagnostics) true // Load in footer ); diff --git a/src/Admin/assets/css/admin-styles.css b/src/Admin/assets/css/admin-styles.css index 31a518f..405d774 100644 --- a/src/Admin/assets/css/admin-styles.css +++ b/src/Admin/assets/css/admin-styles.css @@ -1,19 +1,13 @@ .wrap { margin-top: 20px; - background: #f0f0f1; - color: #3c434a; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; - font-size: 13px; - line-height: 1.4em; } .wrap h1 { font-size: 1.3125rem; flex-grow: 1; - color: #2d2d2d; - font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #23282d; line-height: 1.5rem; - font-weight: 700; + font-weight: 600; padding: 0; } @@ -24,7 +18,7 @@ .modal-content { background-color: #fff; padding: 20px; - border-radius: 8px; + border-radius: 3px; } .close { @@ -40,17 +34,17 @@ } .heading-row { - background-color: #1B719E; + background-color: #0073aa; color: #fff; - padding: 25px; - border-radius: 5px; + padding: 20px; + border-radius: 3px; } .heading-row .section-title { margin: 0; font-size: 18px; color: #fff; - font-weight: 400; + font-weight: 600; } .metric-container { @@ -71,11 +65,11 @@ } .metric-card { - background-color: #f7f7f7; - border-radius: 8px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 3px; padding: 20px; text-align: center; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); flex-direction: column; display: flex; justify-content: center; @@ -84,21 +78,21 @@ .metric-card h3 { margin-top: 10px; - font-size: 17px; + font-size: 16px; font-weight: 600; - color: #333; + color: #23282d; margin-bottom: 5px; } .metric-card p { margin: 5px 0; font-size: 14px; - color: #555; + color: #666; } .icon { - font-size: 40px; - margin-bottom: 20px; + font-size: 32px; + margin-bottom: 15px; color: #0073aa; } @@ -109,28 +103,22 @@ } .metric-box .dashicons { - font-size: 30px; - color: #1B719E; - padding-bottom: 15px; -} - -.metric-box .dashicons .dashicons-clock { - padding-bottom: 0px; -} - -.dashicons-bell:before { - vertical-align: middle; + font-size: 24px; + color: #0073aa; + padding-bottom: 10px; } .metric-box h3 { - font-size: 13px; - margin-bottom: 10px; + font-size: 14px; + margin-bottom: 8px; + font-weight: 600; } .metric-box p { - font-size: 17px; - font-weight: bold; + font-size: 16px; + font-weight: 600; margin: 0; + color: #23282d; } .chart-box { @@ -154,7 +142,6 @@ margin-bottom: 20px; grid-template-columns: repeat(2, 1fr); } - } .section-title { @@ -162,6 +149,7 @@ font-weight: 600; margin-bottom: 15px; margin: 0em 0 0em 0 !important; + color: #23282d; } .section-title-tab { @@ -169,45 +157,45 @@ font-weight: 600; margin-bottom: 15px; margin: 0em 0 1em 0 !important; + color: #23282d; } .recent-activity-section, .status-section { - margin-bottom: 30px; + margin-bottom: 20px; padding: 20px; - background-color: #f9f9f9; + background-color: #fff; border: 1px solid #ddd; - border-radius: 8px; + border-radius: 3px; } .recent-activity-section h2 { - margin : 0px; + margin: 0px; + color: #23282d; + font-weight: 600; } .recent-activity-section table { width: 100%; border-collapse: collapse; - margin-top: 10px; + margin-top: 15px; } .recent-activity-section th, .recent-activity-section td { - padding: 8px; + padding: 8px 10px; text-align: left; border-bottom: 1px solid #ddd; } .recent-activity-section th { - background-color: #e6e6e6; - font-weight: 700; + background-color: #f1f1f1; + font-weight: 600; + color: #23282d; } .recent-activity-section tbody tr:nth-child(even) { - background-color: #f5f5f5; -} - -.recent-activity-section, .status-section { - margin-bottom: 20px; + background-color: #f9f9f9; } .system-wrapper { @@ -220,9 +208,9 @@ .status-section { width: calc(50% - 10px); - border: 1px solid #ccc; + border: 1px solid #ddd; padding: 20px; - border-radius: 8px; + border-radius: 3px; box-sizing: border-box; } @@ -247,12 +235,6 @@ justify-content: space-between; } -.actions-section .button-primary, -.actions-section .button-secondary { - padding: 6px 12px; - font-size: 14px; -} - .header-row { display: flex; justify-content: space-between; @@ -260,23 +242,13 @@ margin-bottom: 20px; } -.wp-core-ui .button-primary { - padding: 2px 10px; -} - -.wp-core-ui .button, -.wp-core-ui .button-secondary { - padding: 2px 10px; -} - .malware-help-content { - font-family: Arial, sans-serif; max-width: 100%; margin: 20px 0px 20px 0px; padding: 20px; background-color: #fff; - border-radius: 8px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border: 1px solid #ddd; + border-radius: 3px; } .malware-help-content .header-row { @@ -290,8 +262,9 @@ .help-section h2 { font-size: 18px; color: #0073aa; - border-bottom: 1px solid #0073aa; + border-bottom: 1px solid #ddd; padding-bottom: 5px; + font-weight: 600; } .help-section ol { @@ -312,52 +285,311 @@ text-decoration: underline; } -.button-primary { - margin-top: 15px; - font-size: 18px; - padding: 10px 20px; +/* =============================================== + BATCH SCANNING UI - WORDPRESS STYLE + =============================================== */ + +/* Scan Controls */ +.scan-controls { + display: flex; + gap: 10px; + align-items: center; + margin-bottom: 20px; +} + +.scan-controls .button-primary { + background: #0073aa; + border-color: #0073aa; + color: #fff; +} + +.scan-controls .button-primary:hover:not(:disabled) { + background: #005177; + border-color: #005177; +} + +.scan-controls .button-primary:disabled { + background: #ddd; + border-color: #ddd; + color: #999; + cursor: not-allowed; +} + +#cancel-scan { + background: #a00; + border-color: #a00; + color: #fff; +} + +#cancel-scan:hover { + background: #900; + border-color: #900; +} + +/* Real-time Progress Section */ +.scan-info-grid { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 20px; + margin: 20px 0; } .file-count-info { - margin: 10px 0; + display: flex; + flex-direction: column; + gap: 15px; +} + +.count-item { + background: #fff; + padding: 15px; + border: 1px solid #ddd; + border-radius: 3px; + border-left: 4px solid #0073aa; +} + +.count-value { + display: inline-block; + font-weight: 600; + color: #0073aa; font-size: 16px; + margin-left: 10px; + min-width: 50px; +} + +.progress-container { + display: flex; + flex-direction: column; + justify-content: center; + gap: 10px; } .progress-bar { width: 100%; height: 20px; - background-color: #f1f1f1; - border-radius: 10px; + background: #f1f1f1; + border: 1px solid #ddd; + border-radius: 3px; overflow: hidden; } #progress-bar-fill { width: 0%; height: 100%; - background-color: #28a745; - transition: width 0.5s; + background: #0073aa; + transition: width 0.3s ease; +} + +#progress-status { + margin: 0; + font-size: 14px; + color: #666; + text-align: center; + background: #f9f9f9; + padding: 8px 15px; + border: 1px solid #ddd; + border-radius: 3px; +} + +/* Status Badges */ +.status-badge { + display: inline-block; + padding: 3px 8px; + border-radius: 3px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + text-align: center; + min-width: 60px; +} + +.status-ignored { + background: #ffb900; + color: #fff; +} + +.status-removed { + background: #a00; + color: #fff; +} + +.status-cleaned { + background: #46b450; + color: #fff; +} + +.status-quarantined { + background: #f56e28; + color: #fff; +} + +.status-infected { + background: #dc3232; + color: #fff; +} + +/* Enhanced Results Table */ +.scan-results-section { + background-color: #fff; + border: 1px solid #ddd; + border-radius: 3px; + padding: 20px; + margin: 20px 0; +} + +.scan-summary { + background: #f9f9f9; + padding: 15px; + border: 1px solid #ddd; + border-radius: 3px; + margin-bottom: 20px; + border-left: 4px solid #0073aa; +} + +.summary-item { + display: flex; + justify-content: space-between; + margin-bottom: 5px; +} + +.summary-label { + font-weight: 600; + color: #23282d; +} + +.summary-value { + color: #0073aa; + font-weight: 600; } .scan-results-table { margin-top: 20px; width: 100%; + border: 1px solid #ddd; + border-radius: 3px; + border-collapse: collapse; } .scan-results-table th { text-align: left; + background: #f1f1f1; + color: #23282d; + font-weight: 600; + padding: 12px; + border-bottom: 1px solid #ddd; +} + +.scan-results-table td { + padding: 10px 12px; + vertical-align: middle; + border-bottom: 1px solid #f1f1f1; +} + +.scan-results-table tbody tr:hover { + background-color: #f9f9f9; +} + +.scan-results-table .no-results td { + text-align: center; + padding: 30px 20px; + color: #666; + font-style: italic; +} + +/* Action Buttons */ +.action-buttons { + display: flex; + gap: 10px; + justify-content: center; + flex-wrap: wrap; + margin-top: 20px; +} + +#quarantine-files { + background: #f56e28; + border-color: #f56e28; + color: #fff; +} + +#delete-files { + background: #a00; + border-color: #a00; + color: #fff; +} + +#export-results { + background: #666; + border-color: #666; + color: #fff; +} + +/* Loading Spinners */ +.wsp-spinner { + border: 2px solid #f3f3f3; + border-top: 2px solid #0073aa; + border-radius: 50%; + width: 16px; + height: 16px; + animation: spin 1s linear infinite; + display: inline-block; + margin-left: 5px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Responsive Design for Batch Scanning */ +@media (max-width: 768px) { + .scan-info-grid { + grid-template-columns: 1fr; + gap: 15px; + } + + .scan-controls { + flex-direction: column; + align-items: stretch; + } + + .scan-controls .button { + width: 100%; + margin-bottom: 10px; + } + + .action-buttons { + flex-direction: column; + } + + .action-buttons .button { + width: 100%; + margin-bottom: 10px; + } + + .scan-results-table { + font-size: 12px; + } + + .scan-results-table th, + .scan-results-table td { + padding: 8px 6px; + } } +/* File severity indicators */ .severity-high { - color: red !important; - font-weight: bold; + color: #dc3232 !important; + font-weight: 600; } .severity-medium { - color: orange !important; + color: #ffb900 !important; + font-weight: 600; } .severity-low { - color: green !important; + color: #46b450 !important; + font-weight: 600; } .scan-actions { @@ -370,15 +602,18 @@ } #ignored-files-accordion h3 { - background: #007cba; + background: #0073aa; color: #fff; padding: 10px; cursor: pointer; margin: 0; + font-weight: 600; } #ignored-files-accordion > div { padding: 10px; + background: #fff; + border: 1px solid #ddd; } .scan-data-table-wrapper { @@ -403,80 +638,73 @@ list-style: none; margin: 0; padding: 0; - font-family: Arial, sans-serif; } /* Root list styling */ .folder-structure > li { - margin: 10px 0; + margin: 8px 0; } /* Checkbox and label spacing */ .folder-structure input[type="checkbox"] { - margin-right: 5px; + margin-right: 8px; } /* Label text styling */ .folder-structure label { cursor: pointer; - font-weight: 500; - color: #333; + font-weight: 400; + color: #23282d; } /* Nested list styling */ .folder-structure ul { list-style: none; - padding-left: 20px; /* Indent nested folders */ - border-left: 2px solid #ddd; /* Optional vertical line for nesting */ + padding-left: 20px; + border-left: 1px solid #ddd; margin: 5px 0; } /* Add icons to folders */ .folder-structure > li > label::before, .folder-structure ul li > label::before { - content: "đ"; /* Folder icon */ + content: "đ"; margin-right: 6px; - color: #f39c12; } /* Subfolder styling */ .folder-structure ul li > label::before { - content: "đ"; /* Subfolder icon */ - color: #3498db; + content: "đ"; } /* Hover effect for labels */ .folder-structure label:hover { color: #0073aa; - text-decoration: underline; -} - -.hidden { - display: none; } .folder-toggle { cursor: pointer; - font-weight: bold; + font-weight: 600; margin-right: 5px; + color: #0073aa; } .folder-structure ul { - margin-left: 20px; /* Indent nested folders */ + margin-left: 20px; } .ignore-file-types { margin-top: 20px; - background-color: #f9f9f9; + background-color: #fff; padding: 15px; border: 1px solid #ddd; - border-radius: 8px; + border-radius: 3px; } .ignore-file-types h4 { margin-bottom: 15px; font-weight: 600; - color: #007cba; + color: #23282d; } .file-types-list { @@ -486,23 +714,22 @@ } .file-type-item { - flex: 1 1 150px; + flex: 1 1 140px; display: flex; align-items: center; - background-color: #fff; - padding: 8px; - border: 1px solid #e0e0e0; - border-radius: 6px; - transition: all 0.3s ease; + background-color: #f9f9f9; + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 3px; } .file-type-item:hover { - border-color: #007cba; - background-color: #f1f9ff; + border-color: #0073aa; + background-color: #f1f1f1; } .file-type-item input[type="checkbox"] { - margin-right: 10px; + margin-right: 8px; cursor: pointer; } @@ -511,33 +738,13 @@ display: inline-block; width: 12px; height: 12px; - border: 2px solid #ccc; - border-top-color: #0073aa; /* WordPress blue */ + border: 2px solid #ddd; + border-top-color: #0073aa; border-radius: 50%; animation: spin 0.8s linear infinite; margin-left: 5px; } -@keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } -} - -.wsp-spinner { - border: 3px solid #f3f3f3; - border-top: 3px solid #0073aa; /* WordPress blue */ - border-radius: 50%; - width: 15px; - height: 15px; - animation: spin 1s linear infinite; - display: inline-block; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - /* Styling for the header row */ .header-row { display: flex; @@ -546,15 +753,11 @@ } .intro-text { - font-size: 16px; - color: #333; + font-size: 14px; + color: #666; margin-bottom: 20px; } -.widefat td { - vertical-align: middle; -} - /* Styling for the cleanup option */ .cleanup-option { margin-top: 20px; @@ -563,11 +766,12 @@ gap: 10px; } -/* Help Page Starts */ +/* Help Page */ .malware-help-header h1 { - font-size: 28px; + font-size: 24px; margin-bottom: 20px; - color: #2c3e50; + color: #23282d; + font-weight: 600; } .malware-help-grid { @@ -577,30 +781,25 @@ } .help-card { - background: #f8f9fa; + background: #fff; + border: 1px solid #ddd; border-left: 4px solid #0073aa; padding: 20px; - border-radius: 8px; - box-shadow: 0 2px 5px rgba(0,0,0,0.05); - transition: all 0.3s ease; -} - -.help-card:hover { - border-left-color: #00a0d2; - background: #fff; - box-shadow: 0 4px 10px rgba(0,0,0,0.08); + border-radius: 3px; } .help-card h2 { font-size: 16px; color: #0073aa; margin-top: 0; + font-weight: 600; } .help-card h3 { margin-top: 15px; font-size: 14px; - color: #444; + color: #23282d; + font-weight: 600; } .help-card ol, @@ -615,33 +814,37 @@ .support-link { color: #0073aa; + text-decoration: none; +} + +.support-link:hover { text-decoration: underline; } -/* Help Page End */ -/* Settings Page Starts */ +/* Settings Page */ .malware-settings-wrapper { max-width: 100%; margin: 20px 0px 20px 0px; - padding: 30px; + padding: 20px; background: #fff; - border-radius: 12px; - box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05); - font-family: "Segoe UI", Roboto, sans-serif; + border: 1px solid #ddd; + border-radius: 3px; } .malware-settings-wrapper h1 { - font-size: 26px; + font-size: 23px; margin-bottom: 20px; display: flex; align-items: center; gap: 10px; + color: #23282d; + font-weight: 600; } .settings-section { margin-bottom: 15px; - padding-bottom: 10px; - border-bottom: 1px solid #eee; + padding-bottom: 15px; + border-bottom: 1px solid #ddd; } .settings-section:last-child { @@ -652,15 +855,17 @@ font-weight: 600; display: block; margin-bottom: 6px; + color: #23282d; } .settings-section input[type="email"], .settings-section select, .settings-section textarea { width: 100%; - padding: 10px 12px; - border: 1px solid #ccc; - border-radius: 6px; + max-width: 500px; + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 3px; font-size: 14px; } @@ -679,60 +884,65 @@ } .settings-submit { - text-align: right; - margin-top: 30px; -} - -.settings-submit input[type="submit"] { - background-color: #2271b1; - color: white; - border: none; - padding: 10px 20px; - border-radius: 6px; - cursor: pointer; - font-size: 15px; + text-align: left; + margin-top: 20px; } -/* Settings Page Ends */ -/* Notification Section Start */ +/* Notification Section */ .malware-notice { - padding: 1px 15px; - border-radius: 5px; - margin: 10px 0; + padding: 12px 15px; + border-radius: 3px; + margin: 15px 0; font-weight: 500; - border-left: 5px solid; + border-left: 4px solid; } .notice-success { - background-color: #e6f4ea; - color: #276738; - border-color: #3c763d; + background-color: #f0f8ff; + color: #155724; + border-color: #46b450; } .notice-warning { - background-color: #fff9e6; - color: #7c5b00; - border-color: #ffcc00; + background-color: #fffbf0; + color: #856404; + border-color: #ffb900; } .notice-error { - background-color: #fdecea; - color: #a12622; - border-color: #d93025; + background-color: #ffeaea; + color: #721c24; + border-color: #dc3232; } -/* Notification Section Ends */ -/* Php Security Section Start */ +/* System Status Section */ .system-status-list { display: grid; grid-template-columns: repeat(2, 1fr); - gap: 10px 30px; /* vertical and horizontal gaps */ + gap: 10px 30px; list-style: none; padding-left: 0; } + .system-status-list li { background: #f9f9f9; padding: 8px 12px; - border-radius: 4px; + border: 1px solid #ddd; + border-radius: 3px; } -/* Php Security Section End */ \ No newline at end of file + +/* Folder structure panel */ +.folder-structure-panel { + background: #fff; + border: 1px solid #ddd; + border-radius: 3px; + padding: 15px; + margin: 15px 0; +} + +.folder-structure-panel h4 { + margin-top: 0; + margin-bottom: 15px; + font-weight: 600; + color: #23282d; +} \ No newline at end of file diff --git a/src/Admin/assets/js/admin-scripts.js b/src/Admin/assets/js/admin-scripts.js index 4eb7b8a..d6ebe03 100644 --- a/src/Admin/assets/js/admin-scripts.js +++ b/src/Admin/assets/js/admin-scripts.js @@ -7,8 +7,10 @@ document.getElementById('start-scan').addEventListener('click', function () { const ignoreFileTypes = Array.from(document.querySelectorAll('input[name="ignore_file_types[]"]:checked')) .map(fileType => fileType.value); - // Pass the values to the startScan function - startScan(ignoreFolders, ignoreFileTypes); + // Note: Folder and file type selections are processed server-side + + // Pass the values to the batch scan function + startBatchScan(ignoreFolders, ignoreFileTypes); }); function showLoadingSpinner(element) { @@ -27,7 +29,21 @@ function toggleFolder(element) { } } -function startScan(ignoreFolders, ignoreFileTypes) { +// Global variables for batch scanning +let currentScanSession = null; +let progressPollingInterval = null; +let isScanningActive = false; + +/** + * Start a new batch scan with real-time progress + */ +function startBatchScan(ignoreFolders, ignoreFileTypes) { + if (isScanningActive) { + console.warn('Scan already in progress'); + return; + } + + // Get UI elements const progressBar = document.getElementById('progress-bar-fill'); const progressStatus = document.getElementById('progress-status'); const totalFilesElem = document.getElementById('total-files'); @@ -36,6 +52,19 @@ function startScan(ignoreFolders, ignoreFileTypes) { const resultsBody = document.getElementById('scan-results-body'); const resultsSection = document.getElementById('scan-results-section'); const cleanupFiles = document.getElementById('cleanup-files'); + const startButton = document.getElementById('start-scan'); + + // Reset UI + resetScanUI(); + isScanningActive = true; + startButton.disabled = true; + startButton.textContent = 'Scanning...'; + + // Show cancel button + const cancelButton = document.getElementById('cancel-scan'); + if (cancelButton) { + cancelButton.classList.remove('hidden'); + } // Show loading spinners showLoadingSpinner(totalFilesElem); @@ -48,122 +77,383 @@ function startScan(ignoreFolders, ignoreFileTypes) { progressBar.style.width = '0%'; progressStatus.textContent = 'Initializing scan...'; - // Reset previous results - resultsBody.innerHTML = ''; - progressSection.classList.remove('hidden'); - resultsSection.classList.add('hidden'); - progressBar.style.width = '0%'; - progressStatus.textContent = 'Initializing scan...'; - - // Fake progress variables - let progress = 0; - const fakeInterval = 500; // Progress interval in milliseconds (0.5 seconds) - - // Fake progress updater - const progressInterval = setInterval(() => { - if (progress < 90) { // Cap the progress at 90% before AJAX completes - progress += 5; // Increment progress - progressBar.style.width = progress + '%'; - progressStatus.textContent = `Scanning... ${progress}%`; - }else{ - progressStatus.textContent = `Preparing results...`; - } - }, fakeInterval); - - // Start AJAX call to perform the scan + // Initialize batch scan jQuery.ajax({ url: malwareScannerAjax.ajax_url, type: 'POST', data: { - action: 'start_malware_scan', - directory: './', + action: 'initialize_batch_scan', ignore_folders: ignoreFolders, ignore_file_types: ignoreFileTypes, - cleanup_files: cleanupFiles.checked, + cleanup_files: cleanupFiles.checked ? 'true' : 'false', security: malwareScannerAjax.security }, success: function (response) { - clearInterval(progressInterval); - // console.log('Scan response:', response); if (response.success) { - const report = response.data.report; - const totalFiles = response.data.file.scannedFiles || 0; - const scannedFiles = report.detected || 0; - const progress = (scannedFiles / totalFiles) * 100; + currentScanSession = response.data; + + // Update UI with initial info + hideLoadingSpinner(totalFilesElem, currentScanSession.total_files); + hideLoadingSpinner(scannedFilesElem, 0); + + // Enhanced status message with folder info + let statusMessage = `Scan initialized: ${currentScanSession.total_files} files to scan`; + if (ignoreFolders.length > 0) { + statusMessage += ` (${ignoreFolders.length} folders ignored)`; + } + progressStatus.textContent = statusMessage; - // Hide loading spinners and update counts - hideLoadingSpinner(totalFilesElem, totalFiles); - hideLoadingSpinner(scannedFilesElem, scannedFiles); + // Start processing batches + processBatchSequentially(0); + + // Start progress polling + startProgressPolling(); - // Update progress bar and status - progressBar.style.width = '100%'; - progressStatus.textContent = `Scan completed: ${scannedFiles} / ${totalFiles}`; + } else { + handleScanError(response.data.message || 'Failed to initialize scan'); + } + }, + error: function (jqXHR, textStatus, errorThrown) { + handleScanError('Network error during scan initialization: ' + errorThrown); + } + }); +} + +/** + * Process batches sequentially + */ +function processBatchSequentially(batchNumber) { + if (!currentScanSession || !isScanningActive) { + return; + } + + if (batchNumber >= currentScanSession.total_batches) { + // All batches completed + completeScan(); + return; + } + + const progressStatus = document.getElementById('progress-status'); + progressStatus.textContent = `Processing files...`; + + jQuery.ajax({ + url: malwareScannerAjax.ajax_url, + type: 'POST', + data: { + action: 'process_scan_batch', + session_id: currentScanSession.session_id, + batch_number: batchNumber, + security: malwareScannerAjax.security + }, + success: function (response) { + if (response.success) { + const data = response.data; + + // Update progress + updateProgressDisplay(data); - // Display scan results if any - displayResults(report); + if (data.is_complete) { + completeScan(); + } else { + // Process next batch after a short delay + setTimeout(() => { + processBatchSequentially(batchNumber + 1); + }, 100); + } } else { - progressStatus.textContent = 'Scan failed'; - console.error('Scan error:', response.data.message); + handleScanError(response.data.message || 'Batch processing failed'); } }, error: function (jqXHR, textStatus, errorThrown) { - clearInterval(progressInterval); - progressBar.style.width = '0%'; - progressStatus.textContent = 'Error during scan'; - console.error('Scan error:', errorThrown); + // Try to continue with next batch on error, but log it + console.error('Batch processing error:', errorThrown); + setTimeout(() => { + processBatchSequentially(batchNumber + 1); + }, 1000); + } + }); +} + +/** + * Start polling for progress updates + */ +function startProgressPolling() { + if (progressPollingInterval) { + clearInterval(progressPollingInterval); + } + + progressPollingInterval = setInterval(() => { + if (!currentScanSession || !isScanningActive) { + clearInterval(progressPollingInterval); + return; + } + + jQuery.ajax({ + url: malwareScannerAjax.ajax_url, + type: 'POST', + data: { + action: 'get_scan_progress', + session_id: currentScanSession.session_id, + security: malwareScannerAjax.security + }, + success: function (response) { + if (response.success) { + updateProgressDisplay(response.data); + + if (response.data.is_complete) { + completeScan(); + } + } + }, + error: function () { + // Silently handle polling errors + } + }); + }, 2000); // Poll every 2 seconds +} + +/** + * Update progress display with real data + */ +function updateProgressDisplay(data) { + const progressBar = document.getElementById('progress-bar-fill'); + const progressStatus = document.getElementById('progress-status'); + const scannedFilesElem = document.getElementById('scanned-files'); + + // Update progress bar + progressBar.style.width = data.progress_percentage + '%'; + + // Update scanned files count + hideLoadingSpinner(scannedFilesElem, data.processed_files); + + // Update status text with just percentage + const statusText = `Scanning: ${data.processed_files} / ${data.total_files} files (${data.progress_percentage}%)`; + progressStatus.textContent = statusText; +} + +/** + * Complete the scan and show results + */ +function completeScan() { + const progressBar = document.getElementById('progress-bar-fill'); + const progressStatus = document.getElementById('progress-status'); + const startButton = document.getElementById('start-scan'); + + // Stop polling + if (progressPollingInterval) { + clearInterval(progressPollingInterval); + progressPollingInterval = null; + } + + // Get final results + jQuery.ajax({ + url: malwareScannerAjax.ajax_url, + type: 'POST', + data: { + action: 'get_scan_progress', + session_id: currentScanSession.session_id, + security: malwareScannerAjax.security + }, + success: function (response) { + if (response.success) { + const finalResults = response.data.scan_results; + + // Update final progress + progressBar.style.width = '100%'; + progressStatus.textContent = `Scan completed: ${response.data.processed_files} files scanned, ${finalResults.detected} threats detected`; + + // Display results + displayBatchScanResults(finalResults); + + // Send email if enabled (handled by backend during batch processing) + console.log('Scan completed successfully'); + } + }, + error: function () { + progressStatus.textContent = 'Scan completed with errors'; }, complete: function () { - // Remove loading spinners after the request completes - // totalFilesElem.classList.remove('wsp-spinner'); - // scannedFilesElem.classList.remove('wsp-spinner'); + // Reset scan state + isScanningActive = false; + currentScanSession = null; + startButton.disabled = false; + startButton.textContent = 'đ Start Scan'; + + // Hide cancel button + const cancelButton = document.getElementById('cancel-scan'); + if (cancelButton) { + cancelButton.classList.add('hidden'); + } } }); } -function displayResults(report) { +/** + * Handle scan errors with enhanced messaging + */ +function handleScanError(errorMessage) { + const progressStatus = document.getElementById('progress-status'); + const progressBar = document.getElementById('progress-bar-fill'); + const startButton = document.getElementById('start-scan'); + + // Check for common error types and provide better user messages + let userFriendlyMessage = errorMessage; + + if (errorMessage.includes('memory') || errorMessage.includes('Memory') || errorMessage.includes('exhausted')) { + userFriendlyMessage = 'đ§ Memory limit reached. The scanner will automatically optimize memory usage on retry. Please try again.'; + } else if (errorMessage.includes('timeout') || errorMessage.includes('execution time')) { + userFriendlyMessage = 'â° Scan timeout prevented. The scanner will process files more efficiently on retry.'; + } else if (errorMessage.includes('permission') || errorMessage.includes('Permission')) { + userFriendlyMessage = 'đ File permission error. Please check file permissions and try again.'; + } else if (errorMessage.includes('network') || errorMessage.includes('Network')) { + userFriendlyMessage = 'đ Network connection issue. Please check your connection and try again.'; + } + + progressStatus.innerHTML = ` +
' . esc_html($value) . '
-| WSP Malware Scanner | -' . esc_html($scan_date) . ' | -
' . esc_html($value) . '
+| WSP Malware Scanner | +' . esc_html($scan_date) . ' | +
| ' . $card('Total Files Scanned', intval($report['totalFilesScanned'])) . ' |
- ' . $card('Malware Detected', intval($report['malwareDetected'])) . ' |
- ' . $card('Ignored Files', count($report['ignoredFiles'])) . ' |
- ' . $card('Removed Files', count($report['removedFiles'])) . ' |
-
| ' . $card('Edited Files', count($report['editedFiles'])) . ' |
- ' . $card('Quarantined Files', count($report['quarantinedFiles'])) . ' |
- ' . $card('Whitelisted Files', count($report['whitelistedFiles'])) . ' |
- ' . $card('Infected Files', !empty($report['infectedFilesFound']) ? count($report['infectedFilesFound']) : 0) . ' |
-
| ' . $card('Total Files Scanned', $report_data['totalFilesScanned']) . ' |
+ ' . $card('Malware Detected', $report_data['malwareDetected']) . ' |
+ ' . $card('Ignored Files', $report_data['ignoredFiles']) . ' |
+ ' . $card('Removed Files', $report_data['removedFiles']) . ' |
+
| ' . $card('Edited Files', $report_data['editedFiles']) . ' |
+ ' . $card('Quarantined Files', $report_data['quarantinedFiles']) . ' |
+ ' . $card('Whitelisted Files', $report_data['whitelistedFiles']) . ' |
+ ' . $card('Infected Files', $report_data['infectedFilesFound']) . ' |
+