ContentSecurityPolicyHeader::parse()   C
last analyzed

Complexity

Conditions 13
Paths 54

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 39
c 0
b 0
f 0
ccs 0
cts 20
cp 0
rs 6.6166
cc 13
nc 54
nop 0
crap 182

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
/**
4
 * @license  http://opensource.org/licenses/mit-license.php MIT
5
 * @link     https://github.com/nicoSWD
6
 * @author   Nicolas Oelgart <[email protected]>
7
 */
8
namespace nicoSWD\SecHeaderCheck\Domain\Validator\Header;
9
10
use nicoSWD\SecHeaderCheck\Domain\Result\Result\ContentSecurityPolicyHeaderResult;
11
use nicoSWD\SecHeaderCheck\Domain\Validator\AbstractHeaderParser;
12
13
final class ContentSecurityPolicyHeader extends AbstractHeaderParser
14
{
15
    private const DEFAULT_SRC = 'default-src';
16
    private const SCRIPT_SRC = 'script-src';
17
    private const IMG_SRC = 'img-src';
18
    private const FRAME_ANCESTORS = 'frame-ancestors';
19
    private const REPORT_URI = 'report-uri';
20
    private const CONNECT_SRC = 'connect-src';
21
    private const STYLE_SRC = 'style-src';
22
    private const SANDBOX = 'sandbox';
23
24
    private const EXPECTED_DIRECTIVES = [
25
        self::SCRIPT_SRC,
26
        self::IMG_SRC,
27
        self::FRAME_ANCESTORS,
28
        self::REPORT_URI,
29
        self::CONNECT_SRC,
30
        self::STYLE_SRC,
31
    ];
32
33
    private $foundDirectives = [];
34
35
    public function parse(): ContentSecurityPolicyHeaderResult
36
    {
37
        foreach ($this->getDirectives($this->getValue()) as $directive) {
38
            [$directiveName, $policy] = $this->parseDirectiveNameAndPolicy($directive);
39
40
            switch ($directiveName) {
41
                case self::DEFAULT_SRC:
42
                case self::IMG_SRC:
43
                case self::SCRIPT_SRC:
44
                case self::FRAME_ANCESTORS:
45
                case self::REPORT_URI:
46
                case self::CONNECT_SRC:
47
                case self::STYLE_SRC:
48
                case self::SANDBOX:
49
                    if ($this->hasValidPolicy($policy)) {
50
                        $this->foundDirectives[] = $directiveName;
51
                    } else {
52
//                        $this->addWarning(new ContentSecurityPolicyInvalidWarning());
53
                    }
54
                    break;
55
                default:
56
//                    $this->addWarning(new ContentSecurityPolicyWithInvalidDirectiveWarning($directiveName));
57
                    break;
58
            }
59
        }
60
61
        $missingDirectives = $this->getMissingDirectives();
62
63
        if (count($missingDirectives) > 0) {
64
            if (!in_array(self::FRAME_ANCESTORS, $missingDirectives, true)) {
65
//                $this->addWarning(new ContentSecurityPolicyMissingFrameAncestorsDirective());
66
            } else {
67
//                $this->addWarning(new ContentSecurityPolicyWithMissingDirectiveWarning(implode(', ', $missingDirectives)));
68
            }
69
        }
70
71
        $contentSecurityPolicyResult = new ContentSecurityPolicyHeaderResult($this->getName(), $this->getValue());
72
73
        return $contentSecurityPolicyResult;
74
75
    }
76
77
    private function parseDirectiveNameAndPolicy(string $directiveAndValues): array
78
    {
79
        $directiveAndValues = $this->splitSkipEmpty('~[ ]~', $directiveAndValues);
80
        $directiveName = array_shift($directiveAndValues);
81
82
        return [strtolower($directiveName), $directiveAndValues];
83
    }
84
85
    private function hasValidPolicy(array $values): bool
0 ignored issues
show
Unused Code introduced by
The parameter $values is not used and could be removed. ( Ignorable by Annotation )

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

85
    private function hasValidPolicy(/** @scrutinizer ignore-unused */ array $values): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
86
    {
87
        return true;
88
    }
89
90
    private function getDirectives(string $header): array
91
    {
92
        return $this->splitSkipEmpty('~;~', $header);
93
    }
94
95
    private function splitSkipEmpty(string $regex, string $header): array
96
    {
97
        return array_map('trim', preg_split($regex, $header, -1, PREG_SPLIT_NO_EMPTY));
0 ignored issues
show
Bug introduced by
It seems like preg_split($regex, $head...er\PREG_SPLIT_NO_EMPTY) can also be of type false; however, parameter $arr1 of array_map() 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

97
        return array_map('trim', /** @scrutinizer ignore-type */ preg_split($regex, $header, -1, PREG_SPLIT_NO_EMPTY));
Loading history...
98
    }
99
100
    private function getMissingDirectives(): array
101
    {
102
        if (!in_array(self::DEFAULT_SRC, $this->foundDirectives, true)) {
103
            return array_diff(self::EXPECTED_DIRECTIVES, $this->foundDirectives);
104
        }
105
106
        return [];
107
    }
108
}
109