Passed
Push — master ( b3e27e...db1dbb )
by Jean-Christophe
01:47
created

ContentSecurity::reportOnly()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 3
c 0
b 0
f 0
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 __construct(?bool $reportOnly = null) {
26
		if (isset($reportOnly)) {
27
			$this->reportOnly($reportOnly);
28
		}
29
	}
30
31
	public function addPolicy(string $directive, string ...$values): self {
32
		$policies = $this->policies[$directive] ?? [];
33
		foreach ($values as $v) {
34
			if (\in_array($v, CspValues::QUOTED)) {
35
				$v = "'$v'";
36
			}
37
			$policies[$v] = true;
38
		}
39
		$this->policies[$directive] = $policies;
40
		return $this;
41
	}
42
43
	public function addPolicyDefault(string $directive, string ...$values): self {
44
		$default = \array_keys($this->policies[CspDirectives::DEFAULT_SRC] ?? []);
45
		$values = \array_merge($default, $values);
46
		$this->addPolicy($directive, ...$values);
47
		return $this;
48
	}
49
50
	public function addNonce(string $nonce, string ...$directives): self {
51
		foreach ($directives as $directive) {
52
			$this->addPolicy($directive, "'nonce-$nonce'", CspValues::STRICT_DYNAMIC);
53
		}
54
		return $this;
55
	}
56
57
	public function addNonceDefault(string $nonce, string ...$directives): self {
58
		foreach ($directives as $directive) {
59
			$this->addPolicyDefault($directive, "'nonce-$nonce'", CspValues::STRICT_DYNAMIC);
60
		}
61
		return $this;
62
	}
63
64
	public function setDefaultSrc(string ...$policies) {
65
		return $this->addPolicy(CspDirectives::DEFAULT_SRC, ...$policies);
66
	}
67
68
	public function generate(): string {
69
		$strs = '';
70
		foreach ($this->policies as $directive => $policy) {
71
			$policies = \array_keys($policy);
72
			$strs .= $directive . ' ' . \implode(' ', $policies) . ';';
73
		}
74
		return $strs;
75
	}
76
77
	public function reportOnly(bool $reportOnly = true): self {
78
		$this->header = $reportOnly ? self::DEBUG_HEADER : self::HEADER;
79
		return $this;
80
	}
81
82
	public function addHeaderToResponse(?bool $reportOnly = null): void {
83
		if (isset($reportOnly)) {
84
			$this->reportOnly($reportOnly);
85
		}
86
		UResponse::header($this->header, $this->generate(), false);
87
	}
88
89
	public static function nonce($nonce, string ...$directives): ContentSecurity {
90
		$csp = new self();
91
		return $csp->addNonce($nonce, ...$directives);
92
	}
93
94
	public static function all(): ContentSecurity {
95
		$csp = new self();
96
		return $csp->addPolicy(CspDirectives::DEFAULT_SRC, CspValues::SELF);
97
	}
98
99
	public static function defaultUbiquity(): ContentSecurity {
100
		$csp = new self();
101
		$csp->addPolicy(CspDirectives::DEFAULT_SRC, 'self', 'cdn.jsdelivr.net', 'cdnjs.cloudflare.com');
102
		$csp->addPolicyDefault(CspDirectives::FONT_SRC, 'fonts.googleapis.com', 'fonts.gstatic.com', 'data:');
103
		$csp->addPolicyDefault(CspDirectives::STYLE_SRC, CspValues::UNSAFE_INLINE, 'fonts.googleapis.com');
104
		$csp->addPolicyDefault(CspDirectives::SCRIPT_SRC_ELM, CspValues::UNSAFE_INLINE);
105
		$csp->addPolicy(CspDirectives::IMG_SRC, 'data:');
106
		return $csp;
107
	}
108
}
109