Test Failed
Push — master ( 398493...d4ef72 )
by Michael
11:04
created

Protector::write_file_badips()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 10
nc 2
nop 1
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
                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) {
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type true; however, parameter $result of mysqli_num_rows() does only seem to accept mysqli_result, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

173
        if (!$result || mysqli_num_rows(/** @scrutinizer ignore-type */ $result) < 5) {
Loading history...
174
            return false;
175
        }
176
        $db_conf = array();
177
        while (list($key, $val) = mysqli_fetch_row($result)) {
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type true; however, parameter $result of mysqli_fetch_row() does only seem to accept mysqli_result, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

177
        while (list($key, $val) = mysqli_fetch_row(/** @scrutinizer ignore-type */ $result)) {
Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

185
            fwrite(/** @scrutinizer ignore-type */ $fp, $db_conf_serialized);
Loading history...
186
            fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

186
            fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
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;
219
        } else {
220
            $ret = $this->call_filter('prepurge_exit');
221
            if ($ret == false) {
222
                die('Protector detects attacking actions');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

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
            $domain =  defined(XOOPS_COOKIE_DOMAIN) ? XOOPS_COOKIE_DOMAIN : '';
244
            $past = time() - 3600;
245
            foreach ($_COOKIE as $key => $value) {
246
                setcookie($key, '', $past, '', $domain);
247
                setcookie($key, '', $past, '/', $domain);
248
            }
249
        }
250
    }
251
252
    public function purgeNoExit()
253
    {
254
        $this->purgeSession();
255
        $this->purgeCookies();
256
    }
257
258
    public function deactivateCurrentUser()
259
    {
260
        /* @var $xoopsUser XoopsUser */
261
        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...
262
263
        if (is_object($xoopsUser)) {
264
            /** @var XoopsMemberHandler */
265
            $userHandler = xoops_getHandler('user');
266
            $xoopsUser->setVar('level', 0);
267
            $actkey = substr(md5(uniqid(mt_rand(), 1)), 0, 8);
268
            $xoopsUser->setVar('actkey', $actkey);
269
            $userHandler->insert($xoopsUser);
270
        }
271
        $this->purgeNoExit();
272
    }
273
274
    /**
275
     * @param string $type
276
     * @param int    $uid
277
     * @param bool   $unique_check
278
     * @param int    $level
279
     *
280
     * @return bool
281
     */
282
    public function output_log($type = 'UNKNOWN', $uid = 0, $unique_check = false, $level = 1)
283
    {
284
        if ($this->_logged) {
285
            return true;
286
        }
287
288
        if (!($this->_conf['log_level'] & $level)) {
289
            return true;
290
        }
291
292
        if (empty($this->_conn)) {
293
            $this->_conn = new mysqli(XOOPS_DB_HOST, XOOPS_DB_USER, XOOPS_DB_PASS);
294
            if (0 !== $this->_conn->connect_errno) {
0 ignored issues
show
introduced by
The condition 0 !== $this->_conn->connect_errno is always true. If 0 !== $this->_conn->connect_errno can have other possible types, add them to php/mysqli/mysqli.php:66
Loading history...
295
                die('db connection failed.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
296
            }
297
            if (!mysqli_select_db($this->_conn, XOOPS_DB_NAME)) {
298
                die('db selection failed.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
299
            }
300
        }
301
302
        $ip    = \Xmf\IPAddress::fromRequest()->asReadable();
303
        $agent = @$_SERVER['HTTP_USER_AGENT'];
304
305
        if ($unique_check) {
306
            $result = mysqli_query($this->_conn, 'SELECT ip,type FROM ' . XOOPS_DB_PREFIX . '_' . $this->mydirname . '_log ORDER BY timestamp DESC LIMIT 1');
307
            list($last_ip, $last_type) = mysqli_fetch_row($result);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type boolean; however, parameter $result of mysqli_fetch_row() does only seem to accept mysqli_result, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

307
            list($last_ip, $last_type) = mysqli_fetch_row(/** @scrutinizer ignore-type */ $result);
Loading history...
308
            if ($last_ip == $ip && $last_type == $type) {
309
                $this->_logged = true;
310
311
                return true;
312
            }
313
        }
314
315
        mysqli_query(
316
            $this->_conn,
317
            'INSERT INTO ' . XOOPS_DB_PREFIX . '_' . $this->mydirname . "_log SET ip='"
318
            . mysqli_real_escape_string($this->_conn, $ip) . "',agent='"
319
            . mysqli_real_escape_string($this->_conn, $agent) . "',type='"
320
            . mysqli_real_escape_string($this->_conn, $type) . "',description='"
321
            . mysqli_real_escape_string($this->_conn, $this->message) . "',uid='"
322
            . (int)$uid . "',timestamp=NOW()"
323
        );
324
        $this->_logged = true;
325
326
        return true;
327
    }
328
329
    /**
330
     * @param $expire
331
     *
332
     * @return bool
333
     */
334
    public function write_file_bwlimit($expire)
335
    {
336
        $expire = min((int)$expire, time() + 300);
337
338
        $fp = @fopen($this->get_filepath4bwlimit(), 'w');
339
        if ($fp) {
0 ignored issues
show
introduced by
$fp is of type resource|false, thus it always evaluated to false.
Loading history...
340
            @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for flock(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

340
            /** @scrutinizer ignore-unhandled */ @flock($fp, LOCK_EX);

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...
341
            fwrite($fp, $expire . "\n");
342
            @flock($fp, LOCK_UN);
343
            fclose($fp);
344
345
            return true;
346
        } else {
347
            return false;
348
        }
349
    }
350
351
    /**
352
     * @return mixed
353
     */
354
    public function get_bwlimit()
355
    {
356
        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...
357
        $expire = min((int)$expire, time() + 300);
358
359
        return $expire;
360
    }
361
362
    /**
363
     * @return string
364
     */
365
    public static function get_filepath4bwlimit()
366
    {
367
        return XOOPS_VAR_PATH . '/protector/bwlimit' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
368
    }
369
370
    /**
371
     * @param $bad_ips
372
     *
373
     * @return bool
374
     */
375
    public function write_file_badips($bad_ips)
376
    {
377
        asort($bad_ips);
378
379
        $fp = @fopen($this->get_filepath4badips(), 'w');
380
        if ($fp) {
0 ignored issues
show
introduced by
$fp is of type resource|false, thus it always evaluated to false.
Loading history...
381
            @flock($fp, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for flock(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

381
            /** @scrutinizer ignore-unhandled */ @flock($fp, LOCK_EX);

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...
382
            fwrite($fp, serialize($bad_ips) . "\n");
383
            @flock($fp, LOCK_UN);
384
            fclose($fp);
385
386
            return true;
387
        } else {
388
            return false;
389
        }
390
    }
391
392
    /**
393
     * @param int  $jailed_time
394
     * @param null|string|false $ip
395
     *
396
     * @return bool
397
     */
398
    public function register_bad_ips($jailed_time = 0, $ip = null)
399
    {
400
        if (empty($ip)) {
401
            $ip = \Xmf\IPAddress::fromRequest()->asReadable();
402
        }
403
        if (empty($ip)) {
404
            return false;
405
        }
406
407
        $bad_ips      = $this->get_bad_ips(true);
408
        $bad_ips[$ip] = $jailed_time ?: 0x7fffffff;
409
410
        return $this->write_file_badips($bad_ips);
411
    }
412
413
    /**
414
     * @param bool $with_jailed_time
415
     *
416
     * @return array|mixed
417
     */
418
    public function get_bad_ips($with_jailed_time = false)
419
    {
420
        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...
421
        $bad_ips = empty($bad_ips_serialized) ? array() : @unserialize($bad_ips_serialized);
422
        if (!is_array($bad_ips) || isset($bad_ips[0])) {
423
            $bad_ips = array();
424
        }
425
426
        // expire jailed_time
427
        $pos = 0;
428
        foreach ($bad_ips as $bad_ip => $jailed_time) {
429
            if ($jailed_time >= time()) {
430
                break;
431
            }
432
            ++$pos;
433
        }
434
        $bad_ips = array_slice($bad_ips, $pos);
435
436
        if ($with_jailed_time) {
437
            return $bad_ips;
438
        } else {
439
            return array_keys($bad_ips);
440
        }
441
    }
442
443
    /**
444
     * @return string
445
     */
446
    public static function get_filepath4badips()
447
    {
448
        return XOOPS_VAR_PATH . '/protector/badips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
449
    }
450
451
    /**
452
     * @param bool $with_info
453
     *
454
     * @return array|mixed
455
     */
456
    public function get_group1_ips($with_info = false)
457
    {
458
        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...
459
        $group1_ips = empty($group1_ips_serialized) ? array() : @unserialize($group1_ips_serialized);
460
        if (!is_array($group1_ips)) {
461
            $group1_ips = array();
462
        }
463
464
        if ($with_info) {
465
            $group1_ips = array_flip($group1_ips);
466
        }
467
468
        return $group1_ips;
469
    }
470
471
    /**
472
     * @return string
473
     */
474
    public static function get_filepath4group1ips()
475
    {
476
        return XOOPS_VAR_PATH . '/protector/group1ips' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
477
    }
478
479
    /**
480
     * @return string
481
     */
482
    public function get_filepath4confighcache()
483
    {
484
        return XOOPS_VAR_PATH . '/protector/configcache' . substr(md5(XOOPS_ROOT_PATH . XOOPS_DB_USER . XOOPS_DB_PREFIX), 0, 6);
485
    }
486
487
    /**
488
     * @param $ips
489
     *
490
     * @return bool
491
     */
492
    public function ip_match($ips)
493
    {
494
        $requestIp = \Xmf\IPAddress::fromRequest()->asReadable();
495
        if (false === $requestIp) { // nothing to match
0 ignored issues
show
introduced by
The condition false === $requestIp is always false.
Loading history...
496
            $this->ip_matched_info = null;
497
            return false;
498
        }
499
        foreach ($ips as $ip => $info) {
500
            if ($ip) {
501
                switch (strtolower(substr($ip, -1))) {
502
                    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...
503
                    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...
504
                        // foward match
505
                        if (substr($requestIp, 0, strlen($ip)) == $ip) {
506
                            $this->ip_matched_info = $info;
507
                            return true;
508
                        }
509
                        break;
510
                    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...
511
                    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...
512
                    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...
513
                    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...
514
                    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...
515
                    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...
516
                    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...
517
                    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...
518
                    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...
519
                    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...
520
                    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...
521
                    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...
522
                    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...
523
                    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...
524
                    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...
525
                    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...
526
                        // full match
527
                        if ($requestIp == $ip) {
528
                            $this->ip_matched_info = $info;
529
                            return true;
530
                        }
531
                        break;
532
                    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...
533
                        // perl regex
534
                        if (@preg_match($ip, $requestIp)) {
535
                            $this->ip_matched_info = $info;
536
                            return true;
537
                        }
538
                        break;
539
                }
540
            }
541
        }
542
        $this->ip_matched_info = null;
543
        return false;
544
    }
545
546
    /**
547
     * @param null|string|false $ip
548
     *
549
     * @return bool
550
     */
551
    public function deny_by_htaccess($ip = null)
552
    {
553
        if (empty($ip)) {
554
            $ip = \Xmf\IPAddress::fromRequest()->asReadable();
555
        }
556
        if (empty($ip)) {
557
            return false;
558
        }
559
        if (!function_exists('file_get_contents')) {
560
            return false;
561
        }
562
563
        $target_htaccess = XOOPS_ROOT_PATH . '/.htaccess';
564
        $backup_htaccess = XOOPS_ROOT_PATH . '/uploads/.htaccess.bak';
565
566
        $ht_body = file_get_contents($target_htaccess);
567
568
        // make backup as uploads/.htaccess.bak automatically
569
        if ($ht_body && !file_exists($backup_htaccess)) {
570
            $fw = fopen($backup_htaccess, 'w');
571
            fwrite($fw, $ht_body);
0 ignored issues
show
Bug introduced by
It seems like $fw can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

571
            fwrite(/** @scrutinizer ignore-type */ $fw, $ht_body);
Loading history...
572
            fclose($fw);
0 ignored issues
show
Bug introduced by
It seems like $fw can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

572
            fclose(/** @scrutinizer ignore-type */ $fw);
Loading history...
573
        }
574
575
        // if .htaccess is broken, restore from backup
576
        if (!$ht_body && file_exists($backup_htaccess)) {
577
            $ht_body = file_get_contents($backup_htaccess);
578
        }
579
580
        // new .htaccess
581
        if ($ht_body === false) {
582
            $ht_body = '';
583
        }
584
585
        if (preg_match("/^(.*)#PROTECTOR#\s+(DENY FROM .*)\n#PROTECTOR#\n(.*)$/si", $ht_body, $regs)) {
586
            if (substr($regs[2], -strlen($ip)) == $ip) {
587
                return true;
588
            }
589
            $new_ht_body = $regs[1] . "#PROTECTOR#\n" . $regs[2] . " $ip\n#PROTECTOR#\n" . $regs[3];
590
        } else {
591
            $new_ht_body = "#PROTECTOR#\nDENY FROM $ip\n#PROTECTOR#\n" . $ht_body;
592
        }
593
594
        // error_log( "$new_ht_body\n" , 3 , "/tmp/error_log" ) ;
595
596
        $fw = fopen($target_htaccess, 'w');
597
        @flock($fw, LOCK_EX);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for flock(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

597
        /** @scrutinizer ignore-unhandled */ @flock($fw, LOCK_EX);

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...
Bug introduced by
It seems like $fw can also be of type false; however, parameter $handle of flock() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

597
        @flock(/** @scrutinizer ignore-type */ $fw, LOCK_EX);
Loading history...
598
        fwrite($fw, $new_ht_body);
599
        @flock($fw, LOCK_UN);
600
        fclose($fw);
601
602
        return true;
603
    }
604
605
    /**
606
     * @return array
607
     */
608
    public function getDblayertrapDoubtfuls()
609
    {
610
        return $this->_dblayertrap_doubtfuls;
611
    }
612
613
    /**
614
     * @param $val
615
     * @return null
616
     */
617
    protected function _dblayertrap_check_recursive($val)
618
    {
619
        if (is_array($val)) {
620
            foreach ($val as $subval) {
621
                $this->_dblayertrap_check_recursive($subval);
622
            }
623
        } else {
624
            if (strlen($val) < 6) {
625
                return null;
626
            }
627
            $val = get_magic_quotes_gpc() ? stripslashes($val) : $val;
628
            foreach ($this->_dblayertrap_doubtful_needles as $needle) {
629
                if (false !== stripos($val, $needle)) {
630
                    $this->_dblayertrap_doubtfuls[] = $val;
631
                }
632
            }
633
        }
634
    }
635
636
    /**
637
     * @param  bool $force_override
638
     * @return null
639
     */
640
    public function dblayertrap_init($force_override = false)
641
    {
642
        if (!empty($GLOBALS['xoopsOption']['nocommon']) || defined('_LEGACY_PREVENT_EXEC_COMMON_') || defined('_LEGACY_PREVENT_LOAD_CORE_')) {
643
            return null;
644
        } // skip
645
646
        $this->_dblayertrap_doubtfuls = array();
647
        $this->_dblayertrap_check_recursive($_GET);
648
        $this->_dblayertrap_check_recursive($_POST);
649
        $this->_dblayertrap_check_recursive($_COOKIE);
650
        if (empty($this->_conf['dblayertrap_wo_server'])) {
651
            $this->_dblayertrap_check_recursive($_SERVER);
652
        }
653
654
        if (!empty($this->_dblayertrap_doubtfuls) || $force_override) {
655
            @define('XOOPS_DB_ALTERNATIVE', 'ProtectorMysqlDatabase');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for define(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

655
            /** @scrutinizer ignore-unhandled */ @define('XOOPS_DB_ALTERNATIVE', 'ProtectorMysqlDatabase');

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...
656
            require_once dirname(__DIR__) . '/class/ProtectorMysqlDatabase.class.php';
657
        }
658
    }
659
660
    /**
661
     * @param $val
662
     */
663
    protected function _bigumbrella_check_recursive($val)
664
    {
665
        if (is_array($val)) {
666
            foreach ($val as $subval) {
667
                $this->_bigumbrella_check_recursive($subval);
668
            }
669
        } else {
670
            if (preg_match('/[<\'"].{15}/s', $val, $regs)) {
671
                $this->_bigumbrella_doubtfuls[] = $regs[0];
672
            }
673
        }
674
    }
675
676
    public function bigumbrella_init()
677
    {
678
        $this->_bigumbrella_doubtfuls = array();
679
        $this->_bigumbrella_check_recursive($_GET);
680
        $this->_bigumbrella_check_recursive(@$_SERVER['PHP_SELF']);
681
682
        if (!empty($this->_bigumbrella_doubtfuls)) {
683
            ob_start(array($this, 'bigumbrella_outputcheck'));
684
        }
685
    }
686
687
    /**
688
     * @param $s
689
     *
690
     * @return string
691
     */
692
    public function bigumbrella_outputcheck($s)
693
    {
694
        if (defined('BIGUMBRELLA_DISABLED')) {
695
            return $s;
696
        }
697
698
        if (function_exists('headers_list')) {
699
            foreach (headers_list() as $header) {
700
                if (false !== stripos($header, 'Content-Type:') && false === stripos($header, 'text/html')) {
701
                    return $s;
702
                }
703
            }
704
        }
705
706
        if (!is_array($this->_bigumbrella_doubtfuls)) {
0 ignored issues
show
introduced by
The condition is_array($this->_bigumbrella_doubtfuls) is always true.
Loading history...
707
            return 'bigumbrella injection found.';
708
        }
709
710
        foreach ($this->_bigumbrella_doubtfuls as $doubtful) {
711
            if (false !== strpos($s, $doubtful)) {
712
                return 'XSS found by Protector.';
713
            }
714
        }
715
716
        return $s;
717
    }
718
719
    /**
720
     * @return bool
721
     */
722
    public function intval_allrequestsendid()
723
    {
724
        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...
725
726
        if ($this->_done_intval) {
727
            return true;
728
        } else {
729
            $this->_done_intval = true;
730
        }
731
732
        foreach ($_GET as $key => $val) {
733
            if (substr($key, -2) === 'id' && !is_array($_GET[$key])) {
734
                $newval     = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
735
                $_GET[$key] = $HTTP_GET_VARS[$key] = $newval;
736
                if ($_REQUEST[$key] == $_GET[$key]) {
737
                    $_REQUEST[$key] = $newval;
738
                }
739
            }
740
        }
741
        foreach ($_POST as $key => $val) {
742
            if (substr($key, -2) === 'id' && !is_array($_POST[$key])) {
743
                $newval      = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
744
                $_POST[$key] = $HTTP_POST_VARS[$key] = $newval;
745
                if ($_REQUEST[$key] == $_POST[$key]) {
746
                    $_REQUEST[$key] = $newval;
747
                }
748
            }
749
        }
750
        foreach ($_COOKIE as $key => $val) {
751
            if (substr($key, -2) === 'id' && !is_array($_COOKIE[$key])) {
752
                $newval        = preg_replace('/[^0-9a-zA-Z_-]/', '', $val);
753
                $_COOKIE[$key] = $HTTP_COOKIE_VARS[$key] = $newval;
754
                if ($_REQUEST[$key] == $_COOKIE[$key]) {
755
                    $_REQUEST[$key] = $newval;
756
                }
757
            }
758
        }
759
760
        return true;
761
    }
762
763
    /**
764
     * @return bool
765
     */
766
    public function eliminate_dotdot()
767
    {
768
        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...
769
770
        if ($this->_done_dotdot) {
771
            return true;
772
        } else {
773
            $this->_done_dotdot = true;
774
        }
775
776
        foreach ($_GET as $key => $val) {
777
            if (is_array($_GET[$key])) {
778
                continue;
779
            }
780
            if (substr(trim($val), 0, 3) === '../' || false !== strpos($val, '/../')) {
781
                $this->last_error_type = 'DirTraversal';
782
                $this->message .= "Directory Traversal '$val' found.\n";
783
                $this->output_log($this->last_error_type, 0, false, 64);
784
                $sanitized_val = str_replace(chr(0), '', $val);
785
                if (substr($sanitized_val, -2) !== ' .') {
786
                    $sanitized_val .= ' .';
787
                }
788
                $_GET[$key] = $HTTP_GET_VARS[$key] = $sanitized_val;
789
                if ($_REQUEST[$key] == $_GET[$key]) {
790
                    $_REQUEST[$key] = $sanitized_val;
791
                }
792
            }
793
        }
794
795
        /*    foreach ($_POST as $key => $val) {
796
                if( is_array( $_POST[ $key ] ) ) continue ;
797
                if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) {
798
                    $this->last_error_type = 'ParentDir' ;
799
                    $this->message .= "Doubtful file specification '$val' found.\n" ;
800
                    $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
801
                    $sanitized_val = str_replace( chr(0) , '' , $val ) ;
802
                    if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
803
                    $_POST[ $key ] = $HTTP_POST_VARS[ $key ] = $sanitized_val ;
804
                    if ($_REQUEST[ $key ] == $_POST[ $key ]) {
805
                        $_REQUEST[ $key ] = $sanitized_val ;
806
                    }
807
                }
808
            }
809
            foreach ($_COOKIE as $key => $val) {
810
                if( is_array( $_COOKIE[ $key ] ) ) continue ;
811
                if ( substr( trim( $val ) , 0 , 3 ) == '../' || false !== strpos( $val , '../../' ) ) {
812
                    $this->last_error_type = 'ParentDir' ;
813
                    $this->message .= "Doubtful file specification '$val' found.\n" ;
814
                    $this->output_log( $this->last_error_type , 0 , false , 128 ) ;
815
                    $sanitized_val = str_replace( chr(0) , '' , $val ) ;
816
                    if( substr( $sanitized_val , -2 ) != ' .' ) $sanitized_val .= ' .' ;
817
                    $_COOKIE[ $key ] = $HTTP_COOKIE_VARS[ $key ] = $sanitized_val ;
818
                    if ($_REQUEST[ $key ] == $_COOKIE[ $key ]) {
819
                        $_REQUEST[ $key ] = $sanitized_val ;
820
                    }
821
                }
822
            }*/
823
824
        return true;
825
    }
826
827
    /**
828
     * @param $current
829
     * @param $indexes
830
     *
831
     * @return bool
832
     */
833
    public function &get_ref_from_base64index(&$current, $indexes)
834
    {
835
        foreach ($indexes as $index) {
836
            $index = base64_decode($index);
837
            if (!is_array($current)) {
838
                return false;
839
            }
840
            $current =& $current[$index];
841
        }
842
843
        return $current;
844
    }
845
846
    /**
847
     * @param $key
848
     * @param $val
849
     */
850
    public function replace_doubtful($key, $val)
851
    {
852
        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...
853
854
        $index_expression = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $index_expression is dead and can be removed.
Loading history...
855
        $indexes          = explode('_', $key);
856
        $base_array       = array_shift($indexes);
857
858
        switch ($base_array) {
859
            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...
860
                $main_ref   =& $this->get_ref_from_base64index($_GET, $indexes);
861
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_GET_VARS, $indexes);
862
                break;
863
            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...
864
                $main_ref   =& $this->get_ref_from_base64index($_POST, $indexes);
865
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_POST_VARS, $indexes);
866
                break;
867
            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...
868
                $main_ref   =& $this->get_ref_from_base64index($_COOKIE, $indexes);
869
                $legacy_ref =& $this->get_ref_from_base64index($HTTP_COOKIE_VARS, $indexes);
870
                break;
871
            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...
872
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
873
        }
874
        if (!isset($main_ref)) {
875
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

951
                        /** @scrutinizer ignore-unhandled */ @unlink($temp_file);

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...
952
                    }
953
954
                    if ($image_attributes === false || $image_extensions[(int)$image_attributes[2]] != $ext) {
955
                        $this->message .= "Attempt to upload camouflaged image file {$_file['name']}.\n";
956
                        $this->_safe_badext    = false;
957
                        $this->last_error_type = 'UPLOAD';
958
                    }
959
                }
960
            }
961
        }
962
963
        return $this->_safe_badext;
964
    }
965
966
    /**
967
     * @return bool
968
     */
969
    public function check_contami_systemglobals()
970
    {
971
        /*    if( $this->_done_contami ) return $this->_safe_contami ;
972
    else $this->_done_contami = true ; */
973
974
        /*    foreach ($this->_bad_globals as $bad_global) {
975
                if ( isset( $_REQUEST[ $bad_global ] ) ) {
976
                    $this->message .= "Attempt to inject '$bad_global' was found.\n" ;
977
                    $this->_safe_contami = false ;
978
                    $this->last_error_type = 'CONTAMI' ;
979
                }
980
            }*/
981
982
        return $this->_safe_contami;
983
    }
984
985
    /**
986
     * @param bool $sanitize
987
     *
988
     * @return bool
989
     */
990
    public function check_sql_isolatedcommentin($sanitize = true)
991
    {
992
        if ($this->_done_isocom) {
993
            return $this->_safe_isocom;
994
        } else {
995
            $this->_done_isocom = true;
996
        }
997
998
        foreach ($this->_doubtful_requests as $key => $val) {
999
            $str = $val;
1000
            while ($str = strstr($str, '/*')) { /* */
1001
                $str = strstr(substr($str, 2), '*/');
1002
                if ($str === false) {
1003
                    $this->message .= "Isolated comment-in found. ($val)\n";
1004
                    if ($sanitize) {
1005
                        $this->replace_doubtful($key, $val . '*/');
1006
                    }
1007
                    $this->_safe_isocom    = false;
1008
                    $this->last_error_type = 'ISOCOM';
1009
                }
1010
            }
1011
        }
1012
1013
        return $this->_safe_isocom;
1014
    }
1015
1016
    /**
1017
     * @param bool $sanitize
1018
     *
1019
     * @return bool
1020
     */
1021
    public function check_sql_union($sanitize = true)
1022
    {
1023
        if ($this->_done_union) {
1024
            return $this->_safe_union;
1025
        } else {
1026
            $this->_done_union = true;
1027
        }
1028
1029
        foreach ($this->_doubtful_requests as $key => $val) {
1030
            $str = str_replace(array('/*', '*/'), '', preg_replace('?/\*.+\*/?sU', '', $val));
1031
            if (preg_match('/\sUNION\s+(ALL|SELECT)/i', $str)) {
1032
                $this->message .= "Pattern like SQL injection found. ($val)\n";
1033
                if ($sanitize) {
1034
                    //                    $this->replace_doubtful($key, preg_replace('/union/i', 'uni-on', $val));
1035
                    $this->replace_doubtful($key, str_ireplace('union', 'uni-on', $val));
1036
                }
1037
                $this->_safe_union     = false;
1038
                $this->last_error_type = 'UNION';
1039
            }
1040
        }
1041
1042
        return $this->_safe_union;
1043
    }
1044
1045
    /**
1046
     * @param $uid
1047
     *
1048
     * @return bool
1049
     */
1050
    public function stopforumspam($uid)
1051
    {
1052
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
1053
            return false;
1054
        }
1055
1056
        $result = $this->stopForumSpamLookup(
1057
            isset($_POST['email']) ? $_POST['email'] : null,
1058
            $_SERVER['REMOTE_ADDR'],
1059
            isset($_POST['uname']) ? $_POST['uname'] : null
1060
        );
1061
1062
        if (false === $result || isset($result['http_code'])) {
1063
            return false;
1064
        }
1065
1066
        $spammer = false;
1067
        if (isset($result['email']) && isset($result['email']['lastseen'])) {
1068
            $spammer = true;
1069
        }
1070
1071
        if (isset($result['ip']) && isset($result['ip']['lastseen'])) {
1072
            $last        = strtotime($result['ip']['lastseen']);
1073
            $oneMonth    = 60 * 60 * 24 * 31;
1074
            $oneMonthAgo = time() - $oneMonth;
1075
            if ($last > $oneMonthAgo) {
1076
                $spammer = true;
1077
            }
1078
        }
1079
1080
        if (!$spammer) {
1081
            return false;
1082
        }
1083
1084
        $this->last_error_type = 'SPAMMER POST';
1085
1086
        switch ($this->_conf['stopforumspam_action']) {
1087
            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...
1088
            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...
1089
                break;
1090
            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...
1091
                $_POST = array();
1092
                $this->message .= 'POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
1093
                break;
1094
            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...
1095
                $_POST = array();
1096
                $this->message .= 'BAN and POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
1097
                $this->_should_be_banned_time0 = true;
1098
                break;
1099
            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...
1100
                $_POST = array();
1101
                $this->message .= 'Ban and POST deleted for IP:' . $_SERVER['REMOTE_ADDR'];
1102
                $this->_should_be_banned = true;
1103
                break;
1104
        }
1105
1106
        $this->output_log($this->last_error_type, $uid, false, 16);
1107
1108
        return true;
1109
    }
1110
1111
    public function stopForumSpamLookup($email, $ip, $username)
1112
    {
1113
        if (!function_exists('curl_init')) {
1114
            return false;
1115
        }
1116
1117
        $query = '';
1118
        $query .= (empty($ip)) ? '' : '&ip=' . $ip;
1119
        $query .= (empty($email)) ? '' : '&email=' . $email;
1120
        $query .= (empty($username)) ? '' : '&username=' . $username;
1121
1122
        if (empty($query)) {
1123
            return false;
1124
        }
1125
1126
        $url = 'http://www.stopforumspam.com/api?f=json' . $query;
1127
        $ch  = curl_init();
1128
        curl_setopt($ch, CURLOPT_URL, $url);
1129
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1130
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
1131
        $result = curl_exec($ch);
1132
        if (false === $result) {
1133
            $result = curl_getinfo($ch);
1134
        } else {
1135
            $result = json_decode(curl_exec($ch), true);
1136
        }
1137
        curl_close($ch);
1138
1139
        return $result;
1140
    }
1141
1142
    /**
1143
     * @param int  $uid
1144
     * @param bool $can_ban
1145
     *
1146
     * @return bool
1147
     */
1148
    public function check_dos_attack($uid = 0, $can_ban = false)
1149
    {
1150
        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...
1151
1152
        if ($this->_done_dos) {
1153
            return true;
1154
        }
1155
1156
        $ip      = \Xmf\IPAddress::fromRequest();
1157
        if (false === $ip->asReadable()) {
0 ignored issues
show
introduced by
The condition false === $ip->asReadable() is always false.
Loading history...
1158
            return true;
1159
        }
1160
        $uri     = @$_SERVER['REQUEST_URI'];
1161
1162
        // skip ajax fine uploader traffic
1163
        $parts = parse_url($uri);
1164
        $basename = empty($parts['path']) ? '' : basename($parts['path']);
1165
        if (('ajaxfineupload.php' === $basename) && ('' !== \Xmf\Request::getHeader('Authorization', ''))) {
1166
            return true;
1167
        }
1168
1169
        $ip4sql  = $xoopsDB->quote($ip->asReadable());
1170
        $uri4sql = $xoopsDB->quote($uri);
1171
1172
        // gargage collection
1173
        $result = $xoopsDB->queryF(
1174
            'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1175
            . ' WHERE expire < UNIX_TIMESTAMP()'
1176
        );
1177
1178
        // for older versions before updating this module
1179
        if ($result === false) {
1180
            $this->_done_dos = true;
1181
1182
            return true;
1183
        }
1184
1185
        // sql for recording access log (INSERT should be placed after SELECT)
1186
        $sql4insertlog = 'INSERT INTO ' . $xoopsDB->prefix($this->mydirname . '_access')
1187
                         . " SET ip={$ip4sql}, request_uri={$uri4sql},"
1188
                         . " expire=UNIX_TIMESTAMP()+'" . (int)$this->_conf['dos_expire'] . "'";
1189
1190
        // bandwidth limitation
1191
        if (@$this->_conf['bwlimit_count'] >= 10) {
1192
            $result = $xoopsDB->query('SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access'));
1193
            list($bw_count) = $xoopsDB->fetchRow($result);
1194
            if ($bw_count > $this->_conf['bwlimit_count']) {
1195
                $this->write_file_bwlimit(time() + $this->_conf['dos_expire']);
1196
            }
1197
        }
1198
1199
        // F5 attack check (High load & same URI)
1200
        $result = $xoopsDB->query(
1201
            'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1202
            . " WHERE ip={$ip4sql} AND request_uri={$uri4sql}");
1203
        list($f5_count) = $xoopsDB->fetchRow($result);
1204
        if ($f5_count > $this->_conf['dos_f5count']) {
1205
1206
            // delayed insert
1207
            $xoopsDB->queryF($sql4insertlog);
1208
1209
            // extends the expires of the IP with 5 minutes at least (pending)
1210
            // $result = $xoopsDB->queryF( "UPDATE ".$xoopsDB->prefix($this->mydirname.'_access')." SET expire=UNIX_TIMESTAMP()+300 WHERE ip='$ip4sql' AND expire<UNIX_TIMESTAMP()+300" ) ;
1211
1212
            // call the filter first
1213
            $ret = $this->call_filter('f5attack_overrun');
0 ignored issues
show
Unused Code introduced by
The assignment to $ret is dead and can be removed.
Loading history...
1214
1215
            // actions for F5 Attack
1216
            $this->_done_dos       = true;
1217
            $this->last_error_type = 'DoS';
1218
            switch ($this->_conf['dos_f5action']) {
1219
                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...
1220
                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...
1221
                    $this->output_log($this->last_error_type, $uid, true, 16);
1222
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1223
                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...
1224
                    $this->output_log($this->last_error_type, $uid, true, 16);
1225
1226
                    return true;
1227
                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...
1228
                    if ($can_ban) {
1229
                        $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1230
                    }
1231
                    break;
1232
                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...
1233
                    if ($can_ban) {
1234
                        $this->register_bad_ips();
1235
                    }
1236
                    break;
1237
                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...
1238
                    if ($can_ban) {
1239
                        $this->deny_by_htaccess();
1240
                    }
1241
                    break;
1242
                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...
1243
                    sleep(5);
1244
                    break;
1245
            }
1246
1247
            return false;
1248
        }
1249
1250
        // Check its Agent
1251
        if (trim($this->_conf['dos_crsafe']) != '' && preg_match($this->_conf['dos_crsafe'], @$_SERVER['HTTP_USER_AGENT'])) {
1252
            // welcomed crawler
1253
            $this->_done_dos = true;
1254
1255
            return true;
1256
        }
1257
1258
        // Crawler check (High load & different URI)
1259
        $result = $xoopsDB->query(
1260
            'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . " WHERE ip={$ip4sql}"
1261
        );
1262
        list($crawler_count) = $xoopsDB->fetchRow($result);
1263
1264
        // delayed insert
1265
        $xoopsDB->queryF($sql4insertlog);
1266
1267
        if ($crawler_count > $this->_conf['dos_crcount']) {
1268
1269
            // call the filter first
1270
            $ret = $this->call_filter('crawler_overrun');
1271
1272
            // actions for bad Crawler
1273
            $this->_done_dos       = true;
1274
            $this->last_error_type = 'CRAWLER';
1275
            switch ($this->_conf['dos_craction']) {
1276
                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...
1277
                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...
1278
                    $this->output_log($this->last_error_type, $uid, true, 16);
1279
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1280
                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...
1281
                    $this->output_log($this->last_error_type, $uid, true, 16);
1282
1283
                    return true;
1284
                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...
1285
                    if ($can_ban) {
1286
                        $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1287
                    }
1288
                    break;
1289
                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...
1290
                    if ($can_ban) {
1291
                        $this->register_bad_ips();
1292
                    }
1293
                    break;
1294
                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...
1295
                    if ($can_ban) {
1296
                        $this->deny_by_htaccess();
1297
                    }
1298
                    break;
1299
                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...
1300
                    sleep(5);
1301
                    break;
1302
            }
1303
1304
            return false;
1305
        }
1306
1307
        return true;
1308
    }
1309
1310
    //
1311
    /**
1312
     * @return bool|null
1313
     */
1314
    public function check_brute_force()
1315
    {
1316
        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...
1317
1318
        $ip      = \Xmf\IPAddress::fromRequest();
1319
        if (false === $ip->asReadable()) {
0 ignored issues
show
introduced by
The condition false === $ip->asReadable() is always false.
Loading history...
1320
            return true;
1321
        }
1322
        $uri     = @$_SERVER['REQUEST_URI'];
1323
        $ip4sql  = $xoopsDB->quote($ip->asReadable());
1324
        $uri4sql = $xoopsDB->quote($uri);
1325
1326
        $victim_uname = empty($_COOKIE['autologin_uname']) ? $_POST['uname'] : $_COOKIE['autologin_uname'];
1327
        // some UA send 'deleted' as a value of the deleted cookie.
1328
        if ($victim_uname === 'deleted') {
1329
            return null;
1330
        }
1331
        $mal4sql = $xoopsDB->quote("BRUTE FORCE: $victim_uname");
1332
1333
        // gargage collection
1334
        $result = $xoopsDB->queryF(
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
1335
            'DELETE FROM ' . $xoopsDB->prefix($this->mydirname . '_access') . ' WHERE expire < UNIX_TIMESTAMP()'
1336
        );
1337
1338
        // sql for recording access log (INSERT should be placed after SELECT)
1339
        $sql4insertlog = 'INSERT INTO ' . $xoopsDB->prefix($this->mydirname . '_access')
1340
                         . " SET ip={$ip4sql}, request_uri={$uri4sql}, malicious_actions={$mal4sql}, expire=UNIX_TIMESTAMP()+600";
1341
1342
        // count check
1343
        $result = $xoopsDB->query(
1344
            'SELECT COUNT(*) FROM ' . $xoopsDB->prefix($this->mydirname . '_access')
1345
            . " WHERE ip={$ip4sql} AND malicious_actions like 'BRUTE FORCE:%'"
1346
        );
1347
        list($bf_count) = $xoopsDB->fetchRow($result);
1348
        if ($bf_count > $this->_conf['bf_count']) {
1349
            $this->register_bad_ips(time() + $this->_conf['banip_time0']);
1350
            $this->last_error_type = 'BruteForce';
1351
            $this->message .= "Trying to login as '" . addslashes($victim_uname) . "' found.\n";
1352
            $this->output_log('BRUTE FORCE', 0, true, 1);
1353
            $ret = $this->call_filter('bruteforce_overrun');
1354
            if ($ret == false) {
1355
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1356
            }
1357
        }
1358
        // delayed insert
1359
        $xoopsDB->queryF($sql4insertlog);
1360
        return null;
1361
    }
1362
1363
    /**
1364
     * @param $val
1365
     */
1366
    protected function _spam_check_point_recursive($val)
1367
    {
1368
        if (is_array($val)) {
1369
            foreach ($val as $subval) {
1370
                $this->_spam_check_point_recursive($subval);
1371
            }
1372
        } else {
1373
            // http_host
1374
            $path_array = parse_url(XOOPS_URL);
1375
            $http_host  = empty($path_array['host']) ? 'www.xoops.org' : $path_array['host'];
1376
1377
            // count URI up
1378
            $count = -1;
1379
            foreach (preg_split('#https?\:\/\/#i', $val) as $fragment) {
1380
                if (strncmp($fragment, $http_host, strlen($http_host)) !== 0) {
1381
                    ++$count;
1382
                }
1383
            }
1384
            if ($count > 0) {
1385
                $this->_spamcount_uri += $count;
1386
            }
1387
1388
            // count BBCode likd [url=www....] up (without [url=http://...])
1389
            $this->_spamcount_uri += count(preg_split('/\[url=(?!http|\\"http|\\\'http|' . $http_host . ')/i', $val)) - 1;
0 ignored issues
show
Bug introduced by
It seems like preg_split('/\[url=(?!ht...ttp_host . ')/i', $val) can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1389
            $this->_spamcount_uri += count(/** @scrutinizer ignore-type */ preg_split('/\[url=(?!http|\\"http|\\\'http|' . $http_host . ')/i', $val)) - 1;
Loading history...
1390
        }
1391
    }
1392
1393
    /**
1394
     * @param $points4deny
1395
     * @param $uid
1396
     */
1397
    public function spam_check($points4deny, $uid)
1398
    {
1399
        $this->_spamcount_uri = 0;
1400
        $this->_spam_check_point_recursive($_POST);
1401
1402
        if ($this->_spamcount_uri >= $points4deny) {
1403
            $this->message .= @$_SERVER['REQUEST_URI'] . " SPAM POINT: $this->_spamcount_uri\n";
1404
            $this->output_log('URI SPAM', $uid, false, 128);
1405
            $ret = $this->call_filter('spamcheck_overrun');
1406
            if ($ret == false) {
1407
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1408
            }
1409
        }
1410
    }
1411
1412
    public function disable_features()
1413
    {
1414
        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...
1415
1416
        // disable "Notice: Undefined index: ..."
1417
        $error_reporting_level = error_reporting(0);
1418
1419
        //
1420
        // bit 1 : disable XMLRPC , criteria bug
1421
        //
1422
        if ($this->_conf['disable_features'] & 1) {
1423
1424
            // zx 2005/1/5 disable xmlrpc.php in root
1425
            if (/* ! stristr( $_SERVER['SCRIPT_NAME'] , 'modules' ) && */
1426
                substr(@$_SERVER['SCRIPT_NAME'], -10) === 'xmlrpc.php'
1427
            ) {
1428
                $this->output_log('xmlrpc', 0, true, 1);
1429
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1430
            }
1431
1432
            // security bug of class/criteria.php 2005/6/27
1433
            if ($_POST['uname'] === '0' || $_COOKIE['autologin_pass'] === '0') {
1434
                $this->output_log('CRITERIA');
1435
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1436
            }
1437
        }
1438
1439
        //
1440
        // bit 11 : XSS+CSRFs in XOOPS < 2.0.10
1441
        //
1442
        if ($this->_conf['disable_features'] & 1024) {
1443
1444
            // root controllers
1445
            if (false === stripos(@$_SERVER['SCRIPT_NAME'], 'modules')) {
1446
                // zx 2004/12/13 misc.php debug (file check)
1447
                if (substr(@$_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ($_GET['type'] === 'debug' || $_POST['type'] === 'debug') && !preg_match('/^dummy_\d+\.html$/', $_GET['file'])) {
1448
                    $this->output_log('misc debug');
1449
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1450
                }
1451
1452
                // zx 2004/12/13 misc.php smilies
1453
                if (substr(@$_SERVER['SCRIPT_NAME'], -8) === 'misc.php' && ($_GET['type'] === 'smilies' || $_POST['type'] === 'smilies') && !preg_match('/^[0-9a-z_]*$/i', $_GET['target'])) {
1454
                    $this->output_log('misc smilies');
1455
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1456
                }
1457
1458
                // zx 2005/1/5 edituser.php avatarchoose
1459
                if (substr(@$_SERVER['SCRIPT_NAME'], -12) === 'edituser.php' && $_POST['op'] === 'avatarchoose' && false !== strpos($_POST['user_avatar'], '..')) {
1460
                    $this->output_log('edituser avatarchoose');
1461
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1462
                }
1463
            }
1464
1465
            // zx 2005/1/4 findusers
1466
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'findusers' || $_POST['fct'] === 'findusers')) {
1467
                foreach ($_POST as $key => $val) {
1468
                    if (false !== strpos($key, "'") || false !== strpos($val, "'")) {
1469
                        $this->output_log('findusers');
1470
                        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1471
                    }
1472
                }
1473
            }
1474
1475
            // preview CSRF zx 2004/12/14
1476
            // news submit.php
1477
            if (substr(@$_SERVER['SCRIPT_NAME'], -23) === 'modules/news/submit.php' && isset($_POST['preview']) && strpos(@$_SERVER['HTTP_REFERER'], XOOPS_URL . '/modules/news/submit.php') !== 0) {
1478
                $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
1479
            }
1480
            // news admin/index.php
1481
            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) {
1482
                $HTTP_POST_VARS['nohtml'] = $_POST['nohtml'] = 1;
1483
            }
1484
            // comment comment_post.php
1485
            if (isset($_POST['com_dopreview']) && false === strpos(substr(@$_SERVER['HTTP_REFERER'], -16), 'comment_post.php')) {
1486
                $HTTP_POST_VARS['dohtml'] = $_POST['dohtml'] = 0;
1487
            }
1488
            // disable preview of system's blocksadmin
1489
            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 */) {
1490
                die("Danger! don't use this preview. Use 'altsys module' instead.(by Protector)");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1491
            }
1492
            // tpl preview
1493
            if (substr(@$_SERVER['SCRIPT_NAME'], -24) === 'modules/system/admin.php' && ($_GET['fct'] === 'tplsets' || $_POST['fct'] === 'tplsets')) {
1494
                if ($_POST['op'] === 'previewpopup' || $_GET['op'] === 'previewpopup' || isset($_POST['previewtpl'])) {
1495
                    die("Danger! don't use this preview.(by Protector)");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1496
                }
1497
            }
1498
        }
1499
1500
        // restore reporting level
1501
        error_reporting($error_reporting_level);
1502
    }
1503
1504
    /**
1505
     * @param        $type
1506
     * @param string $dying_message
1507
     *
1508
     * @return int|mixed
1509
     */
1510
    public function call_filter($type, $dying_message = '')
1511
    {
1512
        require_once __DIR__ . '/ProtectorFilter.php';
1513
        $filter_handler = ProtectorFilterHandler::getInstance();
1514
        $ret            = $filter_handler->execute($type);
1515
        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...
1516
            die($dying_message);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1517
        }
1518
1519
        return $ret;
1520
    }
1521
}
1522