diff --git a/inc/spbc-admin.php b/inc/spbc-admin.php index 51e56fdca..49eaec414 100644 --- a/inc/spbc-admin.php +++ b/inc/spbc-admin.php @@ -4,6 +4,7 @@ use CleantalkSP\SpbctWP\CleantalkSettingsTemplates; use CleantalkSP\SpbctWP\Cron; use CleantalkSP\SpbctWP\Escape; +use CleantalkSP\SpbctWP\Scanner\DBTrigger\DBTriggerService; use CleantalkSP\SpbctWP\VulnerabilityAlarm\Dto\PluginReport; use CleantalkSP\SpbctWP\VulnerabilityAlarm\Dto\ThemeReport; use CleantalkSP\SpbctWP\VulnerabilityAlarm\VulnerabilityAlarmService; @@ -472,275 +473,6 @@ function changedPluginName(){ return $meta; } -/** - * Register stylesheet and scripts. - * @psalm-suppress InvalidArgument, UnusedParam - */ -function spbc_enqueue_scripts($hook) -{ - // If the user is not admin - if ( ! current_user_can('upload_files')) { - return; - } - - global $spbc; - - // For ALL admin pages - SpbcEnqueue::getInstance()->css('spbc-admin.css'); - SpbcEnqueue::getInstance()->css('spbc-icons.css'); - SpbcEnqueue::getInstance()->js('spbc-common.js', array('jquery')); - SpbcEnqueue::getInstance()->js('spbc-admin.js', array('jquery')); - SpbcEnqueue::getInstance()->js('spbc-react-bundle.js', array('wp-i18n'), ['in_footer']); - wp_set_script_translations('spbc-react-bundle-js', 'security-malware-firewall'); - - $vulnerability_show_install = ( - isset($spbc->settings['vulnerability_check__test_before_install']) && - $spbc->settings['vulnerability_check__test_before_install'] == true - ); - - $vulnerability_show_list = ( - isset($spbc->settings['vulnerability_check__enable_cron'], $spbc->settings['vulnerability_check__warn_on_modules_pages']) && - $spbc->settings['vulnerability_check__enable_cron'] == true && - $spbc->settings['vulnerability_check__warn_on_modules_pages'] == true - ); - wp_localize_script('spbc-common-js', 'spbcSettings', array( - 'wpms' => (int) is_multisite(), - 'is_main_site' => (int) is_main_site(), - 'img_path' => SPBC_PATH . '/images', - 'key_is_ok' => $spbc->key_is_ok, - 'critical' => $spbc->data['display_scanner_warnings']['critical'], - 'secfw_enabled' => $spbc->settings['secfw__enabled'], - 'ajax_nonce' => wp_create_nonce("spbc_secret_nonce"), - 'ajaxurl' => admin_url('admin-ajax.php', 'relative'), - //'debug' => !empty($debug) ? 1 : 0, - 'key_changed' => ! empty($spbc->data['key_changed']), - 'admin_bar__admins_online_counter' => $spbc->settings['admin_bar__admins_online_counter'] ? 1 : 0, - 'needToWhitelist' => ! Cookie::getString('spbc_secfw_ip_wl'), - 'frontendAnalysisAmount' => (defined('SPBCT_ALLOW_CURL_SINGLE') && SPBCT_ALLOW_CURL_SINGLE) ? 2 : 20, - 'spbctNoticeDismissSuccess' => \CleantalkSP\SpbctWP\AdminBannersModule\AdminBannersHandler::getJSONOfPostNotices(), - 'vulnerabilityShowInstall' => $vulnerability_show_install, - 'vulnerabilityShowList' => $vulnerability_show_list, - 'spbcSpinner' => array ( - 'imgSource' => SPBC_PATH . '/images/preloader2.gif', - 'altText' => esc_html__('Loading...', 'security-malware-firewall') - ), - 'wl_mode_enabled' => $spbc->data['wl_mode_enabled'], - 'wl_company_name' => $spbc->data['wl_company_name'], - 'wl_support_url' => $spbc->data['wl_support_url'], - 'default_wl_support_url' => $spbc->default_data['wl_support_url'], - )); - - SpbcEnqueue::getInstance()->js('spbc-cookie.js', array('jquery')); - - wp_localize_script( - 'spbc-cookie-js', - 'spbcPublic', - array ( - '_ajax_nonce' => wp_create_nonce('ct_secret_stuff'), - '_rest_nonce' => wp_create_nonce('wp_rest'), - '_ajax_url' => admin_url('admin-ajax.php', 'relative'), - '_rest_url' => esc_url(get_rest_url()), - // '_apbct_ajax_url' => APBCT_URL_PATH . '/lib/Cleantalk/ApbctWP/Ajax.php', - 'data__set_cookies' => $spbc->settings['data__set_cookies'], - 'data__set_cookies__alt_sessions_type' => $spbc->settings['data__set_cookies__alt_sessions_type'], - 'no_confirm_row_actions' => spbc_get_no_confirm_row_actions(), - ) - ); - - if ($spbc->settings['upload_checker__file_check'] && in_array($hook, array('upload.php', 'media-new.php'))) { - SpbcEnqueue::getInstance()->js('spbc-upload.js', array('jquery')); - } - - // Load UI (modal window) for profile pages - if ($hook === 'profile.php' || $hook === 'user-edit.php') { - SpbcEnqueue::getInstance()->custom( - 'jquery-ui', - SPBC_PATH . '/css/jquery-ui.min.css', - array(), - '1.12.1', - null, - 'all' - ); - wp_enqueue_script('jquery-ui-dialog'); - } - - // For settings page - if ($hook === 'settings_page_spbc') { - $button_template = ''; - - $button_template_send = sprintf($button_template, '', 'send', __('Send for analysys', 'security-malware-firewall')); - $button_template_delete = sprintf($button_template, '', 'delete', __('Delete', 'security-malware-firewall')); - $button_template_approve = sprintf($button_template, '', 'approve', __('Approve', 'security-malware-firewall')); - $button_template_view = sprintf($button_template, '', 'view', __('View', 'security-malware-firewall')); - $button_template_view_bad = sprintf($button_template, '', 'view_bad', __('View suspicious code', 'security-malware-firewall')); - $button_template_replace = sprintf($button_template, '', 'replace', __('Replace with original', 'security-malware-firewall')); - $button_template_compare = sprintf($button_template, '', 'compare', __('Show difference', 'security-malware-firewall')); - $actions_unknown = $button_template_send . $button_template_delete . $button_template_approve . $button_template_view; - $actions_modified = $button_template_approve . $button_template_replace . $button_template_compare . $button_template_view_bad; - - // CSS - SpbcEnqueue::getInstance()->css('spbc-settings.css'); - SpbcEnqueue::getInstance()->css('spbc-settings-media.css'); - SpbcEnqueue::getInstance()->css('spbc-table.css'); - wp_deregister_style('jquery-ui-style'); - SpbcEnqueue::getInstance()->custom( - 'jquery-ui', - SPBC_PATH . '/css/jquery-ui.min.css', - array(), - '1.12.1', - null, - 'all' - ); - // JS - SpbcEnqueue::getInstance()->custom( - 'jquery-ui', - SPBC_PATH . '/js/jquery-ui.min.js', - array('jquery'), - '1.13.1', - true, - null - ); - SpbcEnqueue::getInstance()->js('spbc-settings.js', array('jquery'), true); - SpbcEnqueue::getInstance()->js('spbc-table.js', array('jquery'), true); - SpbcEnqueue::getInstance()->js('spbc-scanner-plugin.js', array('jquery'), true); - - wp_localize_script('spbc-table-js', 'spbcTableLocalize', array( - 'scannerIsActive' => esc_html__('Scanner is active for now. Try later.', 'security-malware-firewall'), - )); - - SpbcEnqueue::getInstance()->js('spbc-modal.js', array('jquery'), true); - - wp_localize_script('spbc-settings-js', 'spbcSettingsSecLogs', array( - 'amount' => SPBC_LAST_ACTIONS_TO_VIEW, - 'clicks' => 0, - )); - - wp_localize_script('spbc-settings-js', 'spbcSettingsFWLogs', array( - 'moderate' => $spbc->moderate ? 1 : 0, - 'amount' => SPBC_LAST_ACTIONS_TO_VIEW, - 'clicks' => 0, - )); - - wp_localize_script('spbc-settings-js', 'spbcTable', array( - 'warning_bulk' => __('Are sure you want to perform these actions?', 'security-malware-firewall'), - - 'warning_default' => __('Do you want to proceed?', 'security-malware-firewall'), - - 'warning_h_approve' => __('Do you want to approve this file?', 'security-malware-firewall'), - 'warning_t_approve' => __('If you agree, this file will be approved.', 'security-malware-firewall'), - - 'warning_h_send' => __('Do you want to proceed?', 'security-malware-firewall'), - 'warning_t_send' => __('This file will be sent to the Cloud to analyze for a malware, usually processing takes up to 1 minute. The result will be shown in the Analysis log.', 'security-malware-firewall'), - - 'warning_h_delete' => __('This can\'t be undone and could damage your website. Are you sure?', 'security-malware-firewall'), - 'warning_t_delete' => __('If you agree, this file will be deleted.', 'security-malware-firewall'), - - 'warning_h_replace' => __('This can\'t be undone. Are you sure?', 'security-malware-firewall'), - 'warning_t_replace' => __('If you agree, this file will be replaced.', 'security-malware-firewall'), - - 'warning_h_quarantine' => __('This could damage your website, but you will have an option to restore the file.', 'security-malware-firewall'), - 'warning_t_quarantine' => __('If you agree, this file will be quarantined.', 'security-malware-firewall'), - )); - - // Getting scanner settings - $scanner_settings = array_filter( - (array) $spbc->settings, - function ($key) { - return strpos($key, 'scanner') === 0; - }, - ARRAY_FILTER_USE_KEY - ); - - wp_localize_script('spbc-settings-js', 'spbcScaner', array( - - // PARAMS - 'settings' => $scanner_settings, - 'states' => ScannerQueue::$stages, - 'timezone_shift' => $spbc->data['site_utc_offset_in_seconds'] ?: false, - - // Settings / Statuses - 'scaner_enabled' => $spbc->scaner_enabled ? 1 : 0, - '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, - '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, - 'wp_content_dir' => realpath(WP_CONTENT_DIR), - 'wp_root_dir' => realpath(ABSPATH), - - // Templates - 'row_template' => '%s%s%s%s%s', - 'row_template_links' => '%s%s%s', - 'actions_unknown' => $actions_unknown, - 'actions_modified' => $actions_modified, - 'page_selector_template' => '', - - //TRANSLATIONS - - //Confirmation - 'scan_modified_confiramation' => __('There is more than 30 modified files and this could take time. Do you want to proceed?', 'security-malware-firewall'), - 'warning_about_cancel' => __('Scan will be performed in the background mode soon.', 'security-malware-firewall'), - 'delete_warning' => __('Are you sure you want to delete the file? It can not be undone.'), - // Buttons - 'button_scan_perform' => __('Perform Scan', 'security-malware-firewall'), - 'button_scan_pause' => __('Pause scan', 'security-malware-firewall'), - 'button_scan_resume' => __('Resume scan', 'security-malware-firewall'), - // Progress bar - 'progressbar_get_cms_hashes' => __('Receiving hashes', 'security-malware-firewall'), - 'progressbar_get_modules_hashes' => __('Receiving plugins hashes', 'security-malware-firewall'), - 'progressbar_get_approved_hashes' => __('Updating statuses for the approved files', 'security-malware-firewall'), - 'progressbar_get_denied_hashes' => __('Updating statuses for the denied files', 'security-malware-firewall'), - 'progressbar_clean_results' => __('Preparing', 'security-malware-firewall'), - // Scanning core - 'progressbar_file_system_analysis' => __('Scanning for modifications', 'security-malware-firewall'), - 'progressbar_heuristic_analysis' => __('Heuristic analysis', 'security-malware-firewall'), - 'progressbar_schedule_send_heuristic_suspicious_files' => __('Schedule suspicious files to be automatically sent for analysis', 'security-malware-firewall'), - 'progressbar_signature_analysis' => __('Searching for signatures', 'security-malware-firewall'), - //Cure - 'progressbar_auto_cure_backup' => __('Creating a backup', 'security-malware-firewall'), - 'progressbar_auto_cure' => __('Cure', 'security-malware-firewall'), - // Links - 'progressbar_outbound_links' => __('Scanning links', 'security-malware-firewall'), - // Frontend - 'progressbar_frontend_analysis' => __('Scanning pages', 'security-malware-firewall'), - // Other - 'progressbar_important_files_listing' => __('Check pages for listing', 'security-malware-firewall'), - 'progressbar_send_results' => __('Sending results', 'security-malware-firewall'), - // Warnings - 'result_text_bad_template' => __('Recommend to scan all (%s) of the found files to make sure the website is secure.', 'security-malware-firewall'), - 'result_text_good_template' => __('No threats are found.', 'security-malware-firewall'), - //Misc - 'look_below_for_scan_res' => __('Look below for scan results.', 'security-malware-firewall'), - 'view_all_results' => sprintf( - __('
%sView all scan results for this website%s', 'security-malware-firewall'), - '', - '' - ), - 'last_scan_was_just_now' => __('The last scan of this website happened just now. Files scanned: %s.', 'security-malware-firewall'), - 'last_scan_was_just_now_links' => __('The last scan of this website happened just now. Files scanned: %s. Outbound links found: %s.', 'security-malware-firewall'), - 'copy_log_to_clipboard_hint' => __('Copied!', 'security-malware-firewall'), - 'copy_log_to_clipboard_hint_failed' => __('Failed to copy!', 'security-malware-firewall'), - 'copy_log_to_clipboard_hint_unsupported' => __('Clipboard API not supported in local environment', 'security-malware-firewall'), - )); - - wp_localize_script('spbc-settings-js', 'spbcDescriptions', array( - 'waf__enabled' => __('Bla bla', 'security-malware-firewall'), - 'waf__xss_check' => __('Cross-Site Scripting (XSS) — prevents malicious code to be executed/sent to any user. As a result malicious scripts can not get access to the cookie files, session tokens and any other confidential information browsers use and store. Such scripts can even overwrite content of HTML pages. CleanTalk WAF monitors for patterns of these parameters and block them.', 'security-malware-firewall'), - 'waf__sql_check' => __('SQL Injection — one of the most popular ways to hack websites and programs that work with databases. It is based on injection of a custom SQL code into database queries. It could transmit data through GET, POST requests or cookie files in an SQL code. If a website is vulnerable and execute such injections then it would allow attackers to apply changes to the website\'s MySQL database.', 'security-malware-firewall'), - 'upload_checker__file_check' => __('The option checks each uploaded file to a website for malicious code. If it\'s possible for visitors to upload files to a website, for instance a work resume, then attackers could abuse it and upload an infected file to execute it later and get access to your website.', 'security-malware-firewall'), - 'traffic_control__enabled' => __('It analyzes quantity of requests towards website from any IP address for a certain period of time. For example, for an ordinary visitor it\'s impossible to generate 2000 requests within 1 hour. Big amount of requests towards website from the same IP address indicates that there is a high chance of presence of a malicious program.', 'security-malware-firewall'), - 'scanner__outbound_links' => __('This option allows you to know the number of outgoing links on your website and website addresses they lead to. The option\'s purpose is to check your website and find hidden, forgotten and spam links. You should always remember if you have links to other websites which have a bad reputation, it could affect your visitors\' trust and your SEO.', 'security-malware-firewall'), - 'scanner__heuristic_analysis' => __('Often, authors of malicious code disguise their code which makes it difficult to identify it by their signatures. The malicious code itself can be placed anywhere on the site, for example the obfuscated PHP-code in the "logo.png" file, and the code itself is called by one inconspicuous line in "index.php". Therefore, the usage of plugins to search for malicious code is preferable. Heuristic analysis can indicate suspicious PHP constructions in a file that you should pay attention to.', 'security-malware-firewall'), - 'scanner__signature_analysis' => __('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__auto_cure' => __('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'), - 'misc__backend_logs_enable' => __('To control appearing errors you have to check log file of your hosting account regularly. It\'s inconvenient and just a few webmasters pay attention to it. Also, errors could appear for a short period of time and only when one specific function is running, they can\'t be spotted in other circumstances so it\'s hard to catch them. PHP errors tell you that some of your website functionality doesn\'t work correctly, furthermore hackers may use these errors to get access to your website. The CleanTalk Scanner will check your website backend once per hour. Statistics of errors is available in your CleanTalk Dashboard.', 'security-malware-firewall'), - 'data__set_cookies' => __('Part of the CleanTalk FireWall functions depend on cookie files, so disabling this option could lead to deceleration of the firewall work. It will affect user identification who are logged in right now. Traffic Control will not be able to determine authorized users and they could be blocked when the request limit is reached. We do not recommend to disable this option without serious reasons. However, you should disable this option is you\'re using Varnish.', 'security-malware-firewall'), - '2fa__enable' => __('Two-Factor Authentication for WordPress admin accounts will improve your website security and make it safer, if not impossible, for hackers to breach your WordPress account. Two-Factor Authentication works via e-mail. Authentication code will be sent to your admin email. When authorizing, a one-time code will be sent to your email. While entering the code, make sure that it does not contain spaces. With your first authorization, the CleanTalk Security plugin remembers your browser and you won’t have to input your authorization code every time anymore. However, if you started to use a new device or a new browser then you are required to input your authorization code. The plugin will remember your browser for 30 days.', 'security-malware-firewall'), - )); - } -} - function spbc_admin_add_script_attribute($tag, $handle) { $async_scripts = array( @@ -1160,14 +892,17 @@ function spbc_set_malware_scan_warns() $oscron_has_dangerous = OsCronTasksStorage::getCountOfDangerousTasks(); + $triggers_has_dangerous = DBTriggerService::countTriggersStorage(); + $spbc->data['display_scanner_warnings'] = array( 'critical' => $critical_count, 'signatures' => $signatures_count, 'frontend' => $frontend_count, 'analysis' => $analysis_has_dangerous, 'oscron' => $oscron_has_dangerous, - 'analysis_all_safe' => !$analysis_has_uncheked && !$analysis_has_dangerous && !$oscron_has_dangerous, - 'warn_on_admin_bar' => $critical_count || $frontend_count || $analysis_has_dangerous || $oscron_has_dangerous, + 'db_triggers' => $triggers_has_dangerous, + 'analysis_all_safe' => !$analysis_has_uncheked && !$analysis_has_dangerous && !$oscron_has_dangerous && !$triggers_has_dangerous, + 'warn_on_admin_bar' => $critical_count || $frontend_count || $analysis_has_dangerous || $oscron_has_dangerous || $triggers_has_dangerous, ); $spbc->notice_critical_files_warning = !empty($critical_count); $spbc->save('data'); diff --git a/inc/spbc-settings.php b/inc/spbc-settings.php index 017ee5e5a..7624bd14b 100644 --- a/inc/spbc-settings.php +++ b/inc/spbc-settings.php @@ -10,6 +10,7 @@ use CleantalkSP\SpbctWP\LinkConstructor; use CleantalkSP\SpbctWP\ListTable; use CleantalkSP\SpbctWP\Scanner; +use CleantalkSP\SpbctWP\Scanner\DBTrigger\DBTriggerView; use CleantalkSP\SpbctWP\Scanner\OSCron\Storages\OsCronTasksStorage; use CleantalkSP\SpbctWP\Scanner\OSCron\View\OSCronView; use CleantalkSP\SpbctWP\Scanner\ScanningLog\ScanningLogFacade; @@ -18,6 +19,7 @@ use CleantalkSP\SpbctWP\VulnerabilityAlarm\VulnerabilityAlarmView; use CleantalkSP\Variables\Post; use CleantalkSP\Variables\Server; +use CleantalkSP\SpbctWP\Scanner\DBTrigger\DBTriggerService; // Scanner AJAX actions require_once(SPBC_PLUGIN_DIR . 'inc/spbc-scanner.php'); @@ -689,6 +691,12 @@ function spbc_settings__register() 'description' => Scanner\OSCron\View\OSCronLocale::getInstance()->settings__option_description, 'long_description' => false, ), + 'scanner__db_trigger_analysis' => array( + 'type' => 'field', + 'title' => __('DB Trigger analysis', 'security-malware-firewall'), + 'description' => __('Will search for known malicious signatures in database triggers.', 'security-malware-firewall'), + 'long_description' => false, + ), 'scanner__dir_exclusions_view' => array( 'type' => 'field', 'input_type' => 'textarea', @@ -1350,7 +1358,7 @@ function spbc_settings__draw_elements($elems_to_draw = null, $direct_call = fals echo ''; break; case 'section_banner': - spbc_settings__create_notice_on_tab('found_critical_files'); + spbc_settings__create_notice_on_tab(); break; case 'field': call_user_func($elem['callback'], $elem); @@ -2607,52 +2615,49 @@ function spbc_field_traffic_control_logs__prepare_data(&$table) /** * Creates a banner with a notification - * @param string $flag_text_banner which banner text to show * @return void */ -function spbc_settings__create_notice_on_tab($flag_text_banner) +function spbc_settings__create_notice_on_tab() { global $spbc; - switch ($flag_text_banner) { - case 'found_critical_files': // Creates a banner with a notification if important files are found and if the banner has not already been closed - if ($spbc->data['display_scanner_warnings']['critical'] > 0 && - Cookie::getString('spbct_notice-found_critical_files') != '1') { - $email = spbc_get_admin_email(); - $website = get_home_url(); - $text = __("There's a high probability that your website has been compromised, as critical files show signs of infection. Take action now by ordering malware removal from our experienced security specialists.", 'security-malware-firewall'); - //generate button - $landing_page_link = LinkConstructor::buildCleanTalkLink( - 'banner_link_for_treatment', - 'website-malware-removal', - array( - 'email' => esc_attr($email), - 'website' => esc_attr($website), - ), - $domain = 'https://l.cleantalk.org' - ); - $button_text = __('Request Malware removal', 'security-malware-firewall'); - $button_div = '
'; - $button_div .= ' - ' - . '  ' - . $button_text - . ' - '; - $button_div .= '
'; - $text .= $button_div; - } else { - return; - } + $flag_text_banner = ''; - break; + if ($spbc->data['display_scanner_warnings']['critical'] > 0 && + Cookie::getString('spbct_notice-found_critical_files') != '1') { + $flag_text_banner = 'found_critical_files'; + $text = __("There's a high probability that your website has been compromised, as critical files show signs of infection. Take action now by ordering malware removal from our experienced security specialists.", 'security-malware-firewall'); + } - default: - $text = false; - break; + if ( $spbc->data['display_scanner_warnings']['db_triggers'] > 0 && + Cookie::getString('spbct_notice-found_db_triggers') != '1') { + $flag_text_banner = 'found_db_triggers'; + $text = DBTriggerView::getWarningTextForMalwareRemovalBanner(); } - if ($text != false) { + if (!empty($text)) { + $email = spbc_get_admin_email(); + $website = get_home_url(); + $button_text = __('Request Malware removal', 'security-malware-firewall'); + $landing_page_link = LinkConstructor::buildCleanTalkLink( + 'banner_link_for_treatment', + 'website-malware-removal', + array( + 'email' => esc_attr($email), + 'website' => esc_attr($website), + ), + $domain = 'https://l.cleantalk.org' + ); + $button_div = '
'; + $button_div .= ' + ' + . '  ' + . $button_text + . ' + '; + $button_div .= '
'; + $text .= $button_div; + $template = '
@@ -2665,8 +2670,6 @@ function spbc_settings__create_notice_on_tab($flag_text_banner)
'; printf($template, $flag_text_banner, $flag_text_banner, $text); - } else { - return; } } @@ -3090,6 +3093,33 @@ function spbc_scanner_oscron_prepare_data(&$table) $table = OSCronView::prepareTableData($table); } +/** + * Settings function wrapper. Get count found in db trigger. + * @return int + */ +function spbc_scanner_db_trigger_count_found() +{ + return DBTriggerService::countTriggersStorage(); +} + +/** + * Settings function wrapper. Get data for db trigger. + * @return array + */ +function spbc_scanner_db_trigger_get_scanned() +{ + return DBTriggerService::loadTriggersStorage(); +} + +/** + * Settings function wrapper. Modify data in triggers table. + * @param $table + */ +function spbc_scanner_db_trigger_prepare_data(&$table) +{ + $table = DBTriggerView::prepareTableData($table); +} + function spbc_field_scanner__prepare_data__files_quarantine(&$table) { global $spbc; @@ -3437,6 +3467,10 @@ function spbc_field_scanner() echo '' . __('OS Cron Analysis', 'security-malware-firewall') . ' -> '; } + if ($spbc->settings['scanner__db_trigger_analysis']) { + echo '' . __('DB Trigger Analysis', 'security-malware-firewall') . ' -> '; + } + echo '' . __('Updating statuses for the denied files', 'security-malware-firewall') . ' -> ' . '' . __('Updating statuses for the approved files', 'security-malware-firewall') . ' -> '; @@ -3678,6 +3712,7 @@ function spbc_field_scanner__show_accordion($direct_call = false) 'quarantined' => __('Punished files.', 'security-malware-firewall'), 'analysis_log' => $analysis_log_description, 'cure_log' => $cure_log_description, + 'db_trigger' => DBTriggerView::getScannerTabDescription(), 'skipped' => __('List of files that were not checked by the scanner.', 'security-malware-firewall'), ); @@ -3750,6 +3785,13 @@ function spbc_field_scanner__show_accordion($direct_call = false) ), 'display' => (bool) $spbc->settings['scanner__os_cron_analysis'] ), + 'db_trigger_analysis' => array( + 'category_description' => __('DB Trigger Analysis', 'security-malware-firewall'), + 'types' => array( + 'db_trigger', + ), + 'display' => (bool) $spbc->settings['scanner__db_trigger_analysis'] + ), 'pages' => array( 'category_description' => __('Pages scan results', 'security-malware-firewall'), 'types' => array( @@ -3793,6 +3835,7 @@ function spbc_field_scanner__show_accordion($direct_call = false) || ($type_name === 'frontend_malware' && $spbc->data['display_scanner_warnings']['frontend']) || ($type_name === 'analysis_log' && $spbc->data['display_scanner_warnings']['analysis']) || ($type_name === 'oscron' && $spbc->data['display_scanner_warnings']['oscron']) + || ($type_name === 'db_trigger' && $spbc->data['display_scanner_warnings']['db_triggers']) ) { $danger_dot = ''; } @@ -4243,6 +4286,30 @@ function spbc_list_table__get_args_by_type($table_type) ); break; + case 'db_trigger': + $args = array( + 'func_data_total' => 'spbc_scanner_db_trigger_count_found', + 'func_data_get' => 'spbc_scanner_db_trigger_get_scanned', + 'func_data_prepare' => 'spbc_scanner_db_trigger_prepare_data', + 'if_empty_items' => '
' + . __('DB Trigger not found in the server environment or is unavailable to read/write.', 'security-malware-firewall') + . '
', + 'columns' => array( + 'cb' => array('heading' => '', 'class' => 'check-column', 'width_percent' => 2), + 'about_trigger' => array( 'heading' => 'About trigger', 'width_percent' => 30 ), // name, table, time, action + 'code' => array( 'heading' => 'Code', 'width_percent' => 43 ), + 'signature' => array( 'heading' => 'Signature', 'width_percent' => 10 ), + 'analysis_status' => array( 'heading' => 'Verdict', 'width_percent' => 15 ), + ), + 'actions' => array( + 'delete' => array('name' => 'Delete',), + ), + 'bulk_actions' => array( + 'delete' => array('name' => 'Delete',), + ), + ); + break; + case 'approved': $args = array_replace_recursive( $accordion_default_args, diff --git a/js/spbc-scanner-plugin.min.js b/js/spbc-scanner-plugin.min.js index 4c672c6a1..363897337 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","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 console.log("init"),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")),jQuery("#spbc_scanner_clear").length||jQuery('
').insertBefore("#spbcscan-scanner-caption"))}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","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 console.log("init"),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")),jQuery("#spbc_scanner_clear").length||jQuery('
').insertBefore("#spbcscan-scanner-caption"))}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}} //# 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 6ea56f56e..b0480d9a8 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 '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 console.log('init');\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 if (!jQuery('#spbc_scanner_clear').length) {\n let clearLink = '

Clear scanner logs


';\n jQuery(clearLink).insertBefore('#spbcscan-scanner-caption');\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","console","log","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","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","insertBefore","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 '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 console.log('init');\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 if (!jQuery('#spbc_scanner_clear').length) {\n let clearLink = '

Clear scanner logs


';\n jQuery(clearLink).insertBefore('#spbcscan-scanner-caption');\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","console","log","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","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","insertBefore","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 diff --git a/js/src/spbc-scanner-plugin.js b/js/src/spbc-scanner-plugin.js index 4a3145e9b..f0ec25af6 100644 --- a/js/src/spbc-scanner-plugin.js +++ b/js/src/spbc-scanner-plugin.js @@ -23,6 +23,7 @@ class SpbcMalwareScanner {/* eslint-disable-line no-unused-vars */ 'auto_cure_backup', 'auto_cure', 'os_cron_analysis', + 'db_trigger_analysis', 'outbound_links', 'frontend_analysis', 'important_files_listing', diff --git a/lib/CleantalkSP/SpbctWP/ListTable.php b/lib/CleantalkSP/SpbctWP/ListTable.php index 9053f8654..7e7e79975 100644 --- a/lib/CleantalkSP/SpbctWP/ListTable.php +++ b/lib/CleantalkSP/SpbctWP/ListTable.php @@ -3,6 +3,7 @@ namespace CleantalkSP\SpbctWP; use CleantalkSP\SpbctWP\Scanner\Cure; +use CleantalkSP\SpbctWP\Scanner\DBTrigger\DBTriggerService; use CleantalkSP\SpbctWP\Scanner\OSCron\OSCronController; use CleantalkSP\SpbctWP\Scanner\OSCron\View\OSCronLocale; use CleantalkSP\Variables\Post; @@ -652,7 +653,10 @@ public static function ajaxRowActionHandler() $action = Post::getString('add_action'); $is_frontend_malware_action = $action === 'disapprove_page' || $action === 'approve_page'; - if ( $action !== 'restore' && ! $is_frontend_malware_action && strpos($action, 'oscron_task') === false ) { + if ( $action !== 'restore' && ! $is_frontend_malware_action && + strpos($action, 'oscron_task') === false && + strpos($action, 'delete-trigger') === false + ) { $check_file_exist_result = self::spbcCheckFileExist(); if (isset($check_file_exist_result['error'])) { @@ -713,6 +717,9 @@ public static function ajaxRowActionHandler() case 'enable_oscron_task': self::ajaxRowActionHandlerApproveOSCronTask(); break; + case 'delete-trigger': + self::ajaxRowActionHandlerDeleteTrigger(); + break; default: wp_send_json(array('temp_html' => '')); } @@ -740,6 +747,22 @@ public static function ajaxRowActionHandlerApproveOSCronTask() } } + public static function ajaxRowActionHandlerDeleteTrigger() + { + global $spbc; + $result = DBTriggerService::deleteTrigger(Post::get('id', null, 'word')); + if ($result) { + $out = array( + 'html' => '', + ); + wp_send_json($out); + } else { + wp_send_json_error(esc_html((string)$result)); + } + } + public static function ajaxRowActionHandlerDisableOSCronTask() { global $spbc; diff --git a/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerModel.php b/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerModel.php new file mode 100644 index 000000000..b1945191c --- /dev/null +++ b/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerModel.php @@ -0,0 +1,125 @@ +getDataBaseTriggers(); + $db_triggers = $this->removeSentTriggers($db_triggers); + + $signatures = $this->getSignaturesForTriggers(); + + $bad_triggers = $this->analyzeDBTriggers($db_triggers, $signatures); + + DBTriggerService::saveTriggersStorage($bad_triggers); + } catch (\Exception $error) { + return $error->getMessage(); + } + + return true; + } + + /** + * Remove the triggers that have already been sent. + * + * @param array $db_triggers The list of triggers. + * @return array The list of triggers without the sent ones. + */ + private function removeSentTriggers($db_triggers) + { + $scanned_triggers = DBTriggerService::loadTriggersStorage(); + $scanned_triggers = array_map(function ($trigger) { + return $trigger['name']; + }, $scanned_triggers); + $db_triggers = array_filter($db_triggers, function ($trigger) use ($scanned_triggers) { + if (in_array($trigger->TRIGGER_NAME, $scanned_triggers)) { + return false; + } + return true; + }); + return $db_triggers; + } + + /** + * Analyze the triggers and return the bad ones. + * + * @param array $db_triggers The list of triggers. + * @return array The list of bad triggers. + */ + private function analyzeDBTriggers($db_triggers, $signatures) + { + global $spbc; + + $bad_triggers = array(); + $triggered_signatures = []; + + foreach ($db_triggers as $trigger) { + $trigger_code = $trigger->ACTION_STATEMENT; + $signature = self::isTriggerBad($trigger_code, $signatures); + + if ($signature) { + $bad_triggers[] = array( + 'name' => $trigger->TRIGGER_NAME, + 'table' => $trigger->EVENT_OBJECT_TABLE, + 'time' => $trigger->ACTION_TIMING, + 'action' => $trigger->EVENT_MANIPULATION, + 'code' => $trigger_code, + 'signature' => current($signature)[0], + 'status' => 'Vulnerable trigger', + 'sent' => true, + ); + $triggered_signatures[key($signature)] = current($signature); + } + } + + if ( count($triggered_signatures) > 0 ) { + $signature_idx = $spbc->data['scanner']['signatures_found']; + foreach ( $triggered_signatures as $signature_id => $_signature ) { + $signature_idx[$signature_id] = ! empty($signature_idx[$signature_id]) + ? (int)$signature_idx[$signature_id] + 1 + : 1; + } + + $spbc->data['scanner']['signatures_found'] = $signature_idx; + $spbc->save('data'); + } + + return $bad_triggers; + } + + /** + * Check if the trigger is bad. + * + * @param string $trigger_code The code of the trigger. + * @return bool|array The signature name if the trigger is bad, false otherwise. + */ + private static function isTriggerBad($trigger_code, $signatures) + { + foreach ($signatures as $signature_id => $signature) { + if (preg_match('/' . $signature[1] . '/is', $trigger_code)) { + return [$signature_id => $signature]; + } + } + + return false; + } + + public function getDataBaseTriggers() + { + return DBTriggerService::getDataBaseTriggers(); + } + + public function getSignaturesForTriggers() + { + return DBTriggerService::getSignaturesForTriggers(); + } +} diff --git a/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerService.php b/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerService.php new file mode 100644 index 000000000..419a5d7ea --- /dev/null +++ b/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerService.php @@ -0,0 +1,127 @@ +get_results( + $wpdb->prepare( + "SELECT TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_STATEMENT, ACTION_TIMING + FROM information_schema.TRIGGERS + WHERE TRIGGER_SCHEMA = %s", + $wpdb->dbname + ) + ); + + return is_array($db_triggers) ? $db_triggers : array(); + } + + /** + * Get cloud signatures for triggers from local database. + * @return array + */ + final public static function getSignaturesForTriggers() + { + global $wpdb; + $cloud_signatures = $wpdb->get_results( + $wpdb->prepare( + "SELECT id, name, body FROM " . SPBC_TBL_SCAN_SIGNATURES . " WHERE type = %s", + 'TRIGGER' + ), + ARRAY_N + ); + if (!is_array($cloud_signatures)) { + $cloud_signatures = array(); + } + foreach ($cloud_signatures as $key => $signature) { + $cloud_signatures[$signature[0]] = [$signature[1], $signature[2]]; + unset($cloud_signatures[$key]); + } + return $cloud_signatures; + } + + /** + * Delete trigger from storage. + * @param string $trigger_name + * @return bool + */ + final public static function deleteTrigger($trigger_name) + { + global $wpdb; + + // @psalm-suppress WpdbUnsafeMethodsIssue + $wpdb->query('DROP TRIGGER IF EXISTS ' . $trigger_name); + + $triggers = self::loadTriggersStorage(); + $triggers = array_filter($triggers, function ($trigger) use ($trigger_name) { + return $trigger['name'] !== $trigger_name; + }); + return update_option(self::$option_name, $triggers); + } + + /** + * Remove triggers that are not in the database schema, but are in the storage. + * @return void + */ + final public static function removeNotExistsTriggers() + { + $db_triggers = self::getDataBaseTriggers(); + $scanned_triggers = self::loadTriggersStorage(); + + $scanned_triggers_names = array_column($scanned_triggers, 'name'); + $db_triggers_names = array_column($db_triggers, 'TRIGGER_NAME'); + $not_exists_triggers = array_diff($scanned_triggers_names, $db_triggers_names); + + // Update storage with remaining triggers + if (!empty($not_exists_triggers)) { + $updated_triggers = array_filter($scanned_triggers, function ($trigger) use ($not_exists_triggers) { + return !in_array($trigger['name'], $not_exists_triggers); + }); + update_option(self::$option_name, $updated_triggers); + } + } +} diff --git a/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerView.php b/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerView.php new file mode 100644 index 000000000..2e72f4c2e --- /dev/null +++ b/lib/CleantalkSP/SpbctWP/Scanner/DBTrigger/DBTriggerView.php @@ -0,0 +1,63 @@ +items = DBTriggerService::loadTriggersStorage(); + $table->items_count = DBTriggerService::countTriggersStorage(); + $table->columns = [ + 'cb' => array('heading' => '', 'class' => 'check-column', 'width_percent' => 2), + 'about_trigger' => array( 'heading' => 'About trigger', 'width_percent' => 30 ), // name, table, time, action + 'code' => array( 'heading' => 'Code', 'width_percent' => 43 ), + 'signature' => array( 'heading' => 'Signature', 'width_percent' => 10 ), + 'analysis_status' => array( 'heading' => 'Verdict', 'width_percent' => 15 ), + ]; + $table->actions = [ + 'delete' => array('name' => 'Delete',), + ]; + foreach ($table->items as $key => $item) { + $table->items[$key]['about_trigger'] = sprintf( + '%s
%s
%s
%s', + __('Name', 'security-malware-firewall') . ': ' . $item['name'], + __('Table', 'security-malware-firewall') . ': ' . $item['table'], + __('Time', 'security-malware-firewall') . ': ' . $item['time'], + __('Action', 'security-malware-firewall') . ': ' . $item['action'] + ); + $table->items[$key]['about_trigger'] .= sprintf( + '
%s
', + $item['name'], + $item['name'], + __('Delete', 'security-malware-firewall') + ); + $table->items[$key]['code'] = $item['code']; + $table->items[$key]['signature'] = $item['signature']; + $table->items[$key]['analysis_status'] = $item['status']; + $table->items[$key]['cb'] = 1; + } + return $table; + } + + final public static function getScannerTabDescription() + { + return '
' + . + __('This feature scans your WordPress database tables to find suspicious schema triggers that probably may cause malware infection.', 'security-malware-firewall') + . + '
'; + } + + final public static function getWarningTextForMalwareRemovalBanner() + { + return __("There's a high probability that your website has been compromised, as suspicious database triggers show signs of infection. Take action now by ordering malware removal from our experienced security specialists.", 'security-malware-firewall'); + } +} diff --git a/lib/CleantalkSP/SpbctWP/Scanner/ScannerQueue.php b/lib/CleantalkSP/SpbctWP/Scanner/ScannerQueue.php index 52581bd22..a9259b737 100644 --- a/lib/CleantalkSP/SpbctWP/Scanner/ScannerQueue.php +++ b/lib/CleantalkSP/SpbctWP/Scanner/ScannerQueue.php @@ -3,10 +3,12 @@ namespace CleantalkSP\SpbctWP\Scanner; use CleantalkSP\SpbctWP\Scanner\CureLog\CureLog; +use CleantalkSP\SpbctWP\Scanner\DBTrigger\DBTriggerModel; use CleantalkSP\SpbctWP\Scanner\OSCron\OSCronModel; use CleantalkSP\SpbctWP\Scanner\ScanningLog\ScanningLogFacade; use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\ScannerFileStatuses; use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\ScanningStagesStorage; +use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\Stages\DBTriggerAnalysis; use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\Stages\FileSystemAnalysis; use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\Stages\FrontendAnalysis; use CleantalkSP\SpbctWP\Scanner\ScanningStagesModule\Stages\GetApprovedHashes; @@ -45,6 +47,7 @@ class ScannerQueue 'clean_results', 'file_system_analysis', 'os_cron_analysis', + 'db_trigger_analysis', 'get_denied_hashes', 'get_approved_hashes', 'signature_analysis', @@ -56,6 +59,7 @@ class ScannerQueue 'frontend_analysis', 'important_files_listing', 'send_results', + // ATTENTION! Do not forget to localize this array in the frontend, use SpbcEnqueue. Do not forget to localize progress bar! ); /** @@ -116,6 +120,14 @@ public function __construct($stage = '', $offset = null, $amount = null, $root_d if ( isset($spbc->settings['scanner__schedule_send_heuristic_suspicious_files']) && $spbc->settings['scanner__schedule_send_heuristic_suspicious_files'] == 0) { unset(self::$stages['schedule_send_heuristic_suspicious_files']); } + + if ( isset($spbc->settings['scanner__os_cron_analysis']) && $spbc->settings['scanner__os_cron_analysis'] == 0) { + unset(self::$stages['os_cron_analysis']); + } + + if ( isset($spbc->settings['scanner__db_trigger_analysis']) && $spbc->settings['scanner__db_trigger_analysis'] == 0) { + unset(self::$stages['db_trigger_analysis']); + } } /** @@ -751,6 +763,9 @@ public function clean_results($offset = null, $amount = 50000) // phpcs:ignore P // Deleting newly added exclusions $this->deleteFilesOfExclusionDirs($spbc->settings['scanner__dir_exclusions']); + $spbc->data['scanner']['signatures_found'] = []; // Clearing ids of the signatures found + $spbc->save('data'); + $out = array( 'total' => (int)$deleted, 'processed' => (int)$deleted, @@ -1060,6 +1075,35 @@ public function os_cron_analysis() // phpcs:ignore PSR1.Methods.CamelCapsMethodN return array('end' => 1); } + /** + * Scan database triggers for malicious code + * + * @return array + */ + public function db_trigger_analysis() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps + { + $scanning_stages_storage = new ScanningStagesStorage(); + $scanning_stages_storage->converter->loadCollection(); + $stage_data_obj = $scanning_stages_storage->getStage(DBTriggerAnalysis::class); + + $trigger_scanner = new DBTriggerModel(); + $result = $trigger_scanner->run(); + + if (true !== $result) { + ScanningLogFacade::writeToLog( + '' . $stage_data_obj::getTitle() . ' ' . $result + ); + + return array('end' => 1); + } + + ScanningLogFacade::writeToLog( + '' . $stage_data_obj::getTitle() . ' ' . $stage_data_obj->getDescription() + ); + + return array('end' => 1); + } + /** * Getting remote hashes of denied files * @@ -1288,7 +1332,6 @@ public function signature_analysis($status = 'UNKNOWN,MODIFIED,OK,INFECTED,ERROR ); } $spbc->data['scanner']['scanned_total'] = 0; - $spbc->data['scanner']['signatures_found'] = []; // Clearing ids of the signatures found $total = $this->countFilesByStatusAndChecked($status_raw, 'SIGNATURE_ANALYSIS'); if ( ! isset($total['total']) ) { error_log('countFilesByStatusAndChecked: ' . $total['error'] . ' ' . $total['comment']); diff --git a/lib/CleantalkSP/SpbctWP/Scanner/ScanningStagesModule/Stages/DBTriggerAnalysis.php b/lib/CleantalkSP/SpbctWP/Scanner/ScanningStagesModule/Stages/DBTriggerAnalysis.php new file mode 100644 index 000000000..0f66dcb2e --- /dev/null +++ b/lib/CleantalkSP/SpbctWP/Scanner/ScanningStagesModule/Stages/DBTriggerAnalysis.php @@ -0,0 +1,34 @@ +scanned_count_files + . '.'; + } + + public function getName() + { + return __CLASS__; + } + + public function getData() + { + return array(); + } +} diff --git a/lib/CleantalkSP/SpbctWP/SpbcEnqueue.php b/lib/CleantalkSP/SpbctWP/SpbcEnqueue.php index ec223eb7e..aed8b6e16 100644 --- a/lib/CleantalkSP/SpbctWP/SpbcEnqueue.php +++ b/lib/CleantalkSP/SpbctWP/SpbcEnqueue.php @@ -293,6 +293,10 @@ function ($key) { //Cure 'progressbar_auto_cure_backup' => __('Creating a backup', 'security-malware-firewall'), 'progressbar_auto_cure' => __('Cure', 'security-malware-firewall'), + //OS Cron + 'progressbar_os_cron_analysis' => __('Cron tasks analysis', 'security-malware-firewall'), + //DB triggers + 'progressbar_db_trigger_analysis' => __('DB Trigger analysis', 'security-malware-firewall'), // Links 'progressbar_outbound_links' => __('Scanning links', 'security-malware-firewall'), // Frontend diff --git a/lib/CleantalkSP/SpbctWP/State.php b/lib/CleantalkSP/SpbctWP/State.php index eb11dd4c7..ed413a6c5 100644 --- a/lib/CleantalkSP/SpbctWP/State.php +++ b/lib/CleantalkSP/SpbctWP/State.php @@ -76,6 +76,7 @@ class State extends \CleantalkSP\Common\State 'scanner__signature_analysis' => 1, 'scanner__auto_cure' => 1, 'scanner__os_cron_analysis' => 1, + 'scanner__db_trigger_analysis' => 1, 'scanner__dir_exclusions' => '', 'scanner__dir_exclusions_view' => '', 'scanner__list_unknown' => 1, @@ -202,6 +203,7 @@ class State extends \CleantalkSP\Common\State 'frontend' => false, 'analysis' => false, 'oscron' => false, + 'db_triggers' => false, 'warn_on_admin_bar' => false ), 'site_utc_offset_in_seconds' => 0, diff --git a/lib/CleantalkSP/Updater/UpdaterScripts.php b/lib/CleantalkSP/Updater/UpdaterScripts.php index 9830800c3..0e1050dea 100644 --- a/lib/CleantalkSP/Updater/UpdaterScripts.php +++ b/lib/CleantalkSP/Updater/UpdaterScripts.php @@ -1438,4 +1438,16 @@ public static function updateTo_2_149_1() //phpcs:ignore PSR1.Methods.CamelCapsM $spbc->save('data'); } + + /** + * @return void + */ + public static function updateTo_2_154_1() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps + { + $sql_change_signatures_enum = + 'ALTER TABLE ' + . SPBC_TBL_SCAN_SIGNATURES + . " MODIFY COLUMN `type` ENUM('FILE','CODE_PHP','CODE_HTML','CODE_JS','WAF_RULE','TRIGGER','CRON') NOT NULL"; + DB::getInstance()->execute($sql_change_signatures_enum); + } } diff --git a/tests/Scanner/testDBTriggerModel.php b/tests/Scanner/testDBTriggerModel.php new file mode 100644 index 000000000..dd7c16b53 --- /dev/null +++ b/tests/Scanner/testDBTriggerModel.php @@ -0,0 +1,106 @@ + 'trigger1', + 'EVENT_MANIPULATION' => 'INSERT', + 'EVENT_OBJECT_TABLE' => 'table1', + 'ACTION_STATEMENT' => 'malicious_code', + 'ACTION_TIMING' => 'BEFORE', + ], + ]; + + $mock_signatures = [666 => ['malicious_signature', 'malicious_code']]; + + // Create a partial mock of DBTriggerModel + $dbTriggerModelMock = $this->getMockBuilder(DBTriggerModel::class) + ->setMethods(['getDataBaseTriggers', 'getSignaturesForTriggers']) // Mock specific methods + ->getMock(); + + // Mock the getDataBaseTriggers method + $dbTriggerModelMock->method('getDataBaseTriggers') + ->willReturn($mock_triggers); + + // Mock the getSignaturesForTriggers method + $dbTriggerModelMock->method('getSignaturesForTriggers') + ->willReturn($mock_signatures); + + DBTriggerService::saveTriggersStorage([]); + + $result = $dbTriggerModelMock->run(); + + $found_triggers = DBTriggerService::loadTriggersStorage(); + + $expected_result = [ + [ + 'name' => 'trigger1', + 'table' => 'table1', + 'time' => 'BEFORE', + 'action' => 'INSERT', + 'code' => 'malicious_code', + 'signature' => 'malicious_signature', + 'status' => 'Vulnerable trigger', + 'sent' => true, + ], + ]; + + $this->assertTrue($result); + $this->assertNotEmpty($found_triggers); + $this->assertEquals($expected_result, $found_triggers); + } + + public function testRunFail() + { + $mock_triggers = [ + (object)[ + 'EVENT_MANIPULATION' => 'INSERT', + 'EVENT_OBJECT_TABLE' => 'table1', + 'ACTION_TIMING' => 'BEFORE', + ], + ]; + + $mock_signatures = [666 => ['malicious_signature', 'malicious_code']]; + + // Create a partial mock of DBTriggerModel + $dbTriggerModelMock = $this->getMockBuilder(DBTriggerModel::class) + ->setMethods(['getDataBaseTriggers', 'getSignaturesForTriggers']) // Mock specific methods + ->getMock(); + + // Mock the getDataBaseTriggers method + $dbTriggerModelMock->method('getDataBaseTriggers') + ->willReturn($mock_triggers); + + // Mock the getSignaturesForTriggers method + $dbTriggerModelMock->method('getSignaturesForTriggers') + ->willReturn($mock_signatures); + + DBTriggerService::saveTriggersStorage([]); + + $result = $dbTriggerModelMock->run(); + + $found_triggers = DBTriggerService::loadTriggersStorage(); + + $expected_result = [ + [ + 'name' => 'trigger1', + 'table' => 'table1', + 'time' => 'BEFORE', + 'action' => 'INSERT', + 'code' => 'malicious_code', + 'signature' => 'malicious_signature', + 'status' => 'Vulnerable trigger', + ], + ]; + + $this->assertIsString($result); + $this->assertNotEquals($expected_result, $found_triggers); + } +} diff --git a/tests/Scanner/testDBTriggerService.php b/tests/Scanner/testDBTriggerService.php new file mode 100644 index 000000000..e820f84d5 --- /dev/null +++ b/tests/Scanner/testDBTriggerService.php @@ -0,0 +1,93 @@ +origin_wpdb = $wpdb; + } + + public function tearDown() { + global $wpdb; + $wpdb = $this->origin_wpdb; + } + + public function testSaveTriggersStorage() + { + update_option('spbc_db_triggers', []); + + $bad_triggers = [ + ['name' => 'trigger1', 'table' => 'table1', 'time' => 'BEFORE', 'action' => 'INSERT', 'code' => 'code1', 'signature' => 'sig1', 'status' => 'Vulnerable trigger'], + ]; + + DBTriggerService::saveTriggersStorage($bad_triggers); + + $this->assertEquals($bad_triggers, get_option('spbc_db_triggers')); + } + + public function testLoadTriggersStorage() + { + update_option('spbc_db_triggers', []); + + $expected_triggers = [ + ['name' => 'trigger1', 'table' => 'table1', 'time' => 'BEFORE', 'action' => 'INSERT', 'code' => 'code1', 'signature' => 'sig1', 'status' => 'Vulnerable trigger'], + ]; + + update_option('spbc_db_triggers', $expected_triggers); + + $this->assertEquals($expected_triggers, DBTriggerService::loadTriggersStorage()); + } + + public function testCountTriggersStorage() + { + $triggers = [ + ['name' => 'trigger1', 'table' => 'table1', 'time' => 'BEFORE', 'action' => 'INSERT', 'code' => 'code1', 'signature' => 'sig1', 'status' => 'Vulnerable trigger'], + ['name' => 'trigger2', 'table' => 'table2', 'time' => 'AFTER', 'action' => 'UPDATE', 'code' => 'code2', 'signature' => 'sig2', 'status' => 'Vulnerable trigger'], + ]; + + update_option('spbc_db_triggers', $triggers); + + $this->assertEquals(2, DBTriggerService::countTriggersStorage()); + } + + public function testGetDataBaseTriggers() + { + global $wpdb; + + $wpdb = $this->createMock(\wpdb::class); + $wpdb->dbname = 'test_db'; + $wpdb->method('get_results')->willReturn([ + (object)[ + 'TRIGGER_NAME' => 'trigger1', + 'EVENT_MANIPULATION' => 'INSERT', + 'EVENT_OBJECT_TABLE' => 'table1', + 'ACTION_STATEMENT' => 'code1', + 'ACTION_TIMING' => 'BEFORE', + ], + ]); + + $triggers = DBTriggerService::getDataBaseTriggers(); + + $this->assertCount(1, $triggers); + $this->assertEquals('trigger1', $triggers[0]->TRIGGER_NAME); + } + + public function testGetSignaturesForTriggers() + { + global $wpdb; + + $wpdb = $this->createMock(\wpdb::class); + $wpdb->method('get_results')->willReturn([ + [666, 'sig1', 'code1'], + ]); + + $signatures = DBTriggerService::getSignaturesForTriggers(); + + $this->assertArrayHasKey(666, $signatures); + $this->assertEquals(['sig1', 'code1'], $signatures[666]); + } +}