Completed
Pull Request — master (#39)
by Tom
08:12 queued 04:52
created

Policy::applyTo()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 1
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
namespace Spatie\Csp\Policies;
4
5
use ReflectionClass;
6
use Spatie\Csp\Keyword;
7
use Spatie\Csp\Directive;
8
use Illuminate\Http\Request;
9
use Spatie\Csp\Exceptions\InvalidDirective;
10
use Symfony\Component\HttpFoundation\Response;
11
12
abstract class Policy
13
{
14
    protected $directives = [];
15
16
    protected $reportOnly = false;
17
18
    abstract public function configure();
19
20
    /**
21
     * @param string $directive
22
     * @param string|array $values
23
     *
24
     * @return \Spatie\Csp\Policies\Policy
25
     *
26
     * @throws \Spatie\Csp\Exceptions\InvalidDirective
27
     */
28
    public function addDirective(string $directive, $values): self
29
    {
30
        $this->guardAgainstInvalidDirectives($directive);
31
32
        $rules = array_flatten(array_map(function ($values) {
33
            return empty($values) ? $values : array_filter(explode(' ', $values));
34
        }, array_wrap($values)));
35
36
        foreach ($rules as $rule) {
37
            $sanitizedValue = $this->sanitizeValue($rule);
38
39
            if (! in_array($sanitizedValue, $this->directives[$directive] ?? [])) {
40
                $this->directives[$directive][] = $sanitizedValue;
41
            }
42
        }
43
44
        return $this;
45
    }
46
47
    public function reportOnly(): self
48
    {
49
        $this->reportOnly = true;
50
51
        return $this;
52
    }
53
54
    public function enforce(): self
55
    {
56
        $this->reportOnly = false;
57
58
        return $this;
59
    }
60
61
    public function reportTo(string $uri): self
62
    {
63
        $this->directives['report-uri'] = [$uri];
64
65
        return $this;
66
    }
67
68
    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...
69
    {
70
        return config('csp.enabled');
71
    }
72
73
    public function addNonceForDirective(string $directive): self
74
    {
75
        return $this->addDirective($directive, "'nonce-".app('csp-nonce')."'");
76
    }
77
78
    public function applyTo(Response $response)
79
    {
80
        $this->configure();
81
82
        $headerName = $this->reportOnly
83
            ? 'Content-Security-Policy-Report-Only'
84
            : 'Content-Security-Policy';
85
86
        if ($response->headers->has($headerName)) {
87
            return;
88
        }
89
90
        $response->headers->set($headerName, (string) $this);
91
    }
92
93
    public function __toString()
94
    {
95
        return collect($this->directives)
96
            ->map(function (array $values, string $directive) {
97
                $valueString = implode(' ', $values);
98
99
                return empty($valueString) ? "{$directive}" : "{$directive} {$valueString}";
100
            })
101
            ->implode(';');
102
    }
103
104
    protected function guardAgainstInvalidDirectives(string $directive)
105
    {
106
        if (! Directive::isValid($directive)) {
107
            throw InvalidDirective::notSupported($directive);
108
        }
109
    }
110
111
    protected function isHash(string $value): bool
112
    {
113
        $acceptableHashingAlgorithms = [
114
          'sha256-',
115
          'sha384-',
116
          'sha512-',
117
        ];
118
119
        return starts_with($value, $acceptableHashingAlgorithms);
120
    }
121
122
    protected function isKeyword(string $value): bool
123
    {
124
        $keywords = (new ReflectionClass(Keyword::class))->getConstants();
125
126
        return in_array($value, $keywords);
127
    }
128
129
    protected function sanitizeValue(string $value): string
130
    {
131
        if (
132
            $this->isKeyword($value)
133
            || $this->isHash($value)
134
        ) {
135
            return "'{$value}'";
136
        }
137
138
        return $value;
139
    }
140
}
141