diff --git a/inc/spbc-backups.php b/inc/spbc-backups.php index 459b6693..7a867bf2 100644 --- a/inc/spbc-backups.php +++ b/inc/spbc-backups.php @@ -52,8 +52,8 @@ function spbc_backup__delete($direct_call = false, $backup_id = null) if (empty($output['error'])) { if (rmdir(SPBC_PLUGIN_DIR . 'backups/backup_' . $backup_id)) { - if ($wpdb->delete(SPBC_TBL_BACKUPED_FILES, array('backup_id' => $backup_id), array('%d'))) { - if ($wpdb->delete(SPBC_TBL_BACKUPS, array('backup_id' => $backup_id), array('%d'))) { + if (false !== $wpdb->delete(SPBC_TBL_BACKUPED_FILES, array('backup_id' => $backup_id), array('%d'))) { + if (false !== $wpdb->delete(SPBC_TBL_BACKUPS, array('backup_id' => $backup_id), array('%d'))) { $output = array( 'html' => 'Backup deleted', 'success' => true, @@ -91,7 +91,7 @@ function spbc_backup__files_with_signatures_handler() $output = array('success' => true); - $files_to_backup = $wpdb->get_results('SELECT path, weak_spots FROM ' . SPBC_TBL_SCAN_FILES . ' WHERE weak_spots LIKE "%\"SIGNATURES\":%";', ARRAY_A); + $files_to_backup = $wpdb->get_results('SELECT path, weak_spots, checked_heuristic, checked_signatures, status, severity FROM ' . SPBC_TBL_SCAN_FILES . ' WHERE weak_spots LIKE "%\"SIGNATURES\":%";', ARRAY_A); if (!is_array($files_to_backup) || !count($files_to_backup)) { $output = array('success' => true); @@ -100,6 +100,9 @@ function spbc_backup__files_with_signatures_handler() $sql_data = array(); foreach ($files_to_backup as $file) { + if (spbc_file_has_backup($file['path'])) { + continue; + } $weak_spots = json_decode($file['weak_spots'], true); $signtures_in_file = array(); @@ -113,15 +116,6 @@ function spbc_backup__files_with_signatures_handler() continue; } - $sql_signatures_placeholder = rtrim(str_repeat('%s,', count($signtures_in_file)), ','); - $sql_signatures = 'SELECT * FROM ' . SPBC_TBL_SCAN_SIGNATURES . " WHERE id IN ($sql_signatures_placeholder) AND cci IS NOT NULL;"; - $signatures_with_cci = $wpdb->get_results($wpdb->prepare($sql_signatures, $signtures_in_file), ARRAY_A); - - // Backup only files which will be cured - if (!$signatures_with_cci) { - continue; - } - // Adding new backup batch if ( ! isset($backup_id)) { $wpdb->insert(SPBC_TBL_BACKUPS, array('type' => 'SIGNATURES', 'datetime' => date('Y-m-d H:i:s'))); @@ -143,8 +137,16 @@ function spbc_backup__files_with_signatures_handler() $result = spbc_backup__file($file['path'], $backup_id); + $backup_prev_results_state = json_encode($file); + $backup_prev_results_state = $backup_prev_results_state === false ? 'ERROR' : $backup_prev_results_state; + if (empty($result['error'])) { - $sql_data[] = '(' . $backup_id . ',' . Helper::prepareParamForSQLQuery($file['path']) . ',' . Helper::prepareParamForSQLQuery($result) . ')'; + $sql_data[] = '(' + . $backup_id . ',' + . Helper::prepareParamForSQLQuery($file['path']) . ',' + . Helper::prepareParamForSQLQuery($result) . ',' + . Helper::prepareParamForSQLQuery($backup_prev_results_state) + . ')'; } else { // Mark the backup STOPPED while errors occurred $wpdb->update(SPBC_TBL_BACKUPS, array('status' => 'STOPPED'), array('backup_id' => $backup_id)); @@ -161,7 +163,7 @@ function spbc_backup__files_with_signatures_handler() $backup_id = isset($backup_id) ? $backup_id : $spbc->data['scanner']['last_backup']; // Writing backuped files to DB - $sql_query = 'INSERT INTO ' . SPBC_TBL_BACKUPED_FILES . ' (backup_id, real_path, back_path) VALUES'; + $sql_query = 'INSERT INTO ' . SPBC_TBL_BACKUPED_FILES . ' (backup_id, real_path, back_path, backup_prev_results_state) VALUES'; // suppress because data is already prepared in Helper::prepareParamForSQLQuery method // @psalm-suppress WpdbUnsafeMethodsIssue $result = $wpdb->query($sql_query . implode(',', $sql_data) . ';'); diff --git a/inc/spbc-scanner.php b/inc/spbc-scanner.php index 476f4b55..44b46047 100644 --- a/inc/spbc-scanner.php +++ b/inc/spbc-scanner.php @@ -1,5 +1,6 @@ status === 'OK') { + $cure_log->deleteCureLogRecord($file_data['fast_hash']); + // update file in the table + $wpdb->update( + SPBC_TBL_SCAN_FILES, + array( + 'checked_signatures' => 1, + 'checked_heuristic' => 1, + 'status' => $file_data['status'] === 'MODIFIED' ? 'MODIFIED' : $merged_result['status'], + 'severity' => $merged_result['severity'], + 'weak_spots' => json_encode($merged_result['weak_spots']), + 'full_hash' => md5_file(spbc_get_root_path() . $file_data['path']), + ), + array('fast_hash' => $file_data['fast_hash']), + array('%s', '%s', '%s', '%s', '%s', '%s'), + array('%s') + ); + return esc_html__('No threats detected for current file statement.', 'security-malware-firewall'); + } $cure_stage = new CureStage(DB::getInstance()); $cure_log_record = $cure_stage->processCure($file_data); @@ -2111,19 +2138,37 @@ function spbc_restore_file_from_backup_ajax_action_handler($id) // Getting backup path $sql_prepared = $wpdb->prepare( - 'SELECT back_path ' + 'SELECT back_path, backup_prev_results_state ' . ' FROM ' . SPBC_TBL_BACKUPED_FILES . ' WHERE real_path="%s"' . ' ORDER BY backup_id DESC LIMIT 1;', $file_path ); - $backup_path = $wpdb->get_row($sql_prepared, ARRAY_A); + // prepare backup data + $backup_data = $wpdb->get_results($sql_prepared, ARRAY_A); + $backup_path = isset($backup_data[0]['back_path']) ? $backup_data[0]['back_path'] : null; + // prepare previous state of the file + $backup_prev_results_state = isset($backup_data[0]['backup_prev_results_state']) ? $backup_data[0]['backup_prev_results_state'] : null; + $backup_prev_results_state = empty($backup_prev_results_state) || json_decode($backup_prev_results_state, true) === false + ? null + : json_decode($backup_prev_results_state, true); + if ( + !isset( + $backup_prev_results_state['weak_spots'], + $backup_prev_results_state['checked_heuristic'], + $backup_prev_results_state['checked_signatures'], + $backup_prev_results_state['status'], + $backup_prev_results_state['severity'], + $backup_prev_results_state['path'] + ) + ) { + $backup_prev_results_state = null; + } if (is_null($backup_path)) { return array('error' => esc_html__('Error: Backup not found.', 'security-malware-firewall')); } - $backup_path = $backup_path['back_path']; $full_backup_path = ABSPATH . ltrim($backup_path, '/'); // Trying to replace backup and original file @@ -2161,20 +2206,41 @@ function spbc_restore_file_from_backup_ajax_action_handler($id) $delete = $wpdb->query($sql_prepared); if (is_null($delete)) { - return array('error' => esc_html__('Error: Something is wrong.', 'security-malware-firewall')); + return array('error' => esc_html__('Error: Something is wrong during deleting backup.', 'security-malware-firewall')); } - // Remove from cure log + // update cure log $sql_prepared = $wpdb->prepare( - 'DELETE ' - . ' FROM ' . SPBC_TBL_CURE_LOG - . ' WHERE real_path="%s";', - $file_path + 'UPDATE ' . SPBC_TBL_CURE_LOG + . ' SET is_restored = 1, ' + . ' cure_status = 0 ' + . ' WHERE real_path = %s;', + array($file_path) ); - $delete = $wpdb->query($sql_prepared); + $update_cure_log = $wpdb->query($sql_prepared); - if (is_null($delete)) { - return array('error' => esc_html__('Error: Something is wrong.', 'security-malware-firewall')); + if (is_null($update_cure_log)) { + return array('error' => esc_html__('Error: Something is wrong during updating cure log.', 'security-malware-firewall')); + } + + if ($backup_prev_results_state !== null) { + // update scan results table + $sql_prepared = $wpdb->prepare( + 'UPDATE ' . SPBC_TBL_SCAN_FILES + . ' SET weak_spots = %s, checked_heuristic = %s, checked_signatures = %s, status = %s, severity = %s' + . ' WHERE path = %s;', + array( + $backup_prev_results_state['weak_spots'], + $backup_prev_results_state['checked_heuristic'], + $backup_prev_results_state['checked_signatures'], + $backup_prev_results_state['status'], + $backup_prev_results_state['severity'], + $file_path) + ); + $updated = $wpdb->query($sql_prepared); + if (is_null($updated)) { + return array('error' => esc_html__('Error: Something is wrong during saving previous state of file.', 'security-malware-firewall')); + } } } catch (\Exception $e) { return array('error' => esc_html__('Error: Something is wrong.', 'security-malware-firewall')); diff --git a/inc/spbc-settings.php b/inc/spbc-settings.php index c60dbf6a..f169809a 100644 --- a/inc/spbc-settings.php +++ b/inc/spbc-settings.php @@ -4409,7 +4409,7 @@ function spbc_list_table__get_args_by_type($table_type) 'cb' => array('heading' => '', 'class' => 'check-column', 'width_percent' => 2), 'real_path' => array('heading' => 'Path','primary' => true, 'width_percent' => 27), 'last_cure_date' => array('heading' => 'Cure date', 'width_percent' => 10), - 'cured' => array('heading' => 'Status', 'width_percent' => 13), + 'cure_status' => array('heading' => 'Status', 'width_percent' => 13), 'weak_spots_cured' => array('heading' => 'Threats cured', 'width_percent' => 24), 'weak_spots_uncured' => array('heading' => 'Threats uncured', 'width_percent' => 24), ), @@ -5785,13 +5785,22 @@ function spbc_scanner__cure_log_data_prepare(&$table) if ($table->items_count) { foreach ($table->rows as $_key => $row) { // Add Cure action if file was not cure - if ($row->cured === 'CURED') { + if ($row->cure_status === 'CURED') { unset($row->actions['cure']); } - $cure_status_string = $row->cured === 'CURED' - ? '' . $row->cured . '' - : '' . $row->cured . ''; + // rewrite on restored + if ($row->cure_status === 'RESTORED' || $row->cure_status === 'FAILED') { + unset($row->actions['restore']); + } + + $cure_status_string = $row->cure_status === 'CURED' + ? '' . $row->cure_status . '' + : '' . $row->cure_status . ''; + // rewrite on restored + if ($row->is_restored === '1') { + $cure_status_string = '' . $row->cure_status . ''; + } $table->items[] = array( 'cb' => $row->fast_hash, @@ -5799,7 +5808,7 @@ function spbc_scanner__cure_log_data_prepare(&$table) 'actions' => $row->actions, 'real_path' => $row->real_path, 'last_cure_date' => $row->last_cure_date, - 'cured' => $cure_status_string, + 'cure_status' => $cure_status_string, 'weak_spots_cured' => $row->weak_spots_cured, 'weak_spots_uncured' => $row->weak_spots_uncured, ); diff --git a/js/spbc-table.min.js b/js/spbc-table.min.js index ea6b8cde..e6ea06f4 100644 --- a/js/spbc-table.min.js +++ b/js/spbc-table.min.js @@ -1,2 +1,2 @@ -let spbcBulkAction=null,noConfirmActions=spbcGetNoConfirmActions(spbcPublic);function spbcReloadAccordion(c=null,s=""){spbcSendAJAXRequest({action:"spbc_scanner_tab__reload_accordion"},{notJson:!0,callback:function(e,t,n,a){jQuery(a).accordion("destroy"),jQuery(a).html(e),jQuery(a).accordion({header:"h3",heightStyle:"content",collapsible:!0,active:!1}),spbcTblBulkActionsListen(),spbcTblRowActionsListen(),spbcTblPaginationListen(),spbcTblSortListen(),spbcStartShowHide(),null!==c&&null!==s&&interactScannerTab(c,s)}},jQuery("#spbc_scan_accordion"))}function interactScannerTab(e,t){var n,a;"string"==typeof e&&""!==e&&"string"==typeof t&&""!==t&&(n=jQuery('#spbc_scan_accordion div[refresh_control_tab="'+e+'"]')[0],e="h3[aria-controls=spbc_scan_accordion_tab_"+e+"]",e=jQuery(e)[0],void 0!==n)&&void 0!==e&&(jQuery('
').appendTo(e),a=jQuery(".spbc_accordion_header_caption__update_status"),jQuery('
'+t+"
").appendTo(a),e.click(),n.setAttribute("class","ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-active"),jQuery(".row-actions").hide())}function spbcTblBulkActionsListen(){jQuery(".tbl-bulk_actions-all--apply").off("click").on("click",function(){if(spbcScanner.active)alert(spbcTableLocalize.scannerIsActive);else{var e=jQuery(this);let n=e.siblings("select").children()[e.siblings("select").first()[0].selectedIndex].value;if(-1!==["approve","disapprove","send","check_analysis_status","approve_page","disapprove_page","cure","delete_from_analysis_log","restore"].indexOf(n)){if(noConfirmActions.any.includes(n)||confirm(spbcTable.warning_bulk)){var t={action:"spbc_tbl-action--bulk",add_action:n,status:e.parents(".tbl-root").attr("type")};if("cure"===n)spbcScannerCureBulk(e,!0);else{if("restore"===n){var a=e.closest("#spbc_tbl__scanner_cure_log").find(".cb-select");let n=[];a.each(function(e,t){t=jQuery(t).val();n.push(t)}),t.selectedIds=n}if("delete_from_analysis_log"===n){a=e.closest("#spbc_tbl__scanner_analysis_log").find(".cb-select");let n=[];a.each(function(e,t){t=jQuery(t).val();n.push(t)}),t.file_ids=n}a={button:this,spinner:e.children(".tbl-preloader--small"),callback:function(e){var t;spbcReloadAccordion(),"check_analysis_status"===n&&void 0!==e&&(t="

Analysis status updated. Total: "+e.counters.total+", updated: "+e.counters.updated+", skipped: "+e.counters.skipped+", failed: "+e.counters.failed+", queued: "+e.counters.queued+"

",spbcModal.open().put(t)),"send"===n&&void 0!==e&&(t="

Files have been sent: "+e.files_sent_counter+"

",spbcModal.open().put(t))},errorOutput:function(e,t){t&&(e=e+"
Additional information:
"+t),spbcModal.open().putError(e)},timeout:6e4};spbcSendAJAXRequest(t,a)}}}else alert("This action is not supported for all files yet =(")}}),jQuery(".tbl-bulk_actions--apply").off("click").on("click",function(e){var n=spbcBulkAction||jQuery(this);let a=(spbcBulkAction=n).siblings("select").children()[n.siblings("select").first()[0].selectedIndex].value;if(spbcBulkAction||noConfirmActions.any.includes(a)||confirm(spbcTable.warning_bulk))if("cure"===a)spbcScannerCureBulk(jQuery(this),!1);else if("restore"===a)spbcScannerRestoreSelected(jQuery(this));else if("delete_from_analysis_log"===a)spbcScannerAnalysisLogDeleteFromLog(jQuery(this));else if("allow"===a||"ban"===a){let t="ban"===a?"deny":a;var c=n.parents(".tbl-root").find(".cb-select:checked"),c=(c&&(n.children(".tbl-preloader--small").show(),c.each(function(){var e=jQuery(this).parents("tr").find(".tbl-row_action--"+a).data("ip");spbcSecLogsFilterIp(e,t,!0),jQuery(this).prop("checked",!1)})),n.children(".tbl-preloader--small").hide(),"allow"===a?"Allowed":"Banned"),c="

Success. Selected IPs have been "+c+". Changes will be applied within 10 minutes.

";spbcModal.open().put(c),void setTimeout(spbcReloadAccordion,1900)}else"-1"!==a&&n.parents(".tbl-root").find(".cb-select").is(":checked")?(n.children(".tbl-preloader--small").show(),n.parents(".tbl-root").find(".cb-select:checked").first().parents("tr").find(".tbl-row_action--"+a)[0]?(n.parents(".tbl-root").find(".cb-select:checked").first().parents("tr").find(".tbl-row_action--"+a).click(),n.parents(".tbl-root").find(".cb-select:checked").first().prop("checked",!1)):(n.parents(".tbl-root").find(".cb-select:checked").first().prop("checked",!1),n.click())):(n.children(".tbl-preloader--small").hide(),spbcBulkAction=null,"check_analysis_status"!==a&&"disapprove"!==a||spbcModal.open().put("

All available files are updated.

"),"send"===a&&spbcModal.open().put("

All available files are sent.

"),setTimeout(spbcReloadAccordion,1900)),noConfirmActions.restricted.includes(a)&&(spbcBulkAction=null)})}function spbcScannerCureBulk(i,e=!1){let n=[];var e=e?".cb-select":".cb-select:checked",e=i.closest("#spbc_tbl__scanner_cure_log").find(e);0===e.length?alert("Please, select elements."):(e.each(function(e,t){t=jQuery(t).val();n.push(t)}),e={action:"spbc_cure_selected",security:spbcSettings.ajax_nonce,selectedIds:n},jQuery.ajax({type:"POST",url:spbcSettings.ajaxurl,data:e,beforeSend:function(){i.closest("#spbc_tbl__scanner_cure_log").find(".tbl-button---white_blue .tbl-preloader--in_button").show()},success:function(e){i.closest("#spbc_tbl__scanner_cure_log").find(".tbl-button---white_blue .tbl-preloader--in_button").hide();var t=e.data,n=document.createElement("div"),a=document.createElement("p"),c=document.createElement("p"),s=document.createElement("div"),o=t.hasOwnProperty("failed_to_cure")?t.failed_to_cure:[];0")),a.innerHTML=t.message,c.innerHTML+="Cured: "+(t.hasOwnProperty("cured_on_request")?t.cured_on_request:0),c.innerHTML+=", already cured: "+(t.hasOwnProperty("skipped")?t.skipped:0),c.innerHTML+=", failed to cure: "+o.length,n.append(a),n.append(c),n.append(s),e.success?spbcModal.open().put(n.outerHTML):spbcModal.open().putError(n.outerHTML),document.addEventListener("spbcModalClosed",function(e){spbcReloadAccordion()})}}))}function spbcScannerRestoreSelected(t){var e=t.closest("#spbc_tbl__scanner_cure_log").find(".cb-select:checked");let n=[];0===e.length&&alert("Please, select elements."),e.each(function(e,t){t=jQuery(t).val();n.push(t)});e={action:"spbc_restore_selected",security:spbcSettings.ajax_nonce,selectedIds:n};jQuery.ajax({type:"POST",url:spbcSettings.ajaxurl,data:e,beforeSend:function(){t.closest("#spbc_tbl__scanner_cure_log").find(".tbl-button---white_blue .tbl-preloader--in_button").show()},success:function(e){t.closest("#spbc_tbl__scanner_cure_log").find(".tbl-button---white_blue .tbl-preloader--in_button").hide(),e.success?(spbcModal.open().put(e.data),document.addEventListener("spbcModalClosed",function(e){document.location.reload()})):spbcModal.open().putError(e.data)}})}function spbcTblRowActionsListen(){jQuery(".tbl-row_action--ajax").off("click").on("click",function(){if(spbcScanner.active)alert(spbcTableLocalize.scannerIsActive);else{let t=jQuery(this),n={action:"spbc_tbl-action--row",add_action:t.attr("row-action"),id:t.parents(".row-actions").attr("uid"),cols:t.parents(".row-actions").attr("cols_amount"),page_url:t.parent().attr("uid"),page_id:t.parent().attr("page_id")},a={callback:spbcTblRowActionsCallback,errorOutput:function(e,t,n){spbcModal.open().putError(e,n)},spinner:t.parent().siblings(".tbl-preloader--tiny")};"delete"===t.attr("row-action")&&(a.timeout=6e4);var e,c,s=t.parent().parent()[0].firstChild.innerHTML,o=spbcTable["warning_h_"+t.attr("row-action")]||spbcTable.warning_default,i=spbcTable["warning_t_"+t.attr("row-action")]||"";spbcBulkAction||noConfirmActions.any.includes(n.add_action)?spbcSendAJAXRequest(n,a,t.parents("tr")):(e="quarantine"===t.attr("row-action")?"Quarantine":"Yes",c="quarantine"===t.attr("row-action")?"Cancel":"No",spbcModal.open().confirm(o,i,s,e=>{e&&spbcSendAJAXRequest(n,a,t.parents("tr"))},e,c))}})}function spbcGetNoConfirmActions(e){var t={any:[],restricted:[],defaults:[]};let n=t;try{"function"==typeof(n=void 0!==e&&e.hasOwnProperty("no_confirm_row_actions")?JSON.parse(e.no_confirm_row_actions):n).any.includes&&"function"==typeof n.restricted.includes&&"function"==typeof n.defaults.includes||(n=t)}catch(e){n=t}return n}function spbcTblRowActionsCallback(t,n,e,a){if(t.color&&a.css({background:t.background,color:t.color}),t.html&&(a.parent().parent().parent().prepend(t.html),setTimeout(function(){a.fadeOut(300)},1500),spbcBulkAction||setTimeout(spbcReloadAccordion,1900)),"copy_file_info"===n.add_action&&(!0===t.success&&t.data.file_info?window.prompt("Copy the file info below and send it to support@cleantalk.org: ",t.data.file_info):(c=void 0!==t.data.error?t.data.error:"Unknown copy_file_info error",spbcModal.open().putError(c))),t.temp_html){let e=a.html();if(a.html(t.temp_html),void 0!==t.updated_template){var c=t.updated_template_type,t=t.updated_template,s=jQuery(t).find(".wp-list-table tbody tr").length,o=jQuery('[aria-controls="spbc_scan_accordion_tab_'+c+'"]');if(0n&&jQuery(t).css("outline","1px solid green")}),o.click()}else"analysis_log"===c&&(i='",c='