Completed
Pull Request — master (#547)
by Richard
09:44
created

Protector::stopForumSpamLookup()   C

Complexity

Conditions 7
Paths 25

Size

Total Lines 30
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 21
nc 25
nop 3
dl 0
loc 30
rs 6.7272
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Class Protector
5
 */
6
class Protector
7
{
8
    public $mydirname;
9
10
    public $_conn;
11
    public $_conf            = array();
12
    public $_conf_serialized = '';
13
14
    public $_bad_globals = array();
15
16
    public $message                = '';
17
    public $warning                = false;
18
    public $error                  = false;
19
    public $_doubtful_requests     = array();
20
    public $_bigumbrella_doubtfuls = array();
21
22
    public $_dblayertrap_doubtfuls        = array();
23
    public $_dblayertrap_doubtful_needles = array(
24
        'information_schema',
25
        'select',
26
        "'",
27
        '"');
28
29
    public $_logged = false;
30
31
    public $_done_badext   = false;
32
    public $_done_intval   = false;
33
    public $_done_dotdot   = false;
34
    public $_done_nullbyte = false;
35
    public $_done_contami  = false;
36
    public $_done_isocom   = false;
37
    public $_done_union    = false;
38
    public $_done_dos      = false;
39
40
    public $_safe_badext  = true;
41
    public $_safe_contami = true;
42
    public $_safe_isocom  = true;
43
    public $_safe_union   = true;
44
45
    public $_spamcount_uri = 0;
46
47
    public $_should_be_banned_time0 = false;
48
    public $_should_be_banned       = false;
49
50
    public $_dos_stage;
51
52
    public $ip_matched_info;
53
54
    public $last_error_type = 'UNKNOWN';
55
56
    /**
57
     * Constructor
58
     */
59
    protected function __construct()
60
    {
61
        $this->mydirname = 'protector';
62
63
        // Preferences from configs/cache
64
        $this->_conf_serialized = @file_get_contents($this->get_filepath4confighcache());
65
        $this->_conf            = @unserialize($this->_conf_serialized);
66
        if (empty($this->_conf)) {
67
            $this->_conf = array();
68
        }
69
70
        if (!empty($this->_conf['global_disabled'])) {
71
            return;
72
        }
73
74
        // die if PHP_SELF XSS found (disabled in 2.53)
75
        //    if ( preg_match( '/[<>\'";\n ]/' , @$_SERVER['PHP_SELF'] ) ) {
76
        //        $this->message .= "Invalid PHP_SELF '{$_SERVER['PHP_SELF']}' found.\n" ;
77
        //        $this->output_log( 'PHP_SELF XSS' ) ;
78
        //        die( 'invalid PHP_SELF' ) ;
79
        //    }
80
81
        // sanitize against PHP_SELF/PATH_INFO XSS (disabled in 3.33)
82
        //    $_SERVER['PHP_SELF'] = strtr( @$_SERVER['PHP_SELF'] , array( '<' => '%3C' , '>' => '%3E' , "'" => '%27' , '"' => '%22' ) ) ;
83
        //    if( ! empty( $_SERVER['PATH_INFO'] ) ) $_SERVER['PATH_INFO'] = strtr( @$_SERVER['PATH_INFO'] , array( '<' => '%3C' , '>' => '%3E' , "'" => '%27' , '"' => '%22' ) ) ;
84
85
        $this->_bad_globals = array(
86
            'GLOBALS',
87
            '_SESSION',
88
            'HTTP_SESSION_VARS',
89
            '_GET',
90
            'HTTP_GET_VARS',
91
            '_POST',
92
            'HTTP_POST_VARS',
93
            '_COOKIE',
94
            'HTTP_COOKIE_VARS',
95
            '_SERVER',
96
            'HTTP_SERVER_VARS',
97
            '_REQUEST',
98
            '_ENV',
99
            '_FILES',
100
            'xoopsDB',
101
            'xoopsUser',
102
            'xoopsUserId',
103
            'xoopsUserGroups',
104
            'xoopsUserIsAdmin',
105
            'xoopsConfig',
106
            'xoopsOption',
107
            'xoopsModule',
108
            'xoopsModuleConfig');
109
110
        $this->_initial_recursive($_GET, 'G');
111
        $this->_initial_recursive($_POST, 'P');
112
        $this->_initial_recursive($_COOKIE, 'C');
113
    }
114
115
    /**
116
     * @param $val
117
     * @param $key
118
     */
119
    protected function _initial_recursive($val, $key)
120
    {
121
        if (is_array($val)) {
122
            foreach ($val as $subkey => $subval) {
123
                // check bad globals
124 View Code Duplication
                if (in_array($subkey, $this->_bad_globals, true)) {
125
                    $this->message .= "Attempt to inject '$subkey' was found.\n";
126
                    $this->_safe_contami   = false;
127
                    $this->last_error_type = 'CONTAMI';
128
                }
129
                $this->_initial_recursive($subval, $key . '_' . base64_encode($subkey));
130
            }
131
        } else {
132
            // check nullbyte attack
133
            if (@$this->_conf['san_nullbyte'] && false !== strpos($val, chr(0))) {
134
                $val = str_replace(chr(0), ' ', $val);
135
                $this->replace_doubtful($key, $val);
136
                $this->message .= "Injecting Null-byte '$val' found.\n";
137
                $this->output_log('NullByte', 0, false, 32);
138
                // $this->purge() ;
139
            }
140
141
            // register as doubtful requests against SQL Injections
142
            if (preg_match('?[\s\'"`/]?', $val)) {
143
                $this->_doubtful_requests["$key"] = $val;
144
            }
145
        }
146
    }
147
148
    /**
149
     * @return Protector
150
     */
151
    public static function getInstance()
152
    {
153
        static $instance;
154
        if (!isset($instance)) {
155
            $instance = new Protector();
156
        }
157
158
        return $instance;
159
    }
160
161
    /**
162
     * @return bool
163
     */
164
    public function updateConfFromDb()
165
    {
166
        $constpref = '_MI_' . strtoupper($this->mydirname);
167
168
        if (empty($this->_conn)) {
169
            return false;
170
        }
171
172
        $result = @mysqli_query($this->_conn, 'SELECT conf_name,conf_value FROM ' . XOOPS_DB_PREFIX . "_config WHERE conf_title like '" . $constpref . "%'");
173
        if (!$result || mysqli_num_rows($result) < 5) {
174
            return false;
175
        }
176
        $db_conf = array();
177
        while (list($key, $val) = mysqli_fetch_row($result)) {
178
            $db_conf[$key] = $val;
179
        }
180
        $db_conf_serialized = serialize($db_conf);
181
182
        // update config cache
183
        if ($db_conf_serialized != $this->_conf_serialized) {
184
            $fp = fopen($this->get_filepath4confighcache(), 'w');
185
            fwrite($fp, $db_conf_serialized);
186
            fclose($fp);
187
            $this->_conf = $db_conf;
188
        }
189
190
        return true;
191
    }
192
193
    /**
194
     * @param $conn
195
     */
196
    public function setConn($conn)
197
    {
198
        $this->_conn = $conn;
199
    }
200
201
    /**
202
     * @return array
203
     */
204
    public function getConf()
205
    {
206
        return $this->_conf;
207
    }
208
209
    /**
210
     * @param bool $redirect_to_top
211
     */
212
    public function purge($redirect_to_top = false)
213
    {
214
        $this->purgeNoExit();
215
216
        if ($redirect_to_top) {
217
            header('Location: ' . XOOPS_URL . '/');
218
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method purge() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
219
        } else {
220
            $ret = $this->call_filter('prepurge_exit');
221
            if ($ret == false) {
222
                die('Protector detects attacking actions');
0 ignored issues
show
Coding Style Compatibility introduced by
The method purge() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
223
            }
224
        }
225
    }
226
227
    public function purgeSession()
228
    {
229
        // clear all session values
230
        if (isset($_SESSION)) {
231
            foreach ($_SESSION as $key => $val) {
232
                $_SESSION[$key] = '';
233
                if (isset($GLOBALS[$key])) {
234
                    $GLOBALS[$key] = '';
235
                }
236
            }
237
        }
238
    }
239
240
    public function purgeCookies()
241
    {
242
        if (!headers_sent()) {
243
            // clear typical session id of PHP
244
            setcookie('PHPSESSID', '', time() - 3600, '/', '', 0);
245
            if (isset($_COOKIE[session_name()])) {
246
                setcookie(session_name(), '', time() - 3600, '/', '', 0);
247
            }
248
249
            // clear autologin cookies
250
            $xoops_cookie_path = defined('XOOPS_COOKIE_PATH') ? XOOPS_COOKIE_PATH : preg_replace('?http[s]{0,1}://[^/]+(/.*)$?', "$1", XOOPS_URL);
251
            if ($xoops_cookie_path == XOOPS_URL) {
252
                $xoops_cookie_path = '/';
253
            }
254
            setcookie($GLOBALS['xoopsConfig']['usercookie'], null, time() - 3600, '/', XOOPS_COOKIE_DOMAIN, 0);
255
            setcookie($GLOBALS['xoopsConfig']['usercookie'], null, time() - 3600, '/');
256
257
            setcookie('autologin_uname', '', time() - 3600, $xoops_cookie_path, '', 0);
258
            setcookie('autologin_pass', '', time() - 3600, $xoops_cookie_path, '', 0);
259
        }
260
    }
261
262
    public function purgeNoExit()
263
    {
264
        $this->purgeSession();
265
        $this->purgeCookies();
266
    }
267
268
    public function deactivateCurrentUser()
269
    {
270
        /* @var $xoopsUser XoopsUser */
271
        global $xoopsUser;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
272
273
        if (is_object($xoopsUser)) {
274
            /** @var XoopsMemberHandler */
275
            $userHandler = xoops_getHandler('user');
276
            $xoopsUser->setVar('level', 0);
277
            $actkey = substr(md5(uniqid(mt_rand(), 1)), 0, 8);
278
            $xoopsUser->setVar('actkey', $actkey);
279
            $userHandler->insert($xoopsUser);
280
        }
281
        $this->purgeNoExit();
282
    }
283
284
    /**
285
     * @param string $type
286
     * @param int    $uid
287
     * @param bool   $unique_check
288
     * @param int    $level
289
     *
290
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
291
     */
292
    public function output_log($type = 'UNKNOWN', $uid = 0, $unique_check = false, $level = 1)
293
    {
294
        if ($this->_logged) {
295
            return true;
296
        }
297
298
        if (!($this->_conf['log_level'] & $level)) {
299
            return true;
300
        }
301
302
        if (empty($this->_conn)) {
303
            $this->_conn = new mysqli(XOOPS_DB_HOST, XOOPS_DB_USER, XOOPS_DB_PASS);
304
            if (0 !== $this->_conn->connect_errno) {
305
                die('db connection failed.');
0 ignored issues
show
Coding Style Compatibility introduced by
The method output_log() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
306
            }
307
            if (!mysqli_select_db($this->_conn, XOOPS_DB_NAME)) {
308
                die('db selection failed.');
0 ignored issues
show
Coding Style Compatibility introduced by
The method output_log() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
309
            }
310
        }
311
312
        $ip    = \Xmf\IPAddress::fromRequest()->asReadable();
313
        $agent = @$_SERVER['HTTP_USER_AGENT'];
314
315
        if ($unique_check) {
316
            $result = mysqli_query($this->_conn, 'SELECT ip,type FROM ' . XOOPS_DB_PREFIX . '_' . $this->mydirname . '_log ORDER BY timestamp DESC LIMIT 1');
317
            list($last_ip, $last_type) = mysqli_fetch_row($result);
318
            if ($last_ip == $ip && $last_type == $type) {
319
                $this->_logged = true;
320
321
                return true;
322
            }
323
        }
324
325
        mysqli_query(
326
            $this->_conn,
327
            'INSERT INTO ' . XOOPS_DB_PREFIX . '_' . $this->mydirname . "_log SET ip='"
328
            . mysqli_real_escape_string($this->_conn, $ip) . "',agent='"
329
            . mysqli_real_escape_string($this->_conn, $agent) . "',type='"
330
            . mysqli_real_escape_string($this->_conn, $type) . "',description='"
331
            . mysqli_real_escape_string($this->_conn, $this->message) . "',uid='"
332
            . (int)$uid . "',timestamp=NOW()"
333
        );
334
        $this->_logged = true;
335
336
        return true;
337
    }
338
339
    /**
340
     * @param $expire
341
     *
342
     * @return bool
343
     */
344 View Code Duplication
    public function write_file_bwlimit($expire)
345
    {
346
        $expire = min((int)$expire, time() + 300);
347
348
        $fp = @fopen($this->get_filepath4bwlimit(), 'w');
349
        if ($fp) {
350
            @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
351
            fwrite($fp, $expire . "\n");
352
            @flock($fp, LOCK_UN);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
353
            fclose($fp);
354
355
            return true;
356
        } else {
357
            return false;
358
        }
359
    }
360
361
    /**
362
     * @return mixed
363
     */
364
    public function get_bwlimit()
365
    {
366
        list($expire) = @file(Protector::get_filepath4bwlimit());
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
367
        $expire = min((int)$expire, time() + 300);
368
369
        return $expire;
370
    }
371
372
    /**
373
     * @return string
374
     */
375
    public static function get_filepath4bwlimit()
376
    {
377
        return XOOPS_TRUST_PATH . '/modules/protector/configs/bwlimit' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
378
    }
379
380
    /**
381
     * @param $bad_ips
382
     *
383
     * @return bool
384
     */
385 View Code Duplication
    public function write_file_badips($bad_ips)
386
    {
387
        asort($bad_ips);
388
389
        $fp = @fopen($this->get_filepath4badips(), 'w');
390
        if ($fp) {
391
            @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
392
            fwrite($fp, serialize($bad_ips) . "\n");
393
            @flock($fp, LOCK_UN);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
394
            fclose($fp);
395
396
            return true;
397
        } else {
398
            return false;
399
        }
400
    }
401
402
    /**
403
     * @param int  $jailed_time
404
     * @param null|string|false $ip
405
     *
406
     * @return bool
407
     */
408
    public function register_bad_ips($jailed_time = 0, $ip = null)
409
    {
410
        if (empty($ip)) {
411
            $ip = \Xmf\IPAddress::fromRequest()->asReadable();
412
        }
413
        if (empty($ip)) {
414
            return false;
415
        }
416
417
        $bad_ips      = $this->get_bad_ips(true);
418
        $bad_ips[$ip] = $jailed_time ?: 0x7fffffff;
419
420
        return $this->write_file_badips($bad_ips);
421
    }
422
423
    /**
424
     * @param bool $with_jailed_time
425
     *
426
     * @return array|mixed
427
     */
428
    public function get_bad_ips($with_jailed_time = false)
429
    {
430
        list($bad_ips_serialized) = @file(Protector::get_filepath4badips());
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
431
        $bad_ips = empty($bad_ips_serialized) ? array() : @unserialize($bad_ips_serialized);
432
        if (!is_array($bad_ips) || isset($bad_ips[0])) {
433
            $bad_ips = array();
434
        }
435
436
        // expire jailed_time
437
        $pos = 0;
438
        foreach ($bad_ips as $bad_ip => $jailed_time) {
439
            if ($jailed_time >= time()) {
440
                break;
441
            }
442
            ++$pos;
443
        }
444
        $bad_ips = array_slice($bad_ips, $pos);
445
446
        if ($with_jailed_time) {
447
            return $bad_ips;
448
        } else {
449
            return array_keys($bad_ips);
450
        }
451
    }
452
453
    /**
454
     * @return string
455
     */
456
    public static function get_filepath4badips()
457
    {
458
        return XOOPS_TRUST_PATH . '/modules/protector/configs/badips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
459
    }
460
461
    /**
462
     * @param bool $with_info
463
     *
464
     * @return array|mixed
465
     */
466
    public function get_group1_ips($with_info = false)
467
    {
468
        list($group1_ips_serialized) = @file(Protector::get_filepath4group1ips());
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
469
        $group1_ips = empty($group1_ips_serialized) ? array() : @unserialize($group1_ips_serialized);
470
        if (!is_array($group1_ips)) {
471
            $group1_ips = array();
472
        }
473
474
        if ($with_info) {
475
            $group1_ips = array_flip($group1_ips);
476
        }
477
478
        return $group1_ips;
479
    }
480
481
    /**
482
     * @return string
483
     */
484
    public static function get_filepath4group1ips()
485
    {
486
        return XOOPS_TRUST_PATH . '/modules/protector/configs/group1ips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
487
    }
488
489
    /**
490
     * @return string
491
     */
492
    public function get_filepath4confighcache()
493
    {
494
        return XOOPS_TRUST_PATH . '/modules/protector/configs/configcache' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
495
    }
496
497
    /**
498
     * @param $ips
499
     *
500
     * @return bool
501
     */
502
    public function ip_match($ips)
503
    {
504
        $requestIp = \Xmf\IPAddress::fromRequest()->asReadable();
505
        if (false === $requestIp) { // nothing to match
506
            $this->ip_matched_info = null;
507
            return false;
508
        }
509
        foreach ($ips as $ip => $info) {
510
            if ($ip) {
511
                switch (strtolower(substr($ip, -1))) {
512
                    case '.' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
513
                    case ':' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
514
                        // foward match
515
                        if (substr($requestIp, 0, strlen($ip)) == $ip) {
516
                            $this->ip_matched_info = $info;
517
                            return true;
518
                        }
519
                        break;
520
                    case '0' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
521
                    case '1' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
522
                    case '2' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
523
                    case '3' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
524
                    case '4' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
525
                    case '5' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
526
                    case '6' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
527
                    case '7' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
528
                    case '8' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
529
                    case '9' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
530
                    case 'a' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
531
                    case 'b' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
532
                    case 'c' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
533
                    case 'd' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
534
                    case 'e' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
535
                    case 'f' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
536
                        // full match
537
                        if ($requestIp == $ip) {
538
                            $this->ip_matched_info = $info;
539
                            return true;
540
                        }
541
                        break;
542
                    default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
543
                        // perl regex
544
                        if (@preg_match($ip, $requestIp)) {
545
                            $this->ip_matched_info = $info;
546
                            return true;
547
                        }
548
                        break;
549
                }
550
            }
551
        }
552
        $this->ip_matched_info = null;
553
        return false;
554
    }
555
556
    /**
557
     * @param null|string|false $ip
558
     *
559
     * @return bool
560
     */
561
    public function deny_by_htaccess($ip = null)
562
    {
563
        if (empty($ip)) {
564
            $ip = \Xmf\IPAddress::fromRequest()->asReadable();
565
        }
566
        if (empty($ip)) {
567
            return false;
568
        }
569
        if (!function_exists('file_get_contents')) {
570
            return false;
571
        }
572
573
        $target_htaccess = XOOPS_ROOT_PATH . '/.htaccess';
574
        $backup_htaccess = XOOPS_ROOT_PATH . '/uploads/.htaccess.bak';
575
576
        $ht_body = file_get_contents($target_htaccess);
577
578
        // make backup as uploads/.htaccess.bak automatically
579
        if ($ht_body && !file_exists($backup_htaccess)) {
580
            $fw = fopen($backup_htaccess, 'w');
581
            fwrite($fw, $ht_body);
582
            fclose($fw);
583
        }
584
585
        // if .htaccess is broken, restore from backup
586
        if (!$ht_body && file_exists($backup_htaccess)) {
587
            $ht_body = file_get_contents($backup_htaccess);
588
        }
589
590
        // new .htaccess
591
        if ($ht_body === false) {
592
            $ht_body = '';
593
        }
594
595
        if (preg_match("/^(.*)#PROTECTOR#\s+(DENY FROM .*)\n#PROTECTOR#\n(.*)$/si", $ht_body, $regs)) {
596
            if (substr($regs[2], -strlen($ip)) == $ip) {
597
                return true;
598
            }
599
            $new_ht_body = $regs[1] . "#PROTECTOR#\n" . $regs[2] . " $ip\n#PROTECTOR#\n" . $regs[3];
600
        } else {
601
            $new_ht_body = "#PROTECTOR#\nDENY FROM $ip\n#PROTECTOR#\n" . $ht_body;
602
        }
603
604
        // error_log( "$new_ht_body\n" , 3 , "/tmp/error_log" ) ;
605
606
        $fw = fopen($target_htaccess, 'w');
607
        @flock($fw, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
608
        fwrite($fw, $new_ht_body);
609
        @flock($fw, LOCK_UN);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
610
        fclose($fw);
611
612
        return true;
613
    }
614
615
    /**
616
     * @return array
617
     */
618
    public function getDblayertrapDoubtfuls()
619
    {
620
        return $this->_dblayertrap_doubtfuls;
621
    }
622
623
    /**
624
     * @param $val
625
     * @return null
626
     */
627
    protected function _dblayertrap_check_recursive($val)
628
    {
629
        if (is_array($val)) {
630
            foreach ($val as $subval) {
631
                $this->_dblayertrap_check_recursive($subval);
632
            }
633
        } else {
634
            if (strlen($val) < 6) {
635
                return null;
636
            }
637
            $val = get_magic_quotes_gpc() ? stripslashes($val) : $val;
638
            foreach ($this->_dblayertrap_doubtful_needles as $needle) {
639
                if (false !== stripos($val, $needle)) {
640
                    $this->_dblayertrap_doubtfuls[] = $val;
641
                }
642
            }
643
        }
644
    }
645
646
    /**
647
     * @param  bool $force_override
648
     * @return null
649
     */
650
    public function dblayertrap_init($force_override = false)
651
    {
652
        if (!empty($GLOBALS['xoopsOption']['nocommon']) || defined('_LEGACY_PREVENT_EXEC_COMMON_') || defined('_LEGACY_PREVENT_LOAD_CORE_')) {
653
            return null;
654
        } // skip
655
656
        $this->_dblayertrap_doubtfuls = array();
657
        $this->_dblayertrap_check_recursive($_GET);
658
        $this->_dblayertrap_check_recursive($_POST);
659
        $this->_dblayertrap_check_recursive($_COOKIE);
660
        if (empty($this->_conf['dblayertrap_wo_server'])) {
661
            $this->_dblayertrap_check_recursive($_SERVER);
662
        }
663
664
        if (!empty($this->_dblayertrap_doubtfuls) || $force_override) {
665
            @define('XOOPS_DB_ALTERNATIVE', 'ProtectorMysqlDatabase');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
666
            require_once dirname(__DIR__) . '/class/ProtectorMysqlDatabase.class.php';
667
        }
668
    }
669
670
    /**
671
     * @param $val
672
     */
673
    protected function _bigumbrella_check_recursive($val)
674
    {
675
        if (is_array($val)) {
676
            foreach ($val as $subval) {
677
                $this->_bigumbrella_check_recursive($subval);
678
            }
679
        } else {
680
            if (preg_match('/[<\'"].{15}/s', $val, $regs)) {
681
                $this->_bigumbrella_doubtfuls[] = $regs[0];
682
            }
683
        }
684
    }
685
686
    public function bigumbrella_init()
687
    {
688
        $this->_bigumbrella_doubtfuls = array();
689
        $this->_bigumbrella_check_recursive($_GET);
690
        $this->_bigumbrella_check_recursive(@$_SERVER['PHP_SELF']);
691
692
        if (!empty($this->_bigumbrella_doubtfuls)) {
693
            ob_start(array($this, 'bigumbrella_outputcheck'));
694
        }
695
    }
696
697
    /**
698
     * @param $s
699
     *
700
     * @return string
701
     */
702
    public function bigumbrella_outputcheck($s)
703
    {
704
        if (defined('BIGUMBRELLA_DISABLED')) {
705
            return $s;
706
        }
707
708
        if (function_exists('headers_list')) {
709
            foreach (headers_list() as $header) {
710
                if (false !== stripos($header, 'Content-Type:') && false === stripos($header, 'text/html')) {
711
                    return $s;
712
                }
713
            }
714
        }
715
716
        if (!is_array($this->_bigumbrella_doubtfuls)) {
717
            return 'bigumbrella injection found.';
718
        }
719
720
        foreach ($this->_bigumbrella_doubtfuls as $doubtful) {
721
            if (false !== strpos($s, $doubtful)) {
722
                return 'XSS found by Protector.';
723
            }
724
        }
725
726
        return $s;
727
    }
728
729
    /**
730
     * @return bool
731
     */
732
    public function intval_allrequestsendid()
733
    {
734
        global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
735
736
        if ($this->_done_intval) {
737
            return true;
738
        } else {
739
            $this->_done_intval = true;
740
        }
741
742 View Code Duplication
        foreach ($_GET as $key => $val) {
743
            if (substr($key, -2) === 'id' && !is_array($_GET[$key])) {
744
                $newval     = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
745
                $_GET[$key] = $HTTP_GET_VARS[$key] = $newval;
746
                if ($_REQUEST[$key] == $_GET[$key]) {
747
                    $_REQUEST[$key] = $newval;
748
                }
749
            }
750
        }
751 View Code Duplication
        foreach ($_POST as $key => $val) {
752
            if (substr($key, -2) === 'id' && !is_array($_POST[$key])) {
753
                $newval      = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
754
                $_POST[$key] = $HTTP_POST_VARS[$key] = $newval;
755
                if ($_REQUEST[$key] == $_POST[$key]) {
756
                    $_REQUEST[$key] = $newval;
757
                }
758
            }
759
        }
760 View Code Duplication
        foreach ($_COOKIE as $key => $val) {
761
            if (substr($key, -2) === 'id' && !is_array($_COOKIE[$key])) {
762
                $newval        = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
763
                $_COOKIE[$key] = $HTTP_COOKIE_VARS[$key] = $newval;
764
                if ($_REQUEST[$key] == $_COOKIE[$key]) {
765
                    $_REQUEST[$key] = $newval;
766
                }
767
            }
768
        }
769
770
        return true;
771
    }
772
773
    /**
774
     * @return bool
775
     */
776
    public function eliminate_dotdot()
777
    {
778
        global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
779
780
        if ($this->_done_dotdot) {
781
            return true;
782
        } else {
783
            $this->_done_dotdot = true;
784
        }
785
786
        foreach ($_GET as $key => $val) {
787
            if (is_array($_GET[$key])) {
788
                continue;
789
            }
790
            if (substr(trim($val), 0, 3) === '../' || false !== strpos($val, '/../')) {
791
                $this->last_error_type = 'DirTraversal';
792
                $this->message .= "Directory Traversal '$val' found.\n";
793
                $this->output_log($this->last_error_type, 0, false, 64);
794
                $sanitized_val = str_replace(chr(0), '', $val);
795
                if (substr($sanitized_val, -2) !== ' .') {
796
                    $sanitized_val .= ' .';
797
                }
798
                $_GET[$key] = $HTTP_GET_VARS[$key] = $sanitized_val;
799
                if ($_REQUEST[$key] == $_GET[$key]) {
800
                    $_REQUEST[$key] = $sanitized_val;
801
                }
802
            }
803
        }
804
805
        /*    foreach ($_POST as $key => $val) {
806
                if( is_array( $_POST[ $key ] ) ) continue ;
807
                if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) {
808
                    $this->last_error_type = 'ParentDir' ;
809
                    $this->message .= "Doubtful file specification '$val' found.\n" ;
810
                    $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
811
                    $sanitized_val = str_replace( chr(0) , '' , $val ) ;
812
                    if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
813
                    $_POST[ $key ] = $HTTP_POST_VARS[ $key ] = $sanitized_val ;
814
                    if ($_REQUEST[ $key ] == $_POST[ $key ]) {
815
                        $_REQUEST[ $key ] = $sanitized_val ;
816
                    }
817
                }
818
            }
819
            foreach ($_COOKIE as $key => $val) {
820
                if( is_array( $_COOKIE[ $key ] ) ) continue ;
821
                if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) {
822
                    $this->last_error_type = 'ParentDir' ;
823
                    $this->message .= "Doubtful file specification '$val' found.\n" ;
824
                    $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
825
                    $sanitized_val = str_replace( chr(0) , '' , $val ) ;
826
                    if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
827
                    $_COOKIE[ $key ] = $HTTP_COOKIE_VARS[ $key ] = $sanitized_val ;
828
                    if ($_REQUEST[ $key ] == $_COOKIE[ $key ]) {
829
                        $_REQUEST[ $key ] = $sanitized_val ;
830
                    }
831
                }
832
            }*/
833
834
        return true;
835
    }
836
837
    /**
838
     * @param $current
839
     * @param $indexes
840
     *
841
     * @return bool
842
     */
843
    public function &get_ref_from_base64index(&$current, $indexes)
844
    {
845
        foreach ($indexes as $index) {
846
            $index = base64_decode($index);
847
            if (!is_array($current)) {
848
                return false;
849
            }
850
            $current =& $current[$index];
851
        }
852
853
        return $current;
854
    }
855
856
    /**
857
     * @param $key
858
     * @param $val
859
     */
860
    public function replace_doubtful($key, $val)
861
    {
862
        global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
863
864
        $index_expression = '';
0 ignored issues
show
Unused Code introduced by
$index_expression is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
865
        $indexes          = explode('_', $key);
866
        $base_array       = array_shift($indexes);
867
868
        switch ($base_array) {
869
            case 'G' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
870
                $main_ref   =& $this->get_ref_from_base64index($_GET, $indexes);
871
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_GET_VARS, $indexes);
872
                break;
873
            case 'P' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
874
                $main_ref   =& $this->get_ref_from_base64index($_POST, $indexes);
875
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_POST_VARS, $indexes);
876
                break;
877
            case 'C' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
878
                $main_ref   =& $this->get_ref_from_base64index($_COOKIE, $indexes);
879
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_COOKIE_VARS, $indexes);
880
                break;
881
            default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
882
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method replace_doubtful() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
883
        }
884
        if (!isset($main_ref)) {
885
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method replace_doubtful() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
886
        }
887
        $request_ref =& $this->get_ref_from_base64index($_REQUEST, $indexes);
888
        if ($request_ref !== false && $main_ref == $request_ref) {
889
            $request_ref = $val;
890
        }
891
        $main_ref   = $val;
892
        $legacy_ref = $val;
893
    }
