1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
xscan.php
См. документацию.
1<?php
2
3use PhpParser\Node;
4use PhpParser\NodeFinder;
7
9
10class CBitrixXscan
11{
12 static $var = '\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*';
13 static $spaces = "[ \r\t\n]*";
14 static $request = '(?:_REQUEST|_GET|_POST|_COOKIE|_SERVER(?!\[[\'"]DOCUMENT_ROOT[\'"]\])|_FILES)';
15 static $functions = '(?:parse_str|hex2bin|str_rot13|base64_decode|url_decode|str_replace|str_ireplace|preg_replace|move_uploaded_file)';
16 static $evals = ['eval', 'assert', 'create_function', 'exec', 'passthru', 'pcntl_exec', 'popen', 'proc_open', 'set_include_path', 'shell_exec', 'system'];
17 static $evals_reg = '(?:assert|call_user_func|call_user_func_array|create_function|eval|exec|ob_start|passthru|pcntl_exec|popen|proc_open|set_include_path|shell_exec|system)';
18 static $black_reg = '(https?://[0-9a-z\-]+\.pw/|password_verify|https?://(?:sw\.)?bitrix\.dev|wp-config|wp-admin|wp-login|deprecated-media-js|customize-menus-rtl|adminer_errors|/etc/passwd|/etc/hosts|mysql_pdo|__halt_compiler|/bin/sh|registerPHPFunctions)';
19 static $mehtods = [
20 'Bitrix\Im\Call\Auth::authorizeById',
21 'Bitrix\ImOpenLines\Controller\Widget\Filter\Authorization::authorizeById',
22 'Bitrix\Imopenlines\Widget\Auth::authorizeById',
23 'Bitrix\Sale\Delivery\Services\Automatic::createConfig',
24 'Bitrix\Sender\Internals\DataExport::toCsv',
25 'Bitrix\Sender\Internals\QueryController\Base::call',
26 'CAllSaleBasket::ExecuteCallbackFunction',
27 'CAllSaleOrder::PrepareSql',
28 'CBPHelper::UsersStringToArray',
29 'CControllerClient::RunCommand',
30 'CMailFilter::CheckPHP',
31 'CMailFilter::DoPHPAction',
32 'CRestUtil::makeAuth',
33 'CSaleHelper::getOptionOrImportValues',
34 'CWebDavTools::sendJsonResponse',
35 ];
36 private const false_positives = ['9223e925409363b7db262cfea1b6a7e2', '4d2cb64743ff3647bad4dea540d5b08e', 'd40c4da27ce1860c111fc0e68a4b39b5',
37 'ef9287187dc22a6ce47476fd80720878', '13484affcdf9f45d29b61d732f8a5855', '4a171d5dc7381cce26227c5d83b5ba0c', 'b41d3b390f0f5ac060f9819e40bda7eb',
38 '40142320d26a29586dc8528cfb183aac', 'f454f39a15ec9240d93df67536372c1b', '29bba835e33ab80598f88e438857f342', '77cdd8164d4940cb6bfaac906383a766',
39 '5b3425a6ff518fa2337b373e1c799959', '7c60ccaee2b919c9e6b16b307eb80dab', 'bde611db5c3545005a7270edcffd8dc2', '4d6b616171dbf06ff57d1dab8ea6bbce',
40 'a85abce54b4deb8cb157438dddca5a7c', 'de4f7ee97d421cf14d3951c0b4e5c2dd', '379918e8f6486ce9a7bb2ed5a69dbee6', '7ac4a2afcee04e683b092eb9402ee7ed',
41 '1d5eb769111fc9c7be2021300ee5740e', 'f2357a1fe8e984052b6ee69933d467dc', 'a9158139e1a619ca8cc320cf4469c250'];
42
43
44 static $default_config = ['request' => true, 'from_request' => true, 'crypted' => true, 'files' => true,
45 'assigned' => false, 'params' => false, 'concat' => true, 'hardcoded' => false, 'value' => true, 'recursive' => false];
46 public static $database = false;
47 public $db_log = null;
48 public $db_file = null;
49 public $doc_root = null;
50 public $start_time = null;
51 public $time_limit = null;
52 public $base_dir = null;
53 public $break_point = null;
54 public $skip_path = null;
55 public $found = false;
56 public $db_error = false;
57 public $progress = 0;
58 public $total = 0;
59 public $collect_exceptions = true;
60 private $errors = [];
61
62 static $cryptors = ['rot13', 'str_rot13', 'base32_decode', 'base64_decode', 'gzinflate', 'unserialize',
63 'url_decode', 'pack', 'unpack', 'hex2bin', 'bzdecompress', 'gzuncompress', 'lzf_decompress', 'strrev'];
64 static $string_change = ['preg_replace', 'str_ireplace', 'str_replace', 'substr', 'strrev'];
65 static $scoring = [
66 '[337] strings from black list' => [0.9],
67 '[630] long line' => [0.4],
68 '[321] base64_encoded code' => [0.8],
69 '[610] strange vars' => [0.5],
70 '[302] preg_replace_eval' => [0.9],
71 '[663] binary data' => [0.75],
72 '[640] strange exif' => [0.6],
73 '[500] php wrapper' => [0.7],
74 '[665] chars by code' => [0.8],
75 '[665] encoded code' => [0.8],
76 '[303] create_function' => [0.8, 0.8, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
77 '[300] eval' => [1, 0.4, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.9],
78 '[302] unsafe callable argument' => [0.8, 0.8, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
79 '[307] danger method' => [1, 0.4, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.9],
80 '[662] function return as a function' => [0.9, 0.8, 0.1, 1, 1, 0.3, 0.7, 0.7, 0.8],
81 '[663] strange function' => [1, 1, 0.1, 1, 1, 0.8, 0.7, 0.8, 0.9],
82 '[302] eregi' => [0.8, 0.8, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
83 '[887] backticks' => [1, 0.8, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
84 '[600] strange include' => [0.8, 0.8, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
85 '[660] array member as a function' => [0.9, 0.8, 0.1, 1, 0.8, 0.3, 0.5, 0.8, 0.8],
86 '[298] mysql function' => [0.6, 0.8, 0.1, 1, 0.8, 0.9, 0.7, 0.8, 0.8],
87 '[300] command injection' => [1, 0.7, 0.1, 1, 0.8, 0.6, 0.7, 0.8, 0.9],
88 '[299] mail function' => [0.6, 0.8, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
89 '[650] variable as a function' => [0.9, 0.8, 0.1, 1, 0.8, 0.3, 0.5, 0.8, 0.8],
90 '[304] filter_callback' => [0.6, 0.8, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
91 '[305] strange function and eval' => [0.8, 0.8, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
92 '[301] file operations' => [0.5, 0.4, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
93 '[302] file operations' => [0.8, 0.4, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
94 '[400] bitrix auth' => [0.9, 0.8, 1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
95 '[308] no prolog file operations' => [0.5, 0.4, 0.1, 1, 0.8, 0.3, 0.7, 0.8, 0.8],
96 ];
97 private $results = [];
98 private $tags = [];
99 private $result_collection = null;
100 private $score = 1;
101
102 function __construct($progress = 0, $total = 0, $start_path='')
103 {
104 $this->doc_root = rtrim($_SERVER['DOCUMENT_ROOT'], '/');
105 $this->base_dir = $start_path;
106
107 $this->result_collection = new \Bitrix\Security\XScanResults();
108 $this->db_file = $this->doc_root . '/bitrix/modules/security/data/database.json';
109 $this->start_time = time();
110
111 $mem = (int)ini_get('memory_limit');
112 $this->time_limit = ini_get('max_execution_time') ?: 30;
113 $this->time_limit = min($this->time_limit, 30);
114 $this->time_limit = $this->time_limit * 0.7;
115
116 $this->db_error = $mem >= 0 && $mem <= 128;
117
118 $this->progress = $progress;
119 $this->total = $total;
120
121 $this->parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7);
122 $this->nodeFinder = new NodeFinder;
123 $this->errorHandler = new PhpParser\ErrorHandler\Collecting;
124 $this->pprinter = new PhpParser\PrettyPrinter\Standard;
125
126 $errs = XScanResultTable::getList(['select' => ['SRC'], 'filter' => ['TYPE' => 'file', 'MESSAGE' => 'error']]);
127
128 while ($row = $errs->fetch())
129 {
130 $this->errors[] = $row['SRC'];
131 }
132 }
133
134 function start($start_path)
135 {
136 $this->clean();
137 $this->CheckTriggers();
138 $this->CheckEvents();
139 $this->CheckAgents();
140 $this->CheckTemplates();
141 $this->Search($start_path, 'count');
142 }
143
144
145 private function clean()
146 {
147 global $DB;
148 $DB->Query("TRUNCATE TABLE b_sec_xscan_results", true);
149 $this->errors = [];
150 }
151
152 function CheckTriggers()
153 {
154 global $DB;
155
156 if ($r = $DB->Query('SHOW triggers;', true))
157 {
158 if ($row = $r->Fetch())
159 {
160 $result = (new XScanResult)->setType('trigg')->setSrc($row['Trigger'])->setScore(1)->setMessage('[040] triggers');
161 $this->result_collection[] = $result;
162 }
163 }
164 }
165
166 function CheckEvents()
167 {
168 global $DB;
169
170 $r = $DB->Query('SELECT * from b_module_to_module');
171
172 while ($row = $r->Fetch())
173 {
174 if ($row['TO_CLASS'] && $row['TO_METHOD'])
175 {
176 $class_method = trim($row['TO_CLASS'] . '::' . $row['TO_METHOD'], '\\');
177 $found = false;
178 foreach (self::$mehtods as $mtd)
179 {
180 if (stripos($class_method, $mtd) !== false)
181 {
182 $found = true;
183 break;
184 }
185 }
186
187 if ($found)
188 {
189 $result = (new XScanResult)->setType('event')->setSrc($row['ID'])->setScore(1)->setMessage('[050] dangerous method at event, check arguments');
190 $this->result_collection[] = $result;
191 }
192 }
193 }
194 }
195
196 function CheckAgents()
197 {
198 global $DB;
199
200 $r = $DB->Query('SELECT * from b_agent');
201
202 while ($row = $r->Fetch())
203 {
204 if (!$row['NAME'])
205 {
206 continue;
207 }
208
209 $src = "<?php\n" . $row['NAME'] . "\n?>";
210 $this->CheckCode($src);
211
212 if ($this->results)
213 {
214 $message = [];
215 foreach ($this->results as $res)
216 {
217 $message[] = $res['subj'];
218 }
219
220 if (is_array($message))
221 {
222 $message = implode(' <br> ', array_unique($message));
223 }
224
225 $result = (new XScanResult)->setType('agent')->setSrc($row['ID'])->setScore(1)->setMessage($message);
226 $this->result_collection[] = $result;
227 }
228 }
229 }
230
231 function CheckTemplates()
232 {
233 global $DB;
234
235 $r = $DB->Query('SELECT * from b_site_template');
236
237 while ($row = $r->Fetch())
238 {
239 if (!$row['CONDITION'])
240 {
241 continue;
242 }
243
244 $src = "<?php\n" . $row['CONDITION'] . "\n?>";
245 $this->CheckCode($src);
246
247 if ($this->results)
248 {
249 $message = [];
250 foreach ($this->results as $res)
251 {
252 $message[] = $res['subj'];
253 }
254
255 if (is_array($message))
256 {
257 $message = implode(' <br> ', array_unique($message));
258 }
259
260 $result = (new XScanResult)->setType('tmpl')->setSrc($row['ID'])->setScore(1)->setMessage($message);
261 $this->result_collection[] = $result;
262 }
263 }
264 }
265
266 static function crc($a)
267 {
268 return crc32(implode('|', $a));
269 }
270
271 static function CountBlocks($src, &$result)
272 {
273 $code = strtolower($src);
274
275 $code = preg_replace('~<\?(php|=)?~', '', $code);
276 $code = preg_replace('~<[^>$()]*?>~', '', $code);
277 $code = str_replace('?>', '', $code);
278 $code = preg_split('~[\n;{}(),\s.]+~', $code);
279
280 $arr = [];
281
282 foreach ($code as $chunk)
283 {
284 $chunk = trim($chunk);
285
286 if ($chunk !== '')
287 {
288 $arr[] = $chunk;
289 }
290 }
291
292 $crcs = [];
293
294 if (!empty($arr))
295 {
296 while (count($arr) < 3)
297 {
298 $arr[] = $arr[0];
299 }
300
301 $block = [$arr[0], $arr[1], $arr[2]];
302 $crcs[] = self::crc($block);
303
304 $end = count($arr) - 1;
305 for ($i = 3; $i <= $end; $i++)
306 {
307 $block = [$block[1], $block[2], $arr[$i]];
308 $crcs[] = self::crc($block);
309 }
310 }
311
312 $result = array_unique($crcs);
313
314 unset($code);
315 unset($arr);
316 unset($crcs);
317 }
318
319 function SearchInDataBase($src)
320 {
321 $result = [];
322 $found = [];
323 self::CountBlocks($src, $result);
324
325 foreach ($result as $token)
326 {
327 if (isset(self::$database['tokens'][$token]))
328 {
329 foreach (self::$database['tokens'][$token] as $shell)
330 {
331 if (!isset($found[$shell]))
332 {
333 $found[$shell] = 0;
334 }
335 $found[$shell] += 1;
336 }
337 }
338 }
339
340 $bFound = false;
341
342 foreach ($found as $key => $value)
343 {
344 if ($value / self::$database['shells'][$key] > 0.8)
345 {
346 $bFound = true;
347 break;
348 }
349 }
350
351 unset($result);
352 unset($found);
353
354 return $bFound;
355 }
356
357 function addResult($subj, $code, $score, $checksum = '')
358 {
359 $this->results[] = ['subj' => $subj, 'code' => $code, 'score' => $score, 'checksum' => $checksum];
360 }
361
362
363 static function detectDocRoot($file_path)
364 {
365 static $doc_root;
366
367 if (!$doc_root || strpos($file_path, $doc_root . '/') !== 0)
368 {
369 $path = explode('/', ltrim($file_path, '/'));
370 $doc_root = '';
371 $found = false;
372 foreach ($path as $comp)
373 {
374 if (is_file($doc_root . '/bitrix/.settings.php'))
375 {
376 $found = true;
377 break;
378 }
379 $doc_root .= '/' . $comp;
380 }
381
382 if (!$found)
383 {
384 $doc_root = '';
385 }
386 }
387 return $doc_root;
388 }
389
390
391 static function getVersion($moduleName, $doc_root)
392 {
393 $moduleName = preg_replace("/[^a-zA-Z0-9_.]+/i", "", trim($moduleName));
394 if ($moduleName == '')
395 return false;
396
397
398 if ($moduleName == 'main')
399 {
400 $content = file_get_contents("$doc_root/bitrix/modules/main/classes/general/version.php");
401
402 }
403 else
404 {
405 $content = file_get_contents("$doc_root/bitrix/modules/$moduleName/install/version.php");
406 }
407
408 preg_match('/\d+\.\d+\.\d+/', $content, $m);
409 $version = count($m)? $m[0]: false;
410
411 return $version;
412 }
413
414 static function getHashes($module, $version)
415 {
416 global $DB;
417
418 static $static_cache;
419
420 if (!is_array($static_cache)){
421 $static_cache = [];
422 }
423
424 $key = $module . '_' . $version;
425
426 if (isset($static_cache[$key]))
427 {
428 return $static_cache[$key];
429 }
430
431 $cache = \Bitrix\Main\Data\Cache::createInstance();
432
433 if ($cache && $cache->initCache(12 * 3600, 'xscan_' . $key, 'xscan'))
434 {
435 $result = $cache->getVars();
436 $static_cache[$key] = $result;
437 return $static_cache[$key];
438 }
439 else
440 {
441 $sHost = \Bitrix\Main\Application::getInstance()->getLicense()->getDomainStoreLicense();
442 $dbtype = mb_strtolower($DB->type);
443 $http = new \Bitrix\Main\Web\HttpClient();
444
445 $data = $http->get($sHost . "/bitrix/updates/checksum.php?check_sum=Y&module_id={$module}&ver={$version}&dbtype={$dbtype}&mode=2");
446 $result = @unserialize(gzinflate($data), ['allowed_classes' => false]);
447
448 $static_cache[$key] = [];
449
450 if (is_array($result)) {
451 $result = array_filter($result, function ($value) {
452 return preg_match('/\.(php|js)$/', $value);
453 }, ARRAY_FILTER_USE_KEY);
454
455 $cache->startDataCache();
456 $cache->endDataCache($result);
457
458 $static_cache[$key] = $result;
459 }
460 return $static_cache[$key];
461 }
462
463 return false;
464 }
465
466 static function checkByHash($file_path)
467 {
468 static $map = [];
469
470 if (!preg_match("~/bitrix/(?:modules|components|wizards|templates/bitrix24|blocks/bitrix|js)/~", $file_path))
471 {
472 return null;
473 }
474
475 $doc_root = static::detectDocRoot($file_path);
476
477 if(!$doc_root)
478 {
479 return null;
480 }
481
482 $module = '';
483 $file = '';
484
485 if (preg_match("~bitrix/modules/(.+?)/(.+)~", $file_path, $matches))
486 {
487 $file = $matches[2];
488 $module = $matches[1];
489 }
490 elseif (preg_match("~bitrix/js/(.+?)/(.+)~", $file_path, $matches))
491 {
492 $file = 'install/js/' . $matches[1] . '/' .$matches[2];
493 $module = $matches[1];
494 }
495 elseif(preg_match("~/bitrix(/components/bitrix/mobile\.tasks\..+)~", $file_path, $matches))
496 {
497 $file = 'install' . $matches[1];
498 $module = 'tasksmobile';
499 }
500 else
501 {
502 $len = strlen($doc_root . '/bitrix');
503 $file = 'install' . substr($file_path, $len);
504 preg_match('~^install/.+?/.+?/.+?/~', $file, $key);
505 $key = $key ? rtrim($key[0], '/') : '';
506
507 if (!isset($map[$doc_root]))
508 {
509 $map[$doc_root] = [];
510
511 foreach(glob("{$doc_root}/bitrix/modules/*") as $dir){
512
513 if (is_dir($dir))
514 {
515 $mod = basename($dir);
516 $len = strlen($dir) + 1;
517
518 foreach (['components', 'wizards', 'blocks', 'templates'] as $sub)
519 {
520 if (!is_dir("{$dir}/install/{$sub}"))
521 {
522 continue;
523 }
524
525 foreach(glob("{$dir}/install/{$sub}/*/*") as $c)
526 {
527 $c = substr($c, $len);
528 $map[$doc_root][$c] = $mod;
529 }
530 }
531 }
532 }
533 }
534
535 if (isset($map[$doc_root]) && isset($map[$doc_root][$key]))
536 {
537 $module = $map[$doc_root][$key];
538 }
539 }
540
541 if (!$module || !$file)
542 {
543 return null;
544 }
545
546 $version = static::getVersion($module, $doc_root);
547 if (!$version)
548 {
549 return null;
550 }
551
552 $hashes = static::getHashes($module, $version);
553
554 if ($hashes && isset($hashes[$file]))
555 {
556 return $hashes[$file] === md5_file($file_path);
557 }
558
559 return null;
560 }
561
562 function CheckFile($file_path)
563 {
564 $this->results = [];
565 $this->tags = [];
566
567 static $me;
568 if (!$me)
569 {
570 $me = realpath(__FILE__);
571 }
572 if (realpath($file_path) == $me)
573 {
574 return false;
575 }
576
577 if (in_array($file_path, $this->errors))
578 {
579 return false;
580 }
581
582 if ($this->SystemFile($file_path))
583 {
584 return false;
585 }
586
587 # CODE 100
588 if (basename($file_path) == '.htaccess')
589 {
590 $src = file_get_contents($file_path);
591 $res = preg_match('#<(\?|script)#i', $src, $regs);
592 if ($res)
593 {
594 $this->addResult('[100] htaccess', $regs[0], 1);
595 return true;
596 }
597
598 $res = preg_match('#\bwp-[a-z]+\.php#i', $src, $regs);
599 if ($res)
600 {
601 $this->addResult('[100] htaccess', $regs[0], 1);
602 return true;
603 }
604
605 if (stripos($src, '(index.php|cache.php)') !== false)
606 {
607 $this->addResult('[100] htaccess', '(index.php|cache.php)', 1);
608 return true;
609 }
610
611 if (preg_match_all('#x-httpd-php[578]?\s+(.+)#i', $src, $regs))
612 {
613 foreach ($regs[1] as $i => $val)
614 {
615 $val = preg_split('/\s+/', $val);
616 foreach ($val as $ext)
617 {
618 $ext = trim(strtolower($ext), '"\'');
619 if (!in_array($ext, ['.php', '.php5', '.php7', '.html', '']))
620 {
621 $this->addResult('[100] htaccess', $regs[0][$i], 1);
622 return true;
623 }
624 }
625 }
626 }
627
628 return false;
629 }
630
631 # CODE 110
632 if (preg_match('#^/upload/.*\.php$#i', str_replace($this->doc_root, '', $file_path)))
633 {
634 $this->addResult('[110] php file in upload dir', '', 1);
635 return true;
636 }
637
638 $hash_check = static::checkByHash($file_path);
639
640 if ($hash_check === true)
641 {
642 return false;
643 }
644 elseif ($hash_check === false)
645 {
646 $this->addResult('[201] file was modified', '', 1);
647 }
648
649 if (!preg_match('#\.php[578]?$#i', $file_path, $regs))
650 {
651 return !empty($this->results);
652 }
653
654 # CODE 200
655 if (($src = @file_get_contents($file_path)) === false)
656 {
657 $this->addResult('[200] read error', '', 1);
658 return true;
659 }
660
661 $this->CheckCodeInternal($src, $file_path);
662 $tot = 1;
663
664 foreach ($this->results as $value)
665 {
666 $tot = $tot * (1 - $value['score']);
667 }
668
669 $tot = round(1 - $tot, 2);
670
671 $this->score = $tot;
672
673 return !empty($this->results);
674 }
675
676 function CalcChecksum($file_path, $code, $subj)
677 {
678
679 $doc_root = static::detectDocRoot($file_path);
680
681 if ($doc_root)
682 {
683 $file_path = substr($file_path, strlen($doc_root));
684 }
685
686 if (strpos($file_path, '/') !== 0)
687 {
688 $file_path = '/' . $file_path;
689 }
690
691 $file_path = preg_replace('#^/bitrix/modules/[a-z0-9._]+/install/components/bitrix#', '/bitrix/components/bitrix', $file_path);
692 $checksum = md5($file_path . '|' . trim($code) . '|' . $subj);
693
694 return $checksum;
695 }
696
697 function IsFalsePositive($checksum)
698 {
699 return in_array($checksum, self::false_positives, true);
700 }
701
702 function getResult()
703 {
704 return $this->results;
705 }
706
707 function getErrors()
708 {
709 return $this->errors;
710 }
711
712 function setErrors($val)
713 {
714 $this->errors = $val;
715 }
716
717 function getTags()
718 {
719 return $this->tags;
720 }
721
722 function getScore()
723 {
724 return $this->score;
725 }
726
727 function CheckCode(&$src, $file_path = false)
728 {
729 $this->results = [];
730 $this->tags = [];
731 return $this->CheckCodeInternal($src, $file_path);
732 }
733
734 private function load_db(){
735
736 if (self::$database || $this->db_error)
737 {
738 return;
739 }
740
741 $cache = \Bitrix\Main\Data\Cache::createInstance();
742
743 if ($cache && $cache->initCache(12 * 3600, 'xscan_db', 'xscan'))
744 {
745 $result = $cache->getVars();
746 }
747 else
748 {
749 $http = new \Bitrix\Main\Web\HttpClient();
750 $data = $http->get("https://wwall.bitrix.info/database.gz");
751 $result = $http->getStatus() == 200 && $data ? json_decode(gzuncompress($data), true) : 'error';
752 $result = is_array($result) ? $result : 'error';
753 $cache->startDataCache();
754 $cache->endDataCache($result);
755 unset($data);
756 }
757
758 if (is_array($result) && isset($result['tokens']) && isset($result['shells']))
759 {
760 self::$database = $result;
761 unset($result);
762 }
763 elseif (is_file($this->db_file))
764 {
765 $tmp = file_get_contents($this->db_file);
766 self::$database = json_decode($tmp, true);
767 unset($tmp);
768 }
769 else
770 {
771 $this->db_error = true;
772 }
773
774 }
775
776 private function CheckCodeInternal(&$src, $file_path = false)
777 {
778 $file_path = $file_path ? $file_path : '';
779
780 $this->load_db();
781
782 $code = preg_replace("/<\?=/", "<?php echo ", $src);
783 $code = preg_replace("/<\?(?!php)/", "<?php ", $code);
784 $code = preg_replace("/else if\s*\‍(/", "elseif (", $code); // crutch
785
786 # OBFUSCATORS
787
788 $cmt = '';
789 if (
790 ($cmt = '$$') && substr_count($code, '${${') > 0 ||
791 ($cmt = 'vars') && preg_match_all('/(?:\$|function\s+)(?:[o0]{4,}|[il]{4,})/i', $code) > 3 ||
792 ($cmt = 'goto') && preg_match_all('/goto\s+[0-9A-Z]+\s*;/i', $code) > 2 ||
793 ($cmt = 'globals') && preg_match_all('/\$GLOBALS\s*\[["\'][0-9_]+["\']\]/', $code) > 3 ||
794 ($cmt = 'base64') && preg_match_all("/base64_decode\s*\‍(\s*[^$].{3,14}\‍)/i", $code) > 3 ||
795 ($cmt = 'base64') && preg_match_all("~(?:base64_decode\s*\‍(\s*)(?:(['\"])[0-9a-z=+\/_]{1,20}\\1(?:\s*\.\s*)?){2,}~i", $code) > 3 ||
796 ($cmt = 'functions') && preg_match_all("/function\s+_\w{1,3}\b/i", $code) > 3 ||
797 ($cmt = 'concat') && preg_match_all("~(?:(?:(['\"])[0-9a-z=+\/_]{1,20}\\1|\\$?[0-9a-z_]+\‍(\‍))(?:\s*\.\s*)?){3,}~i", $code) > 20 ||
798 // ($cmt = 'len') && strlen($code) / max(substr_count($code, "\n"), 1) > 500 ||
799 // ($cmt = 'base_strings') && preg_match_all('~[0-9A-Z+/]{80,100}~i', $code) > 5 ||
800 ($cmt = 'urlenc') && preg_match_all('/(%[0-9A-Z]{2}){80,100}/i', $code) > 2 ||
801 ($cmt = 'base64_keys') && substr_count($code, "[base64") > 1 ||
802 ($cmt = 'unicode') && preg_match_all('~[\p{So}\p{Sk}\p{Nl}]{4,}~u', $code) > 2 /* ||
803 ($cmt = 'long_space') && preg_match('/\t{30,}+(?:[()$]|\S.*?[()$])/', $code) ||
804 ($cmt = 'long_space') && preg_match('/[\t ]{80,}+(?:[()$]|\S.*?[()$])/', $code) */
805 )
806 {
807 $this->tags[] = defined('XSCAN_DEBUG') ? "obfuscator [$cmt]" : "obfuscator";
808 if (in_array($cmt, ['$$', 'vars', 'goto', 'globals', 'base64', 'functions', 'concat', 'unicode']))
809 {
810 $this->addResult('[001] obfuscator', '', 0.6);
811 }
812 }
813 else
814 {
815 $comments = [];
816 preg_match_all('~/\*(.+?)\*/~', $code, $comments);
817 $cnt = 0;
818 $comments = $comments ? $comments[1] : [];
819
820 $comments = array_unique($comments);
821
822 foreach ($comments as $comment)
823 {
824 $comment = trim($comment);
825 if (strlen($comment) <= 15 && preg_match('~([A-Za-z\s_]++|[0-9]++|[!@#$%^&():;`<>?,.{}|\~[\]+-=?]){4,}~', $comment))
826 {
827 $cnt += 1;
828 if ($cnt > 20)
829 {
830 $this->tags[] = defined('XSCAN_DEBUG') ? "obfuscator [comments]" : "obfuscator";
831 $this->addResult('[001] obfuscator', '', 0.6);
832 break;
833 }
834 }
835 }
836
837 unset($comments);
838
839 if ($cnt < 20)
840 {
841 $funcsVars = [];
842 preg_match_all('/(?:\$|function\s+)([0-9a-z_]++)/i', $code, $funcsVars);
843 $cnt = 0;
844 $funcsVars = $funcsVars ? $funcsVars[1] : [];
845 $funcsVars = array_unique($funcsVars);
846
847 foreach ($funcsVars as $value)
848 {
849 $value = str_replace('24', '', $value); // crutch
850 if (preg_match('/\d/', $value) && preg_match('/_\d|(?:[a-z_]++|[A-Z_]++|[0-9]++){4,}/', $value))
851 {
852 $cnt++;
853 }
854 }
855
856 if (count($funcsVars) && $cnt / count($funcsVars) > 0.5)
857 {
858 $this->tags[] = defined('XSCAN_DEBUG') ? "obfuscator [rand_names]" : "obfuscator";
859 }
860 unset($funcsVars);
861 }
862 }
863
864 if (strpos($file_path, '/bitrix/modules/main/') !== false)
865 {
866 $this->tags[] = 'core';
867 }
868
869 static $included;
870 if (!$included)
871 {
872 $included = get_included_files();
873 }
874
875 if (in_array($file_path, $included) || in_array(realpath($file_path), $included)){
876 $this->tags[] = "included";
877 }
878
879 if (preg_match('~/bitrix/(?:modules|components)/[0-9a-z_]+\.[0-9a-z_]+/~i', $file_path) ||
880 preg_match('~/bitrix/components/(?!bitrix/)~i', $file_path))
881 {
882 $this->tags[] = 'marketplace';
883 }
884
885 if (preg_match('/(?:[a-z_]++|[0-9]++){4,}/i', $file_path))
886 {
887 $this->tags[] = 'random_name';
888 }
889
890 if (preg_match('~/lang/~i', $file_path))
891 {
892 $this->tags[] = 'lang';
893 }
894
895 if (preg_match('~/\.~i', $file_path))
896 {
897 $this->tags[] = 'hidden';
898 }
899
900 if (strpos($file_path, '/bitrix/modules/') === false &&
901 strpos($file_path, '/upload/') === false &&
902 strpos($file_path, '/bitrix/php_interface/') === false &&
903 strpos($src, 'B_PROLOG_INCLUDED') === false &&
904 strpos($src, '/bitrix/header.php') === false &&
905 strpos($src, '/bitrix/modules/main/start.php') === false &&
906 strpos($src, '/bitrix/modules/main/include/prolog') === false &&
907 strpos($src, '/bitrix/modules/main/include/mainpage.php') === false &&
908 strpos($src, '/bitrix/main/include/routing_index.php') === false
909 )
910 {
911 $this->tags[] = 'no_prolog';
912
913 if (preg_match('/copy\s*\‍(|file_put_contents|move_uploaded_file|fwrite|fputs/i', $src, $m))
914 {
915 $subj = '[308] no prolog file operations';
916 $checksum = $this->CalcChecksum($file_path, $m[0], $subj);
917 if (!$this->IsFalsePositive($checksum))
918 {
919 $this->addResult($subj, $m[0], self::CalcCrit($subj), $checksum);
920 }
921 }
922 }
923
924 $parser = $this->parser;
925 $errorHandler = $this->errorHandler;
926 $pprinter = $this->pprinter;
927
928 $errorHandler->clearErrors();
929
930 try
931 {
932 $stmts = $parser->parse($code, $errorHandler);
933 $params = [];
934
935 if (!$stmts && $errorHandler->getErrors())
936 {
937 throw new Exception('syntax error in file');
938 }
939
940 $this->CheckStmts($stmts, $params, $file_path);
941 }
942 catch (Exception $e)
943 {
944 // echo 'Parse Error: ' . $file_path . " " . $e->getMessage() . "\n";
945 if ($this->collect_exceptions)
946 {
947 $this->addResult('[000] syntax error in file', '', 1);
948 }
949 }
950
951 # REGEXP BASED CODES
952
953 $src = preg_replace('#/\*.*?\*/#s', '', $src);
954 $src = preg_replace('#[\r\n][ \t]*//.*#m', '', $src);
955 $src = preg_replace('/[\r\n][ \t]*#.*/m', '', $src);
956
957 # CODE 007
958 if (self::$database && $this->SearchInDataBase($src))
959 {
960 $this->addResult('[007] looks like a well-known shell', '', 1);
961 return true;
962 }
963
964 # CODE 302
965 if (preg_match_all('#preg_replace' . self::$spaces . '(\‍(((?>[^()]+)|(?-2))*\‍))#i', $src, $regs))
966 {
967 foreach ($regs[1] as $i => $val)
968 {
969 $code = $regs[0][$i];
970 $spiltter = $val[2];
971 $spl = $spiltter === '#' ? '~' : '#';
972 if (preg_match($spl . preg_quote($spiltter) . '[imsxADSUXju]*e[imsxADSUXju]*[\'"]' . $spl, $val))
973 {
974 $subj = '[302] preg_replace_eval';
975 $checksum = $this->CalcChecksum($file_path, $code, $subj);
976 if (!$this->IsFalsePositive($checksum))
977 {
978 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
979 }
980 }
981 }
982 }
983
984 $content = preg_replace('/[\'"]\s*?\.\s*?[\'"]/smi', '', $src);
985
986 # CODE 321
987 if (preg_match_all('#[A-Za-z0-9+/]{20,}=*#i', $content, $regs))
988 {
989 foreach ($regs[0] as $val)
990 {
991 $code = $val;
992 $val = base64_decode($val);
993 if (preg_match('#(' . self::$request . '|' . self::$functions . '|' . self::$evals_reg . '|' . self::$black_reg . ')#i', $val))
994 {
995 $subj = '[321] base64_encoded code';
996 $checksum = $this->CalcChecksum($file_path, $code, $subj);
997 if (!$this->IsFalsePositive($checksum))
998 {
999 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
1000 }
1001 }
1002 }
1003 }
1004// unset($content);
1005
1006 # CODE 337
1007 if (preg_match_all('#' . self::$black_reg . '#i', $content, $regs))
1008 {
1009 $code = implode(' | ', $regs[0]);
1010
1011 $subj = '[337] strings from black list';
1012 $checksum = $this->CalcChecksum($file_path, $code, $subj);
1013 if (!$this->IsFalsePositive($checksum))
1014 {
1015 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
1016 }
1017 }
1018
1019 # CODE 400
1020 /* if (preg_match_all('#\$(USER|GLOBALS..USER..)->Authorize' . self::$spaces . '(\‍(((?>[^()]+)|(?-2))*\‍))#i', $src, $regs)) {*/
1021//
1022// foreach ($regs[3] as $i => $val) {
1023// $code = $regs[0][$i];
1024//
1025// $val = explode(',', $val)[0];
1026//
1027// if (preg_match('#' . self::$request . '|([\'"]?0?[xbe]?[0-9]+[\'"]?)#', $val)) {
1028// $subj = '[400] bitrix auth';
1029// if ($checksum = $this->CalcChecksum($file_path, $code, $subj) && $this->IsFalsePositive($checksum)) {
1030// $this->results[] = [$subj, $code];
1031// }
1032//
1033// }
1034// }
1035// }
1036
1037 # CODE 500
1038 if (preg_match_all('#[\'"](php://filter|phar://)#i', $content, $regs))
1039 {
1040 foreach ($regs[0] as $i => $value)
1041 {
1042 $code = $value;
1043 $subj = '[500] php wrapper';
1044 $checksum = $this->CalcChecksum($file_path, $code, $subj);
1045 if (!$this->IsFalsePositive($checksum))
1046 {
1047 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
1048 }
1049 }
1050 }
1051
1052 # CODE 630
1053 if (preg_match('#[a-z0-9+=/\n\r]{255,}#im', $src, $regs))
1054 {
1055 $code = $regs[0];
1056 if (!preg_match('#data:image/[^;]+;base64,[a-z0-9+=/]{255,}#i', $src, $regs))
1057 {
1058 $subj = '[630] long line';
1059 $checksum = $this->CalcChecksum($file_path, $code, $subj);
1060 if (!$this->IsFalsePositive($checksum))
1061 {
1062 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
1063 }
1064 }
1065 }
1066
1067 # CODE 640
1068 if (preg_match_all('#exif_read_data\‍(#i', $src, $regs))
1069 {
1070 foreach ($regs[0] as $i => $value)
1071 {
1072 $code = $value;
1073 $subj = '[640] strange exif';
1074 $checksum = $this->CalcChecksum($file_path, $code, $subj);
1075 if (!$this->IsFalsePositive($checksum))
1076 {
1077 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
1078 }
1079 }
1080 }
1081
1082 # CODE 663
1083 if (preg_match("#^.*([\x01-\x08\x0b\x0c\x0f-\x1f])#m", $src, $regs))
1084 {
1085 $code = $regs[1];
1086 if (!preg_match('#^\$ser_content = #', $regs[0]))
1087 {
1088 $subj = '[663] binary data';
1089 $checksum = $this->CalcChecksum($file_path, $code, $subj);
1090 if (!$this->IsFalsePositive($checksum))
1091 {
1092 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
1093 }
1094 }
1095 }
1096
1097 # CODE 665
1098 if ($file_path && preg_match_all('#(?:\\\\x[a-f0-9]{2}|\\\\[0-9]{2,3})+#i', $content, $regs))
1099 {
1100 $regs = $regs[0];
1101 $all = implode("", $regs);
1102 if (count($regs) > 1)
1103 {
1104 $regs[] = $all;
1105 }
1106 $found = false;
1107
1108 foreach ($regs as $code)
1109 {
1110 $val = stripcslashes($code);
1111 if (preg_match('#(' . self::$request . '|' . self::$functions . '|' . self::$evals_reg . '|' . self::$black_reg . ')#i', $val))
1112 {
1113 $subj = '[665] encoded code';
1114 $checksum = $this->CalcChecksum($file_path, $code, $subj);
1115 if (!$this->IsFalsePositive($checksum))
1116 {
1117 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
1118 $found = true;
1119 }
1120 }
1121 elseif (preg_match_all('#[A-Za-z0-9+/]{20,}=*#i', $val, $regs))
1122 {
1123 foreach ($regs[0] as $val)
1124 {
1125 $val = base64_decode($val);
1126 if (preg_match('#(' . self::$request . '|' . self::$functions . '|' . self::$evals_reg . '|' . self::$black_reg . ')#i', $val))
1127 {
1128 $subj = '[665] encoded code';
1129 $checksum = $this->CalcChecksum($file_path, $code, $subj);
1130 if (!$this->IsFalsePositive($checksum))
1131 {
1132 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
1133 $found = true;
1134 }
1135 }
1136 }
1137 }
1138 }
1139
1140 if (!$found && strlen($all) / filesize($file_path) > 0.1)
1141 {
1142 $subj = '[665] chars by code';
1143 $checksum = $this->CalcChecksum($file_path, $code, $subj);
1144 if (!$this->IsFalsePositive($checksum))
1145 {
1146 $this->addResult($subj, $code, self::CalcCrit($subj), $checksum);
1147 }
1148 }
1149 }
1150
1151 unset($src);
1152 unset($content);
1153
1154 return !empty($this->results);
1155 }
1156
1157 function CheckStmts($stmts, &$params, $file_path, $in_closure = false)
1158 {
1159 $nodeFinder = $this->nodeFinder;
1160 $pprinter = $this->pprinter;
1161
1162 $nodeFinder->find($stmts, function (Node $node) use (&$file_path) {
1163 if ($node instanceof Node\Stmt\Function_ || $node instanceof Node\Stmt\ClassMethod)
1164 {
1165 $this->CheckStmts($node->stmts, $node->params, $file_path);
1166 $node->stmts = [];
1167 }
1168 elseif ($node instanceof Node\Expr\Closure)
1169 {
1170 $this->CheckStmts($node->stmts, $node->params, $file_path, true);
1171 $node->stmts = [];
1172 }
1173 elseif ($node instanceof Node\Expr\ArrowFunction)
1174 {
1175 $this->CheckStmts($node->expr, $node->params, $file_path, true);
1176 $node->stmts = [];
1177 }
1178 });
1179
1180 $nodes = ['assigns' => [], 'variables' => [], 'params' => [], 'foreaches' => [], 'calls' => [], 'evals' => [],
1181 'backticks' => [], 'includes' => [], 'auth' => [], 'mtds' => [], 'strings' => []];
1182 $extract = false;
1183
1184 $nodeFinder->find($stmts, function (Node $node) use (&$nodes, &$pprinter, &$extract)
1185 {
1186 if ($node->getComments())
1187 {
1188 $node->setAttribute('comments', []);
1189 }
1190
1191// if ($node instanceof Node\Stmt\Function_) {
1192// $name = $node->name instanceof Node\Identifier ? $node->name->toString() : false
1193// if (is_string($name) && self::isVarStrange('$' . trim($name, '_'))) {
1194// $this->addResult('[110] strange function name', $name, 0.3);
1195// }
1196// }
1197 if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignOp)
1198 {
1199 $nodes['assigns'][] = $node;
1200 }
1201 if ($node instanceof Node\Expr\Variable)
1202 {
1203 $nodes['variables'][] = $node;
1204 }
1205 if ($node instanceof Node\Stmt\Foreach_)
1206 {
1207 $nodes['foreaches'][] = $node;
1208 }
1209 if ($node instanceof Node\Expr\FuncCall)
1210 {
1211 $nodes['calls'][] = $node;
1212 if ($node->name instanceof Node\Name && $node->name->toLowerString() === 'extract')
1213 {
1214 if (count($node->args) < 2 || $pprinter->prettyPrintExpr($node->args[1]->value) !== 'EXTR_SKIP')
1215 {
1216 $extract = true;
1217 }
1218 }
1219 }
1220 if ($node instanceof Node\Expr\Eval_)
1221 {
1222 $nodes['evals'][] = $node;
1223 }
1224 if ($node instanceof Node\Expr\ShellExec)
1225 {
1226 $nodes['backticks'][] = $node;
1227 }
1228 if ($node instanceof Node\Expr\Include_)
1229 {
1230 $nodes['includes'][] = $node;
1231 }
1232 if ($node instanceof Node\Expr\MethodCall && $node->name instanceof Node\Identifier && $node->name->toLowerString() == 'authorize'
1233 && preg_match('/user|globals/i', $pprinter->prettyPrintExpr($node->var)))
1234 {
1235 $nodes['auth'][] = $node;
1236 }
1237
1238 if ($node instanceof Node\Expr\MethodCall && !($node->name instanceof Node\Identifier)
1239 && preg_match('/^\$(user|globals)/i', $pprinter->prettyPrintExpr($node->var)))
1240 {
1241 $nodes['auth'][] = $node;
1242 }
1243
1244 if ($node instanceof Node\Expr\MethodCall && $node->name instanceof Node\Expr\Variable &&
1245 $node->var instanceof Node\Expr\ArrayDimFetch &&
1246 $node->var->var instanceof Node\Expr\Variable && $node->var->var->name == 'GLOBALS' &&
1247 (!($node->var->dim instanceof Node\Scalar\String_) || $node->var->dim->value == 'USER')
1248 )
1249 {
1250 $nodes['auth'][] = $node;
1251 }
1252
1253 if ($node instanceof Node\Expr\StaticCall)
1254 {
1255 $nodes['mtds'][] = $node;
1256 }
1257
1258 if ($node instanceof Node\Scalar\String_ && $node->value)
1259 {
1260 $nodes['strings'][] = $node;
1261 }
1262
1263 # this is dirty hack
1264 if ($node instanceof Node\Expr\ArrayDimFetch && $node->var instanceof Node\Expr\Variable && $node->var->name == '_SERVER')
1265 {
1266 $dim = $node->dim instanceof Node\Scalar\String_ ? $node->dim->value : "qwerty";
1267 if (!preg_match('/^(?:DOCUMENT_ROOT|SERVER_ADDR|REMOTE_ADDR|SERVER_NAME|HTTPS|SERVER_PORT|REMOTE_PORT)$/', $dim))
1268 {
1269 $node->var->name = '_REQUEST';
1270 }
1271 }
1272
1273// if ($node instanceof Node\Expr\ArrayDimFetch && $node->var instanceof Node\Expr\Variable && $node->var->name == 'GLOBALS') {
1274// $dim = $node->dim instanceof Node\Scalar\String_ ? $node->dim->value : "qwerty";
1275// if (preg_match('/^(?:_GET|_POST|_REQUEST|_COOKIE|_FILES|_SERVER)$/', $dim)) {
1276// $node->var->name = '_REQUEST';
1277// }
1278// }
1279 });
1280
1281 $vars_names = [];
1282
1283 $vars = [
1284 'request' => ['_GET' => true, '_POST' => true, '_REQUEST' => true, '_COOKIE' => true, '_FILES' => true],
1285 'params' => [],
1286 'from_request' => [],
1287 'crypted' => [],
1288 'assigned' => ['_GET' => true, '_POST' => true, '_REQUEST' => true, '_COOKIE' => true, '_SESSION' => true,
1289 '_SERVER' => true, '_FILES' => true, 'this' => true, 'USER' => true, 'DB' => true, 'APPLICATION' => true],
1290 'values' => [],
1291 'closures' => [],
1292 ];
1293
1294 foreach ($nodes['variables'] as $var)
1295 {
1296 if (is_string($var->name))
1297 {
1298 $var = '$' . $var->name;
1299 }
1300 else
1301 {
1302 $var = $this->pprinter->prettyPrintExpr($var->name);
1303 }
1304
1305 $vars_names[] = $var;
1306 }
1307 $vars_names = array_unique($vars_names);
1308
1309 foreach ($params as $param)
1310 {
1311 $n = substr($this->pprinter->prettyPrintExpr($param->var), 1);
1312 $vars['params'][$n] = true;
1313 $vars['assigned'][$n] = true;
1314
1315 if ($param->type instanceof Node\Name\FullyQualified && implode('', $param->type->parts) == 'Closure')
1316 {
1317 $vars['closures'][] = $n;
1318 }
1319 }
1320
1321 foreach ($nodes['assigns'] as $fnd)
1322 {
1323 $n = substr($this->pprinter->prettyPrintExpr($fnd->var), 1);
1324 $vars['assigned'][$n] = true;
1325 if ($fnd->expr instanceof Node\Expr\Closure)
1326 {
1327 $vars['closures'][] = $n;
1328 }
1329 }
1330
1331 foreach ($nodes['foreaches'] as $fnd)
1332 {
1333 if ($fnd->keyVar)
1334 {
1335 $n = substr($this->pprinter->prettyPrintExpr($fnd->keyVar), 1);
1336 $vars['assigned'][$n] = true;
1337 }
1338 if ($fnd->valueVar)
1339 {
1340 $n = substr($this->pprinter->prettyPrintExpr($fnd->valueVar), 1);
1341 $vars['assigned'][$n] = true;
1342 }
1343 }
1344
1345 for ($_ = 0; $_ < 2; $_++)
1346 {
1347 $res = [];
1348 foreach ($nodes['assigns'] as $node)
1349 {
1350 $flag = $nodeFinder->findFirst($node->expr,
1351 function (Node $node) use (&$vars) {
1352 return $node instanceof Node\Expr\Variable && is_string($node->name) && $node->name && (isset($vars['request'][$node->name]) || isset($vars['from_request'][$node->name]));
1353 }
1354 );
1355
1356 if ($flag)
1357 {
1358 $res[] = $node;
1359 }
1360 }
1361
1362 foreach ($res as $fnd)
1363 {
1364 $n = substr($this->pprinter->prettyPrintExpr($fnd->var), 1);
1365 $vars['from_request'][$n] = true;
1366 }
1367 }
1368
1369 for ($_ = 0; $_ < 1; $_++)
1370 {
1371 $res = [];
1372 foreach ($nodes['assigns'] as $node)
1373 {
1374 $flag = $nodeFinder->findFirst($node->expr,
1375 function (Node $node) {
1376 return $node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name &&
1377 in_array($node->name->toLowerString(), self::$cryptors, true);
1378 }
1379 );
1380 if ($flag)
1381 {
1382 $res[] = $node;
1383 }
1384 }
1385
1386 foreach ($res as $fnd)
1387 {
1388 $n = substr($this->pprinter->prettyPrintExpr($fnd->var), 1);
1389 $vars['crypted'][$n] = true;
1390 }
1391 }
1392
1393 foreach ($nodes['assigns'] as $fnd)
1394 {
1395 $n = substr($this->pprinter->prettyPrintExpr($fnd->var), 1);
1396 $tmp = $this->parseValue($fnd->expr, $vars);
1397 if (!isset($vars['values'][$n]))
1398 {
1399 $vars['values'][$n] = $tmp;
1400 }
1401 else
1402 {
1403 $vars['values'][$n] .= '|' . $tmp;
1404 }
1405 }
1406
1407// print_r($vars['values']);
1408
1409 $crypto_vars = array_keys($vars['crypted']);
1410
1411 # CODE 300
1412
1413 $res = [];
1414 $config = self::genConfig(['params' => true, 'assigned' => $extract]);
1415 foreach ($nodes['evals'] as $node)
1416 {
1417 [$flag, $comment] = $this->CheckArg($node->expr, $vars, $config);
1418 if ($flag)
1419 {
1420 $node->setAttribute('comment', $comment);
1421 $res[] = $node;
1422 }
1423 }
1424
1425 $this->CheckResults($res, '[300] eval', $file_path);
1426
1427 # CODE 298
1428
1429 $sql_map = [
1430 'mysqli_connect' => [0, 1, 2, 3],
1431 'mysqli_query' => [1],
1432 'mysqli_real_query' => [1],
1433 # i know its removed at php7
1434 'mysql_connect' => [0, 1, 2],
1435 'mysql_query' => [1],
1436 'mysql_db_query' => [0, 1],
1437 ];
1438
1439 $config = self::genConfig(['params' => true]);
1440 $res = $this->checkFuncCalls($nodes['calls'], $sql_map, $nodeFinder, $vars, $config);
1441
1442 $this->CheckResults($res, '[298] mysql function', $file_path);
1443
1444 # CODE 299
1445
1446 $mail_map = [
1447 'mail' => [0],
1448 'bxmail' => [0],
1449 ];
1450
1451 $config = self::genConfig();
1452 $res = $this->checkFuncCalls($nodes['calls'], $mail_map, $nodeFinder, $vars, $config);
1453
1454 $this->CheckResults($res, '[299] mail function', $file_path);
1455
1456 # CODE 300
1457
1458 $evals_map = [
1459 'assert' => [0],
1460 'create_function' => [0],
1461 'exec' => [0],
1462 'passthru' => [0],
1463 'pcntl_exec' => [0],
1464 'popen' => [0],
1465 'proc_open' => [0],
1466 'set_include_path' => [0],
1467 'shell_exec' => [0],
1468 'system' => [0]
1469 ];
1470
1471 $config = self::genConfig(['params' => true, 'assigned' => $extract]);
1472 $res = $this->checkFuncCalls($nodes['calls'], $evals_map, $nodeFinder, $vars, $config);
1473
1474 $this->CheckResults($res, '[300] command injection', $file_path);
1475
1476 # CODE 301
1477
1478 $files_map = [
1479 'copy' => [1], // 0,1
1480 'file_get_contents' => [0],
1481 'file_put_contents' => [0],
1482 'move_uploaded_file' => [1], // 0,1
1483 'opendir' => [0],
1484 'fopen' => [0]
1485 ];
1486
1487 $config = self::genConfig(['concat' => false, 'files' => false, 'value' => false]);
1488 $res = $this->checkFuncCalls($nodes['calls'], $files_map, $nodeFinder, $vars, $config);
1489
1490 $this->CheckResults($res, '[301] file operations', $file_path);
1491
1492 /*
1493 $files_map = [
1494 'file_put_contents' => [1],
1495 'fwrite' => [1],
1496 'fputs' => [1],
1497
1498 ];
1499
1500 $config = self::genConfig(['value'=> true, 'recursive' => true, 'concat'=> false]);
1501 $res = $this->checkFuncCalls($nodes['calls'], $files_map, $nodeFinder, $vars, $config);
1502
1503 $this->CheckResults($res, '[302] file operations', $file_path);
1504 */
1505
1506 # CODE 302
1507
1508 $f_w_clb_map = ['call_user_func' => [0],
1509 'call_user_func_array' => [0],
1510 'forward_static_call' => [0],
1511 'forward_static_call_array' => [0],
1512 'register_shutdown_function' => [0],
1513 'register_tick_function' => [0],
1514 'ob_start' => [0],
1515 'usort' => [1],
1516 'uasort' => [1],
1517 'uksort' => [1],
1518 'array_walk' => [1],
1519 'array_walk_recursive' => [1],
1520 'array_reduce' => [1],
1521 'array_intersect_ukey' => [2],
1522 'array_uintersect' => [2],
1523 'array_uintersect_assoc' => [2],
1524 'array_intersect_uassoc' => [2],
1525 'array_uintersect_uassoc' => [2, 3],
1526 'array_diff_ukey' => [2],
1527 'array_udiff' => [2],
1528 'array_udiff_assoc' => [2],
1529 'array_diff_uassoc' => [2],
1530 'array_udiff_uassoc' => [2, 3],
1531 'array_filter' => [1],
1532 'array_map' => [0],
1533 'mb_ereg_replace_callback' => [1]
1534 ];
1535
1536 $config = self::genConfig(['assigned' => $extract]);
1537 $res = $this->checkFuncCalls($nodes['calls'], $f_w_clb_map, $nodeFinder, $vars, $config);
1538
1539 $this->CheckResults($res, '[302] unsafe callable argument', $file_path);
1540
1541 # CODE 303
1542
1543 $some_calls = array_filter($nodes['calls'], function (Node $node) use (&$danger) {
1544 return $node->name instanceof Node\Name && $node->name->toLowerString() == 'create_function';
1545 }
1546 );
1547
1548 $res = [];
1549 foreach ($some_calls as $node)
1550 {
1551 $flag = $nodeFinder->findFirst($node->args,
1552 function (Node $node) {
1553 return ($node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Name && (!function_exists($node->name->toLowerString())
1554 || in_array($node->name->toLowerString(), self::$cryptors, true)
1555 || in_array($node->name->toLowerString(), self::$string_change, true))
1556 ) ||
1557 ($node instanceof Node\Scalar\String_ && preg_match('/(?:assert|' . implode('|', self::$cryptors) . ')/i', $node->value));
1558 }
1559 );
1560
1561 if ($flag)
1562 {
1563 $res[] = $node;
1564 }
1565 }
1566
1567 $this->CheckResults($res, '[303] create_function', $file_path);
1568
1569 # CODE 304
1570
1571 $clb = ['filter_input', 'filter_input_array', 'filter_var', 'filter_var_array'];
1572
1573 $some_calls = array_filter($nodes['calls'], function (Node $node) use (&$clb) {
1574 return $node->name instanceof Node\Name && in_array($node->name->toLowerString(), $clb, true);
1575 }
1576 );
1577
1578 $res = [];
1579 foreach ($some_calls as $node)
1580 {
1581 if (preg_match_all('#(?:_POST|_GET|_COOKIE|_REQUEST|FILTER_CALLBACK|1024|filter_input|filter_var)|' . self::$evals_reg . '|' . self::$functions . '#i', $pprinter->prettyPrint($node->args)) > 1)
1582 {
1583 $res[] = $node;
1584 }
1585 }
1586
1587 $this->CheckResults($res, '[304] filter_callback', $file_path);
1588
1589 # CODE 305
1590
1591 $res = [];
1592 foreach ($nodes['evals'] as $node)
1593 {
1594 $flag = $nodeFinder->findFirst($node->expr,
1595 function (Node $node) {
1596 return ($node instanceof Node\Expr\FuncCall && (
1597 ($node->name instanceof Node\Name && !function_exists($node->name->toLowerString())) ||
1598 ($node->name instanceof Node\Expr\Variable) ||
1599 ($node->name instanceof Node\Expr\ArrayDimFetch)
1600 )
1601 );
1602 }
1603 );
1604
1605 if ($flag)
1606 {
1607 $node->setAttribute('comment', 'strange code');
1608 $res[] = $node;
1609 }
1610 }
1611
1612 $this->CheckResults($res, '[305] strange function and eval', $file_path);
1613
1614 # CODE 306
1615
1616 $eregi_map = [
1617 'mb_eregi_replace' => [1],
1618 'mb_ereg_replace' => [1],
1619 ];
1620
1621 $config = self::genConfig();
1622 $res = $this->checkFuncCalls($nodes['calls'], $eregi_map, $nodeFinder, $vars, $config);
1623
1624 $this->CheckResults($res, '[302] eregi', $file_path);
1625
1626 # CODE 307
1627
1628 $res = [];
1629 foreach ($nodes['mtds'] as $node)
1630 {
1631 $class = $node->class instanceof Node\Name ? $node->class->toString() : '';
1632 $mtd = $node->name instanceof Node\Identifier ? $node->name->toString() : '';
1633
1634 if (!$class || !$mtd)
1635 {
1636 continue;
1637 }
1638
1639 $class_method = "$class::$mtd";
1640 foreach (self::$mehtods as $mtd)
1641 {
1642 if (stripos($class_method, $mtd) !== false)
1643 {
1644 $arg = isset($node->args[0]) ? $node->args[0] : false;
1645
1646 [$flag, $comment] = $this->CheckArg($arg->value, $vars, $config);
1647 if ($flag)
1648 {
1649 $res[] = $node;
1650 $node->setAttribute('comment', $comment);
1651 }
1652 break;
1653 }
1654 }
1655 }
1656
1657 $this->CheckResults($res, '[307] danger method', $file_path);
1658
1659 # CODE 400
1660
1661 $config = self::genConfig(['hardcoded' => true]);
1662
1663 $res = [];
1664 foreach ($nodes['auth'] as $node)
1665 {
1666 $arg = isset($node->args[0]) ? $node->args[0] : false;
1667 if ($arg === false)
1668 {
1669 continue;
1670 }
1671 $flag = false;
1672 $comment = '';
1673
1674 [$flag, $comment] = $this->CheckArg($arg->value, $vars, $config);
1675 if ($flag)
1676 {
1677 $node->setAttribute('comment', $comment);
1678
1679 $res[] = $node;
1680 }
1681 elseif ($node->var instanceof Node\Expr\ArrayDimFetch){
1682 $res[] = $node;
1683 }
1684 }
1685
1686 $this->CheckResults($res, '[400] bitrix auth', $file_path);
1687
1688 # CODE 600
1689
1690 $res = [];
1691 $config = self::genConfig(['concat' => false, 'value' => false]);
1692
1693 foreach ($nodes['includes'] as $node)
1694 {
1695 $flag = false;
1696 $comment = '';
1697
1698 $inc = $pprinter->prettyPrintExpr($node->expr);
1699
1700 if (preg_match('/\.(gif|png|jpg|jpeg|var|pdf|exe)/i', $inc))
1701 {
1702 $flag = true;
1703 $comment = 'gif|png|jpg|jpeg|var|pdf|exe';
1704 }
1705 elseif (preg_match('#(https?|ftps?|compress\.zlib|php|glob|data|phar|zip)://#i', $inc))
1706 {
1707 $flag = true;
1708 $comment = 'wrapper';
1709 }
1710 else
1711 {
1712 [$flag, $comment] = $this->CheckArg($node->expr, $vars, $config);
1713// $flag = $flag || $nodeFinder->findFirst($node->expr,
1714// function (Node $node) use (&$vars) {
1715// return $node instanceof Node\Expr\Variable && is_string($node->name) && $node->name && (isset($vars['request'][$node->name]) || isset($vars['crypted'][$node->name]));
1716// }
1717// );
1718 }
1719
1720 if ($flag)
1721 {
1722 $node->setAttribute('comment', $comment);
1723 $res[] = $node;
1724 }
1725 }
1726
1727 $this->CheckResults($res, '[600] strange include', $file_path);
1728
1729 # CODE 610 615 620
1730
1731 $checked = [];
1732 foreach ($nodes['variables'] as $var)
1733 {
1734 $v = $this->pprinter->prettyPrintExpr($var);
1735 if (in_array($v, $checked, true))
1736 {
1737 continue;
1738 }
1739 $checked[] = $v;
1740 if ($var->name instanceof Node\Expr\BinaryOp || preg_match('#\$_{3,}#i', $v) || preg_match('#\$\{.*?(?:->|::|\‍()#i', $v))
1741 {
1742 $subj = '[610] strange vars';
1743 $checksum = $this->CalcChecksum($file_path, $v, $subj);
1744 if (!$this->IsFalsePositive($checksum))
1745 {
1746 $this->addResult($subj, $v, self::CalcCrit($subj), $checksum);
1747 }
1748 }
1749
1750 if (preg_match('#\${["\']\\\\x[0-9]{2}[a-z0-9\\\\]+["\']}#i', $v))
1751 {
1752 $subj = '[615] hidden vars';
1753 $checksum = $this->CalcChecksum($file_path, $v, $subj);
1754 if (!$this->IsFalsePositive($checksum))
1755 {
1756 $this->addResult($subj, $v, self::CalcCrit($subj), $checksum);
1757 }
1758 }
1759
1760 if (preg_match("#\$(?:[\x80-\xff][_\x80-\xff]*|_(?:[\x80-\xff][_\x80-\xff]*|_[_\x80-\xff]+))" . self::$spaces . '=#i', $v))
1761 {
1762 $subj = '[620] binary vars';
1763 $checksum = $this->CalcChecksum($file_path, $v, $subj);
1764 if (!$this->IsFalsePositive($checksum))
1765 {
1766 $this->addResult($subj, $v, self::CalcCrit($subj), $checksum);
1767 }
1768 }
1769 }
1770
1771 # CODE 650
1772
1773 $res = [];
1774 $config = self::genConfig();
1775
1776 foreach ($nodes['calls'] as $node)
1777 {
1778 $flag = false;
1779 $comment = '';
1780 if ($node->name instanceof Node\Expr\Variable)
1781 {
1782 $var = is_string($node->name) ? '$' . $node->name : $this->pprinter->prettyPrintExpr($node->name);
1783 $name = substr($var, 1);
1784
1785 [$flag, $comment] = $this->CheckArg($node->name, $vars, $config);
1786 if (!$flag)
1787 {
1788 $flag = $nodeFinder->findFirst($node->args,
1789 function (Node $node) {
1790 return $node instanceof Node\Expr\FuncCall && $node->name instanceof Node\Expr\Variable;
1791 });
1792
1793 if ($flag)
1794 {
1795 $comment = 'functions inside';
1796 }
1797 }
1798
1799 if (!$flag)
1800 {
1801 foreach ($node->args as $arg)
1802 {
1803 if ($arg instanceof Node\Arg)
1804 {
1805 [$flag, $comment] = $this->CheckArg($arg->value, $vars, $config);
1806 if ($flag)
1807 {
1809 break;
1810 }
1811 }
1812 }
1813 }
1814 if (!$flag && !in_array($name, $vars['closures'], true) && !$in_closure)
1815 {
1816 $comment = 'other';
1817 if (self::isVarStrange($var))
1818 {
1819 $comment = 'strange var';
1820 }
1821 $flag = true;
1822 }
1823
1824 if ($flag)
1825 {
1826 $node->setAttribute('comment', $comment);
1827 $res[] = $node;
1828 }
1829 }
1830 }
1831
1832 $this->CheckResults($res, '[650] variable as a function', $file_path);
1833
1834 # CODE 660
1835
1836 $config = self::genConfig();
1837
1838 $res = [];
1839 foreach ($nodes['calls'] as $node)
1840 {
1841 if ($node->name instanceof Node\Expr\ArrayDimFetch)
1842 {
1843 $res[] = $node;
1844
1845 [$flag, $comment] = $this->CheckArg($node->name, $vars, $config);
1846
1847 if (!$flag)
1848 {
1849 foreach ($node->args as $arg)
1850 {
1851 [$flag, $comment] = $this->CheckArg($arg->value, $vars, $config);
1852 if ($flag)
1853 {
1854 break;
1855 }
1856 }
1857 }
1858
1859 if ($flag)
1860 {
1861 $node->setAttribute('comment', $comment);
1862 }
1863 }
1864 }
1865
1866 $this->CheckResults($res, '[660] array member as a function', $file_path);
1867
1868 # CODE 662
1869
1870 $config = self::genConfig();
1871
1872 $res = [];
1873 foreach ($nodes['calls'] as $node)
1874 {
1875 if ($node->name instanceof Node\Expr\FuncCall)
1876 {
1877 $res[] = $node;
1878
1879 [$flag, $comment] = $this->CheckArg($node->name, $vars, $config);
1880 if ($flag)
1881 {
1882 $node->setAttribute('comment', $comment);
1883 }
1884 }
1885 }
1886
1887 $this->CheckResults($res, '[662] function return as a function', $file_path);
1888
1889 $res = [];
1890 foreach ($nodes['calls'] as $node)
1891 {
1892 if ($node->name instanceof Node\Scalar\String_ || $node->name instanceof Node\Expr\BinaryOp)
1893 {
1894 $res[] = $node;
1895
1896 [$flag, $comment] = $this->CheckArg($node->name, $vars, $config);
1897 if ($flag)
1898 {
1899 $node->setAttribute('comment', $comment);
1900 }
1901 }
1902 }
1903
1904 $this->CheckResults($res, '[663] strange function', $file_path);
1905
1906 # CODE 665
1907 $res = [];
1908 foreach ($nodes['strings'] as $node)
1909 {
1910 $str = $node->value;
1911
1912 $str2 = base64_decode($str);
1913 if ($str2 && preg_match('#(' . self::$request . '|' . self::$functions . '|' . self::$evals_reg . '|' . self::$black_reg . ')#i', $str2))
1914 {
1915 $res[] = $node;
1916 continue;
1917 }
1918
1919 if (preg_match('/^[a-z\s0-9:._-]+$/i', $str))
1920 {
1921 continue;
1922 }
1923
1924 if (strpos($str, '<?') !== false && preg_match('#(' . self::$request . '|' . self::$functions . '|' . self::$evals_reg . '|' . self::$black_reg . ')#i', $str))
1925 {
1926 $subscan = new CBitrixXscan();
1927 $subscan->collect_exceptions = false;
1928 // $str2 = "<?php\n" . $str;
1929 if ($subscan->CheckCode($str))
1930 {
1931 $res[] = $node;
1932 }
1933 unset($str, $str2, $subscan);
1934 }
1935 }
1936
1937 $this->CheckResults($res, '[665] encoded code', $file_path);
1938
1939 # CODE 887
1940
1941 $this->CheckResults($nodes['backticks'], '[887] backticks', $file_path);
1942
1943 unset($stmts, $nodes, $some_calls, $res, $req, $code);
1944 }
1945
1946 public static function genConfig($options = false)
1947 {
1948 $config = self::$default_config;
1949 if (is_array($options))
1950 {
1951 foreach ($options as $key => $val)
1952 {
1953 $config[$key] = $val;
1954 }
1955 }
1956
1957 return $config;
1958 }
1959
1960 public function checkFuncCalls(&$all_calls, &$funcs_map, &$nodeFinder, &$vars, &$config)
1961 {
1962 $funcs = array_keys($funcs_map);
1963
1964 $some_calls = array_filter($all_calls, function (Node $node) use (&$funcs) {
1965 return $node->name instanceof Node\Name && in_array($node->name->toLowerString(), $funcs, true);
1966 });
1967
1968 $result = [];
1969 foreach ($some_calls as $node)
1970 {
1971 $ret = false;
1972 $func = $node->name->toLowerString();
1973
1974 $comment = '';
1975
1976 foreach ($funcs_map[$func] as $i)
1977 {
1978 if (!isset($node->args[$i]) || $ret)
1979 {
1980 continue;
1981 }
1982
1983 if ($node->args[$i] instanceof Node\Arg && $node->args[$i]->value instanceof Node\Expr\Closure)
1984 {
1985 continue;
1986 }
1987
1988 $arg = $node->args[$i]->value;
1989
1990 [$ret, $comment] = $this->CheckArg($arg, $vars, $config);
1991 }
1992
1993 if ($ret && $comment)
1994 {
1995 $node->setAttribute('comment', $comment);
1996 $result[] = $node;
1997 }
1998 }
1999 return $result;
2000 }
2001
2002 public function parseValue($node, &$vars)
2003 {
2004 $ret = '';
2005 $temp_name = false;
2006
2007 while ($node instanceof Node\Expr\ArrayDimFetch || $node instanceof Node\Expr\PropertyFetch)
2008 {
2009 if ($node instanceof Node\Expr\ArrayDimFetch && $node->dim instanceof Node\Scalar\String_ and $node->dim->value == 'tmp_name')
2010 {
2011 $temp_name = true;
2012 }
2013 if ($node instanceof Node\Expr\ArrayDimFetch && $node->var instanceof Node\Expr\Variable && $node->var->name == 'GLOBALS'
2014 && $node->dim instanceof Node\Scalar\String_)
2015 {
2016 $node = new Node\Expr\Variable($node->dim->value);
2017 }
2018 else
2019 {
2020 $node = $node->var;
2021 }
2022 }
2023
2024 if ($node instanceof Node\Expr\Variable)
2025 {
2026 $name = $node->name;
2027 if (is_string($name) && $name)
2028 {
2029 if (isset($vars['values'][$name]))
2030 {
2031 $ret = $vars['values'][$name];
2032 }
2033 elseif (isset($vars['request'][$name]) && !$temp_name)
2034 {
2035 $ret = '$_REQUEST';
2036 }
2037 elseif (isset($vars['from_request'][$name]))
2038 {
2039 $ret = '$_FROM_REQUEST';
2040 }
2041 elseif (isset($vars['crypted'][$name]))
2042 {
2043 $ret = 'CRYPTED';
2044 }
2045 elseif (isset($vars['params'][$name]))
2046 {
2047 $ret = 'PARAMS';
2048 }
2049 }
2050 elseif (!is_string($name))
2051 {
2052 $ret = $name = $this->parseValue($name, $vars);
2053 }
2054 }
2055 elseif ($node instanceof Node\Expr\BinaryOp)
2056 {
2057 $left = $this->parseValue($node->left, $vars);
2058 $right = $this->parseValue($node->right, $vars);
2059
2060 if ($node instanceof Node\Expr\BinaryOp\Div && (int)$right != 0)
2061 {
2062 $ret = (string)((int)$left / (int)$right);
2063 }
2064 elseif ($node instanceof Node\Expr\BinaryOp\Mul)
2065 {
2066 $ret = (string)((int)$left * (int)$right);
2067 }
2068 elseif ($node instanceof Node\Expr\BinaryOp\Minus)
2069 {
2070 $ret = (string)((int)$left - (int)$right);
2071 }
2072 elseif ($node instanceof Node\Expr\BinaryOp\Plus)
2073 {
2074 $ret = (string)((int)$left + (int)$right);
2075 }
2076 elseif ($node instanceof Node\Expr\BinaryOp\BitwiseXor)
2077 {
2078 $ret = (string)($left ^ $right);
2079 }
2080 else
2081 {
2082 $ret = $left . $right;
2083 }
2084 }
2085 elseif ($node instanceof Node\Scalar\Encapsed)
2086 {
2087 foreach ($node->parts as $part)
2088 {
2089 $part = $this->parseValue($part, $vars);
2090 $ret .= $part;
2091 }
2092 }
2093 elseif ($node instanceof Node\Scalar\LNumber ||
2094 $node instanceof Node\Scalar\DNumber ||
2095 $node instanceof Node\Scalar\String_ ||
2096 $node instanceof Node\Scalar\EncapsedStringPart
2097 )
2098 {
2099 $ret = (string)$node->value;
2100 }
2101 elseif ($node instanceof Node\Expr\FuncCall)
2102 {
2103 $name = $node->name instanceof Node\Name ? $node->name->toLowerString() : "\$v";
2104
2105 if ($name === 'chr')
2106 {
2107 $v = $this->parseValue($node->args[0]->value, $vars);
2108 $ret = chr((int)$v);
2109 }
2110 else
2111 {
2112 $args = [];
2113 foreach ($node->args as $arg)
2114 {
2115 if ($arg instanceof Node\Arg)
2116 {
2117 $args[] = $this->parseValue($arg->value, $vars);
2118 }
2119 }
2120
2121 $ret = "$name(" . implode(",", $args) . ")";
2122
2123 [$a, $b] = self::checkString($ret);
2124 $ret = $a ? $b : '';
2125 unset($args);
2126 }
2127 }
2128 elseif ($node instanceof Node\Expr\Ternary)
2129 {
2130 $if = $this->parseValue($node->if, $vars);
2131 $ret = $if ?: $this->parseValue($node->else, $vars);
2132 }
2133 elseif ($node && property_exists($node, 'expr'))
2134 {
2135 return $this->parseValue($node->expr, $vars);
2136 }
2137
2138 return $ret;
2139 }
2140
2141 public function CheckArg($arg, &$vars, &$config)
2142 {
2143 $ret = false;
2144 $comment = '';
2145 $temp_name = false;
2146
2147 while ($arg instanceof Node\Expr\ArrayDimFetch || $arg instanceof Node\Expr\PropertyFetch)
2148 {
2149 if ($arg instanceof Node\Expr\ArrayDimFetch && $arg->dim instanceof Node\Scalar\String_ and $arg->dim->value == 'tmp_name')
2150 {
2151 $temp_name = true;
2152 }
2153
2154 if ($arg instanceof Node\Expr\ArrayDimFetch && $arg->var instanceof Node\Expr\Variable && $arg->var->name == 'GLOBALS'
2155 && $arg->dim instanceof Node\Scalar\String_)
2156 {
2157 $arg = new Node\Expr\Variable($arg->dim->value);
2158 }
2159 else
2160 {
2161 $arg = $arg->var;
2162 }
2163 }
2164
2165 $temp_name = $temp_name && ($arg instanceof Node\Expr\Variable and is_string($arg->name) && $arg->name == '_FILES');
2166
2167 while ($arg instanceof Node\Expr\ConstFetch)
2168 {
2169 $arg = $arg->name;
2170 }
2171
2172 if ($config['hardcoded'] && (
2173 $arg instanceof Node\Scalar\LNumber ||
2174 $arg instanceof Node\Scalar\DNumber ||
2175 $arg instanceof Node\Scalar\String_ ||
2176 $arg instanceof Node\Scalar\Encapsed)
2177 )
2178 {
2179 $comment = 'hardcoded value';
2180 $ret = true;
2181 }
2182 elseif ($arg instanceof Node\Expr\Variable)
2183 {
2184 $name = $arg->name;
2185 if (!is_string($name))
2186 {
2187 $ret = true;
2188 $comment = 'crypted var';
2189 }
2190 else
2191 {
2192 $ret = is_string($name) && $name && ($config['files'] || (!$config['files'] && !$temp_name)) && (
2193 ($config['request'] && isset($vars['request'][$name]) && $comment = 'request') ||
2194 ($config['from_request'] && isset($vars['from_request'][$name]) && $comment = 'var from request') ||
2195 ($config['crypted'] && isset($vars['crypted'][$name]) && $comment = 'crypted var') ||
2196 ($config['assigned'] && !isset($vars['assigned'][$name]) && $comment = 'var was not assigned') ||
2197 ($config['params'] && isset($vars['params'][$name]) && $comment = 'var from params') ||
2198 ($name == 'GLOBALS' && $comment = 'strange globals')
2199 );
2200 }
2201 }
2202 elseif ($arg instanceof Node\Expr\FuncCall && $arg->name instanceof Node\Name)
2203 {
2204 $name = $arg->name->toLowerString();
2205 $ret = in_array($name, self::$evals, true) || in_array($name, ['getenv', 'debug_backtrace'], true) || ($config['crypted'] && in_array($name, self::$cryptors, true));
2206 if (!$ret)
2207 {
2208 foreach ($arg->args as $argv)
2209 {
2210 [$ret, $comment] = $this->CheckArg($argv->value, $vars, $config);
2211 if ($ret)
2212 {
2213 break;
2214 }
2215 }
2216 }
2217 else
2218 {
2219 $comment = 'danger function';
2220 }
2221 }
2222 elseif ($arg instanceof Node\Scalar\String_)
2223 {
2224 $comment = 'danger function';
2225 $ret = preg_match('/^(' . implode('|', self::$evals) . '|call_user_func|getenv)$/i', $arg->value);
2226 }
2227 elseif ($arg instanceof Node\Scalar\EncapsedStringPart)
2228 {
2229 $comment = 'danger function';
2230 $ret = preg_match('/(' . implode('|', self::$evals) . '|call_user_func|getenv)/i', $arg->value);
2231 }
2232 elseif ($arg instanceof Node\Name)
2233 {
2234 $comment = 'danger function';
2235 $func = $arg->toLowerString();
2236 $ret = preg_match('/(' . implode('|', self::$evals) . '|call_user_func|getenv)/i', $func);
2237 }
2238 elseif ($arg instanceof Node\Expr\BinaryOp\Concat || $arg instanceof Node\Expr\BinaryOp\Coalesce)
2239 {
2240 [$a, $b] = $this->CheckArg($arg->left, $vars, $config);
2241 if ($a)
2242 {
2243 [$ret, $comment] = [$a, $b];
2244 }
2245 else
2246 {
2247 [$a, $b] = $this->CheckArg($arg->right, $vars, $config);
2248 if ($a)
2249 {
2250 [$ret, $comment] = [$a, $b];
2251 }
2252 elseif ($config['concat'])
2253 {
2254 $comment = 'strange concatination';
2255 $ret = true;
2256 }
2257 }
2258 }
2259 elseif ($arg instanceof Node\Scalar\Encapsed)
2260 {
2261 foreach ($arg->parts as $part)
2262 {
2263 [$a, $b] = $this->CheckArg($part, $vars, $config);
2264 if ($a)
2265 {
2266 [$ret, $comment] = [$a, $b];
2267 }
2268 }
2269 }
2270 elseif ($arg instanceof Node\Expr\Ternary)
2271 {
2272 [$a, $b] = $this->CheckArg($arg->if, $vars, $config);
2273 if ($a)
2274 {
2275 [$ret, $comment] = [$a, $b];
2276 }
2277 else
2278 {
2279 [$a, $b] = $this->CheckArg($arg->else, $vars, $config);
2280 if ($a)
2281 {
2282 [$ret, $comment] = [$a, $b];
2283 }
2284 }
2285 }
2286
2287// print_r($arg);
2288
2289 if (!$ret && $config['value'])
2290 {
2291 $val = $this->parseValue($arg, $vars);
2292
2293 if ($config['recursive'])
2294 {
2295 $subscan = new CBitrixXscan();
2296 $subscan->collect_exceptions = false;
2297 $res = $subscan->CheckCode($val);
2298 if ($res)
2299 {
2300 [$ret, $comment] = [true, 'recursive'];
2301 }
2302 unset($subscan);
2303 }
2304 else
2305 {
2306 [$ret, $comment] = self::checkString($val);
2307 }
2308 }
2309
2310 return [$ret, $comment];
2311 }
2312
2313 public static function checkString($val)
2314 {
2315 $ret = '';
2316 $comment = '';
2317
2318 if (preg_match('/BXS_(?:EVAL|CRYPTED|BLACKLIST|REQUEST)/', $val, $m))
2319 {
2320 $ret = true;
2321 $comment = $m[0];
2322 }
2323 elseif (preg_match('/\b(' . implode('|', self::$evals) . '|getenv)\b/i', $val))
2324 {
2325 $ret = true;
2326 $comment = 'BXS_EVAL';
2327 }
2328 elseif (preg_match('/\b(' . implode('|', self::$cryptors) . '|CRYPTED)\b/i', $val))
2329 {
2330 $ret = true;
2331 $comment = 'BXS_CRYPTED';
2332 }
2333 elseif (preg_match('#\b' . self::$black_reg . '\b#i', $val))
2334 {
2335 $ret = true;
2336 $comment = 'BXS_BLACKLIST';
2337 }
2338 elseif (preg_match('/(\$_REQUEST|\$_FROM_REQUEST)/i', $val))
2339 {
2340 $ret = true;
2341 $comment = 'BXS_REQUEST';
2342 }
2343 return [$ret, $comment];
2344 }
2345
2346 public static function isVarStrange($var)
2347 {
2348 $ret = 0;
2349 $ret = preg_match('/^\$_?([0o]+|[1li]+)$/i', $var); // obfusacator
2350 $ret = $ret || preg_match('/^\$__/i', $var) || $var == '$_';
2351 $ret = $ret || preg_match('/__/', $var);
2352 $ret = $ret || preg_match('/^\$_*[a-z0-9]{1,2}$/i', $var); // very short
2353
2354 $ret = $ret || preg_match('/\d{2,}$/i', $var); // 2+ digits in the end
2355 $ret = $ret || preg_match_all('/[A-Z][a-z][A-Z]/', $var) > 1; // CaSe dAnCe
2356
2357 $ret = $ret || preg_match('/[^$a-z0-9_]/i', $var);
2358 $ret = $ret || preg_match('/[a-z]+[0-9]+[a-z]+/i', $var); // digits in centre
2359
2360 $ret = $ret || (preg_match_all('#[qwrtpsdfghjklzxcvbnm]{4,}#i', $var, $regs)
2361 && (strlen(implode('', $regs[0])) / strlen($var) > 0.4));
2362
2363 return $ret > 0;
2364 }
2365
2366 public static function CalcCrit($subj, $com = '')
2367 {
2368 if (!isset(self::$scoring[$subj]))
2369 {
2370 die("error: " . $subj);
2371 }
2372
2373 static $nums = [
2374 'self' => 0,
2375 'strange concatination' => 1,
2376 'hardcoded value' => 2,
2377 'request' => 3,
2378 'danger function' => 4,
2379 'var from params' => 5,
2380 'var was not assigned' => 6,
2381 'crypted var' => 7,
2382 'var from request' => 8,
2383 ];
2384
2385 $self = self::$scoring[$subj][0];
2386
2387 if ($com == 'other')
2388 {
2389 $arg = 0.3;
2390 }
2391 else
2392 {
2393 $num = isset($nums[$com]) ? $nums[$com] : 0;
2394 $arg = isset(self::$scoring[$subj][$num]) ? self::$scoring[$subj][$num] : 1;
2395 }
2396
2397 return round($self * $arg, 2);
2398 }
2399
2400 public function CheckResults(&$res, $subj, $file_path)
2401 {
2402 foreach ($res as $r)
2403 {
2404 $code = $this->pprinter->prettyPrintExpr($r);
2405
2406 $com = $r->getAttribute('comment', '');
2407 $crit = self::CalcCrit($subj, $com);
2408 $checksum = $this->CalcChecksum($file_path, $code, $subj);
2409
2410 $str = defined('XSCAN_DEBUG') ? "$subj [$com] | $crit | $checksum" : $subj;
2411
2412 if (!$this->IsFalsePositive($checksum))
2413 {
2414 $this->addResult($str, $code, $crit, $checksum);
2415 }
2416 }
2417 }
2418
2419 public static function ParseNode(&$node)
2420 {
2421 if (isset($arr[0]))
2422 {
2423 foreach ($node as $v)
2424 {
2425 self::ParseNode($v);
2426 }
2427 return;
2428 }
2429 }
2430
2431 static function CountVars($str)
2432 {
2433 $regular = '#' . self::$var . '#';
2434 if (!preg_match_all($regular, $str, $regs))
2435 {
2436 return 0;
2437 }
2438 $ar0 = $regs[0];
2439 $ar0 = array_unique($ar0);
2440 $ar0 = array_filter($ar0, function ($v) {
2441 return !in_array($v, ['$_GET', '$_POST', '$_REQUEST', '$_GET', '$_SERVER', '$_FILES', '$APPLICATION', '$DB', '$USER']);
2442 });
2443
2444 return count($ar0);
2445 }
2446
2447 static function StatVulnCheck($str, $bAll = false)
2448 {
2449 $regular = $bAll ? '#\$?[a-z_]+#i' : '#' . self::$var . '#';
2450 if (!preg_match_all($regular, $str, $regs))
2451 {
2452 return false;
2453 }
2454 $ar0 = $regs[0];
2455 $ar1 = array_unique($ar0);
2456 $uniq = count($ar1) / count($ar0);
2457
2458 $ar2 = [];
2459 foreach ($ar1 as $var)
2460 {
2461 if ($bAll && function_exists($var))
2462 {
2463 $p = 0;
2464 }
2465 elseif ($bAll && preg_match('#^[a-z]{1,2}$#i', $var))
2466 {
2467 $p = 1;
2468 }
2469 elseif (preg_match('#^\$?(function|php|csv|sql|__DIR__|__FILE__|__LINE__|DBDebug|DBType|DBName|DBPassword|DBHost|APPLICATION)$#i', $var))
2470 {
2471 $p = 0;
2472 }
2473 elseif (preg_match('#__#', $var))
2474 {
2475 $p = 1;
2476 }
2477 elseif (preg_match('#^\$(ar|str)[A-Z]#', $var, $regs))
2478 {
2479 $p = 0;
2480 }
2481 elseif (preg_match_all('#([qwrtpsdfghjklzxcvbnm]{3,}|[a-z]+[0-9]+[a-z]+)#i', $var, $regs))
2482 {
2483 $p = strlen(implode('', $regs[0])) / strlen($var) > 0.3;
2484 }
2485 else
2486 {
2487 $p = 0;
2488 }
2489
2490 $ar2[] = $p;
2491 }
2492 $prob = array_sum($ar2) / count($ar2);
2493 if ($prob < 0.3)
2494 {
2495 return false;
2496 }
2497
2498 if (!$bAll)
2499 {
2500 return self::StatVulnCheck($str, true);
2501 }
2502
2503 return true;
2504 }
2505
2506 function Search($path, $mode = 'search')
2507 {
2508 $path = str_replace('\\', '/', $path);
2509 do
2510 {
2511 $path = str_replace('//', '/', $path, $flag);
2512 }
2513 while ($flag);
2514
2515 if (php_sapi_name() != "cli")
2516 {
2517 header('xscan-bp: ' . $path, true);
2518 }
2519
2520 if ($this->start_time && time() - $this->start_time > $this->time_limit)
2521 {
2522 if ($mode == 'search' && !$this->break_point)
2523 {
2524 $this->break_point = $path;
2525 }
2526 if ($mode == 'count')
2527 {
2528 $this->total = 0;
2529 }
2530 return;
2531 }
2532
2533 if ($mode == 'search' && $this->skip_path && !$this->found)
2534 {
2535 if (strpos($this->skip_path, dirname($path)) !== 0)
2536 {
2537 return;
2538 }
2539
2540 if ($this->skip_path == $path)
2541 {
2542 $this->found = true;
2543 }
2544 }
2545
2546 if (is_dir($path)) // dir
2547 {
2548 $p = realpath($path);
2549
2550 if (is_link($path))
2551 {
2552 if ($this->base_dir && strpos($p . '/', $this->base_dir . '/') !== false)
2553 {
2554 return true;
2555 }
2556
2557 $d = dirname($path);
2558 if (strpos($p, $d) !== false || strpos($d, $p) !== false)
2559 {
2560 return true;
2561 }
2562 }
2563
2564 $dir = opendir($path);
2565 $isbitrix = basename($path) == 'bitrix' && is_file($path . '/.settings.php');
2566
2567 while ($item = readdir($dir))
2568 {
2569 if ($item == '.' || $item == '..')
2570 {
2571 continue;
2572 }
2573
2574 if ($isbitrix && in_array($item, ['cache', 'managed_cache', 'stack_cache', 'updates']))
2575 {
2576 continue;
2577 }
2578
2579 $this->Search($path . '/' . $item, $mode);
2580 }
2581 closedir($dir);
2582 }
2583 elseif (preg_match('/\.(?:htaccess|php[578]?|js)$/i', $path)) // file
2584 {
2585 if ($mode == 'count')
2586 {
2587 $this->total += 1;
2588 return;
2589 }
2590
2591 if (!$this->skip_path || $this->found)
2592 {
2593 $this->progress += 1;
2594 $res = $this->CheckFile($path);
2595 if ($res)
2596 {
2597 $this->pushResult($path);
2598 }
2599 }
2600 }
2601 }
2602
2603 function SystemFile($f)
2604 {
2605 static $system = [
2606 '/bitrix/modules/controller/install/activities/bitrix/controllerremoteiblockactivity/controllerremoteiblockactivity.php',
2607 '/bitrix/activities/bitrix/controllerremoteiblockactivity/controllerremoteiblockactivity.php',
2608 '/bitrix/modules/main/classes/general/update_class.php',
2609 '/bitrix/modules/main/classes/general/file.php',
2610 '/bitrix/modules/imconnectorserver/lib/connectors/telegrambot/emojiruleset.php',
2611 '/bitrix/modules/imconnectorserver/lib/connectors/facebook/emojiruleset.php',
2612 '/bitrix/modules/main/include.php',
2613 '/bitrix/modules/main/classes/general/update_client.php',
2614 '/bitrix/modules/main/install/wizard/wizard.php',
2615 '/bitrix/modules/main/start.php',
2616 '/bitrix/modules/landing/lib/mutator.php',
2617 '/bitrix/modules/main/tools.php',
2618 '/bitrix/modules/main/lib/engine/response/redirect.php',
2619 '/bitrix/modules/main/lib/config/option.php',
2620 '/bitrix/modules/main/classes/general/main.php',
2621 '/bitrix/modules/main/lib/UpdateSystem/PortalInfo.php',
2622 '/bitrix/modules/main/lib/UpdateSystem/HashCodeParser.php',
2623 '/bitrix/modules/main/lib/UpdateSystem/ActivationSystem.php',
2624 '/bitrix/modules/main/lib/license.php',
2625 '/bitrix/modules/crm/classes/general/sql_helper.php',
2626 '/bitrix/modules/main/lib/security/w/wwall.php',
2627 '/bitrix/modules/main/lib/security/w/rules/intvalrule.php',
2628 '/bitrix/modules/main/lib/security/w/rules/pregmatchrule.php',
2629 '/bitrix/modules/main/lib/security/w/rules/pregreplacerule.php',
2630 '/bitrix/modules/main/lib/security/w/rules/rule.php'
2631 ];
2632 foreach ($system as $path)
2633 {
2634 if (preg_match('#' . $path . '$#', $f))
2635 {
2636 return true;
2637 }
2638 }
2639 return false;
2640 }
2641
2642 function pushResult($f)
2643 {
2644 $message = [];
2645 foreach ($this->results as $res)
2646 {
2647 $message[] = $res['subj'];
2648 }
2649
2650 if (is_array($message))
2651 {
2652 $message = implode(' <br> ', array_unique($message));
2653 }
2654
2655 $stat = @stat($f);
2656
2657 $result = (new XScanResult)->setType('file')->setSrc($f)->setScore($this->score)->setMessage($message);
2658 if (is_array($stat))
2659 {
2660 $result->setCtime(ConvertTimeStamp($stat['ctime'], "FULL"));
2661 $result->setMtime(ConvertTimeStamp($stat['mtime'], "FULL"));
2662 }
2663 $result->setTags(implode(' ', $this->tags));
2664
2665 $this->result_collection[] = $result;
2666 }
2667
2668 function SavetoDB()
2669 {
2670 if (isset($this->result_collection) && $this->result_collection)
2671 {
2672 $this->result_collection->save(true);
2673 }
2674 unset($this->result_collection);
2675 }
2676
2677 static function ShowMsg($str, $color = 'green')
2678 {
2679 $class = $color == 'green' ? 'ui-alert-primary ui-alert-icon-info' : 'ui-alert-danger ui-alert-icon-danger';
2680 return '<br><div class="ui-alert ' . $class . '"><span class="ui-alert-message">' . $str . '</span></div><br>';
2681 }
2682
2683 static function HumanSize($s)
2684 {
2685 $i = 0;
2686 $ar = ['b', 'kb', 'M', 'G'];
2687 while ($s > 1024)
2688 {
2689 $s /= 1024;
2690 $i++;
2691 }
2692 return round($s, 1) . ' ' . $ar[$i];
2693 }
2694
2695 static function getIsolateButton($file_path)
2696 {
2697 $file_path = htmlspecialcharsbx(CUtil::JSEscape($file_path));
2698 return '<a class="ui-btn ui-btn-danger ui-btn-sm" style="text-decoration: none; color: #ffffff;" onclick="xscan_prison(\'' . $file_path . '\')">' . GetMessage("BITRIX_XSCAN_ISOLATE") . '</a>';
2699 }
2700
2701 static function getUnIsolateButton($file_path)
2702 {
2703 $file_path = htmlspecialcharsbx(CUtil::JSEscape($file_path));
2704 return '<a class="ui-btn ui-btn-primary ui-btn-sm" style="text-decoration: none; color: #ffffff;" onclick="xscan_release(\'' . $file_path . '\')">' . GetMessage("BITRIX_XSCAN_UNISOLATE") . '</a>';
2705 }
2706
2707 static function getHideButton($file_path)
2708 {
2709 $file_path = htmlspecialcharsbx(CUtil::JSEscape($file_path));
2710 return '<a class="ui-btn ui-btn-success ui-btn-sm" style="text-decoration: none; color: #ffffff;" onclick="xscan_hide(\'' . $file_path . '\')">' . GetMessage("BITRIX_XSCAN_HIDE_BTN") . '</a>';
2711 }
2712
2713 static function getFileWatchLink($file_path)
2714 {
2715 return sprintf(
2716 '<a href="?action=showfile&file=%s">%s</a>',
2717 urlencode($file_path),
2718 htmlspecialcharsbx($file_path)
2719 );
2720 }
2721
2722 static function getFileWatchButton($file_path)
2723 {
2724 return sprintf(
2725 '<a class="ui-btn ui-btn-sm" style="text-decoration: none; color: #ffffff;" target="_blank" href="?action=showfile&file=%s">' . GetMessage("BITRIX_XSCAN_WATCH_EVENT") . '</a>',
2726 urlencode($file_path)
2727 );
2728 }
2729
2730 static function getEventWatchLink($event, $table, $id)
2731 {
2732 return sprintf(
2733 '<a target="_blank" href="/bitrix/admin/perfmon_row_edit.php?table_name=%s&pk[ID]=%d">%s</a>',
2734 $table,
2735 $id,
2736 htmlspecialcharsbx($event)
2737 );
2738 }
2739
2740 static function getEventWatchButton($table, $id)
2741 {
2742 return sprintf(
2743 '<a class="ui-btn ui-btn-sm" target="_blank" style="text-decoration: none; color: #ffffff;" href="/bitrix/admin/perfmon_row_edit.php?table_name=%s&pk[ID]=%d">' . GetMessage("BITRIX_XSCAN_WATCH_EVENT") . '</a>',
2744 $table,
2745 $id
2746 );
2747 }
2748
2749 static function getTriggerWatchButton()
2750 {
2751 return '<a class="ui-btn ui-btn-sm" target="_blank" style="text-decoration: none; color: #ffffff;" href="/bitrix/admin/sql.php?query=show%20triggers;">' . GetMessage("BITRIX_XSCAN_WATCH_EVENT") . '</a>';
2752 }
2753
2754 static function getTotal($filter)
2755 {
2756 return XScanResultTable::getCount($filter);
2757 }
2758
2759 static function getList($filter, $nav, $sort)
2760 {
2761 $output = [];
2762
2763 $results = XScanResultTable::getList([
2764 'filter' => $filter,
2765 'offset' => $nav->getOffset(),
2766 'limit' => $nav->getlimit(),
2767 'order' => $sort['sort']
2768 ]);
2769
2770 foreach ($results as $result)
2771 {
2772 if ($result['TYPE'] === 'file')
2773 {
2774 $type = $result['MESSAGE'];
2775 $f = $result['SRC'];
2776
2777 $code = preg_match('#\[([0-9]+)\]#', $type, $regs) ? $regs[1] : 0;
2778 $fu = urlencode(trim($f));
2779 $bInPrison = strpos('[100]', $type) === false;
2780
2781 if (!file_exists($f) && file_exists($new_f = preg_replace('#\.php[578]?$#i', '.ph_', $f)))
2782 {
2783 $bInPrison = false;
2784 $f = $new_f;
2785 $fu = urlencode(trim($new_f));
2786 }
2787
2788 $action = '';
2789
2790 if (preg_match('/\.ph[_p][578]?$/i', $f))
2791 {
2792 $action = strtolower(substr($f, -4)) !== '.ph_' ? self::getIsolateButton($f) : self::getUnIsolateButton($f);
2793 }
2794
2795 $output[] = [
2796 'data' => [
2797 'ID' => $result['ID'],
2798 'FILE_NAME' => self::getFileWatchLink($f),
2799 'FILE_TYPE' => $type,
2800 'FILE_SCORE' => $result['SCORE'],
2801 'FILE_SIZE' => self::HumanSize(@filesize($f)),
2802 'FILE_MODIFY' => $result['MTIME'],
2803 'FILE_CREATE' => $result['CTIME'],
2804 'TAGS' => $result['TAGS'],
2805 'ACTIONS' => $action,
2806 'HIDE' => self::getHideButton($f),
2807 ]
2808 ];
2809 }
2810 elseif ($result['TYPE'] === 'trigg')
2811 {
2812 $output[] = [
2813 'data' => [
2814 'ID' => $result['ID'],
2815 'FILE_NAME' => $result['SRC'],
2816 'FILE_TYPE' => $result['MESSAGE'],
2817 'FILE_SCORE' => $result['SCORE'],
2818 'ACTIONS' => self::getTriggerWatchButton()
2819 ]
2820 ];
2821 }
2822 else
2823 {
2824
2825 $table = match ($result['TYPE'])
2826 {
2827 'agent' => 'b_agent',
2828 'event' => 'b_module_to_module',
2829 'tmpl' => 'b_site_template',
2830 };
2831
2832 $output[] = [
2833 'data' => [
2834 'ID' => $result['ID'],
2835 'FILE_NAME' => self::getEventWatchLink($result['TYPE'] . " " . $result['SRC'], $table, $result['SRC']),
2836 'FILE_TYPE' => $result['MESSAGE'],
2837 'FILE_SCORE' => $result['SCORE'],
2838 'ACTIONS' => self::getEventWatchButton($table, $result['SRC'])
2839 ]
2840 ];
2841 }
2842 }
2843
2844 return $output;
2845 }
2846}
$path
Определения access_edit.php:21
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
$start_time
Определения clock_selector.php:9
if($childrenCount<=0) $mod
Определения sync.php:11
$right
Определения options.php:8
$options
Определения commerceml2.php:49
$str
Определения commerceml2.php:63
$content
Определения commerceml.php:144
$f
Определения component_props.php:52
if(!is_array($prop["VALUES"])) $tmp
Определения component_props.php:203
$data['IS_AVAILABLE']
Определения .description.php:13
$arr
Определения file_new.php:624
background color
Определения file_new.php:745
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
$bFound
Определения get_search.php:40
$p
Определения group_list_element_edit.php:23
$errors
Определения iblock_catalog_edit.php:74
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
global $DB
Определения cron_frame.php:29
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения tools.php:2701
ConvertTimeStamp($timestamp=false, $type="SHORT", $site=false, $bSearchInSitesOnly=false)
Определения tools.php:733
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
Определения tools.php:3778
$name
Определения menu_edit.php:35
$map
Определения config.php:5
__construct(?int $storeId, int $productId, string $barcode, int $userId)
Определения basestorebarcodeaction.php:38
$value
Определения Param.php:39
setType(string $type)
Определения Param.php:121
getErrors()
Определения errorableimplementation.php:34
$var
Определения payment.php:63
$message
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$ar
Определения options.php:199
$dir
Определения quickway.php:303
$config
Определения quickway.php:69
if(empty($signedUserToken)) $key
Определения quickway.php:257
die
Определения quickway.php:367
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$comment
Определения template.php:15
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
else $a
Определения template.php:137
$val
Определения options.php:1793
$sHost
Определения result.php:13
$matches
Определения index.php:22
$n
Определения update_log.php:107