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 0000000..ecfe4eb
Binary files /dev/null and b/uniforce/css/images/ui-icons_444444_256x240.png differ
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 0000000..580fcd1
Binary files /dev/null and b/uniforce/css/images/ui-icons_555555_256x240.png differ
diff --git a/uniforce/css/images/ui-icons_777620_256x240.png b/uniforce/css/images/ui-icons_777620_256x240.png
new file mode 100644
index 0000000..4c889d2
Binary files /dev/null and b/uniforce/css/images/ui-icons_777620_256x240.png differ
diff --git a/uniforce/css/images/ui-icons_777777_256x240.png b/uniforce/css/images/ui-icons_777777_256x240.png
new file mode 100644
index 0000000..b952d12
Binary files /dev/null and b/uniforce/css/images/ui-icons_777777_256x240.png differ
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 0000000..9e23bc6
Binary files /dev/null and b/uniforce/css/images/ui-icons_cc0000_256x240.png differ
diff --git a/uniforce/css/images/ui-icons_ffffff_256x240.png b/uniforce/css/images/ui-icons_ffffff_256x240.png
new file mode 100644
index 0000000..ad42b0a
Binary files /dev/null and b/uniforce/css/images/ui-icons_ffffff_256x240.png differ
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/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 0e34c55..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();
@@ -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' ) );
@@ -289,9 +265,40 @@ 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();
}
+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
*/
@@ -312,7 +319,7 @@ function usp_install_cron(){
* @return bool
*/
function usp_uninstall(){
-
+
$usp = State::getInstance();
foreach ( $usp->data->modified_files as $file ){
@@ -323,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' );
@@ -332,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' );
@@ -342,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();
@@ -375,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' => '' );
@@ -422,7 +430,7 @@ function usp_detect_cms($path_to_index, $out = array( 'name' => 'Unknown', 'admi
$out = array( 'name' => 'phpBB', 'admin_page' => '/' );
}
-
+
return $out;
}
@@ -444,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');
@@ -463,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 ) ) );
}
@@ -484,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;
@@ -495,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 ){
@@ -507,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,
@@ -539,21 +547,22 @@ 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
$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' );
@@ -614,7 +623,7 @@ function usp_check_account_status( $key = null ){
$usp->data->save();
$usp->settings->save();
- return $usp->valid;
+ return $usp->data->valid;
}
/**
@@ -623,10 +632,55 @@ 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();
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/common.php b/uniforce/inc/common.php
index 42f03ff..7f6bfc7 100644
--- a/uniforce/inc/common.php
+++ b/uniforce/inc/common.php
@@ -5,15 +5,16 @@
*
* Sets all main constants
*
- * Version: 3.7.0
+ * Version: 3.8.0
*/
+use Cleantalk\USP\Common\Err;
use Cleantalk\USP\Common\State;
use Cleantalk\USP\Variables\Server;
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 );
@@ -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/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/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/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 ''
. __('A lot of files found to scan. It would take time.', 'security-malware-firewall')
. '
';
// 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/inc/settings.php b/uniforce/inc/settings.php
index e07ea29..6b727f7 100644
--- a/uniforce/inc/settings.php
+++ b/uniforce/inc/settings.php
@@ -13,16 +13,22 @@ 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 '![](img/preloader.gif)
';
- }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();
+ $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 '![](img/preloader.gif)
';
+ } 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 = '' )
@@ -56,24 +62,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 '
Warning: Malware scanner will use local database to store scan results. Please, check your OpenSSL module for PHP.
';
-
+
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/index.php b/uniforce/index.php
index 8561766..1bba9fb 100644
--- a/uniforce/index.php
+++ b/uniforce/index.php
@@ -1,6 +1,6 @@
x[1])
+ var window_height = window.innerHeight;
var row_template = '';
+ var row_template_weak_spots = '';
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';
+ let content_height = Object.keys(result.file).length * 19 < 76 ? 76 : Object.keys(result.file).length * 19 + 19,
+ visible_height = (window.screen.availHeight/100) * 75,
+ overflow = content_height < visible_height ? 'hidden' : 'scroll',
+ height = overflow === 'scroll' ? visible_height : content_height;
+
+ jQuery('#spbc_dialog').css({
+ height: height,
+ overflow: overflow,
+ })
- jQuery('#spbc_dialog').data('overflow', overflow);
jQuery('#spbc_dialog').dialog({
modal:true,
- title: result.file_path,
- position: { my: "center", at: "center" , of: window },
+ title: ('Loaded: ' + result.file_path),
+ position: { my: "center top", at: "center top+100px" , of: window },
width: +(jQuery('body').width() / 100 * 70),
- height: overflow === 'scroll' ? visible_height : content_height,
- // minHeight: 300,
show: { effect: "blind", duration: 500 },
+ maxHeight: visible_height,
draggable: true,
+ resizable: false,
closeText: "Close",
open: function(event, ui) {
- console.log(jQuery(event.target).data('overflow'));
- document.body.style.overflow = 'hidden';
- if(jQuery(event.target).data('overflow') == 'scroll') event.target.style.overflow = 'scroll';
+ event.target.style.overflow = overflow;
+ jQuery('#spbc_dialog').height(height);
+ jQuery('.ui-widget-overlay').on('click', function() {
+ jQuery("#spbc_dialog").dialog('close');
+ });
+ },
+ beforeClose: function(event, ui) {
+ document.body.style.overflow = 'auto';
+ jQuery('#spbc_dialog').empty();
},
- beforeClose: function(event, ui) { document.body.style.overflow = 'auto'; },
});
}
@@ -100,4 +116,4 @@ jQuery(document).ready(function(){
spbc_scanner.start();
});
-});
\ No newline at end of file
+});
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/Common/API.php b/uniforce/lib/Cleantalk/USP/Common/API.php
index 6e98fba..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 )
@@ -652,15 +653,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/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/Common/Helper.php b/uniforce/lib/Cleantalk/USP/Common/Helper.php
index 527c34a..ea6308b 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'])){
@@ -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
@@ -541,6 +578,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:
@@ -822,14 +862,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]);
@@ -1171,5 +1218,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/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
diff --git a/uniforce/lib/Cleantalk/USP/DB.php b/uniforce/lib/Cleantalk/USP/DB.php
index f22ef30..c5c0b4c 100644
--- a/uniforce/lib/Cleantalk/USP/DB.php
+++ b/uniforce/lib/Cleantalk/USP/DB.php
@@ -3,51 +3,53 @@
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
*/
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());
+ }
}
-
+
/**
* Safely replace place holders
*
@@ -59,7 +61,7 @@ public function init( ...$params ){
public function prepare( $query, $param = array() ) {
return parent::prepare( $query, $param );
}
-
+
/**
* Executes a query to DB
*
@@ -76,7 +78,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 +89,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 +101,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 +116,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 +132,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 +144,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
+}
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/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 'class ? $this->class : ''). '" '
+ .'value="" '
+ .( $this->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/lib/Cleantalk/USP/Scanner/Helper.php b/uniforce/lib/Cleantalk/USP/Scanner/Helper.php
index 03a235d..15aed7c 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://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 ) {
-
+
$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,38 @@ 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 );
+ $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
return array('error' => 'COULDNT_UNPACK');
@@ -86,7 +89,7 @@ static public function get_hashes__signature( $last_signature_update = 0 )
}else
return array('error' =>'NO_VERSION_FILE');
}
-
+
/**
* Getting real hashs of approved files
*
@@ -97,48 +100,46 @@ 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-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) {
-
+
$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
return array('error' =>'BAD_HASHES_FILE');
-
} else {
return array('error' => 'Empty hashes file');
}
-
} else {
return array( 'error' => 'COULDNT_UNPACK' );
}
@@ -146,10 +147,10 @@ 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');
}
-
+
/**
* Scanning file
*
@@ -160,19 +161,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])){
@@ -186,16 +187,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
*
@@ -206,7 +207,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']);
@@ -217,18 +218,18 @@ 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;
}
-
+
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
*
@@ -237,7 +238,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
*
@@ -250,7 +251,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 ) ) ||
@@ -259,7 +260,7 @@ public static function file__get_string_number_with_needle($file_path, $signatur
$out = $number;
}
}
-
+
return $out;
}
-}
\ 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 bae5da6..59e9848 100644
--- a/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php
+++ b/uniforce/lib/Cleantalk/USP/Scanner/Scanner.php
@@ -7,63 +7,65 @@
class Scanner
{
+ const FILE_MAX_SIZE = 2621440; // 2.5 MB
+
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);
@@ -76,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'
*
@@ -108,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){
@@ -120,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);
@@ -155,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)){
@@ -177,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){
@@ -191,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);
}
@@ -269,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
*/
@@ -286,10 +288,10 @@ 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'])
- ? md5_file($val['path'])
+ ? $this->files[$key]['size'] > self::FILE_MAX_SIZE ? 'file_is_too_big' : md5_file($val['path'])
: 'unknown';
}
@@ -298,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
*/
@@ -310,48 +312,51 @@ 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){
-
+ foreach ($signatures as $signature){
if( $signature['type'] === 'FILE' ){
- if( $file_info['full_hash'] === $signature['body'] ){
+ if( $file_info['full_hash'] === $signature ){
$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
+ continue;
+ }
$file_content = file_get_contents( $root_path . $file_info['path'] );
$is_regexp = preg_match( '/^\/.*\/$/', $signature['body'] );
if(
- ( $is_regexp && preg_match( $signature['body'], $file_content ) ) ||
+ ( $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'], $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'];
}
}
}
-
+
$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;
@@ -362,38 +367,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;
@@ -404,14 +409,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;
}
-
-
+
+
}
diff --git a/uniforce/lib/Cleantalk/USP/ScannerController.php b/uniforce/lib/Cleantalk/USP/ScannerController.php
index 979168b..a585540 100644
--- a/uniforce/lib/Cleantalk/USP/ScannerController.php
+++ b/uniforce/lib/Cleantalk/USP/ScannerController.php
@@ -12,44 +12,44 @@
use Cleantalk\USP\Variables\Get;
class ScannerController {
-
+
const table__scanner___files = 'scanner_files';
const table__scanner___links = 'scanner_links';
const table__scanner___backups = 'scanner_backups';
const table__scanner___backup_files = 'scanner_backup_files';
-
+
private static $instance;
-
+
/**
* DB handler
*
* @var DB
*/
public $db = null;
-
+
/**
* Site root directory
*
* @var string
*/
private $root = '';
-
+
/**
* Current action offset
*
* @var int
*/
private $offset = 0;
-
+
/**
* Current action
*
* @var string
*/
private $state = '';
-
+
function __construct( $root_dir, $db_params = null ){
-
+
if( $db_params ){
@$this->db = DB::getInstance(
$db_params[0],
@@ -57,13 +57,13 @@ function __construct( $root_dir, $db_params = null ){
$db_params[2]
);
}
-
+
$this->root = $root_dir;
$this->offset = intval( Get::get( 'offset' ) ) ?: $this->offset;
$this->offset = intval( Get::get( 'amount' ) ) ?: $this->offset;
$this->state = strval( Get::get( 'state' ) ) ?: $this->state;
}
-
+
private static $states = array(
'create_db',
'clear_table',
@@ -76,20 +76,20 @@ function __construct( $root_dir, $db_params = null ){
'frontend_analysis',
'outbound_links',
);
-
+
public function action__scanner__controller(){
-
+
$usp = State::getInstance();
sleep(5);
-
+
switch( $this->state ){
-
+
// Creating DB
case 'create_db':
$result = $this->action__scanner__create_db();
break;
-
+
// Cleaning table
case 'clear_table':
$result = $this->action__scanner__clear_table(
@@ -97,14 +97,14 @@ public function action__scanner__controller(){
10000
);
break;
-
+
//Signatures
case 'get_signatures':
-
+
$result = $this->action__scanner__get_signatures();
-
+
break;
-
+
// Searching for new files
case 'surface_analysis':
$result = $this->action__scanner__surface_analysis(
@@ -113,12 +113,12 @@ public function action__scanner__controller(){
$this->root
);
break;
-
+
// Searching for new files
case 'get_approved':
$result = $this->action__scanner__get_approved();
break;
-
+
// Signatures
case 'signature_analysis':
@@ -127,18 +127,18 @@ public function action__scanner__controller(){
10,
$this->root
);
-
+
break;
// Heuristic
case 'analysis_heuristic':
-
+
$result = $this->action__scanner__heuristic_analysis(
$this->offset,
10,
$this->root
);
-
+
break;
// Send result
@@ -152,7 +152,7 @@ public function action__scanner__controller(){
// Make next call if everything is ok
if( ! isset( $end ) && empty( $result['error'] ) ){
-
+
$remote_call_params = array(
'plugin_name' => 'security',
'spbc_remote_call_token' => md5( $usp->settings->key ),
@@ -160,13 +160,13 @@ public function action__scanner__controller(){
'state' => $result['end'] ? $this->next_state( $this->state ) : $this->state,
'offset' => $result['end'] ? 0 : $this->offset + $result['processed'],
);
-
+
Helper::http__request(
CT_USP_AJAX_URI,
$remote_call_params,
'get async'
);
-
+
}
// Delete or add an error
@@ -176,34 +176,34 @@ public function action__scanner__controller(){
return true;
}
-
+
/**
* Creates remote DB and get DB params
*
* @return array|bool[]
*/
public function action__scanner__create_db(){
-
+
$usp = State::getInstance();
-
+
$result = API::method__dbc2c_get_info( $usp->key );
-
+
if( empty( $result['error'] ) ){
$usp->data->db_request_string = 'mysql:host=' . $result['db_host'] . ';dbname=' . $result['db_name'] . ';charset=utf8';
$usp->data->db_user = $result['db_user'];
$usp->data->db_password = $result['db_password'];
$usp->data->db_created = $result['created'];
$usp->data->save();
-
+
$out = array('success' => true, 'end' => true);
-
+
}else
$out = $result;
-
+
return $out;
-
+
}
-
+
/**
* Clears all data about scanned files
*
@@ -213,33 +213,33 @@ public function action__scanner__create_db(){
* @return array
*/
public function action__scanner__clear_table( $offset = null, $amount = null ){
-
+
if( ! $this->db ) return array('error' => 'DB_NOT_PROVIDED');
if( $this->db instanceof Cleantalk\USP\DB ) return array('error' => 'DB_BAD_CONNECTION');
-
+
$offset = $offset ?: (int) Get::get('offset');
$amount = $amount ?: (int) Get::get('amount');
-
+
$result = $this->db->fetch_all(
'SELECT count(fast_hash) as cnt'
. ' FROM ' . self::table__scanner___files
);
$total = (int)$result[0]['cnt'];
-
+
$result = $this->db->fetch_all(
'SELECT path, fast_hash, status'
. ' FROM ' . self::table__scanner___files
. " LIMIT $offset, $amount;"
);
$checked = count($result);
-
+
$to_delete = array();
foreach($result as $value){
if( ! file_exists( $this->root . $value['path'] ) && $value['status'] != 'QUARANTINED' ){
$to_delete[] = "'{$value['fast_hash']}'";
}
} unset($value);
-
+
$deleted = 0;
if( ! empty( $to_delete ) ){
$deleted = $this->db->exec(
@@ -248,69 +248,74 @@ public function action__scanner__clear_table( $offset = null, $amount = null ){
. ' WHERE fast_hash IN (' . implode( ',', $to_delete ) . ');'
);
}
-
+
$out = array(
'checked' => (int) $checked,
'deleted' => (int) $deleted,
'processed' => (int) $checked - (int) $deleted,
'end' => $total <= $offset + $amount,
);
-
+
// Count if needed
if( $offset == 0 )
$out['total'] = $total;
-
+
if($deleted === false)
$out['error'] = 'COULDNT_DELETE';
return $out;
}
-
+
public function action__scanner__get_signatures(){
-
+
$usp = State::getInstance();
-
+
$out = array(
'success' => true,
);
-
+
if ( $usp->settings->scanner_signature_analysis ) {
-
+
$result = ScannerHelper::get_hashes__signature($usp->data->stat->scanner->signature_last_update);
-
+
if(empty($result['error'])){
-
+
$signatures = new \Cleantalk\USP\Common\Storage( 'signatures', $result, '', 'csv' );
$signatures->save();
-
+
$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()
: $out;
}
-
+
public function action__scanner__surface_analysis( $offset = null, $amount = null, $path = CT_USP_SITE_ROOT ){
-
+
if( ! $this->db ) return array('error' => 'DB_NOT_PROVIDED');
if( $this->db instanceof Cleantalk\USP\DB ) return array('error' => 'DB_BAD_CONNECTION');
-
+
$offset = $offset ?: (int) Get::get('offset');
$amount = $amount ?: (int) Get::get('amount');
$time_start = microtime(true);
-
+
$path_to_scan = $path ?: realpath($this->root);
$root_path = realpath($this->root);
$init_params = array(
@@ -324,23 +329,23 @@ public function action__scanner__surface_analysis( $offset = null, $amount = nul
'files_mandatory' => array(),
'dir_exceptions' => array()
);
-
+
$scanner = new Scanner($path_to_scan, $root_path, $init_params);
-
+
if( $scanner->files_count ){
-
+
$sql_query =
'INSERT INTO ' . self::table__scanner___files
. ' (`path`, `size`, `perms`, `mtime`,`status`,`fast_hash`, `full_hash`) VALUES ';
-
+
$sql_query__params = array();
foreach($scanner->files as $key => $file){
-
+
$file['path'] = addslashes($file['path']);
$sql_query__params[] = '("' . implode( '", "', $file ) .'")';
-
+
} unset($key, $file);
-
+
$sql_query .= implode( ',', $sql_query__params );
$sql_query .= " ON DUPLICATE KEY UPDATE
@@ -400,12 +405,12 @@ public function action__scanner__surface_analysis( $offset = null, $amount = nul
weak_spots,
NULL
);";
-
+
$success = $this->db->execute($sql_query);
-
+
}else
$output = array('error' => __FUNCTION__ . ' No files to scan',);
-
+
if(isset($success)){
$output = array(
'processed' => $scanner->files_count,
@@ -414,7 +419,7 @@ public function action__scanner__surface_analysis( $offset = null, $amount = nul
'exec_time' => round(microtime(true) - $time_start, 3),
);
}
-
+
if( $offset == 0 ){
$scanner = new Scanner(
realpath( $this->root ),
@@ -429,21 +434,21 @@ public function action__scanner__surface_analysis( $offset = null, $amount = nul
);
$output['total'] = (int) $scanner->files_count;
}
-
+
return $output;
}
-
+
/**
* Getting remote hashes of approved files
*
* @return array
*/
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'])) {
-
+
$prepared_sql = $this->db->prepare('UPDATE '. self::table__scanner___files
.' SET
checked_signature = 1,
@@ -452,7 +457,7 @@ public function action__scanner__get_approved() {
severity = NULL
WHERE path = :path AND full_hash = :full_hash;'
);
-
+
foreach ($result as $key => $value) {
$prepared_sql->execute(array(
':path' => $value[0],
@@ -460,19 +465,19 @@ public function action__scanner__get_approved() {
));
}
}
-
+
return array(
'end' => 1,
'processed' => empty($result['error']) ? count($result) : 0,
);
-
+
}
-
+
public function action__scanner__signature_analysis( $offset = null, $amount = null, $status = "'UNKNOWN','MODIFIED','OK','INFECTED'" ){
-
+
if( ! $this->db ) return array('error' => 'DB_NOT_PROVIDED');
if( $this->db instanceof Cleantalk\USP\DB ) return array('error' => 'DB_BAD_CONNECTION');
-
+
$status = Get::get( 'status' ) ? stripslashes( Get::get( 'status' ) ) : $status;
$offset = $offset ?: (int) Get::get('offset');
$amount = $amount ?: (int) Get::get('amount');
@@ -482,7 +487,7 @@ public function action__scanner__signature_analysis( $offset = null, $amount = n
'processed' => 0,
'scanned' => 0,
);
-
+
if( $offset == 0 ){
$result = $this->db->fetch_all(
'SELECT COUNT(fast_hash) as cnt'
@@ -491,7 +496,7 @@ public function action__scanner__signature_analysis( $offset = null, $amount = n
);
$out['total'] = (int) $result[0]['cnt'];
}
-
+
$files_to_check = $this->db->fetch_all(
'SELECT path, source_type, source_name, source_version, status, checked_signature, fast_hash, real_full_hash, full_hash, weak_spots, difference, severity'
.' FROM ' . self::table__scanner___files
@@ -502,7 +507,7 @@ public function action__scanner__signature_analysis( $offset = null, $amount = n
if ( $files_to_check ) {
if ( ! empty( $files_to_check ) ) {
-
+
$prepared_query = $this
->db
->prepare(
@@ -514,62 +519,68 @@ 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();
+
+ $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 ) {
-
- $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 );
-
+
+ $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']);
-
+
$out['processed']++;
}
}
}
-
+
$out['end'] = $out['processed'] < $amount;
-
+
return $out;
}
-
+
public function action__scanner__heuristic_analysis( $offset = null, $amount = null, $path = '', $status = "'MODIFIED','UNKNOWN'" ) {
-
+
if( ! $this->db ) return array('error' => 'DB_NOT_PROVIDED');
if( $this->db instanceof Cleantalk\USP\DB ) return array('error' => 'DB_BAD_CONNECTION');
-
+
$status = Get::get( 'status' ) ? stripslashes( Get::get( 'status' ) ) : $status;
$offset = $offset ?: (int) Get::get('offset');
$amount = $amount ?: (int) Get::get('amount');
@@ -580,7 +591,7 @@ public function action__scanner__heuristic_analysis( $offset = null, $amount = n
'processed' => 0,
'scanned' => 0,
);
-
+
if( $offset == 0 ){
$result = $this->db->fetch_all(
'SELECT COUNT(fast_hash) as cnt'
@@ -589,16 +600,16 @@ public function action__scanner__heuristic_analysis( $offset = null, $amount = n
);
$out['total'] = (int) $result[0]['cnt'];
}
-
+
$files_to_check = $this->db->fetch_all(
'SELECT path, source_type, source_name, source_version, status, checked_heuristic, fast_hash, real_full_hash, full_hash, weak_spots, difference, severity'
.' FROM ' . self::table__scanner___files
." WHERE checked_heuristic = 0 AND (source_status <> 'OUTDATED' OR source_status IS NULL)"
." LIMIT $amount"
);
-
+
if ( $files_to_check && count( $files_to_check )) {
-
+
$prepared_query = $this->db->prepare('UPDATE '. self::table__scanner___files
.' SET '
.' checked_heuristic = 1,'
@@ -607,19 +618,19 @@ public function action__scanner__heuristic_analysis( $offset = null, $amount = n
.' weak_spots = ?'
.' WHERE fast_hash = ?'
);
-
+
foreach ( $files_to_check as $file ) {
$result = Scanner::file__scan__heuristic( $this->root, $file );
-
+
if(empty($result['error'])){
-
+
$status = $file['status'] === 'MODIFIED' ? 'MODIFIED' : $result['status'];
$weak_spots = $result['weak_spots'] ? json_encode( $result['weak_spots'] ) : NULL;
$severity = $file['severity']
? $file['severity']
: ( $result['severity'] ? $result['severity'] : NULL );
-
+
$result_db = $prepared_query->execute(
array(
$status,
@@ -628,10 +639,10 @@ public function action__scanner__heuristic_analysis( $offset = null, $amount = n
$file['fast_hash'],
)
);
-
+
$result['status'] !== 'OK' ? $out['found']++ : $out['found'];
$result_db !== false ? $out['scanned']++ : $out['scanned'];
-
+
}else
return array( 'error' => 'Heuristic scan: ' . $result['error']);
@@ -639,26 +650,26 @@ public function action__scanner__heuristic_analysis( $offset = null, $amount = n
}
}
-
+
$out['end'] = $out['processed'] < $amount;
-
+
return $out;
}
public function action__scanner__send_results( ) {
-
+
if( ! $this->db ) return array('error' => 'DB_NOT_PROVIDED');
if( $this->db instanceof Cleantalk\USP\DB ) return array('error' => 'DB_BAD_CONNECTION');
-
+
$usp = State::getInstance();
-
+
$total_scanned = $this->count_files_by_status( "'UNKNOWN','OK','APPROVED','MODIFIED','INFECTED','QUARANTINED'" );
$bad_files = $this->get_files_by_status( "'UNKNOWN', 'MODIFIED'", array( 'path', 'full_hash', 'mtime', 'size', 'status') );
-
+
$unknown = array();
$modified = array();
-
+
if( count( $bad_files ) ){
foreach( $bad_files as $file ){
$file['path'] = Helper::is_windows() ? str_replace( '\\', '/', $file['path'] ) : $file['path'];
@@ -691,7 +702,7 @@ public function action__scanner__send_results( ) {
$modified,
$unknown
);
-
+
if( empty( $result['error'] ) ){
$usp->data->stat->scanner->last_sent = time();
@@ -707,7 +718,7 @@ public function action__scanner__send_results( ) {
return $result;
}
-
+
public function get_files_by_status( $status, $data = '*' ) {
$data = is_array( $data ) ? implode( ', ', $data ) : $data;
return $this->db
@@ -716,59 +727,59 @@ public function get_files_by_status( $status, $data = '*' ) {
.' FROM ' . self::table__scanner___files
." WHERE status IN ( $status )");
}
-
+
public function count_files_by_status( $status ) {
return $this->db->fetch_all(
'SELECT COUNT(fast_hash) as cnt'
.' FROM ' . self::table__scanner___files
." WHERE status IN ( $status )")[0]['cnt'];
}
-
+
public function next_state( $state ){
-
+
$state = self::$states[ array_search( $state, self::$states ) + 1 ];
$usp = State::getInstance();
$setting = 'scanner_' . $state;
-
+
// Recursion
if( isset( $usp->settings->$setting ) && $usp->settings->$setting === 0 ){
$state = $this->next_state( $state );
$this->offset = 0;
}
-
+
// Recursion. Base case
return $state;
}
-
+
public static function action__scanner__controller___no_sql(){
-
+
$usp = State::getInstance();
-
+
sleep(5);
-
+
$state = Get::get('state')
? Get::get('state')
: 'clear_table';
-
+
$prev_state = $state;
$additional_params = array();
-
+
switch($state){
-
+
// Cleaning table
case 'clear_table':
self::action__scanner__clear_table___no_sql();
$state = array_search( $state, self::$states );
break;
-
+
// Signatures
case 'signature_scan':
if(empty($usp->settings->scanner_signature_analysis)){
$state = array_search( $state, self::$states );
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 )
@@ -778,34 +789,34 @@ public static function action__scanner__controller___no_sql(){
$state = 'heuristic_scan';
}
break;
-
+
// Heuristic
case 'heuristic_scan':
if(empty($usp->settings->scanner_heuristic_analysis)){
$state = 'cure_backup';
break;
}
-
+
$result = self::action__scanner__scan_heuristic___no_sql(
(int) Get::get('offset'),
10
);
-
+
if(empty($result['error'])){
if($result['processed'] != 10)
$state = 'send_results';
}
break;
-
+
// Send result
case 'send_results':
-
+
$result = self::action__scanner__send_results___no_sql();
$end = true;
-
+
break;
}
-
+
// Make next call if everything is ok
if(!isset($end) && empty($result['error'])){
$def_params = array(
@@ -820,51 +831,51 @@ public static function action__scanner__controller___no_sql(){
'get async'
);
}
-
+
// Delete or add an error
empty($result['error'])
? $usp->error_delete($prev_state, 'and_save_data', 'cron_scan')
: $usp->error_add($prev_state, $result, 'cron_scan');
-
+
return true;
}
-
-
+
+
/**
* Clears all data about scanned files
*
* @return array
*/
public static function action__scanner__clear_table___no_sql(){
-
+
State::getInstance()->scan_result->count()
? State::getInstance()->scan_result->delete()
: null;
-
+
return array(
'processed' => 1,
'success' => 1,
'end' => true,
);
}
-
+
public function action__scanner__get_signatures___no_sql() {
return $this->action__scanner__get_signatures();
}
-
+
public static function action__scanner__signature_analysis___no_sql( $offset = 0, $amount = 10, $path = CT_USP_SITE_ROOT ){
-
+
$offset = Get::get( 'offset' ) ? Get::get( 'offset' ) : $offset;
$amount = Get::get( 'amount' ) ? Get::get( 'amount' ) : $amount;
$path = Get::get( 'path' ) ? realpath( Get::get( 'path' ) ) : realpath( $path );
-
+
$usp = State::getInstance();
-
+
$out = array(
'found' => 0,
'processed' => 0,
);
-
+
// Count files on the first call with offset
if( $offset == 0 ){
$path_to_scan = realpath( $path );
@@ -879,18 +890,18 @@ public static function action__scanner__signature_analysis___no_sql( $offset = 0
$scanner = new \Cleantalk\USP\Scanner\Scanner($path_to_scan, $root_path, $init_params);
$out['total'] = $scanner->files_count;
}
-
+
$files_to_check = self::get_files( $offset, $amount );
-
+
if ( $files_to_check ) {
-
+
$scanned = 0;
$found = 0;
-
+
if ( ! empty( $files_to_check ) ) {
-
+
// Initialing results
-
+
$signatures = new Storage('signatures', null, '', 'csv', array(
'id',
'name',
@@ -901,15 +912,22 @@ public static function action__scanner__signature_analysis___no_sql( $offset = 0
'cci'
) );
$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'] ) ) {
-
+
if ( $result['status'] !== 'OK' ) {
-
+
$usp->scan_result[] = array(
'path' => $file['path'],
'size' => $file['size'],
@@ -919,41 +937,41 @@ public static function action__scanner__signature_analysis___no_sql( $offset = 0
'fast_hash' => $file['fast_hash'],
'full_hash' => $file['full_hash'],
);
-
+
$out['found']++;
-
+
}
-
+
}else
return array( 'error' => 'Signature scan: ' . $result['error']);
-
+
$out['processed']++;
}
-
+
$usp->scan_result->save();
-
+
}
-
+
}
-
+
$out['end'] = $out['processed'] < $amount;
-
+
return $out;
}
-
+
public static function action__scanner__heuristic_analysis___no_sql( $offset = 0, $amount = 10, $path = CT_USP_SITE_ROOT ) {
-
+
$offset = Get::get( 'offset' ) ? Get::get( 'offset' ) : $offset;
$amount = Get::get( 'amount' ) ? Get::get( 'amount' ) : $amount;
$path = Get::get( 'path' ) ? realpath( Get::get( 'path' ) ) : realpath( $path );
-
+
$usp = State::getInstance();
-
+
$out = array(
'found' => 0,
'processed' => 0,
);
-
+
// Count files on the first call with offset
if( $offset == 0 ){
$path_to_scan = realpath( $path );
@@ -968,20 +986,20 @@ public static function action__scanner__heuristic_analysis___no_sql( $offset = 0
$scanner = new \Cleantalk\USP\Scanner\Scanner($path_to_scan, $root_path, $init_params);
$out['total'] = $scanner->files_count;
}
-
-
+
+
$files_to_check = self::get_files( $offset, $amount );
-
+
if ( $files_to_check ) {
if ( count( $files_to_check ) ) {
foreach ( $files_to_check as $file ) {
-
+
$result = Scanner::file__scan__heuristic( CT_USP_SITE_ROOT, $file );
-
+
if ( empty( $result['error'] ) ) {
-
+
if ( $result['status'] !== 'OK' ) {
-
+
$usp->scan_result[] = array(
'path' => $file['path'],
'size' => $file['size'],
@@ -991,38 +1009,38 @@ public static function action__scanner__heuristic_analysis___no_sql( $offset = 0
'fast_hash' => $file['fast_hash'],
'full_hash' => $file['full_hash'],
);
-
+
$out['found'] ++;
-
+
}
-
+
}else
return array( 'error' => 'Heuristic scan: ' . $result['error']);
-
+
$out['processed']++;
-
+
}
-
+
$usp->scan_result->save();
}
}
-
+
$out['end'] = $out['processed'] < $amount;
-
+
return $out;
-
+
}
-
+
public static function action__scanner__send_results___no_sql( $total_scanned = 0 ) {
-
+
$usp = State::getInstance();
-
+
$total_scanned = $total_scanned ? $total_scanned : Get::get( 'total_scanned' );
-
+
$files = $usp->scan_result->convertToArray();
-
+
$files_count = count( $files );
-
+
$unknown = array();
$modified = array();
if($files_count){
@@ -1038,7 +1056,7 @@ public static function action__scanner__send_results___no_sql( $total_scanned =
);
}
}
-
+
// API. Sending files scan result
$result = API::method__security_mscan_logs(
$usp->key,
@@ -1049,26 +1067,26 @@ public static function action__scanner__send_results___no_sql( $total_scanned =
$modified,
$unknown
);
-
+
if(empty($result['error'])){
-
+
$usp->data->stat->scanner->last_sent = time();
$usp->data->stat->scanner->last_scan = time();
$usp->data->stat->scanner->last_scan_amount = isset($_GET['total_scanned']) ? $_GET['total_scanned'] : $total_scanned;
-
+
}else
Err::add('scanner_result_send', $result['error']);
-
+
$usp->data->save();
-
+
$result['end'] = 1;
return $result;
-
+
}
-
-
+
+
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(
@@ -1082,11 +1100,11 @@ 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;
}
-}
\ No newline at end of file
+}
diff --git a/uniforce/lib/Cleantalk/USP/Security/Firewall.php b/uniforce/lib/Cleantalk/USP/Security/Firewall.php
index 9a60159..87071c2 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',
@@ -166,43 +168,63 @@ 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';
+ $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
+ $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;
+ //proceed result array
$result = array(
-
+
// 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,
'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;
-
+
}
/**
diff --git a/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php b/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php
index 4542c63..8d17969 100644
--- a/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php
+++ b/uniforce/lib/Cleantalk/USP/Uniforce/Cron.php
@@ -16,10 +16,17 @@ public static function getTasks(){
if( ! file_exists( self::CRON_FILE ) ){
file_put_contents(
self::CRON_FILE,
- "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,82 +224,116 @@ 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 );
continue;
}
- $auth_ip = $log[1] ? (string) $log[1] : '0.0.0.0';
+ $_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],
+ );
- if( (string) $log[8] > 0 ){
- for( $i = 0; (string) $log[8] > $i; $i ++ ){
+ //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['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 ++ ){
$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['http_user_agent'],
)
);
}
}
-
- $result = API::method__security_logs( $ct_key, $data );
-
+
+ $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' );
}
@@ -312,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;
@@ -438,4 +472,4 @@ public static function is_login_page() {
return false;
}
-}
\ 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 77ddcaf..60471d1 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;
@@ -121,14 +121,15 @@ 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;
}
-
// Not in base
}else {
-
+
$results[] = array(
'module' => $this->module_name,
'ip' => $current_ip,
@@ -138,15 +139,15 @@ public function check() {
'mask' => null,
'status' => 'PASS',
);
-
+
}
-
+
}
-
+
return $results;
-
+
}
-
+
/**
* Sends and wipe SFW log
*
@@ -155,38 +156,74 @@ 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' => 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
+ 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;
@@ -203,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;
@@ -219,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' );
@@ -244,47 +281,53 @@ 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 ){
-
+
+ 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;
}
-
+
$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'] ) ){
-
- State::getInstance()->fw_stats->updating = true;
+
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();
-
+
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'] ),
@@ -292,26 +335,30 @@ public static function update( $api_key ){
),
array( 'get', 'async' )
);
-
- }else
- return $result;
-
+
+ }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 += $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();
+ // Make sure to write all fs actions
+ sleep(3);
+
// Make next call
Helper::http__request(
Server::get( 'HTTP_HOST' ) . CT_USP_AJAX_URI,
@@ -328,46 +375,54 @@ public static function update( $api_key ){
array( 'get', 'async' )
);
- }else
- return $result;
-
+ } 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 += $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();
- }else
- return $result;
+ 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;
+ }
}
}
-
+
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 ){
@@ -382,11 +437,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
@@ -457,7 +512,7 @@ static public function update__get_multifiles( $spbc_key ){
}else
return $result;
}
-
+
/**
* Writes entries from remote files to Firewall database.
*
@@ -466,32 +521,37 @@ 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;
}
-
+
+ //skip ipv6 because of reasons :(
+ 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 ) ) ),
@@ -500,29 +560,30 @@ 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->insert( $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' );
+
+ } else {
+ Err::add( 'Updating FW', 'No data to save' );
+ }
}
-
return $inserted;
-
- }else
- Err::prepend( 'Updating FW' );
+
+ } else {
+ Err::prepend( 'Updating FW' );
+ }
}
-
+
/**
* Adding local exclusions to to the FireWall database.
*
@@ -531,16 +592,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(
@@ -552,37 +613,49 @@ static public function update__write_to_db__exclusions( $exclusions = array() ){
}
}
}
-
+
$db = new FileDB( 'fw_nets' );
-
+
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');
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, );
+
+ }
+
+ /**
+ * 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/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php b/uniforce/lib/Cleantalk/USP/Uniforce/Firewall/WAF.php
index 6551140..a942318 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,72 +36,78 @@ 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'] ) {
-
+
+ $decoded_signatures = array();
+ foreach ($signatures as $signature => $value){
+ $decoded_signatures[$signature] = $value;
+ $decoded_signatures[$signature]['body'] = base64_decode($value['body']);
+ }
+
+ foreach ( $decoded_signatures as $signature_body_decoded ) {
+
+ switch ( $signature_body_decoded['attack_type'] ) {
+
case 'SQL_INJECTION':
- $this->waf_sql_patterns[] = $signature['body'];
+ $this->waf_sql_patterns[] = $signature_body_decoded['body'];
break;
case 'XSS':
- $this->waf_xss_patterns[] = $signature['body'];
+ $this->waf_xss_patterns[] = $signature_body_decoded['body'];
break;
case 'EXPLOIT':
- $this->waf_exploit_patterns[] = $signature['body'];
+ $this->waf_exploit_patterns[] = $signature_body_decoded['body'];
break;
}
}
}
-
+
// 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,14 +118,18 @@ public function check() {
)
);
}
-
+
return $results;
-
+
}
-
- private function signatures__get(){
-
- $signatures = new Storage('signatures', null, '', 'csv', array(
+
+ /**
+ * Get array of WAF signatures. Return array of signatures or false if no WAF rules found.
+ * @return array|false
+ */
+ private function signatures__get(){
+
+ $signatures_source = new Storage('signatures', null, '', 'csv', array(
'id',
'name',
'body',
@@ -128,14 +138,17 @@ 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;
}
-
+
/**
* Checks array for XSS-attack patterns
*
@@ -144,9 +157,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 );
@@ -154,7 +167,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 );
@@ -168,11 +181,11 @@ private function waf_xss_check( $arr ) {
}
}
}
-
+
return false;
-
+
}
-
+
/**
* Checks array for SQL injections
*
@@ -181,16 +194,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 );
@@ -203,18 +216,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 );
@@ -226,18 +239,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 ) ) {
@@ -264,8 +277,8 @@ private function waf_file_check() {
}
}
}
-
+
return false;
-
+
}
-}
\ No newline at end of file
+}
diff --git a/uniforce/uniforce.php b/uniforce/uniforce.php
index b4dcbd5..d434565 100644
--- a/uniforce/uniforce.php
+++ b/uniforce/uniforce.php
@@ -16,12 +16,16 @@
// 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
$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,
@@ -61,7 +65,7 @@
return;
}
- if( $firewall->module__is_loaded__any() ){
+ if( $firewall->module__is_loaded__any() && ! usp__is_admin() ){
$firewall->run();
}
@@ -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;
}
diff --git a/uniforce/version.php b/uniforce/version.php
index c6197cf..9e152e0 100644
--- a/uniforce/version.php
+++ b/uniforce/version.php
@@ -1,4 +1,4 @@
@@ -29,23 +29,23 @@
-
+
Universal Anti-Spam Plugin by CleanTalk
-
+
-
+
-
+
-
+
![Cleantalk logo](img/ct_logo.png)
@@ -70,14 +70,14 @@
-
+
-
+
+
-
@@ -85,9 +85,9 @@
var security = '';
var ajax_url = location.href;
-
+