Passed
Push — master ( 9df593...ba041f )
by Jean-Christophe
01:31
created

ContentSecurity   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 2
Metric Value
wmc 16
eloc 42
c 4
b 0
f 2
dl 0
loc 80
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A addPolicy() 0 10 3
A addNonce() 0 6 2
A addPolicyDefault() 0 5 1
A setDefaultSrc() 0 2 1
A addHeaderToResponse() 0 5 2
A reportOnly() 0 3 2
A all() 0 3 1
A defaultUbiquity() 0 8 1
A generate() 0 7 2
A nonce() 0 3 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 addPolicyDefault(string $directive, string ...$values): self {
38
		$default = \array_keys($this->policies[CspDirectives::DEFAULT_SRC] ?? []);
39
		$values = \array_merge($default, $values);
40
		$this->addPolicy($directive, ...$values);
41
		return $this;
42
	}
43
44
	public function addNonce(string $nonce, string ...$directives): self {
45
		foreach ($directives as $directive) {
46
			$this->policies[$directive]["'nonce-$nonce'"] = true;
47
			$this->policies[$directive]["'" . CspValues::STRICT_DYNAMIC . "'"] = true;
48
		}
49
		return $this;
50
	}
51
52
	public function setDefaultSrc(string ...$policies) {
53
		return $this->addPolicy(CspDirectives::DEFAULT_SRC, ...$policies);
54
	}
55
56
	public function generate(): string {
57
		$strs = '';
58
		foreach ($this->policies as $directive => $policy) {
59
			$policies = \array_keys($policy);
60
			$strs .= $directive . ' ' . \implode(' ', $policies) . ';';
61
		}
62
		return $strs;
63
	}
64
65
	public function reportOnly(bool $reportOnly = true): self {
66
		$this->header = $reportOnly ? self::DEBUG_HEADER : self::HEADER;
67
		return $this;
68
	}
69
70
	public function addHeaderToResponse(?bool $reportOnly = null): void {
71
		if (isset($reportOnly)) {
72
			$this->reportOnly($reportOnly);
73
		}
74
		UResponse::header($this->header, $this->generate(), false);
75
	}
76
77
	public static function nonce($nonce, string ...$directives): ContentSecurity {
78
		$csp = new self();
79
		return $csp->addNonce($nonce, ...$directives);
80
	}
81
82
	public static function all(): ContentSecurity {
83
		$csp = new self();
84
		return $csp->addPolicy(CspDirectives::DEFAULT_SRC, CspValues::SELF);
85
	}
86
87
	public static function defaultUbiquity(): ContentSecurity {
88
		$csp = new self();
89
		$csp->addPolicy(CspDirectives::DEFAULT_SRC, 'self', 'cdn.jsdelivr.net', 'cdnjs.cloudflare.com');
90
		$csp->addPolicyDefault(CspDirectives::FONT_SRC, 'fonts.googleapis.com', 'fonts.gstatic.com', 'data:');
91
		$csp->addPolicyDefault(CspDirectives::STYLE_SRC, CspValues::UNSAFE_INLINE, 'fonts.googleapis.com');
92
		$csp->addPolicyDefault(CspDirectives::SCRIPT_SRC_ELM, CspValues::UNSAFE_INLINE);
93
		$csp->addPolicy(CspDirectives::IMG_SRC, 'data:');
94
		return $csp;
95
	}
96
}
97