894
895
    /**
896
     * @return bool
897
     */
898
    public function check_uploaded_files()
899
    {
900
        if ($this->_done_badext) {
901
            return $this->_safe_badext;
902
        } else {
903
            $this->_done_badext = true;
904
        }
905
906
        // extensions never uploaded
907
        $bad_extensions = array('php', 'phtml', 'phtm', 'php3', 'php4', 'cgi', 'pl', 'asp');
908
        // extensions needed image check (anti-IE Content-Type XSS)
909
        $image_extensions = array(
910
            1  => 'gif',
911
            2  => 'jpg',
912
            3  => 'png',
913
            4  => 'swf',
914
            5  => 'psd',
915
            6  => 'bmp',
916
            7  => 'tif',
917
            8  => 'tif',
918
            9  => 'jpc',
919
            10 => 'jp2',
920
            11 => 'jpx',
921
            12 => 'jb2',
922
            13 => 'swc',
923
            14 => 'iff',
924
            15 => 'wbmp',
925
            16 => 'xbm');
926
927
        foreach ($_FILES as $_file) {
928
            if (!empty($_file['error'])) {
929
                continue;
930
            }
931
            if (!empty($_file['name']) && is_string($_file['name'])) {
932
                $ext = strtolower(substr(strrchr($_file['name'], '.'), 1));
933
                if ($ext === 'jpeg') {
934
                    $ext = 'jpg';
935
                } elseif ($ext === 'tiff') {
936
                    $ext = 'tif';
937
                }
938
939
                // anti multiple dot file (Apache mod_mime.c)
940
                if (count(explode('.', str_replace('.tar.gz', '.tgz', $_file['name']))) > 2) {
941
                    $this->message .= "Attempt to multiple dot file {$_file['name']}.\n";
942
                    $this->_safe_badext    = false;
943
                    $this->last_error_type = 'UPLOAD';
944
                }
945
946
                // anti dangerous extensions
947 View Code Duplication
                if (in_array($ext, $bad_extensions)) {
948
                    $this->message .= "Attempt to upload {$_file['name']}.\n";
949
                    $this->_safe_badext    = false;
950
                    $this->last_error_type = 'UPLOAD';
951
                }
952
953
                // anti camouflaged image file
954
                if (in_array($ext, $image_extensions)) {
955
                    $image_attributes = @getimagesize($_file['tmp_name']);
956
                    if ($image_attributes === false && is_uploaded_file($_file['tmp_name'])) {
957
                        // open_basedir restriction
958
                        $temp_file = XOOPS_ROOT_PATH . '/uploads/protector_upload_temporary' . md5(time());
959
                        move_uploaded_file($_file['tmp_name'], $temp_file);
960
                        $image_attributes = @getimagesize($temp_file);
961
                        @unlink($temp_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
962
                    }
963
964
                    if ($image_attributes === false || $image_extensions[(int)$image_attributes[2]] != $ext) {
965
                        $this->message .= "Attempt to upload camouflaged image file {$_file['name']}.\n";
966
                        $this->_safe_badext    = false;
967
                        $this->last_error_type = 'UPLOAD';
968
                    }
969
                }
970
            }
971
        }
972
973
        return $this->_safe_badext;
974
    }
975
976
    /**
977
     * @return bool
978
     */
979
    public function check_contami_systemglobals()
980
    {
981
        /*    if( $this->_done_contami ) return $this->_safe_contami ;
982
    else $this->_done_contami = true ; */
983
984
        /*    foreach ($this->_bad_globals as $bad_global) {
985
                if ( isset( $_REQUEST[ $bad_global ] ) ) {
986
                    $this->message .= "Attempt to inject '$bad_global' was found.\n" ;
987
                    $this->_safe_contami = false ;
988
                    $this->last_error_type = 'CONTAMI' ;
989
                }
990
            }*/
991
992
        return $this->_safe_contami;
993
    }
994
995
    /**
996
     * @param bool $sanitize
997
     *
998
     * @return bool
999
     */
1000
    public function check_sql_isolatedcommentin($sanitize = true)
1001
    {
1002
        if ($this->_done_isocom) {
1003
            return $this->_safe_isocom;
1004
        } else {
1005
            $this->_done_isocom = true;
1006
        }
1007
1008
        foreach ($this->_doubtful_requests as $key => $val) {
1009
            $str = $val;
1010
            while ($str = strstr($str, '/*')) { /* */
1011
                $str = strstr(substr($str, 2), '*/');
1012
                if ($str === false) {
1013
                    $this->message .= "Isolated comment-in found. ($val)\n";
1014
                    if ($sanitize) {
1015
                        $this->replace_doubtful($key, $val . '*/');
1016
                    }
1017
                    $this->_safe_isocom    = false;
1018
                    $this->last_error_type = 'ISOCOM';
1019
                }
1020
            }
1021
        }
1022
1023
        return $this->_safe_isocom;
1024
    }
1025
1026
    /**
1027
     * @param bool $sanitize
1028
     *
1029
     * @return bool
1030
     */
1031
    public function check_sql_union($sanitize = true)
1032
    {
1033
        if ($this->_done_union) {
1034
            return $this->_safe_union;
1035
        } else {
1036
            $this->_done_union = true;
1037
        }
1038
1039
        foreach ($this->_doubtful_requests as $key => $val) {
1040
            $str = str_replace(array('/*', '*/'), '', preg_replace('?/\*.+\*/?sU', '', $val));
1041
            if (preg_match('/\sUNION\s+(ALL|SELECT)/i', $str)) {
1042
                $this->message .= "Pattern like SQL injection found. ($val)\n";
1043
                if ($sanitize) {
1044
                    //                    $this->replace_doubtful($key, preg_replace('/union/i', 'uni-on', $val));
1045
                    $this->replace_doubtful($key, str_ireplace('union', 'uni-on', $val));
1046
                }
1047
                $this->_safe_union     = false;
1048
                $this->last_error_type = 'UNION';
1049
            }
1050
        }
1051
1052
        return $this->_safe_union;
1053
    }
1054
1055
    /**
1056
     * @param $uid
1057
     *
1058
     * @return bool
1059
     */
1060
    public function stopforumspam($uid)
1061
    {
1062
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
1063
            return false;
1064
        }
1065
1066
        $result = $this->stopForumSpamLookup(
1067
            isset($_POST['email']) ? $_POST['email'] : null,
1068
            $_SERVER['REMOTE_ADDR'],
1069
            isset($_POST['uname']) ? $_POST['uname'] : null
1070
        );
1071
1072
        if (false === $result || is_set($result['http_code'])) {
1073
            return false;
1074
        }
1075
1076
        $spammer = false;
1077
        if (isset($result['email']) && isset($result['email']['lastseen'])) {
1078
            $spammer = true;
1079
        }
1080
1081
        if (isset($result['ip']) && isset($result['ip']['lastseen'])) {
1082
            $last        = strtotime($result['ip']['lastseen']);
1083
            $oneMonth    = 60 * 60 * 24 * 31;
1084
            $oneMonthAgo = time() - $oneMonth;
1085
            if ($last > $oneMonthAgo) {
1086
                $spammer = true;
1087
            }
1088
        }
1089
1090
        if (!$spammer) {
1091
            return false;
1092
        }
1093
1094
        $this->last_error_type = 'SPAMMER POST';
1095
1096
        switch ($this->_conf['stopforumspam_action']) {
1097
            default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1098
            case 'log' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1099
                break;
1100
            case 'san' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1101
                $_POST = array();
1102
                $this->message .= 'POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
1103
                break;
1104
            case 'biptime0' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1105
                $_POST = array();
1106
                $this->message .= 'BAN and POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
1107
                $this->_should_be_banned_time0 = true;
1108
                break;
1109
            case 'bip' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1110
                $_POST = array();
1111
                $this->message .= 'Ban and POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
1112
                $this->_should_be_banned = true;
1113
                break;
1114
        }
1115
1116
        $this->output_log($this->last_error_type, $uid, false, 16);
1117
1118
        return true;
1119
    }
