Passed
Push — 2.x ( 5eb243...f5b171 )
by Terry
01:58
created

Firewall::getOption()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 3
nop 2
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Shieldon\Firewall;
14
15
use Psr\Http\Message\ServerRequestInterface;
16
use Psr\Http\Message\ResponseInterface;
17
use Psr\Http\Server\MiddlewareInterface;
18
use Shieldon\Firewall\Kernel;
19
use Shieldon\Firewall\Captcha as Captcha;
20
use Shieldon\Firewall\Component as Component;
21
use Shieldon\Firewall\Driver as Driver;
22
use Shieldon\Firewall\Middleware as Middleware;
23
use Shieldon\Firewall\Security as Security;
24
use Shieldon\Messenger as Messenger;
25
use Shieldon\Firewall\Messenger\MessengerFactory;
26
use Shieldon\Firewall\Utils\Container;
27
use Shieldon\Firewall\Log\ActionLogger;
28
use Shieldon\Firewall\FirewallTrait;
29
use Shieldon\Psr15\RequestHandler;
30
use function Shieldon\Firewall\get_request;
31
use function Shieldon\Firewall\get_response;
32
33
use PDO;
34
use PDOException;
35
use Redis;
36
use RedisException;
37
use RuntimeException;
38
39
use function array_column;
40
use function defined;
41
use function file_exists;
42
use function file_get_contents;
43
use function file_put_contents;
44
use function is_dir;
45
use function json_decode;
46
use function json_encode;
47
use function mkdir;
48
use function rtrim;
49
use function strpos;
50
use function umask;
51
use function time;
52
use function strtotime;
53
use function date;
54
55
/**
56
 * Managed Firewall.
57
 */
