From dbc46ceceac9f406f509444f5f82177bd1b5f4cf Mon Sep 17 00:00:00 2001 From: Artem Anoshin Date: Mon, 22 Aug 2022 12:39:22 +0400 Subject: [PATCH 01/45] Fix: Fixed notice about undefined in Cron::class --- uniforce/lib/Cleantalk/USP/Uniforce/Cron.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php b/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php index 4542c63..54fb20e 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php @@ -20,6 +20,13 @@ public static function getTasks(){ ); } require self::CRON_FILE; + + global $uniforce_tasks; + + if (empty($uniforce_tasks)) { + return array(); + } + return $uniforce_tasks; } From f0d245d8e6a1ba59fdee10ca90e54608ccdb44dc Mon Sep 17 00:00:00 2001 From: alexandergull Date: Mon, 29 Aug 2022 17:26:41 +0500 Subject: [PATCH 02/45] Fix. ScannerController.php -> action__scanner__signature_analysis. Output the error if can't get signatures list. --- .../lib/Cleantalk/USP/ScannerController.php | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/ScannerController.php b/uniforce/lib/Cleantalk/USP/ScannerController.php index 979168b..30fd0a7 100644 --- a/uniforce/lib/Cleantalk/USP/ScannerController.php +++ b/uniforce/lib/Cleantalk/USP/ScannerController.php @@ -286,16 +286,21 @@ public function action__scanner__get_signatures(){ $usp->data->stat->scanner->signature_last_update = time(); $usp->data->stat->scanner->signature_entries = count( $result ); $usp->data->save(); - - }elseif($result['error'] === 'UP_TO_DATE'){ - $out['success'] = 'UP_TO_DATE'; - }else - $out['updated'] = count($result); - + + $out['updated'] = count($result); + + } elseif ( $result['error'] === 'UP_TO_DATE' ) { + $out['success'] = 'UP_TO_DATE'; + } else { + Err::add($result['error']); + } + $out['end'] = 1; - }else - Err::add('Signatures scan is disabled'); + }else{ + Err::add('Signatures scan is disabled'); + } + return Err::check() ? Err::check_and_output() From 36890d05c2307c5ee2692ee85759098b40ce6112 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Mon, 29 Aug 2022 19:39:58 +0500 Subject: [PATCH 03/45] Imp. ScannerController.php -> action__scanner__signature_analysis. Moved signatures object creation out from cycle. --- .../lib/Cleantalk/USP/ScannerController.php | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/ScannerController.php b/uniforce/lib/Cleantalk/USP/ScannerController.php index 30fd0a7..ffeb69e 100644 --- a/uniforce/lib/Cleantalk/USP/ScannerController.php +++ b/uniforce/lib/Cleantalk/USP/ScannerController.php @@ -519,23 +519,23 @@ public function action__scanner__signature_analysis( $offset = null, $amount = n .' weak_spots = :weak_spots' .' WHERE fast_hash = :fast_hash' ); + + $signatures = new Storage('signatures', null, '', 'csv', array( + 'id', + 'name', + 'body', + 'type', + 'attack_type', + 'submitted', + 'cci' + ) ); + $signatures = $signatures->convertToArray(); // Initialing results foreach ( $files_to_check as $file ) { - - $signatures = new Storage('signatures', null, '', 'csv', array( - 'id', - 'name', - 'body', - 'type', - 'attack_type', - 'submitted', - 'cci' - ) ); - $signatures = $signatures->convertToArray(); - + $result = Scanner::file__scan__for_signatures( $this->root, $file, $signatures ); - + if ( empty( $result['error'] ) ) { $status = ! empty( $file['status'] ) && $file['status'] === 'MODIFIED' ? 'MODIFIED' : $result['status']; From 2033d18d7b9695c179e4e6c5f80bac1d759c55c1 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 28 Sep 2022 17:05:23 +0500 Subject: [PATCH 04/45] Fix. usp_settings__plugin_state. Version comparison exception. --- uniforce/inc/settings.php | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/uniforce/inc/settings.php b/uniforce/inc/settings.php index e07ea29..e10da92 100644 --- a/uniforce/inc/settings.php +++ b/uniforce/inc/settings.php @@ -13,16 +13,21 @@ function ctusp_settings__show_modified_files(){ } } -function usp_settings__plugin_state(){ - $usp = State::getInstance(); - if( version_compare( $usp->plugin_meta->version, $usp->plugin_meta->latest_version ) === -1 ){ - echo '

There is a newer version. Update to the latest ' . $usp->plugin_meta->latest_version . '

'; - echo '

'; - }elseif( version_compare( $usp->plugin_meta->version, $usp->plugin_meta->latest_version ) === 1 ){ - echo '

You are using more than the latest version '. $usp->plugin_meta->version . '

'; - }else{ - echo '

You are using the latest version '. $usp->plugin_meta->version . '

'; - } +function usp_settings__plugin_state() +{ + $usp = State::getInstance(); + if ( ! empty($usp->plugin_meta->latest_version) ) { + if ( version_compare($usp->plugin_meta->version, $usp->plugin_meta->latest_version) === -1 ) { + echo '

There is a newer version. Update to the latest ' . $usp->plugin_meta->latest_version . '

'; + echo '

'; + } elseif ( version_compare($usp->plugin_meta->version, $usp->plugin_meta->latest_version) === 1 ) { + echo '

You are using more than the latest version ' . $usp->plugin_meta->version . ' < ' . $usp->plugin_meta->latest_version . '

'; + } else { + echo '

You are using the latest version ' . $usp->plugin_meta->version . '

'; + } + } else { + echo '

Can\'t check module version updates. You use version ' . $usp->plugin_meta->version. '

