Passed
Push — master ( ba041f...b3e27e )
by Jean-Christophe
01:16
created

ContentSecurity   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 86
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 3
Metric Value
wmc 18
eloc 44
c 5
b 0
f 3
dl 0
loc 86
rs 10

11 Methods

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