1120
1121
    public function stopForumSpamLookup($email, $ip, $username)
1122
    {
1123
        if (!function_exists('curl_init')) {
1124
            return false;
1125
        }
1126
1127
        $query = '';
1128
        $query .= (empty($ip)) ? '' : '&ip=' . $ip;
1129
        $query .= (empty($email)) ? '' : '&email=' . $email;
1130
        $query .= (empty($username)) ? '' : '&username=' . $username;
1131
1132
        if (empty($query)) {
1133
            return false;
1134
        }
1135
1136
        $url = 'http://www.stopforumspam.com/api?f=json' . $query;
1137
        $ch  = curl_init();
1138
        curl_setopt($ch, CURLOPT_URL, $url);
1139
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1140
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
1141
        $result = curl_exec($ch);
1142
        if (false === $result) {
1143
            $result = curl_getinfo($ch);
1144
        } else {
1145
            $result = json_decode(curl_exec($ch), true);
1146
        }
1147
        curl_close($ch);
1148
1149
        return $result;
1150
    }
1151
1152
    /**
1153
     * @param int  $uid
1154
     * @param bool $can_ban
1155
     *
1156
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1157
     */
1158
    public function check_dos_attack($uid = 0, $can_ban = false)
1159
    {
1160
        global $xoopsDB;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1161
1162
        if ($this->_done_dos) {
1163
            return true;
1164
        }
1165
1166
        $ip      = \Xmf\IPAddress::fromRequest();
1167
        if (false === $ip->asReadable()) {
1168
            return true;
1169
        }
1170
        $uri     = @$_SERVER['REQUEST_URI'];
1171
1172
        // skip ajax fine uploader traffic
1173
        $parts = parse_url($uri);
1174
        $basename = empty($parts['path']) ? '' : basename($parts['path']);
1175
        if (('ajaxfineupload.php' === $basename) && ('' !== \Xmf\Request::getHeader('Authorization', ''))) {
1176
            return true;
1177
        }
1178
1179
        $ip4sql  = $xoopsDB->quote($ip->asReadable());
1180
        $uri4sql = $xoopsDB->quote($uri);
1181
1182
        // gargage collection
1183
        $result = $xoopsDB->queryF(
1184
            'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1185
            . ' WHERE expire < UNIX_TIMESTAMP()'
1186
        );
1187
1188
        // for older versions before updating this module
1189
        if ($result === false) {
1190
            $this->_done_dos = true;
1191
1192
            return true;
1193
        }
1194
1195
        // sql for recording access log (INSERT should be placed after SELECT)
1196
        $sql4insertlog = 'INSERT INTO ' . $xoopsDB->prefix($this->mydirname . '_access')
1197
                         . " SET ip={$ip4sql}, request_uri={$uri4sql},"
1198
                         . " expire=UNIX_TIMESTAMP()+'" . (int)$this->_conf['dos_expire'] . "'";
1199
1200
        // bandwidth limitation
1201
        if (@$this->_conf['bwlimit_count'] >= 10) {
1202
            $result = $xoopsDB->query('SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access'));
1203
            list($bw_count) = $xoopsDB->fetchRow($result);
1204
            if ($bw_count > $this->_conf['bwlimit_count']) {
1205
                $this->write_file_bwlimit(time() + $this->_conf['dos_expire']);
1206
            }
1207
        }
