diff --git a/inc/spbc-backups.php b/inc/spbc-backups.php index 78a4e84ca..5fb6474ba 100644 --- a/inc/spbc-backups.php +++ b/inc/spbc-backups.php @@ -174,7 +174,7 @@ function spbc_backup__files_with_signatures_handler() $output = array('success' => true); - $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); + $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\":%" AND source != \'BINARY\';', ARRAY_A); if (!is_array($files_to_backup) || !count($files_to_backup)) { $output = array('success' => true); diff --git a/inc/spbc-settings.php b/inc/spbc-settings.php index 3a8312e6b..31a6f7700 100644 --- a/inc/spbc-settings.php +++ b/inc/spbc-settings.php @@ -552,6 +552,12 @@ function spbc_settings__register() 'description' => __('Will search for known malicious signatures in files. Unknown files will be shown in the results only if both options heuristic analysis and signature analysis are enabled.', 'security-malware-firewall'), 'long_description' => true, ), + 'scanner__binary_analysis' => array( + 'type' => 'field', + 'title' => __('Binary analysis', 'security-malware-firewall'), + 'description' => __('Search for suspicious binary patterns in files.', 'security-malware-firewall'), + 'long_description' => true, + ), 'scanner__os_cron_analysis' => array( 'type' => 'field', 'title' => Scanner\OSCron\View\OSCronLocale::getInstance()->settings__option_title, @@ -2324,6 +2330,14 @@ function spbc_field_scanner__prepare_data__files(&$table) unset($row->actions['delete']); } + // Binary files: leave only view/delete/quarantine actions + if ( isset($row->source) && $row->source === 'BINARY' ) { + unset($row->actions['send']); + unset($row->actions['view_bad']); + unset($row->actions['replace']); + unset($row->actions['compare']); + } + $table->items[] = array( 'cb' => $row->fast_hash, 'uid' => $row->fast_hash, @@ -2978,6 +2992,12 @@ function spbc_field_scanner() . ' -> '; } + if ($spbc->settings['scanner__binary_analysis']) { + echo '' + . __('Binary analysis', 'security-malware-firewall') + . ' -> '; + } + if ($spbc->settings['scanner__schedule_send_heuristic_suspicious_files']) { echo '' . __('Schedule suspicious files sending', 'security-malware-firewall') @@ -4881,6 +4901,10 @@ function spbc_settings__get_description() 'title' => __('Signature analysis', 'security-malware-firewall'), 'desc' => __('Code signatures — it\'s a code sequence a malicious program consists of. Signatures are being added to the database after analysis of the infected files. Search for such malicious code sequences is performed in scanning by signatures. If any part of code matches a virus code from the database, such files would be marked as critical.', 'security-malware-firewall') ), + 'scanner__binary_analysis' => array( + 'title' => __('Binary analysis', 'security-malware-firewall'), + 'desc' => __('Search for suspicious binary patterns in files.', 'security-malware-firewall') + ), 'scanner__auto_cure' => array( 'title' => __('Cure malware', 'security-malware-firewall'), 'desc' => __('It cures infected files automatically if the scanner knows cure methods for these specific cases. If the option is disabled then when the scanning process ends you will be presented with several actions you can do to the found files: Cure. Malicious code will be removed from the file. Replace. The file will be replaced with the original file. Delete. The file will be put in quarantine. Do nothing. Before any action is chosen, backups of the files will be created and if the cure is unsuccessful it\'s possible to restore each file.', 'security-malware-firewall') diff --git a/js/spbc-scanner-plugin.min.js b/js/spbc-scanner-plugin.min.js index e635fef3e..3f805b82b 100644 --- a/js/spbc-scanner-plugin.min.js +++ b/js/spbc-scanner-plugin.min.js @@ -1,2 +1,2 @@ -class SpbcMalwareScanner{first_start=!0;active=!1;root="";settings=[];states=["get_cms_hashes","get_modules_hashes","clean_results","file_system_analysis","get_approved_hashes","get_denied_hashes","signature_analysis","heuristic_analysis","schedule_send_heuristic_suspicious_files","auto_cure_backup","auto_cure","os_cron_analysis","db_trigger_analysis","outbound_links","frontend_analysis","important_files_listing","send_results"];state=null;offset=0;amount=0;amount_coefficient=1;total_scanned=0;scan_percent=0;percent_completed=0;paused=!1;button=null;spinner=null;progress_overall=null;progressbar=null;progressbar_text=null;timeout=6e4;state_timer=0;constructor(t){for(var e in jQuery("#spbcscan-results-log-module").length&&jQuery(".spbc-scan-log-title").removeClass("spbc---hidden"),void 0!==t.settings.auto_cure&&(t.settings.scanner__auto_cure_backup="1"),t)void 0!==this[e]&&(this[e]=t[e])}actionControl(){null===this.state?this.start():this.paused?(this.resume(),this.controller()):this.pause()}start(){this.active=!0,this.state_timer=Math.round((new Date).getTime()/1e3),this.state=this.getNextState(null),this.setPercents(0),this.scan_percent=0,this.offset=0,this.progress_overall.children("span").removeClass("spbc_bold").filter(".spbc_overall_scan_status_"+this.state).addClass("spbc_bold"),this.progressbar.show(500),this.progress_overall.show(500),this.button.html(spbcScaner.button_scan_pause),this.spinner.css({display:"inline"}),setTimeout(()=>{this.controller()},1e3)}pause(t,e,s){console.log("PAUSE"),this.button.html(spbcScaner.button_scan_resume),this.spinner.css({display:"none"}),this.paused=!0,this.active=!1}resume(t){console.log("RESUME"),this.button.html(spbcScaner.button_scan_pause),this.spinner.css({display:"inline"}),this.paused=!1,this.active=!0}end(t){this.progressbar.hide(500),this.progress_overall.hide(500),this.button.html(spbcScaner.button_scan_perform),this.spinner.css({display:"none"}),this.state=null,this.plug=!1,this.total_scanned=0,this.active=!1,t?document.location=document.location:spbcSendAJAXRequest({action:"spbc_scanner_tab__reload_accordion"},{notJson:!0,callback:function(t,e,s,a){jQuery(a).accordion("destroy").html(t).accordion({header:"h3",heightStyle:"content",collapsible:!0,active:!1}),spbcTblBulkActionsListen(),spbcTblRowActionsListen(),spbcTblPaginationListen(),spbcTblSortListen(),spbcStartShowHide(),spbcScannerReloadScanInfo()}},jQuery("#spbc_scan_accordion"))}controller(t){if(console.log(this.state),void 0!==t&&t.end){if(this.state=this.getNextState(this.state),void 0===this.state)return void this.end();this.setPercents(0),this.scan_percent=0,this.offset=0,this.progress_overall.children("span").removeClass("spbc_bold").filter(".spbc_overall_scan_status_"+this.state).addClass("spbc_bold")}if(!0!==this.paused){var e={action:"spbc_scanner_controller_front",method:this.state,offset:this.offset},t={type:"GET",success:this.success,callback:this.successCallback,error:this.error,errorOutput:this.errorOutput,complete:null,context:this,timeout:12e4};switch(this.state){case"get_modules_hashes":this.amount=2;break;case"clear_table":this.amount=1e4;break;case"file_system_analysis":this.amount=700;break;case"auto_cure":this.amount=5;break;case"outbound_links":this.amount=10;break;case"frontend_analysis":this.amount=spbcSettings.frontendAnalysisAmount;break;case"signature_analysis":this.amount=10,e.status="UNKNOWN,MODIFIED,OK,INFECTED,ERROR";break;case"heuristic_analysis":this.amount=4,e.status="UNKNOWN,MODIFIED,OK,INFECTED,ERROR";break;case"schedule_send_heuristic_suspicious_files":this.amount=1}e.amount=Math.round(this.amount*this.amount_coefficient),spbcSendAJAXRequest(e,t,jQuery("#spbc_scan_accordion"))}}setCoefficients(t){let e=this.amount_coefficient;"file_system_analysis"===t&&(e*=1.5),this.amount_coefficient=e}getNextState(t){return t=null===t?this.states[0]:this.states[this.states.indexOf(t)+1],t=void 0!==this.settings["scanner__"+t]&&0==+this.settings["scanner__"+t]?this.getNextState(t):t}setPercents(t){this.percent_completed=Math.floor(100*t)/100,this.progressbar.progressbar("option","value",this.percent_completed),this.progressbar_text.text(spbcScaner["progressbar_"+this.state]+" - "+this.percent_completed+"%")}success(t){t.error?this.error({status:200,responseText:t.error},t.error,t.msg):this.successCallback&&this.successCallback(t,this.data,this.obj)}successCallback(e){if(console.log(e),this.interactAccordion(e),void 0!==e.total&&(this.scan_percent=100/e.total),void 0!==e.processed_items&&("heuristic_analysis"===this.state&&0!==typeof e.total&&this.logRaw('

Heuristic Analysis

'),"signature_analysis"===this.state&&0!==typeof e.total&&this.logRaw('

Signature Analysis

'),this.logFileEntry(e.processed_items)),void 0!==e.stage_data_for_logging&&this.logStageEntry(e.stage_data_for_logging),void 0!==e.cured&&0{this.controller(e)},300)}interactAccordion(t){t.hasOwnProperty("interactivity_data")&&t.interactivity_data.hasOwnProperty("update_text")&&t.interactivity_data.update_text&&t.interactivity_data.hasOwnProperty("refresh_data")&&t.interactivity_data.refresh_data.hasOwnProperty("do_refresh")&&t.interactivity_data.refresh_data.do_refresh&&t.interactivity_data.refresh_data.hasOwnProperty("control_tab")&&t.interactivity_data.refresh_data.control_tab&&spbcReloadAccordion(t.interactivity_data.refresh_data.control_tab,t.interactivity_data.update_text)}error(t,e,s){var a=this.errorOutput;if(console.log("%c APBCT_AJAX_ERROR","color: red;"),console.log(e),console.log(s),console.log(t),"error"==e&&(""==s||"Not found"==s)&&(this.tryCount||(this.tryCount=0,this.retryLimit=30),this.tryCount++,console.log("Try #"+this.tryCount),this.setCoefficients(this.state),this.tryCount<=this.retryLimit))this.pause(),this.resume(),this.controller();else{if(200===t.status)if("parsererror"===e)a("Unexpected response from server. See console for details.",this.state),console.log("%c "+t.responseText,"color: pink;");else{let t=e;void 0!==s&&(t+=" Additional info: "+s),a(t,this.state)}else 500===t.status?a("Internal server error.",this.state):a("Unexpected response code: "+t.status+". Error: "+e,this.state);this.progressbar&&this.progressbar.fadeOut("slow"),this.end()}}errorOutput(t,e){spbcModal.open().putError(t+"
Stage: "+e)}logRaw(t){jQuery(".spbc-scan-log-title").removeClass("spbc---hidden"),jQuery(".spbc_log-wrapper").removeClass("spbc---hidden"),jQuery(".spbc_log-wrapper .panel-body").prepend(t)}logFileEntry(t){for(var e in t)e&&this.logRaw('

'+this.getSiteUTCShiftedTimeString()+" - "+t[e].path+" - "+t[e].module+": "+t[e].status+"

")}logStageEntry(t){void 0!==jQuery(".panel-body .spbc_log-line span").first()&&void 0!==jQuery(".panel-body .spbc_log-line span").first()[0]&&jQuery(".panel-body .spbc_log-line span").first()[0].textContent===t.description||this.logRaw('

test '+this.getSiteUTCShiftedTimeString()+" - "+t.title+" "+t.description+"

