Completed
Push — master ( ef4351...f1ad05 )
by Freek
01:57
created

Policy::isSpecialDirective()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 1
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Spatie\Csp\Policies;
4
5
use Spatie\Csp\Value;
6
use Spatie\Csp\Directive;
7
use Illuminate\Http\Request;
8
use Spatie\Csp\Exceptions\InvalidDirective;
9
use Symfony\Component\HttpFoundation\Response;
10
11
abstract class Policy
12
{
13
    protected $directives = [];
14
15
    protected $reportOnly = false;
16
17
    abstract public function configure();
18
19
    /**
20
     * @param string $directive
21
     * @param string|array $values
22
     *
23
     * @return \Spatie\Csp\Policies\Policy
24
     *
25
     * @throws \Spatie\Csp\Exceptions\InvalidDirective
26
     */
27
    public function addDirective(string $directive, $values): self
28
    {
29
        $this->guardAgainstInvalidDirectives($directive);
30
31
        $rules = array_flatten(array_map(function ($values) {
32
            return array_filter(explode(' ', $values));
33
        }, array_wrap($values)));
34
35
        foreach ($rules as $rule) {
36
            $sanitizedValue = $this->sanitizeValue($rule);
37
38
            if (! in_array($sanitizedValue, $this->directives[$directive] ?? [])) {
39
                $this->directives[$directive][] = $sanitizedValue;
40
            }
41
        }
42
43
        return $this;
44
    }
45
46
    public function reportOnly(): self
47
    {
48
        $this->reportOnly = true;
49
50
        return $this;
51
    }
52
53
    public function enforce(): self
54
    {
55
        $this->reportOnly = false;
56
57
        return $this;
58
    }
59
60
    public function reportTo(string $uri): self
61
    {
62
        $this->directives['report-uri'] = [$uri];
63
64
        return $this;
65
    }
66
67
    public function shouldBeApplied(Request $request, Response $response): bool
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $response is not used and could be removed.

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

Loading history...
68
    {
69
        return config('csp.enabled');
70
    }
71
72
    public function addNonceForDirective(string $directive): self
73
    {
74
        return $this->addDirective($directive, "'nonce-".app('csp-nonce')."'");
75
    }
76
77
    public function applyTo(Response $response)
78
    {
79
        $this->configure();
80
81
        $headerName = $this->reportOnly
82
            ? 'Content-Security-Policy-Report-Only'
83
            : 'Content-Security-Policy';
84
85
        if ($response->headers->has($headerName)) {
86
            return;
87
        }
88
89
        $response->headers->set($headerName, (string) $this);
90
    }
91
92
    public function __toString()
93
    {
94
        return collect($this->directives)
95
            ->map(function (array $values, string $directive) {
96
                $valueString = implode(' ', $values);
97
98
                return "{$directive} {$valueString}";
99
            })
100
            ->implode(';');
101
    }
102
103
    protected function guardAgainstInvalidDirectives(string $directive)
104
    {
105
        if (! Directive::isValid($directive)) {
106
            throw InvalidDirective::notSupported($directive);
107
        }
108
    }
109
110
    protected function isHash(string $value): bool
111
    {
112
        $acceptableHashingAlgorithms = [
113
          'sha256-',
114
          'sha384-',
115
          'sha512-',
116
        ];
117
118
        return starts_with($value, $acceptableHashingAlgorithms);
119
    }
120
121
    protected function isSpecialDirective(string $value): bool
122
    {
123
        $specialDirectiveValues = [
124
            Value::NONE,
125
            Value::REPORT_SAMPLE,
126
            Value::SELF,
127
            Value::STRICT_DYNAMIC,
128
            Value::UNSAFE_EVAL,
129
            Value::UNSAFE_INLINE,
130
        ];
131
132
        return in_array($value, $specialDirectiveValues);
133
    }
134
135
    protected function sanitizeValue(string $value): string
136
    {
137
        if ($this->isSpecialDirective($value) || $this->isHash($value)) {
138
            return "'{$value}'";
139
        }
140
141
        return $value;
142
    }
143
}
144