1208
1209
        // F5 attack check (High load & same URI)
1210
        $result = $xoopsDB->query(
1211
            'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1212
            . " WHERE ip={$ip4sql} AND request_uri={$uri4sql}");
1213
        list($f5_count) = $xoopsDB->fetchRow($result);
1214 View Code Duplication
        if ($f5_count > $this->_conf['dos_f5count']) {
1215
1216
            // delayed insert
1217
            $xoopsDB->queryF($sql4insertlog);
1218
1219
            // extends the expires of the IP with 5 minutes at least (pending)
1220
            // $result = $xoopsDB->queryF( "UPDATE ".$xoopsDB->prefix($this->mydirname.'_access')." SET expire=UNIX_TIMESTAMP()+300 WHERE ip='$ip4sql' AND expire<UNIX_TIMESTAMP()+300" ) ;
1221
1222
            // call the filter first
1223
            $ret = $this->call_filter('f5attack_overrun');
0 ignored issues
show
Unused Code introduced by
$ret is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1224
1225
            // actions for F5 Attack
1226
            $this->_done_dos       = true;
1227
            $this->last_error_type = 'DoS';
1228
            switch ($this->_conf['dos_f5action']) {
1229
                default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1230
                case 'exit' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1231
                    $this->output_log($this->last_error_type, $uid, true, 16);
1232
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method check_dos_attack() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1233
                case 'none' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1234
                    $this->output_log($this->last_error_type, $uid, true, 16);
1235
1236
                    return true;
1237
                case 'biptime0' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1238
                    if ($can_ban) {
1239
                        $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1240
                    }
1241
                    break;
1242
                case 'bip' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1243
                    if ($can_ban) {
1244
                        $this->register_bad_ips();
1245
                    }
1246
                    break;
1247
                case 'hta' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1248
                    if ($can_ban) {
1249
                        $this->deny_by_htaccess();
1250
                    }
1251
                    break;
1252
                case 'sleep' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1253
                    sleep(5);
1254
                    break;
1255
            }
1256
1257
            return false;
1258
        }