")}showLinkForShuffleSalts(t){jQuery("#spbc_notice_about_shuffle_link").remove(),jQuery(jQuery(".spbc_tab--active .spbc_wrapper_field p")[1]).after('")}getSiteUTCShiftedTimeString(){let t=!1;var e=-1*(new Date).getTimezoneOffset()*1e3*60,e=(t="undefined"!=typeof spbcScaner&&void 0!==spbcScaner.timezone_shift&&!1!==spbcScaner.timezone_shift?Date.now()-e+1e3*spbcScaner.timezone_shift:t)?new Date(t):new Date,s=new Intl.DateTimeFormat("en-US",{month:"short"}).format,a=String(e.getMinutes()).padStart(2,"0"),i=String(e.getSeconds()).padStart(2,"0");return s(e)+" "+e.getDate()+" "+e.getFullYear()+" "+e.getHours()+":"+a+":"+i}} +class SpbcMalwareScanner{first_start=!0;active=!1;root="";settings=[];states=["get_cms_hashes","get_modules_hashes","clean_results","file_system_analysis","get_approved_hashes","get_denied_hashes","signature_analysis","heuristic_analysis","schedule_send_heuristic_suspicious_files","auto_cure_backup","auto_cure","os_cron_analysis","db_trigger_analysis","binary_analysis","outbound_links","frontend_analysis","important_files_listing","send_results"];state=null;offset=0;amount=0;amount_coefficient=1;total_scanned=0;scan_percent=0;percent_completed=0;paused=!1;button=null;spinner=null;progress_overall=null;progressbar=null;progressbar_text=null;timeout=6e4;state_timer=0;constructor(t){for(var e in jQuery("#spbcscan-results-log-module").length&&jQuery(".spbc-scan-log-title").removeClass("spbc---hidden"),void 0!==t.settings.auto_cure&&(t.settings.scanner__auto_cure_backup="1"),t)void 0!==this[e]&&(this[e]=t[e])}actionControl(){null===this.state?this.start():this.paused?(this.resume(),this.controller()):this.pause()}start(){this.active=!0,this.state_timer=Math.round((new Date).getTime()/1e3),this.state=this.getNextState(null),this.setPercents(0),this.scan_percent=0,this.offset=0,this.progress_overall.children("span").removeClass("spbc_bold").filter(".spbc_overall_scan_status_"+this.state).addClass("spbc_bold"),this.progressbar.show(500),this.progress_overall.show(500),this.button.html(spbcScaner.button_scan_pause),this.spinner.css({display:"inline"}),setTimeout(()=>{this.controller()},1e3)}pause(t,e,s){console.log("PAUSE"),this.button.html(spbcScaner.button_scan_resume),this.spinner.css({display:"none"}),this.paused=!0,this.active=!1}resume(t){console.log("RESUME"),this.button.html(spbcScaner.button_scan_pause),this.spinner.css({display:"inline"}),this.paused=!1,this.active=!0}end(t){this.progressbar.hide(500),this.progress_overall.hide(500),this.button.html(spbcScaner.button_scan_perform),this.spinner.css({display:"none"}),this.state=null,this.plug=!1,this.total_scanned=0,this.active=!1,t?document.location=document.location:spbcSendAJAXRequest({action:"spbc_scanner_tab__reload_accordion"},{notJson:!0,callback:function(t,e,s,a){jQuery(a).accordion("destroy").html(t).accordion({header:"h3",heightStyle:"content",collapsible:!0,active:!1}),spbcTblBulkActionsListen(),spbcTblRowActionsListen(),spbcTblPaginationListen(),spbcTblSortListen(),spbcStartShowHide(),spbcScannerReloadScanInfo()}},jQuery("#spbc_scan_accordion"))}controller(t){if(console.log(this.state),void 0!==t&&t.end){if(this.state=this.getNextState(this.state),void 0===this.state)return void this.end();this.setPercents(0),this.scan_percent=0,this.offset=0,this.progress_overall.children("span").removeClass("spbc_bold").filter(".spbc_overall_scan_status_"+this.state).addClass("spbc_bold")}if(!0!==this.paused){var e={action:"spbc_scanner_controller_front",method:this.state,offset:this.offset},t={type:"GET",success:this.success,callback:this.successCallback,error:this.error,errorOutput:this.errorOutput,complete:null,context:this,timeout:12e4};switch(this.state){case"get_modules_hashes":this.amount=2;break;case"clear_table":this.amount=1e4;break;case"file_system_analysis":this.amount=700;break;case"auto_cure":this.amount=5;break;case"outbound_links":this.amount=10;break;case"frontend_analysis":this.amount=spbcSettings.frontendAnalysisAmount;break;case"signature_analysis":this.amount=10,e.status="UNKNOWN,MODIFIED,OK,INFECTED,ERROR";break;case"heuristic_analysis":case"binary_analysis":this.amount=4,e.status="UNKNOWN,MODIFIED,OK,INFECTED,ERROR";break;case"schedule_send_heuristic_suspicious_files":this.amount=1}e.amount=Math.round(this.amount*this.amount_coefficient),spbcSendAJAXRequest(e,t,jQuery("#spbc_scan_accordion"))}}setCoefficients(t){let e=this.amount_coefficient;"file_system_analysis"===t&&(e*=1.5),this.amount_coefficient=e}getNextState(t){return t=null===t?this.states[0]:this.states[this.states.indexOf(t)+1],t=void 0!==this.settings["scanner__"+t]&&0==+this.settings["scanner__"+t]?this.getNextState(t):t}setPercents(t){this.percent_completed=Math.floor(100*t)/100,this.progressbar.progressbar("option","value",this.percent_completed),this.progressbar_text.text(spbcScaner["progressbar_"+this.state]+" - "+this.percent_completed+"%")}success(t){t.error?this.error({status:200,responseText:t.error},t.error,t.msg):this.successCallback&&this.successCallback(t,this.data,this.obj)}successCallback(e){if(console.log(e),this.interactAccordion(e),void 0!==e.total&&(this.scan_percent=100/e.total),void 0!==e.processed_items&&("heuristic_analysis"===this.state&&0!==typeof e.total&&this.logRaw('

Heuristic Analysis

'),"signature_analysis"===this.state&&0!==typeof e.total&&this.logRaw('

Signature Analysis

'),this.logFileEntry(e.processed_items)),void 0!==e.stage_data_for_logging&&this.logStageEntry(e.stage_data_for_logging),void 0!==e.cured&&0{this.controller(e)},300)}interactAccordion(t){t.hasOwnProperty("interactivity_data")&&t.interactivity_data.hasOwnProperty("update_text")&&t.interactivity_data.update_text&&t.interactivity_data.hasOwnProperty("refresh_data")&&t.interactivity_data.refresh_data.hasOwnProperty("do_refresh")&&t.interactivity_data.refresh_data.do_refresh&&t.interactivity_data.refresh_data.hasOwnProperty("control_tab")&&t.interactivity_data.refresh_data.control_tab&&spbcReloadAccordion(t.interactivity_data.refresh_data.control_tab,t.interactivity_data.update_text)}error(t,e,s){var a=this.errorOutput;if(console.log("%c APBCT_AJAX_ERROR","color: red;"),console.log(e),console.log(s),console.log(t),"error"==e&&(""==s||"Not found"==s)&&(this.tryCount||(this.tryCount=0,this.retryLimit=30),this.tryCount++,console.log("Try #"+this.tryCount),this.setCoefficients(this.state),this.tryCount<=this.retryLimit))this.pause(),this.resume(),this.controller();else{if(200===t.status)if("parsererror"===e)a("Unexpected response from server. See console for details.",this.state),console.log("%c "+t.responseText,"color: pink;");else{let t=e;void 0!==s&&(t+=" Additional info: "+s),a(t,this.state)}else 500===t.status?a("Internal server error.",this.state):a("Unexpected response code: "+t.status+". Error: "+e,this.state);this.progressbar&&this.progressbar.fadeOut("slow"),this.end()}}errorOutput(t,e){spbcModal.open().putError(t+"
Stage: "+e)}logRaw(t){jQuery(".spbc-scan-log-title").removeClass("spbc---hidden"),jQuery(".spbc_log-wrapper").removeClass("spbc---hidden"),jQuery(".spbc_log-wrapper .panel-body").prepend(t)}logFileEntry(t){for(var e in t)e&&this.logRaw('

'+this.getSiteUTCShiftedTimeString()+" - "+t[e].path+" - "+t[e].module+": "+t[e].status+"

")}logStageEntry(t){void 0!==jQuery(".panel-body .spbc_log-line span").first()&&void 0!==jQuery(".panel-body .spbc_log-line span").first()[0]&&jQuery(".panel-body .spbc_log-line span").first()[0].textContent===t.description||this.logRaw('

test '+this.getSiteUTCShiftedTimeString()+" - "+t.title+" "+t.description+"

