1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Yandex PHP Library |
4
|
|
|
* |
5
|
|
|
* @copyright NIX Solutions Ltd. |
6
|
|
|
* @link https://github.com/nixsolutions/yandex-php-library |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* @namespace |
11
|
|
|
*/ |
12
|
|
|
namespace Yandex\SafeBrowsing; |
13
|
|
|
|
14
|
|
|
use Psr\Http\Message\UriInterface; |
15
|
|
|
use Yandex\Common\AbstractServiceClient; |
16
|
|
|
use GuzzleHttp\Psr7\Response; |
17
|
|
|
use GuzzleHttp\Exception\ClientException; |
18
|
|
|
use Yandex\Common\Exception\ForbiddenException; |
19
|
|
|
use Yandex\Common\Exception\NotFoundException; |
20
|
|
|
use Yandex\Common\Exception\UnauthorizedException; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Class SafeBrowsingClient |
24
|
|
|
* |
25
|
|
|
* @category Yandex |
26
|
|
|
* @package SafeBrowsing |
27
|
|
|
* |
28
|
|
|
* @author Alexander Khaylo <[email protected]> |
29
|
|
|
* @created 31.01.14 17:32 |
30
|
|
|
*/ |
31
|
|
|
class SafeBrowsingClient extends AbstractServiceClient |
32
|
|
|
{ |
33
|
|
|
/** |
34
|
|
|
* @var string |
35
|
|
|
*/ |
36
|
|
|
protected $serviceDomain = 'sba.yandex.net'; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var |
40
|
|
|
*/ |
41
|
|
|
protected $apiKey; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var string |
45
|
|
|
*/ |
46
|
|
|
protected $appVer = '2.3'; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var string |
50
|
|
|
*/ |
51
|
|
|
protected $pVer = '2.3'; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var array |
55
|
|
|
*/ |
56
|
|
|
protected $malwareShavars = [ |
57
|
|
|
'ydx-malware-shavar', |
58
|
|
|
'ydx-phish-shavar', |
59
|
|
|
'goog-malware-shavar', |
60
|
|
|
'goog-phish-shavar' |
61
|
|
|
]; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @param string $apiKey |
65
|
|
|
*/ |
66
|
30 |
|
public function __construct($apiKey = '') |
67
|
|
|
{ |
68
|
30 |
|
$this->setApiKey($apiKey); |
69
|
30 |
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @param string $apiKey |
73
|
|
|
*/ |
74
|
30 |
|
public function setApiKey($apiKey) |
75
|
|
|
{ |
76
|
30 |
|
$this->apiKey = $apiKey; |
77
|
30 |
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @return string |
81
|
|
|
*/ |
82
|
3 |
|
public function getApiKey() |
83
|
|
|
{ |
84
|
3 |
|
return $this->apiKey; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @param array $malwareShavars |
89
|
|
|
*/ |
90
|
2 |
|
public function setMalwareShavars($malwareShavars) |
91
|
|
|
{ |
92
|
2 |
|
$this->malwareShavars = $malwareShavars; |
93
|
2 |
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @return array |
97
|
|
|
*/ |
98
|
1 |
|
public function getMalwareShavars() |
99
|
|
|
{ |
100
|
1 |
|
return $this->malwareShavars; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Get url to service resource with parameters |
105
|
|
|
* |
106
|
|
|
* @param string $resource |
107
|
|
|
* @return string |
108
|
|
|
*/ |
109
|
14 |
|
public function getServiceUrl($resource = '') |
110
|
|
|
{ |
111
|
14 |
|
return $this->serviceScheme . '://' . $this->serviceDomain . '/' |
112
|
14 |
|
. $resource . '?client=api&apikey=' . $this->apiKey . '&appver=' . $this->appVer . '&pver=' . $this->pVer; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Get url to service Lookup resource with parameters |
117
|
|
|
* |
118
|
|
|
* @param string $url |
119
|
|
|
* @return string |
120
|
|
|
*/ |
121
|
2 |
|
public function getLookupUrl($url = '') |
122
|
|
|
{ |
123
|
2 |
|
$pVer = '3.5'; //Specific version |
124
|
2 |
|
return $this->serviceScheme . '://' . $this->serviceDomain . '/' |
125
|
2 |
|
. 'lookup?client=api&apikey=' . $this->apiKey . '&pver=' . $pVer . '&url=' . $url; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Get url to service Check Adult resource with parameters |
130
|
|
|
* |
131
|
|
|
* @param string $url |
132
|
|
|
* @return string |
133
|
|
|
*/ |
134
|
6 |
|
public function getCheckAdultUrl($url = '') |
135
|
|
|
{ |
136
|
6 |
|
$pVer = '4.0'; //Specific version |
137
|
6 |
|
return $this->serviceScheme . '://' . $this->serviceDomain . '/' |
138
|
6 |
|
. 'cp?client=api&pver=' . $pVer . '&url=' . $url; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Sends a request |
143
|
|
|
* |
144
|
|
|
* @param string $method HTTP method |
145
|
|
|
* @param string|UriInterface $uri URI object or string. |
146
|
|
|
* @param array $options Request options to apply. |
147
|
|
|
* |
148
|
|
|
* @return Response |
149
|
|
|
* |
150
|
|
|
* @throws ForbiddenException |
151
|
|
|
* @throws UnauthorizedException |
152
|
|
|
* @throws SafeBrowsingException |
153
|
|
|
* @throws NotFoundException |
154
|
|
|
*/ |
155
|
23 |
|
protected function sendRequest($method, $uri, array $options = []) |
156
|
|
|
{ |
157
|
|
|
try { |
158
|
23 |
|
$response = $this->getClient()->request($method, $uri, $options); |
|
|
|
|
159
|
23 |
|
} catch (ClientException $ex) { |
160
|
4 |
|
$result = $ex->getResponse(); |
161
|
4 |
|
$code = $result->getStatusCode(); |
162
|
4 |
|
$message = $result->getReasonPhrase(); |
163
|
|
|
|
164
|
4 |
|
if ($code === 403) { |
165
|
1 |
|
throw new ForbiddenException($message); |
166
|
|
|
} |
167
|
|
|
|
168
|
3 |
|
if ($code === 401) { |
169
|
1 |
|
throw new UnauthorizedException($message); |
170
|
|
|
} |
171
|
|
|
|
172
|
2 |
|
if ($code === 404) { |
173
|
1 |
|
throw new NotFoundException($message); |
174
|
|
|
} |
175
|
|
|
|
176
|
1 |
|
throw new SafeBrowsingException( |
177
|
1 |
|
'Service responded with error code: "' . $code . '" and message: "' . $message . '"', |
178
|
|
|
$code |
179
|
1 |
|
); |
180
|
|
|
} |
181
|
|
|
|
182
|
19 |
|
return $response; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* @param string $bodyString |
187
|
|
|
* @see https://developers.google.com/safe-browsing/developers_guide_v2#HTTPRequestForHashes |
188
|
|
|
* @return array |
189
|
|
|
*/ |
190
|
3 |
View Code Duplication |
public function checkHash($bodyString = '') |
|
|
|
|
191
|
|
|
{ |
192
|
3 |
|
$resource = 'gethash'; |
193
|
|
|
|
194
|
3 |
|
$response = $this->sendRequest( |
195
|
3 |
|
'POST', |
196
|
3 |
|
$this->getServiceUrl($resource), |
197
|
|
|
[ |
198
|
|
|
'body' => $bodyString |
199
|
3 |
|
] |
200
|
3 |
|
); |
201
|
|
|
|
202
|
|
|
return [ |
203
|
3 |
|
'code' => $response->getStatusCode(), |
204
|
3 |
|
'data' => $response->getBody() |
205
|
3 |
|
]; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* @param string $bodyString |
210
|
|
|
* @see https://developers.google.com/safe-browsing/developers_guide_v2#HTTPRequestForData |
211
|
|
|
* @return array |
212
|
|
|
*/ |
213
|
10 |
View Code Duplication |
public function getChunks($bodyString = '') |
|
|
|
|
214
|
|
|
{ |
215
|
10 |
|
$resource = 'downloads'; |
216
|
|
|
|
217
|
10 |
|
$response = $this->sendRequest( |
218
|
10 |
|
'POST', |
219
|
10 |
|
$this->getServiceUrl($resource), |
220
|
|
|
[ |
221
|
|
|
'body' => $bodyString |
222
|
10 |
|
] |
223
|
10 |
|
); |
224
|
|
|
|
225
|
|
|
return [ |
226
|
10 |
|
'code' => $response->getStatusCode(), |
227
|
10 |
|
'data' => $response->getBody() |
228
|
10 |
|
]; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* @see https://developers.google.com/safe-browsing/developers_guide_v2#HTTPRequestForList |
233
|
|
|
* @return array |
234
|
|
|
*/ |
235
|
1 |
|
public function getShavarsList() |
236
|
|
|
{ |
237
|
1 |
|
$resource = 'list'; |
238
|
1 |
|
$response = $this->sendRequest('GET', $this->getServiceUrl($resource)); |
239
|
1 |
|
return explode("\n", trim($response->getBody())); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* @param string $url |
244
|
|
|
* @return string|false |
245
|
|
|
*/ |
246
|
2 |
|
public function lookup($url) |
247
|
|
|
{ |
248
|
2 |
|
$response = $this->sendRequest('GET', $this->getLookupUrl($url)); |
249
|
2 |
|
if ($response->getStatusCode() === 200) { |
250
|
1 |
|
return $response->getBody()->getContents(); |
251
|
|
|
} |
252
|
1 |
|
return false; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* @param string $url |
257
|
|
|
* @return bool |
258
|
|
|
*/ |
259
|
6 |
|
public function checkAdult($url) |
260
|
|
|
{ |
261
|
6 |
|
$response = $this->sendRequest('GET', $this->getCheckAdultUrl($url)); |
262
|
2 |
|
if ($response->getBody()->getContents() === 'adult') { |
263
|
1 |
|
return true; |
264
|
|
|
} |
265
|
1 |
|
return false; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* @param string $url |
270
|
|
|
* @return string |
271
|
|
|
*/ |
272
|
1 |
|
public function getChunkByUrl($url) |
273
|
|
|
{ |
274
|
1 |
|
$client = $this->getClient(); |
275
|
|
|
|
276
|
1 |
|
$host = parse_url($url, PHP_URL_HOST); |
277
|
1 |
|
$headers = $client->getConfig('headers'); |
278
|
1 |
|
if ($host) { |
|
|
|
|
279
|
1 |
|
$headers['Host'] = $host; |
280
|
1 |
|
} |
281
|
|
|
|
282
|
1 |
|
$response = $this->sendRequest( |
283
|
1 |
|
'GET', |
284
|
1 |
|
$url, |
285
|
|
|
[ |
286
|
|
|
'headers' => $headers |
287
|
1 |
|
] |
288
|
1 |
|
); |
289
|
1 |
|
return $response->getBody()->getContents(); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* @param string $url |
294
|
|
|
* @return bool|array |
295
|
|
|
* @throws \Exception |
296
|
|
|
*/ |
297
|
3 |
|
public function searchUrl($url) |
298
|
|
|
{ |
299
|
3 |
|
$hashes = $this->getHashesByUrl($url); |
300
|
|
|
|
301
|
3 |
|
foreach ($hashes as $hash) { |
302
|
3 |
|
$prefixPack = pack("H*", $hash['prefix']); |
303
|
3 |
|
$prefixSize = strlen($hash['prefix']) / 2; |
304
|
3 |
|
$length = count($prefixPack) * $prefixSize; |
305
|
3 |
|
$bodyString = "$prefixSize:$length\n" . $prefixPack; |
306
|
3 |
|
$result = $this->checkHash($bodyString); |
307
|
|
|
|
308
|
3 |
|
if ($result['code'] == 200 && !empty($result['data'])) { |
309
|
1 |
|
$malwareShavars = $this->getFullHashes($result['data']); |
310
|
1 |
|
foreach ($malwareShavars as $fullHashes) { |
311
|
1 |
|
foreach ($fullHashes as $fullHash) { |
312
|
1 |
|
if ($fullHash === $hash['full']) { |
313
|
1 |
|
return $hash; |
314
|
|
|
} |
315
|
1 |
|
} |
316
|
1 |
|
} |
317
|
3 |
|
} elseif ($result['code'] == 204 && strlen($result['data']) == 0) { |
318
|
|
|
//204 Means no match |
319
|
1 |
|
} else { |
320
|
1 |
|
throw new SafeBrowsingException( |
321
|
1 |
|
"ERROR: Invalid response returned from Safe Browsing ({$result['code']})" |
322
|
1 |
|
); |
323
|
|
|
} |
324
|
2 |
|
} |
325
|
1 |
|
return false; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* @param string $responseData |
330
|
|
|
* @return array |
331
|
|
|
*/ |
332
|
1 |
|
public function getFullHashes($responseData) |
333
|
|
|
{ |
334
|
1 |
|
$hashesData = []; |
335
|
1 |
|
while (strlen($responseData) > 0) { |
336
|
1 |
|
$splithead = explode("\n", $responseData, 2); |
337
|
|
|
|
338
|
1 |
|
list($listname, $malwareId, $length) = explode(':', $splithead[0]); |
339
|
1 |
|
$data = bin2hex(substr($splithead[1], 0, $length)); |
340
|
1 |
|
while (strlen($data) > 0) { |
341
|
1 |
|
$hashesData[$listname][$malwareId] = substr($data, 0, 64); |
342
|
1 |
|
$data = substr($data, 64); |
343
|
1 |
|
} |
344
|
1 |
|
$responseData = substr($splithead[1], $length); |
345
|
1 |
|
} |
346
|
1 |
|
return $hashesData; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* @param string $url |
351
|
|
|
* @return array |
352
|
|
|
*/ |
353
|
5 |
|
public function getHashesByUrl($url) |
354
|
|
|
{ |
355
|
|
|
//Remove line feeds, return carriages, tabs, vertical tabs |
356
|
5 |
|
$url = trim(str_replace(["\x09", "\x0A", "\x0D", "\x0B"], '', $url)); |
357
|
|
|
//extract hostname |
358
|
5 |
|
$parts = parse_url(strtolower($url)); |
359
|
5 |
|
if (!isset($parts['scheme'])) { |
360
|
|
|
//Add default scheme |
361
|
3 |
|
$parts = parse_url('http://' . $url); |
362
|
3 |
|
} |
363
|
5 |
|
$host = $parts['host']; |
364
|
|
|
|
365
|
|
|
//const |
366
|
5 |
|
$maxCountDomains = 5; |
367
|
|
|
|
368
|
|
|
//Exact hostname in the url |
369
|
5 |
|
$hosts = []; |
370
|
5 |
|
if (filter_var($host, FILTER_VALIDATE_IP)) { |
371
|
1 |
|
$hosts[] = $host . '/'; |
372
|
1 |
|
} else { |
373
|
4 |
|
$domains = explode('.', $host); |
374
|
4 |
|
$countDomains = count($domains); |
375
|
4 |
|
if ($countDomains > $maxCountDomains) { |
376
|
1 |
|
$domains = array_slice($domains, $countDomains - $maxCountDomains, $maxCountDomains); |
377
|
1 |
|
} |
378
|
|
|
|
379
|
4 |
|
while (count($domains) > 1) { |
380
|
4 |
|
$hosts[] = implode('.', $domains) . '/'; |
381
|
4 |
|
array_shift($domains); |
382
|
4 |
|
} |
383
|
|
|
} |
384
|
|
|
|
385
|
5 |
|
$hosts = array_unique($hosts); |
386
|
5 |
|
return $this->getHashesByHosts($hosts); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* @param array $hosts |
391
|
|
|
* @return array |
392
|
|
|
*/ |
393
|
5 |
|
private function getHashesByHosts($hosts) |
394
|
|
|
{ |
395
|
5 |
|
$hashes = []; |
396
|
5 |
|
foreach ($hosts as $host) { |
397
|
5 |
|
$hashes[] = $this->getHashByHost($host); |
398
|
5 |
|
} |
399
|
5 |
|
return $hashes; |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* @param string $host |
404
|
|
|
* @return array |
405
|
|
|
*/ |
406
|
5 |
|
private function getHashByHost($host) |
407
|
|
|
{ |
408
|
|
|
//SHA-256 |
409
|
5 |
|
$hash = hash('sha256', $host); |
410
|
5 |
|
return ["host" => $host, "prefix" => substr($hash, 0, 8), "full" => $hash]; |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* @param array $savedChunks |
415
|
|
|
* @return string |
416
|
|
|
* @throws SafeBrowsingException |
417
|
|
|
*/ |
418
|
11 |
|
private function prepareDownloadsRequest($savedChunks = []) |
419
|
|
|
{ |
420
|
11 |
|
$body = ''; |
421
|
11 |
|
if (count($this->malwareShavars) < 1) { |
422
|
1 |
|
throw new SafeBrowsingException( |
423
|
|
|
'ERROR: Empty malware shavars' |
424
|
1 |
|
); |
425
|
|
|
} |
426
|
|
|
|
427
|
10 |
|
foreach ($this->malwareShavars as $malwareShavar) { |
428
|
10 |
|
if ($savedChunks && isset($savedChunks[$malwareShavar])) { |
|
|
|
|
429
|
|
|
//ydx-malware-shavar;s:18888-19061:a:21355-21687 |
430
|
|
|
|
431
|
1 |
|
$range = ''; |
432
|
1 |
|
if (isset($savedChunks[$malwareShavar]['removed']) |
433
|
1 |
|
&& isset($savedChunks[$malwareShavar]['removed']['min']) |
434
|
1 |
|
&& isset($savedChunks[$malwareShavar]['removed']['max']) |
435
|
1 |
|
&& $savedChunks[$malwareShavar]['removed']['min'] > 0 |
436
|
1 |
|
&& $savedChunks[$malwareShavar]['removed']['max'] > 0 |
437
|
1 |
|
) { |
438
|
1 |
|
$range .= 's:' . $savedChunks[$malwareShavar]['removed']['min'] |
439
|
1 |
|
. '-' . $savedChunks[$malwareShavar]['removed']['max']; |
440
|
1 |
|
} |
441
|
|
|
|
442
|
1 |
|
if (isset($savedChunks[$malwareShavar]['added']) |
443
|
1 |
|
&& isset($savedChunks[$malwareShavar]['added']['min']) |
444
|
1 |
|
&& isset($savedChunks[$malwareShavar]['added']['max']) |
445
|
1 |
|
&& $savedChunks[$malwareShavar]['added']['min'] > 0 |
446
|
1 |
|
&& $savedChunks[$malwareShavar]['added']['max'] > 0 |
447
|
1 |
|
) { |
448
|
1 |
|
if ($range) { |
449
|
1 |
|
$range .= ':'; |
450
|
1 |
|
} |
451
|
1 |
|
$range .= 'a:' . $savedChunks[$malwareShavar]['added']['min'] |
452
|
1 |
|
. '-' . $savedChunks[$malwareShavar]['added']['max']; |
453
|
|
|
|
454
|
1 |
|
$body .= $malwareShavar . ';' . $range . "\n"; |
455
|
1 |
|
} |
456
|
1 |
|
} else { |
457
|
10 |
|
$body .= $malwareShavar . ";\n"; |
458
|
|
|
} |
459
|
10 |
|
} |
460
|
10 |
|
return $body; |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
/** |
464
|
|
|
* Get malwares prefixes data |
465
|
|
|
* |
466
|
|
|
* @param array $savedChunks |
467
|
|
|
* @return array |
468
|
|
|
* @throws SafeBrowsingException |
469
|
|
|
*/ |
470
|
11 |
|
public function getMalwaresData($savedChunks = []) |
471
|
|
|
{ |
472
|
11 |
|
$body = $this->prepareDownloadsRequest($savedChunks); |
473
|
|
|
|
474
|
10 |
|
$response = $this->getChunks($body); |
475
|
10 |
|
$result = []; |
476
|
|
|
|
477
|
10 |
|
$response['data'] = (string) $response['data']; |
478
|
|
|
|
479
|
10 |
|
if (substr_count($response['data'], 'r:pleasereset') > 0) { |
480
|
1 |
|
return 'pleasereset'; |
481
|
|
|
} |
482
|
|
|
|
483
|
9 |
|
$chunksList = []; |
484
|
9 |
|
if (substr_count($response['data'], 'i:') < 1) { |
485
|
1 |
|
throw new SafeBrowsingException( |
486
|
|
|
'ERROR: Incorrect data in list' |
487
|
1 |
|
); |
488
|
|
|
} |
489
|
|
|
|
490
|
8 |
|
$shavarsData = explode('i:', $response['data']); |
491
|
8 |
|
unset($shavarsData[0]); |
492
|
8 |
|
foreach ($shavarsData as $shavar) { |
493
|
8 |
|
$listData = explode("\n", trim($shavar)); |
494
|
8 |
|
$chunksList[array_shift($listData)] = $listData; |
495
|
8 |
|
} |
496
|
8 |
|
foreach ($chunksList as $listName => $list) { |
497
|
8 |
|
$chunksByList = []; |
498
|
8 |
|
foreach ($list as $value) { |
499
|
8 |
|
if (substr_count($value, "u:") > 0) { |
500
|
|
|
try { |
501
|
7 |
|
$chunkData = $this->getChunkByUrl('http://' . trim(str_replace('u:', '', $value))); |
502
|
6 |
|
$processed = $this->parseChunk($chunkData); |
503
|
3 |
|
$chunksByList[$processed['type']][$processed['chunk_num']] = $processed['prefixes']; |
504
|
7 |
|
} catch (NotFoundException $e) { |
505
|
1 |
|
continue; |
506
|
|
|
} |
507
|
4 |
|
} elseif (substr_count($value, "ad:") > 0) { |
508
|
1 |
View Code Duplication |
if (substr_count($value, ',') > 0) { |
|
|
|
|
509
|
1 |
|
$ranges = explode(',', trim(str_replace("ad:", "", $value))); |
510
|
1 |
|
$rangesData = []; |
511
|
1 |
|
foreach ($ranges as $range) { |
512
|
1 |
|
list($min, $max) = explode('-', $range); |
513
|
1 |
|
$rangesData[] = [ |
514
|
1 |
|
'min' => $min, |
515
|
|
|
'max' => $max |
516
|
1 |
|
]; |
517
|
1 |
|
} |
518
|
1 |
|
$chunksByList['delete_added_ranges'] = $rangesData; |
519
|
1 |
|
} else { |
520
|
1 |
|
$range = trim(str_replace("sd:", "", $value)); |
521
|
1 |
|
list($min, $max) = explode('-', $range); |
522
|
1 |
|
$chunksByList['delete_added_ranges'] = [ |
523
|
|
|
[ |
524
|
1 |
|
'min' => $min, |
525
|
|
|
'max' => $max |
526
|
1 |
|
] |
527
|
1 |
|
]; |
528
|
|
|
} |
529
|
1 |
|
} elseif (substr_count($value, "sd:") > 0) { |
530
|
1 |
View Code Duplication |
if (substr_count($value, ',') > 0) { |
|
|
|
|
531
|
1 |
|
$ranges = explode(',', trim(str_replace("sd:", "", $value))); |
532
|
1 |
|
$rangesData = []; |
533
|
1 |
|
foreach ($ranges as $range) { |
534
|
1 |
|
list($min, $max) = explode('-', $range); |
535
|
1 |
|
$rangesData[] = [ |
536
|
1 |
|
'min' => $min, |
537
|
|
|
'max' => $max |
538
|
1 |
|
]; |
539
|
1 |
|
} |
540
|
1 |
|
$chunksByList['delete_removed_ranges'] = $rangesData; |
541
|
1 |
|
} else { |
542
|
1 |
|
$range = trim(str_replace("sd:", "", $value)); |
543
|
1 |
|
list($min, $max) = explode('-', $range); |
544
|
1 |
|
$chunksByList['delete_removed_ranges'] = [ |
545
|
|
|
[ |
546
|
1 |
|
'min' => $min, |
547
|
|
|
'max' => $max |
548
|
1 |
|
] |
549
|
1 |
|
]; |
550
|
|
|
} |
551
|
1 |
|
} |
552
|
5 |
|
} |
553
|
|
|
|
554
|
5 |
|
$result[$listName] = $chunksByList; |
555
|
5 |
|
} |
556
|
|
|
|
557
|
5 |
|
return $result; |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Parsing chunk |
562
|
|
|
* |
563
|
|
|
* @param string $data |
564
|
|
|
* @return array |
565
|
|
|
* @throws SafeBrowsingException |
566
|
|
|
*/ |
567
|
6 |
|
private function parseChunk($data) |
568
|
|
|
{ |
569
|
6 |
|
$data = trim($data); |
570
|
6 |
|
if (strlen($data) === 0) { |
571
|
1 |
|
throw new SafeBrowsingException( |
572
|
1 |
|
'ERROR: Incorrect chunk data "' . $data . '"' |
573
|
1 |
|
); |
574
|
|
|
} |
575
|
|
|
|
576
|
5 |
|
$splitHead = explode("\n", $data, 2); |
577
|
5 |
|
$chunkInfo = explode(':', $splitHead[0]); |
578
|
5 |
|
list($type, $chunkNum, $hashLen, $chunkLen) = $chunkInfo; |
579
|
|
|
|
580
|
5 |
|
if ($chunkLen < 1) { |
581
|
1 |
|
throw new SafeBrowsingException( |
582
|
1 |
|
'ERROR: In chunkNum "' . $chunkNum . '" incorrect chunk length "' . $chunkLen . '"' |
583
|
1 |
|
); |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
//Convert to hex for easy processing |
587
|
|
|
//First get chunkData according to length |
588
|
4 |
|
$chunkData = bin2hex(substr($splitHead[1], 0, $chunkLen)); |
589
|
4 |
|
if ($type == 'a') { |
590
|
1 |
|
$prefixes = []; |
591
|
1 |
|
while (strlen($chunkData) > 0) { |
592
|
1 |
|
$prefixes[] = substr($chunkData, 0, 8); |
593
|
1 |
|
$count = hexdec(substr($chunkData, 8, 2)); |
594
|
1 |
|
$chunkData = substr($chunkData, 10); |
595
|
1 |
View Code Duplication |
for ($i = 0; $i < $count; $i++) { |
|
|
|
|
596
|
1 |
|
$chunkData = substr($chunkData, (($hashLen * 2))); |
597
|
1 |
|
} |
598
|
1 |
|
} |
599
|
|
|
|
600
|
|
|
return [ |
601
|
1 |
|
'type' => 'added', |
602
|
1 |
|
'chunk_num' => $chunkNum, |
603
|
|
|
'prefixes' => $prefixes |
604
|
1 |
|
]; |
605
|
3 |
|
} elseif ($type == 's') { |
606
|
2 |
|
$prefixes = []; |
607
|
2 |
|
while (strlen($chunkData) > 0) { |
608
|
2 |
|
$prefixes[] = substr($chunkData, 0, 8); |
609
|
2 |
|
$count = hexdec(substr($chunkData, 8, 2)); |
610
|
2 |
|
$chunkData = substr($chunkData, 10); |
611
|
2 |
|
if ($count > 0) { |
612
|
1 |
View Code Duplication |
for ($i = 0; $i < $count; $i++) { |
|
|
|
|
613
|
1 |
|
$chunkData = substr($chunkData, (($hashLen * 2) + 8)); |
614
|
1 |
|
} |
615
|
1 |
|
} else { |
616
|
1 |
|
$chunkData = substr($chunkData, 8); |
617
|
|
|
} |
618
|
2 |
|
} |
619
|
|
|
|
620
|
|
|
return [ |
621
|
2 |
|
'type' => 'removed', |
622
|
2 |
|
'chunk_num' => $chunkNum, |
623
|
|
|
'prefixes' => $prefixes |
624
|
2 |
|
]; |
625
|
|
|
} else { |
626
|
1 |
|
throw new SafeBrowsingException( |
627
|
1 |
|
'ERROR: In chunkNum "' . $chunkNum . '" incorrect type "' . $type . '"' |
628
|
1 |
|
); |
629
|
|
|
} |
630
|
|
|
} |
631
|
|
|
} |
632
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.