1259
1260
        // Check its Agent
1261
        if (trim($this->_conf['dos_crsafe']) != '' && preg_match($this->_conf['dos_crsafe'], @$_SERVER['HTTP_USER_AGENT'])) {
1262
            // welcomed crawler
1263
            $this->_done_dos = true;
1264
1265
            return true;
1266
        }
1267
1268
        // Crawler check (High load & different URI)
1269
        $result = $xoopsDB->query(
1270
            'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . " WHERE ip={$ip4sql}"
1271
        );
1272
        list($crawler_count) = $xoopsDB->fetchRow($result);
1273
1274
        // delayed insert
1275
        $xoopsDB->queryF($sql4insertlog);
1276
1277 View Code Duplication
        if ($crawler_count > $this->_conf['dos_crcount']) {
1278
1279
            // call the filter first
1280
            $ret = $this->call_filter('crawler_overrun');
0 ignored issues
show
Unused Code introduced by
$ret is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1281
1282
            // actions for bad Crawler
1283
            $this->_done_dos       = true;
1284
            $this->last_error_type = 'CRAWLER';
1285
            switch ($this->_conf['dos_craction']) {
1286
                default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1287
                case 'exit' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1288
                    $this->output_log($this->last_error_type, $uid, true, 16);
1289
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method check_dos_attack() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1290
                case 'none' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1291
                    $this->output_log($this->last_error_type, $uid, true, 16);
1292
1293
                    return true;
1294
                case 'biptime0' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1295
                    if ($can_ban) {
1296
                        $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1297
                    }
1298
                    break;
1299
                case 'bip' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1300
                    if ($can_ban) {
1301
                        $this->register_bad_ips();
1302
                    }
1303
                    break;
1304
                case 'hta' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1305
                    if ($can_ban) {
1306
                        $this->deny_by_htaccess();
1307
                    }
1308
                    break;
1309
                case 'sleep' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1310
                    sleep(5);
1311
                    break;
1312
            }
1313
1314
            return false;
1315
        }