")}showLinkForShuffleSalts(t){jQuery("#spbc_notice_about_shuffle_link").remove(),jQuery(jQuery(".spbc_tab--active .spbc_wrapper_field p")[1]).after('")}getSiteUTCShiftedTimeString(){let t=!1;var e=-1*(new Date).getTimezoneOffset()*1e3*60,e=(t="undefined"!=typeof spbcScaner&&void 0!==spbcScaner.timezone_shift&&!1!==spbcScaner.timezone_shift?Date.now()-e+1e3*spbcScaner.timezone_shift:t)?new Date(t):new Date,s=new Intl.DateTimeFormat("en-US",{month:"short"}).format,a=String(e.getMinutes()).padStart(2,"0"),i=String(e.getSeconds()).padStart(2,"0");return s(e)+" "+e.getDate()+" "+e.getFullYear()+" "+e.getHours()+":"+a+":"+i}} //# sourceMappingURL=spbc-scanner-plugin.min.js.map diff --git a/js/spbc-scanner-plugin.min.js.map b/js/spbc-scanner-plugin.min.js.map index d43b21e41..403885ce3 100644 --- a/js/spbc-scanner-plugin.min.js.map +++ b/js/spbc-scanner-plugin.min.js.map @@ -1 +1 @@ -{"version":3,"file":"spbc-scanner-plugin.min.js","sources":["spbc-scanner-plugin.js"],"sourcesContent":["'use strict';\n\n/**\n * class SpbcMalwareScanner\n */\nclass SpbcMalwareScanner {/* eslint-disable-line no-unused-vars */\n first_start = true;\n\n active = false;\n\n root = '';\n settings = [];\n states = [\n 'get_cms_hashes',\n 'get_modules_hashes',\n 'clean_results',\n 'file_system_analysis',\n 'get_approved_hashes',\n 'get_denied_hashes',\n 'signature_analysis',\n 'heuristic_analysis',\n 'schedule_send_heuristic_suspicious_files',\n 'auto_cure_backup',\n 'auto_cure',\n 'os_cron_analysis',\n 'db_trigger_analysis',\n 'outbound_links',\n 'frontend_analysis',\n 'important_files_listing',\n 'send_results',\n ];\n state = null;\n offset = 0;\n amount = 0;\n amount_coefficient = 1;\n total_scanned = 0;\n scan_percent = 0;\n percent_completed = 0;\n\n paused = false;\n\n button = null;\n spinner = null;\n\n progress_overall = null;\n progressbar = null;\n progressbar_text = null;\n\n timeout = 60000;\n\n state_timer = 0;\n\n /**\n * constructor\n * @param {array} properties\n */\n constructor( properties ) {\n if (jQuery('#spbcscan-results-log-module').length) {\n jQuery('.spbc-scan-log-title').removeClass('spbc---hidden');\n }\n\n // Crunch for cure backups\n if ( typeof properties['settings']['auto_cure'] !== 'undefined' ) {\n properties['settings']['scanner__auto_cure_backup'] = '1';\n }\n\n for ( let key in properties ) {\n if ( typeof this[key] !== 'undefined' ) {\n this[key] = properties[key];\n }\n }\n };\n\n /**\n * Function Action Control\n */\n actionControl() {\n if (this.state === null) {\n this.start();\n } else if (this.paused) {\n this.resume();\n this.controller();\n } else {\n this.pause();\n }\n };\n\n /**\n * Function Start\n */\n start() {\n this.active = true;\n this.state_timer = Math.round(new Date().getTime() /1000);\n\n this.state = this.getNextState( null );\n\n this.setPercents( 0 );\n this.scan_percent = 0;\n this.offset = 0;\n this.progress_overall.children('span')\n .removeClass('spbc_bold')\n .filter('.spbc_overall_scan_status_' + this.state)\n .addClass('spbc_bold');\n\n this.progressbar.show(500);\n this.progress_overall.show(500);\n this.button.html(spbcScaner.button_scan_pause);\n this.spinner.css({display: 'inline'});\n\n setTimeout(() => {\n this.controller();\n }, 1000);\n };\n\n /**\n * Function Pause\n * @param {*} result\n * @param {*} data\n * @param {*} opt\n */\n pause( result, data, opt ) {\n console.log('PAUSE');\n this.button.html(spbcScaner.button_scan_resume);\n this.spinner.css({display: 'none'});\n this.paused = true;\n this.active = false;\n };\n\n /**\n * Function Resume\n * @param {*} opt\n */\n resume( opt ) {\n console.log('RESUME');\n this.button.html(spbcScaner.button_scan_pause);\n this.spinner.css({display: 'inline'});\n this.paused = false;\n this.active = true;\n };\n\n /**\n * Function End\n * @param {bool} reload\n */\n end( reload ) {\n this.progressbar.hide(500);\n this.progress_overall.hide(500);\n this.button.html(spbcScaner.button_scan_perform);\n this.spinner.css({display: 'none'});\n this.state = null;\n this.plug = false;\n this.total_scanned = 0;\n this.active = false;\n\n if (reload) {\n document.location = document.location;\n } else {\n spbcSendAJAXRequest(\n {action: 'spbc_scanner_tab__reload_accordion'},\n {\n notJson: true,\n callback: function(result, data, params, obj) {\n jQuery(obj).accordion('destroy')\n .html(result)\n .accordion({\n header: 'h3',\n heightStyle: 'content',\n collapsible: true,\n active: false,\n });\n spbcTblBulkActionsListen();\n spbcTblRowActionsListen();\n spbcTblPaginationListen();\n spbcTblSortListen();\n spbcStartShowHide();\n spbcScannerReloadScanInfo();\n },\n },\n jQuery('#spbc_scan_accordion'),\n );\n }\n };\n\n /**\n * Function Controller\n * @param {obj} result\n */\n controller( result ) {\n console.log(this.state);\n\n // The current stage is over. Switching to the new one\n if ( typeof result !== 'undefined' && result.end ) {\n this.state = this.getNextState( this.state );\n\n // End condition\n if (typeof this.state === 'undefined') {\n this.end();\n return;\n }\n\n // Set percent to 0\n this.setPercents( 0 );\n this.scan_percent = 0;\n this.offset = 0;\n\n // Changing visualizing of the current stage\n this.progress_overall.children('span')\n .removeClass('spbc_bold')\n .filter('.spbc_overall_scan_status_' + this.state)\n .addClass('spbc_bold');\n }\n\n // Break execution if paused\n if ( this.paused === true ) {\n return;\n }\n\n // // AJAX params\n let data = {\n action: 'spbc_scanner_controller_front',\n method: this.state,\n offset: this.offset,\n };\n\n let params = {\n type: 'GET',\n success: this.success,\n callback: this.successCallback,\n error: this.error,\n errorOutput: this.errorOutput,\n complete: null,\n context: this,\n timeout: 120000,\n };\n\n switch (this.state) {\n case 'get_modules_hashes': this.amount = 2; break;\n case 'clear_table': this.amount = 10000; break;\n case 'file_system_analysis': this.amount = 700; break;\n case 'auto_cure': this.amount = 5; break;\n case 'outbound_links': this.amount = 10; break;\n case 'frontend_analysis': this.amount = spbcSettings.frontendAnalysisAmount; break;\n case 'signature_analysis': this.amount = 10; data.status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR'; break;\n case 'heuristic_analysis': this.amount = 4; data.status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR'; break;\n case 'schedule_send_heuristic_suspicious_files': this.amount = 1; break;\n }\n\n data.amount = Math.round(this.amount * this.amount_coefficient);\n\n spbcSendAJAXRequest(\n data,\n params,\n jQuery('#spbc_scan_accordion'),\n );\n };\n\n /**\n * Set Coefficients\n * @param {string} state\n */\n setCoefficients( state ) {\n let coefficient = this.amount_coefficient;\n switch (state) {\n case 'file_system_analysis': coefficient *= 1.5; break;\n }\n this.amount_coefficient = coefficient;\n };\n\n /**\n * Get Next State\n * @param {string} state\n * @return {number}\n */\n getNextState( state ) {\n state = state === null ? this.states[0] : this.states[this.states.indexOf( state ) + 1];\n\n if (typeof this.settings['scanner__' + state] !== 'undefined' && +this.settings['scanner__' + state] === 0) {\n state = this.getNextState( state );\n }\n\n return state;\n };\n\n /**\n * Set Percents\n * @param {number} percents\n */\n setPercents( percents ) {\n this.percent_completed = Math.floor( percents * 100 ) / 100;\n this.progressbar.progressbar( 'option', 'value', this.percent_completed );\n this.progressbar_text.text( spbcScaner['progressbar_' + this.state] + ' - ' + this.percent_completed + '%' );\n };\n\n /**\n * Function Success\n * @param {obj} response\n */\n success( response ) {\n if ( !! response.error ) {\n this.error(\n {status: 200, responseText: response.error},\n response.error,\n response.msg,\n );\n } else {\n if ( this.successCallback ) {\n this.successCallback( response, this.data, this.obj );\n }\n }\n };\n\n // Processing response from backend\n /**\n * Success Callback\n * @param {obj} result\n */\n successCallback( result ) {\n console.log( result );\n\n this.interactAccordion(result);\n\n if ( typeof result.total !== 'undefined' ) {\n this.scan_percent = 100 / result.total;\n }\n\n if ( typeof result.processed_items !== 'undefined') {\n if ( this.state === 'heuristic_analysis' && typeof result.total !== 0 ) {\n this.logRaw('

Heuristic Analysis

');\n }\n if ( this.state === 'signature_analysis' && typeof result.total !== 0 ) {\n this.logRaw('

Signature Analysis

');\n }\n\n this.logFileEntry( result.processed_items );\n }\n\n if ( typeof result.stage_data_for_logging !== 'undefined') {\n this.logStageEntry( result.stage_data_for_logging );\n }\n\n // Add link on shuffle salt if cured\n if (result.cured !== undefined && Number(result.cured) > 0) {\n this.showLinkForShuffleSalts(result.message);\n }\n\n if ( result.end !== true && result.end !== 1 ) {\n let processedPercents = this.percent_completed + result.processed * this.scan_percent;\n if (result.stage_data_for_logging.title === 'File System Analysis' && processedPercents > 100) {\n processedPercents = 100;\n }\n this.setPercents(processedPercents);\n this.offset = this.offset + result.processed;\n this.controller( result );\n } else {\n console.log( this.state +\n ' stage took ' +\n ( Math.round(new Date().getTime() /1000) - this.state_timer ) +\n ' seconds to complete' );\n this.state_timer = Math.round(new Date().getTime()/1000);\n this.setPercents( 100 );\n this.scan_percent = 0;\n this.offset = 0;\n setTimeout(() => {\n this.controller( result );\n }, 300);\n }\n };\n\n /**\n * Run interactive refresh for accordion.\n * @param {obj|string[]} result\n */\n interactAccordion(result) {\n // validation control\n if (result.hasOwnProperty('interactivity_data') &&\n result.interactivity_data.hasOwnProperty('update_text') &&\n result.interactivity_data.update_text &&\n result.interactivity_data.hasOwnProperty('refresh_data') &&\n result.interactivity_data.refresh_data.hasOwnProperty('do_refresh') &&\n result.interactivity_data.refresh_data.do_refresh &&\n result.interactivity_data.refresh_data.hasOwnProperty('control_tab') &&\n result.interactivity_data.refresh_data.control_tab\n ) {\n spbcReloadAccordion(\n result.interactivity_data.refresh_data.control_tab,\n result.interactivity_data.update_text,\n );\n }\n }\n\n /**\n * Function Error\n * @param {object} xhr\n * @param {string} status\n * @param {string} error\n */\n error( xhr, status, error ) {\n let errorOutput = this.errorOutput;\n\n console.log( '%c APBCT_AJAX_ERROR', 'color: red;' );\n console.log( status );\n console.log( error );\n console.log( xhr );\n\n if (status == 'error' && (error == '' || error == 'Not found')) {\n if (!this.tryCount) {\n this.tryCount = 0;\n this.retryLimit = 30;\n }\n this.tryCount++;\n console.log('Try #' + this.tryCount);\n this.setCoefficients(this.state);\n if (this.tryCount <= this.retryLimit) {\n this.pause();\n this.resume();\n this.controller();\n return;\n }\n }\n\n if ( xhr.status === 200 ) {\n if ( status === 'parsererror' ) {\n errorOutput( 'Unexpected response from server. See console for details.', this.state );\n console.log( '%c ' + xhr.responseText, 'color: pink;' );\n } else {\n let errorString = status;\n if ( typeof error !== 'undefined' ) {\n errorString += ' Additional info: ' + error;\n }\n errorOutput( errorString, this.state );\n }\n } else if (xhr.status === 500) {\n errorOutput( 'Internal server error.', this.state);\n } else {\n errorOutput('Unexpected response code: ' + xhr.status + '. Error: ' + status, this.state);\n }\n\n if ( this.progressbar ) {\n this.progressbar.fadeOut('slow');\n }\n\n this.end();\n };\n\n /**\n * Error Output\n * @param {string} errorMsg\n * @param {string} stage\n */\n errorOutput( errorMsg, stage ) {\n spbcModal.open().putError( errorMsg + '
Stage: ' + stage);\n };\n\n /**\n * Log Raw\n * @param {htmlString|Element|Text|Array|jQuery} messageToLog\n */\n logRaw(messageToLog) {\n jQuery('.spbc-scan-log-title').removeClass('spbc---hidden');\n jQuery('.spbc_log-wrapper').removeClass('spbc---hidden');\n jQuery('.spbc_log-wrapper .panel-body').prepend( messageToLog );\n };\n\n /**\n * Log File Entry\n * @param {array} items\n */\n logFileEntry(items) {\n for ( let key in items ) {\n if ( key ) {\n this.logRaw(\n '

' +\n this.getSiteUTCShiftedTimeString() + ' - ' +\n items[key].path + ' - ' + items[key].module +\n ': ' + items[key].status + '' +\n '

');\n }\n }\n };\n\n /**\n * Log Stage Entry\n * @param {obj} data\n */\n logStageEntry(data) {\n if (typeof jQuery('.panel-body .spbc_log-line span').first() !== 'undefined' &&\n typeof jQuery('.panel-body .spbc_log-line span').first()[0] !== 'undefined' &&\n jQuery('.panel-body .spbc_log-line span').first()[0].textContent === data.description\n ) {\n return;\n }\n this.logRaw( '

test ' +\n this.getSiteUTCShiftedTimeString() + ' - ' + '' +\n data.title + ' ' + '' + data.description + '

' );\n };\n\n /**\n * Show Link For Shuffle Salts\n * @param {string} message\n */\n showLinkForShuffleSalts(message) {\n jQuery('#spbc_notice_about_shuffle_link').remove();\n jQuery(jQuery('.spbc_tab--active .spbc_wrapper_field p')[1])\n .after(\n '
' +\n '' +\n message +\n '' +\n '
',\n );\n }\n\n /**\n * Get Site UTC Shifted Time String\n * @return {string}\n */\n getSiteUTCShiftedTimeString() {\n let utcShiftedTs = false;\n // gettings current system/browser offset\n let currentBrowserOffset = new Date().getTimezoneOffset();\n currentBrowserOffset = currentBrowserOffset * -1 * 1000 * 60;\n // chek if global ct object is defined\n if (typeof spbcScaner !== 'undefined' &&\n typeof spbcScaner.timezone_shift !== 'undefined' &&\n spbcScaner.timezone_shift !== false) {\n utcShiftedTs = Date.now() - currentBrowserOffset + (spbcScaner.timezone_shift * 1000);\n }\n let ctDate = utcShiftedTs ? new Date(utcShiftedTs) : new Date();\n // construct date string\n let shortMonthName = new Intl.DateTimeFormat('en-US', {month: 'short'}).format;\n let minutes = String(ctDate.getMinutes()).padStart(2, '0');\n let seconds = String(ctDate.getSeconds()).padStart(2, '0');\n return shortMonthName(ctDate) + ' ' +\n ctDate.getDate() + ' ' + ctDate.getFullYear() + ' ' +\n ctDate.getHours() + ':' + minutes + ':' + seconds;\n }\n}\n"],"names":["SpbcMalwareScanner","first_start","active","root","settings","states","state","offset","amount","amount_coefficient","total_scanned","scan_percent","percent_completed","paused","button","spinner","progress_overall","progressbar","progressbar_text","timeout","state_timer","constructor","properties","let","key","jQuery","length","removeClass","this","actionControl","start","resume","controller","pause","Math","round","Date","getTime","getNextState","setPercents","children","filter","addClass","show","html","spbcScaner","button_scan_pause","css","display","setTimeout","result","data","opt","console","log","button_scan_resume","end","reload","hide","button_scan_perform","plug","document","location","spbcSendAJAXRequest","action","notJson","callback","params","obj","accordion","header","heightStyle","collapsible","spbcTblBulkActionsListen","spbcTblRowActionsListen","spbcTblPaginationListen","spbcTblSortListen","spbcStartShowHide","spbcScannerReloadScanInfo","method","type","success","successCallback","error","errorOutput","complete","context","spbcSettings","frontendAnalysisAmount","status","setCoefficients","coefficient","indexOf","percents","floor","text","response","responseText","msg","interactAccordion","total","processed_items","logRaw","logFileEntry","stage_data_for_logging","logStageEntry","undefined","cured","Number","showLinkForShuffleSalts","message","processedPercents","processed","title","hasOwnProperty","interactivity_data","update_text","refresh_data","do_refresh","control_tab","spbcReloadAccordion","xhr","tryCount","retryLimit","errorString","fadeOut","errorMsg","stage","spbcModal","open","putError","messageToLog","prepend","items","getSiteUTCShiftedTimeString","path","module","first","textContent","description","remove","after","utcShiftedTs","currentBrowserOffset","getTimezoneOffset","ctDate","timezone_shift","now","shortMonthName","Intl","DateTimeFormat","month","format","minutes","String","getMinutes","padStart","seconds","getSeconds","getDate","getFullYear","getHours"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"spbc-scanner-plugin.min.js","sources":["spbc-scanner-plugin.js"],"sourcesContent":["'use strict';\n\n/**\n * class SpbcMalwareScanner\n */\nclass SpbcMalwareScanner {/* eslint-disable-line no-unused-vars */\n first_start = true;\n\n active = false;\n\n root = '';\n settings = [];\n states = [\n 'get_cms_hashes',\n 'get_modules_hashes',\n 'clean_results',\n 'file_system_analysis',\n 'get_approved_hashes',\n 'get_denied_hashes',\n 'signature_analysis',\n 'heuristic_analysis',\n 'schedule_send_heuristic_suspicious_files',\n 'auto_cure_backup',\n 'auto_cure',\n 'os_cron_analysis',\n 'db_trigger_analysis',\n 'binary_analysis',\n 'outbound_links',\n 'frontend_analysis',\n 'important_files_listing',\n 'send_results',\n ];\n state = null;\n offset = 0;\n amount = 0;\n amount_coefficient = 1;\n total_scanned = 0;\n scan_percent = 0;\n percent_completed = 0;\n\n paused = false;\n\n button = null;\n spinner = null;\n\n progress_overall = null;\n progressbar = null;\n progressbar_text = null;\n\n timeout = 60000;\n\n state_timer = 0;\n\n /**\n * constructor\n * @param {array} properties\n */\n constructor( properties ) {\n if (jQuery('#spbcscan-results-log-module').length) {\n jQuery('.spbc-scan-log-title').removeClass('spbc---hidden');\n }\n\n // Crunch for cure backups\n if ( typeof properties['settings']['auto_cure'] !== 'undefined' ) {\n properties['settings']['scanner__auto_cure_backup'] = '1';\n }\n\n for ( let key in properties ) {\n if ( typeof this[key] !== 'undefined' ) {\n this[key] = properties[key];\n }\n }\n };\n\n /**\n * Function Action Control\n */\n actionControl() {\n if (this.state === null) {\n this.start();\n } else if (this.paused) {\n this.resume();\n this.controller();\n } else {\n this.pause();\n }\n };\n\n /**\n * Function Start\n */\n start() {\n this.active = true;\n this.state_timer = Math.round(new Date().getTime() /1000);\n\n this.state = this.getNextState( null );\n\n this.setPercents( 0 );\n this.scan_percent = 0;\n this.offset = 0;\n this.progress_overall.children('span')\n .removeClass('spbc_bold')\n .filter('.spbc_overall_scan_status_' + this.state)\n .addClass('spbc_bold');\n\n this.progressbar.show(500);\n this.progress_overall.show(500);\n this.button.html(spbcScaner.button_scan_pause);\n this.spinner.css({display: 'inline'});\n\n setTimeout(() => {\n this.controller();\n }, 1000);\n };\n\n /**\n * Function Pause\n * @param {*} result\n * @param {*} data\n * @param {*} opt\n */\n pause( result, data, opt ) {\n console.log('PAUSE');\n this.button.html(spbcScaner.button_scan_resume);\n this.spinner.css({display: 'none'});\n this.paused = true;\n this.active = false;\n };\n\n /**\n * Function Resume\n * @param {*} opt\n */\n resume( opt ) {\n console.log('RESUME');\n this.button.html(spbcScaner.button_scan_pause);\n this.spinner.css({display: 'inline'});\n this.paused = false;\n this.active = true;\n };\n\n /**\n * Function End\n * @param {bool} reload\n */\n end( reload ) {\n this.progressbar.hide(500);\n this.progress_overall.hide(500);\n this.button.html(spbcScaner.button_scan_perform);\n this.spinner.css({display: 'none'});\n this.state = null;\n this.plug = false;\n this.total_scanned = 0;\n this.active = false;\n\n if (reload) {\n document.location = document.location;\n } else {\n spbcSendAJAXRequest(\n {action: 'spbc_scanner_tab__reload_accordion'},\n {\n notJson: true,\n callback: function(result, data, params, obj) {\n jQuery(obj).accordion('destroy')\n .html(result)\n .accordion({\n header: 'h3',\n heightStyle: 'content',\n collapsible: true,\n active: false,\n });\n spbcTblBulkActionsListen();\n spbcTblRowActionsListen();\n spbcTblPaginationListen();\n spbcTblSortListen();\n spbcStartShowHide();\n spbcScannerReloadScanInfo();\n },\n },\n jQuery('#spbc_scan_accordion'),\n );\n }\n };\n\n /**\n * Function Controller\n * @param {obj} result\n */\n controller( result ) {\n console.log(this.state);\n\n // The current stage is over. Switching to the new one\n if ( typeof result !== 'undefined' && result.end ) {\n this.state = this.getNextState( this.state );\n\n // End condition\n if (typeof this.state === 'undefined') {\n this.end();\n return;\n }\n\n // Set percent to 0\n this.setPercents( 0 );\n this.scan_percent = 0;\n this.offset = 0;\n\n // Changing visualizing of the current stage\n this.progress_overall.children('span')\n .removeClass('spbc_bold')\n .filter('.spbc_overall_scan_status_' + this.state)\n .addClass('spbc_bold');\n }\n\n // Break execution if paused\n if ( this.paused === true ) {\n return;\n }\n\n // // AJAX params\n let data = {\n action: 'spbc_scanner_controller_front',\n method: this.state,\n offset: this.offset,\n };\n\n let params = {\n type: 'GET',\n success: this.success,\n callback: this.successCallback,\n error: this.error,\n errorOutput: this.errorOutput,\n complete: null,\n context: this,\n timeout: 120000,\n };\n\n switch (this.state) {\n case 'get_modules_hashes': this.amount = 2; break;\n case 'clear_table': this.amount = 10000; break;\n case 'file_system_analysis': this.amount = 700; break;\n case 'auto_cure': this.amount = 5; break;\n case 'outbound_links': this.amount = 10; break;\n case 'frontend_analysis': this.amount = spbcSettings.frontendAnalysisAmount; break;\n case 'signature_analysis': this.amount = 10; data.status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR'; break;\n case 'heuristic_analysis': this.amount = 4; data.status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR'; break;\n case 'binary_analysis': this.amount = 4; data.status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR'; break;\n case 'schedule_send_heuristic_suspicious_files': this.amount = 1; break;\n }\n\n data.amount = Math.round(this.amount * this.amount_coefficient);\n\n spbcSendAJAXRequest(\n data,\n params,\n jQuery('#spbc_scan_accordion'),\n );\n };\n\n /**\n * Set Coefficients\n * @param {string} state\n */\n setCoefficients( state ) {\n let coefficient = this.amount_coefficient;\n switch (state) {\n case 'file_system_analysis': coefficient *= 1.5; break;\n }\n this.amount_coefficient = coefficient;\n };\n\n /**\n * Get Next State\n * @param {string} state\n * @return {number}\n */\n getNextState( state ) {\n state = state === null ? this.states[0] : this.states[this.states.indexOf( state ) + 1];\n\n if (typeof this.settings['scanner__' + state] !== 'undefined' && +this.settings['scanner__' + state] === 0) {\n state = this.getNextState( state );\n }\n\n return state;\n };\n\n /**\n * Set Percents\n * @param {number} percents\n */\n setPercents( percents ) {\n this.percent_completed = Math.floor( percents * 100 ) / 100;\n this.progressbar.progressbar( 'option', 'value', this.percent_completed );\n this.progressbar_text.text( spbcScaner['progressbar_' + this.state] + ' - ' + this.percent_completed + '%' );\n };\n\n /**\n * Function Success\n * @param {obj} response\n */\n success( response ) {\n if ( !! response.error ) {\n this.error(\n {status: 200, responseText: response.error},\n response.error,\n response.msg,\n );\n } else {\n if ( this.successCallback ) {\n this.successCallback( response, this.data, this.obj );\n }\n }\n };\n\n // Processing response from backend\n /**\n * Success Callback\n * @param {obj} result\n */\n successCallback( result ) {\n console.log( result );\n\n this.interactAccordion(result);\n\n if ( typeof result.total !== 'undefined' ) {\n this.scan_percent = 100 / result.total;\n }\n\n if ( typeof result.processed_items !== 'undefined') {\n if ( this.state === 'heuristic_analysis' && typeof result.total !== 0 ) {\n this.logRaw('

Heuristic Analysis

');\n }\n if ( this.state === 'signature_analysis' && typeof result.total !== 0 ) {\n this.logRaw('

Signature Analysis

