-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcsp.php
More file actions
125 lines (110 loc) · 3.34 KB
/
Copy pathcsp.php
File metadata and controls
125 lines (110 loc) · 3.34 KB
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<?php
/**
* Manages the Content-Security-Policy log for the Torque Wordpress plugin
*
* @package hexydec/torque
*/
namespace hexydec\torque;
class csp {
public static function get(string $file, ?string $key = null) : ?array {
static $csp = null;
if (!$csp && \file_exists($file) && ($data = \file_get_contents($file)) !== false) {
$csp = [];
foreach (\explode("\n", $data) AS $item) {
$digest = \md5($item);
if (($item = \json_decode($item, true)) !== null && !empty($item['csp-report'])) {
if (isset($item['csp-report']['effective-directive'], $item['csp-report']['blocked-uri'])) {
$parts = \explode('-', $item['csp-report']['effective-directive']);
$directive = $parts[0].'-'.$parts[1];
if (!isset($csp[$directive])) {
$csp[$directive] = [];
}
if (!isset($csp[$directive][$digest])) {
$csp[$directive][$digest] = $item['csp-report'];
}
}
}
}
}
return $key ? $csp[$key] ?? null : $csp;
}
public static function violations(string $file, ?string $key = null) : ?array {
static $csp = null;
if ($csp === null && ($data = self::get($file)) !== null) {
$csp = [];
$replacements = [
'inline' => "'unsafe-inline'",
'eval' => "'unsafe-eval'",
'data' => 'data:',
'self' => "'self"
];
foreach ($data AS $key => $item) {
$csp[$key] = [];
foreach ($item AS $row) {
$value = $replacements[$row['blocked-uri']] ?? $row['blocked-uri'];
if (!isset($csp[$key][$value])) {
$csp[$key][$value] = [];
}
if (!\in_array($row['document-uri'], $csp[$key][$value], true)) {
$csp[$key][$value][] = $row['document-uri'];
}
}
}
}
return $key ? ($csp[$key] ?? null) : $csp;
}
public static function recommendations(string $file, string $key) : ?array {
if (($data = self::violations($file, $key)) !== null) {
// define kewords
$keywords = ["'unsafe-inline'", "'unsafe-eval'", 'data' => 'data:', "'self'", 'blob:'];
// build recommendations
$recs = [];
foreach (\array_keys($data) AS $href) {
if (!\in_array($href, $keywords)) {
$value = \mb_substr($href, 0, \mb_strrpos($href, '/') + 1);
// compare the urls folder by folder
$found = false;
foreach ($recs AS $r => $item) {
$last = null;
$pos = 7;
// compare one more folder each time
while (($pos = \strpos($value, '/', $pos + 1)) !== false) {
// match
if (\strncmp($item, $value, $pos + 1) === 0) {
$last = $pos;
// matched previous
} elseif ($last) {
break;
}
}
// overwrite found item with last match as may now be shorter
if ($last) {
$recs[$r] = \substr($value, 0, $last + 1);
$found = true;
break;
}
}
// new item that doesn't match any previous items
if (!$found) {
$recs[] = $href;
}
// add keyword
} elseif (!\in_array($href, $recs)) {
$recs[] = $href;
}
}
// build root URL
$self = (($_SERVER['HTTPS'] ?? 'off') !== 'off' ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'];
$folder = \str_replace('\\', '/', \mb_substr(\ABSPATH, \mb_strlen($_SERVER['DOCUMENT_ROOT'])));
$base = $self.$folder;
// replace root with 'self'
foreach ($recs AS $key => $item) {
if ($item === $base) {
$recs[$key] = "'self'";
}
}
return $recs;
}
return null;
}
}