PluginProbe
WebTotem Security / 2.4.35
WebTotem Security v2.4.35
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.35, at lib/Helper.php

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