');\n }\n\n this.logFileEntry( result.processed_items );\n }\n\n if ( typeof result.stage_data_for_logging !== 'undefined') {\n this.logStageEntry( result.stage_data_for_logging );\n }\n\n // Add link on shuffle salt if cured\n if (result.cured !== undefined && Number(result.cured) > 0) {\n this.showLinkForShuffleSalts(result.message);\n }\n\n if ( result.end !== true && result.end !== 1 ) {\n let processedPercents = this.percent_completed + result.processed * this.scan_percent;\n if (result.stage_data_for_logging.title === 'File System Analysis' && processedPercents > 100) {\n processedPercents = 100;\n }\n this.setPercents(processedPercents);\n this.offset = this.offset + result.processed;\n this.controller( result );\n } else {\n console.log( this.state +\n ' stage took ' +\n ( Math.round(new Date().getTime() /1000) - this.state_timer ) +\n ' seconds to complete' );\n this.state_timer = Math.round(new Date().getTime()/1000);\n this.setPercents( 100 );\n this.scan_percent = 0;\n this.offset = 0;\n setTimeout(() => {\n this.controller( result );\n }, 300);\n }\n };\n\n /**\n * Run interactive refresh for accordion.\n * @param {obj|string[]} result\n */\n interactAccordion(result) {\n // validation control\n if (result.hasOwnProperty('interactivity_data') &&\n result.interactivity_data.hasOwnProperty('update_text') &&\n result.interactivity_data.update_text &&\n result.interactivity_data.hasOwnProperty('refresh_data') &&\n result.interactivity_data.refresh_data.hasOwnProperty('do_refresh') &&\n result.interactivity_data.refresh_data.do_refresh &&\n result.interactivity_data.refresh_data.hasOwnProperty('control_tab') &&\n result.interactivity_data.refresh_data.control_tab\n ) {\n spbcReloadAccordion(\n result.interactivity_data.refresh_data.control_tab,\n result.interactivity_data.update_text,\n );\n }\n }\n\n /**\n * Function Error\n * @param {object} xhr\n * @param {string} status\n * @param {string} error\n */\n error( xhr, status, error ) {\n let errorOutput = this.errorOutput;\n\n console.log( '%c APBCT_AJAX_ERROR', 'color: red;' );\n console.log( status );\n console.log( error );\n console.log( xhr );\n\n if (status == 'error' && (error == '' || error == 'Not found')) {\n if (!this.tryCount) {\n this.tryCount = 0;\n this.retryLimit = 30;\n }\n this.tryCount++;\n console.log('Try #' + this.tryCount);\n this.setCoefficients(this.state);\n if (this.tryCount <= this.retryLimit) {\n this.pause();\n this.resume();\n this.controller();\n return;\n }\n }\n\n if ( xhr.status === 200 ) {\n if ( status === 'parsererror' ) {\n errorOutput( 'Unexpected response from server. See console for details.', this.state );\n console.log( '%c ' + xhr.responseText, 'color: pink;' );\n } else {\n let errorString = status;\n if ( typeof error !== 'undefined' ) {\n errorString += ' Additional info: ' + error;\n }\n errorOutput( errorString, this.state );\n }\n } else if (xhr.status === 500) {\n errorOutput( 'Internal server error.', this.state);\n } else {\n errorOutput('Unexpected response code: ' + xhr.status + '. Error: ' + status, this.state);\n }\n\n if ( this.progressbar ) {\n this.progressbar.fadeOut('slow');\n }\n\n this.end();\n };\n\n /**\n * Error Output\n * @param {string} errorMsg\n * @param {string} stage\n */\n errorOutput( errorMsg, stage ) {\n spbcModal.open().putError( errorMsg + '
Stage: ' + stage);\n };\n\n /**\n * Log Raw\n * @param {htmlString|Element|Text|Array|jQuery} messageToLog\n */\n logRaw(messageToLog) {\n jQuery('.spbc-scan-log-title').removeClass('spbc---hidden');\n jQuery('.spbc_log-wrapper').removeClass('spbc---hidden');\n jQuery('.spbc_log-wrapper .panel-body').prepend( messageToLog );\n };\n\n /**\n * Log File Entry\n * @param {array} items\n */\n logFileEntry(items) {\n for ( let key in items ) {\n if ( key ) {\n this.logRaw(\n '

' +\n this.getSiteUTCShiftedTimeString() + ' - ' +\n items[key].path + ' - ' + items[key].module +\n ': ' + items[key].status + '' +\n '

');\n }\n }\n };\n\n /**\n * Log Stage Entry\n * @param {obj} data\n */\n logStageEntry(data) {\n if (typeof jQuery('.panel-body .spbc_log-line span').first() !== 'undefined' &&\n typeof jQuery('.panel-body .spbc_log-line span').first()[0] !== 'undefined' &&\n jQuery('.panel-body .spbc_log-line span').first()[0].textContent === data.description\n ) {\n return;\n }\n this.logRaw( '

test ' +\n this.getSiteUTCShiftedTimeString() + ' - ' + '' +\n data.title + ' ' + '' + data.description + '