1316
1317
        return true;
1318
    }
1319
1320
    //
1321
    /**
1322
     * @return bool|null
1323
     */
1324
    public function check_brute_force()
1325
    {
1326
        global $xoopsDB;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1327
1328
        $ip      = \Xmf\IPAddress::fromRequest();
1329
        if (false === $ip->asReadable()) {
1330
            return true;
1331
        }
1332
        $uri     = @$_SERVER['REQUEST_URI'];
1333
        $ip4sql  = $xoopsDB->quote($ip->asReadable());
1334
        $uri4sql = $xoopsDB->quote($uri);
1335
1336
        $victim_uname = empty($_COOKIE['autologin_uname']) ? $_POST['uname'] : $_COOKIE['autologin_uname'];
1337
        // some UA send 'deleted' as a value of the deleted cookie.
1338
        if ($victim_uname === 'deleted') {
1339
            return null;
1340
        }
1341
        $mal4sql = $xoopsDB->quote("BRUTE FORCE: $victim_uname");
1342
1343
        // gargage collection
1344
        $result = $xoopsDB->queryF(
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1345
            'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . ' WHERE expire < UNIX_TIMESTAMP()'
1346
        );
1347
1348
        // sql for recording access log (INSERT should be placed after SELECT)
1349
        $sql4insertlog = 'INSERT INTO ' . $xoopsDB->prefix($this->mydirname . '_access')
1350
                         . " SET ip={$ip4sql}, request_uri={$uri4sql}, malicious_actions={$mal4sql}, expire=UNIX_TIMESTAMP()+600";
1351
1352
        // count check
1353
        $result = $xoopsDB->query(
1354
            'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1355
            . " WHERE ip={$ip4sql} AND malicious_actions like 'BRUTE FORCE:%'"
1356
        );
1357
        list($bf_count) = $xoopsDB->fetchRow($result);
1358
        if ($bf_count > $this->_conf['bf_count']) {
1359
            $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1360
            $this->last_error_type = 'BruteForce';
1361
            $this->message .= "Trying to login as '" . addslashes($victim_uname) . "' found.\n";
1362
            $this->output_log('BRUTE FORCE', 0, true, 1);
1363
            $ret = $this->call_filter('bruteforce_overrun');
1364
            if ($ret == false) {
1365
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method check_brute_force() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1366
            }
1367
        }
1368
        // delayed insert
1369
        $xoopsDB->queryF($sql4insertlog);
1370
        return null;
1371
    }
1372
1373
    /**
1374
     * @param $val
1375
     */
1376
    protected function _spam_check_point_recursive($val)
1377
    {
1378
        if (is_array($val)) {
1379
            foreach ($val as $subval) {
1380
                $this->_spam_check_point_recursive($subval);
1381
            }
1382
        } else {
1383
            // http_host
1384
            $path_array = parse_url(XOOPS_URL);
1385
            $http_host  = empty($path_array['host']) ? 'www.xoops.org' : $path_array['host'];
1386
1387
            // count URI up
1388
            $count = -1;
1389
            foreach (preg_split('#https?\:\/\/#i', $val) as $fragment) {
1390
                if (strncmp($fragment, $http_host, strlen($http_host)) !== 0) {
1391
                    ++$count;
1392
                }
1393
            }
1394
            if ($count > 0) {
1395
                $this->_spamcount_uri += $count;
1396
            }
1397
1398
            // count BBCode likd [url=www....] up (without [url=http://...])
1399
            $this->_spamcount_uri += count(preg_split('/\[url=(?!http|\\"http|\\\'http|' . $http_host . ')/i', $val)) - 1;
1400
        }
1401
    }
1402
1403
    /**
1404
     * @param $points4deny
1405
     * @param $uid
1406
     */
1407
    public function spam_check($points4deny, $uid)