'; + } } function usp_settings__show_fw_statistics( $out = '' ) From f96b565a4f6bbe2555ff3309691e9790d9409f63 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 28 Sep 2022 21:37:37 +0500 Subject: [PATCH 05/45] Fix. FW and BFP logs sending. Timestamp fix. Fix. FW and BFP logs sending. Formatting of log file to log data for API call refactored. --- uniforce/lib/Cleantalk/USP/Common/Helper.php | 23 ++++++ .../Cleantalk/USP/Uniforce/Firewall/BFP.php | 78 +++++++++++++------ .../Cleantalk/USP/Uniforce/Firewall/FW.php | 60 +++++++++++--- 3 files changed, 127 insertions(+), 34 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Common/Helper.php b/uniforce/lib/Cleantalk/USP/Common/Helper.php index 527c34a..5adb194 100644 --- a/uniforce/lib/Cleantalk/USP/Common/Helper.php +++ b/uniforce/lib/Cleantalk/USP/Common/Helper.php @@ -1171,5 +1171,28 @@ static function search_page_errors($string_page){ || stripos($string_page, 'there has been a critical error on your website') !== false ); } + + /** + * Try to return int value of timestamp converted from arg, false if fails. + * @param $arg + * + * @return bool|int + */ + static function arg_to_timestamp($arg) + { + if ( ! is_int($arg) ) { + if ( $arg === (string)(int)$arg ) { + $arg = (int)$arg; + } else { + return false; + } + } + + if ( $arg > time() || 0 > $arg ) { + return false; + } + + return $arg; + } } \ No newline at end of file diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php index 654ff0a..dc14618 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php @@ -247,46 +247,80 @@ public static function send_log( $ct_key ){ continue; } + $_log = array( + 'event' => $log[0], + 'ip' => $log[1], + 'timestamp' => $log[2], + 'page_url' => $log[3], + 'http_user_agent' => $log[4], + //unused +// 'page' => $log[5], +// 'page_time' => $log[6], +// 'browser_sign' => $log[7], + 'hits' => $log[8], + ); + + //datetime legacy + if ( !empty($_log['timestamp']) && !Helper::arg_to_timestamp($_log['timestamp']) ){ + $_log['datetime'] = $_log['timestamp']; + } else { + $_log['datetime'] = !empty($_log['timestamp']) + ? gmdate('Y-m-d H:i:s', $_log['timestamp']) + : gmdate('Y-m-d H:i:s', 0); + } + + //timestamp conversion + if ( !empty($_log['timestamp']) && Helper::arg_to_timestamp($_log['timestamp']) ){ + $_log['timestamp'] = Helper::arg_to_timestamp($_log['timestamp']); + } else { + $_log['timestamp'] = 0; + } + + $auth_ip = $log[1] ? (string) $log[1] : '0.0.0.0'; - if( (string) $log[8] > 0 ){ - for( $i = 0; (string) $log[8] > $i; $i ++ ){ + if( (int) $_log['hits'] > 0 ){ //todo AG: for what this for cycle? + for( $i = 0; (int) $_log['hits'] > $i; $i ++ ){ $data[] = array( - 'datetime' => is_string($log[2]) ? $log[2] : gmdate('Y-m-d H:i:s', $log[2]), - 'datetime_gmt' => is_string($log[2]) ? strtotime($log[2]) : $log[2], + 'datetime' => $_log['datetime'], + 'datetime_gmt' => $_log['timestamp'], 'user_login' => null, - 'event' => (string) $log[0], - 'auth_ip' => strpos( ':', $auth_ip ) === false ? (int) sprintf( '%u', ip2long( $auth_ip ) ) : $auth_ip, - 'page_url' => (string) $log[3], + 'event' => (string) $_log['event'], + 'auth_ip' => strpos( ':', $auth_ip ) === false + ? (int) sprintf( '%u', ip2long( $auth_ip ) ) + : $auth_ip, + 'page_url' => (string) $_log['page_url'], 'event_runtime' => null, 'role' => null, ); } - }else{ - $data[] = array( - 'datetime' => is_string($log[2]) ? $log[2] : gmdate('Y-m-d H:i:s', $log[2]), - 'datetime_gmt' => is_string($log[2]) ? strtotime($log[2]) : $log[2], - 'user_login' => null, - 'event' => (string) $log[0], - 'auth_ip' => strpos( ':', $auth_ip ) === false ? (int) sprintf( '%u', ip2long( $auth_ip ) ) : $auth_ip, - 'page_url' => (string) $log[3], - 'event_runtime' => null, - 'role' => null, - ); + } else { + $data[] = array( + 'datetime' => $_log['datetime'], + 'datetime_gmt' => $_log['timestamp'], + 'user_login' => null, + 'event' => (string) $_log['event'], + 'auth_ip' => strpos( ':', $auth_ip ) === false + ? (int) sprintf( '%u', ip2long( $auth_ip ) ) + : $auth_ip, + 'page_url' => (string) $_log['page_url'], + 'event_runtime' => null, + 'role' => null, + ); } // Adding user agent if it's login event - if( in_array( (string) $log[0], array( 'login', 'login_2fa', 'login_new_device', 'logout', ) ) ){ + if( in_array( (string) $_log['event'], array( 'login', 'login_2fa', 'login_new_device', 'logout', ) ) ){ $data[] = array_merge( array_pop( $data ), array( - 'user_agent' => $log[4], + 'user_agent' => $_log['user_agent'], ) ); } } - - $result = API::method__security_logs( $ct_key, $data ); + + $result = API::method__security_logs( $ct_key, $data ); if( empty( $result['error'] ) ){ diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php index 77ddcaf..dad7f13 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php @@ -172,21 +172,57 @@ public static function send_log( $ct_key ){ $log = file_get_contents( $log_dir_path . DS . $log_file ); $log = str_getcsv( $log ); + + $_log = array( + 'timestamp' => $log[2], + 'page_url' => $log[6], + 'ip' => $log[1], + 'http_user_agent' => $log[7], + 'request_method' => $log[8], + 'x_forwarded_for' => $log[9], + 'is_personal' => $log[10], + 'matched_networks' => $log[11], + 'hits' => $log[5], + 'mask' => $log[12] + ); + + //datetime legacy + if ( !empty($_log['timestamp']) && !Helper::arg_to_timestamp($_log['timestamp']) ){ + $_log['datetime'] = $_log['timestamp']; + } else { + $_log['datetime'] = !empty($_log['timestamp']) + ? gmdate('Y-m-d H:i:s', $_log['timestamp']) + : gmdate('Y-m-d H:i:s', 0); + } + + //timestamp conversion + if ( !empty($_log['timestamp']) && Helper::arg_to_timestamp($_log['timestamp']) ){ + $_log['timestamp'] = Helper::arg_to_timestamp($_log['timestamp']); + } else { + $_log['timestamp'] = 0; + } //Compile log $to_data = array( - 'datetime' => isset( $log[2] ) ? gmdate('Y-m-d H:i:s', $log[2]) : 0, - 'datetime_gmt' => isset( $log[2] ) ? $log[2] : 0, - 'page_url' => isset( $log[6] ) ? $log[6] : 0, - 'visitor_ip' => isset( $log[1] ) ? ( Helper::ip__validate( $log[1] ) == 'v4' ? (int) sprintf( '%u', ip2long( $log[1] ) ) : (string) $log[1] ) : 0, - 'http_user_agent' => isset( $log[7] ) ? $log[7] : 0, - 'request_method' => isset( $log[8] ) ? $log[8] : 0, - 'x_forwarded_for' => isset( $log[9] ) ? $log[9] : 0, - 'is_personal' => isset( $log[10] ) ? $log[10] : null, - 'matched_networks' => isset( $log[11] ) ? $log[11] . '/' . $log[12] : null, - 'hits' => isset( $log[5] ) ? $log[5] : 0, + 'datetime' => $_log['datetime'], + //named because of reasons + 'datetime_gmt' => $_log['timestamp'], + 'page_url' => isset( $_log['page_url'] ) ? $_log['page_url'] : 0, + 'visitor_ip' => isset( $_log['ip'] ) + ? ( Helper::ip__validate( $_log['ip'] ) == 'v4' + ? (int) sprintf( '%u', ip2long( $_log['ip'] ) ) + : (string) $_log['ip'] ) + : 0, + 'http_user_agent' => isset( $_log['http_user_agent'] ) ? $_log['http_user_agent'] : 0, + 'request_method' => isset( $_log['request_method'] ) ? $_log['request_method'] : 0, + 'x_forwarded_for' => isset( $_log['x_forwarded_for'] ) ? $_log['x_forwarded_for'] : 0, + 'is_personal' => isset( $_log['is_personal'] ) ? $_log['is_personal'] : null, + 'matched_networks' => isset( $_log['matched_networks'] ) + ? $_log['matched_networks'] . '/' . $_log['mask'] + : null, + 'hits' => isset( $_log['hits'] ) ? $_log['hits'] : 0, ); - + // Legacy switch( $log[3] ){ case 'PASS_BY_TRUSTED_NETWORK': $to_data['status_efw'] = 3; break; @@ -223,7 +259,7 @@ public static function send_log( $ct_key ){ $data[] = $to_data; } unset($key, $value, $result, $to_data); - + //Sending the request $result = API::method__security_logs__sendFWData( $ct_key, $data ); From 058a2b907ea7297466763c7d741133b11d2a8459 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 2 Nov 2022 16:01:17 +0500 Subject: [PATCH 06/45] Fix. Signatures get. Show alert if can not connect signatures source. --- uniforce/lib/Cleantalk/USP/ScannerController.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/ScannerController.php b/uniforce/lib/Cleantalk/USP/ScannerController.php index 979168b..cfc4971 100644 --- a/uniforce/lib/Cleantalk/USP/ScannerController.php +++ b/uniforce/lib/Cleantalk/USP/ScannerController.php @@ -289,9 +289,11 @@ public function action__scanner__get_signatures(){ }elseif($result['error'] === 'UP_TO_DATE'){ $out['success'] = 'UP_TO_DATE'; - }else - $out['updated'] = count($result); - + }else{ + Err::add($result['error']); + $out['updated'] = count($result); + } + $out['end'] = 1; }else From 0209e0518d43550d343782d1b405eb035fd0cdec Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 2 Nov 2022 16:41:11 +0500 Subject: [PATCH 07/45] Fix. WAF. Now correctly extract and handle WAF rules. --- .../Cleantalk/USP/Uniforce/Firewall/WAF.php | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php index 6551140..afbedf3 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php @@ -116,10 +116,14 @@ public function check() { return $results; } - - private function signatures__get(){ + + /** + * Get array of WAF signatures. Return array of signatures or false if no WAF rules found. + * @return array|false + */ + private function signatures__get(){ - $signatures = new Storage('signatures', null, '', 'csv', array( + $signatures_source = new Storage('signatures', null, '', 'csv', array( 'id', 'name', 'body', @@ -128,12 +132,15 @@ private function signatures__get(){ 'submitted', 'cci' ) ); - - foreach( $signatures->convertToArray() as $signature ){ - if( $signature['type'] === 'WAF_RULE' ) - $signatures[] = $signature; - } - return $signatures; + + $signatures = []; + + foreach ( $signatures_source->convertToArray() as $signature ) { + if ( $signature['type'] === 'WAF_RULE' ) { + $signatures[] = $signature; + } + } + return !empty($signatures) ? $signatures : false; } /** From 4b6843d5ebde731265d81620dc203931d102e9dc Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 2 Nov 2022 17:45:31 +0500 Subject: [PATCH 08/45] Fix. Amazon URLs. --- uniforce/lib/Cleantalk/USP/Scanner/Helper.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Scanner/Helper.php b/uniforce/lib/Cleantalk/USP/Scanner/Helper.php index 03a235d..729f9c6 100644 --- a/uniforce/lib/Cleantalk/USP/Scanner/Helper.php +++ b/uniforce/lib/Cleantalk/USP/Scanner/Helper.php @@ -4,8 +4,8 @@ class Helper { - const signatures_version_file_url = 'https://s3-us-west-2.amazonaws.com/cleantalk-security/security_signatures/version.txt'; - const signatures_file_url = 'https://s3-us-west-2.amazonaws.com/cleantalk-security/security_signatures/security_signatures_v2.csv.gz'; + const signatures_version_file_url = 'https://cleantalk-security.s3.amazonaws.com/security_signatures/version.txt'; + const signatures_file_url = 'https://cleantalk-security.s3.amazonaws.com/security_signatures/security_signatures_v2.csv.gz'; public static function get_files( $offset = 0, $amount = 1500, $path = CT_USP_SITE_ROOT ) { @@ -98,7 +98,7 @@ static public function get_hashes__signature( $last_signature_update = 0 ) */ static public function get_hashes__approved_files($cms, $type, $version) { - $file_path = 'https://cleantalk-security.s3-us-west-2.amazonaws.com/extensions_checksums/'.$cms.'/'.$type.'/'.$version.'.csv.gz'; + $file_path = 'https://cleantalk-security.s3.amazonaws.com/extensions_checksums/'.$cms.'/'.$type.'/'.$version.'.csv.gz'; if( \Cleantalk\USP\Uniforce\Helper::http__request($file_path, array(), 'get_code') == 200) { @@ -217,7 +217,7 @@ static public function file__get_original($file_info) $url_path = 'https://themes.svn.wordpress.org/'.$file_info['source'].'/'.$file_info['version'].'/'.$file_info['path']; break; default: - $url_path = 'http://cleantalk-security.s3.amazonaws.com/cms_sources/'.$file_info['source'].'/'.$file_info['version'].$file_info['path']; + $url_path = 'https://cleantalk-security.s3.amazonaws.com/cms_sources/'.$file_info['source'].'/'.$file_info['version'].$file_info['path']; break; } From 9bebe94fd1e5800f5263ef4096da05609bcc0722 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Thu, 3 Nov 2022 12:15:56 +0500 Subject: [PATCH 09/45] Mod. Installing. Updating latest version is duplicated from cron to the installing process. --- uniforce/inc/admin.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/uniforce/inc/admin.php b/uniforce/inc/admin.php index 0e34c55..686ed28 100644 --- a/uniforce/inc/admin.php +++ b/uniforce/inc/admin.php @@ -289,6 +289,10 @@ function usp_install_config($modified_files, $api_key, $cms, $exclusions ){ $usp->plugin_meta->is_installed = true; $usp->plugin_meta->version = SPBCT_VERSION; + if ( empty($usp->plugin_meta->latest_version) ) { + $updater = new \Cleantalk\USP\Updater\Updater(CT_USP_ROOT); + $usp->plugin_meta->latest_version = $updater->getLatestVersion(); + } $usp->plugin_meta->save(); } From 6df997ceba3ff16c679e58efaffe05e5d7aa8615 Mon Sep 17 00:00:00 2001 From: Glomberg Date: Tue, 15 Nov 2022 10:18:33 +0300 Subject: [PATCH 10/45] Fix. SecFW. Writing fs stats sleep implemented. --- uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php index 77ddcaf..7edb03b 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php @@ -312,6 +312,9 @@ public static function update( $api_key ){ State::getInstance()->fw_stats->update_percent = round( ( ( (int) $current_file_num + 1 ) / (int) $url_count ), 2) * 100; State::getInstance()->fw_stats->save(); + // Make sure to write all fs actions + sleep(3); + // Make next call Helper::http__request( Server::get( 'HTTP_HOST' ) . CT_USP_AJAX_URI, From 69b039ea5940c81cc6d04711abb9ff197f97f209 Mon Sep 17 00:00:00 2001 From: Glomberg Date: Tue, 22 Nov 2022 16:33:20 +0300 Subject: [PATCH 11/45] Fix. Server variables getting from $_SERVER instead of filter_input. --- uniforce/lib/Cleantalk/USP/Common/Helper.php | 2 +- uniforce/lib/Cleantalk/USP/Common/RemoteCalls.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Common/Helper.php b/uniforce/lib/Cleantalk/USP/Common/Helper.php index 527c34a..1e075e4 100644 --- a/uniforce/lib/Cleantalk/USP/Common/Helper.php +++ b/uniforce/lib/Cleantalk/USP/Common/Helper.php @@ -159,7 +159,7 @@ static public function ip__get($ip_types = array('real', 'remote_addr', 'x_forwa } // Is private network - if($ip_type === false || ($ip_type && (self::ip__is_private_network($ips['real'], $ip_type) || self::ip__mask_match($ips['real'], filter_input(INPUT_SERVER, 'SERVER_ADDR') . '/24', $ip_type)))){ + if($ip_type === false || ($ip_type && (self::ip__is_private_network($ips['real'], $ip_type) || self::ip__mask_match($ips['real'], $_SERVER['SERVER_ADDR'] . '/24', $ip_type)))){ // X-Forwarded-For if(isset($headers['X-Forwarded-For'])){ diff --git a/uniforce/lib/Cleantalk/USP/Common/RemoteCalls.php b/uniforce/lib/Cleantalk/USP/Common/RemoteCalls.php index 7a607bd..7ae9df5 100644 --- a/uniforce/lib/Cleantalk/USP/Common/RemoteCalls.php +++ b/uniforce/lib/Cleantalk/USP/Common/RemoteCalls.php @@ -28,7 +28,7 @@ public static function perform(){ $cooldown = isset($usp->remote_calls->$action->cooldown) ? $usp->remote_calls->$action->cooldown : self::COOLDOWN; - $pass_cooldown = Helper::ip__get(array('real')) === filter_input(INPUT_SERVER, 'SERVER_ADDR'); + $pass_cooldown = Helper::ip__get(array('real')) === $_SERVER['SERVER_ADDR']; if(time() - $usp->remote_calls->$action->last_call >= $cooldown || $pass_cooldown From f62feb89261dbdeacc989a9193f7043fcd6dc757 Mon Sep 17 00:00:00 2001 From: Glomberg Date: Thu, 24 Nov 2022 11:43:11 +0300 Subject: [PATCH 12/45] Fix. SecFW. Updater fixed - preventing several update process running. --- uniforce/inc/cron_functions.php | 3 +++ uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php | 12 +++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/uniforce/inc/cron_functions.php b/uniforce/inc/cron_functions.php index ce0fa9f..98133e6 100644 --- a/uniforce/inc/cron_functions.php +++ b/uniforce/inc/cron_functions.php @@ -13,6 +13,9 @@ function uniforce_fw_update( $immediate = false ){ // SFW actions if( $usp->key && $usp->settings->fw ){ + State::getInstance()->fw_stats->updating = false; + State::getInstance()->fw_stats->save(); + // Update SFW Helper::http__request( Server::get('HTTP_HOST') . CT_USP_AJAX_URI, diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php index 7edb03b..54ce5e7 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php @@ -257,7 +257,14 @@ public static function update( $api_key ){ // Get multifiles if( ! $multifile_url ){ - + + if ( State::getInstance()->fw_stats->updating ) { + return ['error' => 'Updating is under process.']; + } + + State::getInstance()->fw_stats->updating = true; + State::getInstance()->fw_stats->save(); + $result = self::update__get_multifiles( $api_key ); if( ! empty( $result['error'] ) ){ return $result; @@ -271,8 +278,7 @@ public static function update( $api_key ){ State::getInstance()->fw_stats->updating_folder = CT_USP_ROOT . DS . 'fw_files'; $download_files_result = Helper::http__download_remote_file__multi( $result['file_urls'], State::getInstance()->fw_stats->updating_folder ); if( empty( $download_files_result['error'] ) ){ - - State::getInstance()->fw_stats->updating = true; + State::getInstance()->fw_stats->update_percent = 0; State::getInstance()->fw_stats->entries = 0; State::getInstance()->fw_stats->update_start = time(); From d9fd6f556865e6e18384b2dd54d1a9d9900be925 Mon Sep 17 00:00:00 2001 From: svfcode Date: Fri, 25 Nov 2022 23:39:44 +0400 Subject: [PATCH 13/45] Fix. Scanner. View bad code (modal dialog) --- uniforce/css/settings-scanner.css | 3 ++- uniforce/js/scanner.js | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/uniforce/css/settings-scanner.css b/uniforce/css/settings-scanner.css index 9c1a867..7f136dc 100644 --- a/uniforce/css/settings-scanner.css +++ b/uniforce/css/settings-scanner.css @@ -18,4 +18,5 @@ .spbc_view_file_row_wrapper:nth-child(odd) { background: #ccc; } .spbc_view_file_row_wrapper:nth-child(even) { background: #ddd; } .spbc_view_file_row_num { display: inline-block; width: 30px; margin: 0 0 0 5px; border-right: 1px solid black; } -.spbc_view_file_row { display: inline; margin: 0 0 0 5px; } \ No newline at end of file +.spbc_view_file_row { display: inline; margin: 0 0 0 5px; } +.spbc_view_file_row_wrapper_weak_spots { background-color: #f00; } \ No newline at end of file diff --git a/uniforce/js/scanner.js b/uniforce/js/scanner.js index c8fb4a6..bc3da53 100644 --- a/uniforce/js/scanner.js +++ b/uniforce/js/scanner.js @@ -22,16 +22,23 @@ function spbc_scanner_button_file_view_event(obj){ function spbc_scannerButtonFileView_callback(result, data, params){ console.log('FILE_VIEWED'); + var weak_spots = result.weak_spots.match(/\d+/g); + var window_height = window.innerHeight; var row_template = '
%s

%s


'; + var row_template_weak_spots = '
%s

%s


'; jQuery('#spbc_dialog').empty(); for(row in result.file){ - jQuery('#spbc_dialog').append(row_template.printf(row, result.file[row])); + if (weak_spots.includes(row)) { + jQuery('#spbc_dialog').append(row_template_weak_spots.printf(row, result.file[row])); + } else { + jQuery('#spbc_dialog').append(row_template.printf(row, result.file[row])); + } } var content_height = Object.keys(result.file).length * 19 + 19, visible_height = (document.documentElement.clientHeight) / 10 * 75; content_height = content_height < 76 ? 76 : content_height; - var overflow = content_height < visible_height ? 'no_scroll' : 'scroll'; + var overflow = content_height < window_height ? 'no_scroll' : 'scroll'; jQuery('#spbc_dialog').data('overflow', overflow); jQuery('#spbc_dialog').dialog({ @@ -39,8 +46,7 @@ function spbc_scannerButtonFileView_callback(result, data, params){ title: result.file_path, position: { my: "center", at: "center" , of: window }, width: +(jQuery('body').width() / 100 * 70), - height: overflow === 'scroll' ? visible_height : content_height, - // minHeight: 300, + maxHeight: window_height, show: { effect: "blind", duration: 500 }, draggable: true, closeText: "Close", @@ -48,6 +54,7 @@ function spbc_scannerButtonFileView_callback(result, data, params){ console.log(jQuery(event.target).data('overflow')); document.body.style.overflow = 'hidden'; if(jQuery(event.target).data('overflow') == 'scroll') event.target.style.overflow = 'scroll'; + setTimeout(function(){ jQuery('.spbc_view_file_row_wrapper_weak_spots')[0].scrollIntoView({block: "center"}); }, 100); }, beforeClose: function(event, ui) { document.body.style.overflow = 'auto'; }, }); From ac5e22e1e7479075f63633283b2ec2f3199b4f5a Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 30 Nov 2022 19:27:01 +0500 Subject: [PATCH 14/45] Fix. Empty page on main during BFP work. --- uniforce/lib/Cleantalk/USP/Common/Helper.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Common/Helper.php b/uniforce/lib/Cleantalk/USP/Common/Helper.php index 1e075e4..041ec73 100644 --- a/uniforce/lib/Cleantalk/USP/Common/Helper.php +++ b/uniforce/lib/Cleantalk/USP/Common/Helper.php @@ -822,14 +822,21 @@ public static function getFilenameFromUrl( $url ) * * returns array */ - static public function http__get_headers(){ + public static function http__get_headers() + { $headers = array(); - foreach($_SERVER as $key => $val){ - if(preg_match('/\AHTTP_/', $key)){ + + if ( !is_array($_SERVER) ) { + return $headers; + } + + foreach($_SERVER as $key => $val){ + + if(preg_match('/\AHTTP_/', $key)){ $server_key = preg_replace('/\AHTTP_/', '', $key); $key_parts = explode('_', $server_key); - if(count($key_parts) > 0 and strlen($server_key) > 2){ + if(count($key_parts) > 0 && strlen($server_key) > 2){ foreach($key_parts as $part_index => $part){ $key_parts[$part_index] = function_exists('mb_strtolower') ? mb_strtolower($part) : strtolower($part); $key_parts[$part_index][0] = strtoupper($key_parts[$part_index][0]); From 0f32dc7073aa621b70f3880414d7e5efce18373f Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 30 Nov 2022 19:27:50 +0500 Subject: [PATCH 15/45] Fix. Duplicated script adding. --- uniforce/uniforce.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/uniforce/uniforce.php b/uniforce/uniforce.php index b4dcbd5..9c9e9b5 100644 --- a/uniforce/uniforce.php +++ b/uniforce/uniforce.php @@ -16,6 +16,10 @@ // Helper functions require_once( CT_USP_INC . 'functions.php' ); +global $usp_is_js_attached; + +$usp_is_js_attached = false; + if( $usp->settings->fw || $usp->settings->waf || $usp->settings->bfp ){ // Security FireWall @@ -75,6 +79,10 @@ \Cleantalk\USP\Uniforce\Firewall\BFP::update_log( 'view' ); function uniforce_attach_js( $buffer ){ + global $usp_is_js_attached; + if ($usp_is_js_attached){ + return $buffer; + } if( !(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') // No ajax @@ -91,11 +99,11 @@ function uniforce_attach_js( $buffer ){ 1 ); } - + if( State::getInstance()->settings->bfp && State::getInstance()->detected_cms ){ - + if( \Cleantalk\USP\Uniforce\Firewall\BFP::is_logged_in( State::getInstance()->detected_cms ) ) { - + if( ! Cookie::get( 'spbct_authorized' ) ){ setcookie( 'spbct_authorized', md5( State::getInstance()->key ), 0, '/' ); \Cleantalk\USP\Uniforce\Firewall\BFP::update_log( 'login' ); @@ -103,18 +111,20 @@ function uniforce_attach_js( $buffer ){ } }else{ - + if( Cookie::get('spbct_authorized') ) { \Cleantalk\USP\Uniforce\Firewall\BFP::update_log( 'logout' ); setcookie( 'spbct_authorized', md5( State::getInstance()->key ), time()-3600, '/' ); } - + if( Post::get( 'spbct_login_form' ) ) \Cleantalk\USP\Uniforce\Firewall\BFP::update_log( 'auth_failed' ); - + } } + $usp_is_js_attached = true; + return $buffer; } From 31daee1bd94f99b95eb99c5a4ab63e2e37b24abe Mon Sep 17 00:00:00 2001 From: alexandergull Date: Mon, 12 Dec 2022 17:14:33 +0500 Subject: [PATCH 16/45] Fix. FW check. Trusted networks now gain highest priority, more thatn personal records. --- .../lib/Cleantalk/USP/Security/Firewall.php | 53 ++++++++++++------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Security/Firewall.php b/uniforce/lib/Cleantalk/USP/Security/Firewall.php index 9a60159..21c5751 100644 --- a/uniforce/lib/Cleantalk/USP/Security/Firewall.php +++ b/uniforce/lib/Cleantalk/USP/Security/Firewall.php @@ -166,43 +166,58 @@ public function run() { * @return array Single element array of result */ private function prioritize( $results ){ - - $current_fw_result_priority = 0; - $result = array( - 'status' => 'PASS', - 'ip' => '', - ); - - if( is_array( $results ) ) { - foreach ( $results as $fw_result ) { - $priority = array_search( $fw_result['status'], $this->statuses_priority ) + ( isset($fw_result['is_personal']) && $fw_result['is_personal'] ? count ( $this->statuses_priority ) : 0 ); - if( $priority >= $current_fw_result_priority ){ - $current_fw_result_priority = $priority; + + $final_priority = 0; + $result = array( + 'status' => 'PASS', + 'ip' => '', + ); + + if ( is_array($results) ) { + foreach ( $results as $fw_result ) { + /** + * Calculating priority. Records that have status PASS_BY_TRUSTED_NETWORK gain hardcoded highest priority. + * Personal records are next by priority in accordance of this->statuses_priority. + */ + $status_priority_from_table = array_search($fw_result['status'], $this->statuses_priority); + $is_personal_flag = isset($fw_result['is_personal']) && $fw_result['is_personal']; + $is_trusted_network_flag = isset($fw_result['status']) && $fw_result['status'] == 'PASS_BY_TRUSTED_NETWORK'; + //used to gain maximum priority + $total_count_of_statuses = count($this->statuses_priority); + $current_record_priority = $status_priority_from_table; + if ( $is_personal_flag || $is_trusted_network_flag ) { + //set maximum priority + $current_record_priority += $total_count_of_statuses; + } + //set new final priority if it is less than current record priority + if ( $current_record_priority >= $final_priority ) { + $final_priority = $current_record_priority; + //proceed result array $result = array( - + // Necessary params 'module' => $fw_result['module'], 'ip' => $fw_result['ip'], 'status' => $fw_result['status'], - + // FW 'is_personal' => !empty( $fw_result['is_personal'] ) ? (int)$fw_result['is_personal'] : 0, 'country_code' => !empty( $fw_result['country_code'] ) ? $fw_result['country_code'] : '', 'network' => !empty( $fw_result['network'] ) ? $fw_result['network'] : 0, 'mask' => !empty( $fw_result['mask'] ) ? $fw_result['mask'] : 0, - + // WAF 'pattern' => !empty( $fw_result['pattern'] ) ? $fw_result['pattern'] : array(), - + // Security - 'event' => !empty( $fw_result['event'] ) ? $fw_result['event'] : 0, + 'event' => !empty( $fw_result['event'] ) ? $fw_result['event'] : 0, ); } } } - + return $result; - + } /** From ef82d2441f52ef0be87997c8715133c7311d1d3c Mon Sep 17 00:00:00 2001 From: svfcode Date: Thu, 15 Dec 2022 17:05:58 +0400 Subject: [PATCH 17/45] Fix. Firewall. Add missing statuses. --- uniforce/lib/Cleantalk/USP/Security/Firewall.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/uniforce/lib/Cleantalk/USP/Security/Firewall.php b/uniforce/lib/Cleantalk/USP/Security/Firewall.php index 9a60159..f26c0ca 100644 --- a/uniforce/lib/Cleantalk/USP/Security/Firewall.php +++ b/uniforce/lib/Cleantalk/USP/Security/Firewall.php @@ -30,6 +30,8 @@ class Firewall private $statuses_priority = array( 'PASS', 'DENY', + 'DENY_BY_SEC_FW', + 'DENY_BY_SPAM_FW', 'DENY_BY_NETWORK', 'DENY_BY_BFP', 'DENY_BY_DOS', From fd827aa5002395919d58004dce833902389d090c Mon Sep 17 00:00:00 2001 From: Viktor Date: Wed, 8 Mar 2023 11:35:59 +0300 Subject: [PATCH 18/45] New. Changing admin password logic implemented. #12 * Fix. SecFW. Do not run firewall for uniforce admin user. * Fix. Settings. New fields group for the password changing added. * Fix. Settings. The password changing logic implemented. * Fix. Settings. Sending changed password to the email. * Fix. Settings. Changing password - checking password length more than 8 chars. --- uniforce/css/settings.css | 4 +- uniforce/inc/actions.php | 4 + uniforce/inc/admin.php | 100 +++++++++++++++----- uniforce/inc/functions.php | 8 +- uniforce/js/settings.js | 54 +++++++++++ uniforce/lib/Cleantalk/USP/Layout/Field.php | 22 +++++ uniforce/uniforce.php | 2 +- uniforce/view/settings.php | 18 ++++ 8 files changed, 183 insertions(+), 29 deletions(-) diff --git a/uniforce/css/settings.css b/uniforce/css/settings.css index a2972a1..ef53a9e 100644 --- a/uniforce/css/settings.css +++ b/uniforce/css/settings.css @@ -90,7 +90,9 @@ label.checkbox { vertical-align: middle; margin: 0 5px 3px 0; } - .ctusp_field input[type=text]{ + .ctusp_field input[type=text], + .ctusp_field input[type=password] + { padding: 6px 12px; border-radius: 4px; border: 1px #999 solid; diff --git a/uniforce/inc/actions.php b/uniforce/inc/actions.php index 4131589..8443ae3 100644 --- a/uniforce/inc/actions.php +++ b/uniforce/inc/actions.php @@ -48,6 +48,10 @@ usp_do_uninstall(); break; + case 'change_admin_password' : + usp_do_change_admin_password(); + break; + case 'spbc_tbl-action--row': call_user_func( '\Cleantalk\USP\Layout\ListTable::ajax__row_action_handler' ); break; diff --git a/uniforce/inc/admin.php b/uniforce/inc/admin.php index 686ed28..bc98508 100644 --- a/uniforce/inc/admin.php +++ b/uniforce/inc/admin.php @@ -247,31 +247,7 @@ function usp_install_config($modified_files, $api_key, $cms, $exclusions ){ if( Post::get( 'user_token' ) ) $usp->data->user_token = trim( Post::get( 'user_token' ) ); - $host = $_SERVER['HTTP_HOST'] ?: 'Your Site'; - $to = trim( Post::get( 'email' ) ); - $subject = 'UniForce settings password for ' . $host; - $message = "Hi,

- Your credentials to get access to settings of Uniforce (Universal security plugin by CleanTalk) are bellow,

- Login: $login
- Password: $pass
- Settings URL: https://$host/uniforce/
- Dashboard: https://cleantalk.org/my/?cp_mode=security

- --
- With regards,
- CleanTalk team."; - - $headers = 'MIME-Version: 1.0' . "\r\n"; - $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; - - // Sending password - if( trim( Post::get( 'email' ) ) && Post::get( 'admin_password' ) ){ - mail( - $to, - $subject, - $message, - $headers - ); - } + usp_send_pass_to_email(trim(Post::get('email')), $login, $pass); if( Post::get( 'account_name_ob' ) ) $usp->data->account_name_ob = trim( Post::get( 'account_name_ob' ) ); @@ -296,6 +272,33 @@ function usp_install_config($modified_files, $api_key, $cms, $exclusions ){ $usp->plugin_meta->save(); } +function usp_send_pass_to_email($to, $login, $pass) +{ + $host = $_SERVER['HTTP_HOST'] ?: 'Your Site'; + //$to = trim( Post::get( 'email' ) ); + $subject = 'UniForce settings password for ' . $host; + $message = "Hi,

+ Your credentials to get access to settings of Uniforce (Universal security plugin by CleanTalk) are bellow,

+ Login: $login
+ Password: $pass
+ Settings URL: https://$host/uniforce/
+ Dashboard: https://cleantalk.org/my/?cp_mode=security

+ --
+ With regards,
+ CleanTalk team."; + + $headers = 'MIME-Version: 1.0' . "\r\n"; + $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; + + // Sending password + mail( + $to, + $subject, + $message, + $headers + ); +} + /** * Modify cron */ @@ -633,4 +636,49 @@ function usp_do_uninstall() { Err::check() or die(json_encode(array('success' => true))); die(Err::check_and_output( 'as_json' )); -} \ No newline at end of file +} + +/** + * AJAX handler for the changing admin password logic + * + * @return string json + */ +function usp_do_change_admin_password() +{ + $usp = State::getInstance(); + + // Changing password logic + // 1 if the fields not empty + if ( Post::get('old_password') && Post::get('new_password') && Post::get('new_password_confirm') ) { + + // 2 if the old password is right + if ( $usp->data->password !== hash( 'sha256', trim(Post::get('old_password'))) ) { + Err::add('Changing admin password', 'The old password is wrong'); + die(Err::check_and_output( 'as_json' )); + } + + // 3 if the password is too short + if ( strlen(Post::get('new_password')) < 8 ) { + Err::add('Changing admin password', 'Password must be more than 8 characters'); + die(Err::check_and_output( 'as_json' )); + } + + // 4 if the new password confirmed + if ( Post::get('new_password') !== Post::get('new_password_confirm') ) { + Err::add('Changing admin password', 'New password is not confirmed'); + die(Err::check_and_output( 'as_json' )); + } + + // 5 save the new password + $usp->data->password = hash('sha256', trim(Post::get('new_password'))); + $usp->data->save(); + + usp_send_pass_to_email($usp->data->email, $usp->data->email, Post::get('new_password')); + + } else { + Err::add('Changing admin password', 'All fields are required'); + } + + Err::check() or die(json_encode(array('success' => true))); + die(Err::check_and_output( 'as_json' )); +} diff --git a/uniforce/inc/functions.php b/uniforce/inc/functions.php index 03ff60e..bbd2ac0 100644 --- a/uniforce/inc/functions.php +++ b/uniforce/inc/functions.php @@ -1,6 +1,7 @@ /**/"; -} \ No newline at end of file +} + +function usp__is_admin() +{ + return Cookie::get( 'authentificated' ) === State::getInstance()->data->security_key; +} diff --git a/uniforce/js/settings.js b/uniforce/js/settings.js index 2793ca8..d415bac 100644 --- a/uniforce/js/settings.js +++ b/uniforce/js/settings.js @@ -23,6 +23,11 @@ jQuery(document).ready(function() { uninstall(); }); + // Change admin password + $("#ctusp_field---change_admin_password").on('click', function(event){ + changeAdminPassword(); + }); + // Update // Logout $("#btn-update").on('click', function(event){ @@ -169,6 +174,55 @@ function uninstall(){ } +function changeAdminPassword() { + const newPassword = $('#ctusp_field---new_password').val(); + if ( newPassword.length < 8 ) { + $("body").overhang({ + type: "error", + message: 'Error: Password must be more than 8 characters', + duration: 43200, + overlay: true, + closeConfirm: true, + easing: 'linear' + }); + return; + } + ctAJAX( + { + data: { + action: 'change_admin_password', + old_password: $('#ctusp_field---old_password').val(), + new_password: newPassword, + new_password_confirm: $('#ctusp_field---new_password_confirm').val(), + }, + successCallback: function(result, data, params, obj) { + if (result.success) { + $("body").overhang({ + type: "success", + message: "New password saved!", + duration: 3, + overlay: true, + // closeConfirm: true, + easing: 'linear' + }); + } + }, + spinner: $('#ctusp_field---change_admin_password+div>.preloader'), + button: $('#ctusp_field---change_admin_password'), + errorOutput: function( msg ){ + $("body").overhang({ + type: "error", + message: 'Error: ' + msg, + duration: 43200, + overlay: true, + closeConfirm: true, + easing: 'linear' + }); + } + } + ); +} + function update(){ ctAJAX({ data: { action: 'update'}, diff --git a/uniforce/lib/Cleantalk/USP/Layout/Field.php b/uniforce/lib/Cleantalk/USP/Layout/Field.php index b041e2a..7157e03 100644 --- a/uniforce/lib/Cleantalk/USP/Layout/Field.php +++ b/uniforce/lib/Cleantalk/USP/Layout/Field.php @@ -121,6 +121,28 @@ public function draw_element__text() { : ''; } + public function draw_element__password() { + + $name = $this->getName(); + + if($this->title_first) + echo ' '; + + echo 'parent_field && !$this->state->settings->{$this->parent_field} ? ' disabled="disabled"' : '') + .($this->child_fields ? ' onchange="uspSettingsDependencies([\''.implode("','",$this->child_fields).'\'])"' : '') + .' />'; + + if(!$this->title_first) + echo ' '; + + echo $this->description + ?'
'. $this->description .'
' + : ''; + } + public function draw_element__checkbox() { $name = $this->getName(); diff --git a/uniforce/uniforce.php b/uniforce/uniforce.php index 9c9e9b5..0d1db43 100644 --- a/uniforce/uniforce.php +++ b/uniforce/uniforce.php @@ -65,7 +65,7 @@ return; } - if( $firewall->module__is_loaded__any() ){ + if( $firewall->module__is_loaded__any() && ! usp__is_admin() ){ $firewall->run(); } diff --git a/uniforce/view/settings.php b/uniforce/view/settings.php index 5951ee0..a2f2ffe 100644 --- a/uniforce/view/settings.php +++ b/uniforce/view/settings.php @@ -220,6 +220,24 @@ ->setTitle('Signature analysis') ->setDescription('Will search for known malicious signatures in files.') ->getParent( 2) + ->add_group('Change password') + ->add_field('old_password') + ->set_title('Type the old password') + ->setInput_type('password') + ->getParent() + ->add_field('new_password') + ->set_title('Type the new password') + ->setInput_type('password') + ->getParent() + ->add_field('new_password_confirm') + ->set_title('Confirm the new password') + ->setInput_type('password') + ->getParent() + ->add_field('change_admin_password') + ->setInput_type('button') + ->setTitle('Change password') + ->setDescription('Changing admin password') + ->getParent() ->add_group( 'Danger Zone' ) ->add_field( 'uninstall' ) ->setInput_type( 'button' ) From c680e96efce26aabdb66a4ea909ca2feace4818f Mon Sep 17 00:00:00 2001 From: alexandergull Date: Fri, 17 Mar 2023 16:43:16 +0500 Subject: [PATCH 19/45] Fix. Get hashes of approved files. Fix URL and version. --- uniforce/lib/Cleantalk/USP/Scanner/Helper.php | 10 ++++------ uniforce/lib/Cleantalk/USP/ScannerController.php | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Scanner/Helper.php b/uniforce/lib/Cleantalk/USP/Scanner/Helper.php index 729f9c6..7b35f9d 100644 --- a/uniforce/lib/Cleantalk/USP/Scanner/Helper.php +++ b/uniforce/lib/Cleantalk/USP/Scanner/Helper.php @@ -97,8 +97,8 @@ static public function get_hashes__signature( $last_signature_update = 0 ) * @return array Array with all files hashes or Error Array */ static public function get_hashes__approved_files($cms, $type, $version) { - - $file_path = 'https://cleantalk-security.s3.amazonaws.com/extensions_checksums/'.$cms.'/'.$type.'/'.$version.'.csv.gz'; + + $file_path = 'https://cleantalk-security.s3.amazonaws.com/extensions_checksums/' . $cms . '/' . $type . '/' . $version . '.csv.gz'; if( \Cleantalk\USP\Uniforce\Helper::http__request($file_path, array(), 'get_code') == 200) { @@ -134,11 +134,9 @@ static public function get_hashes__approved_files($cms, $type, $version) { return $result; }else return array('error' =>'BAD_HASHES_FILE'); - } else { return array('error' => 'Empty hashes file'); } - } else { return array( 'error' => 'COULDNT_UNPACK' ); } @@ -146,8 +144,8 @@ static public function get_hashes__approved_files($cms, $type, $version) { return array( 'error' => 'Function gzdecode not exists. Please update your PHP to version 5.4' ); } } - }else - return array('error' =>'REMOTE_FILE_NOT_FOUND'); + } + return array('error' =>'REMOTE_FILE_NOT_FOUND'); } /** diff --git a/uniforce/lib/Cleantalk/USP/ScannerController.php b/uniforce/lib/Cleantalk/USP/ScannerController.php index cfc4971..c50432b 100644 --- a/uniforce/lib/Cleantalk/USP/ScannerController.php +++ b/uniforce/lib/Cleantalk/USP/ScannerController.php @@ -442,7 +442,7 @@ public function action__scanner__surface_analysis( $offset = null, $amount = nul */ public function action__scanner__get_approved() { - $result = ScannerHelper::get_hashes__approved_files('usp','approved', '1.0.0'); + $result = ScannerHelper::get_hashes__approved_files('usp','approved', SPBCT_VERSION); if (empty($result['error'])) { From a67546b618d2e72e95a4f3513f337eb3ccf3254d Mon Sep 17 00:00:00 2001 From: alexandergull Date: Thu, 30 Mar 2023 11:48:07 +0500 Subject: [PATCH 20/45] Fix. Cron tasks init on install fixed. --- uniforce/lib/Cleantalk/USP/Common/Cron.php | 4 +++- uniforce/lib/Cleantalk/USP/Uniforce/Cron.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Common/Cron.php b/uniforce/lib/Cleantalk/USP/Common/Cron.php index b69c516..39f99a2 100644 --- a/uniforce/lib/Cleantalk/USP/Common/Cron.php +++ b/uniforce/lib/Cleantalk/USP/Common/Cron.php @@ -148,6 +148,8 @@ public function runTasks() $error = Err::get_last('string')['error']; } + Err::add('CRON: can not execute task: ' . $error); + $this->tasks_completed[$task] = false; } @@ -180,7 +182,7 @@ public function runTasks() if(isset($this->tasks[$task], $this->tasks_completed[$task])){ $this->tasks[$task]['next_call'] = $this->tasks_completed[$task] ? time() + $this->tasks[$task]['period'] - : time() + round($this->tasks[$task]['period']/4); + : time() + (int)round($this->tasks[$task]['period']/4); } if(empty($this->tasks[$task]['next_call']) || $this->tasks[$task]['next_call'] < time()){ diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php b/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php index 54fb20e..8d17969 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php @@ -16,7 +16,7 @@ public static function getTasks(){ if( ! file_exists( self::CRON_FILE ) ){ file_put_contents( self::CRON_FILE, - " Date: Sat, 29 Apr 2023 21:50:43 +0500 Subject: [PATCH 21/45] Fix. Show error if key is not valid. --- uniforce/inc/admin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uniforce/inc/admin.php b/uniforce/inc/admin.php index bc98508..aa40a7c 100644 --- a/uniforce/inc/admin.php +++ b/uniforce/inc/admin.php @@ -621,7 +621,7 @@ function usp_check_account_status( $key = null ){ $usp->data->save(); $usp->settings->save(); - return $usp->valid; + return $usp->data->valid; } /** From 564e3ffcf0fcc5fda8e4d2abed6c3df6157b5f56 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Sat, 29 Apr 2023 21:51:40 +0500 Subject: [PATCH 22/45] Fix. Helper. HTTP request. Try to connect via HTTP 2.0 if 1.0 has been failed. --- uniforce/lib/Cleantalk/USP/Common/API.php | 19 +++++++++++++------ uniforce/lib/Cleantalk/USP/Common/Helper.php | 3 +++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Common/API.php b/uniforce/lib/Cleantalk/USP/Common/API.php index 6e98fba..2a03e01 100644 --- a/uniforce/lib/Cleantalk/USP/Common/API.php +++ b/uniforce/lib/Cleantalk/USP/Common/API.php @@ -652,15 +652,22 @@ static public function send_request($data, $url = self::URL, $ssl = false) $data['agent'] = static::get_agent(); // Add ssl to 'presets' if enabled - if( $ssl ) - array_push( $presets, 'ssl' ); - + if( $ssl ){ + array_push( $presets, 'ssl' ); + } + $result = Helper::http__request( $url, $data, $presets ); // Retry with SSL enabled if failed - if( ! empty ( $result['error'] ) && $ssl === false ) - $result = Helper::http__request( $url, $data, 'api ssl' ); - + if( ! empty ( $result['error'] ) && $ssl === false ) { + $result = Helper::http__request( $url, $data, 'api ssl' ); + } + + //Retry with HTTP 20 if failed + if( ! empty ( $result['error'] )) { + $result = Helper::http__request( $url, $data, 'api http_20' ); + } + return $result; } diff --git a/uniforce/lib/Cleantalk/USP/Common/Helper.php b/uniforce/lib/Cleantalk/USP/Common/Helper.php index 041ec73..181f24b 100644 --- a/uniforce/lib/Cleantalk/USP/Common/Helper.php +++ b/uniforce/lib/Cleantalk/USP/Common/Helper.php @@ -541,6 +541,9 @@ static public function http__request($url, $data = array(), $presets = null, $op $opts[CURLOPT_POSTFIELDS] = null; $opts[CURLOPT_HEADER] = false; break; + case 'http_20': + $opts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; + break; default: From b2fddee44a15238f4064edb86a323c793ddbbc15 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Sat, 29 Apr 2023 22:39:23 +0500 Subject: [PATCH 23/45] Fix. FW update. Skip ivp6 networks. --- uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php index 54ce5e7..d4b8958 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php @@ -125,7 +125,6 @@ public function check() { $results[] = $result_entry; } - // Not in base }else { @@ -500,6 +499,11 @@ public static function update__write_to_db( $file_url ){ if( in_array($entry[0], $networks_to_skip ) ){ continue; } + + //skip ipv6 because of reasons :( + if ( !is_numeric($entry[0]) ){ + continue; + } $nets_for_save[] = array( 'network' => $entry[0], From 927dc14b699aa5f1dac8e42c6328c4b6adccfb79 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Sat, 29 Apr 2023 23:23:26 +0500 Subject: [PATCH 24/45] New. Skip status 99 for included networks. --- uniforce/lib/Cleantalk/USP/Common/API.php | 1 + uniforce/lib/Cleantalk/USP/Security/Firewall.php | 11 ++++++++--- uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Common/API.php b/uniforce/lib/Cleantalk/USP/Common/API.php index 2a03e01..5408e7c 100644 --- a/uniforce/lib/Cleantalk/USP/Common/API.php +++ b/uniforce/lib/Cleantalk/USP/Common/API.php @@ -380,6 +380,7 @@ static public function method__security_firewall_data_file($api_key, $out = null $request = array( 'auth_key' => $api_key, 'method_name' => 'security_firewall_data_file', + 'version' => 2 ); if( $out ) diff --git a/uniforce/lib/Cleantalk/USP/Security/Firewall.php b/uniforce/lib/Cleantalk/USP/Security/Firewall.php index 46090fb..87071c2 100644 --- a/uniforce/lib/Cleantalk/USP/Security/Firewall.php +++ b/uniforce/lib/Cleantalk/USP/Security/Firewall.php @@ -184,13 +184,19 @@ private function prioritize( $results ){ $status_priority_from_table = array_search($fw_result['status'], $this->statuses_priority); $is_personal_flag = isset($fw_result['is_personal']) && $fw_result['is_personal']; $is_trusted_network_flag = isset($fw_result['status']) && $fw_result['status'] == 'PASS_BY_TRUSTED_NETWORK'; + $is_skipped_network_flag = isset($fw_result['status']) && $fw_result['status'] == 'PASS_AS_SKIPPED_NETWORK'; //used to gain maximum priority $total_count_of_statuses = count($this->statuses_priority); $current_record_priority = $status_priority_from_table; if ( $is_personal_flag || $is_trusted_network_flag ) { - //set maximum priority + // set maximum priority $current_record_priority += $total_count_of_statuses; + // set maximum priority if skipped network (status 99 - PASS_AS_SKIPPED_NETWORK) + if ( $is_skipped_network_flag ) { + $current_record_priority++; + } } + $final_status = $fw_result['status'] === 'PASS_AS_SKIPPED_NETWORK' ? 'PASS' : $fw_result['status']; //set new final priority if it is less than current record priority if ( $current_record_priority >= $final_priority ) { $final_priority = $current_record_priority; @@ -200,7 +206,7 @@ private function prioritize( $results ){ // Necessary params 'module' => $fw_result['module'], 'ip' => $fw_result['ip'], - 'status' => $fw_result['status'], + 'status' => $final_status, // FW 'is_personal' => !empty( $fw_result['is_personal'] ) ? (int)$fw_result['is_personal'] : 0, @@ -217,7 +223,6 @@ private function prioritize( $results ){ } } } - return $result; } diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php index d4b8958..4c148c1 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php @@ -121,6 +121,8 @@ public function check() { case -2: $result_entry = array_merge( $result_entry, array('status' => 'DENY_BY_DOS', ) ); break; case -3: $result_entry = array_merge( $result_entry, array('status' => 'DENY_BY_SEC_FW', ) ); break; case -4: $result_entry = array_merge( $result_entry, array('status' => 'DENY_BY_SPAM_FW', ) ); break; + case 99: $result_entry = array_merge( $result_entry, array('status' => 'PASS_AS_SKIPPED_NETWORK', ) ); break; + default: $result_entry = array_merge( $result_entry, array('status' => 'PASS', ) ); break; } $results[] = $result_entry; From 83255d5d4dd89b344cb651d573dad299b326deba Mon Sep 17 00:00:00 2001 From: Glomberg Date: Tue, 15 Aug 2023 10:50:26 +0300 Subject: [PATCH 25/45] Fix. Scanner. Large file size checking. --- uniforce/lib/Cleantalk/USP/Scanner/Scanner.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php b/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php index bae5da6..94dc272 100644 --- a/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php +++ b/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php @@ -7,6 +7,8 @@ class Scanner { + const FILE_MAX_SIZE = 2621440; // 2.5 MB + public $path = ''; // Main path public $path_lenght = 0; @@ -289,7 +291,7 @@ public function file__details($file_list, $path_offset) // Full hash $this->files[$key]['full_hash'] = is_readable($val['path']) - ? md5_file($val['path']) + ? $this->files[$key]['size'] > self::FILE_MAX_SIZE ? 'file_is_too_big' : md5_file($val['path']) : 'unknown'; } @@ -337,6 +339,10 @@ static public function file__scan__for_signatures($root_path, $file_info, $signa } if( in_array( $signature['type'], array('CODE_PHP', 'CODE_JS', 'CODE_HTML' ) ) ) { + if ( filesize($root_path . $file_info['path']) > self::FILE_MAX_SIZE ) { + // File is too big to getting the content + continue; + } $file_content = file_get_contents( $root_path . $file_info['path'] ); $is_regexp = preg_match( '/^\/.*\/$/', $signature['body'] ); if( From 132cd4b6f201dd65adf9b96fbb4992c5df7cd1b0 Mon Sep 17 00:00:00 2001 From: SVFCode Date: Mon, 21 Aug 2023 08:48:17 +0300 Subject: [PATCH 26/45] Update. SecFW. Updating database without pause in the protection. (#14) * Update. SecFW. Update database without pause in protect. * Fix. SFW. Delete temp data on uninstall. --- uniforce/inc/admin.php | 2 + uniforce/lib/Cleantalk/USP/File/FileDB.php | 111 ++++++++++++++++-- uniforce/lib/Cleantalk/USP/File/Storage.php | 36 ++++++ .../Cleantalk/USP/Uniforce/Firewall/FW.php | 30 ++++- uniforce/uniforce.php | 2 +- 5 files changed, 161 insertions(+), 20 deletions(-) diff --git a/uniforce/inc/admin.php b/uniforce/inc/admin.php index aa40a7c..c9b96f1 100644 --- a/uniforce/inc/admin.php +++ b/uniforce/inc/admin.php @@ -330,6 +330,7 @@ function usp_uninstall(){ // Deleting FW data $db = new \Cleantalk\USP\File\FileDB( 'fw_nets' ); $db->delete(); + $db->deleteTemp(); // Deleting options and their files $usp->delete( 'data' ); @@ -561,6 +562,7 @@ function usp_do_save_settings() { // Deleting FW data $db = new \Cleantalk\USP\File\FileDB( 'fw_nets' ); $db->delete(); + $db->deleteTemp(); State::getInstance()->data->save(); Cron::removeTask( 'sfw_update' ); Cron::removeTask( 'fw_send_logs' ); diff --git a/uniforce/lib/Cleantalk/USP/File/FileDB.php b/uniforce/lib/Cleantalk/USP/File/FileDB.php index f174821..4463fe1 100644 --- a/uniforce/lib/Cleantalk/USP/File/FileDB.php +++ b/uniforce/lib/Cleantalk/USP/File/FileDB.php @@ -26,8 +26,14 @@ class FileDB { */ private $meta; + /** + * @var \Cleantalk\USP\Common\Storage + */ + private $meta_temp; + private $indexes_description; private $indexes; + private $indexes_temp; private $indexed_column; private $index_type; @@ -59,18 +65,27 @@ public function __construct( $db_name ) { $this->getIndexes(); } } + + $this->getMetaDataTemp(); // @todo handle error + + if (! $this->meta_temp->is_empty()) { + $this->storage = new \Cleantalk\USP\File\Storage( $db_name, $this->meta_temp->cols ); + if ($this->meta_temp->indexes) { + $this->getIndexesTemp(); + } + } } - - public function insert( $data ){ + + public function insertTemp( $data ){ $inserted = 0; for( $number = 0; isset( $data[ $number ] ); $number++ ){ - switch ( $this->addIndex( $number + 1, $data[ $number ] ) ){ + switch ( $this->addIndexTemp( $number + 1, $data[ $number ] ) ){ case true: - if( $this->storage->put( $data[ $number ] ) ){ + if( $this->storage->putTemp( $data[ $number ] ) ){ $inserted++; } break; @@ -82,12 +97,12 @@ public function insert( $data ){ } - $this->meta->rows += $inserted; - $this->meta->save(); + $this->meta_temp->rows += $inserted; + $this->meta_temp->save(); return $inserted; } - + public function delete() { // Clear indexes @@ -118,6 +133,37 @@ public function delete() { // Clear and delete a storage $this->storage->delete(); } + + public function deleteTemp() { + + // Clear indexes + if( $this->meta->indexes ){ + + foreach( $this->meta->indexes as &$index ){ + + // @todo make multiple indexes support + $column_to_index = $index['columns'][0]; + + switch( $index['type'] ){ + case 'bintree': + $this->indexes_temp[ $column_to_index ]->clear_tree(); + break; + case 'btree': + $this->indexes_temp[ $column_to_index ]->clear(); + break; + } + $index['status'] = false; + } unset( $index ); + + } + + // Reset rows amount + $this->meta_temp->rows = 0; + $this->meta_temp->save(); + + // Clear and delete a storage + $this->storage->deleteTemp(); + } /** * Set what columns to select @@ -341,6 +387,19 @@ private function getMetaData(){ $this->meta->cols_num = count( $this->meta->cols ); } } + + /** + * Getting metadata for temp data and creates new file if not exists + */ + private function getMetaDataTemp(){ + + $this->meta_temp = new Storage( $this->name . '_meta', null ); + + if( ! $this->meta_temp->is_empty() ){ + $this->meta_temp->line_length = array_sum( array_column( $this->meta_temp->cols, 'length' ) ); + $this->meta_temp->cols_num = count( $this->meta_temp->cols ); + } + } private function getIndexes() { @@ -369,10 +428,38 @@ function($result, $item){ return $result . ucfirst( $item ); } } } + + private function getIndexesTemp() { + + foreach( $this->meta_temp->indexes as $index ){ + // Index file name = databaseName_allColumnsNames.indexType + $index_name = + $this->name + . '_' . lcfirst( array_reduce( + $index['columns'], + function($result, $item){ return $result . ucfirst( $item ); } + ) ) + . '_temp.' . $index['type']; + + // @todo extend indexes on a few columns + switch( $index['type'] ){ + + case 'bintree': + $this->indexes_temp[ $index['columns'][0] ] = new BinaryTree( self::FS_PATH . $index_name ); + break; + + case 'btree': + $this->indexes_temp[ $index['columns'][0] ] = new BTree( self::FS_PATH . $index_name ); + break; + } + + } + + } - private function addIndex( $number, $data ) { + private function addIndexTemp( $number, $data ) { - foreach ( $this->meta->indexes as $key => &$index ){ + foreach ( $this->meta_temp->indexes as $key => &$index ){ // @todo this is a crunch $column_to_index = $index['columns'][0]; @@ -380,10 +467,10 @@ private function addIndex( $number, $data ) { switch ( $index['type'] ){ case 'bintree': - $result = $this->indexes[ $column_to_index ]->add_key( $value_to_index, $this->meta->rows + $number ); + $result = $this->indexes_temp[ $column_to_index ]->add_key( $value_to_index, $this->meta_temp->rows + $number ); break; case 'btree': - $result = $this->indexes[ $column_to_index ]->put( $value_to_index, $this->meta->rows + $number ); + $result = $this->indexes_temp[ $column_to_index ]->put( $value_to_index, $this->meta_temp->rows + $number ); break; default: $result = false; @@ -394,10 +481,8 @@ private function addIndex( $number, $data ) { $index['status'] = 'ready'; $out = true; }elseif( $result === true ){ -// Err::add('Insertion', 'Duplicate key for column "' . $index . '": ' . $data[ array_search( $index, $columns_name ) ] ); $out = false; }elseif( $result === false ){ -// Err::add('Insertion', 'No index added for column "' . $index . '": ' . array_search( $index, $columns_name ) ); $out = false; }else{ $out = false; diff --git a/uniforce/lib/Cleantalk/USP/File/Storage.php b/uniforce/lib/Cleantalk/USP/File/Storage.php index 1a53443..d2cbfa0 100644 --- a/uniforce/lib/Cleantalk/USP/File/Storage.php +++ b/uniforce/lib/Cleantalk/USP/File/Storage.php @@ -12,11 +12,13 @@ class Storage { private $name; private $path; + private $path_temp; /** * @var false|resource */ private $stream; + private $stream_temp; private $cols; private $line_length; @@ -35,10 +37,12 @@ public function __construct( $name, $cols, $folder = null ){ $this->folder = $folder ?: CT_USP_ROOT . 'data' . DIRECTORY_SEPARATOR; $this->name = $name; $this->path = $this->folder . $name . '.storage'; + $this->path_temp = $this->folder . $name . '_temp.storage'; $this->cols = $cols; $this->line_length = array_sum( array_column( $this->cols, 'length' ) ); $this->stream = fopen( $this->path, 'a+b' ); + $this->stream_temp = fopen( $this->path_temp, 'a+b' ); } /** @@ -65,6 +69,31 @@ public function put( $row ) { return (bool) $res; } + + /** + * @param $row + * + * @return bool|int + */ + public function putTemp( $row ) { + + $res = false; + + if( + $this->checkRowFormat( $row ) && + $this->covertRowToRaw( $row ) + ){ + fseek( $this->stream, 0, SEEK_END ); + $res = fwrite( $this->stream_temp, $this->input_buffer . $this->row_separator ); + } + + if ( ! $res ){ + $err = error_get_last(); + Err::add( $err['message'] ); + } + + return (bool) $res; + } /** * @return bool @@ -72,6 +101,13 @@ public function put( $row ) { public function delete(){ return ftruncate( $this->stream, 0 ) && unlink( $this->path ); } + + /** + * @return bool + */ + public function deleteTemp(){ + return ftruncate( $this->stream_temp, 0 ) && unlink( $this->path_temp ); + } private function checkRowFormat( $data ){ diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php index 4c148c1..c891a17 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php @@ -281,7 +281,7 @@ public static function update( $api_key ){ if( empty( $download_files_result['error'] ) ){ State::getInstance()->fw_stats->update_percent = 0; - State::getInstance()->fw_stats->entries = 0; + State::getInstance()->fw_stats->entries_temp = 0; State::getInstance()->fw_stats->update_start = time(); State::getInstance()->fw_stats->save(); @@ -315,7 +315,7 @@ public static function update( $api_key ){ } //Increment firewall entries - State::getInstance()->fw_stats->entries += $result; + State::getInstance()->fw_stats->entries_temp += $result; State::getInstance()->fw_stats->update_percent = round( ( ( (int) $current_file_num + 1 ) / (int) $url_count ), 2) * 100; State::getInstance()->fw_stats->save(); @@ -350,13 +350,19 @@ public static function update( $api_key ){ if( empty( $result['error'] ) ){ //Increment firewall entries - State::getInstance()->fw_stats->entries += $result; + State::getInstance()->fw_stats->entries_temp += $result; + State::getInstance()->fw_stats->entries = State::getInstance()->fw_stats->entries_temp; State::getInstance()->fw_stats->updating = false; State::getInstance()->fw_stats->update_percent = 0; State::getInstance()->fw_stats->last_update = time(); State::getInstance()->fw_stats->updated_in = time() - State::getInstance()->fw_stats->update_start; State::getInstance()->fw_stats->save(); + if ($url_count === $current_file_num) { + rename(CT_USP_ROOT . DS . 'data' . DS . 'fw_nets_network_temp.btree', CT_USP_ROOT . DS . 'data' . DS . 'fw_nets_network.btree'); + rename(CT_USP_ROOT . DS . 'data' . DS . 'fw_nets_temp.storage', CT_USP_ROOT . DS . 'data' . DS . 'fw_nets.storage'); + } + }else return $result; } @@ -437,7 +443,7 @@ static public function update__get_multifiles( $spbc_key ){ if($data !== false){ - $result__clear_db = self::clear_data(); + $result__clear_db = self::clear_temp_data(); if( empty( $result__clear_db['error'] ) ){ @@ -520,7 +526,7 @@ public static function update__write_to_db( $file_url ){ if( ! empty( $nets_for_save ) ){ - $inserted += $db->insert( $nets_for_save ); + $inserted += $db->insertTemp( $nets_for_save ); if ( Err::check() ){ Err::prepend( 'Updating FW' ); @@ -572,7 +578,7 @@ static public function update__write_to_db__exclusions( $exclusions = array() ){ if( isset( $nets_for_save ) ){ - $inserted = $db->insert( $nets_for_save ); + $inserted = $db->insertTemp( $nets_for_save ); if ( Err::check() ){ Err::prepend('Updating FW exclusions'); @@ -600,4 +606,16 @@ public static function clear_data() { return array( 'success' => true, ); } + + /** + * Clear temp SFW table + * + * @return bool[] + */ + public static function clear_temp_data() { + $db = new FileDB( 'fw_nets' ); + $db->deleteTemp(); + + return array( 'success' => true, ); + } } \ No newline at end of file diff --git a/uniforce/uniforce.php b/uniforce/uniforce.php index 0d1db43..d434565 100644 --- a/uniforce/uniforce.php +++ b/uniforce/uniforce.php @@ -25,7 +25,7 @@ // Security FireWall $firewall = new \Cleantalk\USP\Uniforce\Firewall(); - if( $usp->settings->fw && ! $usp->fw_stats->updating && $usp->fw_stats->entries ) + if( $usp->settings->fw && $usp->fw_stats->entries ) $firewall->module__load( new \Cleantalk\USP\Uniforce\Firewall\FW( array( 'state' => $usp, From 76c86c9fcc8746a84ddf679206131b72d4579903 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Mon, 28 Aug 2023 21:33:31 +0500 Subject: [PATCH 27/45] Version: 3.8.0 --- uniforce/inc/common.php | 4 ++-- uniforce/index.php | 2 +- uniforce/version.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/uniforce/inc/common.php b/uniforce/inc/common.php index 42f03ff..0e419d2 100644 --- a/uniforce/inc/common.php +++ b/uniforce/inc/common.php @@ -5,7 +5,7 @@ * * Sets all main constants * - * Version: 3.7.0 + * Version: 3.8.0 */ use Cleantalk\USP\Common\State; @@ -13,7 +13,7 @@ use Cleantalk\USP\Common\RemoteCalls; if( ! defined( 'SPBCT_PLUGIN' ) ) define( 'SPBCT_PLUGIN', 'uniforce' ); -if( ! defined( 'SPBCT_VERSION' ) ) define( 'SPBCT_VERSION', '3.7.0' ); +if( ! defined( 'SPBCT_VERSION' ) ) define( 'SPBCT_VERSION', '3.8.0' ); if( ! defined( 'SPBCT_AGENT' ) ) define( 'SPBCT_AGENT', SPBCT_PLUGIN . '-' . str_replace( '.', '', SPBCT_VERSION ) ); if( ! defined( 'SPBCT_USER_AGENT' ) ) define( 'SPBCT_USER_AGENT', 'Cleantalk-Security-Universal-Plugin/' . SPBCT_VERSION ); diff --git a/uniforce/index.php b/uniforce/index.php index 8561766..1bba9fb 100644 --- a/uniforce/index.php +++ b/uniforce/index.php @@ -1,6 +1,6 @@ Date: Mon, 28 Aug 2023 22:08:23 +0500 Subject: [PATCH 28/45] Fix. FW ogs files array statement check. --- uniforce/inc/admin.php | 62 ++-- uniforce/inc/settings.php | 14 +- .../Cleantalk/USP/Uniforce/Firewall/FW.php | 268 +++++++++--------- 3 files changed, 172 insertions(+), 172 deletions(-) diff --git a/uniforce/inc/admin.php b/uniforce/inc/admin.php index c9b96f1..c73593a 100644 --- a/uniforce/inc/admin.php +++ b/uniforce/inc/admin.php @@ -156,9 +156,9 @@ function usp_do_install() { * @param $exclusions */ function usp_install($files, $api_key, $cms, $exclusions ){ - + foreach ($files as $file){ - + $file_content = file_get_contents( $file ); // Check if short PHP tags used if( preg_match( "/<\?[^(php)]/", $file_content ) ) { @@ -172,13 +172,13 @@ function usp_install($files, $api_key, $cms, $exclusions ){ // Adding \n", 'start'); - + if( ! Err::check() ){ - + // Adding ? > to the end if it's not there if($php_open_tags <= $php_close_tags) File::inject__code($file, "\n$open_php_tag\n" . PHP_EOL, 'end'); - + if( ! Err::check() ){ // Addition to the top of the script @@ -188,9 +188,9 @@ function usp_install($files, $api_key, $cms, $exclusions ){ '(<\?php)|(<\?)', 'top_code' ); - + if( ! Err::check() ){ - + // Addition to index.php Bottom (JavaScript test) File::inject__code( $file, @@ -201,7 +201,7 @@ function usp_install($files, $api_key, $cms, $exclusions ){ 'end', 'bottom_code' ); - + } } } @@ -210,7 +210,7 @@ function usp_install($files, $api_key, $cms, $exclusions ){ // Install settings in cofig if everything is ok if( ! Err::check() ) usp_install_config( $files, $api_key, $cms, $exclusions ); - + // Set cron tasks if( ! Err::check() ) usp_install_cron(); @@ -319,7 +319,7 @@ function usp_install_cron(){ * @return bool */ function usp_uninstall(){ - + $usp = State::getInstance(); foreach ( $usp->data->modified_files as $file ){ @@ -340,7 +340,7 @@ function usp_uninstall(){ $usp->delete( 'signatures' ); $usp->delete( 'fw_stats' ); $usp->delete( 'plugin_meta' ); - + $usp->delete( 'bfp_blacklist' ); $usp->delete( 'bfp_blacklist_fast' ); @@ -350,7 +350,7 @@ function usp_uninstall(){ // Deleting any logs usp_uninstall_logs(); - setcookie('authentificated', 0, time()-86400, '/', null, false, true); + setcookie('authentificated', 0, time()-86400, '/', '', false, true); return ! Err::check(); @@ -383,12 +383,12 @@ function usp_uninstall_logs() { * @return array */ function usp_detect_cms($path_to_index, $out = array( 'name' => 'Unknown', 'admin_page' => '' ) ){ - + if( is_file($path_to_index) ){ - + // Detecting CMS $index_file = file_get_contents( $path_to_index ); - + //X-Cart 4 if (preg_match('/(xcart_4_.*?)/', $index_file)) $out = array( 'name' => 'X-Cart 4', 'admin_page' => '' ); @@ -430,7 +430,7 @@ function usp_detect_cms($path_to_index, $out = array( 'name' => 'Unknown', 'admi $out = array( 'name' => 'phpBB', 'admin_page' => '/' ); } - + return $out; } @@ -452,10 +452,10 @@ function usp_do_login($apikey, $password, $email ) { if( $password ){ if( ( Post::get( 'login' ) == $apikey || Post::get( 'login' ) === $email ) && hash( 'sha256', trim( Post::get( 'password' ) ) ) == $password ) - setcookie('authentificated', State::getInstance()->data->security_key, 0, '/', null, false, true); + setcookie('authentificated', State::getInstance()->data->security_key, 0, '/', '', false, true); else Err::add('Incorrect login or password'); - + // No match }else Err::add('Incorrect login'); @@ -471,7 +471,7 @@ function usp_do_login($apikey, $password, $email ) { */ function usp_do_logout() { - setcookie('authentificated', 0, time()-86400, '/', null, false, true); + setcookie('authentificated', 0, time()-86400, '/', '', false, true); die( json_encode( array( 'success' => true ) ) ); } @@ -492,10 +492,10 @@ function usp_do_save_settings() { : $value; settype($settings[$setting], gettype($value)); } unset($setting, $value); - + // Recognizing new key $new_key_is_set = $usp->settings->key !== $settings['key']; - + // Set values foreach ( $settings as $setting => $value) { $usp->settings->$setting = $value; @@ -503,7 +503,7 @@ function usp_do_save_settings() { // validate the new key $usp->data->key_is_ok = usp_check_account_status(); - + // BFP actions if( $usp->settings->key ){ @@ -515,27 +515,27 @@ function usp_do_save_settings() { $usp->data->stat->bfp->count = 0; } } - + if( $new_key_is_set ){ $scanner_controller = new \Cleantalk\USP\ScannerController( CT_USP_SITE_ROOT ); $scanner_controller->action__scanner__create_db(); } - + // Update signatures if( $usp->settings->scanner_signature_analysis ){ $scanner_controller = new \Cleantalk\USP\ScannerController( CT_USP_SITE_ROOT ); $scanner_controller->action__scanner__get_signatures(); } - + $usp->data->save(); $usp->settings->save(); - + // FireWall actions // Last in the list because it can overwrite the data in the the remote call it makes if( ( $usp->settings->fw || $usp->settings->waf ) && $usp->settings->key ){ - + // Update SFW Helper::http__request( Server::get('HTTP_HOST') . CT_USP_AJAX_URI, @@ -547,16 +547,16 @@ function usp_do_save_settings() { ), 'get async' ); - + // Send FW logs $result = \Cleantalk\USP\Uniforce\Firewall\FW::send_log( $usp->settings->key ); - + if( empty( $result['error'] ) && ! Err::check() ) { $usp->fw_stats->logs_sent_time = time(); $usp->fw_stats->logs_sent_amount = $result['rows']; $usp->fw_stats->save(); } - + // Cleaning up Firewall data } else { // Deleting FW data @@ -632,7 +632,7 @@ function usp_check_account_status( $key = null ){ */ function usp_do_uninstall() { - setcookie('authentificated', 0, time()-86400, '/', null, false, true); + setcookie('authentificated', 0, time()-86400, '/', '', false, true); usp_uninstall(); diff --git a/uniforce/inc/settings.php b/uniforce/inc/settings.php index e10da92..fa23a63 100644 --- a/uniforce/inc/settings.php +++ b/uniforce/inc/settings.php @@ -61,24 +61,24 @@ function usp_settings__show_fw_statistics( $out = '' ) } function usp_settings__show_scanner_statistics(){ - + $usp = State::getInstance(); $stat = State::getInstance()->data->stat; - + if( State::getInstance()->data->no_sql ) echo ''; - + echo 'Last scan: ' . ( $stat->scanner->last_scan ? date('M d Y H:i:s', $stat->scanner->last_scan) : 'never' ) . '
'; echo 'Number of scanned files at the last scan: ' . $stat->scanner->last_scan_amount . '
'; - + echo '
'; - + echo 'Signature last update: ' . ( $stat->scanner->signature_last_update ? date('M d Y H:i:s', $stat->scanner->signature_last_update) : 'never.' ) . '
'; echo 'Signatures in local base: ' . $stat->scanner->signature_entries . '.
'; - -} \ No newline at end of file + +} diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php index 8f88421..42af462 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php @@ -12,22 +12,22 @@ use Cleantalk\USP\File\FileDB; class FW extends \Cleantalk\USP\Uniforce\Firewall\FirewallModule { - + // Domains which are skipping exclusions private static $test_domains = array( 'lc', 'loc', 'lh', 'test' ); - + public $module_name = 'FW'; - + /** * @var bool */ protected $test; - + // Additional params protected $api_key = ''; - + protected $real_ip; - + /** * FireWall_module constructor. * Use this method to prepare any data for the module working. @@ -35,18 +35,18 @@ class FW extends \Cleantalk\USP\Uniforce\Firewall\FirewallModule { * @param array $params */ public function __construct( $params = array() ){ - + parent::__construct( $params ); - + } - + /** * @param $ips */ public function ip__append_additional( &$ips ){ - + $this->real_ip = isset( $ips['real'] ) ? $ips['real'] : null; - + if( Get::get('spbct_test') == md5( $this->api_key ) ){ $ip_type = Helper::ip__validate( Get::get('spbct_test_ip') ); $test_ip = $ip_type == 'v6' ? Helper::ip__v6_normalize( Get::get('spbct_test_ip') ) : Get::get('spbct_test_ip'); @@ -57,23 +57,23 @@ public function ip__append_additional( &$ips ){ } } } - + /** * Check every IP using FireWall data table. * * @return array */ public function check() { - + $results = array(); - + foreach( $this->ip_array as $ip_origin => $current_ip ) { - + $ip_type = Helper::ip__validate($current_ip); - + // IPv4 query if( $ip_type && $ip_type === 'v4' ){ - + $current_ip_v4 = sprintf( "%u", ip2long( $current_ip ) ); // Creating IPs to search for ( $needles = array(), $m = 6; $m <= 32; $m ++ ) { @@ -82,13 +82,13 @@ public function check() { $needles[] = sprintf( "%u", bindec( $mask & base_convert( $current_ip_v4, 10, 2 ) ) ); } $needles = array_unique( $needles ); - + $db = new FileDB( 'fw_nets' ); $db_results = $db ->setWhere( array( 'network' => $needles, ) ) ->setLimit( 0, 20 ) ->select( 'network', 'mask', 'status', 'is_personal' ); - + for( $i = 0; isset( $db_results[ $i ] ); $i ++ ){ if( ! Helper::ip__mask_match( $current_ip, @@ -98,12 +98,12 @@ public function check() { } } } - + // In base if( ! empty( $db_results ) ) { - + foreach( $db_results as $entry ) { - + $result_entry = array( 'module' => $this->module_name, 'ip' => $current_ip, @@ -112,7 +112,7 @@ public function check() { 'network' => $entry['network'], 'mask' => $entry['mask'], ); - + switch ( $entry['status'] ) { case 2: $result_entry = array_merge( $result_entry, array('status' => 'PASS_BY_TRUSTED_NETWORK', ) ); break; case 1: $result_entry = array_merge( $result_entry, array('status' => 'PASS_BY_WHITELIST', ) ); break; @@ -124,12 +124,12 @@ public function check() { case 99: $result_entry = array_merge( $result_entry, array('status' => 'PASS_AS_SKIPPED_NETWORK', ) ); break; default: $result_entry = array_merge( $result_entry, array('status' => 'PASS', ) ); break; } - + $results[] = $result_entry; } // Not in base }else { - + $results[] = array( 'module' => $this->module_name, 'ip' => $current_ip, @@ -139,15 +139,15 @@ public function check() { 'mask' => null, 'status' => 'PASS', ); - + } - + } - + return $results; - + } - + /** * Sends and wipe SFW log * @@ -156,35 +156,35 @@ public function check() { * @return array|bool array('error' => STRING) */ public static function send_log( $ct_key ){ - + $log_dir_path = CT_USP_ROOT . 'data/fw_logs'; - + if( ! is_dir( $log_dir_path ) ) return array( 'rows' => 0 ); - + $log_files = array_diff( scandir( $log_dir_path ), array( '.', '..', 'index.php' ) ); - + if( ! empty( $log_files ) ){ - + //Compile logs $data = array(); - + foreach ( $log_files as $log_file ){ - + $log = file_get_contents( $log_dir_path . DS . $log_file ); $log = str_getcsv( $log ); $_log = array( - 'timestamp' => $log[2], - 'page_url' => $log[6], - 'ip' => $log[1], - 'http_user_agent' => $log[7], - 'request_method' => $log[8], - 'x_forwarded_for' => $log[9], - 'is_personal' => $log[10], - 'matched_networks' => $log[11], - 'hits' => $log[5], - 'mask' => $log[12] + 'timestamp' => isset($log[2]) ? $log[2] : '', + 'page_url' => isset($log[6]) ? $log[6] : '', + 'ip' => isset($log[1]) ? $log[1] : '', + 'http_user_agent' => isset($log[7]) ? $log[7] : '', + 'request_method' => isset($log[8]) ? $log[8] : '', + 'x_forwarded_for' => isset($log[9]) ? $log[9] : '', + 'is_personal' => isset($log[10]) ? $log[10] : '', + 'matched_networks' => isset($log[11]) ? $log[11] : '', + 'hits' => isset($log[5]) ? $log[5] : '', + 'mask' => isset($log[12]) ? $log[12] : '' ); //datetime legacy @@ -202,7 +202,7 @@ public static function send_log( $ct_key ){ } else { $_log['timestamp'] = 0; } - + //Compile log $to_data = array( 'datetime' => $_log['datetime'], @@ -240,7 +240,7 @@ public static function send_log( $ct_key ){ case 'DENY_BY_SEC_FW': $to_data['status_efw'] = -8; break; case 'DENY_BY_SPAM_FW': $to_data['status_efw'] = -9; break; } - + switch( $log[3] ){ case 'PASS_BY_TRUSTED_NETWORK': $to_data['status'] = 3; break; case 'PASS_BY_WHITELIST': $to_data['status'] = 2; break; @@ -256,23 +256,23 @@ public static function send_log( $ct_key ){ case 'DENY_BY_SEC_FW': $to_data['status'] = -8; break; case 'DENY_BY_SPAM_FW': $to_data['status'] = -9; break; } - + $data[] = $to_data; - + } unset($key, $value, $result, $to_data); //Sending the request $result = API::method__security_logs__sendFWData( $ct_key, $data ); - + //Checking answer and deleting all lines from the table if( empty( $result['error'] ) ){ - + if( $result['rows'] == count( $data ) ){ - + foreach ( $log_files as $log_file ){ unlink( $log_dir_path . DS . $log_file ); } - + return $result; }else return array( 'error' => 'SENT_AND_RECEIVED_LOGS_COUNT_DOESNT_MACH' ); @@ -281,17 +281,17 @@ public static function send_log( $ct_key ){ }else return array( 'rows' => 0 ); } - + public static function update( $api_key ){ - + $multifile_url = Get::get( 'multifile_url' ); $url_count = Get::get( 'url_count' ); $current_file_num = Get::get( 'current_file_num' ); - + $files = isset( State::getInstance()->fw_stats['updating_folder'] ) ? glob( State::getInstance()->fw_stats['updating_folder'] . DS . '/*csv.gz' ) : array(); - + // Get multifiles if( ! $multifile_url ){ @@ -306,12 +306,12 @@ public static function update( $api_key ){ if( ! empty( $result['error'] ) ){ return $result; } - + $update_folder = self::update__prepare_upd_dir( CT_USP_ROOT . DS . 'fw_files' ); if( ! empty( $update_folder['error'] ) ){ return $update_folder; } - + State::getInstance()->fw_stats->updating_folder = CT_USP_ROOT . DS . 'fw_files'; $download_files_result = Helper::http__download_remote_file__multi( $result['file_urls'], State::getInstance()->fw_stats->updating_folder ); if( empty( $download_files_result['error'] ) ){ @@ -320,14 +320,14 @@ public static function update( $api_key ){ State::getInstance()->fw_stats->entries_temp = 0; State::getInstance()->fw_stats->update_start = time(); State::getInstance()->fw_stats->save(); - + Helper::http__request( Server::get( 'HTTP_HOST' ) . CT_USP_AJAX_URI, array( 'spbc_remote_call_token' => md5( $api_key ), 'spbc_remote_call_action' => 'update_security_firewall', 'plugin_name' => 'spbc', - + // Additional params 'multifile_url' => $result['multifile_url'], 'url_count' => count( $result['file_urls'] ), @@ -335,21 +335,21 @@ public static function update( $api_key ){ ), array( 'get', 'async' ) ); - + }else return $result; - + // Write to DB }elseif( count( $files ) ){ - + $result = self::update__write_to_db( reset( $files ) ); - + if( empty( $result['error'] ) ){ - + if( file_exists(reset($files)) ){ unlink(reset($files)); } - + //Increment firewall entries State::getInstance()->fw_stats->entries_temp += $result; State::getInstance()->fw_stats->update_percent = round( ( ( (int) $current_file_num + 1 ) / (int) $url_count ), 2) * 100; @@ -376,15 +376,15 @@ public static function update( $api_key ){ }else return $result; - + // Write exclusions }else{ - + $result = self::update__write_to_db__exclusions(); usleep( 500000 ); - + if( empty( $result['error'] ) ){ - + //Increment firewall entries State::getInstance()->fw_stats->entries_temp += $result; State::getInstance()->fw_stats->entries = State::getInstance()->fw_stats->entries_temp; @@ -403,23 +403,23 @@ public static function update( $api_key ){ return $result; } } - + public static function update__prepare_upd_dir( $dir_name ){ - + global $spbc; - + if( $dir_name === '' ) { return array( 'error' => 'FW dir can not be blank.' ); } - + $dir_name .= DS; - + if( ! is_dir( $dir_name ) && ! mkdir( $dir_name ) ){ - + return ! is_writable( CT_USP_ROOT ) ? array( 'error' => 'Can not to make FW dir. Low permissions: ' . fileperms( CT_USP_ROOT ) ) : array( 'error' => 'Can not to make FW dir. Unknown reason.' ); - + } else { $files = glob( $dir_name . '/*' ); if( $files === false ){ @@ -434,11 +434,11 @@ public static function update__prepare_upd_dir( $dir_name ){ } } } - + return (bool) file_put_contents( $dir_name . 'index.php', ' $file_url, 'file_urls' => array_column(Helper::buffer__parse__csv($data), 0), ); - + }else return $result__clear_db; }else @@ -509,7 +509,7 @@ static public function update__get_multifiles( $spbc_key ){ }else return $result; } - + /** * Writes entries from remote files to Firewall database. * @@ -518,27 +518,27 @@ static public function update__get_multifiles( $spbc_key ){ * @return array|bool|int|mixed|string */ public static function update__write_to_db( $file_url ){ - + $data = Helper::get_data_from_local_gz( $file_url ); - + if ( ! Err::check() ) { - + $db = new FileDB( 'fw_nets' ); $networks_to_skip = array(); if( in_array( Server::get_domain(), self::$test_domains ) ){ $networks_to_skip[] = ip2long( '127.0.0.1' ); } - - + + $inserted = 0; while( $data !== '' ){ - + for( $i = 0, $nets_for_save = array(); $i < 2500 && $data !== ''; $i++ ){ - + $entry = Helper::buffer__csv__pop_line_to_array( $data ); if( in_array($entry[0], $networks_to_skip ) ){ continue; @@ -548,7 +548,7 @@ public static function update__write_to_db( $file_url ){ if ( !is_numeric($entry[0]) ){ continue; } - + $nets_for_save[] = array( 'network' => $entry[0], 'mask' => sprintf( '%u', bindec( str_pad( str_repeat( '1', $entry[1] ), 32, 0, STR_PAD_RIGHT ) ) ), @@ -557,29 +557,29 @@ public static function update__write_to_db( $file_url ){ 'is_personal' => isset( $entry[4] ) ? intval( $entry[4] ) : 0, // 'country' => isset( $entry[5] ) ? trim( $entry[5], '"' ) : 0, ); - + } - + if( ! empty( $nets_for_save ) ){ - + $inserted += $db->insertTemp( $nets_for_save ); - + if ( Err::check() ){ Err::prepend( 'Updating FW' ); error_log( var_export( Err::get_all( 'string' ), true ) ); return array( 'error' => Err::get_last( 'string' ), ); } - + }else Err::add( 'Updating FW', 'No data to save' ); } - + return $inserted; - + }else Err::prepend( 'Updating FW' ); } - + /** * Adding local exclusions to to the FireWall database. * @@ -588,16 +588,16 @@ public static function update__write_to_db( $file_url ){ * @return array|bool|int|mixed|string */ static public function update__write_to_db__exclusions( $exclusions = array() ){ - + //Exclusion for servers IP (SERVER_ADDR) if ( Server::get('HTTP_HOST') ) { - + // Exceptions for local hosts if( ! in_array( Server::get_domain(), self::$test_domains ) ){ $exclusions[] = Helper::dns__resolve( Server::get( 'HTTP_HOST' ) ); $exclusions[] = '127.0.0.1'; } - + foreach ( $exclusions as $exclusion ) { if (Helper::ip__validate($exclusion) && sprintf('%u', ip2long($exclusion))) { $nets_for_save[] = array( @@ -609,38 +609,38 @@ static public function update__write_to_db__exclusions( $exclusions = array() ){ } } } - + $db = new FileDB( 'fw_nets' ); - + if( isset( $nets_for_save ) ){ - + $inserted = $db->insertTemp( $nets_for_save ); - + if ( Err::check() ){ Err::prepend('Updating FW exclusions'); error_log( var_export( Err::get_all('string'), true ) ); return array( 'error' => Err::get_last( 'string' ), ); } - + return $inserted; - + }else return 0; } - + /** * Clear SFW table * * @return bool[] */ public static function clear_data() { - + // Clean current database $db = new FileDB( 'fw_nets' ); $db->delete(); - + return array( 'success' => true, ); - + } /** @@ -651,7 +651,7 @@ public static function clear_data() { public static function clear_temp_data() { $db = new FileDB( 'fw_nets' ); $db->deleteTemp(); - + return array( 'success' => true, ); } -} \ No newline at end of file +} From 2836d741cfacd560dcd331d8893d02526ac49081 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Mon, 28 Aug 2023 22:51:33 +0500 Subject: [PATCH 29/45] Helper.php. Tabs to spaces. --- uniforce/lib/Cleantalk/USP/Scanner/Helper.php | 105 +++++++++--------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Scanner/Helper.php b/uniforce/lib/Cleantalk/USP/Scanner/Helper.php index 7b35f9d..a66fe73 100644 --- a/uniforce/lib/Cleantalk/USP/Scanner/Helper.php +++ b/uniforce/lib/Cleantalk/USP/Scanner/Helper.php @@ -3,12 +3,12 @@ namespace Cleantalk\USP\Scanner; class Helper { - + const signatures_version_file_url = 'https://cleantalk-security.s3.amazonaws.com/security_signatures/version.txt'; const signatures_file_url = 'https://cleantalk-security.s3.amazonaws.com/security_signatures/security_signatures_v2.csv.gz'; - + public static function get_files( $offset = 0, $amount = 1500, $path = CT_USP_SITE_ROOT ) { - + $path_to_scan = realpath($path); $root_path = realpath( substr( CT_USP_SITE_ROOT, 0, - 1 ) ); $init_params = array( @@ -22,14 +22,14 @@ public static function get_files( $offset = 0, $amount = 1500, $path = CT_USP_SI 'files_mandatory' => array(), 'dir_exceptions' => array() ); - + $scanner = new Scanner($path_to_scan, $root_path, $init_params); - + return $scanner->files_count ? $scanner->files : false; } - + /** * Static. * Gets and parses signatures from the Cloud @@ -41,35 +41,34 @@ public static function get_files( $offset = 0, $amount = 1500, $path = CT_USP_SI static public function get_hashes__signature( $last_signature_update = 0 ) { if( \Cleantalk\USP\Uniforce\Helper::http__request__get_response_code(self::signatures_version_file_url) == 200) { - + $latest_signatures = \Cleantalk\USP\Uniforce\Helper::http__request__get_content(self::signatures_version_file_url); - + if(strtotime($latest_signatures)){ - + if(strtotime($last_signature_update) < strtotime($latest_signatures)){ - + if(\Cleantalk\USP\Uniforce\Helper::http__request__get_response_code(self::signatures_file_url) == 200) { - + $gz_data = \Cleantalk\USP\Uniforce\Helper::http__request__get_content(self::signatures_file_url); - + if(empty($gz_data['error'])){ - + if(function_exists('gzdecode')){ - + $data = gzdecode($gz_data); - + if($data !== false){ - + // Set map for file $map = strpos( self::signatures_file_url, '_mapped' ) !== false ? \Cleantalk\USP\Uniforce\Helper::buffer__csv__get_map( $data ) // Map from file : array( 'id', 'name', 'body', 'type', 'attack_type', 'submitted', 'cci' ); // Default map - + $out = array(); while( $data ){ $out[] = \Cleantalk\USP\Uniforce\Helper::buffer__csv__pop_line_to_array( $data, $map, true ); } - return $out; }else return array('error' => 'COULDNT_UNPACK'); @@ -86,7 +85,7 @@ static public function get_hashes__signature( $last_signature_update = 0 ) }else return array('error' =>'NO_VERSION_FILE'); } - + /** * Getting real hashs of approved files * @@ -99,37 +98,37 @@ static public function get_hashes__signature( $last_signature_update = 0 ) static public function get_hashes__approved_files($cms, $type, $version) { $file_path = 'https://cleantalk-security.s3.amazonaws.com/extensions_checksums/' . $cms . '/' . $type . '/' . $version . '.csv.gz'; - + if( \Cleantalk\USP\Uniforce\Helper::http__request($file_path, array(), 'get_code') == 200) { - + $gz_data = \Cleantalk\USP\Uniforce\Helper::http__request__get_content($file_path); - + if(empty($gz_data['error'])) { - + if ( function_exists( 'gzdecode' ) ) { - + $data = gzdecode( $gz_data ); - + if ( $data !== false ) { - + $lines = \Cleantalk\USP\Uniforce\Helper::buffer__parse__csv($data); - + if( count( $lines ) > 0 ) { - + $result = array(); - + foreach( $lines as $hash_info ) { - + if(empty($hash_info)) continue; - + preg_match('/.*\.(\S*)$/', $hash_info[0], $matches); $ext = isset($matches[1]) ? $matches[1] : ''; if(!in_array($ext, array('php','html'))) continue; - + $result[] = $hash_info; - + } - + if(count($result)){ return $result; }else @@ -147,7 +146,7 @@ static public function get_hashes__approved_files($cms, $type, $version) { } return array('error' =>'REMOTE_FILE_NOT_FOUND'); } - + /** * Scanning file * @@ -158,19 +157,19 @@ static public function get_hashes__approved_files($cms, $type, $version) { static public function file__get__differences($root_path, $file_info) { if(file_exists($root_path.$file_info['path'])){ - + if(is_readable($root_path.$file_info['path'])){ - + /** @todo Add proper comparing mechanism // Comparing with original file (if it's exists) and getting difference if(!empty($file_info['real_full_hash']) && $file_info['real_full_hash'] !== $file_info['full_hash']){ - + $file_original = \Cleantalk\USP\Scanner\Helper::file__get_original($file_info, $cms); - + if(!empty($file_original['error'])){ - + $file = file($root_path.$file_info['path']); - + // Comparing files strings for($row = 0; !empty($file[$row]); $row++){ if(isset($file[$row]) || isset($file_original[$row])){ @@ -184,16 +183,16 @@ static public function file__get__differences($root_path, $file_info) } } */ - + }else $output = array('error' => 'NOT_READABLE'); }else $output = array('error' => 'NOT_EXISTS'); - + return !empty($output) ? $output : false; - + } - + /** * Get original file's content * @@ -204,7 +203,7 @@ static public function file__get__differences($root_path, $file_info) static public function file__get_original($file_info) { $file_info['path'] = str_replace('\\', '/', $file_info['path']); // Replacing win slashes to Orthodox slashes =) in case of Windows - + switch( $file_info['source_type'] ){ case 'PLUGIN': $file_info['path'] = preg_replace('@/wp-content/plugins/.*?/(.*)$@i', '$1',$file_info['path']); @@ -218,15 +217,15 @@ static public function file__get_original($file_info) $url_path = 'https://cleantalk-security.s3.amazonaws.com/cms_sources/'.$file_info['source'].'/'.$file_info['version'].$file_info['path']; break; } - + if( \Cleantalk\USP\Uniforce\Helper::http__request__get_response_code($url_path) == 200 ){ $out = \Cleantalk\USP\Uniforce\Helper::http__request__get_content($url_path); }else $out = array('error' => 'Couldn\'t get original file'); - + return $out; } - + /** * Checks if the current system is Windows or not * @@ -235,7 +234,7 @@ static public function file__get_original($file_info) static function is_windows(){ return strpos(strtolower(php_uname('s')), 'windows') !== false ? true : false; } - + /** * Returns number of string with a given char position * @@ -248,7 +247,7 @@ static function is_windows(){ public static function file__get_string_number_with_needle($file_path, $signature_body, $is_regexp = false){ $file = file( $file_path ); $out = 0; - + foreach( $file as $number => $line ){ if( ( $is_regexp && preg_match( $signature_body, $line ) ) || @@ -257,7 +256,7 @@ public static function file__get_string_number_with_needle($file_path, $signatur $out = $number; } } - + return $out; } -} \ No newline at end of file +} From 038a2f7ed5f77cee92ba896b02d48f538b702f39 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Mon, 28 Aug 2023 22:52:52 +0500 Subject: [PATCH 30/45] Scanner.php. Tabs to spaces. --- .../lib/Cleantalk/USP/Scanner/Scanner.php | 158 +++++++++--------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php b/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php index 94dc272..e60d0a1 100644 --- a/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php +++ b/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php @@ -11,61 +11,61 @@ class Scanner public $path = ''; // Main path public $path_lenght = 0; - + /** @var array Description Extensions to check */ public $ext = array(); - + /** @var array Exception for extensions */ public $ext_except = array(); - + /** @var array Exception for files paths */ public $files_except = array(); - + /** @var array Exception for directories */ - public $dirs_except = array(); - + public $dirs_except = array(); + /** @var array Mandatory check for files paths */ - public $files_mandatory = array(); - + public $files_mandatory = array(); + /** @var array Mandatory check for directories */ public $dirs_mandatory = array(); - + public $files = array(); public $dirs = array(); - + public $files_count = 0; public $dirs_count = 0; - + private $file_start = 0; private $file_curr = 0; private $file_max = 1000000; - + function __construct($path, $rootpath, $params = array('count' => true)) { // INITIALING PARAMS - + // Main directory $path = realpath($path); if(!is_dir($path)) die("Scan '$path' isn't directory"); if(!is_dir($rootpath)) die("Root '$rootpath' isn't directory"); $this->path_lenght = strlen($rootpath); - - // Processing filters + + // Processing filters $this->ext = !empty($params['extensions']) ? $this->filter_params($params['extensions']) : array(); $this->ext_except = !empty($params['extensions_exceptions']) ? $this->filter_params($params['extensions_exceptions']) : array(); $this->files_except = !empty($params['file_exceptions']) ? $this->filter_params($params['file_exceptions']) : array(); $this->dirs_except = !empty($params['dir_exceptions']) ? $this->filter_params($params['dir_exceptions']) : array(); - + // Mandatory files and dirs $this->files_mandatory = !empty($params['files_mandatory']) ? $this->filter_params($params['files_mandatory']) : array(); $this->dirs_mandatory = !empty($params['dirs_mandatory']) ? $this->filter_params($params['dirs_mandatory']) : array(); - + // Initialing counters $this->file_start = isset($params['offset']) ? $params['offset'] : 0; $this->file_max = isset($params['offset']) && isset($params['amount']) ? $params['offset'] + $params['amount'] : 1000000; - + // DO STUFF - + // Only count files if(!empty($params['count'])){ $this->count_files__mandatory($this->files_mandatory); @@ -78,15 +78,15 @@ function __construct($path, $rootpath, $params = array('count' => true)) // Files $this->files_count = count($this->files); $this->file__details($this->files, $this->path_lenght); - + // Directories // $this->dirs[]['path'] = $path; // $this->dirs_count = count($this->dirs); // $this->dir__details($this->dirs, $this->path_lenght); - + } - + /** * * Function coverting icoming parametrs to array even if it is a string like 'some, example, string' * @@ -110,10 +110,10 @@ public function filter_params($filter) return null; } } - + /** * Counts given mandatory files - * + * * @param array $files Files to count */ public function count_files__mandatory($files){ @@ -122,24 +122,24 @@ public function count_files__mandatory($files){ $this->files_count++; } } - + /** * Count files in directory - * + * * @param string $main_path Path to count files in */ public function count_files_in_dir($main_path) - { + { $paths = array_merge(glob($main_path.'/.*', GLOB_NOSORT), glob($main_path.'/*', GLOB_NOSORT)); - + foreach($paths as $path){ - + // Excluding $path/. and $path/.. directories from the set if(preg_match('/\.$/', $path)) continue; - + if(is_file($path)){ - + // Extensions filter if(!empty($this->ext_except)){ $tmp = explode('.', $path); @@ -157,18 +157,18 @@ public function count_files_in_dir($main_path) if(in_array(basename($path), $this->files_except)) continue; } - + // Dirnames filter foreach($this->dirs_except as $dir_except){ if(preg_match('/'.$dir_except.'/', $path)){ continue(2); } } - + $this->files_count++; - + }elseif(is_dir($path)){ - + // Dirnames filter foreach($this->dirs_except as $dir_except){ if(preg_match('/'.$dir_except.'/', $path)){ @@ -179,10 +179,10 @@ public function count_files_in_dir($main_path) } } } - + /** * Getting mandatory files - * + * * @param array $files Files to get */ public function get_files__mandatory($files){ @@ -193,76 +193,76 @@ public function get_files__mandatory($files){ } } } - + /** * Get all files from directory - * + * * @param string $main_path Path to get files from * @return void */ public function get_file_structure($main_path) { $paths = array_merge(glob($main_path.'/.*', GLOB_NOSORT), glob($main_path.'/*', GLOB_NOSORT)); - + foreach($paths as $path){ - + // Excluding $path/. and $path/.. directories from the set if(preg_match('/\.$/', $path)) continue; - + // Return if file limit is reached if($this->file_curr >= $this->file_max) return; - + if(is_file($path)){ - + // Extensions filter if(!empty($this->ext)){ $tmp = explode('.', $path); if(!in_array($tmp[count($tmp)-1], $this->ext)) continue; } - + // Extensions exception filter if(!empty($this->ext_except)){ $tmp = explode('.', $path); if(in_array($tmp[count($tmp)-1], $this->ext_except)) continue; } - + // Filenames exception filter if(!empty($this->files_except)){ if(in_array(basename($path), $this->files_except)) continue; } - + // Dirnames filter foreach($this->dirs_except as $dir_except){ if(preg_match('/'.$dir_except.'/', $path)){ continue(2); } } - + $this->file_curr++; - + // Skip if start is not reached if($this->file_curr-1 < $this->file_start) continue; - + $this->files[]['path'] = $path; - + }elseif(is_dir($path)){ - + // Dirnames filter foreach($this->dirs_except as $dir_except){ if(preg_match('/'.$dir_except.'/', $path)) continue(2); } - + $this->get_file_structure($path); if($this->file_curr > $this->file_start) $this->dirs[]['path'] = $path; - + }elseif(is_link($path)){ error_log('LINK FOUND: ' . $path); } @@ -271,7 +271,7 @@ public function get_file_structure($main_path) /** * Getting file details like last modified time, size, permissions - * + * * @param array $file_list Array of abolute paths to files * @param int $path_offset Length of CMS root path */ @@ -288,7 +288,7 @@ public function file__details($file_list, $path_offset) // Fast hash $this->files[$key]['fast_hash'] = md5($this->files[$key]['path']); - + // Full hash $this->files[$key]['full_hash'] = is_readable($val['path']) ? $this->files[$key]['size'] > self::FILE_MAX_SIZE ? 'file_is_too_big' : md5_file($val['path']) @@ -300,7 +300,7 @@ public function file__details($file_list, $path_offset) /** * Getting dir details - * + * * @param array $dir_list Array of abolute paths to directories * @param int $path_offset Length of CMS root path */ @@ -312,22 +312,22 @@ public function dir__details($dir_list, $path_offset) $this->dirs[$key]['perms'] = substr(decoct(fileperms($val['path'])), 2); } } - + /** * Scan file thru malware sinatures - * + * * @param string $root_path Path to CMS's root folder * @param array $file_info Array with files data (path, real_full_hash, source_type, source, version), other is optional * @param array $signatures Set of signatures - * + * * @return array|false False or Array of found bad sigantures */ static public function file__scan__for_signatures($root_path, $file_info, $signatures) { if(file_exists($root_path.$file_info['path'])){ - + if(is_readable($root_path.$file_info['path'])){ - + $verdict = array(); foreach ((array)$signatures as $signature){ @@ -337,7 +337,7 @@ static public function file__scan__for_signatures($root_path, $file_info, $signa $verdict['SIGNATURES'][1][] = $signature['id']; } } - + if( in_array( $signature['type'], array('CODE_PHP', 'CODE_JS', 'CODE_HTML' ) ) ) { if ( filesize($root_path . $file_info['path']) > self::FILE_MAX_SIZE ) { // File is too big to getting the content @@ -354,10 +354,10 @@ static public function file__scan__for_signatures($root_path, $file_info, $signa } } } - + $file_info['weak_spots'] = !empty($file_info['weak_spots']) ? json_decode($file_info['weak_spots'], true) : array(); $verdict = Helper::array_merge__save_numeric_keys__recursive($file_info['weak_spots'], $verdict); - + // Processing results if(!empty($verdict)){ $output['weak_spots'] = $verdict; @@ -368,38 +368,38 @@ static public function file__scan__for_signatures($root_path, $file_info, $signa $output['severity'] = null; $output['status'] = 'OK'; } - + }else $output = array('error' => 'NOT_READABLE'); }else $output = array('error' => 'NOT_EXISTS'); - + return $output; } - + /** * Scan file thru heuristic - * + * * @param string $root_path Path to CMS's root folder * @param array $file_info Array with files data (path, real_full_hash, source_type, source, version), other is optional - * + * * @return array|false False or Array of found bad constructs sorted by severity */ static public function file__scan__heuristic($root_path, $file_info) { if(file_exists($root_path.$file_info['path'])){ - + if(is_readable($root_path.$file_info['path'])){ - + $scanner = new ScannerH( $root_path . $file_info['path']); if ( !empty( $scanner -> errors ) ) return $scanner -> errors; $scanner -> process_file(); - + $file_info['weak_spots'] = !empty($file_info['weak_spots']) ? json_decode($file_info['weak_spots'], true) : array(); - + $verdict = Helper::array_merge__save_numeric_keys__recursive($file_info['weak_spots'], $scanner->verdict); - + // Processing results if(!empty($verdict)){ $output['weak_spots'] = $verdict; @@ -410,14 +410,14 @@ static public function file__scan__heuristic($root_path, $file_info) $output['severity'] = null; $output['status'] = 'OK'; } - + }else $output = array('error' => 'NOT_READABLE'); }else $output = array('error' => 'NOT_EXISTS'); - + return $output; } - - + + } From 6d0b80b5caf2893a83cd7e72721f3bdab3ee6a63 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Mon, 28 Aug 2023 22:53:36 +0500 Subject: [PATCH 31/45] WAF.php. Tabs to spaces. --- .../Cleantalk/USP/Uniforce/Firewall/WAF.php | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php index afbedf3..cc3cf42 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php @@ -9,26 +9,26 @@ use Cleantalk\USP\Scanner\ScannerH; class WAF extends \Cleantalk\USP\Uniforce\Firewall\FirewallModule { - + public $module_name = 'WAF'; - + protected $waf_xss_check = false; protected $waf_sql_check = false; protected $waf_file_check = false; protected $waf_exploit_check = false; - + private $waf_pattern = array(); // Why WAF is triggered (reason) - + private $waf_sql_patterns = array(); private $waf_exploit_patterns = array(); private $waf_xss_patterns = array(); - + public $waf_file_mime_check = array( 'text/x-php', 'text/plain', 'image/x-icon', ); - + /** * FireWall_module constructor. * Use this method to prepare any data for the module working. @@ -36,28 +36,28 @@ class WAF extends \Cleantalk\USP\Uniforce\Firewall\FirewallModule { * @param array $params */ public function __construct( $params = array() ){ - + parent::__construct( $params ); - + } - + /** * Use this method to execute main logic of the module. * @return mixed */ public function check() { - + $results = array(); - + // Get signatures $signatures = $this->signatures__get(); - + if ( $signatures ) { - + foreach ( $signatures as $signature ) { switch ( $signature['attack_type'] ) { - + case 'SQL_INJECTION': $this->waf_sql_patterns[] = $signature['body']; break; @@ -70,38 +70,38 @@ public function check() { } } } - + // XSS if( $this->waf_xss_check ){ if($this->waf_xss_check($_POST) || $this->waf_xss_check($_GET) || $this->waf_xss_check($_COOKIE)){ $results[] = array('ip' => end($this->ip_array), 'is_personal' => false, 'status' => 'DENY_BY_WAF_XSS', 'pattern' => $this->waf_pattern); } } - + // SQL-injection if( $this->waf_sql_check ){ if($this->waf_sql_check($_POST) || $this->waf_sql_check($_GET)){ $results[] = array('ip' => end($this->ip_array), 'is_personal' => false, 'status' => 'DENY_BY_WAF_SQL', 'pattern' => $this->waf_pattern); } } - + // File if ($this->waf_file_check ){ if($this->waf_file_check()){ $results[] = array('ip' => end($this->ip_array), 'is_personal' => false, 'status' => 'DENY_BY_WAF_FILE', 'pattern' => $this->waf_pattern); } } - + // Exploits if( $this->waf_exploit_check ){ if($this->waf_exploit_check()){ $results[] = array('ip' => end($this->ip_array), 'is_personal' => false, 'status' => 'DENY_BY_WAF_EXPLOIT', 'pattern' => $this->waf_pattern); } } - + if( ! $results ) $results[] = array( 'status' => 'PASS' ); - + foreach( $results as &$result ){ $result = array_merge( $result, @@ -112,9 +112,9 @@ public function check() { ) ); } - + return $results; - + } /** @@ -122,7 +122,7 @@ public function check() { * @return array|false */ private function signatures__get(){ - + $signatures_source = new Storage('signatures', null, '', 'csv', array( 'id', 'name', @@ -142,7 +142,7 @@ private function signatures__get(){ } return !empty($signatures) ? $signatures : false; } - + /** * Checks array for XSS-attack patterns * @@ -151,9 +151,9 @@ private function signatures__get(){ * @return bool */ private function waf_xss_check( $arr ) { - + foreach( $arr as $name => $param ){ - + // Recursion if( is_array( $param ) ){ $result = $this->waf_xss_check( $param ); @@ -161,7 +161,7 @@ private function waf_xss_check( $arr ) { return true; continue; } - + //Check foreach( $this->waf_xss_patterns as $pattern ){ $is_regexp = preg_match( '@^/.*/$@', $pattern ) || preg_match( '@^#.*#$@', $pattern ); @@ -175,11 +175,11 @@ private function waf_xss_check( $arr ) { } } } - + return false; - + } - + /** * Checks array for SQL injections * @@ -188,16 +188,16 @@ private function waf_xss_check( $arr ) { * @return bool */ private function waf_sql_check( $arr ) { - + foreach( $arr as $name => $param ){ - + if( is_array( $param ) ){ $result = $this->waf_sql_check( $param ); if( $result === true ) return true; continue; } - + foreach( $this->waf_sql_patterns as $pattern ){ $is_regexp = preg_match( '@^/.*/$@', $pattern ) || preg_match( '@^#.*#$@', $pattern ); @@ -210,18 +210,18 @@ private function waf_sql_check( $arr ) { } } } - + return false; - + } - + /** * Checks $_SERVER['QUERY_STRING'] for exploits * * @return bool */ private function waf_exploit_check() { - + foreach( $this->waf_exploit_patterns as $pattern ){ $is_regexp = preg_match( '@^/.*/$@', $pattern ) || preg_match( '@^#.*#$@', $pattern ); @@ -233,18 +233,18 @@ private function waf_exploit_check() { return true; } } - + return false; - + } - + /** * Checks uploaded files for malicious code * * @return boolean Does the file contain malicious code */ private function waf_file_check() { - + if( ! empty( $_FILES ) ){ foreach( $_FILES as $filez ){ if ( ( empty($filez['errror'] ) || $filez['errror'] == UPLOAD_ERR_OK ) ) { @@ -271,8 +271,8 @@ private function waf_file_check() { } } } - + return false; - + } -} \ No newline at end of file +} From 0b82d1b7e3df4505ea0182b1954d37feb906ef7e Mon Sep 17 00:00:00 2001 From: alexandergull Date: Mon, 28 Aug 2023 22:55:00 +0500 Subject: [PATCH 32/45] Mod. Scanner. Work with signatures body via base64 to prevent third-party malware scanners warns. --- uniforce/lib/Cleantalk/USP/Scanner/Helper.php | 6 +++++- uniforce/lib/Cleantalk/USP/Scanner/Scanner.php | 14 ++++++++------ .../lib/Cleantalk/USP/Uniforce/Firewall/WAF.php | 10 ++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Scanner/Helper.php b/uniforce/lib/Cleantalk/USP/Scanner/Helper.php index a66fe73..15aed7c 100644 --- a/uniforce/lib/Cleantalk/USP/Scanner/Helper.php +++ b/uniforce/lib/Cleantalk/USP/Scanner/Helper.php @@ -67,7 +67,11 @@ static public function get_hashes__signature( $last_signature_update = 0 ) $out = array(); while( $data ){ - $out[] = \Cleantalk\USP\Uniforce\Helper::buffer__csv__pop_line_to_array( $data, $map, true ); + $row = \Cleantalk\USP\Uniforce\Helper::buffer__csv__pop_line_to_array( $data, $map, true ); + if ( isset($row['body']) ) { + $row['body'] = base64_encode($row['body']); + } + $out[] = $row; } return $out; }else diff --git a/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php b/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php index e60d0a1..a72c69d 100644 --- a/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php +++ b/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php @@ -331,9 +331,11 @@ static public function file__scan__for_signatures($root_path, $file_info, $signa $verdict = array(); foreach ((array)$signatures as $signature){ - + + $signature_body_decoded = base64_decode($signature['body']); + if( $signature['type'] === 'FILE' ){ - if( $file_info['full_hash'] === $signature['body'] ){ + if( $file_info['full_hash'] === $signature_body_decoded ){ $verdict['SIGNATURES'][1][] = $signature['id']; } } @@ -344,12 +346,12 @@ static public function file__scan__for_signatures($root_path, $file_info, $signa continue; } $file_content = file_get_contents( $root_path . $file_info['path'] ); - $is_regexp = preg_match( '/^\/.*\/$/', $signature['body'] ); + $is_regexp = preg_match( '/^\/.*\/$/', $signature_body_decoded ); if( - ( $is_regexp && preg_match( $signature['body'], $file_content ) ) || - ( ! $is_regexp && strripos( $file_content, stripslashes( $signature['body'] ) ) !== false ) + ( $is_regexp && preg_match( $signature_body_decoded, $file_content ) ) || + ( ! $is_regexp && strripos( $file_content, stripslashes( $signature_body_decoded ) ) !== false ) ){ - $line_number = ScannerHelper::file__get_string_number_with_needle( $file_info['path'], $signature['body'], $is_regexp ); + $line_number = ScannerHelper::file__get_string_number_with_needle( $file_info['path'], $signature_body_decoded, $is_regexp ); $verdict['SIGNATURES'][ $line_number ][] = $signature['id']; } } diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php index cc3cf42..325ff45 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php @@ -55,17 +55,19 @@ public function check() { if ( $signatures ) { foreach ( $signatures as $signature ) { - + + $signature_body_decoded = base64_decode($signature['body']); + switch ( $signature['attack_type'] ) { case 'SQL_INJECTION': - $this->waf_sql_patterns[] = $signature['body']; + $this->waf_sql_patterns[] = $signature_body_decoded; break; case 'XSS': - $this->waf_xss_patterns[] = $signature['body']; + $this->waf_xss_patterns[] = $signature_body_decoded; break; case 'EXPLOIT': - $this->waf_exploit_patterns[] = $signature['body']; + $this->waf_exploit_patterns[] = $signature_body_decoded; break; } } From 6d4d1e583a96ca8e07f36a223ee6ec1fab6eac67 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Fri, 1 Sep 2023 12:56:40 +0500 Subject: [PATCH 33/45] Fix. BFP log. User agent getting. --- uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php index dc14618..3ba97ed 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php @@ -277,7 +277,7 @@ public static function send_log( $ct_key ){ } - $auth_ip = $log[1] ? (string) $log[1] : '0.0.0.0'; + $auth_ip = $_log['ip'] ? (string) $_log['ip']: '0.0.0.0'; if( (int) $_log['hits'] > 0 ){ //todo AG: for what this for cycle? for( $i = 0; (int) $_log['hits'] > $i; $i ++ ){ @@ -314,7 +314,7 @@ public static function send_log( $ct_key ){ $data[] = array_merge( array_pop( $data ), array( - 'user_agent' => $_log['user_agent'], + 'user_agent' => $_log['http_user_agent'], ) ); } From 1e27f035ea7382a9cad7313a5e75bc59a2655c9e Mon Sep 17 00:00:00 2001 From: alexandergull Date: Fri, 1 Sep 2023 12:57:04 +0500 Subject: [PATCH 34/45] Tabs to spaces. BFP.php --- .../Cleantalk/USP/Uniforce/Firewall/BFP.php | 140 +++++++++--------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php index 3ba97ed..657ad18 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/BFP.php @@ -12,20 +12,20 @@ use Cleantalk\USP\Variables\Server; class BFP extends \Cleantalk\USP\Uniforce\Firewall\FirewallModule { - + public $module_name = 'BFP'; - + // Flags protected $is_logged_in = false; protected $is_login_page = false; protected $do_check = false; - + protected $allowed_interval = 900; protected $bf_limit = 5; protected $block_period = 3600; - + protected $chance_to_clean = 100; - + /** * FireWall_module constructor. * Use this method to prepare any data for the module working. @@ -33,38 +33,38 @@ class BFP extends \Cleantalk\USP\Uniforce\Firewall\FirewallModule { * @param array $params */ public function __construct( $params = array() ){ - + parent::__construct( $params ); - + } - + public function check(){ - + $results = array(); - + if( $this->is_login_page && ! $this->is_logged_in && $this->do_check && isset( $this->ip_array['real'] ) ){ - + $block_time = 20 * 60; // 20 minutes $allowed_count = 2; $allowed_interval = 900; // 15 min - + $bfp_blacklist = State::getInstance()->bfp_blacklist; $bfp_blacklist_fast = State::getInstance()->bfp_blacklist_fast; - + $found_ip = null; $current_ip__real = $this->ip_array['real']; - + // Check against black list foreach( $bfp_blacklist as $bad_ip => $bad_ip__details ){ if( $bad_ip === $current_ip__real ){ $found_ip = $bad_ip; $found_ip__details = $bad_ip__details; } - + } unset( $bad_ip, $bad_ip__details ); - + if( $found_ip ) { - + // Remove the IP from the blacklist and proceed the checking if( $found_ip__details->added + $block_time < time() ) { unset( $bfp_blacklist->$current_ip__real ); @@ -72,15 +72,15 @@ public function check(){ }else{ $results[] = array( 'status' => 'DENY_BY_BFP', ); } - + } - + // Check count of logins $found_ip = null; $js_on = spbct_js_test(); - + foreach( $bfp_blacklist_fast as $bad_ip => $bad_ip__details ){ - + if( $bad_ip === $current_ip__real && $bad_ip__details->added + $allowed_interval > time() ){ $found_ip = $bad_ip; $found_ip__details = array( @@ -92,33 +92,33 @@ public function check(){ unset( $bfp_blacklist_fast->$current_ip__real ); $bfp_blacklist_fast->save(); } - + } unset( $bad_ip, $bad_ip__details ); - + if( $found_ip ) { - + //increased allowed count to 20 if JS is on! if( $found_ip__details['js_on'] == 1 ) $allowed_count = $allowed_count * 2; - + // Check count of the logins and move the IP to the black list. if( $found_ip__details['count'] > $allowed_count ){ - + $bfp_blacklist->$current_ip__real['added'] = time(); $bfp_blacklist->save(); - + unset( $bfp_blacklist_fast->$current_ip__real ); $bfp_blacklist_fast->save(); - + $results[] = array( 'status' => 'DENY_BY_BFP', ); - + }else{ - + $bfp_blacklist_fast->$found_ip = $found_ip__details; $bfp_blacklist_fast->save(); - + } - + }else{ $bfp_blacklist_fast->$current_ip__real = array( 'added' => time(), @@ -126,9 +126,9 @@ public function check(){ 'count' => 1 ); $bfp_blacklist_fast->save(); - + } - + // Make the result standard foreach( $results as &$result ){ $result = array_merge( $result, array( @@ -138,13 +138,13 @@ public function check(){ 'module' => 'BFP' ) ); } - + } - + return $results; - + } - + /** * * @@ -154,48 +154,48 @@ public function actions_for_denied( $result ){ $this->state->data->stat->bfp->count++; $this->state->data->save(); } - + /** * @param array|string $fw_result */ public static function update_log( $fw_result ) { - + // Updating common firewall log if( is_array( $fw_result ) && $fw_result['status'] !== 'PASS' ){ parent::update_log( $fw_result ); return; } - + if( is_array( $fw_result ) && $fw_result['status'] !== 'DENY_BY_BFP' ){ $fw_result = 'auth_failed'; } - + global $salt; - + $params_default = array( - + // Necessary 'event' => null, 'auth_ip' => isset( $fw_result['ip'] ) ? $fw_result['ip'] : Helper::ip__get( array( 'real' ) ), 'time' => time(), - + // Unnecessary 'page_url' => substr( Server::get( 'HTTP_HOST' ) . Server::get( 'REQUEST_URI' ), 0, 1024 ), 'user_agent' => substr( Server::get( 'HTTP_USER_AGENT' ), 0, 1024 ), - + // @ToDo Unused params. Implement this logic to the next releases 'page' => null, 'page_time' => null, 'browser_sign' => null, ); $params = array_merge( $params_default, array( 'event' => $fw_result, ) ); - + // Inserting to the logs. $log_path = CT_USP_ROOT . 'data/security_logs/' . hash('sha256', $params['auth_ip'] . $salt . $params['event']) . '.log'; - + if( file_exists( $log_path ) ) $log = explode( ',', file_get_contents( $log_path ) ); - + $log = array( $params['event'], $params['auth_ip'], @@ -207,7 +207,7 @@ public static function update_log( $fw_result ) { $params['browser_sign'], isset($log[8]) ? (int) $log[8] + 1 : 1, ); - + $fd = fopen( $log_path, 'w' ); if( $fd ){ flock( $fd, LOCK_EX ); @@ -215,7 +215,7 @@ public static function update_log( $fw_result ) { fclose( $fd ); } } - + /** * Sends security log * @@ -224,23 +224,23 @@ public static function update_log( $fw_result ) { * @return array|bool|int[]|mixed|string[] */ public static function send_log( $ct_key ){ - + $log_dir_path = CT_USP_ROOT . 'data/security_logs'; - + if( is_dir( $log_dir_path ) ){ - + $log_files = array_diff( scandir( $log_dir_path ), array( '.', '..', 'index.php' ) ); - + if( ! empty( $log_files ) ){ - + //Compile logs $data = array(); - + foreach( $log_files as $log_file ){ - + $log = file_get_contents( $log_dir_path . DS . $log_file ); $log = str_getcsv( $log ); - + // Skip bad files if( ! isset( $log[0], $log[1], $log[2], $log[3], $log[4], $log[5], $log[6], $log[7], $log[8] ) ){ unlink( $log_dir_path . DS . $log_file ); @@ -308,7 +308,7 @@ public static function send_log( $ct_key ){ 'role' => null, ); } - + // Adding user agent if it's login event if( in_array( (string) $_log['event'], array( 'login', 'login_2fa', 'login_new_device', 'logout', ) ) ){ $data[] = array_merge( @@ -321,19 +321,19 @@ public static function send_log( $ct_key ){ } $result = API::method__security_logs( $ct_key, $data ); - + if( empty( $result['error'] ) ){ - + //Clear local table if it's ok. if( $result['rows'] == count( $data ) ){ - + foreach( $log_files as $log_file ){ if( file_exists( $log_dir_path . DS . $log_file ) ) unlink( $log_dir_path . DS . $log_file ); } - + return $result; - + }else{ return array( 'error' => 'SENT_AND_RECEIVED_LOGS_COUNT_DOESNT_MACH' ); } @@ -346,13 +346,13 @@ public static function send_log( $ct_key ){ }else{ return array( 'rows' => 0 ); } // No logs. Directory is not exists. - + } - + public static function is_logged_in( $cms ) { - + $cms = defined( 'USP_DASHBOARD' ) ? 'UniForce' : $cms; - + switch ( $cms ) { case 'Joomla' : return class_exists('JFactory') && \JFactory::getUser()->id; @@ -472,4 +472,4 @@ public static function is_login_page() { return false; } -} \ No newline at end of file +} From bf70481ef770e6ec4bf952fcf15e93a80c6bc28e Mon Sep 17 00:00:00 2001 From: alexandergull Date: Fri, 1 Sep 2023 12:59:59 +0500 Subject: [PATCH 35/45] Tabs to spaces. DB.php --- uniforce/lib/Cleantalk/USP/DB.php | 44 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/DB.php b/uniforce/lib/Cleantalk/USP/DB.php index f22ef30..afb8ef8 100644 --- a/uniforce/lib/Cleantalk/USP/DB.php +++ b/uniforce/lib/Cleantalk/USP/DB.php @@ -3,26 +3,26 @@ namespace Cleantalk\USP; class DB extends \PDO implements Common\DB { - + use Templates\Singleton; - + private static $instance; - + /** * @var string */ public $query = ''; - + /** * @var \PDOStatement */ public $query_result = ''; - + /** * @var int */ public $rows_affected; - + /** * @param mixed ...$params */ @@ -39,15 +39,15 @@ public function init( ...$params ){ \PDO::MYSQL_ATTR_SSL_CERT => CT_USP_DATA_SSL_CERT . 'client-cert.pem', \PDO::MYSQL_ATTR_SSL_KEY => CT_USP_DATA_SSL_CERT . 'client-key.pem', ); - + parent::__construct( $dsn, $username, $password, $options ); - + }else{ self::$instance = null; } - + } - + /** * Safely replace place holders * @@ -59,7 +59,7 @@ public function init( ...$params ){ public function prepare( $query, $param = array() ) { return parent::prepare( $query, $param ); } - + /** * Executes a query to DB * @@ -76,7 +76,7 @@ function q( $query, $mode = \PDO::ATTR_DEFAULT_FETCH_MODE ){ $this->rows_affected = $this->query_result->rowCount(); return $this->query_result; } - + /** * @param $query * @@ -87,8 +87,8 @@ public function execute( $query ) { $this->rows_affected = parent::exec( $query ); return $this->rows_affected; } - - + + /** * Fetch first column from query. * May receive raw or prepared query. @@ -99,10 +99,10 @@ public function execute( $query ) { * @return array|object|void|null */ public function fetch( $query = '', $response_type = 'array' ) { - + if( $this->query !== $query) $this->q( $query ); - + switch( $response_type ){ case 'array': $response_type = \PDO::FETCH_ASSOC; @@ -114,12 +114,12 @@ public function fetch( $query = '', $response_type = 'array' ) { $response_type = \PDO::FETCH_NUM; break; } - + return $this->query_result ->fetch( $response_type ); - + } - + /** * Fetch all result from query. * May receive raw or prepared query. @@ -130,7 +130,7 @@ public function fetch( $query = '', $response_type = 'array' ) { * @return array|object|null */ public function fetch_all( $query = '', $response_type = 'array' ) { - + switch($response_type){ case 'array': $response_type = \PDO::FETCH_ASSOC; @@ -142,8 +142,8 @@ public function fetch_all( $query = '', $response_type = 'array' ) { $response_type = \PDO::FETCH_NUM; break; } - + return parent::query( $query ) ->fetchAll( $response_type ); } -} \ No newline at end of file +} From d69991c058b60a6eb295fc36d590631af943f575 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Fri, 1 Sep 2023 13:18:58 +0500 Subject: [PATCH 36/45] Fix. Handle PDO errors on remote calls check. --- uniforce/inc/common.php | 8 ++++++- uniforce/lib/Cleantalk/USP/DB.php | 40 ++++++++++++++++--------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/uniforce/inc/common.php b/uniforce/inc/common.php index 0e419d2..7f6bfc7 100644 --- a/uniforce/inc/common.php +++ b/uniforce/inc/common.php @@ -8,6 +8,7 @@ * Version: 3.8.0 */ +use Cleantalk\USP\Common\Err; use Cleantalk\USP\Common\State; use Cleantalk\USP\Variables\Server; use Cleantalk\USP\Common\RemoteCalls; @@ -66,4 +67,9 @@ unset( $cron ); // Accept remote calls -RemoteCalls::check() && RemoteCalls::perform(); +try { + RemoteCalls::check() && RemoteCalls::perform(); +} catch (\Exception $e) { + Err::add('Failed to perform test remote call: ' . $e->getMessage()); + exit; +} diff --git a/uniforce/lib/Cleantalk/USP/DB.php b/uniforce/lib/Cleantalk/USP/DB.php index afb8ef8..c5c0b4c 100644 --- a/uniforce/lib/Cleantalk/USP/DB.php +++ b/uniforce/lib/Cleantalk/USP/DB.php @@ -27,25 +27,27 @@ class DB extends \PDO implements Common\DB { * @param mixed ...$params */ public function init( ...$params ){ - - if( $params[0] ){ - $dsn = $params[0]; - $username = $params[1]; - $password = $params[2]; - $options = isset( $params[3] ) ? $params[3] : array( - \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, // Handle errors as an exceptions - \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, // Set default fetch mode as associative array - \PDO::MYSQL_ATTR_SSL_CA => CT_USP_DATA_SSL_CERT . 'ca.pem', - \PDO::MYSQL_ATTR_SSL_CERT => CT_USP_DATA_SSL_CERT . 'client-cert.pem', - \PDO::MYSQL_ATTR_SSL_KEY => CT_USP_DATA_SSL_CERT . 'client-key.pem', - ); - - parent::__construct( $dsn, $username, $password, $options ); - - }else{ - self::$instance = null; - } - + try { + if( $params[0] ){ + $dsn = $params[0]; + $username = $params[1]; + $password = $params[2]; + $options = isset( $params[3] ) ? $params[3] : array( + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, // Handle errors as an exceptions + \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, // Set default fetch mode as associative array + \PDO::MYSQL_ATTR_SSL_CA => CT_USP_DATA_SSL_CERT . 'ca.pem', + \PDO::MYSQL_ATTR_SSL_CERT => CT_USP_DATA_SSL_CERT . 'client-cert.pem', + \PDO::MYSQL_ATTR_SSL_KEY => CT_USP_DATA_SSL_CERT . 'client-key.pem', + ); + + parent::__construct( $dsn, $username, $password, $options ); + + }else{ + self::$instance = null; + } + } catch (\Exception $e) { + throw new \Exception("Can not init DB connect, error:" . $e->getMessage()); + } } /** From 062a376063da2f63d866d1a1717e182ef791bca4 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Fri, 1 Sep 2023 13:20:26 +0500 Subject: [PATCH 37/45] Fix. Call the magic method before empty check. --- uniforce/inc/settings.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/uniforce/inc/settings.php b/uniforce/inc/settings.php index fa23a63..6b727f7 100644 --- a/uniforce/inc/settings.php +++ b/uniforce/inc/settings.php @@ -16,7 +16,8 @@ function ctusp_settings__show_modified_files(){ function usp_settings__plugin_state() { $usp = State::getInstance(); - if ( ! empty($usp->plugin_meta->latest_version) ) { + $usp->plugin_meta->latest_version; + if (! empty($usp->plugin_meta->latest_version) ) { if ( version_compare($usp->plugin_meta->version, $usp->plugin_meta->latest_version) === -1 ) { echo '

There is a newer version. Update to the latest ' . $usp->plugin_meta->latest_version . '

'; echo '

'; From c68cdc97fc63e98b3804f9e7ce335504784173bb Mon Sep 17 00:00:00 2001 From: alexandergull Date: Fri, 1 Sep 2023 15:24:52 +0500 Subject: [PATCH 38/45] Mod. Speed up signatures analysis by increasing amount of files per RC. --- uniforce/js/scanner-plugin.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uniforce/js/scanner-plugin.js b/uniforce/js/scanner-plugin.js index 8953091..1307130 100644 --- a/uniforce/js/scanner-plugin.js +++ b/uniforce/js/scanner-plugin.js @@ -175,8 +175,8 @@ class spbc_Scanner{ case 'get_signatures': params.timeout = 60000; break; case 'clear_table': this.amount = 1000; break; case 'surface_analysis': this.amount = 500; break; - case 'signature_analysis': this.amount = 10; break; - case 'heuristic_analysis': this.amount = 10; break; + case 'signature_analysis': this.amount = this.settings['no_sql'] === 1 ? 200 : 50; break; + case 'heuristic_analysis': this.amount = this.settings['no_sql'] === 1 ? 200 : 50; break; case 'auto_cure': this.amount = 5; break; case 'send_results': break; default: break; @@ -222,4 +222,4 @@ class spbc_Scanner{ }; -}; \ No newline at end of file +}; From 1ef1e44c0f7e7d1600593a5396bd8559e1c43691 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Fri, 1 Sep 2023 15:25:37 +0500 Subject: [PATCH 39/45] Fix. Base64 signatures speed up. --- uniforce/inc/scanner.php | 197 +++++++++--------- .../lib/Cleantalk/USP/Scanner/Scanner.php | 15 +- .../lib/Cleantalk/USP/ScannerController.php | 55 +++-- .../Cleantalk/USP/Uniforce/Firewall/WAF.php | 8 +- 4 files changed, 147 insertions(+), 128 deletions(-) diff --git a/uniforce/inc/scanner.php b/uniforce/inc/scanner.php index 00a8087..3cb5988 100644 --- a/uniforce/inc/scanner.php +++ b/uniforce/inc/scanner.php @@ -17,28 +17,28 @@ * @return array|bool|mixed|string[] */ function spbc_scanner_file_send( $file_id = null ){ - + $file_id = $file_id ?: Post::get('file_id', 'hash'); - + if($file_id){ - + if( State::getInstance()->data->no_sql ) return spbc_scanner_file_send___no_sql( $file_id ); - + $usp = State::getInstance(); $db = DB::getInstance( $usp->data->db_request_string, $usp->data->db_user, $usp->data->db_password ); - + $root_path = substr(CT_USP_SITE_ROOT, 0 ,-1); - + // Getting file info. $file_info = $db->fetch_all('SELECT *' .' FROM scanner_files' .' WHERE fast_hash = "' . $file_id . '"')[0]; - + // Scan file before send it // Heuristic $result_heur = Scanner::file__scan__heuristic($root_path, $file_info); @@ -46,7 +46,7 @@ function spbc_scanner_file_send( $file_id = null ){ $output = array('error' =>'RESCACNNING_FAILED'); die(json_encode($output)); } - + // Signature $signatures = new Storage('signatures', null, '', 'csv', array( 'id', @@ -58,7 +58,12 @@ function spbc_scanner_file_send( $file_id = null ){ 'cci' ) ); $signatures = $signatures->convertToArray(); - $result_sign = Scanner::file__scan__for_signatures($root_path, $file_info, $signatures); + $decoded_signatures = array(); + foreach ($signatures as $signature => $value){ + $decoded_signatures[$signature] = $value; + $decoded_signatures[$signature]['body'] = base64_decode($signature['body']); + } + $result_sign = Scanner::file__scan__for_signatures($root_path, $file_info, $decoded_signatures); if(!empty($result['error'])){ $output = array('error' =>'RESCACNNING_FAILED'); die(json_encode($output)); @@ -130,50 +135,50 @@ function spbc_scanner_file_send( $file_id = null ){ * @return array|bool[]|mixed|string|string[] */ function spbc_scanner_file_delete( $file_id = false ){ - + $file_id = $file_id ?: Post::get('file_id', 'hash'); - + if($file_id){ - + if( State::getInstance()->data->no_sql ) return spbc_scanner_file_delete___no_sql( $file_id ); - + $usp = State::getInstance(); $db = DB::getInstance( $usp->data->db_request_string, $usp->data->db_user, $usp->data->db_password ); - + $root_path = substr(CT_USP_SITE_ROOT, 0 ,-1); - + // Getting file info. $file_info = $db->fetch_all('SELECT *' .' FROM scanner_files' .' WHERE fast_hash = "' . $file_id . '"')[0]; if(!empty($file_info)){ - + $file_path = $file_info['status'] == 'QUARANTINED' ? $file_info['q_path'] : $root_path.$file_info['path']; - + if(file_exists($root_path.$file_info['path'])){ if(is_writable($root_path.$file_info['path'])){ - + // Getting file && API call $remeber = file_get_contents($file_path); $result = unlink($file_path); if($result){ - + $response = Helper::http__request( CT_USP_URI, array(), 'dont_split_to_array get', array( CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0, ) ); - + if( empty( $response['error'] ) ){ - + if( Helper::http__request__get_response_code( CT_USP_URI ) && ! Helper::search_page_errors( $response ) ){ // Deleting row from DB $db->execute('DELETE FROM scanner_files WHERE fast_hash = "'.$file_id.'"'); @@ -210,7 +215,7 @@ function spbc_scanner_file_delete( $file_id = false ){ function spbc_scanner_file_view( $file_id = false ){ $file_id = $file_id ?: Post::get('file_id', 'hash'); - + if($file_id){ $usp = State::getInstance(); @@ -227,11 +232,11 @@ function spbc_scanner_file_view( $file_id = false ){ $file_info = $db->fetch_all('SELECT *' .' FROM scanner_files' .' WHERE fast_hash = "' . $file_id . '"')[0]; - + if ( ! empty( $file_info ) ) { - + $file_path = $file_info['status'] == 'QUARANTINED' ? $file_info['q_path'] : $root_path.$file_info['path']; - + if ( file_exists( $file_path ) ) { if ( is_readable( $file_path ) ) { @@ -284,7 +289,7 @@ function spbc_scanner__display__prepare_data__files( &$table ){ 'cci' ) ); $signatures = $signatures->convertToArray(); - + if($table->items_count){ $root = substr(CT_USP_SITE_ROOT, 0, -1); @@ -298,7 +303,7 @@ function spbc_scanner__display__prepare_data__files( &$table ){ unset($row->actions['view_bad']); if( isset( $row->status ) && $row->status === 'quarantined' ) unset($row->actions['quarantine']); - + $table->items[] = array( 'cb' => $row->fast_hash, 'uid' => $row->fast_hash, @@ -310,7 +315,7 @@ function spbc_scanner__display__prepare_data__files( &$table ){ : $root . $row->path, 'actions' => $row->actions, ); - + if(isset($row->weak_spots)){ $weak_spots = json_decode($row->weak_spots, true); if($weak_spots){ @@ -463,17 +468,17 @@ function usp_scanner__display(){ // Progressbar echo '
'; - + if( $usp->data->stat->scanner->last_scan ){ - + $db = DB::getInstance( $usp->data->db_request_string, $usp->data->db_user, $usp->data->db_password ); - + if( $db ){ - + $table = new ListTable( $db, array( @@ -518,7 +523,7 @@ function usp_scanner__display(){ 'order_by' => array( 'path' => 'asc' ), ) ); - + $table->get_data() ->display(); } @@ -538,7 +543,7 @@ function usp_scanner__display__get_data__files___no_sql( $offset = 0, $limit = 2 } function usp_scanner__display__prepare_data__files___no_sql( &$table ){ - + $usp = State::getInstance(); $signatures = new Storage('signatures', null, '', 'csv', array( 'id', @@ -550,13 +555,13 @@ function usp_scanner__display__prepare_data__files___no_sql( &$table ){ 'cci' ) ); $signatures = $signatures->convertToArray(); - + if($table->items_count){ - + $root = substr(CT_USP_SITE_ROOT, 0, -1); - + foreach($table->rows as $key => $row){ - + // Filtering row actions if( ( isset( $row->last_sent ) && $row->last_sent > $row->mtime ) || $row->size == 0 || $row->size > 1048570) unset($row->actions['send']); @@ -564,7 +569,7 @@ function usp_scanner__display__prepare_data__files___no_sql( &$table ){ unset($row->actions['view_bad']); if( isset( $row->status ) && $row->status === 'quarantined' ) unset($row->actions['quarantine']); - + $table->items[] = array( 'cb' => $row->fast_hash, 'uid' => $row->fast_hash, @@ -576,14 +581,14 @@ function usp_scanner__display__prepare_data__files___no_sql( &$table ){ : $root . $row->path, 'actions' => $row->actions, ); - + if(isset($row->weak_spots)){ $weak_spots = json_decode($row->weak_spots, true); if($weak_spots){ if(!empty($weak_spots['SIGNATURES'])){ foreach ($weak_spots['SIGNATURES'] as $string => $weak_spot_in_string) { foreach ($weak_spot_in_string as $weak_spot) { - + $index = array_search( $weak_spot, array_column($signatures, 'id') @@ -627,7 +632,7 @@ function usp_scanner__display__prepare_data__files___no_sql( &$table ){ } }else $ws_string = ''; - + $table->items[$key]['weak_spots'] = $ws_string; } } @@ -635,45 +640,45 @@ function usp_scanner__display__prepare_data__files___no_sql( &$table ){ } function usp_scanner__display___no_sql(){ - + $usp = State::getInstance(); - + // Key is bad if(!$usp->valid) { - + $button = ''; $link = sprintf( '%s', $button ); echo '

' . __( 'Please, enter valid API key.', 'security-malware-firewall' ) . '

' . $link . '
'; - + return; } - + // Key is ok if ( $usp->valid && ! $usp->moderate ) { - + $button = ''; $link = sprintf( '%s', $usp->user_token, $button ); echo '

' . __( 'Please renew your security license.', 'security-malware-firewall' ) . '

' . $link . '
'; - + return; } - + // Key is ok if ( ! $usp->settings->scanner_heuristic_analysis && ! $usp->settings->scanner_signature_analysis ) { - + $button = ''; $link = sprintf( '%s', $button ); echo '

' . __( 'All types of scannig is switched off, please, enable at least one.', 'security-malware-firewall' ) . '

' . $link . '
'; - + return; } - + // Info about last scanning echo '

'; if( !$usp->data->stat->scanner->last_scan ) @@ -686,10 +691,10 @@ function usp_scanner__display___no_sql(){ date( 'M d Y H:i:s', $usp->data->stat->scanner->last_scan ), $usp->data->stat->scanner->last_scan_amount ); - + } echo '

'; - + // Statistics link echo '

'; echo sprintf( @@ -698,7 +703,7 @@ function usp_scanner__display___no_sql(){ '' ); echo '

'; - + // Start scan button echo '
' .'
'; echo '
'; - + echo ''; // Stages echo '
'; - + echo '' .__('Preparing', 'security-malware-firewall') .' -> ' .'' .__('Counting files', 'security-malware-firewall') .' -> '; if ( $usp->settings->scanner_signature_analysis ) @@ -721,14 +726,14 @@ function usp_scanner__display___no_sql(){ if ( $usp->settings->scanner_heuristic_analysis ) echo ''.__('Heuristic analysis', 'security-malware-firewall').' -> '; echo ''.__('Sending results', 'security-malware-firewall').''; - + echo '
'; - + echo '
'; - + // Progressbar echo '
'; - + $table = new ListTable( NULL, array( @@ -766,7 +771,7 @@ function usp_scanner__display___no_sql(){ 'order_by' => array('path' => 'asc'), ) ); - + $table->get_data(); $table->display(); } @@ -779,15 +784,15 @@ function usp_scanner__display___no_sql(){ * @return array|bool|mixed|string[] */ function spbc_scanner_file_send___no_sql( $file_id = false ){ - + $usp = State::getInstance(); - + $root_path = substr(CT_USP_SITE_ROOT, 0 ,-1); - + $file_id = $file_id ?: Post::get('file_id', 'hash'); - + if($file_id){ - + // Getting file info. $index = array_search( $file_id, @@ -829,17 +834,17 @@ function spbc_scanner_file_send___no_sql( $file_id = false ){ // ); // $file_info['weak_spots'] = $result['weak_spots']; // $file_info['full_hash'] = md5_file($root_path.$file_info['path']); - + if(!empty($file_info)){ if(file_exists($root_path.$file_info['path'])){ if(is_readable($root_path.$file_info['path'])){ if(filesize($root_path.$file_info['path']) > 0){ if(filesize($root_path.$file_info['path']) < 1048570){ - + // Getting file && API call $file = file_get_contents($root_path.$file_info['path']); $result = API::method__security_mscan_files($usp->settings->key, $file_info['path'], $file, $file_info['full_hash'], $file_info['weak_spots']); - + if(empty($result['error'])){ if($result['result']){ @@ -866,7 +871,7 @@ function spbc_scanner_file_send___no_sql( $file_id = false ){ $output = array('error' =>'FILE_NOT_FOUND'); }else $output = array('error' =>'WRONG_FILE_ID'); - + return $output; } @@ -878,37 +883,37 @@ function spbc_scanner_file_send___no_sql( $file_id = false ){ * @return bool[]|string[] */ function spbc_scanner_file_delete___no_sql( $file_id = false ){ - + $usp = State::getInstance(); - + $root_path = substr(CT_USP_SITE_ROOT, 0 ,-1); - + $file_id = $file_id ?: Post::get('file_id', 'hash'); - + if($file_id){ - + // Getting file info. $index = array_search( $file_id, array_column($usp->scan_result->convertToArray(), 'fast_hash') ); $file_info = $usp->scan_result->$index; - + if(!empty($file_info)){ if(file_exists($root_path.$file_info['path'])){ if(is_writable($root_path.$file_info['path'])){ - + // Getting file && API call $result = unlink($root_path.$file_info['path']); - + if($result){ - + // Deleting row from DB unset($usp->scan_result->$index); $usp->scan_result->save(); - + $output = array('success' => true); - + }else $output = array('error' =>'FILE_COULDNT_DELETE'); }else @@ -919,7 +924,7 @@ function spbc_scanner_file_delete___no_sql( $file_id = false ){ $output = array('error' =>'FILE_NOT_FOUND'); }else $output = array('error' =>'WRONG_FILE_ID'); - + return $output; } @@ -929,36 +934,36 @@ function spbc_scanner_file_delete___no_sql( $file_id = false ){ * @param bool|string $file_id */ function spbc_scanner_file_view___no_sql( $file_id = false ){ - + $file_id = $file_id ?: Post::get('file_id', 'hash'); - + if($file_id){ - + $root_path = substr(CT_USP_SITE_ROOT, 0 ,-1); $usp = State::getInstance(); - + // Getting file info. $index = array_search( $file_id, array_column($usp->scan_result->convertToArray(), 'fast_hash') ); $file_info = $usp->scan_result->$index; - + if ( ! empty( $file_info ) ) { if ( file_exists( $root_path . $file_info['path'] ) ) { if ( is_readable( $root_path . $file_info['path'] ) ) { - + // Getting file && API call $file = file( $root_path . $file_info['path'] ); - + if($file !== false && count($file)){ - + $file_text = array(); for($i=0; isset($file[$i]); $i++){ $file_text[$i+1] = htmlspecialchars($file[$i]); $file_text[$i+1] = preg_replace("/[^\S]{4}/", " ", $file_text[$i+1]); } - + if(!empty($file_text)){ $output = array( 'success' => true, @@ -967,7 +972,7 @@ function spbc_scanner_file_view___no_sql( $file_id = false ){ 'difference' => $file_info['difference'], 'weak_spots' => $file_info['weak_spots'] ); - + }else $output = array('error' =>'FILE_TEXT_EMPTY'); }else @@ -980,6 +985,6 @@ function spbc_scanner_file_view___no_sql( $file_id = false ){ $output = array('error' =>'FILE_NOT_FOUND'); }else $output = array('error' =>'WRONG_FILE_ID'); - + die(json_encode( $output, true )); -} \ No newline at end of file +} diff --git a/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php b/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php index a72c69d..59e9848 100644 --- a/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php +++ b/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php @@ -330,12 +330,9 @@ static public function file__scan__for_signatures($root_path, $file_info, $signa $verdict = array(); - foreach ((array)$signatures as $signature){ - - $signature_body_decoded = base64_decode($signature['body']); - + foreach ($signatures as $signature){ if( $signature['type'] === 'FILE' ){ - if( $file_info['full_hash'] === $signature_body_decoded ){ + if( $file_info['full_hash'] === $signature ){ $verdict['SIGNATURES'][1][] = $signature['id']; } } @@ -346,12 +343,12 @@ static public function file__scan__for_signatures($root_path, $file_info, $signa continue; } $file_content = file_get_contents( $root_path . $file_info['path'] ); - $is_regexp = preg_match( '/^\/.*\/$/', $signature_body_decoded ); + $is_regexp = preg_match( '/^\/.*\/$/', $signature['body'] ); if( - ( $is_regexp && preg_match( $signature_body_decoded, $file_content ) ) || - ( ! $is_regexp && strripos( $file_content, stripslashes( $signature_body_decoded ) ) !== false ) + ( $is_regexp && preg_match( $signature, $file_content ) ) || + ( ! $is_regexp && strripos( $file_content, stripslashes( $signature['body'] ) ) !== false ) ){ - $line_number = ScannerHelper::file__get_string_number_with_needle( $file_info['path'], $signature_body_decoded, $is_regexp ); + $line_number = ScannerHelper::file__get_string_number_with_needle( $root_path . $file_info['path'], $signature['body'], $is_regexp ); $verdict['SIGNATURES'][ $line_number ][] = $signature['id']; } } diff --git a/uniforce/lib/Cleantalk/USP/ScannerController.php b/uniforce/lib/Cleantalk/USP/ScannerController.php index 6e6f7f4..a585540 100644 --- a/uniforce/lib/Cleantalk/USP/ScannerController.php +++ b/uniforce/lib/Cleantalk/USP/ScannerController.php @@ -531,31 +531,37 @@ public function action__scanner__signature_analysis( $offset = null, $amount = n ) ); $signatures = $signatures->convertToArray(); + $decoded_signatures = array(); + foreach ($signatures as $signature => $value){ + $decoded_signatures[$signature] = $value; + $decoded_signatures[$signature]['body'] = base64_decode($signature['body']); + } + // Initialing results foreach ( $files_to_check as $file ) { - $result = Scanner::file__scan__for_signatures( $this->root, $file, $signatures ); + $result = Scanner::file__scan__for_signatures( $this->root, $file, $decoded_signatures ); if ( empty( $result['error'] ) ) { - $status = ! empty( $file['status'] ) && $file['status'] === 'MODIFIED' ? 'MODIFIED' : $result['status']; - $weak_spots = ! empty( $result['weak_spots'] ) ? json_encode( $result['weak_spots'] ) : NULL; - $severity = ! empty( $file['severity'] ) - ? $file['severity'] - : ( $result['severity'] ? $result['severity'] : null ); - - $result_db = $prepared_query - ->execute( - array( - ':status' => $status, - ':severity' => $severity, - ':weak_spots' => $weak_spots, - ':fast_hash' => $file['fast_hash'], - ) - ); - - $result['status'] !== 'OK' ? $out['found']++ : $out['found']; - $result_db !== false ? $out['scanned']++ : $out['scanned']; + $status = ! empty( $file['status'] ) && $file['status'] === 'MODIFIED' ? 'MODIFIED' : $result['status']; + $weak_spots = ! empty( $result['weak_spots'] ) ? json_encode( $result['weak_spots'] ) : NULL; + $severity = ! empty( $file['severity'] ) + ? $file['severity'] + : ( $result['severity'] ? $result['severity'] : null ); + + $result_db = $prepared_query + ->execute( + array( + ':status' => $status, + ':severity' => $severity, + ':weak_spots' => $weak_spots, + ':fast_hash' => $file['fast_hash'], + ) + ); + + $result['status'] !== 'OK' ? $out['found']++ : $out['found']; + $result_db !== false ? $out['scanned']++ : $out['scanned']; }else return array( 'error' => 'Signature scan: ' . $result['error']); @@ -773,7 +779,7 @@ public static function action__scanner__controller___no_sql(){ break; } - $result = self::action__scanner__signature_analysis___no_sql( + $result = self::action__scanner__signature_analysis___no_sql( (int) Get::get( 'offset' ), 10, substr( CT_USP_SITE_ROOT, 0, - 1 ) @@ -907,9 +913,16 @@ public static function action__scanner__signature_analysis___no_sql( $offset = 0 ) ); $signatures = $signatures->convertToArray(); + $decoded_signatures = array(); + foreach ($signatures as $signature => $value){ + $decoded_signatures[$signature] = $value; + $decoded_signatures[$signature]['body'] = base64_decode($signature['body']); + } + + foreach ( $files_to_check as $file ) { - $result = Scanner::file__scan__for_signatures( CT_USP_SITE_ROOT, $file, $signatures ); + $result = Scanner::file__scan__for_signatures( CT_USP_SITE_ROOT, $file, $decoded_signatures ); if ( empty( $result['error'] ) ) { diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php index 325ff45..6621544 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php @@ -54,9 +54,13 @@ public function check() { if ( $signatures ) { - foreach ( $signatures as $signature ) { + $decoded_signatures = array(); + foreach ($signatures as $signature => $value){ + $decoded_signatures[$signature] = $value; + $decoded_signatures[$signature]['body'] = base64_decode($signature['body']); + } - $signature_body_decoded = base64_decode($signature['body']); + foreach ( $decoded_signatures as $signature_body_decoded ) { switch ( $signature['attack_type'] ) { From 602504da74de51c47d6ffa670e8357c804794cdf Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 13 Sep 2023 10:55:44 +0500 Subject: [PATCH 40/45] Fix. File viewer weakspots fixed. --- uniforce/js/scanner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uniforce/js/scanner.js b/uniforce/js/scanner.js index bc3da53..e0e221e 100644 --- a/uniforce/js/scanner.js +++ b/uniforce/js/scanner.js @@ -22,7 +22,7 @@ function spbc_scanner_button_file_view_event(obj){ function spbc_scannerButtonFileView_callback(result, data, params){ console.log('FILE_VIEWED'); - var weak_spots = result.weak_spots.match(/\d+/g); + var weak_spots = Array.from(result.weak_spots.matchAll(/[{,].?"(\d+)":/g), x=>x[1]) var window_height = window.innerHeight; var row_template = '
%s

%s


'; var row_template_weak_spots = '
%s

%s


'; @@ -107,4 +107,4 @@ jQuery(document).ready(function(){ spbc_scanner.start(); }); -}); \ No newline at end of file +}); From 953c146b707b189230b76b36cfbb417fb3fc1607 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 13 Sep 2023 14:06:29 +0500 Subject: [PATCH 41/45] Fix. File viewer modal CSS fixes. --- .../css/images/ui-icons_444444_256x240.png | Bin 0 -> 7126 bytes .../css/images/ui-icons_555555_256x240.png | Bin 0 -> 7110 bytes .../css/images/ui-icons_777620_256x240.png | Bin 0 -> 4654 bytes .../css/images/ui-icons_777777_256x240.png | Bin 0 -> 7147 bytes .../css/images/ui-icons_cc0000_256x240.png | Bin 0 -> 4654 bytes .../css/images/ui-icons_ffffff_256x240.png | Bin 0 -> 6523 bytes uniforce/js/scanner.js | 35 +++++++++++------- uniforce/view/check_requirements.php | 28 +++++++------- uniforce/view/footer.php | 2 +- 9 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 uniforce/css/images/ui-icons_444444_256x240.png create mode 100644 uniforce/css/images/ui-icons_555555_256x240.png create mode 100644 uniforce/css/images/ui-icons_777620_256x240.png create mode 100644 uniforce/css/images/ui-icons_777777_256x240.png create mode 100644 uniforce/css/images/ui-icons_cc0000_256x240.png create mode 100644 uniforce/css/images/ui-icons_ffffff_256x240.png diff --git a/uniforce/css/images/ui-icons_444444_256x240.png b/uniforce/css/images/ui-icons_444444_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..ecfe4eb42b04f914d02f60113b4e21f8bf4c915c GIT binary patch literal 7126 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 BLJa@_ literal 0 HcmV?d00001 diff --git a/uniforce/css/images/ui-icons_555555_256x240.png b/uniforce/css/images/ui-icons_555555_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..580fcd1a40160d16d137135b93301f6723d08d4e GIT binary patch literal 7110 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 i`r4LJ)!E9@qYkFuuO*l literal 0 HcmV?d00001 diff --git a/uniforce/css/images/ui-icons_cc0000_256x240.png b/uniforce/css/images/ui-icons_cc0000_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..9e23bc6bfad663260641e69cb39ce4febdaa2a35 GIT binary patch literal 4654 zcmeHLhcnz=_y2x(^{@k z3T)9p&*pIh!=p1HMrJiP zw$|+ffd7sE_5eq%ed(Fk>@k+6R)C#;#0A;x`KI369(k2XuM6x=!@nck?^!+2OUrO| zHo<%ObMSS9eJSdC{CT{5 z>r-lC1);Na@j=g7cmUAFNM8p?q~u4QEvPXDCc4z$Y3W(TAdwuxk~;`u;8oG-A;~UA%4S!zKbd)gjb~S)6)coIr;?EYwebW zUcAq4b?f7R%|);ginx(@23DbHw@H)nN-{i%DqK+-Y$4NnAlwDASX`p#NdX z3PBKk9#;ejiaPp^hiOWtvZsFD=UUyRlZC9THbSzLxl_T>C<0wRrXMdTL&hhF#_d)rGOhESIEBNyVFIwB^COLPL zMpx$_Jmh+^N$WqsY-m?SH5_KP!Yxl=oxA)7G8+NpvZNO<8zvQi4}q*`55*51aW)9r zEuAN?cZFkxN2BgOQ0va(&TJ2EpfLuu42b9%6{R8cl=ZEE z0abp-HGT*0eY!@*idVl#wV87wi3RW5vZ)$Xgp%fvx47Jw(%6Zab$Tb)9O7BP2nfwF zPn_9-o6Pw)!H<=%$f-(x?tP|^Fc#Oyj~thXgZnE_2fUc)X(;AgU(8a|nKNU~KuS?l zNF(tOmQj`IlF4RzhwY>JNwQ|yR{jj}~ciQpQCuT+|8_L%|Msd~RTb(<% z{6+?W45_V!VIHTyd)Apui4BbdyxsRPV*yD4C6MOI)^H0}4T!Ya_Ne_;!eu82n)*kuEEp?OaN%iK8N1lo$XWjp7?zf+`&(Y(JvOtzP(=CAtAU|ws2b_#jhh6vsm*Ki8g3@j4u9uZr za#u-qaSvksCsfy%k7<(0Z#>bXqxRC5d4~Gt&Q0_rW9Lg%aT^?pk)1w%gFBcz#F1Jz zps)h|!c1BiE&W~^-26`Hy*O|H9bcB~+w&Q9LIr-#qklW3Xo_I>Z=gX+%)~XVxH*`F zChcdld^E$Qvdo`2_pel}WL%SBOLWrC#cAy@laF5dCX(Kw{sgXlPcOV9z>T988qsSr z%u81ImEtW%Z0K_dM|lS-uvl6a+n#f=ejt2{REhX`-%{b;IZADhh$mX^ph`=HRJ0tV zbI=c`FO9j|G(4Nw&j7(+XifAiN!&9*J5bnV%_?h(rHO%uR(39m7zuB}xLpI0hl9AC zt9n&DhK~bra77-IqK7RM;e{fZNDL;g%i@y3EJoRvGT3RDcAH?bu;7x}23_=$^gXcU z%6lj@FWH|nX$BgJ)I%arm@gWCLbBi?Y!#(d6Ggtso^(^Y+FzVkG51$cBm34Ax+o~q zLEJ-Ld1vP@qi;Zcoh}{1bKUSSTP3%rqW)t-m*vGSS%ZKgPVWXvvO9) z(`(PxbXa>_xk(5KA6heDdQAI`{UsPf$&M*6vngik^HZqNw6{3tO{vw@Cj`gWjj(uvb2K zgTv^UUI?0QMcc+91;p`qRUP85ogmn^Ht!NzWN7N{9!O0J{K)DC4RM2~k;XW0OQAhQ zKL&N=2))f6+g5Mzw_!;3CMgW}rs^~cuu*^NQhI-gvPUZLhrlMpNLV^LBEYPxXRQ1p zVio(F{M#SG7K9KmNI#*t2M@e+XGH*tM#&28by8$#NPSQmKl2-%aV~DB>Cb)AEnR7oVosQgimIJ8YtWMiD+C4weSS>l-<_!SI~s5U8yZ zd_^*F?-;m*cf+&fYG+9rVz&0U5(-Id{Y*`TnW2iWvHzrZ*Uzoz8fuh38n5E~NFthr zAExz}{MIZ)Mq5L9T?c%_X7X>=!w9~;TF1#%MO!mtZdD-&rtZnBqZ+;PP`M@D{BKt0 z*1KQ>x9#=&4Rs4sG+sOYj0eLiAve5b)p8x`V|;r@_Pi8CGvuDs9PC^FrP?{RD?@aB z(GrxwR88(t40wrZP|Z{JfJ8~7?$&AMH1>1%1iWD!9M?M5%)76#aCr5JmVZ}U1~ZHr zd3^&V;lLR8b)tOf<6LS-_eUwq4VCM19DbM%+F}oC7SK%KT4d-c`1ESet4Pl}H75v} z0-IIdf~L$QnYr;rbdBFhx0xnjZiVTmvItJkG}&g?z?t4D_TS4eP#1Z?gi<5CnIPz9mys~{e zy&>iqp!J;CqH?q(TD|l4$j*DY^G-gHS7eAuw8cm73N&X@Qtd{-EnpSp*y_P>$PjvI z&OP_J=);|Pq8Tymcn5hR-`ztj3`~^3>68!TZ)m8-KCU5hz7Bc+i4`F^_uJCnW69^F zI_Btg`}42{7{eel6B82yWbMk$NIt^|DQs7KLS`_7lByLyz#Jo8I5a^;ttm?tfl2`t zpoXVyH2Ob^_XXC5Z0`ofaG6u`KX)$AY4qOxeYp55fh#eXA=|^#(=#xh8(-UMGD|5? znsj4YfTy8sIatm4l0M?-u@4pT(2G3f$Mks;?ag|L&lNB}4+m>}#}j~b~kUJYWU z{%1btg6c-(2=b`WKnv3QvU%XvE3`;?J)`UJPJxH3{Ia#3pkobBpPmdsal zO|a})=Dgr6ymNb(Bsk2n^MT~2=!0+o9IDzJ0f%@v^T@?)Wr-&A!exX-WHvy z(sRuT;ze(%ea#G+&wUkz@G52=hi;EL{Ovx%Xn^Abt!W~it^I49lQ}<%pX_bBIIjgv zHV}}Ub}hBr8)a$E^}3Mz%i)~<5W2x7(9|j~ey^4_l?P_!FaL~dzDg58)w>j=WM+G1 z9fo@ryZfYpH`XP9o-33^f)O9?RX9AK37b3LV|_?ZTy5&aIloj;Sy3BVu*yry)8T?2 zu10dQxDbT{OU}Qw51dK-Zl|w8g)SLC-rOmpB59O(-?(!$bafJ5IC1~at3EN&NDoy1 zSqaK5biLV&(-B->H$Q0=aZ(2nqHr3~AOm6@mIdX9-r8;1O7Yr_CmoDc>kl~^I7a?8v*|7TN(y91mW@dD{>!!$V%%U zV?SUijAD6oKZAu$e-4e7YIA>iIxyy=3!`Q~8uFiwhX!j9(pxhwuPJbs+|3>Ny==C7 zzK0VZI{8DHMzn=Taw4D^#mt6e3&E!yj2K~0^9`Tqr=O&;ncL&q#hMp<9PGFWk6A&K zwU_Sbg|eY}`FRV@El-m_3ya-4)Nn{ziklD+?jLnPL3fSIwXZiFCN6Or z3uB|S5EW`KjzYRTGs(Fk=;-Fyel6U_`v+RBza%*p-s9vfR$H!u^HTWc$kGQ%2@N!H zG`pc&=1=R7lC7n4Cy!cEq0+5 z4K+T+*X0)}QjBS0mOov3jKwO3>d@EybS0X!(ErJtGu-0o9{EWTLl6=sHGfeMJ{9!p zL+aqmd|43X2R4IPb`Hc|ljW~m4s-tiX%rLWYqg>kDposWznULfNmWQbyl?vp#m~Fq z3)+xNOR*^Wy6s;FP4r5;Ej6wP!2Yz<6{<GGhCw>W| zYlm@j#<;7v2DzUJKo%}5Cka=Ql##KPg{#2jRODr^z|Ryo1NHh@OXa@^egSUY9$|ot zvXX+N3|vz7sx@3mMMmN5=)Z&ueOA2t?^cIwA-p zQbX@jg^S<)?tOmud+t9w=j`sgb7p6rnK|zXd!(aEMsk}3001&|H6?uj09_A(U1I#} zt8#{`+_hk}RMXc60B=qJKm`H7+4U_H4glO?0I+Tb0Foa8fX+FsSx@@<0oX!QRSCEX zi5_gfo)IC`wUvohA*58?f)RU`-2gx@sjeh%;5oAecZh)ZJVi#)aPxfm;`52qr6$f# z7{sR8rg`sXkk!p>dnEp?1a$pfncKPUC1WMTy^duqvR_l)>;x$2)7il#Vr0cyOz85P zWQ`xE8Waf7Zdc&ti|4>(V!84Z>Zma<}Hc5@-Q=50Jz!$bTLsNMPgyh=FD$np_)CX&fRkdpvNsll6<= zch+yXiuomQRwT_hX@viTKTpc|Ee8g@>3t2j%b+TI+FsDKoKs*+@OQ9nghh zp$*OX$N=;8$8c+Ic#z)=W|ZcKk>dy%bkRV27h7{6o~kfD!M|1qp%iBaf&a1e#ce%0 zvrRcQ70+#^qk%`&7an4oG9Ogvts%Wn0}RyN`*AuF0VxBGKtQiT1xYe!`sue z$&F4QPjY>KKD$(uI#Wp~Q_Yh%$D!6E z?>gTdF4q}V&h@fn{>(mWIxj-=Q|N#s4_BPV@cJa;g$Q*4Xw)VtYm=`pu!Atzd8q!b z4#{zUmHp$zUd6%Bv6i77jdIM}Z}|wXi(emH*T#+J0odl7vY}-)R7ozX2DI*3M$JYc z;}Iw0uv!^dGke5bvuP`vqJF@js><6auisQk6F8A5RF$UwIu%};Uxpwpa%I_>=LrNYW0;4^9Y zlkrMEkdJG{2h}tRR(3y^=RT%#O7}wxuxP6PjK08bQN}052cYLwUW2|R^L=EGEW=mw zYs+(awN8?1k0fc*8 z!^0cBKc|um#QW2eq}tzF9~kFsg7l?4r-K>{PtwW`Rcs}U)KWG7B+9kWIZa^{z80t$ zQT)oRb%;hmRi^PCkRn$km7j^86(0~fd!5gn+y00pen^Z_?9F^0@#T5fEW^^grPc&L z2Aud_!R3zU%om#1G}N!-X`=)&hbrLBsmeR*dSIl+yVkXV)EF<1SZN!5J)Z6&scakZ zqTMuy)H1f7+E#(QY2<}Wn_~(0ftL(d&wUF@A;Jb>C6B6z>a~>ZsXo6e zFb%oQN|9VcZ+bTvCvMBpbaIh%CfE`aQgbO3O1EZlje z0QHti9I@>x^OLQEu+DUWoPC0mc9=rOt!GFU;Xz%hZzEsy4O*jIQYl~$j#TMkj`*QJ zEFv=bSuHgG5vo7b@o}_?!P}4aWl>LP$s?Le@bLZR-Y2_R1=8BL+~N~K)KJ3XnMCHo z*II@q=29dIe*yjlVWBT2&#ieueo@6*lGfrOPq#H7lBM|P3?$IyNWTp43Et4Gl4@y3 zPPI>GzFo~Vy-IOyIbYk9TfJ#omRuol?G6$5_XS}^%24){g2a4+6E|E{eN4MjMD+r^ zhh60r5$gj0xRhUK;v=5xq;tkYM`M*-HaR&zRNQN%&cNHfx6z;cpsiA1JEao48ic7_ z;8rKOC;p^&!o54_=^&)tR>1aoyS=N8wnqCTbwgFK`Xvihg&-Qq@dQXZGwX|YI6CyHUi_7aueYmM$c6qQfIG5s=`!re{7ac73JF~nI5 zH|y!JjEA=`em_&<^Bh7RYw}hy7!y;N;!Dfm6CiTv~jS z_Y+I6hWAt#evL6Ut@mOj+X}m4-|x~S^GAO?mF7my#$5^cba`J6+=qADIzPQDr|$nW z&BE;^Z>IMea?WIo;Q}f{hF$zW9z=3XYh#3=}bcs(%6NO)b@7t7E85A?h3lPHnf^5-ddFyZR z3AyjYVZ--GkVaC8C09)IMA%#)X6~>Rfl7vzd48gV?V zdfGrnE-fsb%F)0&WvgTQ(5m#?dbzE$M>GH<^rrmEM2gwEqawtA+V%-r}_PhuP4c2Kg`W! z)OcjI^Kmt~4&;-;pB%j~#&0v}EyzwU4&=>rr6pyyv3#gZS|p}uGE$5HB+mAHEpYf@ z*UN$etzQv{qq{>y#LQBCbd-8{|EKdwe>X+o7ffdbu9J6$4vGensH%)-E7Q&9MLjh? zkt|iGOEBNbVK>!V;vMuV#6O*&Tmp7ERL>7bO6MFyYRyzxd=KB7Dx}th6=}jXrjGl~ z15J_{ez9whv)dDXN@QSvf=gj5rxrYCtg3WJyzz9yl3I4<7??{8YBM60M4&584l_=Zqn8@iM^qRl$ zT{m+4!o*wVqpaEbHN!79^lX9;8kQp^?x8m9C)K#a;|D!_ z&|Eg-ifrIA!WN_@`@ z8Li3@p=r}wM+3DBTKH}QReJHed88@A0MN&E5S5}9qEj|+Y!hvPq* zb3YS8X-l#H$yy(vmyom`BGAj@_-vVTt$b7dQNl5I`?1pm2f)OJxm&cwkZDxq)&IL}3_W{43vQ6k z6`G9Vz8ukO=5!UG*jaQP=0-;XFS4Yj12H6X*>v>aw8FNG@5hIZ@A`+cFNBI5#%kby zcnEAnitSAr;W}1+3CfSXFvZ9zSXG|f&zR=tq|Q$?9X9=a=Dv)R4bo5KXW-4f0)SaGs;3qt-ZYw+hQaudUeI4^?v+CGP}&Ap}%1-b5I~yCa&5 zQ-egQ%55=Ik-5w7mG4Z(?q^bsGDs@2WJgnua@BpX+ejNmZqX`uFWvJE4$UZB4Z{f` z^fgUQ6eR3JKEpEA;GvZRaDl^j7iH@075;fqHdDaVEdA zBe}#-N;9&JSXMC&ib$uer9NaJ`LyZzDJe($Gs7S3Iu;&13XE8A1kVFRsAe?Wk|Gve|`3`M4wEGZx#e|Zm zgN3QTQITiV1&r~X)4t8M&3ObE4w}hw?y@p>2A-OGxx6g7Occ!|r%V!={K;)>`0@zC z^^j?B;)>^-a_Vcd;h)m6<_iMlOOemd9uPka# zz#1jf``uuL*Lj1{QYmSV#x&Y}nt?Vv?3FX!@5T7056rX}x?Q~4c#%0mX4lOoio)l9 zEONKFJ%a59=`gnRBp0OQv=|Jx1i74{Y$4k2qF{G}f3IeFlj+M?xxghIu*=s%FhpgS zk#H999upr$^Ww?UzEAdsr%rNxg5$4Doy2s@h6AoN{3;OZ5vkSKKJ9fO*u}dLqOJB1 z`N-EW`!XHl3YYHhQu|<*5W!cX@-E8K0usPp?++GRJ(PpWwti#Imv6nHlgJXXbF-pK=R;PI(`#j1sM^cCi; zqb|_hTvWpAO4uJZ=W1f5;k8_#9i{>Pp_tka8EsUn$HNWJt&ANk>#EZ2=%wz5F|^dz zX&y?uKPXqiPw5Z1otqE|9=lo>MLaVj3I!eF!}CiIgXCul4+zR<>Qb{mI?xmEPiwdi zD5gp>5DO82;hJKMl68n%+;GIY^k1n(pO!vq} zn=-opx|^c733~Ch13-oB#Zhk{vM}tK< zSc1*9PH6R3Y>}D_&1@fA$I3|x>_fG6W1R%;(>?yY&_^pz-JY@ibzeaOez#A#v}px z`5E!6v~PSpP}DYHMA*xCQM>r=?ieX?uro=8B4E^;kXI^e*rNVtaRl!*$tvdFW&|s5 z)@Op=S55uYSf2RoQX&{ZHPOO&^d$pY`-=m8Q^{I_kAFfN+6woIsdnPc(Etha+E3gG9lD~+Uzw#HOqS<0OTfAkfd^JNJr z|MgLh80?zU`ER!4e|B`JaOR>01f#+|z`R3%DiBs=q!2+e<@}9j=hyVi*cPNqL$%Jl zx;tzhFpMj-XLQ4js;m&Nj|OuPQPwPL*Z3&+7e}=;tinW(rS(^#uuDIb2g1Rk-do%le0Y4 zK@vK@!)Ag+nI;8k%%t%5S=B225zm5gWCo)hwTXQ5d~?Bn19Oy`{y!)bMGgSMPa91P zaDZk`qyaPj43*T#R1EyYRANJ>wNI2#wKfMY!1w>jN!yM5nl$?^u&)DfYxJ$%Uwat`V9 zV?j|^?NfA)M)K>M$}yIn@PBR>_hx+S5@hJiSlSc@xh?}6H?v$;KeBu3P?B zq(v=G;e(&Po*(~Qd4@Tt5#i^rerQ$LvxguvVN2JAx)7lmEK;Y#*A9uC41uTQHs z#9ny?#>|76c_s1p63U|mFiJ)H%L|h=1Ko#V9Id>5A|d8^9+MbD}|R}5VYP}FLwV}eP_rtLQll^>k@$`(&tQ&4vr)TcJr$L z?B@>uil-lBHw3##UG%G7ES>r`er73k)khkA%N!=lXrDWc7r9#5!rQ7%k@j2MaT{c? z4{jksXH$CwUAZ^m)~PlrlX9o}*r;SS^0=bwnsc9buRf@WKe;LM?qZ#VlF*kFhv!Lh z)13$OxUjAc_@AMK9 z%SEx5JgFI_nWdK&r=Tsin8&`4c=JSnY^Z_Ew=G{@s0a0RY<2M?@Y?d zo%{xZqJA5Z4VK)aE%yEXbSKhX1wOC%4eU-r9V~g^a zZ@w=zoyi<3Im5zIsd=$sg21_SUYLLAaJ{6zT~3zQO~XfA`_a<$dsg&~3GwC0_Rd84 zwzEYcv>A39?}X5e0vq4ZKj{=srw^Ujgt9Th${b-WY3XWxEdW87pb!)$4iykE5QIs> zgd~Lpd12QB*bSo1>+^|!034jG>} @@ -29,23 +29,23 @@ - + Universal Anti-Spam Plugin by CleanTalk - + - + - +
- +
Cleantalk logo @@ -70,14 +70,14 @@
- +
- +
- + + - @@ -85,9 +85,9 @@ var security = ''; var ajax_url = location.href; - + - + From 892b411eebd808854c27a3e2c95d448120ac7c00 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 13 Sep 2023 15:22:56 +0500 Subject: [PATCH 42/45] Fix. WAF fixes. --- uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php index 6621544..a942318 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php @@ -57,21 +57,21 @@ public function check() { $decoded_signatures = array(); foreach ($signatures as $signature => $value){ $decoded_signatures[$signature] = $value; - $decoded_signatures[$signature]['body'] = base64_decode($signature['body']); + $decoded_signatures[$signature]['body'] = base64_decode($value['body']); } foreach ( $decoded_signatures as $signature_body_decoded ) { - switch ( $signature['attack_type'] ) { + switch ( $signature_body_decoded['attack_type'] ) { case 'SQL_INJECTION': - $this->waf_sql_patterns[] = $signature_body_decoded; + $this->waf_sql_patterns[] = $signature_body_decoded['body']; break; case 'XSS': - $this->waf_xss_patterns[] = $signature_body_decoded; + $this->waf_xss_patterns[] = $signature_body_decoded['body']; break; case 'EXPLOIT': - $this->waf_exploit_patterns[] = $signature_body_decoded; + $this->waf_exploit_patterns[] = $signature_body_decoded['body']; break; } } From 79292e64ce4a3f8a07bff165b6cc1fa78458500e Mon Sep 17 00:00:00 2001 From: alexandergull Date: Wed, 13 Sep 2023 17:03:58 +0500 Subject: [PATCH 43/45] Mod. Styling. --- .../Cleantalk/USP/Uniforce/Firewall/FW.php | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php index 42af462..60471d1 100644 --- a/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php +++ b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/FW.php @@ -336,8 +336,9 @@ public static function update( $api_key ){ array( 'get', 'async' ) ); - }else - return $result; + }else { + return $result; + } // Write to DB }elseif( count( $files ) ){ @@ -374,8 +375,9 @@ public static function update( $api_key ){ array( 'get', 'async' ) ); - }else - return $result; + } else { + return $result; + } // Write exclusions }else{ @@ -399,8 +401,9 @@ public static function update( $api_key ){ rename(CT_USP_ROOT . DS . 'data' . DS . 'fw_nets_temp.storage', CT_USP_ROOT . DS . 'data' . DS . 'fw_nets.storage'); } - }else - return $result; + } else { + return $result; + } } } @@ -570,14 +573,15 @@ public static function update__write_to_db( $file_url ){ return array( 'error' => Err::get_last( 'string' ), ); } - }else - Err::add( 'Updating FW', 'No data to save' ); + } else { + Err::add( 'Updating FW', 'No data to save' ); + } } - return $inserted; - }else - Err::prepend( 'Updating FW' ); + } else { + Err::prepend( 'Updating FW' ); + } } /** From 74c065743be4c1e25a4dc75e78bdb005d8bcf07b Mon Sep 17 00:00:00 2001 From: Glomberg Date: Tue, 5 Sep 2023 08:14:30 +0300 Subject: [PATCH 44/45] Fix. Helper. PHP 8+ compatibility implemented. --- uniforce/lib/Cleantalk/USP/Common/Helper.php | 131 ++++++++++++------- 1 file changed, 84 insertions(+), 47 deletions(-) diff --git a/uniforce/lib/Cleantalk/USP/Common/Helper.php b/uniforce/lib/Cleantalk/USP/Common/Helper.php index 0831db0..ea6308b 100644 --- a/uniforce/lib/Cleantalk/USP/Common/Helper.php +++ b/uniforce/lib/Cleantalk/USP/Common/Helper.php @@ -224,53 +224,76 @@ static function ip__is_private_network($ip, $ip_type = 'v4') */ static public function ip__mask_match($ip, $cidr, $ip_type = 'v4', $xtet_count = 0) { - if(is_array($cidr)){ - foreach($cidr as $curr_mask){ - if(self::ip__mask_match($ip, $curr_mask, $ip_type)){ - return true; - } - } - unset($curr_mask); - return false; - } - - $xtet_base = ($ip_type == 'v4') ? 8 : 16; - - // Calculate mask - $exploded = explode('/', $cidr); - $net_ip = $exploded[0]; - $mask = $exploded[1]; - - // Exit condition - $xtet_end = ceil($mask / $xtet_base); - if($xtet_count == $xtet_end) - return true; - - // Lenght of bits for comparsion - $mask = $mask - $xtet_base * $xtet_count >= $xtet_base ? $xtet_base : $mask - $xtet_base * $xtet_count; - - // Explode by octets/hextets from IP and Net - $net_ip_xtets = explode($ip_type == 'v4' ? '.' : ':', $net_ip); - $ip_xtets = explode($ip_type == 'v4' ? '.' : ':', $ip); - - // Standartizing. Getting current octets/hextets. Adding leading zeros. - $net_xtet = str_pad(decbin($ip_type == 'v4' ? $net_ip_xtets[$xtet_count] : hexdec($net_ip_xtets[$xtet_count])), $xtet_base, 0, STR_PAD_LEFT); - $ip_xtet = str_pad(decbin($ip_type == 'v4' ? $ip_xtets[$xtet_count] : hexdec($ip_xtets[$xtet_count])), $xtet_base, 0, STR_PAD_LEFT); - - // Comparing bit by bit - for($i = 0, $result = true; $mask != 0; $mask--, $i++){ - if($ip_xtet[$i] != $net_xtet[$i]){ - $result = false; - break; - } - } - - // Recursing. Moving to next octet/hextet. - if($result) - $result = self::ip__mask_match($ip, $cidr, $ip_type, $xtet_count + 1); - - return $result; - + if (is_array($cidr)) { + foreach ($cidr as $curr_mask) { + if (self::ip__mask_match($ip, $curr_mask, $ip_type)) { + return true; + } + } + + return false; + } + + if ( ! self::ip__validate($ip) || ! self::cidrValidate($cidr) ) { + return false; + } + + $xtet_base = ($ip_type === 'v4') ? 8 : 16; + + // Calculate mask + $exploded = explode('/', $cidr); + $net_ip = $exploded[0]; + $mask = (int)$exploded[1]; + + // Exit condition + $xtet_end = ceil($mask / $xtet_base); + if ($xtet_count == $xtet_end) { + return true; + } + + // Length of bits for comparison + $mask = $mask - $xtet_base * $xtet_count >= $xtet_base ? $xtet_base : $mask - $xtet_base * $xtet_count; + + // Explode by octets/hextets from IP and Net + $net_ip_xtets = explode($ip_type === 'v4' ? '.' : ':', $net_ip); + $ip_xtets = explode($ip_type === 'v4' ? '.' : ':', $ip); + + // Standartizing. Getting current octets/hextets. Adding leading zeros. + $net_xtet = str_pad( + decbin( + ($ip_type === 'v4' && (int)$net_ip_xtets[$xtet_count]) ? $net_ip_xtets[$xtet_count] : @hexdec( + $net_ip_xtets[$xtet_count] + ) + ), + $xtet_base, + 0, + STR_PAD_LEFT + ); + $ip_xtet = str_pad( + decbin( + ($ip_type === 'v4' && (int)$ip_xtets[$xtet_count]) ? $ip_xtets[$xtet_count] : @hexdec( + $ip_xtets[$xtet_count] + ) + ), + $xtet_base, + 0, + STR_PAD_LEFT + ); + + // Comparing bit by bit + for ($i = 0, $result = true; $mask != 0; $mask--, $i++) { + if ($ip_xtet[$i] != $net_xtet[$i]) { + $result = false; + break; + } + } + + // Recursing. Moving to next octet/hextet. + if ($result) { + $result = self::ip__mask_match($ip, $cidr, $ip_type, $xtet_count + 1); + } + + return $result; } /** @@ -300,6 +323,20 @@ static public function ip__validate($ip) if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && self::ip__v6_reduce($ip) != '0::0') return 'v6'; // IPv6 return false; // Unknown } + + /** + * Validate CIDR + * + * @param string $cidr expects string like 1.1.1.1/32 + * + * @return bool + */ + public static function cidrValidate($cidr) + { + $cidr = explode('/', $cidr); + + return isset($cidr[0], $cidr[1]) && self::ip__validate($cidr[0]) && preg_match('@\d{1,2}@', $cidr[1]); + } /** * Expand IPv6 From 5bb68fece99345839d09bd3c3b41e955a66130c3 Mon Sep 17 00:00:00 2001 From: alexandergull Date: Mon, 18 Sep 2023 12:37:06 +0500 Subject: [PATCH 45/45] Fix. HTML validation error on install. --- uniforce/view/install.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/uniforce/view/install.php b/uniforce/view/install.php index bbacfda..8d9ed33 100644 --- a/uniforce/view/install.php +++ b/uniforce/view/install.php @@ -50,7 +50,7 @@

Warning: Couldn't connect to cloud SQL. Malware scanner will use local database to store scan results.

- +