Plugin Directory

Changeset 2899041


Ignore:
Timestamp:
04/14/2023 07:59:12 AM (3 years ago)
Author:
wtsec
Message:

2.4.20

  • Fixed an issue with blocking custom administrator roles
  • WP scan improvements
Location:
wt-security
Files:
437 added
1 deleted
13 edited

Legend:

Unmodified
Added
Removed
  • wt-security/trunk/includes/css/main.css

    r2877854 r2899041  
    423423  color: #5e6977;
    424424  font-weight: 500;
    425   padding: 0 16px;
     425  padding: 0 13px;
    426426}
    427427.wtotem_nav__link_active {
     
    13121312  color: #3d50df;
    13131313  padding-left: 32px;
     1314  pointer-events: none;
     1315  cursor: default;
    13141316}
    13151317.wtotem_body .wtotem_min_loader_spinner::before{
     
    57115713  align-items: center;
    57125714  gap: 13px;
    5713 }
    5714 
     5715  word-break: break-all;
     5716}
     5717.wtotem_body .link_source{
     5718  font-size: 14px;
     5719  width: 310px;
     5720  padding: 8px 5px;
     5721  display: flex;
     5722  align-items: center;
     5723  gap: 13px;
     5724  word-break: break-all;
     5725}
    57155726.wtotem_body .span__item{
    57165727  padding: 8px 16px;
  • wt-security/trunk/includes/templates/scan_logs.html.twig

    r2865057 r2899041  
    228228        });
    229229
     230
     231      let wtotem_scan_init = () => {
     232        /* run the reload every 10 seconds */
     233        var wtotem_check_scan_interval = setInterval(() => wtotem_check_scan(), 10000);
     234
     235        /* stop reload after 60 min */
     236        setTimeout(() => { clearInterval(wtotem_check_scan_interval); }, 3600000);
     237      }
     238      var scan_finished = false;
     239      let wtotem_check_scan = () => {
     240        if(!scan_finished){
     241          jQuery.post(ajaxurl, {
     242            action: 'wtotem_ajax',
     243            ajax_action: 'logs',
     244            logs_action: 'check_scan',
     245            wtotem_page_nonce: '{{ page_nonce }}',
     246          }, function (data) {
     247            if (data.scan_finished) {
     248              jQuery('.confidential_files__content').html(data.content.confidential_files);
     249              jQuery('.confidential_files__pagination').html(data.pagination.confidential_files);
     250              jQuery('.confidential_files__num').html(data.count.confidential_files);
     251
     252              jQuery('.links-logs__content').html(data.content.links);
     253              jQuery('.links-logs__pagination').html(data.pagination.links);
     254              jQuery('.links__num').html(data.count.links);
     255
     256              jQuery('.scripts-logs__content').html(data.content.scripts);
     257              jQuery('.scripts-logs__pagination').html(data.pagination.scripts);
     258              jQuery('.scripts__num').html(data.count.scripts);
     259
     260              jQuery('.iframes-logs__content').html(data.content.iframes);
     261              jQuery('.iframes-logs__pagination').html(data.pagination.iframes);
     262              jQuery('.iframes__num').html(data.count.iframes);
     263
     264              jQuery('#next_scan').html(data.next_scan);
     265
     266              jQuery('#rescan').html("{{ 'Start scanning' |trans }}").removeClass('wtotem_min_loader_spinner');
     267
     268              scan_finished = true;
     269            }
     270            jQuery('#wtotem_notifications').html(data.notifications);
     271
     272          });
     273        }
     274      }
     275
     276      // init check_scan
     277      if( {{ scan_init }} ){
     278        wtotem_scan_init();
     279      }
     280
     281
    230282        jQuery('#rescan').on('click', function (e) {
    231283
    232284            let btn = jQuery(this);
    233             btn.html("{{ 'Scan is running' |trans}}").addClass('wtotem_min_loader_spinner').css({'pointerEvents' : 'none'});
     285            btn.html("{{ 'Scan is running' |trans}}").addClass('wtotem_min_loader_spinner');
    234286
    235287            jQuery.post(ajaxurl, {
     
    239291                wtotem_page_nonce: '{{ page_nonce }}',
    240292            }, function (data) {
    241 
    242                 jQuery('.confidential_files__content').html(data.content.confidential_files);
    243                 jQuery('.confidential_files__pagination').html(data.pagination.confidential_files);
    244                 jQuery('.confidential_files__num').html(data.count.confidential_files);
    245 
    246                 jQuery('.links-logs__content').html(data.content.links);
    247                 jQuery('.links-logs__pagination').html(data.pagination.links);
    248                 jQuery('.links__num').html(data.count.links);
    249 
    250                 jQuery('.scripts-logs__content').html(data.content.scripts);
    251                 jQuery('.scripts-logs__pagination').html(data.pagination.scripts);
    252                 jQuery('.scripts__num').html(data.count.scripts);
    253 
    254                 jQuery('.iframes-logs__content').html(data.content.iframes);
    255                 jQuery('.iframes-logs__pagination').html(data.pagination.iframes);
    256                 jQuery('.iframes__num').html(data.count.iframes);
    257 
    258                 jQuery('#wtotem_notifications').html(data.notifications);
    259 
    260                 jQuery('#next_scan').html(data.next_scan);
    261 
    262                 btn.html("{{ 'Start scanning' |trans}}").removeClass('wtotem_min_loader_spinner').css({'pointerEvents' : 'auto'});
    263 
     293              scan_finished = false;
     294              wtotem_scan_init();
     295              jQuery('#wtotem_notifications').html(data.notifications);
    264296            });
    265297        })
     
    347379    </h2>
    348380
    349     <div class="wtotem_control__btn wtotem_btn_wc" id="rescan">{{ 'Start scanning' | trans }}</div>
     381    {% if scan_init %}
     382        <div class="wtotem_control__btn wtotem_btn_wc wtotem_min_loader_spinner" id="rescan">{{ 'Scan is running' | trans }}</div>
     383    {% else %}
     384        <div class="wtotem_control__btn wtotem_btn_wc" id="rescan">{{ 'Start scanning' | trans }}</div>
     385    {% endif %}
    350386</div>
    351387
  • wt-security/trunk/includes/templates/scan_logs_items.html.twig

    r2865057 r2899041  
    1111                    </svg>
    1212                </a>
     13                <p class="link_source"> {{log.source}}</p>
    1314
    1415                <p class="links__type">{% if log.is_internal == true %}{{ 'Internal' | trans }} {% elseif (log.is_internal) == (false) %} {{ 'External' | trans }} {% endif %}</p>
  • wt-security/trunk/lib/AgentManager.php

    r2877854 r2899041  
    137137            $list = $wp_filesystem->dirlist( ABSPATH );
    138138
     139            $uploads_list = $wp_filesystem->dirlist( ABSPATH .'wp-content/uploads' );
     140            foreach ($uploads_list as $key => $item){
     141                $uploads_list[$key]['name'] = 'wp-content/uploads/' . $item['name'];
     142            }
     143
     144            $list = array_merge($list, $uploads_list);
     145
    139146            foreach ( $list as $item ) {
    140147
     
    144151                $recursive = ( $item['type'] == 'd' ) ? true : false;
    145152
    146                 $pattern = '/([a-zA-Z0-9_]{64}.av.php)|([a-zA-Z0-9_]{64}.am.php)|([a-zA-Z0-9_]{64}.waf.php)|(wtotem_[a-zA-Z0-9_]{12,16})/';
     153                $pattern = '/([a-zA-Z0-9_]{64}.av.php)|([a-zA-Z0-9_]{64}.am.php)|([a-zA-Z0-9_]{64}.waf.php)|(\.wtotem_[a-zA-Z0-9_]{12,16})/';
    147154
    148155                if ( preg_match( $pattern, $target_item ) ) {
  • wt-security/trunk/lib/Ajax.php

    r2877854 r2899041  
    124124        }
    125125
    126         if (WebTotemAgentManager::removeAgents()) {
    127             WebTotemAgentManager::amInstall();
    128         }
     126        WebTotemAgentManager::amInstall();
     127
    129128        $response['success'] = true;
    130129        $response['redirect_link'] = WebTotem::adminURL('admin.php?page=wtotem');
     
    641640
    642641            case 'rescan':
     642                WebTotemOption::setOptions(['scan_init' => 1]);
    643643                WebTotemScan::initialize();
    644 
    645                 $content = [];
    646                 $pagination = [];
    647                 $count = [];
    648                 $types = ['links', 'scripts', 'iframes'];
    649 
    650                 foreach ($types as $type) {
    651                     $scan_logs = WebTotemDB::getRows(
    652                         ['AND', ['data_type' => $type]],
    653                         'scan_logs',
    654                         'content'
    655                     );
    656 
    657                     $build[$type][] = [
    658                         'variables' => [
    659                             "logs" => $scan_logs['data'],
    660                             "data_type" => $type
    661                         ],
    662                         'template' => 'scan_logs_items',
     644                $response = [
     645                        'success' => true,
     646                        'notifications' => self::notifications(),
     647                ];
     648
     649            break;
     650
     651            case 'check_scan':
     652
     653                if(WebTotemOption::getOption('scan_init')){
     654                    WebTotemScan::initialize();
     655                    $response = [
     656                        'success' => true,
     657                        'scan_finished' => false,
     658                        'notifications' => self::notifications(),
    663659                    ];
    664                     $content[$type] = $template->arrayRender($build[$type]);
    665                     $pagination[$type] = WebTotem::paginationBuild(10, $scan_logs['count']);
    666                     $count[$type] = $scan_logs['count'];
    667                 }
    668 
    669                 $confidential_files = WebTotemDB::getRows([], 'confidential_files');
    670                 $content['confidential_files'] = $template->arrayRender([
    671                     'variables' => [
    672                         "confidential_files" => WebTotem::getConfidentialFiles($confidential_files['data']),
    673                     ],
    674                     'template' => 'scan_confidential_files',
    675                 ]);
    676                 $pagination['confidential_files'] = WebTotem::paginationBuild(10, $confidential_files['count']);
    677                 $count['confidential_files'] = $confidential_files['count'];
    678 
    679                 // Resetting the task in the cron.
    680                 wp_clear_scheduled_hook('webtotem_daily_cron');
    681                 wp_schedule_event(time() + 86395, 'daily', 'webtotem_daily_cron');
    682 
    683                 $until_next_scan = wp_next_scheduled('webtotem_daily_cron') - time();
    684 
    685                 $hr = floor($until_next_scan / 3600);
    686                 $min = floor(($until_next_scan % 3600) / 60);
    687 
    688                 $response = [
    689                     'success' => true,
    690                     'content' => $content,
    691                     "pagination" => $pagination,
    692                     "next_scan" => sprintf(__('%dh %dm', 'wtotem'), $hr, $min),
    693                     "count" => $count,
    694                     'notifications' => self::notifications(),
    695                 ];
     660                } else {
     661                    $content = [];
     662                    $pagination = [];
     663                    $count = [];
     664                    $types = ['links', 'scripts', 'iframes'];
     665
     666                    foreach ($types as $type) {
     667                        $scan_logs = WebTotemDB::getRows(
     668                                ['AND', ['data_type' => $type]],
     669                                'scan_logs',
     670                                'content'
     671                        );
     672
     673                        $build[$type][] = [
     674                                'variables' => [
     675                                        "logs" => $scan_logs['data'],
     676                                        "data_type" => $type
     677                                ],
     678                                'template' => 'scan_logs_items',
     679                        ];
     680                        $content[$type] = $template->arrayRender($build[$type]);
     681                        $pagination[$type] = WebTotem::paginationBuild(10, $scan_logs['count']);
     682                        $count[$type] = $scan_logs['count'];
     683                    }
     684
     685                    $confidential_files = WebTotemDB::getRows([], 'confidential_files');
     686                    $content['confidential_files'] = $template->arrayRender([
     687                            'variables' => [
     688                                    "confidential_files" => WebTotem::getConfidentialFiles($confidential_files['data']),
     689                            ],
     690                            'template' => 'scan_confidential_files',
     691                    ]);
     692                    $pagination['confidential_files'] = WebTotem::paginationBuild(10, $confidential_files['count']);
     693                    $count['confidential_files'] = $confidential_files['count'];
     694
     695                    // Resetting the task in the cron.
     696                    wp_clear_scheduled_hook('webtotem_daily_cron');
     697                    wp_schedule_event(time() + 86395, 'daily', 'webtotem_daily_cron');
     698
     699                    $until_next_scan = wp_next_scheduled('webtotem_daily_cron') - time();
     700
     701                    $hr = floor($until_next_scan / 3600);
     702                    $min = floor(($until_next_scan % 3600) / 60);
     703
     704                    $response = [
     705                            'success' => true,
     706                            'scan_finished' => true,
     707                            'content' => $content,
     708                            "pagination" => $pagination,
     709                            "next_scan" => sprintf(__('%dh %dm', 'wtotem'), $hr, $min),
     710                            "count" => $count,
     711                            'notifications' => self::notifications(),
     712                    ];
     713                }
    696714
    697715                break;
     
    17551773
    17561774    }
     1775
     1776    /**
     1777     * Initialization scanning and checking the current status.
     1778     *
     1779     * @return void
     1780     */
     1781    public static function wtotem_scan()
     1782    {
     1783
     1784        if (WebTotemRequest::get('ajax_action') !== 'wtotem_scan') {
     1785            return;
     1786        }
     1787
     1788        $logs_action = WebTotemRequest::get('scan_action');
     1789
     1790        switch ($logs_action) {
     1791            case 'init':
     1792                if(!WebTotemOption::getOption('scan_init')){
     1793                    WebTotemOption::setOptions(['scan_init' => 1]);
     1794                    WebTotemScan::initialize();
     1795                    $response = [
     1796                            'success' => true,
     1797                            'scan_start' => 'success',
     1798                    ];
     1799                } else {
     1800                    $response = [
     1801                            'success' => false,
     1802                            'error' => 'The scan is already running',
     1803                    ];
     1804                }
     1805
     1806                break;
     1807
     1808            case 'push':
     1809                if(WebTotemOption::getOption('scan_init')) {
     1810                    WebTotemScan::initialize();
     1811                    $response = [
     1812                            'success' => true,
     1813                            'scan_finished' => false,
     1814                            'push' => 'success',
     1815                    ];
     1816                } else {
     1817                    $response = [
     1818                            'success' => false,
     1819                            'scan_finished' => true,
     1820                            'push' => 'fail',
     1821                            'error' => 'Scan completed',
     1822                    ];
     1823                }
     1824
     1825                break;
     1826        }
     1827
     1828
     1829        wp_send_json($response ?? ['success' => false, 'error' => 'No action found']);
     1830    }
     1831
    17571832
    17581833    /**
  • wt-security/trunk/lib/DB.php

    r2865057 r2899041  
    123123    public static function setData ($options, $table, $where = false) {
    124124        global $wpdb;
    125     $table_name = self::getTable($table);
    126 
    127     if($wpdb->get_var("show tables like '$table_name'") == $table_name) {
    128       if($where && $current = self::getData($where, $table)){
    129         $options['id'] = $current['id'];
    130       }
    131 
    132       $wpdb->replace( $table_name, $options );
    133     }
     125        $table_name = self::getTable($table);
     126
     127        if($wpdb->get_var("show tables like '$table_name'") == $table_name) {
     128            if($where && $current = self::getData($where, $table)){
     129                $options['id'] = $current['id'];
     130            }
     131
     132            $wpdb->replace( $table_name, $options );
     133        }
    134134    }
    135135
     
    143143    if($params){
    144144      $wpdb->delete( $table_name, $params );
     145    } else {
     146        $wpdb->query( "DELETE FROM " . $table_name );
     147        $wpdb->query( "UPDATE " . $table_name . " SET id = 0" );
     148        $wpdb->query( "ALTER TABLE " . $table_name . " AUTO_INCREMENT =0;"  );
    145149    }
    146150    }
     
    193197        return $result;
    194198    }
     199
     200    /**
     201     * Getting rows from the table.
     202     *
     203     * @param string $table
     204     *    Table name.
     205     * @param string $columns
     206     *    Columns.
     207     * @param string $values
     208     *    Values.
     209     */
     210    public static function setRows ($table, $columns, $values) {
     211        global $wpdb;
     212        $table_name = self::getTable($table);
     213
     214        if($wpdb->get_var("show tables like '$table_name'") != $table_name) {
     215            WebTotemDB::install();
     216        }
     217
     218        $wpdb->query( "INSERT INTO " . $table_name . " " . $columns . " VALUES " . $values );
     219    }
    195220
    196221    /**
  • wt-security/trunk/lib/Helper.php

    r2877854 r2899041  
    4545            if(in_array('administrator', $roles)) {
    4646                $user_role = 1;
    47             } elseif(in_array('editor', $roles)) {
     47            } elseif(in_array('editor', $roles) or current_user_can('publish_posts')) {
    4848                $user_role = 2;
    4949            } else {
     
    108108        return $data;
    109109    }
     110
     111    /**
     112     * Removing duplicates by one key.
     113     *
     114     * @param array $array
     115     *    Array.
     116     * @param string $key
     117     *    Delete duplicates with the same key.
     118     *
     119     * @return array
     120     *    Returns array.
     121     */
     122    public static function arrayUniqueKey($array, $key) {
     123        $tmp = $key_array = array();
     124        $i = 0;
     125
     126        foreach ($array as $val) {
     127            if (!in_array($val[$key], $key_array)) {
     128                $key_array[$i] = $val[$key];
     129                $tmp[$i] = $val;
     130            }
     131            $i++;
     132        }
     133        return $tmp;
     134    }
    110135
    111136    /**
  • wt-security/trunk/lib/Option.php

    r2877854 r2899041  
    290290                    'all_hosts' => $all_hosts,
    291291                ]);
     292            } else {
     293                self::setOptions([
     294                    'host_id' => $host_id,
     295                    'host_name' => $host_name,
     296                ]);
    292297            }
    293298
    294         }
    295         else {
     299        } else {
    296300            self::setOptions([
    297301                'host_id' => $host_id,
     
    348352    public static function getMainHost() {
    349353
    350         if(WebTotem::isMultiSite()){
    351             return [
    352                 'id' => get_blog_option(0, 'wtotem_host_id'),
    353                 'name' => get_blog_option(0, 'wtotem_host_name'),
    354             ];
    355         } else{
    356             return [
    357                 'id' => self::getOption('host_id'),
    358                 'name' => self::getOption('host_name'),
    359             ];
    360         }
     354        return [
     355            'id' => self::getOption('host_id'),
     356            'name' => self::getOption('host_name'),
     357        ];
    361358
    362359    }
  • wt-security/trunk/lib/modules/logs/Scan.php

    r2877854 r2899041  
    1111
    1212/**
    13  * WebTotem scan class for Wordpress.
     13 * WebTotem scan class for WordPress.
    1414 */
    15 class WebTotemScan
    16 {
    17 
    18 
     15class WebTotemScan {
     16    /**
     17     *
     18     */
    1919    public static function initialize() {
    20         if (function_exists('set_time_limit')) @set_time_limit(1800);
    21         @ini_set('max_execution_time', '1800');
    22 
    23         self::scanFiles();
    24         self::scanDB();
    25         self::checkConfidentialFiles();
    26 
    27     }
    28 
     20        if(WebTotemOption::getOption('scan_init')){
     21            $time_start = microtime(true);
     22
     23            $max_execution_time = ini_get('max_execution_time');
     24            if($max_execution_time < 1800){
     25                if (function_exists('set_time_limit')) @set_time_limit(1800);
     26                @ini_set('max_execution_time', '1800');
     27            }
     28            $max_execution_time = ini_get('max_execution_time');
     29
     30            $scan_temp = json_decode(WebTotemOption::getOption('scan_temp'), true) ?: [];
     31
     32            if(empty($scan_temp)){
     33                $scan_temp = [
     34                    'current_scan' => 'scanDB',
     35                    'need_to_scan' => [],
     36                    'links' => [],
     37                ];
     38            }
     39
     40            $scan_running = json_decode(WebTotemOption::getOption('scan_running'), true) ?: [];
     41
     42            if($scan_running['status'] == 'stop' || ($time_start - $scan_running['time_start']) > $max_execution_time ){
     43
     44                WebTotemOption::setOptions(['scan_running' => ['status' => 'run', 'time_start' => $time_start]]);
     45
     46                if($scan_temp['current_scan'] == 'scanDB'){
     47                    self::scanDB($scan_temp, $max_execution_time, $time_start);
     48                    WebTotemOption::setOptions(['scan_running' => ['status' => 'stop']]);
     49                    return;
     50                }
     51
     52                if($scan_temp['current_scan'] == 'scanFiles') {
     53                    self::scanFiles($scan_temp, $max_execution_time, $time_start);
     54                    WebTotemOption::setOptions(['scan_running' => ['status' => 'stop']]);
     55                    return;
     56                }
     57
     58                if($scan_temp['current_scan'] == 'checkConfidentialFiles') {
     59                    self::checkConfidentialFiles($scan_temp, $max_execution_time, $time_start);
     60                    WebTotemOption::setOptions(['scan_running' => ['status' => 'stop']]);
     61                    return;
     62                }
     63
     64                if($scan_temp['current_scan'] == 'crawler') {
     65                    WebTotemCrawler::init($scan_temp);
     66                    WebTotemOption::setOptions(['scan_running' => ['status' => 'stop']]);
     67                }
     68
     69            }
     70
     71        }
     72
     73    }
    2974
    3075    /**
     
    3277     * formation of an array of data on them
    3378     */
    34     public static function scanDB($id = null, $table = null)
    35     {
    36         $tables = self::getTables();
     79    public static function scanDB($scan_temp, $max_execution_time, $time_start ) {
     80        $tables = $scan_temp['need_to_scan'] ?? self::getTables();
     81        $links = $scan_temp['links'] ?? [];
    3782
    3883        $needles = ['%href%', '%<iframe%', '%.js%'];
    39         $data = [];
    40 
    41         foreach ($tables['posts'] as $table) {
    42             $rows = self::getRows($table, ['post_content' => $needles]);
    43 
    44             $table_name = self::add_prefix($table);
     84
     85        foreach ($tables['posts'] as $key => $table) {
     86            $rows = self::getRows($table, ['post_content' => $needles], 'guid');
     87
    4588            foreach ($rows as $row) {
    46                 $data[] = [
    47                     'table' => $table_name,
    48                     'link_to_page' => $row->guid,
    49                     'content' => $row->post_content,
    50                     'content_type' => 'post',
    51                 ];
    52             }
     89                $links[] = ['link' => $row->guid, 'page' => __('DB scan', 'wtotem'), 'is_internal' => true];;
     90            }
     91
     92            unset($tables['posts'][$key]);
     93
     94            $time_end = microtime(true);
     95            if (($time_end - $time_start) > $max_execution_time - 5) {
     96                WebTotemOption::setOptions([
     97                    'scan_temp' => [
     98                        'current_scan' => 'scanDB',
     99                        'need_to_scan' => $tables,
     100                        'links' => $links,
     101                    ]
     102                ]);
     103                return;
     104            }
     105
    53106        }
    54107
    55108        foreach ($tables['comments'] as $relation => $table) {
    56             $rows = self::getRows($table, ['comment_content' => $needles]);
     109            $rows = self::getRows($table, ['comment_content' => $needles], 'guid');
    57110
    58111            $posts_ids = array_column($rows, 'comment_post_ID');
     
    60113            $posts_rows = WebTotem::arrayMapIndex(WebTotem::convertObjectToArray($posts_rows), 'ID');
    61114
    62             $table_name = self::add_prefix($table);
    63115            foreach ($rows as $row) {
    64                 $data[] = [
    65                     'table' => $table_name,
    66                     'link_to_page' => $posts_rows[$row->comment_post_ID]['guid'],
    67                     'content' => $row->comment_content,
    68                     'content_type' => 'comment',
    69                 ];
    70             }
    71         }
    72 
    73         $matches_data = [];
    74         foreach ($data as $datum) {
    75             $matches_data = array_merge($matches_data, self::getMatches($datum['content'], $datum['link_to_page']));
    76         }
    77 
    78         self::saveData($matches_data);
     116                $links[] = ['link' => $posts_rows[$row->comment_post_ID]['guid'], 'page' => __('DB scan', 'wtotem'), 'is_internal' => true];
     117            }
     118
     119            unset($tables['comments'][$relation]);
     120
     121            $time_end = microtime(true);
     122            if (($time_end - $time_start) > $max_execution_time - 5) {
     123                WebTotemOption::setOptions([
     124                    'scan_temp' => [
     125                        'current_scan' => 'scanDB',
     126                        'need_to_scan' => $tables,
     127                        'links' => $links,
     128                    ]
     129                ]);
     130                return;
     131            }
     132        }
     133
     134        WebTotemOption::setOptions([
     135            'scan_temp' => [
     136                'current_scan' => 'scanFiles',
     137                'need_to_scan' => [],
     138                'links' => $links,
     139            ]
     140        ]);
     141
    79142    }
    80143
     
    86149     * @param string $table
    87150     *    Table name.
     151     * @param string $fields
     152     *    Required fields.
    88153     *
    89154     * @return array
    90155     */
    91     private static function getRows($table, $options = false)
    92     {
     156    private static function getRows($table, $options = false, $fields = false) {
    93157        global $wpdb;
    94158        $table_name = self::add_prefix($table);
     
    107171        $where = isset($where) ? 'WHERE (' . implode(' OR ', $where) . ')' : '';
    108172        if(strpos($table, 'posts') !== false) {
    109             $where .= " AND post_status = 'publish'";
    110         }
    111 
    112         $rows = $wpdb->get_results("SELECT * FROM $table_name $where");
     173            $where .= $where ? " AND " : "WHERE ";
     174            $where .= "post_status = 'publish'";
     175        }
     176
     177        $fields = $fields ?: '*';
     178        $rows = $wpdb->get_results("SELECT $fields FROM $table_name $where");
    113179
    114180        return (array)$rows ?: [];
     
    118184     * Get an array of tables
    119185     */
    120     private static function getTables()
    121     {
     186    private static function getTables() {
    122187        $tables = [
    123188            'posts' => ['posts'],
     
    144209     * @return string
    145210     */
    146     public static function add_prefix($table)
    147     {
     211    public static function add_prefix($table) {
    148212        global $wpdb;
    149213        return $wpdb->prefix . $table;
     
    154218     * formation of an array of data on them
    155219     */
    156     public static function scanFiles()
    157     {
     220    public static function scanFiles($scan_temp, $max_execution_time, $time_start) {
     221
     222        $tree = $scan_temp['need_to_scan'] ?? [];
     223        $links = $scan_temp['links'] ?? [];
     224
     225        $site_url = get_site_url();
    158226        $fileInfo = new WebTotemFileInfo();
    159227        $abspath = ABSPATH;
    160228
    161         $tree = [];
    162 
    163         // Adding files of active plugins
    164         if (WebTotem::isMultiSite()) {
    165             $all_plugs = array_keys(get_site_option('active_sitewide_plugins'));
    166         } else {
    167             $all_plugs = get_option('active_plugins');
    168         }
    169         foreach ($all_plugs as $value) {
    170             $plugin = explode('/', $value);
    171             $tree = array_merge($tree, $fileInfo->getDirectoryTree(WP_PLUGIN_DIR . '/' . $plugin[0]));
    172         }
    173 
    174         // Adding files of active theme
    175         $tree = array_merge($tree, $fileInfo->getDirectoryTree(get_template_directory()));
    176 
    177         $matches_data = [];
    178         foreach ($tree as $file_path) {
     229        if(empty($tree)){
     230            // Adding files of active plugins
     231            if (WebTotem::isMultiSite()) {
     232                $all_plugs = array_keys(get_site_option('active_sitewide_plugins'));
     233            } else {
     234                $all_plugs = get_option('active_plugins');
     235            }
     236            foreach ($all_plugs as $value) {
     237                $plugin = explode('/', $value);
     238                $tree = array_merge($tree, $fileInfo->getDirectoryTree(WP_PLUGIN_DIR . '/' . $plugin[0]));
     239            }
     240
     241            // Adding files of active theme
     242            $tree = array_merge($tree, $fileInfo->getDirectoryTree(get_template_directory()));
     243        }
     244
     245        foreach ($tree as $key => $file_path) {
    179246            $content = $fileInfo::fileContent($file_path);
    180             $matches_data = array_merge($matches_data, self::getMatches($content, str_replace($abspath, '/', $file_path)));
    181         }
    182 
    183         self::saveData($matches_data, 'files');
     247            if(self::hasMatches($content)){
     248                $link = $site_url . str_replace($abspath, '/', $file_path);
     249                $links[] = ['link' => $link, 'page' => __('File scan', 'wtotem'), 'is_internal' => true];
     250            }
     251            unset($tree[$key]);
     252
     253            $time_end = microtime(true);
     254            if (($time_end - $time_start) > $max_execution_time - 5) {
     255                WebTotemOption::setOptions([
     256                    'scan_temp' => [
     257                        'current_scan' => 'scanFiles',
     258                        'need_to_scan' => $tree,
     259                        'links' => $links,
     260                    ]
     261                ]);
     262                return;
     263            }
     264
     265        }
     266
     267        WebTotemOption::setOptions([
     268            'scan_temp' => [
     269                'current_scan' => 'checkConfidentialFiles',
     270                'need_to_scan' => [],
     271                'ready_to_save' => false,
     272                'links' => $links,
     273            ]
     274        ]);
     275    }
     276
     277
     278    /**
     279     * Get matches.
     280     *
     281     * @param string $content
     282     *
     283     * @return bool
     284     */
     285    private static function hasMatches($content) {
     286        $pattern = '/(<a.*?href=["\'](([\da-z\.-\/]+)([\/\w\.-\?\%\&]*)*\/?)["\'].*?>|<script.*?src=["\'](.*?)["\'].*?>|<iframe.*?src=["\'](.*?)["\'].*?>|onclick="[^"]*location[^"][^\'"]+\'([^\']+)\')/i';
     287        if (preg_match($pattern, $content)) {
     288            return true;
     289        }
     290        return false;
    184291    }
    185292
     
    187294     * Files scanning, search for confidential files.
    188295     */
    189     public static function checkConfidentialFiles()
    190     {
    191 
    192         $rows = WebTotemDB::getRows([], 'confidential_files', false, 'all');
    193         foreach ($rows['data'] as $index => $row) {
    194             if (!file_exists($row['path']) or !WebTotem::isPubliclyAccessible($row['url'], $row['path'])) {
    195                 WebTotemDB::deleteData(['id' => $row['id']], 'confidential_files');
    196                 unset($rows[$index]);
    197             }
    198         }
    199 
    200         $patterns = [
    201             '.user.ini',
    202             'wp-config.php.bak',
    203             'wp-config.php.bak.a2',
    204             'wp-config.php.swo',
    205             'wp-config.php.save',
    206             'wp-config.php~',
    207             'wp-config.old',
    208             '.wp-config.php.swp',
    209             'wp-config.bak',
    210             'wp-config.save',
    211             'wp-config.php_bak',
    212             'wp-config.php.swp',
    213             'wp-config.php.old',
    214             'wp-config.php.original',
    215             'wp-config.php.orig',
    216             'wp-config.txt',
    217             'wp-config.original',
    218             'wp-config.orig',
    219             '*.bak',
    220             '*.back',
    221             '*.backup',
    222             '*.old',
    223         ];
    224 
    225         $mask = implode(',', $patterns);
     296    public static function checkConfidentialFiles($scan_temp, $max_execution_time, $time_start) {
     297
     298        $files = $scan_temp['need_to_scan'] ?? [];
     299        $files_data = $scan_temp['confidential_files'] ?? [];
    226300        $root_path = ABSPATH;
    227301
    228         $files = self::glob_tree_search($root_path, '{' . $mask . '}');
    229 
    230         $paths_in_db = array_column($rows['data'], 'path');
    231         $files_data = [];
     302        if(empty($files) and !$scan_temp['ready_to_save']){
     303            $patterns = [
     304                    '.user.ini',
     305                    'wp-config.php.bak',
     306                    'wp-config.php.bak.a2',
     307                    'wp-config.php.swo',
     308                    'wp-config.php.save',
     309                    'wp-config.php~',
     310                    'wp-config.old',
     311                    '.wp-config.php.swp',
     312                    'wp-config.bak',
     313                    'wp-config.save',
     314                    'wp-config.php_bak',
     315                    'wp-config.php.swp',
     316                    'wp-config.php.old',
     317                    'wp-config.php.original',
     318                    'wp-config.php.orig',
     319                    'wp-config.txt',
     320                    'wp-config.original',
     321                    'wp-config.orig',
     322                    '*.bak',
     323                    '*.back',
     324                    '*.backup',
     325                    '*.old',
     326            ];
     327
     328            $mask = implode(',', $patterns);
     329            $files = self::glob_tree_search($root_path, '{' . $mask . '}',false);
     330            $files = array_merge(self::glob_tree_search($root_path . '/wp-content/', '{' . $mask . '}'), $files);
     331        }
     332
     333
    232334        foreach ($files as $file_path) {
    233335            $url = site_url(str_replace($root_path, '', $file_path));
     
    244346                ];
    245347            }
    246         }
    247 
    248         foreach ($files_data as $file) {
    249             $where = in_array($file['path'], $paths_in_db) ? ['path' => $file['path']] : false;
    250 
    251             WebTotemDB::setData([
    252                 'created_at' => date("Y-m-d H:i:s"),
    253                 'path' => urlencode($file['path']),
    254                 'name' => urlencode($file['name']),
    255                 'size' => $file['size'],
    256                 'modified_at' => $file['modified_at'],
    257                 'url' => $file['url'],
    258             ], 'confidential_files', $where);
    259         }
     348
     349            $time_end = microtime(true);
     350            if (($time_end - $time_start) > $max_execution_time - 5) {
     351                WebTotemOption::setOptions([
     352                        'scan_temp' => [
     353                                'current_scan' => 'checkConfidentialFiles',
     354                                'need_to_scan' => $files,
     355                                'links' => $scan_temp['links'],
     356                                'confidential_files' => $files_data,
     357                        ]
     358                ]);
     359                return;
     360            }
     361
     362        }
     363
     364        if($scan_temp['ready_to_save']){
     365            self::saveData($files_data);
     366        } else {
     367            WebTotemOption::setOptions([
     368                    'scan_temp' => [
     369                            'current_scan' => 'checkConfidentialFiles',
     370                            'need_to_scan' => [],
     371                            'links' => $scan_temp['links'],
     372                            'ready_to_save' => true,
     373                            'confidential_files' => $files_data,
     374                    ]
     375            ]);
     376            return;
     377        }
     378
     379        WebTotemOption::setOptions([
     380                'scan_temp' => [
     381                        'current_scan' => 'crawler',
     382                        'need_to_scan' => [],
     383                        'ready_to_save' => false,
     384                        'links' => $scan_temp['links'],
     385                        'confidential_files' => [],
     386                ]
     387        ]);
     388
     389    }
     390
     391    /**
     392     * Save data.
     393     *
     394     * @param array $data
     395     *    Array matches data.
     396     */
     397    private static function saveData($data) {
     398
     399        WebTotemDB::deleteData([], 'confidential_files');
     400        $values = '';
     401        foreach ($data as $file) {
     402                $values .= sprintf("('%s','%s','%s','%s','%s','%s'),",
     403                        date("Y-m-d H:i:s"),
     404                        urlencode($file['path']),
     405                        urlencode($file['name']),
     406                        $file['size'],
     407                        $file['modified_at'],
     408                        $file['url']
     409                );
     410        }
     411
     412        $values = substr_replace($values, ";", -1);
     413
     414        $columns = '(created_at, path, name, size, modified_at, url)';
     415
     416        WebTotemDB::setRows('confidential_files', $columns, $values);
    260417    }
    261418
     
    271428     *   Array of file paths found by mask.
    272429     */
    273     public static function glob_tree_search($path, $mask)
    274     {
     430    public static function glob_tree_search($path, $mask, $recursively = true) {
    275431        $out = [];
    276432        foreach (glob($path . $mask, GLOB_BRACE) as $file_path) {
     
    278434        }
    279435
    280         foreach (glob($path . '/*', GLOB_ONLYDIR) as $dir) {
    281             $out = array_merge($out, self::glob_tree_search($dir, $mask));
     436        if ($recursively) {
     437            foreach (glob($path . '/*', GLOB_ONLYDIR) as $dir) {
     438                $out = array_merge($out, self::glob_tree_search($dir, $mask));
     439            }
    282440        }
    283441
     
    285443    }
    286444
    287     /**
    288      * Get matches.
    289      *
    290      * @param string $content
    291      *
    292      * @param string $source
    293      *
    294      * @return array
    295      */
    296     private static function getMatches($content, $source)
    297     {
    298         preg_match_all('/(<a.*?href=["\'](([\da-z\.-\/]+)([\/\w\.-\?\%\&]*)*\/?)["\'].*?>|<script.*?src=["\'](.*?)["\'].*?>|<iframe.*?src=["\'](.*?)["\'].*?>)/i', $content, $all_matches);
    299         $array = [];
    300 
    301         foreach ($all_matches[0] as $match) {
    302             preg_match_all('/<a.*?href=["\'](.*?)["\'].*?>/i', $match, $links_matches);
    303             if ($links_matches[1]) $array['links'][] = $links_matches[1];
    304             preg_match_all('/<script.*?src=["\'](.*?)["\'].*?>/i', $match, $js_matches);
    305             if ($js_matches[1]) $array['scripts'][] = $js_matches[1];
    306             preg_match_all('/<iframe.*?src=["\'](.*?)["\'].*?>/i', $match, $iframe_matches);
    307             if ($iframe_matches[1]) $array['iframes'][] = $iframe_matches[1];
    308         }
    309 
    310         foreach ($array as $type => $matches_arr) {
    311             foreach ($matches_arr as $matches) {
    312                 foreach ($matches as $match) {
    313                     $matches_data[] = [
    314                         'data_type' => $type,
    315                         'source' => $source,
    316                         'content' => $match,
    317                     ];
    318                 }
    319             }
    320         }
    321         return $matches_data ?? [];
    322     }
    323 
    324     /**
    325      * Save data.
    326      *
    327      * @param array $matches_data
    328      *    Array matches data.
    329      * @param string $scan_source
    330      *    Scan_source.
    331      */
    332     private static function saveData($matches_data, $scan_source = 'DB')
    333     {
    334 
    335         WebTotemDB::deleteData(['scan_source' => $scan_source], 'scan_logs');
    336         foreach ($matches_data as $datum) {
    337 
    338             $url = $datum['content'];
    339             $domain = WEBTOTEM_SITE_DOMAIN;
    340             $is_internal = (
    341                 false !== stripos($url, '//' . $domain) ||
    342                 stripos($url, '.' . $domain) ||
    343                 (
    344                     0 !== strpos($url, '//') &&
    345                     0 === strpos($url, '/')
    346                 )
    347             );
    348 
    349             WebTotemDB::setData([
    350                 'created_at' => date("Y-m-d H:i:s"),
    351                 'scan_source' => $scan_source, // DB or file
    352                 'data_type' => $datum['data_type'],
    353                 'source' => $datum['source'], // link_to_page or file_path
    354                 'content' => $datum['content'],
    355                 'is_internal' => $is_internal,
    356             ], 'scan_logs');
    357         }
    358     }
    359445}
  • wt-security/trunk/readme.txt

    r2877854 r2899041  
    7171
    7272== Changelog ==
     73= 2.4.20 =
     74* Fixed an issue with blocking custom administrator roles
     75* WP scan improvements
     76
    7377= 2.4.19 =
    7478* Fixed some errors in multisite
     
    127131
    128132= 2.4.7 =
    129 * Fixed a issue related to using the function str_contains
     133* Fixed an issue related to using the function str_contains
    130134
    131135= 2.4.6 =
  • wt-security/trunk/src/Common.php

    r2877854 r2899041  
    7171
    7272  function WtotemDailyCron(){
    73     WebTotemScan::initialize();
     73      WebTotemOption::setOptions(['scan_init' => 1]);
    7474  }
     75
     76    /** Launch of the minute cron. */
     77    if(WebTotemOption::getOption('scan_init')){
     78
     79        // Register the n minute interval
     80        add_filter( 'cron_schedules', 'cron_add_some_min' );
     81        function cron_add_some_min( $schedules ) {
     82            $schedules['some_min'] = array(
     83                    'interval' => 60,
     84                    'display' => __('Every few minutes', 'wtotem'),
     85            );
     86            return $schedules;
     87        }
     88
     89        // Registering an event
     90        add_action( 'wp', 'wtotem_step_cron' );
     91        function wtotem_step_cron() {
     92            if( ! wp_next_scheduled( 'wtotem_step_init_cron' ) ) {
     93                wp_schedule_event( time(), 'some_min', 'wtotem_step_init_cron' );
     94            }
     95        }
     96
     97        // Linking the function to the cron event/task
     98        add_action( 'wtotem_step_init_cron', 'WebTotemScan::initialize' );
     99    }
    75100
    76101  /**
  • wt-security/trunk/src/PageHandler.php

    r2877854 r2899041  
    1818 */
    1919function wtotem_ajax_callback() {
     20
     21    if (WebTotemRequest::get('ajax_action') != NULL) {
     22        WebTotemAjax::wtotem_scan();
     23    }
    2024
    2125    $composer_autoload = WEBTOTEM_PLUGIN_PATH . '/vendor/autoload.php';
     
    503507        'template' => 'attacks_map',
    504508    ];
    505 
    506     $build[] = [
    507         'variables' => [
    508             "title" => __('Firewall activity', 'wtotem'),
    509         ],
    510         'template' => 'section_header',
    511     ];
    512 
    513509
    514510    // Firewall stats.
     
    922918            "iframes_pagination" => WebTotem::paginationBuild(10, $iframes['count']),
    923919
    924             "next_scan" => sprintf(__('%dh %dm', 'wtotem'), $hr, $min),
     920                "next_scan" => sprintf(__('%dh %dm', 'wtotem'), $hr, $min),
     921                "scan_init" => WebTotemOption::getOption('scan_init') ?: 0,
    925922        ],
    926923        'template' => 'scan_logs',
  • wt-security/trunk/wt-security.php

    r2877854 r2899041  
    77 * Text Domain: wtotem
    88 * Domain Path: /lang
    9  * Version: 2.4.19
     9 * Version: 2.4.20
    1010 *
    1111 * PHP version 7.1
     
    5454 * Current version of the plugin's code.
    5555 */
    56 define('WEBTOTEM_VERSION', '2.4.19');
     56define('WEBTOTEM_VERSION', '2.4.20');
    5757
    5858/**
     
    9696require_once 'lib/modules/logs/EventListener.php';
    9797require_once 'lib/modules/logs/Scan.php';
     98require_once 'lib/modules/logs/Crawler.php';
    9899require_once 'lib/Request.php';
    99100require_once 'lib/Interface.php';
Note: See TracChangeset for help on using the changeset viewer.