forked from RainLoop/rainloop-webmail
/
csp.php
executable file
·100 lines (91 loc) · 2.68 KB
/
csp.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<?php
/**
* Controls the content_security_policy
*/
namespace SnappyMail\HTTP;
class CSP
{
public
$report = false,
$report_to = [],
$report_only = false;
private $directives = [
'base-uri' => ["'self'"],
'default-src' => ["'self'", 'data:'],
// Knockout.js requires eval() for observable binding purposes
// Safari < 15.4 does not support strict-dynamic
// 'script-src' => ["'strict-dynamic'", "'unsafe-eval'"],
'script-src' => ["'self'", "'unsafe-eval'"],
// Knockout.js requires unsafe-inline?
// 'script-src' => ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
'img-src' => ["'self'", 'data:'],
'media-src' => ["'self'", 'data:'],
'style-src' => ["'self'", "'unsafe-inline'"],
];
function __construct(string $default = '')
{
if ($default) {
foreach (\explode(';', $default) as $directive) {
$sources = \preg_split('/\\s+/', \trim($directive));
$directive = \array_shift($sources);
if (!isset($this->directives[$directive])) {
$this->directives[$directive] = [];
}
$this->directives[$directive] = \array_merge($this->directives[$directive], $sources);
}
}
}
function __toString() : string
{
// report-uri deprecated
unset($this->directives['report-uri']);
if ($this->report || $this->report_only) {
$this->directives['report-uri'] = [\RainLoop\Utils::WebPath() . '?/CspReport'];
}
$params = [];
foreach ($this->directives as $directive => $sources) {
$params[] = $directive . ' ' . \implode(' ', \array_unique($sources));
}
// if (empty($this->directives['frame-ancestors'])) {
// $params[] = "frame-ancestors 'none';";
// }
return \implode('; ', $params);
}
public function add(string $directive, string $source) : void
{
if (!isset($this->directives[$directive])) {
$this->directives[$directive] = [];
}
$this->directives[$directive][] = $source;
}
public function get(string $directive) : array
{
return isset($this->directives[$directive])
? $this->directives[$directive]
: [];
}
public function setHeaders() : void
{
if ($this->report_only) {
\header('Content-Security-Policy-Report-Only: ' . $this);
} else {
\header('Content-Security-Policy: ' . $this);
}
if (empty($this->directives['frame-ancestors'])) {
\header('X-Frame-Options: DENY');
} else {
// \header('X-Frame-Options: SAMEORIGIN');
}
}
public static function logReport() : void
{
\http_response_code(204);
$json = \file_get_contents('php://input');
$report = \json_decode($json, true);
// Useless to log 'moz-extension' as there's no clue which extension violates
if ($json && $report && 'moz-extension' !== $report['csp-report']['source-file']) {
\SnappyMail\Log::error('CSP', $json);
}
exit;
}
}