1408
    {
1409
        $this->_spamcount_uri = 0;
1410
        $this->_spam_check_point_recursive($_POST);
1411
1412
        if ($this->_spamcount_uri >= $points4deny) {
1413
            $this->message .= @$_SERVER['REQUEST_URI'] . " SPAM POINT: $this->_spamcount_uri\n";
1414
            $this->output_log('URI SPAM', $uid, false, 128);
1415
            $ret = $this->call_filter('spamcheck_overrun');
1416
            if ($ret == false) {
1417
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method spam_check() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1418
            }
1419
        }
1420
    }
1421
1422
    public function disable_features()
1423
    {
1424
        global $HTTP_POST_VARS, $HTTP_GET_VARS, $HTTP_COOKIE_VARS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1425
1426
        // disable "Notice: Undefined index: ..."
1427
        $error_reporting_level = error_reporting(0);
1428
1429
        //
1430
        // bit 1 : disable XMLRPC , criteria bug
1431
        //
1432
        if ($this->_conf['disable_features'] & 1) {
1433
1434
            // zx 2005/1/5 disable xmlrpc.php in root
1435
            if (/* ! stristr( $_SERVER['SCRIPT_NAME'] , 'modules' ) && */
1436
                substr(@$_SERVER['SCRIPT_NAME'], -10) === 'xmlrpc.php'
1437
            ) {
1438
                $this->output_log('xmlrpc', 0, true, 1);
1439
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1440
            }
1441
1442
            // security bug of class/criteria.php 2005/6/27
1443
            if ($_POST['uname'] === '0' || $_COOKIE['autologin_pass'] === '0') {
1444
                $this->output_log('CRITERIA');
1445
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1446
            }
1447
        }
1448
1449
        //
1450
        // bit 11 : XSS+CSRFs in XOOPS < 2.0.10
1451
        //
1452
        if ($this->_conf['disable_features'] & 1024) {
1453
1454
            // root controllers
1455
            if (false === stripos(@$_SERVER['SCRIPT_NAME'], 'modules')) {
1456
                // zx 2004/12/13 misc.php debug (file check)
1457 View Code Duplication
                if (substr(@$_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ($_GET['type'] === 'debug' || $_POST['type'] === 'debug') && !preg_match('/^dummy_\d+\.html$/', $_GET['file'])) {
1458
                    $this->output_log('misc debug');
1459
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1460
                }
1461
1462
                // zx 2004/12/13 misc.php smilies
1463 View Code Duplication
                if (substr(@$_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ($_GET['type'] === 'smilies' || $_POST['type'] === 'smilies') && !preg_match('/^[0-9a-z_]*$/i', $_GET['target'])) {
1464
                    $this->output_log('misc smilies');
1465
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1466
                }
1467
1468
                // zx 2005/1/5 edituser.php avatarchoose
1469
                if (substr(@$_SERVER['SCRIPT_NAME'], -12) === 'edituser.php' && $_POST['op'] === 'avatarchoose' && false !== strpos($_POST['user_avatar'], '..')) {
1470
                    $this->output_log('edituser avatarchoose');
1471
                    exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1472
                }
1473
            }
1474
1475
            // zx 2005/1/4 findusers
1476
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'findusers' || $_POST['fct'] === 'findusers')) {
1477
                foreach ($_POST as $key => $val) {
1478
                    if (false !== strpos($key, "'") || false !== strpos($val, "'")) {
1479
                        $this->output_log('findusers');
1480
                        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1481
                    }
1482
                }
1483
            }
1484
1485
            // preview CSRF zx 2004/12/14
1486
            // news submit.php
1487
            if (substr(@$_SERVER['SCRIPT_NAME'], -23) === 'modules/news/submit.php' && isset($_POST['preview']) && strpos(@$_SERVER['HTTP_REFERER'], XOOPS_URL . '/modules/news/submit.php') !== 0) {
1488
                $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
1489
            }
1490
            // news admin/index.php
1491
            if (substr(@$_SERVER['SCRIPT_NAME'], -28) === 'modules/news/admin/index.php' && ($_POST['op'] === 'preview' || $_GET['op'] === 'preview') && strpos(@$_SERVER['HTTP_REFERER'], XOOPS_URL . '/modules/news/admin/index.php') !== 0) {
1492
                $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
1493
            }
1494
            // comment comment_post.php
1495
            if (isset($_POST['com_dopreview']) && false === strpos(substr(@$_SERVER['HTTP_REFERER'], -16), 'comment_post.php')) {
1496
                $HTTP_POST_VARS['dohtml'] = $_POST['dohtml'] = 0;
1497
            }
1498
            // disable preview of system's blocksadmin
1499
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'blocksadmin' || $_POST['fct'] === 'blocksadmin') && isset($_POST['previewblock']) /* && strpos( $_SERVER['HTTP_REFERER'] , XOOPS_URL.'/modules/system/admin.php' ) !== 0 */) {
1500
                die("Danger! don't use this preview. Use 'altsys module' instead.(by Protector)");
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1501
            }
1502
            // tpl preview
1503
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'tplsets' || $_POST['fct'] === 'tplsets')) {
1504
                if ($_POST['op'] === 'previewpopup' || $_GET['op'] === 'previewpopup' || isset($_POST['previewtpl'])) {
1505
                    die("Danger! don't use this preview.(by Protector)");
0 ignored issues
show
Coding Style Compatibility introduced by
The method disable_features() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1506
                }
1507
            }
1508
        }
1509
1510
        // restore reporting level
1511
        error_reporting($error_reporting_level);
1512
    }
1513
1514
    /**
1515
     * @param        $type
1516
     * @param string $dying_message
1517
     *
1518
     * @return int|mixed
1519
     */
1520
    public function call_filter($type, $dying_message = '')
1521
    {
1522
        require_once __DIR__ . '/ProtectorFilter.php';
1523
        $filter_handler = ProtectorFilterHandler::getInstance();
1524
        $ret            = $filter_handler->execute($type);
1525
        if ($ret == false && $dying_message) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $ret of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
1526
            die($dying_message);
0 ignored issues
show
Coding Style Compatibility introduced by
The method call_filter() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1527
        }
1528
1529
        return $ret;
1530
    }
1531
}
1532