Passed
Push — master ( 052ac9...6628a2 )
by Jean-Christophe
01:32
created

ContentSecurity::addHeaderToResponse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 3
c 1
b 0
f 1
dl 0
loc 5
rs 10
cc 2
nc 2
nop 1
1
<?php
2
namespace Ubiquity\security\csp;
3
4
use Ubiquity\utils\http\UResponse;
5
6
/**
7
 * Creates a Content Security Policy object.
8
 * Ubiquity\security\csp$ContentSecurity
9
 * This class is part of Ubiquity
10
 *
11
 * @author jc
12
 * @version 1.0.0
13
 *
14
 */
15
class ContentSecurity {
16
17
	const HEADER = 'Content-Security-Policy';
18
19
	const DEBUG_HEADER = 'Content-Security-Policy-Report-Only';
20
21
	private array $policies = [];
22
23
	private $header = self::HEADER;
24
25
	public function addPolicy(string $directive, string ...$values): self {
26
		$policies = $this->policies[$directive] ?? [];
27
		foreach ($values as $v) {
28
			if (\in_array($v, CspValues::QUOTED)) {
29
				$v = "'$v'";
30
			}
31
			$policies[$v] = true;
32
		}
33
		$this->policies[$directive] = $policies;
34
		return $this;
35
	}
36
37
	public function addNonce(string $nonce, string ...$directives): self {
38
		$directives ??= [
39
			CspDirectives::DEFAULT_SRC
40
		];
41
		foreach ($directives as $directive) {
42
			$this->policies[$directive]["'nonce-$nonce'"] = true;
43
			$this->policies[$directive][CspValues::STRICT_DYNAMIC] = true;
44
		}
45
		return $this;
46
	}
47
48
	public function setDefaultSrc(string ...$policies) {
49
		return $this->addPolicy(CspDirectives::DEFAULT_SRC, ...$policies);
50
	}
51
52
	public function generate(): string {
53
		$strs = '';
54
		foreach ($this->policies as $directive => $policy) {
55
			$policies = \array_keys($policy);
56
			$strs .= $directive . ' ' . \implode(' ', $policies) . ';';
57
		}
58
		return $strs;
59
	}
60
61
	public function reportOnly(bool $reportOnly = true): self {
62
		$this->header = $reportOnly ? self::DEBUG_HEADER : self::HEADER;
63
		return $this;
64
	}
65
66
	public function addHeaderToResponse(?bool $reportOnly = null): void {
67
		if (isset($reportOnly)) {
68
			$this->reportOnly($reportOnly);
69
		}
70
		UResponse::header($this->header, $this->generate());
71
	}
72
73
	public static function nonce($nonce, string ...$directives): ContentSecurity {
74
		$csp = new self();
75
		return $csp->addNonce($nonce, ...$directives);
76
	}
77
78
	public static function defaultUbiquity(): ContentSecurity {
79
		$csp = new self();
80
		$csp->addPolicy(CspDirectives::DEFAULT_SRC, 'cdn.jsdelivr.net', 'cdnjs.cloudflare.com');
81
		$csp->addPolicy(CspDirectives::FONT_SRC, 'fonts.googleapis.com', 'fonts.gstatic.com', 'data:');
82
		$csp->addPolicy(CspDirectives::STYLE_SRC, CspValues::UNSAFE_INLINE);
83
		return $csp;
84
	}
85
}
86
87