' );\n };\n\n /**\n * Show Link For Shuffle Salts\n * @param {string} message\n */\n showLinkForShuffleSalts(message) {\n jQuery('#spbc_notice_about_shuffle_link').remove();\n jQuery(jQuery('.spbc_tab--active .spbc_wrapper_field p')[1])\n .after(\n '
' +\n '' +\n message +\n '' +\n '
',\n );\n }\n\n /**\n * Get Site UTC Shifted Time String\n * @return {string}\n */\n getSiteUTCShiftedTimeString() {\n let utcShiftedTs = false;\n // gettings current system/browser offset\n let currentBrowserOffset = new Date().getTimezoneOffset();\n currentBrowserOffset = currentBrowserOffset * -1 * 1000 * 60;\n // chek if global ct object is defined\n if (typeof spbcScaner !== 'undefined' &&\n typeof spbcScaner.timezone_shift !== 'undefined' &&\n spbcScaner.timezone_shift !== false) {\n utcShiftedTs = Date.now() - currentBrowserOffset + (spbcScaner.timezone_shift * 1000);\n }\n let ctDate = utcShiftedTs ? new Date(utcShiftedTs) : new Date();\n // construct date string\n let shortMonthName = new Intl.DateTimeFormat('en-US', {month: 'short'}).format;\n let minutes = String(ctDate.getMinutes()).padStart(2, '0');\n let seconds = String(ctDate.getSeconds()).padStart(2, '0');\n return shortMonthName(ctDate) + ' ' +\n ctDate.getDate() + ' ' + ctDate.getFullYear() + ' ' +\n ctDate.getHours() + ':' + minutes + ':' + seconds;\n }\n}\n"],"names":["SpbcMalwareScanner","first_start","active","root","settings","states","state","offset","amount","amount_coefficient","total_scanned","scan_percent","percent_completed","paused","button","spinner","progress_overall","progressbar","progressbar_text","timeout","state_timer","constructor","properties","let","key","jQuery","length","removeClass","this","actionControl","start","resume","controller","pause","Math","round","Date","getTime","getNextState","setPercents","children","filter","addClass","show","html","spbcScaner","button_scan_pause","css","display","setTimeout","result","data","opt","console","log","button_scan_resume","end","reload","hide","button_scan_perform","plug","document","location","spbcSendAJAXRequest","action","notJson","callback","params","obj","accordion","header","heightStyle","collapsible","spbcTblBulkActionsListen","spbcTblRowActionsListen","spbcTblPaginationListen","spbcTblSortListen","spbcStartShowHide","spbcScannerReloadScanInfo","method","type","success","successCallback","error","errorOutput","complete","context","spbcSettings","frontendAnalysisAmount","status","setCoefficients","coefficient","indexOf","percents","floor","text","response","responseText","msg","interactAccordion","total","processed_items","logRaw","logFileEntry","stage_data_for_logging","logStageEntry","undefined","cured","Number","showLinkForShuffleSalts","message","processedPercents","processed","title","hasOwnProperty","interactivity_data","update_text","refresh_data","do_refresh","control_tab","spbcReloadAccordion","xhr","tryCount","retryLimit","errorString","fadeOut","errorMsg","stage","spbcModal","open","putError","messageToLog","prepend","items","getSiteUTCShiftedTimeString","path","module","first","textContent","description","remove","after","utcShiftedTs","currentBrowserOffset","getTimezoneOffset","ctDate","timezone_shift","now","shortMonthName","Intl","DateTimeFormat","month","format","minutes","String","getMinutes","padStart","seconds","getSeconds","getDate","getFullYear","getHours"],"mappings":"MAKMA,mBACFC,YAAc,CAAA,EAEdC,OAAS,CAAA,EAETC,KAAO,GACPC,SAAW,GACXC,OAAS,CACL,iBACA,qBACA,gBACA,uBACA,sBACA,oBACA,qBACA,qBACA,2CACA,mBACA,YACA,mBACA,sBACA,kBACA,iBACA,oBACA,0BACA,gBAEJC,MAAQ,KACRC,OAAS,EACTC,OAAS,EACTC,mBAAqB,EACrBC,cAAgB,EAChBC,aAAe,EACfC,kBAAoB,EAEpBC,OAAS,CAAA,EAETC,OAAS,KACTC,QAAU,KAEVC,iBAAmB,KACnBC,YAAc,KACdC,iBAAmB,KAEnBC,QAAU,IAEVC,YAAc,EAMdC,YAAaC,GAUT,IAAMC,IAAIC,KATNC,OAAO,8BAA8B,EAAEC,QACvCD,OAAO,sBAAsB,EAAEE,YAAY,eAAe,EAIV,KAAA,IAAxCL,EAAqB,SAAa,YAC1CA,EAAqB,SAA6B,0BAAI,KAGzCA,EACa,KAAA,IAAdM,KAAKJ,KACbI,KAAKJ,GAAOF,EAAWE,GAGnC,CAKAK,gBACuB,OAAfD,KAAKtB,MACLsB,KAAKE,MAAM,EACJF,KAAKf,QACZe,KAAKG,OAAO,EACZH,KAAKI,WAAW,GAEhBJ,KAAKK,MAAM,CAEnB,CAKAH,QACIF,KAAK1B,OAAS,CAAA,EACd0B,KAAKR,YAAcc,KAAKC,OAAM,IAAIC,MAAOC,QAAQ,EAAG,GAAI,EAExDT,KAAKtB,MAAQsB,KAAKU,aAAc,IAAK,EAErCV,KAAKW,YAAa,CAAE,EACpBX,KAAKjB,aAAe,EACpBiB,KAAKrB,OAAS,EACdqB,KAAKZ,iBAAiBwB,SAAS,MAAM,EAChCb,YAAY,WAAW,EACvBc,OAAO,6BAA+Bb,KAAKtB,KAAK,EAChDoC,SAAS,WAAW,EAEzBd,KAAKX,YAAY0B,KAAK,GAAG,EACzBf,KAAKZ,iBAAiB2B,KAAK,GAAG,EAC9Bf,KAAKd,OAAO8B,KAAKC,WAAWC,iBAAiB,EAC7ClB,KAAKb,QAAQgC,IAAI,CAACC,QAAS,QAAQ,CAAC,EAEpCC,WAAW,KACPrB,KAAKI,WAAW,CACpB,EAAG,GAAI,CACX,CAQAC,MAAOiB,EAAQC,EAAMC,GACjBC,QAAQC,IAAI,OAAO,EACnB1B,KAAKd,OAAO8B,KAAKC,WAAWU,kBAAkB,EAC9C3B,KAAKb,QAAQgC,IAAI,CAACC,QAAS,MAAM,CAAC,EAClCpB,KAAKf,OAAS,CAAA,EACde,KAAK1B,OAAS,CAAA,CAClB,CAMA6B,OAAQqB,GACJC,QAAQC,IAAI,QAAQ,EACpB1B,KAAKd,OAAO8B,KAAKC,WAAWC,iBAAiB,EAC7ClB,KAAKb,QAAQgC,IAAI,CAACC,QAAS,QAAQ,CAAC,EACpCpB,KAAKf,OAAS,CAAA,EACde,KAAK1B,OAAS,CAAA,CAClB,CAMAsD,IAAKC,GACD7B,KAAKX,YAAYyC,KAAK,GAAG,EACzB9B,KAAKZ,iBAAiB0C,KAAK,GAAG,EAC9B9B,KAAKd,OAAO8B,KAAKC,WAAWc,mBAAmB,EAC/C/B,KAAKb,QAAQgC,IAAI,CAACC,QAAS,MAAM,CAAC,EAClCpB,KAAKtB,MAAQ,KACbsB,KAAKgC,KAAO,CAAA,EACZhC,KAAKlB,cAAgB,EACrBkB,KAAK1B,OAAS,CAAA,EAEVuD,EACAI,SAASC,SAAWD,SAASC,SAE7BC,oBACI,CAACC,OAAQ,oCAAoC,EAC7C,CACIC,QAAS,CAAA,EACTC,SAAU,SAAShB,EAAQC,EAAMgB,EAAQC,GACrC3C,OAAO2C,CAAG,EAAEC,UAAU,SAAS,EAC1BzB,KAAKM,CAAM,EACXmB,UAAU,CACPC,OAAQ,KACRC,YAAa,UACbC,YAAa,CAAA,EACbtE,OAAQ,CAAA,CACZ,CAAC,EACLuE,yBAAyB,EACzBC,wBAAwB,EACxBC,wBAAwB,EACxBC,kBAAkB,EAClBC,kBAAkB,EAClBC,0BAA0B,CAC9B,CACJ,EACArD,OAAO,sBAAsB,CACjC,CAER,CAMAO,WAAYkB,GAIR,GAHAG,QAAQC,IAAI1B,KAAKtB,KAAK,EAGC,KAAA,IAAX4C,GAA0BA,EAAOM,IAAM,CAI/C,GAHA5B,KAAKtB,MAAQsB,KAAKU,aAAcV,KAAKtB,KAAM,EAGjB,KAAA,IAAfsB,KAAKtB,MAEZ,OADAsB,KAAAA,KAAK4B,IAAI,EAKb5B,KAAKW,YAAa,CAAE,EACpBX,KAAKjB,aAAe,EACpBiB,KAAKrB,OAAS,EAGdqB,KAAKZ,iBAAiBwB,SAAS,MAAM,EAChCb,YAAY,WAAW,EACvBc,OAAO,6BAA+Bb,KAAKtB,KAAK,EAChDoC,SAAS,WAAW,CAC7B,CAGA,GAAqB,CAAA,IAAhBd,KAAKf,OAAV,CAKAU,IAAI4B,EAAO,CACPa,OAAQ,gCACRe,OAAQnD,KAAKtB,MACbC,OAAQqB,KAAKrB,MACjB,EAEI4D,EAAS,CACTa,KAAM,MACNC,QAASrD,KAAKqD,QACdf,SAAUtC,KAAKsD,gBACfC,MAAOvD,KAAKuD,MACZC,YAAaxD,KAAKwD,YAClBC,SAAU,KACVC,QAAS1D,KACTT,QAAS,IACb,EAEA,OAAQS,KAAKtB,OACb,IAAK,qBAAsBsB,KAAKpB,OAAS,EAAG,MAC5C,IAAK,cAAeoB,KAAKpB,OAAS,IAAO,MACzC,IAAK,uBAAwBoB,KAAKpB,OAAS,IAAK,MAChD,IAAK,YAAaoB,KAAKpB,OAAS,EAAG,MACnC,IAAK,iBAAkBoB,KAAKpB,OAAS,GAAI,MACzC,IAAK,oBAAqBoB,KAAKpB,OAAS+E,aAAaC,uBAAwB,MAC7E,IAAK,qBAAsB5D,KAAKpB,OAAS,GAAI2C,EAAKsC,OAAS,qCAAsC,MACjG,IAAK,qBACL,IAAK,kBAAmB7D,KAAKpB,OAAS,EAAG2C,EAAKsC,OAAS,qCAAsC,MAC7F,IAAK,2CAA4C7D,KAAKpB,OAAS,CAC/D,CAEA2C,EAAK3C,OAAS0B,KAAKC,MAAMP,KAAKpB,OAASoB,KAAKnB,kBAAkB,EAE9DsD,oBACIZ,EACAgB,EACA1C,OAAO,sBAAsB,CACjC,CAvCA,CAwCJ,CAMAiE,gBAAiBpF,GACbiB,IAAIoE,EAAc/D,KAAKnB,mBAElB,yBADGH,IACqBqF,GAAe,KAE5C/D,KAAKnB,mBAAqBkF,CAC9B,CAOArD,aAAchC,GAOV,OANAA,EAAkB,OAAVA,EAAiBsB,KAAKvB,OAAO,GAAKuB,KAAKvB,OAAOuB,KAAKvB,OAAOuF,QAAStF,CAAM,EAAI,GAGjFA,EAD8C,KAAA,IAAvCsB,KAAKxB,SAAS,YAAcE,IAAkE,GAAxC,CAACsB,KAAKxB,SAAS,YAAcE,GAClFsB,KAAKU,aAAchC,CAAM,EAG9BA,CACX,CAMAiC,YAAasD,GACTjE,KAAKhB,kBAAoBsB,KAAK4D,MAAkB,IAAXD,CAAe,EAAI,IACxDjE,KAAKX,YAAYA,YAAa,SAAU,QAASW,KAAKhB,iBAAkB,EACxEgB,KAAKV,iBAAiB6E,KAAMlD,WAAW,eAAiBjB,KAAKtB,OAAS,MAAQsB,KAAKhB,kBAAoB,GAAI,CAC/G,CAMAqE,QAASe,GACGA,EAASb,MACbvD,KAAKuD,MACD,CAACM,OAAQ,IAAKQ,aAAcD,EAASb,KAAK,EAC1Ca,EAASb,MACTa,EAASE,GACb,EAEKtE,KAAKsD,iBACNtD,KAAKsD,gBAAiBc,EAAUpE,KAAKuB,KAAMvB,KAAKwC,GAAI,CAGhE,CAOAc,gBAAiBhC,GA6Bb,GA5BAG,QAAQC,IAAKJ,CAAO,EAEpBtB,KAAKuE,kBAAkBjD,CAAM,EAEA,KAAA,IAAjBA,EAAOkD,QACfxE,KAAKjB,aAAe,IAAMuC,EAAOkD,OAGE,KAAA,IAA3BlD,EAAOmD,kBACK,uBAAfzE,KAAKtB,OAA0D,IAAxB,OAAO4C,EAAOkD,OACtDxE,KAAK0E,OAAO,2DAA2D,EAEvD,uBAAf1E,KAAKtB,OAA0D,IAAxB,OAAO4C,EAAOkD,OACtDxE,KAAK0E,OAAO,2DAA2D,EAG3E1E,KAAK2E,aAAcrD,EAAOmD,eAAgB,GAGA,KAAA,IAAlCnD,EAAOsD,wBACf5E,KAAK6E,cAAevD,EAAOsD,sBAAuB,EAIjCE,KAAAA,IAAjBxD,EAAOyD,OAA8C,EAAvBC,OAAO1D,EAAOyD,KAAK,GACjD/E,KAAKiF,wBAAwB3D,EAAO4D,OAAO,EAG3B,CAAA,IAAf5D,EAAOM,KAA+B,IAAfN,EAAOM,IAAY,CAC3CjC,IAAIwF,EAAoBnF,KAAKhB,kBAAoBsC,EAAO8D,UAAYpF,KAAKjB,aAC7B,yBAAxCuC,EAAOsD,uBAAuBS,OAAwD,IAApBF,IAClEA,EAAoB,KAExBnF,KAAKW,YAAYwE,CAAiB,EAClCnF,KAAKrB,OAASqB,KAAKrB,OAAS2C,EAAO8D,UACnCpF,KAAKI,WAAYkB,CAAO,CAC5B,MACIG,QAAQC,IAAK1B,KAAKtB,MACd,gBACE4B,KAAKC,OAAM,IAAIC,MAAOC,QAAQ,EAAG,GAAI,EAAIT,KAAKR,aAChD,sBAAuB,EAC3BQ,KAAKR,YAAcc,KAAKC,OAAM,IAAIC,MAAOC,QAAQ,EAAE,GAAI,EACvDT,KAAKW,YAAa,GAAI,EACtBX,KAAKjB,aAAe,EACpBiB,KAAKrB,OAAS,EACd0C,WAAW,KACPrB,KAAKI,WAAYkB,CAAO,CAC5B,EAAG,GAAG,CAEd,CAMAiD,kBAAkBjD,GAEVA,EAAOgE,eAAe,oBAAoB,GAC1ChE,EAAOiE,mBAAmBD,eAAe,aAAa,GACtDhE,EAAOiE,mBAAmBC,aAC1BlE,EAAOiE,mBAAmBD,eAAe,cAAc,GACvDhE,EAAOiE,mBAAmBE,aAAaH,eAAe,YAAY,GAClEhE,EAAOiE,mBAAmBE,aAAaC,YACvCpE,EAAOiE,mBAAmBE,aAAaH,eAAe,aAAa,GACnEhE,EAAOiE,mBAAmBE,aAAaE,aAEvCC,oBACItE,EAAOiE,mBAAmBE,aAAaE,YACvCrE,EAAOiE,mBAAmBC,WAC9B,CAER,CAQAjC,MAAOsC,EAAKhC,EAAQN,GAChB5D,IAAI6D,EAAcxD,KAAKwD,YAOvB,GALA/B,QAAQC,IAAK,sBAAuB,aAAc,EAClDD,QAAQC,IAAKmC,CAAO,EACpBpC,QAAQC,IAAK6B,CAAM,EACnB9B,QAAQC,IAAKmE,CAAI,EAEH,SAAVhC,IAA+B,IAATN,GAAwB,aAATA,KAChCvD,KAAK8F,WACN9F,KAAK8F,SAAW,EAChB9F,KAAK+F,WAAa,IAEtB/F,KAAK8F,QAAQ,GACbrE,QAAQC,IAAI,QAAU1B,KAAK8F,QAAQ,EACnC9F,KAAK8D,gBAAgB9D,KAAKtB,KAAK,EAC3BsB,KAAK8F,UAAY9F,KAAK+F,YACtB/F,KAAKK,MAAM,EACXL,KAAKG,OAAO,EACZH,KAAKI,WAAW,MAXxB,CAgBA,GAAoB,MAAfyF,EAAIhC,OACL,GAAgB,gBAAXA,EACDL,EAAa,4DAA6DxD,KAAKtB,KAAM,EACrF+C,QAAQC,IAAK,MAAQmE,EAAIxB,aAAc,cAAe,MACnD,CACH1E,IAAIqG,EAAcnC,EACI,KAAA,IAAVN,IACRyC,GAAe,qBAAuBzC,GAE1CC,EAAawC,EAAahG,KAAKtB,KAAM,CACzC,MACsB,MAAfmH,EAAIhC,OACXL,EAAa,yBAA0BxD,KAAKtB,KAAK,EAEjD8E,EAAY,6BAA+BqC,EAAIhC,OAAS,YAAcA,EAAQ7D,KAAKtB,KAAK,EAGvFsB,KAAKX,aACNW,KAAKX,YAAY4G,QAAQ,MAAM,EAGnCjG,KAAK4B,IAAI,CAvBT,CAwBJ,CAOA4B,YAAa0C,EAAUC,GACnBC,UAAUC,KAAK,EAAEC,SAAUJ,EAAW,cAAgBC,CAAK,CAC/D,CAMAzB,OAAO6B,GACH1G,OAAO,sBAAsB,EAAEE,YAAY,eAAe,EAC1DF,OAAO,mBAAmB,EAAEE,YAAY,eAAe,EACvDF,OAAO,+BAA+B,EAAE2G,QAASD,CAAa,CAClE,CAMA5B,aAAa8B,GACT,IAAM9G,IAAIC,KAAO6G,EACR7G,GACDI,KAAK0E,OACD,4BACA1E,KAAK0G,4BAA4B,EAAI,MACrCD,EAAM7G,GAAK+G,KAAO,MAAQF,EAAM7G,GAAKgH,OACrC,QAAUH,EAAM7G,GAAKiE,OACrB,UAAM,CAGtB,CAMAgB,cAActD,GACwD,KAAA,IAAvD1B,OAAO,kCAAkC,EAAEgH,MAAM,GACS,KAAA,IAA1DhH,OAAO,kCAAkC,EAAEgH,MAAM,EAAE,IAC1DhH,OAAO,kCAAkC,EAAEgH,MAAM,EAAE,GAAGC,cAAgBvF,EAAKwF,aAI/E/G,KAAK0E,OAAQ,iCACT1E,KAAK0G,4BAA4B,EAAY,SAC7CnF,EAAK8D,MAAkB,cAAW9D,EAAKwF,YAAc,aAAc,CAC3E,CAMA9B,wBAAwBC,GACpBrF,OAAO,iCAAiC,EAAEmH,OAAO,EACjDnH,OAAOA,OAAO,yCAAyC,EAAE,EAAE,EACtDoH,MACG,uKAEA/B,EAEA,YACJ,CACR,CAMAwB,8BACI/G,IAAIuH,EAAe,CAAA,EAEnBvH,IACAwH,EAA8C,CAAC,GADpB,IAAI3G,MAAO4G,kBAAkB,EACL,IAAO,GAOtDC,GAFAH,EAHsB,aAAtB,OAAOjG,YAC8B,KAAA,IAA9BA,WAAWqG,gBACY,CAAA,IAA9BrG,WAAWqG,eACI9G,KAAK+G,IAAI,EAAIJ,EAAoD,IAA5BlG,WAAWqG,eAEtDJ,GAAe,IAAI1G,KAAK0G,CAAY,EAAI,IAAI1G,KAErDgH,EAAiB,IAAIC,KAAKC,eAAe,QAAS,CAACC,MAAO,OAAO,CAAC,EAAEC,OACpEC,EAAUC,OAAOT,EAAOU,WAAW,CAAC,EAAEC,SAAS,EAAG,GAAG,EACrDC,EAAUH,OAAOT,EAAOa,WAAW,CAAC,EAAEF,SAAS,EAAG,GAAG,EACzD,OAAOR,EAAeH,CAAM,EAAI,IAC5BA,EAAOc,QAAQ,EAAI,IAAMd,EAAOe,YAAY,EAAI,IAChDf,EAAOgB,SAAS,EAAI,IAAMR,EAAU,IAAMI,CAClD,CACJ"} \ No newline at end of file diff --git a/js/src/spbc-scanner-plugin.js b/js/src/spbc-scanner-plugin.js index e29332984..a3990bf60 100644 --- a/js/src/spbc-scanner-plugin.js +++ b/js/src/spbc-scanner-plugin.js @@ -24,6 +24,7 @@ class SpbcMalwareScanner {/* eslint-disable-line no-unused-vars */ 'auto_cure', 'os_cron_analysis', 'db_trigger_analysis', + 'binary_analysis', 'outbound_links', 'frontend_analysis', 'important_files_listing', @@ -242,6 +243,7 @@ class SpbcMalwareScanner {/* eslint-disable-line no-unused-vars */ case 'frontend_analysis': this.amount = spbcSettings.frontendAnalysisAmount; break; case 'signature_analysis': this.amount = 10; data.status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR'; break; case 'heuristic_analysis': this.amount = 4; data.status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR'; break; + case 'binary_analysis': this.amount = 4; data.status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR'; break; case 'schedule_send_heuristic_suspicious_files': this.amount = 1; break; } diff --git a/lib/CleantalkSP/SpbctWP/Scanner/BinaryCheckModule/BinaryCheckModule.php b/lib/CleantalkSP/SpbctWP/Scanner/BinaryCheckModule/BinaryCheckModule.php new file mode 100644 index 000000000..aa82c0fc2 --- /dev/null +++ b/lib/CleantalkSP/SpbctWP/Scanner/BinaryCheckModule/BinaryCheckModule.php @@ -0,0 +1,169 @@ +total_count = 0; + $this->scanned_count = 0; + $this->statuses = array(); + + $binary_files = $this->getBinaryFiles(); + $this->total_count = count($binary_files); + $this->analyzeBinaryFiles($binary_files); + + return array( + 'success' => true, + 'end' => 1, + 'total_count' => $this->total_count, + 'scanned_count' => $this->scanned_count, + 'statuses' => $this->statuses, + ); + } + + /** + * @return array + */ + public function getBinaryFiles() + { + global $wpdb; + + $binary_files = $wpdb->get_results( + $wpdb->prepare( + "SELECT * FROM " . SPBC_TBL_SCAN_FILES . " WHERE source = %s", + 'BINARY' + ) + ); + + return $binary_files ?: array(); + } + + /** + * @param array $binary_files + * @return void + */ + private function analyzeBinaryFiles($binary_files) + { + foreach ($binary_files as $binary_file) { + $path = realpath(ABSPATH . $binary_file->path); + if (!$path || !file_exists($path)) { + $this->incrementStatus('SKIPPED_NOT_FOUND'); + continue; + } + + if (!is_readable($path)) { + $this->incrementStatus('SKIPPED_NOT_READABLE'); + continue; + } + + $isCritical = $this->analyzeBinaryFile($path); + $this->scanned_count++; + + if ($isCritical) { + $this->markAsCritical($binary_file->path); + $this->incrementStatus('CRITICAL'); + } else { + $this->incrementStatus('OK'); + } + } + } + + /** + * @param string $status + * @return void + */ + private function incrementStatus($status) + { + if (!isset($this->statuses[$status])) { + $this->statuses[$status] = 0; + } + $this->statuses[$status]++; + } + + private function analyzeBinaryFile($binary_file_path) + { + // Read first bytes of the file (we need at least 4 bytes for all checks) + $handle = fopen($binary_file_path, 'rb'); + if (!$handle) { + return false; + } + + // Read first 4 bytes + $first_bytes = fread($handle, 4); + fclose($handle); + + if (strlen($first_bytes) < 4) { + return false; + } + + // Method 1: Check using ord() to get byte values + $byte1 = ord($first_bytes[0]); + $byte2 = ord($first_bytes[1]); + $byte3 = ord($first_bytes[2]); + $byte4 = ord($first_bytes[3]); + + // Check for ELF (0x7F followed by "ELF" = 0x45 0x4C 0x46) + if ($byte1 === 0x7F && $byte2 === 0x45 && $byte3 === 0x4C && $byte4 === 0x46) { + return true; // ELF file detected + } + + // Check for PE/MZ (Windows executable: 0x4D 0x5A = "MZ") + if ($byte1 === 0x4D && $byte2 === 0x5A) { + return true; // PE file detected + } + + // Check for MACHO (macOS/iOS executables) + // 32-bit big-endian: 0xFE 0xED 0xFA 0xCE + // 32-bit little-endian: 0xCE 0xFA 0xED 0xFE + // 64-bit big-endian: 0xFE 0xED 0xFA 0xCF + // 64-bit little-endian: 0xCF 0xFA 0xED 0xFE + $macho_signatures = array( + array(0xFE, 0xED, 0xFA, 0xCE), // 32-bit big-endian + array(0xCE, 0xFA, 0xED, 0xFE), // 32-bit little-endian + array(0xFE, 0xED, 0xFA, 0xCF), // 64-bit big-endian + array(0xCF, 0xFA, 0xED, 0xFE), // 64-bit little-endian + ); + + foreach ($macho_signatures as $signature) { + if ($byte1 === $signature[0] && $byte2 === $signature[1] && + $byte3 === $signature[2] && $byte4 === $signature[3]) { + return true; // MACHO file detected + } + } + + return false; + } + + private function markAsCritical($binary_file_path) + { + global $wpdb; + + $wpdb->query( + $wpdb->prepare( + "UPDATE " . SPBC_TBL_SCAN_FILES . " SET status = 'INFECTED', severity = 'CRITICAL' WHERE path = %s", + $binary_file_path + ) + ); + } +} diff --git a/lib/CleantalkSP/SpbctWP/Scanner/ScannerActions/CloudAnalysisActions.php b/lib/CleantalkSP/SpbctWP/Scanner/ScannerActions/CloudAnalysisActions.php index 7c845fac5..bff4f25e0 100644 --- a/lib/CleantalkSP/SpbctWP/Scanner/ScannerActions/CloudAnalysisActions.php +++ b/lib/CleantalkSP/SpbctWP/Scanner/ScannerActions/CloudAnalysisActions.php @@ -146,7 +146,8 @@ public static function bulkSendFileForAnalysis($fast_hashes_list = array()) $sql_result = $wpdb->get_results( 'SELECT fast_hash FROM ' . SPBC_TBL_SCAN_FILES . ' WHERE last_sent IS NULL - AND status NOT IN ("APPROVED_BY_USER","APPROVED_BY_CT","APPROVED_BY_CLOUD","DENIED_BY_CT")', + AND status NOT IN ("APPROVED_BY_USER","APPROVED_BY_CT","APPROVED_BY_CLOUD","DENIED_BY_CT") + AND source != \'BINARY\'', ARRAY_A ); diff --git a/lib/CleantalkSP/SpbctWP/Scanner/ScannerActions/FileSystemActions.php b/lib/CleantalkSP/SpbctWP/Scanner/ScannerActions/FileSystemActions.php index 4a2d17f52..9d4d30d03 100644 --- a/lib/CleantalkSP/SpbctWP/Scanner/ScannerActions/FileSystemActions.php +++ b/lib/CleantalkSP/SpbctWP/Scanner/ScannerActions/FileSystemActions.php @@ -341,6 +341,23 @@ public static function viewFile($file_id) return array('error' => 'FILE_NOT_READABLE'); } + // Binary files: render as hex dump (first chunk only) + if (isset($file_info['source']) && $file_info['source'] === 'BINARY') { + $file_text = self::renderBinaryHexDump($file_path); + if (empty($file_text)) { + return array('error' => 'FILE_EMPTY'); + } + + return array( + 'success' => true, + 'file' => $file_text, + 'file_path' => $file_path, + 'difference' => $file_info['difference'], + 'weak_spots' => $file_info['weak_spots'], + 'exec_time' => round(microtime(true) - $time_start), + ); + } + $file = file($file_path); if (!$file) { return array('error' => 'FILE_EMPTY'); @@ -365,4 +382,75 @@ public static function viewFile($file_id) 'exec_time' => round(microtime(true) - $time_start), ); } + + /** + * Render the first bytes of a binary file as a classic hex dump. + * Each output line: offset | 16 hex bytes | ASCII gutter. + * + * @param string $file_path + * @return array + */ + private static function renderBinaryHexDump($file_path) + { + $max_bytes = 4096; + $bytes_per_line = 16; + + $handle = @fopen($file_path, 'rb'); + if (!$handle) { + return array(); + } + + $data = @fread($handle, $max_bytes); + $file_size = @filesize($file_path); + @fclose($handle); + + if ($data === false || $data === '') { + return array(); + } + + $len = strlen($data); + $rows = array(); + $line_no = 1; + + for ($offset = 0; $offset < $len; $offset += $bytes_per_line) { + $chunk = substr($data, $offset, $bytes_per_line); + $hex_parts = array(); + $ascii = ''; + $chunk_len = strlen($chunk); + for ($i = 0; $i < $bytes_per_line; $i++) { + if ($i < $chunk_len) { + $byte = ord($chunk[$i]); + $hex_parts[] = sprintf('%02X', $byte); + $ascii .= ($byte >= 0x20 && $byte <= 0x7E) ? chr($byte) : '.'; + } else { + $hex_parts[] = ' '; + $ascii .= ' '; + } + } + $hex_block = implode(' ', array_slice($hex_parts, 0, 8)) + . ' ' + . implode(' ', array_slice($hex_parts, 8, 8)); + + $rows[$line_no] = sprintf( + '%08X %s |%s|', + $offset, + $hex_block, + htmlspecialchars($ascii) + ); + $line_no++; + } + + if (is_int($file_size) && $file_size > $len) { + $rows[$line_no] = sprintf( + '... %s', + sprintf( + esc_html__('truncated, showing first %1$d of %2$d bytes', 'security-malware-firewall'), + $len, + $file_size + ) + ); + } + + return $rows; + } } diff --git a/lib/CleantalkSP/SpbctWP/Scanner/ScannerQueue.php b/lib/CleantalkSP/SpbctWP/Scanner/ScannerQueue.php index 2531c10d4..bff2a3dd3 100644 --- a/lib/CleantalkSP/SpbctWP/Scanner/ScannerQueue.php +++ b/lib/CleantalkSP/SpbctWP/Scanner/ScannerQueue.php @@ -31,6 +31,8 @@ use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\Stages\OutboundLinks; use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\Stages\ScheduleSendHeuristicSuspiciousFiles; use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\Stages\SignatureAnalysis; +use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\Stages\BinaryAnalysis; +use CleantalkSP\SpbctWP\Scanner\BinaryCheckModule\BinaryCheckModule; use CleantalkSP\SpbctWP\Scanner\Stages\CureStage; use CleantalkSP\SpbctWP\Scanner\Stages\SendResultsStage; use CleantalkSP\SpbctWP\Scanner\Stages\SignatureAnalysis\SignatureAnalysisFacade; @@ -55,6 +57,7 @@ class ScannerQueue 'get_approved_hashes', 'signature_analysis', 'heuristic_analysis', + 'binary_analysis', 'schedule_send_heuristic_suspicious_files', 'auto_cure_backup', 'auto_cure', @@ -874,7 +877,7 @@ public function countFilesByStatusAndChecked($status = '', $caller = '') $query = 'SELECT COUNT(fast_hash) AS cnt' . ' FROM ' . SPBC_TBL_SCAN_FILES - . ' WHERE ' . $caller . " = '0' AND status IN (" . $status . ');';// No need to validate or sanitize, already did + . ' WHERE ' . $caller . " = '0' AND status IN (" . $status . ") AND source != 'BINARY'";// No need to validate or sanitize, already did $result = $this->db->fetch($query); return $result !== null @@ -967,7 +970,7 @@ public function file_system_analysis($offset = null, $amount = null, $path_to_sc //should be offset $detected_at = current_time('timestamp'); $sql_hat = 'INSERT INTO ' . SPBC_TBL_SCAN_FILES - . ' (`path`, `size`, `perms`, `mtime`, `fast_hash`, `full_hash`, `detected_at`, `checked_heuristic`) VALUES '; + . ' (`path`, `size`, `perms`, `mtime`, `fast_hash`, `full_hash`, `detected_at`, `checked_heuristic`, `source`) VALUES '; foreach ( $scanner->output_files as $_key => $file ) { // skip restored files as is @@ -988,6 +991,9 @@ public function file_system_analysis($offset = null, $amount = null, $path_to_sc $file['checked_heuristic'] = $ext === 'js' ? 1 : 0; //JS files end + // if extension is not set, set 'source' to 'binary' + $file['source'] = ! $ext ? 'BINARY' : ''; + if ( ! spbc_check_ascii($file['path']) ) { $sql_query__values_non_ascii[] = '(\'' . implode('\',\'', $file) . '\')'; } else { @@ -1453,7 +1459,7 @@ public function signature_analysis($status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR $files = $this->db->fetchAll( 'SELECT path, source_type, source, version, status, checked_heuristic, checked_signatures, fast_hash, real_full_hash, full_hash, weak_spots, difference, severity, size, error_msg' . ' FROM ' . SPBC_TBL_SCAN_FILES - . " WHERE checked_signatures = 0 AND status IN ($status)" + . " WHERE checked_signatures = 0 AND status IN ($status) AND source != 'BINARY'" . " LIMIT 1000" ); @@ -1640,7 +1646,7 @@ public function heuristic_analysis($status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR $files = $this->db->fetchAll( 'SELECT path, source_type, source, version, status, checked_heuristic, checked_signatures, fast_hash, real_full_hash, full_hash, weak_spots, difference, severity, size, error_msg' . ' FROM ' . SPBC_TBL_SCAN_FILES - . " WHERE checked_heuristic = 0 AND status IN ($status)" + . " WHERE checked_heuristic = 0 AND status IN ($status) AND source != 'BINARY'" . " LIMIT 1000" ); @@ -1843,7 +1849,8 @@ public function schedule_send_heuristic_suspicious_files() // phpcs:ignore PSR1. . ' AND checked_heuristic = 1 ' . ' AND weak_spots NOT LIKE "%SIGNATURES%" ' . ' AND status NOT IN ("APPROVED_BY_USER", "APPROVED_BY_CT", "APPROVED_BY_CLOUD")' - . ' AND (pscan_pending_queue IS NULL OR pscan_pending_queue = 0); ' + . ' AND (pscan_pending_queue IS NULL OR pscan_pending_queue = 0) ' + . ' AND source != \'BINARY\'; ' ); $count = (int)$result_db; @@ -1883,6 +1890,54 @@ public function schedule_send_heuristic_suspicious_files() // phpcs:ignore PSR1. ); } + public function binary_analysis() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps + { + $output = array(); + + // Initialize scanning stages storage + $scanning_stages_storage = new ScanningStagesStorage(); + $scanning_stages_storage->converter->loadCollection(); + $stage_data_obj = $scanning_stages_storage->getStage(BinaryAnalysis::class); + + // Run binary check module + $binary_check_module = new BinaryCheckModule(); + $result = $binary_check_module->run(); + + // Update stage data + $stage_data_obj->set('total_count_files_for_analysis', $result['total_count']); + $stage_data_obj->set('count_files_to_check', $result['total_count']); + $stage_data_obj->set('scanned_count_files', $result['scanned_count']); + $stage_data_obj->set('statuses', $result['statuses']); + + // Save stage data to DB + $scanning_stages_storage->saveToDb(); + + // Adding to log + ScanningLogFacade::writeToLog( + '' . $stage_data_obj::getTitle() . ' ' . $stage_data_obj->getDescription() + ); + + // Prepare output + $output['success'] = $result['success']; + $output['end'] = $result['end']; + $output['processed'] = $result['scanned_count']; + $output['total'] = $result['total_count']; + $output['stage_data_for_logging'] = array( + 'title' => $stage_data_obj::getTitle(), + 'description' => $stage_data_obj->getDescription() + ); + + // Accordion interactivity + $critical_count = isset($result['statuses']['CRITICAL']) ? $result['statuses']['CRITICAL'] : 0; + $refresh_data = array( + 'do_refresh' => !empty($critical_count), + 'control_tab' => 'critical', + ); + $output['interactivity_data'] = ScannerInteractivityData::prepare(__FUNCTION__, $refresh_data); + + return $output; + } + public function auto_cure_backup() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps { return spbc_backup__files_with_signatures(true); diff --git a/lib/CleantalkSP/SpbctWP/Scanner/ScanningStagesModule/Stages/BinaryAnalysis.php b/lib/CleantalkSP/SpbctWP/Scanner/ScanningStagesModule/Stages/BinaryAnalysis.php new file mode 100644 index 000000000..67efff864 --- /dev/null +++ b/lib/CleantalkSP/SpbctWP/Scanner/ScanningStagesModule/Stages/BinaryAnalysis.php @@ -0,0 +1,59 @@ +total_count_files_for_analysis + . '; ' + . __('Files to check ', 'security-malware-firewall') + . $this->count_files_to_check + . '; ' + . __('Scanned files ', 'security-malware-firewall') + . $this->scanned_count_files + . '; ' + . __('Statuses ', 'security-malware-firewall') + . $this->getStatusesWithTitle(); + } + + public function getStatusesWithTitle() + { + $description_array = array(); + foreach ($this->statuses as $status => $count) { + $description_array[] = $status . ': ' . $count; + } + return $description_array ? implode('; ', $description_array) . '.' : ''; + } + + public function getName() + { + return __CLASS__; + } + + public function getData() + { + return array( + 'total_count_files_for_analysis' => $this->total_count_files_for_analysis, + 'count_files_to_check' => $this->count_files_to_check, + 'scanned_count_files' => $this->scanned_count_files, + 'statuses' => $this->statuses + ); + } +} diff --git a/lib/CleantalkSP/SpbctWP/Scanner/Services/SendFileToCloudService.php b/lib/CleantalkSP/SpbctWP/Scanner/Services/SendFileToCloudService.php index c431d207d..e738e1407 100644 --- a/lib/CleantalkSP/SpbctWP/Scanner/Services/SendFileToCloudService.php +++ b/lib/CleantalkSP/SpbctWP/Scanner/Services/SendFileToCloudService.php @@ -31,6 +31,11 @@ public static function sendFile($file_id, $do_rescan = true) return array('error' => 'FILE_NOT_FOUND'); } + // Binary files cannot be sent for analysis + if (isset($file_info['source']) && $file_info['source'] === 'BINARY') { + return array('error' => __('Binary files cannot be sent for analysis.', 'security-malware-firewall')); + } + $root_path = spbc_get_root_path(); // if file not exists, remove it from the database diff --git a/lib/CleantalkSP/SpbctWP/Scanner/Stages/CureStage.php b/lib/CleantalkSP/SpbctWP/Scanner/Stages/CureStage.php index a86b5e545..8af2c5b61 100644 --- a/lib/CleantalkSP/SpbctWP/Scanner/Stages/CureStage.php +++ b/lib/CleantalkSP/SpbctWP/Scanner/Stages/CureStage.php @@ -103,11 +103,11 @@ public function runStage($offset, $amount) */ public function getFilesToCure($limit = null) { - // get files with signatures + // get files with signatures (excluding binary files) $files_with_signatures = ' SELECT fast_hash, full_hash, mtime FROM ' . SPBC_TBL_SCAN_FILES . ' - WHERE weak_spots LIKE "%SIGNATURES%" + WHERE weak_spots LIKE "%SIGNATURES%" AND source != \'BINARY\' '; $files_with_signatures = $this->db->fetchAll($files_with_signatures, OBJECT_K); diff --git a/lib/CleantalkSP/SpbctWP/Scanner/Surface.php b/lib/CleantalkSP/SpbctWP/Scanner/Surface.php index 441e59b44..2c2bc7546 100644 --- a/lib/CleantalkSP/SpbctWP/Scanner/Surface.php +++ b/lib/CleantalkSP/SpbctWP/Scanner/Surface.php @@ -531,6 +531,11 @@ public function countFilesInDir($main_path) $filename = $fileInfo->getFileName(); $currentFileExtension = pathinfo($filename, PATHINFO_EXTENSION); + if ($currentFileExtension === '') { + $this->output_files_count++; + continue; + } + // Extensions filter if ( $this->ext_except || $this->ext ) { if ( @@ -623,6 +628,14 @@ public function getFileStructure($main_path, $iterator_result, $is_root_dir) $path = (string) $path; if ( is_file($path) && !is_link($path) ) { + $currentFileExtension = pathinfo($path, PATHINFO_EXTENSION); + + if ($currentFileExtension === '') { + $this->output_counter_files++; + $this->output_files[]['path'] = $path; + continue; + } + // Extensions filter if ( $this->ext_except || $this->ext ) { $currentFileExtension = pathinfo($path, PATHINFO_EXTENSION); diff --git a/lib/CleantalkSP/SpbctWP/SpbcEnqueue.php b/lib/CleantalkSP/SpbctWP/SpbcEnqueue.php index 08cbf1895..18fab5183 100644 --- a/lib/CleantalkSP/SpbctWP/SpbcEnqueue.php +++ b/lib/CleantalkSP/SpbctWP/SpbcEnqueue.php @@ -294,6 +294,7 @@ function ($key) { 'check_links' => $spbc->settings['scanner__outbound_links'] ? 1 : 0, 'check_heuristic' => $spbc->settings['scanner__heuristic_analysis'] ? 1 : 0, 'check_signature' => $spbc->settings['scanner__signature_analysis'] ? 1 : 0, + 'check_binary' => $spbc->settings['scanner__binary_analysis'] ? 1 : 0, 'auto_cure' => $spbc->settings['scanner__auto_cure'] ? 1 : 0, 'check_frontend' => $spbc->settings['scanner__frontend_analysis'] ? 1 : 0, 'check_listing' => $spbc->settings['scanner__important_files_listing'] ? 1 : 0, diff --git a/lib/CleantalkSP/SpbctWP/State.php b/lib/CleantalkSP/SpbctWP/State.php index fbe4a513e..a92167320 100644 --- a/lib/CleantalkSP/SpbctWP/State.php +++ b/lib/CleantalkSP/SpbctWP/State.php @@ -74,6 +74,7 @@ class State extends \CleantalkSP\Common\State 'scanner__outbound_links_mirrors' => '', 'scanner__important_files_listing' => 1, 'scanner__heuristic_analysis' => 1, + 'scanner__binary_analysis' => 1, 'scanner__schedule_send_heuristic_suspicious_files' => 2, //0 - OFF, 1 - ON, 2 - AUTO 'scanner__signature_analysis' => 1, 'scanner__auto_cure' => 1,