58
class Firewall
59
{
60
    use FirewallTrait;
61
62
    /**
63
     * Collection of PSR-7 or PSR-15 middlewares.
64
     *
65
     * @var array
66
     */
67
    protected $middlewares = [];
68
69
    /**
70
     * Constructor.
71
     */
72
    public function __construct(?ServerRequestInterface $request = null, ?ResponseInterface $response = null)
73
    {
74
        Container::set('firewall', $this);
75
76
        $this->kernel = new Kernel($request, $response);
77
    }
78
79
    /**
80
     * Set up the path of the configuration file.
81
     *
82
     * @param string $source The path.
83
     * @param string $type   The type.
84
     * 
85
     * @return void
86
     */
87
    public function configure(string $source, string $type = 'json')
88
    {
89
        if ($type === 'json') {
90
            $this->directory = rtrim($source, '\\/');
91
            $configFilePath = $this->directory . '/' . $this->filename;
92
93
            if (file_exists($configFilePath)) {
94
                $jsonString = file_get_contents($configFilePath);
95
96
            } else {
97
                $jsonString = file_get_contents(__DIR__ . '/../../config.json');
98
99
                if (defined('PHP_UNIT_TEST')) {
100
                    $jsonString = file_get_contents(__DIR__ . '/../../tests/config.json');
101
                }
102
            }
103
104
            $this->configuration = json_decode($jsonString, true);
105
            $this->kernel->managedBy('managed');
106
107
        } elseif ($type === 'php') {
108
            $this->configuration = require $source;
109
            $this->kernel->managedBy('config');
110
        }
111
112
        $this->setup();
113
    }
114
115
    /**
116
     * Add middlewares and use them before going into Shieldon kernal.
117
     *
118
     * @param MiddlewareInterface $middleware A PSR-15 middlewares.
119
     *
120
     * @return void
121
     */
122
    public function add(MiddlewareInterface $middleware)
123
    {
124
        $this->middlewares[] = $middleware;
125
    }
126
127
    /**
128
     * Setup everything we need.
129
     *
130
     * @return void
131
     */
132
    public function setup(): void
133
    {
134
        $this->status = $this->getOption('daemon');
135
136
        $this->setDriver();
137
138
        $this->setChannel();
139
140
        $this->setIpSource();
141
142
        $this->setLogger();
143
144
        $this->setFilters();
145
146
        $this->setComponents();
147
148
        $this->setCaptchas();
149
150
        $this->setSessionLimit();
151
152
        $this->setCronJob();
153
154
        $this->setExcludedUrls();
155
156
        $this->setXssProtection();
157
158
        $this->setAuthentication();
159
160
        $this->setDialogUI();
161
162
        $this->setMessengers();
163
164
        $this->setMessageEvents();
165
166
        $this->setDenyAttempts();
167
168
        $this->setIptablesWatchingFolder();
169
    }
170
171
    /**
172
     * Just, run!
173
     *
174
     * @return ResponseInterface
175
     */
176
    public function run(): ResponseInterface
177
    {
178
        // If settings are ready, let's start monitoring requests.
179
        if ($this->status) {
180
181
            $response = get_request();
182
183
            // PSR-15 request handler.
184
            $requestHandler = new RequestHandler();
185
186
            foreach ($this->middlewares as $middleware) {
187
                $requestHandler->add($middleware);
188
            }
189
190
            $response = $requestHandler->handle($response);
191
192
            // Something is detected by Middlewares, return.
193
            if ($response->getStatusCode() !== 200) {
194
                return $response;
195
            }
196
197
            $result = $this->kernel->run();
198
199
            if ($result !== $this->kernel::RESPONSE_ALLOW) {
200
201
                if ($this->kernel->captchaResponse()) {
202
                    $this->kernel->unban();
203
204
                    $response = $response->withHeader('Location', $this->kernel->getCurrentUrl());
205
                    $response = $response->withStatus(303);
206
207
                    return $response;
208
                }
209
            }
210
        }
211
212
        return $this->kernel->respond();
213
    }
214
215
    /**
216
     * Set the channel ID.
217
     *
218
     * @return void
219
     */
220
    protected function setChannel(): void
221
    {
222
        $channelId = $this->getOption('channel_id');
223
224
        if ($channelId) {
225
            $this->kernel->setChannel($channelId);
226
        }
227
    }
228
229
    /**
230
     * Set a data driver for Shieldon use.
231
     *
232
     * @return void
233
     */
234
    protected function setDriver(): void
235
    {
236
        $driverType = $this->getOption('driver_type');
237
238
        switch ($driverType) {
239
240
            case 'redis':
241
            
242
                $redisSetting = $this->getOption('redis', 'drivers');
243
244
                try {
245
246
                    $host = '127.0.0.1';
247
                    $port = 6379;
248
249
                    if (!empty($redisSetting['host'])) {
250
                        $host = $redisSetting['host'];
251
                    }
252
253
                    if (!empty($redisSetting['port'])) {
254
                        $port = $redisSetting['port'];
255
                    }
256
257
                    // Create a Redis instance.
258
                    $redis = new Redis();
259
                    $redis->connect($host, $port);
260
261
                    if (!empty($redisSetting['auth'])) {
262
263
                        // @codeCoverageIgnoreStart
264
                        $redis->auth($redisSetting['auth']);
265
                        // @codeCoverageIgnoreEnd
266
                    }
267
268
                    // Use Redis data driver.
269
                    $this->kernel->add(new Driver\RedisDriver($redis));
270
271
                // @codeCoverageIgnoreStart
272
                } catch(RedisException $e) {
273
                    $this->status = false;
274
275
                    echo $e->getMessage();
276
                }
277
                // @codeCoverageIgnoreEnd
278
279
                break;
280
281
            case 'file':
282
            
283
                $fileSetting = $this->getOption('file', 'drivers');
284
285
                if (empty($fileSetting['directory_path'])) {
286
                    $fileSetting['directory_path'] = $this->directory;
287
                }
288
289
                // Use File data driver.
290
                $this->kernel->add(new Driver\FileDriver($fileSetting['directory_path']));
291
292
                break;
293
294
            case 'sqlite':
295
            
296
                $sqliteSetting = $this->getOption('sqlite', 'drivers');
297
298
                if (empty($sqliteSetting['directory_path'])) {
299
                    $sqliteSetting['directory_path'] = '';
300
                    $this->status = false;
301
                }
302
303
                try {
304
                    
305
                    // Specific the sqlite file location.
306
                    $sqliteLocation = $sqliteSetting['directory_path'] . '/shieldon.sqlite3';
307
308
                    // Create a PDO instance.
309
                    $pdoInstance = new PDO('sqlite:' . $sqliteLocation);
310
311
                    // Use Sqlite data driver.
312
                    $this->kernel->add(new Driver\SqliteDriver($pdoInstance));
313
    
314
                // @codeCoverageIgnoreStart
315
                } catch(PDOException $e) {
316
                    $this->status = false;
317
318
                    echo $e->getMessage();
319
                }
320
                // @codeCoverageIgnoreEnd
321
322
                break;
323
324
            case 'mysql':
325
            default:
326
327
                $mysqlSetting = $this->getOption('mysql', 'drivers');
328
329
                try {
330
331
                    // Create a PDO instance.
332
                    $pdoInstance = new PDO(
333
                        'mysql:host=' 
334
                            . $mysqlSetting['host']   . ';dbname=' 
335
                            . $mysqlSetting['dbname'] . ';charset=' 
336
                            . $mysqlSetting['charset']
337
                        , (string) $mysqlSetting['user']
338
                        , (string) $mysqlSetting['pass']
339
                    );
340
341
                    // Use MySQL data driver.
342
                    $this->kernel->add(new Driver\MysqlDriver($pdoInstance));
343
344
                // @codeCoverageIgnoreStart
345
                } catch(PDOException $e) {
346
                    echo $e->getMessage();
347
                }
348
                // @codeCoverageIgnoreEnd
349
            // end switch.
350
        }
351
    }
352
353
    /**
354
     * Set up the action logger.
355
     *
356
     * @return void
357
     */
358
    protected function setLogger(): void
359
    {
360
        $loggerSetting = $this->getOption('action', 'loggers');
361
362
        if ($loggerSetting['enable']) {
363
            if (!empty($loggerSetting['config']['directory_path'])) {
364
                $this->kernel->add(new ActionLogger($loggerSetting['config']['directory_path']));
365
            }
366
        }
367
    }
368
369
    /**
370
     * If you use CDN, please choose the real IP source.
371
     *
372
     * @return void
373
     */
374
    protected function setIpSource(): void
375
    {
376
        $ipSourceType = $this->getOption('ip_variable_source');
377
        $serverParams = get_request()->getServerParams();
378
379
        /**
380
         * REMOTE_ADDR: general
381
         * HTTP_CF_CONNECTING_IP: Cloudflare
382
         * HTTP_X_FORWARDED_FOR: Google Cloud CDN, Google Load-balancer, AWS.
383
         * HTTP_X_FORWARDED_HOST: KeyCDN, or other CDN providers not listed here.
384
         * 
385
         */
386
        $key = array_search(true, $ipSourceType);
0 ignored issues
show
Bug introduced by
It seems like $ipSourceType can also be of type false; however, parameter $haystack of array_search() does only seem to accept 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

386
        $key = array_search(true, /** @scrutinizer ignore-type */ $ipSourceType);
Loading history...
387
        $ip = $serverParams[$key];
388
389
        if (empty($ip)) {
390
            // @codeCoverageIgnoreStart
391
            throw new RuntimeException('IP source is not set correctly.');
392
            // @codeCoverageIgnoreEnd
393
        }
394
395
        $this->kernel->setIp($ip);
396
    }
397
398
    /**
399
     * Set the filiters.
400
     *
401
     * @return void
402
     */
403
    protected function setFilters(): void
404
    {
405
        $sessionSetting   = $this->getOption('session', 'filters');
406
        $cookieSetting    = $this->getOption('cookie', 'filters');
407
        $refererSetting   = $this->getOption('referer', 'filters');
408
        $frequencySetting = $this->getOption('frequency', 'filters');
409
410
        $filterConfig = [
411
            'session'   => $sessionSetting['enable'],
412
            'cookie'    => $cookieSetting['enable'],
413
            'referer'   => $refererSetting['enable'],
414
            'frequency' => $frequencySetting['enable'],
415
        ];
416
417
        $this->kernel->setFilters($filterConfig);
418
419
        $this->kernel->setProperty('limit_unusual_behavior', [
420
            'session' => $sessionSetting['config']['quota'] ?? 5,
421
            'cookie'  => $cookieSetting['config']['quota'] ?? 5,
422
            'referer' => $refererSetting['config']['quota'] ?? 5,
423
        ]);
424
425
        // if ($frequencySetting['enable']) {
426
        $frequencyQuota = [
427
            's' => $frequencySetting['config']['quota_s'] ?? 2,
428
            'm' => $frequencySetting['config']['quota_m'] ?? 10,
429
            'h' => $frequencySetting['config']['quota_h'] ?? 30,
430
            'd' => $frequencySetting['config']['quota_d'] ?? 60,
431
        ];
432
433
        $this->kernel->setProperty('time_unit_quota', $frequencyQuota);
434
435
        // if ($cookieSetting['enable']) {
436
        $cookieName = $cookieSetting['config']['cookie_name'] ?? 'ssjd';
437
        $cookieDomain = $cookieSetting['config']['cookie_domain'] ?? '';
438
        $cookieValue = $cookieSetting['config']['cookie_value'] ?? '1';
439
440
        $this->kernel->setProperty('cookie_name', $cookieName);
441
        $this->kernel->setProperty('cookie_domain', $cookieDomain);
442
        $this->kernel->setProperty('cookie_value', $cookieValue);
443
444
        // if ($refererSetting['enable']) {
445
        $this->kernel->setProperty('interval_check_referer', $refererSetting['config']['time_buffer']);
446
447
        // if ($sessionSetting['enable']) {
448
        $this->kernel->setProperty('interval_check_session', $sessionSetting['config']['time_buffer']);
449
        
450
    }
451
452
    /**
453
     * Set the components.
454
     *
455
     * @return void
456
     */
457
    protected function setComponents(): void
458
    {
459
        $ipSetting = $this->getOption('ip', 'components');
460
        $rdnsSetting = $this->getOption('rdns', 'components');
461
        $headerSetting = $this->getOption('header', 'components');
462
        $userAgentSetting = $this->getOption('user_agent', 'components');
463
        $trustedBotSetting = $this->getOption('trusted_bot', 'components');
464
465
        if ($ipSetting['enable']) {
466
            $componentIp = new Component\Ip();
467
            $this->kernel->add($componentIp);
468
            $this->ipManager();
469
        }
470
471
        if ($trustedBotSetting['enable']) {
472
            $componentTrustedBot = new Component\TrustedBot();
473
474
            if ($trustedBotSetting['strict_mode']) {
475
                $componentTrustedBot->setStrict(true);
476
            }
477
478
            // This component will only allow popular search engline.
479
            // Other bots will go into the checking process.
480
            $this->kernel->add($componentTrustedBot);
481
        }
482
483
        if ($headerSetting['enable']) {
484
            $componentHeader = new Component\Header();
485
486
            // Deny all vistors without common header information.
487
            if ($headerSetting['strict_mode']) {
488
                $componentHeader->setStrict(true);
489
            }
490
491
            $this->kernel->add($componentHeader);
492
        }
493
494
        if ($userAgentSetting['enable']) {
495
            $componentUserAgent = new Component\UserAgent();
496
497
            // Deny all vistors without user-agent information.
498
            if ($userAgentSetting['strict_mode']) {
499
                $componentUserAgent->setStrict(true);
500
            }
501
502
            $this->kernel->add($componentUserAgent);
503
        }
504
505
        if ($rdnsSetting['enable']) {
506
            $componentRdns = new Component\Rdns();
507
508
            // Visitors with empty RDNS record will be blocked.
509
            // IP resolved hostname (RDNS) and IP address must conform with each other.
510
            if ($rdnsSetting['strict_mode']) {
511
                $componentRdns->setStrict(true);
512
            }
513
514
            $this->kernel->add($componentRdns);
515
        }
516
    }
517
518
    /**
519
     * Set the Captcha modules.
520
     *
521
     * @return void
522
     */
523
    protected function setCaptchas(): void
524
    {
525
        $recaptchaSetting = $this->getOption('recaptcha', 'captcha_modules');
526
        $imageSetting = $this->getOption('image', 'captcha_modules');
527
528
        if ($recaptchaSetting['enable']) {
529
530
            $googleRecaptcha = [
531
                'key'     => $recaptchaSetting['config']['site_key'],
532
                'secret'  => $recaptchaSetting['config']['secret_key'],
533
                'version' => $recaptchaSetting['config']['version'],
534
                'lang'    => $recaptchaSetting['config']['lang'],
535
            ];
536
537
            $this->kernel->add(new Captcha\Recaptcha($googleRecaptcha));
538
        }
539
540
        if ($imageSetting['enable']) {
541
542
            $type = $imageSetting['config']['type'] ?? 'alnum';
543
            $length = $imageSetting['config']['length'] ?? 8;
544
545
            switch ($type) {
546
                case 'numeric':
547
                    $imageCaptchaConfig['pool'] = '0123456789';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$imageCaptchaConfig was never initialized. Although not strictly required by PHP, it is generally a good practice to add $imageCaptchaConfig = array(); before regardless.
Loading history...
548
                    break;
549
550
                case 'alpha':
551
                    $imageCaptchaConfig['pool'] = '0123456789abcdefghijklmnopqrstuvwxyz';
552
                    break;
553
554
                case 'alnum':
555
                default:
556
                    $imageCaptchaConfig['pool'] = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
557
            }
558
559
            $imageCaptchaConfig['word_length'] = $length;
560
561
            $this->kernel->add(new Captcha\ImageCaptcha($imageCaptchaConfig));
562
        }
563
    }
564
565
    /**
566
     * Set the messenger modules.
567
     *
568
     * @return void
569
     */
570
    protected function setMessengers(): void
571
    {
572
        // // The ID list of the messenger modules.
573
        $messengerList = [
574
            'telegram',
575
            'line_notify',
576
            'sendgrid',
577
            'native_php_mail',
578
            'smtp',
579
            'mailgun',
580
            'rocket_chat',
581
            'slack',
582
            'slack_webhook',
583
        ];
584
585
        foreach ($messengerList as $messenger) {
586
            $setting = $this->getOption($messenger, 'messengers');
587
588
            if (is_array($setting)) {
589
590
                // Initialize messenger instances from the factory/
591
                if (MessengerFactory::check($messenger, $setting)) {
592
    
593
                    $this->kernel->add(
594
                        MessengerFactory::getInstance(
595
                            // The ID of the messenger module in the configuration.
596
                            $messenger, 
597
                            // The settings of the messenger module in the configuration.
598
                            $setting    
599
                        )
600
                    );
601
                }
602
            }
603
604
            unset($setting);
605
        }
606
    }
607
608
    /**
609
     * Set message events.
610
     *
611
     * @return void
612
     */
613
    protected function setMessageEvents(): void
614
    {
615
        $setting = $this->getOption('failed_attempts_in_a_row', 'events');
616
617
        $notifyDataCircle = false;
618
        $notifySystemFirewall = false;
619
620
        if ($setting['data_circle']['messenger']) {
621
            $notifyDataCircle = true;
622
        }
623
624
        if ($setting['system_firewall']['messenger']) {
625
            $notifyDataCircle = true;
626
        }
627
628
        $this->kernel->setProperty('deny_attempt_notify', [
629
            'data_circle' => $notifyDataCircle,
630
            'system_firewall' => $notifySystemFirewall,
631
        ]);
632
    }
633
634
    /**
635
     * Set deny attempts.
636
     *
637
     * @return void
638
     */
639
    protected function setDenyAttempts(): void
640
    {
641
        $setting = $this->getOption('failed_attempts_in_a_row', 'events');
642
643
        $enableDataCircle = false;
644
        $enableSystemFirewall = false;
645
646
        if ($setting['data_circle']['enable']) {
647
            $enableDataCircle = true;
648
        }
649
650
        if ($setting['system_firewall']['enable']) {
651
            $enableSystemFirewall = true;
652
        }
653
654
        $this->kernel->setProperty('deny_attempt_enable', [
655
            'data_circle' => $enableDataCircle,
656
            'system_firewall' => $enableSystemFirewall,
657
        ]);
658
659
        $this->kernel->setProperty('deny_attempt_buffer', [
660
            'data_circle' => $setting['data_circle']['buffer'] ?? 10,
661
            'system_firewall' => $setting['data_circle']['buffer'] ?? 10,
662
        ]);
663
664
        // Check the time of the last failed attempt. @since 0.2.0
665
        $recordAttempt = $this->getOption('record_attempt');
666
667
        $detectionPeriod = $recordAttempt['detection_period'] ?? 5;
668
        $timeToReset = $recordAttempt['time_to_reset'] ?? 1800;
669
670
        $this->kernel->setProperty('record_attempt_detection_period', $detectionPeriod);
671
        $this->kernel->setProperty('reset_attempt_counter', $timeToReset);
672
    }
673
674
    /**
675
     * Set iptables working folder.
676
     *
677
     * @return void
678
     */
679
    protected function setIptablesWatchingFolder(): void
680
    {
681
        $iptablesSetting = $this->getOption('config', 'iptables');
682
        $this->kernel->setProperty('iptables_watching_folder',  $iptablesSetting['watching_folder']);
683
    }
684
685
    /**
686
     * Set the online session limit.
687
     *
688
     * @return void
689
     */
690
    protected function setSessionLimit(): void
691
    {
692
        $sessionLimitSetting = $this->getOption('online_session_limit');
693
694
        if ($sessionLimitSetting['enable']) {
695
696
            $onlineUsers = $sessionLimitSetting['config']['count'] ?? 100;
697
            $alivePeriod = $sessionLimitSetting['config']['period'] ?? 300;
698
699
            $this->kernel->limitSession($onlineUsers, $alivePeriod);
700
        }
701
    }
702
703
    /**
704
     * Set the cron job.
705
     * This is triggered by the pageviews, not system cron job.
706
     *
707
     * @return void
708
     */
709
    protected function setCronJob(): void 
710
    {
711
        $cronjobSetting = $this->getOption('reset_circle', 'cronjob');
712
713
        if ($cronjobSetting['enable']) {
714
715
            $nowTime = time();
716
717
            $lastResetTime = $cronjobSetting['config']['last_update'];
718
719
            if (!empty($lastResetTime) ) {
720
                $lastResetTime = strtotime($lastResetTime);
721
            } else {
722
                // @codeCoverageIgnoreStart
723
                $lastResetTime = strtotime(date('Y-m-d 00:00:00'));
724
                // @codeCoverageIgnoreEnd
725
            }
726
727
            if (($nowTime - $lastResetTime) > $cronjobSetting['config']['period']) {
728
729
                $updateResetTime = date('Y-m-d 00:00:00');
730
731
                // Update new reset time.
732
                $this->setConfig('cronjob.reset_circle.config.last_update', $updateResetTime);
733
                $this->updateConfig();
734
735
                // Remove all logs.
736
                $this->kernel->driver->rebuild();
737
            }
738
        }
739
    }
740
741
    /**
742
     * Set the URLs that want to be excluded from Shieldon protection.
743
     *
744
     * @return void
745
     */
746
    protected function setExcludedUrls(): void
747
    {
748
        $excludedUrls = $this->getOption('excluded_urls');
749
750
        if (!empty($excludedUrls)) {
751
            $list = array_column($excludedUrls, 'url');
752
753
            $this->kernel->setExcludedUrls($list);
754
        }
755
    }
756
757
    /**
758
     * Set XSS protection.
759
     *
760
     * @return void
761
     */
762
    protected function setXssProtection(): void
763
    {
764
        $xssProtectionOptions = $this->getOption('xss_protection');
765
766
        $xssFilter = new Security\Xss();
767
768
        if ($xssProtectionOptions['post']) {
769
            $this->kernel->setClosure('xss_post', function() use ($xssFilter) {
770
                if (!empty($_POST)) {
771
                    foreach (array_keys($_POST) as $k) {
772
                        $_POST[$k] = $xssFilter->clean($_POST[$k]);
773
                    }
774
                }
775
            });
776
        }
777
778
        if ($xssProtectionOptions['get']) {
779
            $this->kernel->setClosure('xss_get', function() use ($xssFilter) {
780
                if (!empty($_GET)) {
781
                    foreach (array_keys($_GET) as $k) {
782
                        $_GET[$k] = $xssFilter->clean($_GET[$k]);
783
                    }
784
                }
785
            });
786
        }
787
788
        if ($xssProtectionOptions['cookie']) {
789
            $this->kernel->setClosure('xss_cookie', function() use ($xssFilter) {
790
                if (!empty($_COOKIE)) {
791
                    foreach (array_keys($_COOKIE) as $k) {
792
                        $_COOKIE[$k] = $xssFilter->clean($_COOKIE[$k]);
793
                    }
794
                }
795
            });
796
        }
797
798
        $xssProtectedList = $this->getOption('xss_protected_list');
799
800
        if (!empty($xssProtectedList)) {
801
        
802
            $this->kernel->setClosure('xss_protection', function() use ($xssFilter, $xssProtectedList) {
803
804
                foreach ($xssProtectedList as $v) {
805
                    $k = $v['variable'] ?? 'undefined';
806
    
807
                    switch ($v['type']) {
808
809
                        case 'get':
810
811
                            if (!empty($_GET[$k])) {
812
                                $_GET[$k] = $xssFilter->clean($_GET[$k]);
813
                            }
814
                            break;
815
    
816
                        case 'post':
817
    
818
                            if (!empty($_POST[$k])) {
819
                                $_POST[$k] = $xssFilter->clean($_POST[$k]);
820
                            }
821
                            break;
822
    
823
                        case 'cookie':
824
825
                            if (!empty($_COOKIE[$k])) {
826
                                $_COOKIE[$k] = $xssFilter->clean($_COOKIE[$k]);
827
                            }
828
                            break;
829
    
830
                        default:
831
                    }
832
                }
833
            });
834
        }
835
    }
836
837
    /**
838
     * WWW-Athentication.
839
     *
840
     * @return void
841
     */
842
    protected function setAuthentication(): void
843
    {
844
        $authenticateList = $this->getOption('www_authenticate');
845
846
        if (is_array($authenticateList)) {
847
            $this->add(new Middleware\httpAuthentication($authenticateList));
848
        }
849
    }
850
851
    /**
852
     * IP manager.
853
     */
854
    protected function ipManager()
855
    {
856
        $ipList = $this->getOption('ip_manager');
857
858
        $allowedList = [];
859
        $deniedList = [];
860
861
        if (!empty($ipList)) {
862
            foreach ($ipList as $ip) {
863
864
                if (0 === strpos($this->kernel->getCurrentUrl(), $ip['url']) ) {
865
    
866
                    if ('allow' === $ip['rule']) {
867
                        $allowedList[] = $ip['ip'];
868
                    }
869
    
870
                    if ('deny' === $ip['rule']) {
871
                        $deniedList[] = $ip['ip'];
872
                    }
873
                }
874
            }
875
        }
876
877
        if (!empty($allowedList)) {
878
            $this->kernel->component['Ip']->setAllowedItems($allowedList);
879
        }
880
881
        if (!empty($deniedList)) {
882
            $this->kernel->component['Ip']->setDeniedItems($deniedList);
883
        }
884
    }
885
886
    /**
887
     * Set dialog UI.
888
     *
889
     * @return void
890
     */
891
    protected function setDialogUI()
892
    {
893
        $ui = $this->getOption('dialog_ui');
894
895
        if (!empty($ui)) {
896
            get_session()->set('shieldon_ui_lang', $ui['lang']);
897
            $this->kernel->setDialogUI($this->getOption('dialog_ui'));
898
        }
899
    }
900
901
  
902
}
903