PluginProbe
WebTotem Security / 2.4.31
WebTotem Security v2.4.31
3.0.1 3.0.0 trunk 1.0 1.1 1.2 1.3 1.3.1 1.3.2 1.3.3 2.0 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.1 2.2.2 2.2.3 2.2.4 All 109 releases
wt-security / lib / Helper.php

Helper.php in WebTotem Security 2.4.31, at lib/Helper.php

2,041 lines 56.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('WEBTOTEM_INIT') || WEBTOTEM_INIT !== true) {
4 if (!headers_sent()) {
5 header('HTTP/1.1 403 Forbidden');
6 }
7 exit(1);
8 }
9
10 /**
11 * WebTotem Base class for Wordpress.
12 */
13 class WebTotem {
14
15 /**
16 * Returns an URL from the admin dashboard.
17 *
18 * @param string $url
19 * Optional trailing of the URL.
20 * @return string
21 * Full valid URL from the admin dashboard.
22 */
23 public static function adminURL($url = '') {
24 if (self::isMultiSite() and is_super_admin()) {
25 return network_admin_url($url);
26 }
27 return admin_url($url);
28 }
29
30 /**
31 * Define role of current user.
32 *
33 */
34 public static function getUserRole() {
35
36 if (defined('WEBTOTEM_USER_ROLE')) {
37 return true;
38 }
39 $current_user = wp_get_current_user();
40 if ( !($current_user instanceof WP_User) ){
41 $user_role = 0;
42 } else {
43 $roles = $current_user->roles;
44
45 if(in_array('administrator', $roles)) {
46 $user_role = 1;
47 } elseif(in_array('editor', $roles) or current_user_can('publish_posts')) {
48 $user_role = 2;
49 } else {
50 $user_role = 0;
51 }
52 }
53
54 define( 'WEBTOTEM_USER_ROLE', $user_role );
55
56 return true;
57 }
58
59 /**
60 * Check whether the current site is working as a multi-site instance.
61 *
62 * @return bool
63 * Either TRUE or FALSE in case WordPress is being used as a multi-site instance.
64 */
65 public static function isMultiSite() {
66 return (bool) ( (function_exists('is_multisite') && is_multisite()) || (defined('MULTISITE') && MULTISITE == true) );
67 }
68
69
70 /**
71 * Get user email.
72 *
73 * @return string
74 * Returns user email.
75 */
76 public static function getUserEmail() {
77 $email = WebTotemOption::getOption( "user_email" );
78 if(!$email){
79 if(WebTotemOption::isActivated()) {
80 $email = WebTotemAPI::getEmail();
81 }
82 WebTotemOption::setOptions(['user_email' => $email]);
83 }
84 return $email;
85 }
86
87 /**
88 * Returns the md5 hash representing the content of a file.
89 *
90 * @param string $file
91 * Relative path to the file.
92 * @return string
93 * Seven first characters in the hash of the file.
94 */
95 public static function fileVersion($file = '') {
96 return substr(md5_file(WEBTOTEM_PLUGIN_PATH . '/' . $file), 0, 7);
97 }
98
99 /**
100 * Returns full path to image.
101 *
102 * @param string $image
103 * Relative path to the file.
104 * @return string
105 * Full path to image.
106 */
107 public static function getImagePath($image) {
108 return WEBTOTEM_URL. '/includes/img/' . $image;
109 }
110
111 /**
112 * Checking whether the current domain belongs to the kz domain zone.
113 *
114 * @return bool
115 * true is returned if the domain belongs to the kz domain zone.
116 */
117 public static function isKz() {
118 $is_kz = json_decode(WebTotemOption::getOption('is_kz'), true);
119 if(is_array($is_kz)){
120 return $is_kz['value'];
121 }
122
123 if(function_exists('idn_to_utf')){
124 $host = idn_to_utf8($_SERVER['HTTP_HOST']);
125 } else {
126 $host = $_SERVER['HTTP_HOST'];
127 }
128
129 $parts = explode('.', $host);
130 $domain_zone = $parts[count($parts)-1];
131
132 if($domain_zone === 'kz' or $domain_zone === 'қаз'){
133 $is_kz['value'] = true;
134 } else {
135 $is_kz['value'] = false;
136 }
137
138 WebTotemOption::setOptions(['is_kz' => $is_kz]);
139
140 return $is_kz['value'];
141 }
142
143 /**
144 * Convert object to array.
145 *
146 * @param array $data
147 * Array.
148 * @return array
149 * Returns array.
150 */
151 public static function convertObjectToArray($data){
152
153 if(!is_array($data)) $data = (array)$data;
154 array_walk_recursive($data, function(&$item){
155 if(is_object($item)) $item = (array)$item;
156 });
157
158 return $data;
159 }
160
161 /**
162 * Removing duplicates by one key.
163 *
164 * @param array $array
165 * Array.
166 * @param string $key
167 * Delete duplicates with the same key.
168 *
169 * @return array
170 * Returns array.
171 */
172 public static function arrayUniqueKey($array, $key) {
173 $tmp = $key_array = array();
174 $i = 0;
175
176 foreach ($array as $val) {
177 if (!in_array($val[$key], $key_array)) {
178 $key_array[$i] = $val[$key];
179 $tmp[$i] = $val;
180 }
181 $i++;
182 }
183 return $tmp;
184 }
185
186 /**
187 * Returns user IP address.
188 *
189 * @return string
190 * Returns user IP address.
191 */
192 public static function getUserIP() {
193 $arr = [
194 'HTTP_CLIENT_IP',
195 'HTTP_X_FORWARDED_FOR',
196 'HTTP_X_FORWARDED',
197 'HTTP_X_CLUSTER_CLIENT_IP',
198 'HTTP_FORWARDED_FOR',
199 'HTTP_FORWARDED',
200 'HTTP_CF_CONNECTING_IP',
201 'REMOTE_ADDR'
202 ];
203
204 foreach ($arr as $key){
205 if (array_key_exists($key, $_SERVER) === true) {
206 foreach (explode(',', $_SERVER[$key]) as $ip) {
207 $ip = trim($ip);
208 $ip = filter_var($ip, FILTER_VALIDATE_IP);
209 if (!empty($ip)) {
210 return $ip;
211 }
212 }
213 }
214 }
215 return false;
216 }
217
218 /**
219 * Convert the file size to а human-readable format.
220 *
221 * @param string $bytes
222 * File size in bytes.
223 * @param string $decimals
224 * The number of characters after the decimal point.
225 *
226 * @return string
227 * Returns the file size in a human-readable format.
228 */
229 public static function humanFilesize($bytes, $decimals = 2) {
230 $factor = floor((strlen($bytes) - 1) / 3);
231 $unit_of_measurement = ($factor > 0) ? substr("KMGT", $factor - 1, 1) : '';
232 $size = sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . $unit_of_measurement . 'B';
233 return str_replace(".00", "", $size);
234 }
235
236 /**
237 * Check whether the file is publicly accessible.
238 *
239 * @param string $url
240 * http link to the file.
241 * @param string $path
242 * The path to the file.
243 *
244 * @return bool
245 */
246 public static function isPubliclyAccessible($url, $path) {
247 $response = wp_remote_get($url);
248
249 if ((int) floor(((int) wp_remote_retrieve_response_code($response) / 100)) === 2) {
250 $handle = @fopen($path, 'r');
251 if ($handle) {
252 $contents = fread($handle, 700);
253 fclose($handle);
254 $remoteContents = substr(wp_remote_retrieve_body($response), 0, 700);
255
256 return $contents === $remoteContents;
257 }
258 }
259 return false;
260 }
261
262 /**
263 * Check that the training period has passed for the firewall.
264 *
265 * @param string $created_at
266 * Date when the waf configuration was created.
267 *
268 * @return bool
269 * Returns boolean.
270 */
271 public static function isWafTraining($created_at) {
272 if($created_at) {
273 $when_waf_trained = strtotime('+2 day', strtotime($created_at));
274 $today = strtotime('today');
275
276 return ($when_waf_trained < $today) ? FALSE : TRUE;
277 }
278 return FALSE;
279 }
280
281 /**
282 * Check if the data for the period is available.
283 *
284 * @param string $created_at
285 * Date when the agent manager was created.
286 *
287 * @return array
288 * Returns an array with periods.
289 */
290 public static function isPeriodAvailable($created_at) {
291
292 $diff = strtotime(gmdate("Y-m-d H:i:s")) - strtotime($created_at);
293 $daysCount = floor($diff / 86400);
294
295 return [
296 'monthly' => $daysCount > 7,
297 'yearly' => $daysCount > 30,
298 ];
299
300 }
301
302 /**
303 * Converting a date to the appropriate format.
304 *
305 * @param string $date
306 * Date in any format.
307 * @param string $format
308 * The format to which you want to convert the date.
309 *
310 * @return string
311 * Returns converted Date.
312 * @throws Exception
313 */
314 public static function dateFormatter($date, $format = 'M j, Y \/ H:i') {
315 if (!$date) {
316 return __('Unknown', 'wtotem');
317 }
318
319 if ( is_numeric($date) && (int)$date == $date ){
320 $date = date('Y-m-d H:i', $date);
321 }
322
323 if($wp_timezone = wp_timezone()){
324 $UTC = new DateTimeZone("UTC");
325 $date = new DateTime( $date, $UTC );
326 $date->setTimezone( new DateTimeZone($wp_timezone->getName()) );
327 return date_i18n($format,strtotime($date->format('Y-m-d H:i')));
328 }
329
330 $time_zone = WebTotemOption::getOption('time_zone_offset');
331 $user_time = ($time_zone) ? strtotime($time_zone . 'hours', strtotime($date)) : strtotime($date);
332
333 return date_i18n($format, $user_time);
334 }
335
336 /**
337 * Get theme mode data.
338 *
339 * @return array
340 * Returns array with current theme data.
341 */
342 public static function getThemeMode() {
343 $theme_mode = WebTotemOption::getSessionOption('theme_mode');
344 return [
345 "is_dark_mode" => $theme_mode == 'dark' ? 'wtotem_theme—dark' : '',
346 "dark_mode_checked" => $theme_mode == 'dark' ? 'checked' : '',
347 ];
348 }
349
350 /**
351 * Get current user language.
352 *
353 * @return string
354 * Returns current language in 2-letter abbreviations
355 */
356 public static function getLanguage() {
357 $current_language = substr(get_bloginfo('language'), 0,2);
358 $language = (in_array($current_language,['ru','en','pl'])) ? $current_language : 'en' ;
359 return $language;
360 }
361
362 /**
363 * Converting a date to the appropriate format.
364 *
365 * @param string|array $days
366 * Number of days or period to convert.
367 *
368 * @return array
369 * Returns an array of two values "from" and "to"
370 */
371 public static function getPeriod($days) {
372
373 if (!$days) {
374 $days = 30;
375 }
376
377 switch ($days) {
378
379 case is_array($days):
380 $to = $days[1] ?: $days[0];
381 $period = [
382 'from' => strtotime(date('Y-m-d 00:00:01', strtotime(self::formatDate($days[0])))),
383 'to' => strtotime(date('Y-m-d 23:59:59', strtotime(self::formatDate($to)))),
384 ];
385 break;
386
387 case $days <= 1:
388 $period = [
389 'from' => strtotime('-24 hours'),
390 'to' => time(),
391 ];
392 break;
393
394 default:
395 $period = [
396 'from' => time() - ($days * 86400),
397 'to' => time(),
398 ];
399 }
400
401 return $period;
402 }
403
404 /**
405 * Converting a date from "j M, Y" format to 'd-m-Y' format.
406 *
407 * @return string
408 * Returns date in new format
409 */
410 public static function formatDate($date){
411 $month = [
412 'Jan' => 'Янв',
413 'Feb' => 'Фев',
414 'Mar' => 'Мар',
415 'Apr' => 'Апр',
416 'May' => 'Май',
417 'Jun' => 'Июн',
418 'Jul' => 'Июл',
419 'Aug' => 'Авг',
420 'Sep' => 'Сен',
421 'Oct' => 'Окт',
422 'Nov' => 'Ноя',
423 'Dec' => 'Дек',
424 ];
425
426 foreach ($month as $key => $value) {
427 if (strpos($date, $value)) {
428 $date = str_replace($value, $key, $date);
429 }
430 }
431
432 $pattern = '/^(\d{1,2})\s+([a-zA-Z]+),\s+(\d{4})$/';
433
434 if (preg_match($pattern, $date, $matches)) {
435 $monthNumber = date_parse($matches[2])['month'];
436
437 return sprintf('%02d-%02d-%04d', $matches[1], $monthNumber, $matches[3]);
438 }
439
440 return $date;
441
442 }
443
444 /**
445 * Convert an array to a string with quotation marks.
446 *
447 * @param array $array
448 * Data array.
449 *
450 * @return string
451 * Array of data converted to string.
452 */
453 public static function convertArrayToString($array) {
454 if(empty($array)){
455 return '';
456 }
457 return '"' . implode('","', $array) . '"';
458 }
459
460 /**
461 * Converting the response to a readable form.
462 *
463 * @param string $message
464 * Message response from the API server to the request.
465 *
466 * @return string|bool
467 * Returns a message.
468 */
469 public static function messageForHuman($message) {
470
471 $definition = $message;
472
473 switch ($message) {
474 case 'HOSTS_LIMIT_EXCEEDED':
475 $definition = __('Limit of adding sites exceeded.', 'wtotem');
476 break;
477
478 case 'USER_ALREADY_REGISTERED':
479 $definition = __('A user with this email already exists.', 'wtotem');
480 break;
481
482 case 'DUPLICATE_HOST':
483 $definition = __('Duplicate host', 'wtotem');
484 break;
485
486 case 'INVALID_DOMAIN_NAME':
487 $definition = __('Invalid Domain Name', 'wtotem');
488 break;
489 default:
490 $definition = str_replace("_", " ", $definition);
491 $definition = ucfirst(strtolower($definition));
492
493 }
494 return $definition;
495 }
496
497 /**
498 * Get the data associated with the status.
499 *
500 * @param string $status
501 * Module or agent status.
502 *
503 * @return array
504 * Returns an array with status data.
505 */
506 public static function getStatusData($status) {
507 $path = self::getImagePath('');
508 $status = ($status == "installed") ? 'working' : $status;
509
510 switch ($status) {
511
512 case 'clean':
513 case 'up':
514 case 'installed':
515 case 'working':
516 case 'good':
517 $status_data = [
518 'class' => 'is--status--ok',
519 'image' => $path . 'check-mark.svg',
520 'icon' => $path . 'icon_success_status.svg',
521 ];
522 break;
523
524 case 'pending':
525 $status_data = [
526 'class' => 'is--status--pending',
527 'image' => $path . 'loading.svg',
528 'icon' => $path . 'alert-warning.svg',
529 ];
530 break;
531
532 case 'pause':
533 case 'modified':
534 $status_data = [
535 'class' => 'is--status--pending',
536 'image' => $path . 'warning.svg',
537 'icon' => $path . 'alert-warning.svg',
538 ];
539 break;
540
541 case 'expired':
542 case 'no_cert':
543 case 'expires':
544 case 'open_ports':
545 case 'warning':
546 case 'not_supported':
547 case 'not_registered':
548 $status_data = [
549 'class' => 'is--status--warning',
550 'image' => $path . 'warning.svg',
551 'icon' => $path . 'alert-warning.svg',
552 ];
553 break;
554
555 case 'invalid':
556 case 'revoked':
557 case 'untrusted':
558 case 'not_found':
559 case 'wrong_host':
560 case 'error':
561 case 'down':
562 case 'expires_today':
563 case 'infected':
564 case 'deface':
565 case 'not_installed':
566 $status_data = [
567 'class' => 'is--status--error',
568 'image' => $path . 'warning.svg',
569 'icon' => $path . 'alert-warning.svg',
570 ];
571 break;
572
573 default:
574 $status_data = [
575 'class' => 'is--status--pending',
576 'image' => $path . 'warning.svg',
577 'icon' => $path . 'alert-warning.svg',
578 ];
579 }
580 $status_data['name'] = $status;
581 $status_data['text'] = self::getStatusText($status);
582 $status_data['tooltips'] = self::getTooltips($status);
583
584 return $status_data;
585 }
586
587 /**
588 * Get a readable status text.
589 *
590 * @param string $status
591 * Module or agent status.
592 *
593 * @return string
594 * Returns the status text in the current language.
595 */
596 public static function getStatusText($status) {
597 $statuses = [
598 'warning' => __('Warning', 'wtotem'),
599 'error' => __('Error', 'wtotem'),
600 'success' => __('Success', 'wtotem'),
601 'info' => __('Info', 'wtotem'),
602 'invalid' => __('Invalid', 'wtotem'),
603 'ok' => __('Everything is OK', 'wtotem'),
604 'expired' => __('Expired', 'wtotem'),
605 'expires' => __('Expires', 'wtotem'),
606 'expires_today' => __('Expires today', 'wtotem'),
607 'missing' => __('Missing', 'wtotem'),
608 'active' => __('Active', 'wtotem'),
609 'inactive' => __('Inactive', 'wtotem'),
610 'pending' => __('Pending', 'wtotem'),
611 'pause' => __('Disabled', 'wtotem'),
612 'available' => __('Available', 'wtotem'),
613 'not_supported' => __('Not supported', 'wtotem'),
614 'not_registered' => __('Not registered', 'wtotem'),
615 'unsupported' => __('Unsupported', 'wtotem'),
616 'clean' => __('Clean', 'wtotem'),
617 'clear' => __('Clear', 'wtotem'),
618 'blacklisted' => __('Infected', 'wtotem'),
619 'miner_detected' => __('Infected', 'wtotem'),
620 'deface' => __('Deface', 'wtotem'),
621 'modified' => __('Modified', 'wtotem'),
622 'detected' => __('Detected', 'wtotem'),
623 'open_ports' => __('Open ports', 'wtotem'),
624 'blocked' => __('Blocked', 'wtotem'),
625 'connected' => __('Connected', 'wtotem'),
626 'attacks_detected' => __('Attacks detected', 'wtotem'),
627 'signature_found' => __('Signature found', 'wtotem'),
628 'file_changes' => __('File changes', 'wtotem'),
629 'no_cert' => __('No cert', 'wtotem'),
630 'down' => __('Down', 'wtotem'),
631 'up' => __('Up', 'wtotem'),
632 'infected' => __('Infected', 'wtotem'),
633 'not_installed' => __('Need to install', 'wtotem'),
634 'agent_not_available' => __('Agent not available', 'wtotem'),
635 'update_error' => __('Update error', 'wtotem'),
636 'session_error' => __('Session Error', 'wtotem'),
637 'internal_error' => __('Internal Error', 'wtotem'),
638 'installing' => __('Installing', 'wtotem'),
639 'installed' => __('Installed', 'wtotem'),
640 'working' => __('Working', 'wtotem'),
641 "critical" => __('Critical', 'wtotem'),
642 "deleted" => __('Deleted', 'wtotem'),
643 "changed" => __('Changed', 'wtotem'),
644 "new" => __('New', 'wtotem'),
645 "scanned" => __('Scanned', 'wtotem'),
646 "quarantine" => __('In quarantine', 'wtotem'),
647 "good" => __('Good', 'wtotem'),
648 "wrong_host" => __('Wrong host', 'wtotem'),
649 "revoked" => __('Revoked', 'wtotem'),
650 "untrusted" => __('Untrusted', 'wtotem'),
651 "not_found" => __('Not found', 'wtotem'),
652 ];
653
654 return (array_key_exists($status, $statuses)) ? $statuses[$status] : $status;
655 }
656
657 /**
658 * Get tooltips text for status.
659 *
660 * @param string $status
661 * Module or agent status.
662 *
663 * @return string
664 * Returns the status tooltip in the current language.
665 */
666 public static function getTooltips($status) {
667 $tooltips = [
668 'invalid' => __('Invalid -The certificate is invalid. Please, make sure that relevant certificate details filled correctly.', 'wtotem'),
669 'expired' => __('Expired - The certificate has expired. Connection is not secure. Please, renew it.', 'wtotem'),
670 'expires' => __('Expires - The certificate expires soon. Please, take actions.', 'wtotem'),
671 'expires_today' => __('Expires today - The certificate expires today. Please, take actions.', 'wtotem'),
672 'error' => __("Error - Something went wrong. Please, contact us, we'll fix the problem.", 'wtotem'),
673 'pending' => __('Pending - System processes your website. Data will be available soon.', 'wtotem'),
674 'pause' => __('Pause - The module is paused.', 'wtotem'),
675 'clean' => __('Everything is OK - Nothing to worry about. Everything is alright.', 'wtotem'),
676 'deface' => __("Deface - Website hacked. Please, contact us, we'll fix the problem.", 'wtotem'),
677 'open_ports' => __('Open ports - Open ports detected. Your website is vulnerable to attacks.', 'wtotem'),
678 'blocked' => __('Blocked - The module is blocked due to billing issues.', 'wtotem'),
679 'no_cert' => __("No cert - You don't have SSL certificate. We recommend you to install it for security concerns.", 'wtotem'),
680 'down' => __('Down - The website is not available for visitors.', 'wtotem'),
681 'up' => __('Up - The website is available for visitors.', 'wtotem'),
682 'infected' => __('Infected - The website site is blacklisted and may have infected files. Please, check antivirus module.', 'wtotem'),
683 'installing' => __('It means that the agent installation is in progress. Usually, it takes up to one hour.', 'wtotem'),
684 'agent_not_available' => __('We cannot locate the agent right now.', 'wtotem'),
685 'update_error' => __('It seems that your agent failed to update due to permissions restrictions.', 'wtotem'),
686 'session_error' => __('This means that the agent did not create a secure session. Possible causes include network issues, wrong server configuration, third-party firewalls. Please contact our support..', 'wtotem'),
687 'internal_error' => __('It means that the server is overloaded or there might be some problems with the connection. Usually, the issue resolves itself within 10-15 minutes. If the status does not change during two hours, please cordially contact our support..', 'wtotem'),
688 'working' => __('Everything is alright.', 'wtotem'),
689 'installed' => __('Everything is alright.', 'wtotem'),
690 'not_installed' => __('You need to install agent manager to activate antivirus and firewall.', 'wtotem'),
691 ];
692
693 return (array_key_exists($status, $tooltips)) ? $tooltips[$status] : '';
694 }
695
696 /**
697 * Converting site data.
698 *
699 * @param array $data
700 * Sites data from WebTotem.
701 *
702 * @return array
703 * Converted data.
704 */
705 public static function allSitesData($data) {
706
707 $local_sites = get_sites();
708 $main_host = WebTotemOption::getMainHost();
709 $domains = [];
710
711 foreach ($local_sites as $site){
712 $domain = untrailingslashit($site->domain . $site->path);
713 $domains[$domain] = $domain;
714 }
715
716 $sites = [];
717 if(array_key_exists('edges', $data)){
718 foreach ($data['edges'] as $site) {
719 $site = $site['node'];
720 // Take sites only from the multisite network.
721 if(array_key_exists($site['hostname'], $domains)) {
722 unset($domains[$site['hostname']]);
723 $sites[] = [
724 'hostname' => $site['hostname'],
725 'title' => $site['title'],
726 'main_host' => $main_host['id'] == $site['id'],
727 'host_id' => $site['id'],
728 'url' => admin_url('admin.php?page=wtotem_dashboard&hid=' . $site['id']),
729 'firewall' => [
730 'status' => self::getStatusData($site['firewall']['status']),
731 ],
732 'antivirus' => [
733 'status' => self::getStatusData($site['antivirus']['status']),
734 ],
735 'stacks' => self::getStacksData($site['maliciousScript']['stack']),
736 'services' => self::getSiteServicesData($site),
737 ];
738 }
739 }
740
741 }
742 return $sites;
743 }
744
745 /**
746 * Converting stacks data.
747 *
748 * @param array $stacks
749 * Stacks data from WebTotem.
750 *
751 * @return array
752 * Converted data.
753 */
754 protected static function getStacksData($stacks) {
755 $apps = file_get_contents(WEBTOTEM_PLUGIN_PATH . '/includes/js/apps.json');
756 $apps = json_decode($apps, true);
757
758 $path = 'https://assets.wtotem.net/images/apps/';
759 $defaultIcon = WEBTOTEM_URL . '/includes/img/defaultTechnologiesIcon.svg';
760
761 $stackList = array_slice($stacks, 0,3);
762 $list = [];
763 foreach ($stackList as $key => $stack){
764 $list[$key] = [
765 'name' => $stack['name'],
766 'icon' => $path . ($apps[$stack['name']]['icon'] ?: $defaultIcon),
767 ];
768 }
769
770 if(count($stacks) <= 3){
771 $other['count'] = 0;
772 $other['names'] = [];
773 } else {
774 $otherStacks = array_slice($stacks, 3);
775 $other['count'] = count($otherStacks);
776 foreach ($otherStacks as $stack){
777 $other['names'][] = $stack['name'];
778 }
779 }
780 if($other['names']){
781 $other['names'] = implode(",", $other['names']);
782 }
783 return ['list' => $list, 'other' => $other];
784 }
785
786 /**
787 * Converting services data.
788 *
789 * @param $data
790 * Site data from WebTotem.
791 *
792 * @return array
793 * Converted data.
794 */
795 protected static function getSiteServicesData($data) {
796
797 $services = [
798 'ssl' => 'ssl',
799 'availability' => 'wa',
800 'reputation' => 'rc',
801 'ports' => 'ps',
802 'deface' => 'dc',
803 'domain' => 'dec',
804 ];
805
806 $list = [];
807 $other['count'] = 0;
808 $other['names'] = [];
809
810 foreach ($services as $key => $service){
811 if(array_key_exists($key, $data) and is_array($data[$key]) and array_key_exists('status', $data[$key])){
812 $status = self::getServiceStatus($data[$key]['status']);
813
814 if(in_array($status['color'], ['red', 'yellow'])){
815 if(count($list) < 2){
816 $color = $status['color'] == 'red' ? 'white/' : '';
817
818 $list[$key] = [
819 'status' => $status,
820 'icon' => 'services/'. $color . $service . '.svg',
821 'name' => self::getServiceName( $service ),
822 ];
823 } else {
824 $other['names'][] = self::getServiceName( $service );
825 $other['count']++;
826 }
827 }
828
829 }
830 }
831 if($other['names']){
832 $other['names'] = implode(",", $other['names']);
833 }
834 return ['list' => $list, 'other' => $other];
835 }
836
837 /**
838 * Get the data associated with the status.
839 *
840 * @param string $status
841 * Module or agent status.
842 *
843 * @return array
844 * Returns an array with status data.
845 */
846 public static function getServiceStatus($status) {
847 switch ($status) {
848
849 case 'expired':
850 case 'invalid':
851 case 'error':
852 case 'expires_today':
853 case 'down':
854 case 'infected':
855 case 'deface':
856 case 'not_installed':
857 case 'quarantine':
858 $status_data = [
859 'color' => 'red',
860 ];
861 break;
862
863 case 'no_cert':
864 case 'expires':
865 case 'open_ports':
866 case 'modified':
867 case 'not_supported':
868 case 'not_registered':
869 case 'blocked':
870 case 'pause':
871 case 'internal_error':
872 case 'update_error':
873 case 'config_error':
874 case 'agent_not_available':
875 case 'session_error':
876 $status_data = [
877 'color' => 'yellow',
878 ];
879 break;
880
881 case 'clean':
882 case 'installed':
883 case 'up':
884 case 'scanned':
885 case 'working':
886 $status_data = [
887 'color' => 'green',
888 ];
889 break;
890
891 case 'deleted':
892 $status_data = [
893 'color' => 'black',
894 ];
895 break;
896
897 case 'installing':
898 case 'pending':
899 default:
900 $status_data = [
901 'color' => 'gray',
902 ];
903 break;
904
905 }
906
907 return $status_data;
908 }
909
910 /**
911 * Get the translation of service.
912 *
913 * @param $service
914 * Service short name.
915 *
916 * @return string
917 * Translation of service.
918 */
919 public static function getServiceName($service){
920 $services = [
921 "wa" => __('Availability', 'wtotem'),
922 "rc" => __('Reputation', 'wtotem'),
923 "ssl" => 'SSL',
924 "cms" => __('Technologies', 'wtotem'),
925 "dc" => __('Deface', 'wtotem'),
926 "ps" => __('Ports', 'wtotem'),
927 "waf" => __('Firewall', 'wtotem'),
928 "av" => __('Antivirus', 'wtotem'),
929 "dec" => __('Domain', 'wtotem'),
930 ];
931 return $services[$service];
932 }
933
934 /**
935 * Get reports with modules list.
936 *
937 * @param array $edges
938 * Data on generated reports.
939 *
940 * @return array
941 * Returns an array with converted data.
942 */
943 public static function getReports(array $edges) {
944 $modulesLang = [
945 'wa' => __('Availability log', 'wtotem'),
946 'dc' => __('Deface log', 'wtotem'),
947 'ps' => __('Port log', 'wtotem'),
948 'rc' => __('Reputation log', 'wtotem'),
949 'sc' => __('Evaluation log', 'wtotem'),
950 'av' => __('Antivirus log', 'wtotem'),
951 'waf' => __('Firewall log', 'wtotem'),
952 ];
953
954 $reports = [];
955
956 foreach ($edges as $edge) {
957 if (in_array(FALSE, $edge["node"])) {
958 $arr = [];
959 foreach ($edge["node"] as $module => $value) {
960 if ($value == TRUE && array_key_exists($module, $modulesLang)) {
961 $arr[] = $modulesLang[$module];
962 }
963 }
964 $modules = implode(", ", $arr);
965 }
966 else {
967 $modules = __('All modules', 'wtotem');
968 }
969
970 $reports[] = [
971 'id' => $edge["node"]['id'],
972 'modules' => $modules,
973 'created_at' => self::dateFormatter($edge["node"]['createdAt']),
974 ];
975 }
976
977 return $reports;
978 }
979
980 /**
981 * Get reputation status description.
982 *
983 * @param string $status
984 * Reputation status.
985 *
986 * @return string
987 * Returns a description of the reputation status
988 */
989 public static function getReputationInfo($status) {
990 switch ($status) {
991 case 'clean':
992 $data = __("Don't worry, your reputation is good", 'wtotem');
993 break;
994
995 case 'infected':
996 $data = __('Oh, your reputation is bad', 'wtotem');
997 break;
998
999 default:
1000 $data = __('Information is being updated', 'wtotem');
1001 }
1002 return $data;
1003 }
1004
1005 /**
1006 * Get blacklists entries counts.
1007 *
1008 * @param string $status
1009 * Reputation status.
1010 * @param array $virus_list
1011 * Sources where the site can be blacklisted.
1012 *
1013 * @return int
1014 * Number of references in blacklists.
1015 */
1016 public static function blacklistsEntries($status, array $virus_list) {
1017 $count = 0;
1018 if ($status != "clean") {
1019 foreach ($virus_list as &$list) {
1020 if (!empty($list['virus']['type'])) {
1021 $count++;
1022 }
1023 }
1024 }
1025 return $count;
1026 }
1027
1028 /**
1029 * Classification of the rating in the letter grades.
1030 *
1031 * @param int $score
1032 * Site rating from 1 to 100.
1033 *
1034 * @return array
1035 * Returns an array of data.
1036 */
1037 public static function scoreGrading($score) {
1038 if ($score < 0 || $score > 100) {
1039 return ['grade' => '', 'color' => ''];
1040 }
1041
1042 $scores = [
1043 100 => 'A+',
1044 90 => 'A',
1045 80 => 'A-',
1046 70 => 'B+',
1047 60 => 'B',
1048 50 => 'B-',
1049 35 => 'C+',
1050 20 => 'C',
1051 0 => 'C-',
1052 ];
1053
1054 foreach ($scores as $key => $value) {
1055 if ($score >= $key) {
1056 $grade = $value;
1057 break;
1058 }
1059 }
1060
1061 // Set a color depending on the grade.
1062 switch ($score) {
1063 case $score >= 80:
1064 $color = 'green';
1065 break;
1066
1067 case $score >= 50:
1068 $color = 'orange';
1069 break;
1070
1071 default:
1072 $color = 'red';
1073 }
1074
1075 return ['grade' => $grade, 'color' => $color];
1076 }
1077
1078 /**
1079 * Calculate the number of remaining days.
1080 *
1081 * @param string $date
1082 * Expiry date.
1083 *
1084 * @return string
1085 * Returns the number of days before the expiration date.
1086 */
1087 public static function daysLeft($date) {
1088 if ((int) $date === 0) {
1089 $days_left = 0;
1090 }
1091 else {
1092 $now = new \DateTime();
1093 $expiry_date = new \DateTime();
1094 $timestamp = strtotime($date);
1095 $expiry_date->setTimestamp($timestamp);
1096 $days_left = $expiry_date->diff($now)->format("%a");
1097 }
1098 return $days_left;
1099 }
1100
1101 /**
1102 * Converting the firewall logs.
1103 *
1104 * @param array $logs_
1105 * Firewall logs from WebTotem.
1106 *
1107 * @return array
1108 * Converted array of logs.
1109 */
1110 public static function wafLogs(array $logs_) {
1111 $logs = [];
1112 foreach ($logs_ as $key => $log) {
1113 $log = $log['node'];
1114
1115 $logs[$key]['ip'] = $log['ip'];
1116 $logs[$key]['request'] = htmlspecialchars(urldecode($log['request']));
1117 $logs[$key]['time'] = self::dateFormatter($log['time']);
1118 $logs[$key]['country_code'] = strtolower($log['country']);
1119 $logs[$key]['country'] = $log['location']['country']['nameEn'];
1120 $logs[$key]['blocked'] = $log['blocked'] ? __('Blocked IP', 'wtotem') : __('Not blocked', 'wtotem');
1121
1122 $more = [
1123 'ip' => $log['ip'],
1124 'proxy_ip' => $log['proxyIp'],
1125 'source' => $log['source'],
1126 'request' => htmlspecialchars(urldecode($log['request'])),
1127 'user_agent' => $log['userAgent'],
1128 'time' => self::dateFormatter($log['time']),
1129 'type' => $log['type'],
1130 'category' => $log['category'],
1131 'country' => $log['location']['country']['nameEn'],
1132 'payload' => htmlspecialchars(urldecode($log['payload'])),
1133 ];
1134
1135 $logs[$key]['more'] = json_encode($more);
1136 }
1137 return $logs;
1138 }
1139
1140 /**
1141 * Converting firewall data to json for a D3 chart.
1142 *
1143 * @param array $charts
1144 * Charts data from WebTotem.
1145 *
1146 * @return array
1147 * Returns the converted data for chart.
1148 */
1149 public static function generateWafChart(array $charts) {
1150 $sum = 0;
1151 foreach ($charts as $chart) {
1152 $sum += $chart['attacks'];
1153 }
1154 if ($sum == 0) {
1155 return ['chart' => FALSE, 'count_attacks' => 0, 'count_blocks' => 0];
1156 }
1157
1158 // Get days count.
1159 $charts_ = $charts;
1160 $first = array_shift($charts_);
1161 $last = array_pop($charts_);
1162 $days = ceil((strtotime($last['time']) - strtotime($first['time'])) / 86400);
1163
1164 // Set variables.
1165 $count_attacks = $count_blocks = 0;
1166
1167 foreach ($charts as $chart) {
1168 if ($days <= 1) {
1169 $time_zone = WebTotemOption::getOption('time_zone_offset');
1170 $userTime = ($time_zone) ? strtotime($time_zone . ' hours', strtotime($chart['time'])) : strtotime($chart['time']);
1171 }
1172 if (($chart['attacks'] and $days == 2) or $days != 2) {
1173 $result[] = [
1174 'date' => ($days <= 1) ? date("Y-m-d H:00:00", $userTime) : date("Y-m-d", strtotime($chart['time'])),
1175 'count' => $chart['blocked'],
1176 'attacks' => $chart['attacks'],
1177 'blocked' => $chart['blocked'],
1178 ];
1179 $count_attacks += $chart['attacks'];
1180 $count_blocks += $chart['blocked'];
1181 }
1182 }
1183
1184 if (!isset($result)) {
1185 return [
1186 'chart' => FALSE,
1187 'count_attacks' => 0,
1188 'count_blocks' => 0,
1189 'days' => 0,
1190 ];
1191 }
1192
1193 return [
1194 'chart' => json_encode($result),
1195 'count_attacks' => $count_attacks,
1196 'count_blocks' => $count_blocks,
1197 'days' => $days,
1198 ];
1199 }
1200
1201 /**
1202 * Converting data to json for a D3 chart.
1203 *
1204 * @param array $charts
1205 * Charts data from WebTotem.
1206 * @param int $days
1207 * The number of days to build the chart.
1208 *
1209 * @return bool|string
1210 * Returns the converted data for chart.
1211 */
1212 public static function generateChart(array $charts, $days = 7) {
1213 $sum = 0;
1214 foreach ($charts as $chart) {
1215 $sum += $chart['value'];
1216 }
1217 if ($sum == 0) {
1218 return FALSE;
1219 }
1220
1221 $result = [];
1222
1223 foreach ($charts as $chart) {
1224 if ($days <= 1) {
1225 $time_zone = WebTotemOption::getOption('time_zone_offset');
1226 $userTime = ($time_zone) ? strtotime($time_zone . 'hours', strtotime($chart['time'])) : strtotime($chart['time']);
1227 }
1228 $result[] = [
1229 'date' => ($days <= 1) ? date("Y-m-d H:00:00", $userTime) : date("Y-m-d", strtotime($chart['time'])),
1230 'value' => $chart['value'],
1231 ];
1232 }
1233
1234 return json_encode($result, TRUE);
1235 }
1236
1237 /**
1238 * Converting data to json for a D3 chart.
1239 *
1240 * @param array $data
1241 * Charts data from WebTotem.
1242 *
1243 * @return array|bool
1244 * Returns the converted data for chart.
1245 */
1246 public static function generateAttacksMapChart(array $data) {
1247 $attacks = [];
1248 $countries = [];
1249 $labels = [];
1250 foreach ($data as $value) {
1251 $attacks[] = $value['attacks'];
1252 $labels[] = self::getCountryName($value['country']);
1253 $countries[] = $value['location']['country']['nameEn'];
1254 }
1255 $result = ['attacks' => $attacks, 'countries' => $countries, 'labels' => $labels];
1256
1257 if (!$attacks) {
1258 return FALSE;
1259 }
1260
1261 return json_encode($result, TRUE);
1262 }
1263
1264 /**
1265 * Reassembling the antivirus logs.
1266 *
1267 * @param array $logs_
1268 * Antivirus logs from WebTotem.
1269 *
1270 * @return array
1271 * Reassembled array of logs.
1272 */
1273 public static function getAntivirusLogs(array $logs_) {
1274 $logs = [];
1275 foreach ($logs_ as $key => $log) {
1276 $log = $log['node'];
1277
1278 $file_info = new SplFileInfo(urldecode($log['filePath']));
1279
1280 $log['original_path'] = $log['filePath'];
1281 $log['file_path'] = $file_info->getPath() . '/';
1282 $log['file_name'] = $file_info->getFilename();
1283 $log['time'] = self::dateFormatter($log['time']);
1284 $log['permissions_changed'] = $log['permissionsChanged'];
1285 $log['status'] = self::getStatusData($log['event']);
1286 $log['class'] = 'wt-text--green';
1287
1288 switch ($log['event']) {
1289 case 'modified':
1290 case 'quarantine':
1291 $log['class'] = "wt-text--yellow";
1292 break;
1293
1294 case 'deleted':
1295 $log['class'] = "wt-text--light-gray";
1296 break;
1297
1298 case 'infected':
1299 $log['class'] = "wt-text--red";
1300 break;
1301 }
1302
1303 $logs[$key] = $log;
1304 }
1305 return $logs;
1306 }
1307
1308 /**
1309 * Get open path data.
1310 *
1311 * @param array $_ports
1312 * Open ports array.
1313 *
1314 * @return array
1315 * Reassembled array of open ports.
1316 */
1317 public static function getOpenPortsData($ports) {
1318 if(!$ports){
1319 return [];
1320 }
1321 foreach ($ports as $key => $port) {
1322 $summary = '';
1323 if($port['cveList']){
1324 foreach ($port['cveList'] as $item){
1325 $summary .= '<p>' . $item['summary'] . '</p>';
1326 }
1327 }
1328 $ports[$key]['cve_summary'] = $summary;
1329 }
1330 return $ports;
1331 }
1332
1333
1334 /**
1335 * Reassembling the quarantine logs.
1336 *
1337 * @param array $logs_
1338 * Quarantine logs from WebTotem.
1339 *
1340 * @return array
1341 * Reassembled array of logs.
1342 */
1343 public static function getQuarantineLogs(array $logs_) {
1344 $logs = [];
1345 foreach ($logs_ as $key => $log) {
1346 $logs[$key] = $log;
1347 $logs[$key]['path'] = urldecode($log['path']);
1348 $logs[$key]['date'] = self::dateFormatter($log['date']);
1349 }
1350
1351 return $logs;
1352 }
1353
1354 /**
1355 * Generate an array of IP address data.
1356 *
1357 * @param array $data
1358 * IP addresses data from WebTotem.
1359 * @param string $list_name
1360 * Allow or deny list.
1361 *
1362 * @return array
1363 * Returns array of data.
1364 */
1365 public static function getIpList(array $data, $list_name) {
1366 $list = [];
1367 foreach ($data as $item) {
1368 $list[] = [
1369 'ip' => $item['ip'],
1370 'id' => $item['id'],
1371 'created_at' => self::dateFormatter($item['createdAt']),
1372 'list_name' => $list_name,
1373 ];
1374 }
1375 return $list;
1376 }
1377
1378 /**
1379 * Generate an array of URL address data.
1380 *
1381 * @param array $data
1382 * URL addresses data from WebTotem.
1383 *
1384 * @return array
1385 * Returns array of data.
1386 */
1387 public static function getUrlAllowList(array $data) {
1388 $list = [];
1389 foreach ($data as $item) {
1390 $list[] = [
1391 'url' => $item['url'],
1392 'id' => $item['id'],
1393 'created_at' => self::dateFormatter($item['createdAt']),
1394 'list_name' => 'url_allow',
1395 ];
1396 }
1397 return $list;
1398 }
1399
1400 /**
1401 * Convert IP list to be transferred to WebTotem.
1402 *
1403 * @param string $data
1404 * IP list.
1405 *
1406 * @return string
1407 * Returns the converted string.
1408 */
1409 public static function convertIpListForApi($data) {
1410 if (!$data) {
1411 return FALSE;
1412 }
1413
1414 $ips = preg_split("/(?(?=[\s,])[^.]|^$)/", $data);
1415
1416 if (is_array($ips)) {
1417 $ips_ = '[';
1418 foreach ($ips as $ip) {
1419 if (!empty($ip)) {
1420 $ips_ .= '"' . $ip . '",';
1421 }
1422 }
1423 $ips_ = substr($ips_, 0, -1);
1424 $ips_ .= ']';
1425 }
1426 else {
1427 $ips_ = '"' . $ips . '"';
1428 }
1429
1430 return $ips_;
1431 }
1432
1433 /**
1434 * Get data of the country with the most attacks.
1435 *
1436 * @param array $map
1437 * Map logs from WebTotem.
1438 *
1439 * @return array
1440 * Returns array of data.
1441 */
1442 public static function getMostAttacksData($map) {
1443
1444 if ($map) {
1445 $most_attacks_key = array_search(max(array_column($map, 'attacks')), array_column($map, 'attacks'));
1446 $total_attacks = array_sum(array_column($map, 'attacks'));
1447
1448 $data['percent'] = ($total_attacks) ? round($map[$most_attacks_key]['attacks'] / $total_attacks * 100) : 0;
1449 $data['country'] = self::getCountryName($map[$most_attacks_key]['country']);
1450 $data['offset'] = 176 / 100 * (100 - $data['percent']);
1451
1452 return $data;
1453 }
1454
1455 return ['percent' => 0, 'country' => FALSE, 'offset' => 0];
1456 }
1457
1458 /**
1459 * Get data on the three most attacking countries.
1460 *
1461 * @param array $map
1462 * Map logs from WebTotem.
1463 *
1464 * @return array
1465 * Returns array of data.
1466 */
1467 public static function getTreeMostAttacksData($map) {
1468 $total_attacks = array_sum(array_column($map, 'attacks'));
1469
1470 if ($map) {
1471 array_multisort (array_column($map, 'attacks'), SORT_DESC, $map);
1472 $data = array_slice($map, 0, 3);
1473
1474 foreach ($data as $key => $value){
1475 $data[$key]['percent'] = round($value['attacks'] / $total_attacks * 100);
1476 $data[$key]['country'] = self::getCountryName($value['country']);
1477 }
1478
1479 return $data;
1480 }
1481
1482 return [];
1483 }
1484
1485 /**
1486 * Getting the country name by two-letter code.
1487 *
1488 * @param string $key
1489 * Two-letter code.
1490 *
1491 * @return string
1492 * Returns country name.
1493 */
1494 public static function getCountryName($key) {
1495 $countries = WebTotemCountryManager::getStandardList();
1496 $key = (string) $key;
1497
1498 return (array_key_exists($key, $countries)) ? $countries[$key] : $key;
1499 }
1500
1501 /**
1502 * Get configs data.
1503 *
1504 * @param array $array
1505 * Original array.
1506 * @param string $key
1507 * The key to use as an index.
1508 *
1509 * @return array
1510 * Configs data array.
1511 */
1512 public static function getConfigsData(array $array, $key) {
1513 $configs = self::arrayMapIndex($array, $key);
1514
1515 foreach ($configs as $service => $config){
1516 $configs[$service]['checked'] = ($config['isActive']) ? 'checked' : '';
1517 $configs[$service]['notification_checked'] = (isset($config['notifications']) && $config['notifications']) ? 'checked' : '';
1518 }
1519
1520 return $configs;
1521 }
1522
1523 /**
1524 * Get waf setting data.
1525 *
1526 * @param array $settings
1527 * Original array.
1528 *
1529 * @return array
1530 * Configs data array.
1531 */
1532 public static function getWafSettingData(array $settings) {
1533 $_settings['gdn']['checked'] = (isset($settings['gdn']) && !$settings['gdn']) ? '' : 'checked';
1534 $_settings['dos'] = [
1535 'checked' => (isset($settings['dosProtection']) && !$settings['dosProtection']) ? '' : 'checked',
1536 'visually' => (isset($settings['dosProtection']) && !$settings['dosProtection']) ? 'visually-hidden' : '',
1537 ];
1538 $_settings['dos_limit'] = $settings['dosLimit'] ?: 1000;
1539
1540 $_settings['login_attempt'] = [
1541 'checked' => (isset($settings['loginAttemptsProtection']) && !$settings['loginAttemptsProtection']) ? '' : 'checked',
1542 'visually' => (isset($settings['loginAttemptsProtection']) && !$settings['loginAttemptsProtection']) ? 'visually-hidden' : '',
1543 ];
1544 $_settings['login_attempt_limit'] = $settings['loginAttemptsLimit'] ?: 20;
1545
1546 return $_settings;
1547 }
1548
1549 /**
1550 * Get plugin settings data.
1551 *
1552 * @return array
1553 * Configs data array.
1554 */
1555 public static function getPluginSettingsData() {
1556
1557 $settings = WebTotemOption::getPluginSettings();
1558 $_settings = $settings;
1559
1560 $_settings['hide_wp_version_checked'] = (array_key_exists('hide_wp_version', $settings) and $settings['hide_wp_version']) ? 'checked' : '';
1561 $_settings['recaptcha_checked'] = (array_key_exists('recaptcha', $settings) and $settings['recaptcha']) ? 'checked' : '';
1562 $_settings['two_factor_checked'] = (array_key_exists('two_factor', $settings) and $settings['two_factor']) ? 'checked' : '';
1563
1564 return $_settings;
1565 }
1566
1567 /**
1568 * Replace array indexes by key.
1569 *
1570 * @param array $array
1571 * Original array.
1572 * @param string $key
1573 * The key to use as an index.
1574 *
1575 * @return array
1576 * Returns a new array.
1577 */
1578 public static function arrayMapIndex(array $array, $key) {
1579 $new_array = [];
1580 foreach ($array as $item) {
1581 if (array_key_exists($key, $item)) {
1582 $new_array[$item[$key]] = $item;
1583 }
1584 }
1585 return $new_array;
1586 }
1587
1588 /**
1589 * Generate random string.
1590 *
1591 * @param int $length
1592 * The required length of the string.
1593 *
1594 * @return string
1595 * Returns random string.
1596 */
1597 public static function generateRandomString( int $length = 10): string {
1598 $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-';
1599 $charactersLength = strlen($characters);
1600 $randomString = '';
1601 for ($i = 0; $i < $length; $i++) {
1602 $randomString .= $characters[rand(0, $charactersLength - 1)];
1603 }
1604 return $randomString;
1605 }
1606
1607 /**
1608 * Encodes the less than, greater than, ampersand,double quote
1609 * and single quote characters. Will never double encode entities.
1610 *
1611 * @see https://developer.wordpress.org/reference/functions/esc_attr/
1612 *
1613 * @param string $text
1614 * The text which is to be encoded.
1615 *
1616 * @return string
1617 * The encoded text with HTML entities.
1618 */
1619 public static function escape($text = '') {
1620 return esc_attr($text);
1621 }
1622
1623 /**
1624 * Throw generic exception.
1625 *
1626 * @throws Exception
1627 *
1628 * @param string $message
1629 * Error or information message.
1630 * @param string $type
1631 * Either info or error.
1632 *
1633 * @return bool
1634 * False all the time, used for debug.
1635 */
1636 public static function throwException($message, $type = 'error') {
1637 if (defined('WTOTEM_THROW_EXCEPTIONS') && WTOTEM_THROW_EXCEPTIONS === true && is_string($message) ) {
1638 $message = str_replace( '<strong>WebTotem:</strong>', ($type === 'error' ? __('Error:', 'wtotem') : __('Info:', 'wtotem')), $message );
1639 throw new Exception($message, $type === 'error' ? 157 : 333);
1640 }
1641 return false;
1642 }
1643
1644 /**
1645 * Get audit logs data
1646 *
1647 * @return array
1648 */
1649 public static function getAuditLogs($data, $dates_count) {
1650 $logs = [];
1651 foreach ($data as $datum){
1652 $date_time = strtotime($datum['created_at']);
1653 $date = self::dateFormatter($date_time, 'M j, Y');
1654
1655 $logs[$date]['date'] = $date;
1656 $logs[$date]['count'] = $dates_count[$date];
1657 $logs[$date]['logs'][] = [
1658 'time' => self::dateFormatter($date_time,'H:i'),
1659 'user_name' => $datum['user_name'],
1660 'status' => $datum['status'],
1661 'title' => $datum['title'],
1662 'event' => $datum['event'],
1663 'description' => $datum['description'],
1664 'ip' => $datum['ip'],
1665 'viewed' => (int) !$datum['viewed']
1666 ];
1667 }
1668 return $logs;
1669 }
1670
1671 /**
1672 * Update user's plugins cve data
1673 *
1674 * @return void
1675 */
1676 public static function updateCveData() {
1677 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1678 $all_plugins = get_plugins();
1679
1680 $list = [];
1681 foreach ($all_plugins as $plugin) {
1682 if($plugin['TextDomain'] and $plugin['Version']){
1683 $list[] = '{"technology": "' . $plugin['TextDomain'] . '", "version": "' . $plugin['Version'] . '"}';
1684 }
1685 }
1686 $list = implode(', ', $list ?? []);
1687 $cve_list = WebTotem::arrayMapIndex(WebTotemAPI::getCVE($list), 'technology');
1688
1689 $update_plugins = get_site_transient( 'update_plugins' );
1690 $update_plugins = WebTotem::convertObjectToArray($update_plugins->response);
1691
1692 $values = '';
1693 WebTotemDB::deleteData([], 'plugins_cve_list');
1694 foreach ($all_plugins as $key => $plugin) {
1695 if(array_key_exists($plugin['TextDomain'], $cve_list)){
1696 $new_version = $update_plugins[$key]['new_version'] ?? 0;
1697 foreach ($cve_list[$plugin['TextDomain']]['cves'] as $cve){
1698 $cve['published'] = self::dateFormatter($cve['published'], 'Y-m-d');
1699 $values .= sprintf("('%s','%s','%s','%s','%s','%s'),",
1700 $cve['cve_id'],
1701 $plugin['Name'],
1702 $plugin['TextDomain'],
1703 $plugin['Version'],
1704 $new_version,
1705 json_encode($cve)
1706 );
1707 }
1708
1709 }
1710 }
1711
1712 if($values){
1713 $values = substr_replace($values, ";", -1);
1714 $columns = '(cve_id, plugin_name, slug, plugin_version, new_version, cve_data )';
1715 WebTotemDB::setRows('plugins_cve_list', $columns, $values);
1716 }
1717
1718 }
1719
1720 public static function getPluginVersionFromRepository($slug) {
1721 $url = "https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slugs][]={$slug}";
1722 $response = wp_remote_get($url); // WPOrg API call
1723 $plugins = json_decode($response['body']);
1724
1725 // traverse $response object
1726 foreach($plugins as $key => $plugin) {
1727 $version = $plugin->version;
1728 }
1729 return $version;
1730 }
1731
1732 /**
1733 * Update user's plugins cve data
1734 *
1735 * @return bool
1736 */
1737 public static function updateCveDataByPluginName($plugin_data) {
1738 if(!$plugin_data['TextDomain'] or !$plugin_data['Version']){
1739 return false;
1740 }
1741
1742 $list = WebTotemAPI::getCVE('{"technology": "' . $plugin_data['TextDomain'] . '", "version": "' . $plugin_data['Version'] . '"}');
1743 $cve_list = WebTotem::arrayMapIndex($list, 'technology');
1744
1745 $values = '';
1746 WebTotemDB::deleteData(['slug' => $plugin_data['TextDomain']], 'plugins_cve_list');
1747 if(array_key_exists($plugin_data['TextDomain'], $cve_list) and $cve_list[$plugin_data['TextDomain']]['cves']){
1748 $has_new_version = WebTotem::getPluginVersionFromRepository($plugin_data['TextDomain']);
1749 foreach ($cve_list[$plugin_data['TextDomain']]['cves'] as $cve){
1750 $cve['published'] = self::dateFormatter($cve['published'], 'Y-m-d');
1751 $values .= sprintf("('%s','%s','%s','%s','%s','%s'),",
1752 $cve['cve_id'],
1753 $plugin_data['Name'],
1754 $plugin_data['TextDomain'],
1755 $plugin_data['Version'],
1756 ($has_new_version and $has_new_version != $plugin_data['Version']) ? $has_new_version : 0,
1757 json_encode($cve)
1758 );
1759 }
1760 $values = substr_replace($values, ";", -1);
1761 $columns = '(cve_id, plugin_name, slug, plugin_version, new_version, cve_data)';
1762 WebTotemDB::setRows('plugins_cve_list', $columns, $values);
1763 }
1764
1765 return true;
1766 }
1767
1768 public static function get_plugin_info($plugin_slug) {
1769 include_once(ABSPATH . 'wp-admin/includes/plugin.php');
1770
1771 $all_plugins = get_plugins();
1772 $plugin_file = "$plugin_slug/$plugin_slug.php";
1773
1774 if (isset($all_plugins[$plugin_file])) {
1775 $plugin_info = $all_plugins[$plugin_file];
1776 return $plugin_info;
1777 } else {
1778 return false;
1779 }
1780 }
1781
1782 /**
1783 * Get confidential files data
1784 *
1785 * @return array
1786 */
1787 public static function preparePluginsCveList($data) {
1788 foreach ($data as $key => $datum){
1789 $data[$key]['cve_data'] = json_decode($datum['cve_data'], true);
1790 $data[$key]['cve_data']['published'] = self::dateFormatter($data[$key]['cve_data']['published'], 'M j, Y');
1791 }
1792 return $data;
1793 }
1794 /**
1795 * Get confidential files data
1796 *
1797 * @return array
1798 */
1799 public static function getConfidentialFiles($data) {
1800 foreach ($data as $key => $datum){
1801 $data[$key]['modified_at'] = date_i18n('M j, Y \/ H:i', strtotime($datum['modified_at']));
1802 $data[$key]['size'] = self::humanFilesize($datum['size']);
1803 $data[$key]['name'] = urldecode($datum['name']);
1804 $data[$key]['path'] = urldecode($datum['path']);
1805 }
1806 return $data;
1807 }
1808
1809 /**
1810 * Get confidential files data
1811 *
1812 * @return array
1813 */
1814 public static function prepareLinksData($data) {
1815 foreach ($data as $key => $datum){
1816
1817 $content = $datum['content'];
1818 $source = $datum['source'];
1819 if(strpos($content, 'http://') !== 0 and strpos($content, 'https://') !== 0 and strpos($content, '//') !== 0){
1820 $match = substr_count($content, '../');
1821 $content = str_replace("../", "", $content);
1822 $content = ltrim($content, '/');
1823
1824 for($i=0; $i < 1+$match; $i++){
1825 $source = substr($source, 0, strrpos($source, "/"));
1826 }
1827 $data[$key]['link'] = $source . '/' . $content;
1828 } else {
1829 $data[$key]['link'] = $content;
1830 }
1831 }
1832 return $data;
1833 }
1834
1835
1836 /**
1837 * Building navigation and forming a template
1838 *
1839 * @param integer $limit
1840 * number of entries per 1 page
1841 * @param integer $count_all
1842 * total number of all entries
1843 * @param integer $currentPage
1844 * the number of the page being viewed
1845 * @param integer $nextPrev
1846 * Show the "Forward" and "Back" buttons
1847 * @return mixed
1848 * Generated navigation template ready for output
1849 */
1850 public static function paginationBuild($limit, $count_all, $currentPage = 1, $nextPrev = true) {
1851 if( $limit < 1 OR $count_all <= $limit ) return '';
1852 $count_pages = ceil( $count_all / $limit );
1853
1854 $spread = 3;
1855 $separator = "<i>...</i>";
1856 $wrap = "<div class=\"wtotem_pagination\">{pages}</div>";
1857
1858 $nextTitle = '';
1859 $prevTitle = '';
1860
1861 $currentPage = intval( $currentPage );
1862 if( $currentPage < 1 ) $currentPage = 1;
1863
1864 $shift_start = max( $currentPage - $spread, 2 );
1865 $shift_end = min( $currentPage + $spread, $count_pages-1 );
1866 if( $shift_end < $spread * 2 ) {
1867 $shift_end = min( $spread * 2, $count_pages-1 );
1868 }
1869 if( $shift_end == $count_pages - 1 AND $shift_start > 3 ) {
1870 $shift_start = max( 3, min( $count_pages - $spread * 2 + 1, $shift_start ) );
1871 }
1872
1873 $list = self::getPaginationItem( 1, $currentPage );
1874
1875 if ($shift_start == 3) {
1876 $list .= self::getPaginationItem( 2, $currentPage );
1877 } elseif ( $shift_start > 3 ) {
1878 $list .= $separator;
1879 }
1880
1881 for( $i = $shift_start; $i <= $shift_end; $i++ ) {
1882 $list .= self::getPaginationItem( $i, $currentPage );
1883 }
1884
1885 $last_page = $count_pages - 1;
1886 if( $shift_end == $last_page-1 ){
1887 $list .= self::getPaginationItem( $last_page, $currentPage );
1888 } elseif( $shift_end < $last_page ) {
1889 $list .= $separator;
1890 }
1891
1892 $list .= self::getPaginationItem( $count_pages, $currentPage );
1893
1894 if( $nextPrev ) {
1895 $list = self::getPaginationItem(
1896 $currentPage > 1 ? $currentPage - 1 : 0,
1897 $currentPage,
1898 $nextTitle,
1899 true )
1900 . $list
1901 . self::getPaginationItem(
1902 $currentPage < $count_pages ? $currentPage + 1 : 0,
1903 $currentPage,
1904 $prevTitle,
1905 true
1906 );
1907 }
1908
1909 return str_replace( "{pages}", $list, $wrap );
1910 }
1911
1912 /**
1913 * Button/Link Formation
1914 * @param int $page_num
1915 * page number
1916 * @param string $currentPage
1917 * current page
1918 * @param string $page_name
1919 * if specified, the text will be displayed instead of the page number
1920 * @return string
1921 * span block with active page or link.
1922 */
1923 public static function getPaginationItem( $page_num, $currentPage, $page_name = '' ) {
1924 if($page_num === 0){return '';}
1925 $page_name = $page_name ?: $page_num;
1926
1927 if( $currentPage == $page_num ) {
1928 return "<span class=\"wtotem_pagination__number wtotem_pagination__number_active\">{$page_name}</span>";
1929 } else {
1930 return "<a href=\"#\" data-page=\"{$page_num}\" class=\"wtotem_pagination__number\">{$page_name}</a>";
1931 }
1932 }
1933
1934 /**
1935 * Get notifications array.
1936 *
1937 * @return array
1938 * Returns notifications array.
1939 */
1940 public static function getNotifications() {
1941
1942 $notifications_data = WebTotemOption::getNotificationsData();
1943 $notifications = [];
1944
1945 foreach ($notifications_data as $notification) {
1946 switch ($notification['type']) {
1947 case 'error':
1948 $image = 'alert-error.svg';
1949 $class = 'wtotem_alert__title_red';
1950 break;
1951
1952 case 'warning':
1953 $image = 'alert-warning.svg';
1954 $class = 'wtotem_alert__title_yellow';
1955 break;
1956
1957 case 'success':
1958 $image = 'alert-success.svg';
1959 $class = 'wtotem_alert__title_green';
1960 break;
1961
1962 case 'info':
1963 $image = 'info-blue.svg';
1964 $class = 'wtotem_alert__title_blue';
1965 break;
1966 }
1967
1968 $notifications[] = [
1969 "text" => $notification['notice'],
1970 "id" => self::generateRandomString(8),
1971 "type" => self::getStatusText($notification['type']),
1972 "type_raw" => $notification['type'],
1973 "image" => $image,
1974 "class" => $class,
1975 ];
1976 }
1977
1978 return $notifications;
1979 }
1980
1981 /**
1982 * Get current agent installation statuses.
1983 *
1984 * @param array $agents_statuses
1985 * Agents statuses got from the WebTotem API.
1986 *
1987 * @return array
1988 * Returns an array with agent installation status data.
1989 */
1990 public static function getAgentsStatuses(array $agents_statuses) {
1991 $agents = ['am', 'waf', 'av'];
1992 $installing_statuses = [
1993 'not_installed',
1994 'installing',
1995 'internal_error',
1996 'update_error',
1997 'config_error',
1998 'session_error',
1999 ];
2000
2001 $process_statuses = [];
2002 $option_statuses = [];
2003
2004 foreach ($agents as $agent) {
2005 $status = WebTotemAgentManager::checkInstalledService($agent);
2006 $option_statuses[$agent] = $status['option_status'] ?: FALSE;
2007
2008 if ($agent == 'am') {
2009 if ($status['file_status']) {
2010 $process_statuses[$agent] = 'installed';
2011 }
2012 else {
2013 $process_statuses[$agent] = 'failed';
2014 }
2015 }
2016 else {
2017 if ($status['file_status']) {
2018 if (in_array($agents_statuses[$agent], $installing_statuses)) {
2019 $process_statuses[$agent] = 'installing';
2020 }
2021 elseif ($agents_statuses[$agent] == 'agent_not_available') {
2022 $process_statuses[$agent] = 'failed';
2023 }
2024 else {
2025 $process_statuses[$agent] = 'installed';
2026 }
2027 }
2028 else {
2029 $process_statuses[$agent] = 'installing';
2030 }
2031 }
2032 }
2033
2034 return [
2035 'process_statuses' => $process_statuses,
2036 'option_statuses' => $option_statuses,
2037 ];
2038 }
2039
2040 }
2041