This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Nabble\SemaltBlocker; |
||
4 | |||
5 | /** |
||
6 | * The most important class for this package. Basic usage: |
||
7 | * ``` |
||
8 | * Blocker::protect(); |
||
9 | * ```. |
||
10 | */ |
||
11 | class Blocker |
||
12 | { |
||
13 | const SEPERATOR = ':'; |
||
14 | |||
15 | public static $explanation = "Access to this website has been blocked because your referral is set to %s. <a href='%s'>Read why</a>"; |
||
16 | |||
17 | private static $blocklist = './../../domains/blocked'; |
||
18 | private static $reason = 'Not blocking, no reason given'; |
||
19 | |||
20 | ////////////////////////////////////////// |
||
21 | // PUBLIC API // |
||
22 | ////////////////////////////////////////// |
||
23 | |||
24 | /** |
||
25 | * Block a page if referer is found on list of blocked domains. |
||
26 | * |
||
27 | * @param string $action If empty, send 403 response; if URL, redirect here; if non-empty string, print message |
||
28 | */ |
||
29 | public static function protect($action = '') |
||
30 | { |
||
31 | // Try to update the list |
||
32 | if (!defined('SEMALT_UNIT_TESTING')) { |
||
33 | Updater::update(); |
||
34 | } |
||
35 | |||
36 | // Simply stop here if referer is not on the list |
||
37 | if (!self::isRefererOnBlocklist()) { |
||
38 | return; |
||
39 | } |
||
40 | |||
41 | self::doBlock($action); |
||
42 | |||
43 | // Stop execution altogether, bye bye bots |
||
44 | if (!defined('SEMALT_UNIT_TESTING')) { |
||
45 | exit; |
||
46 | } |
||
47 | } |
||
48 | |||
49 | /** |
||
50 | * @param bool $verbose Deprecated. Please use the explain() method instead. |
||
51 | * |
||
52 | * @return bool|string |
||
53 | */ |
||
54 | public static function blocked($verbose = false) |
||
55 | { |
||
56 | $blocked = self::isRefererOnBlocklist(); |
||
57 | if ($verbose === true) { |
||
58 | return self::$reason; |
||
59 | } |
||
60 | |||
61 | return $blocked; |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * @return string |
||
66 | */ |
||
67 | public static function explain() |
||
68 | { |
||
69 | return self::$reason; |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * Send a 403 Forbidden header. |
||
74 | */ |
||
75 | public static function forbidden() |
||
76 | { |
||
77 | $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'); |
||
78 | header($protocol . ' 403 Forbidden'); |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * @return array |
||
83 | */ |
||
84 | public static function getBlocklist() |
||
85 | { |
||
86 | return self::parseBlocklist(self::getBlocklistContents()); |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * @return string |
||
91 | */ |
||
92 | public static function getBlocklistFilename() |
||
93 | { |
||
94 | return __DIR__ . DIRECTORY_SEPARATOR . static::$blocklist; |
||
0 ignored issues
–
show
|
|||
95 | } |
||
96 | |||
97 | ////////////////////////////////////////// |
||
98 | // PRIVATE FUNCTIONS // |
||
99 | ////////////////////////////////////////// |
||
100 | |||
101 | /** |
||
102 | * Responsible for sending action output. |
||
103 | * |
||
104 | * @param string $action |
||
105 | */ |
||
106 | private static function doBlock($action = '') |
||
107 | { |
||
108 | // Clear buffered output |
||
109 | if (!defined('SEMALT_UNIT_TESTING')) { |
||
110 | self::cls(); |
||
111 | } |
||
112 | |||
113 | // Take user defined action |
||
114 | self::blockAction($action); |
||
115 | |||
116 | // If a human comes by, don't just serve a blank page |
||
117 | echo sprintf(self::$explanation, self::getHttpReferer(), 'https://www.google.com/#q=' . urlencode(preg_replace('/https?:\/\//', '', self::getHttpReferer()) . ' referral spam')); |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Execute desired action. |
||
122 | * |
||
123 | * @param string $action |
||
124 | */ |
||
125 | private static function blockAction($action = '') |
||
126 | { |
||
127 | // Redirect or 403 |
||
128 | if (filter_var($action, FILTER_VALIDATE_URL)) { |
||
129 | self::redirect($action); |
||
130 | } else { |
||
131 | self::forbidden(); |
||
132 | if (!empty($action)) { |
||
133 | echo $action . '<br/>'; |
||
134 | } // tell them something nice |
||
135 | } |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Clear output buffer. |
||
140 | */ |
||
141 | private static function cls() |
||
142 | { |
||
143 | while (ob_get_level()) { |
||
144 | ob_end_clean(); |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Redirect to a url by sending the appropriate header. |
||
150 | * |
||
151 | * @param string $url |
||
152 | */ |
||
153 | private static function redirect($url) |
||
154 | { |
||
155 | header('Location: ' . $url); |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * The public use of this function is undocumented. |
||
160 | * |
||
161 | * @return bool |
||
162 | */ |
||
163 | public static function isRefererOnBlocklist() |
||
164 | { |
||
165 | $referer = self::getHttpReferer(); |
||
166 | if ($referer === null) { |
||
167 | self::$reason = 'Not blocking because referer header is not set or empty'; |
||
168 | |||
169 | return false; |
||
170 | } |
||
171 | |||
172 | return self::isUrlOnBlocklist($referer, 'referer'); |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * The public use of this function is undocumented. |
||
177 | * |
||
178 | * @param string $url |
||
179 | * @param string $entity |
||
180 | * |
||
181 | * @return bool |
||
182 | */ |
||
183 | public static function isUrlOnBlocklist($url, $entity = 'url') |
||
184 | { |
||
185 | $rootDomain = Domainparser::getRootDomain($url); |
||
186 | if ($rootDomain === false) { |
||
187 | self::$reason = "Not blocking because we couldn't parse root domain"; |
||
188 | |||
189 | return false; |
||
190 | } |
||
191 | |||
192 | $blocklist = self::getConcatenateBlocklist(); |
||
193 | View Code Duplication | if (substr_count($blocklist, self::SEPERATOR . $rootDomain . self::SEPERATOR)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
194 | self::$reason = 'Blocking because ' . $entity . ' root domain (' . $rootDomain . ') is found on blocklist'; |
||
195 | |||
196 | return true; |
||
197 | } |
||
198 | |||
199 | $hostname = Domainparser::getHostname($url); |
||
200 | View Code Duplication | if (substr_count($blocklist, self::SEPERATOR . $hostname . self::SEPERATOR)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
201 | self::$reason = 'Blocking because ' . $entity . ' hostname (' . $hostname . ') is found on blocklist'; |
||
202 | |||
203 | return true; |
||
204 | } |
||
205 | |||
206 | $path = Domainparser::getPath($url); |
||
207 | if (trim($path, '/')) { |
||
208 | View Code Duplication | if (substr_count($blocklist, self::SEPERATOR . $rootDomain . $path . self::SEPERATOR)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
209 | self::$reason = 'Blocking because ' . $entity . ' root domain/path (' . $rootDomain . $path . ') is found on blocklist'; |
||
210 | |||
211 | return true; |
||
212 | } |
||
213 | View Code Duplication | if (substr_count($blocklist, self::SEPERATOR . $hostname . $path . self::SEPERATOR)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
214 | self::$reason = 'Blocking because ' . $entity . ' hostname/path (' . $hostname . $path . ') is found on blocklist'; |
||
215 | |||
216 | return true; |
||
217 | } |
||
218 | } |
||
219 | |||
220 | self::$reason = 'Not blocking because ' . $entity . ' (' . $url . ') is not matched against blocklist'; |
||
221 | |||
222 | return false; |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Returns HTTP Referer if it is available and not empty, null otherwise. |
||
227 | * |
||
228 | * @return string|null |
||
229 | */ |
||
230 | private static function getHttpReferer() |
||
231 | { |
||
232 | if (isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER'])) { |
||
233 | return $_SERVER['HTTP_REFERER']; |
||
234 | } |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * @return string |
||
239 | */ |
||
240 | private static function getBlocklistContents() |
||
241 | { |
||
242 | $blocklistContent = file_get_contents(self::getBlocklistFilename()); |
||
243 | |||
244 | return $blocklistContent; |
||
245 | } |
||
246 | |||
247 | /** |
||
248 | * @return string |
||
249 | */ |
||
250 | private static function getConcatenateBlocklist() |
||
251 | { |
||
252 | return self::concatenateBlocklist(self::getBlocklistContents()); |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * @param string $blocklistContent |
||
257 | * |
||
258 | * @return array |
||
259 | */ |
||
260 | private static function parseBlocklist($blocklistContent) |
||
261 | { |
||
262 | return array_map('trim', array_filter(explode(PHP_EOL, strtolower($blocklistContent)))); |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * @param string $blocklistContent |
||
267 | * |
||
268 | * @return string |
||
269 | */ |
||
270 | private static function concatenateBlocklist($blocklistContent) |
||
271 | { |
||
272 | return self::SEPERATOR . str_replace(PHP_EOL, self::SEPERATOR, strtolower($blocklistContent)) . self::SEPERATOR; |
||
273 | } |
||
274 | } |
||
275 |
Let’s assume you have a class which uses late-static binding:
The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the
getSomeVariable()
on that sub-class, you will receive a runtime error:In the case above, it makes sense to update
SomeClass
to useself
instead: