PluginProbe
WebTotem Security / 2.4.12
WebTotem Security v2.4.12
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.12, at lib/Helper.php

1,441 lines 37.4 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 // TODO добавить суперадмина
46 if(in_array('administrator', $roles)){
47 $user_role = 1;
48 } else {
49 $user_role = 0;
50 }
51 }
52
53 define( 'WEBTOTEM_USER_ROLE', $user_role );
54
55 return true;
56 }
57
58 /**
59 * Check whether the current site is working as a multi-site instance.
60 *
61 * @return bool
62 * Either TRUE or FALSE in case WordPress is being used as a multi-site instance.
63 */
64 public static function isMultiSite() {
65 return (bool) (function_exists('is_multisite') && is_multisite());
66 }
67
68 /**
69 * Returns the md5 hash representing the content of a file.
70 *
71 * @param string $file
72 * Relative path to the file.
73 * @return string
74 * Seven first characters in the hash of the file.
75 */
76 public static function fileVersion($file = '') {
77 return substr(md5_file(WEBTOTEM_PLUGIN_PATH . '/' . $file), 0, 7);
78 }
79
80 /**
81 * Returns the md5 hash representing the content of a file.
82 *
83 * @param string $image
84 * Relative path to the file.
85 * @return string
86 * Seven first characters in the hash of the file.
87 */
88 public static function getImagePath($image) {
89 return WEBTOTEM_URL. '/includes/img/' . $image;
90 }
91
92 /**
93 * Returns user IP address.
94 *
95 * @return string
96 * Returns user IP address.
97 */
98 public static function getUserIP() {
99 $arr = [
100 'HTTP_CLIENT_IP',
101 'HTTP_X_FORWARDED_FOR',
102 'HTTP_X_FORWARDED',
103 'HTTP_X_CLUSTER_CLIENT_IP',
104 'HTTP_FORWARDED_FOR',
105 'HTTP_FORWARDED',
106 'HTTP_CF_CONNECTING_IP',
107 'REMOTE_ADDR'
108 ];
109
110 foreach ($arr as $key){
111 if (array_key_exists($key, $_SERVER) === true) {
112 foreach (explode(',', $_SERVER[$key]) as $ip) {
113 $ip = trim($ip);
114 $ip = filter_var($ip, FILTER_VALIDATE_IP);
115 if (!empty($ip)) {
116 return $ip;
117 }
118 }
119 }
120 }
121 return false;
122 }
123
124 /**
125 * Check that the training period has passed for the firewall.
126 *
127 * @param string $created_at
128 * Date when the waf configuration was created.
129 *
130 * @return bool
131 * Returns boolean.
132 */
133 public static function isWafTraining($created_at) {
134 if($created_at) {
135 $when_waf_trained = strtotime('+2 day', strtotime($created_at));
136 $today = strtotime('today');
137
138 return ($when_waf_trained < $today) ? FALSE : TRUE;
139 }
140 return FALSE;
141 }
142
143 /**
144 * Converting a date to the appropriate format.
145 *
146 * @param string $date
147 * Date in any format.
148 * @param string $format
149 * The format to which you want to convert the date.
150 *
151 * @return string
152 * Returns converted Date.
153 */
154 public static function dateFormatter($date, $format = 'M j, Y \/ H:i') {
155 if (!$date) {
156 return __('Unknown', 'wtotem');
157 }
158
159 $time_zone = WebTotemOption::getOption('time_zone_offset');
160 $user_time = ($time_zone) ? strtotime($time_zone . 'hours', strtotime($date)) : strtotime($date);
161
162 return date_i18n($format, $user_time);
163 }
164
165 /**
166 * Get theme mode data.
167 *
168 * @return array
169 * Returns array with current theme data.
170 */
171 public static function getThemeMode() {
172 $theme_mode = WebTotemOption::getSessionOption('theme_mode');
173 return [
174 "is_dark_mode" => $theme_mode == 'dark' ? 'wtotem_theme—dark' : '',
175 "dark_mode_checked" => $theme_mode == 'dark' ? 'checked' : '',
176 ];
177 }
178
179 /**
180 * Get current user language.
181 *
182 * @return string
183 * Returns current language in 2-letter abbreviations
184 */
185 public static function getLanguage() {
186 $current_language = substr(get_bloginfo('language'), 0,2);
187 $language = (in_array($current_language,['ru','en','pl'])) ? $current_language : 'en' ;
188 return $language;
189 }
190
191 /**
192 * Converting a date to the appropriate format.
193 *
194 * @param string|array $days
195 * Number of days or period to convert.
196 *
197 * @return array
198 * Returns an array of two values "from" and "to"
199 */
200 public static function getPeriod($days) {
201
202 if (!$days) {
203 $days = 30;
204 }
205
206 switch ($days) {
207
208 case is_array($days):
209 $to = $days[1] ?: $days[0];
210 $period = [
211 'from' => strtotime(date('Y-m-d 00:00:01', strtotime($days[0]))),
212 'to' => strtotime(date('Y-m-d 23:59:59', strtotime($to))),
213 ];
214 break;
215
216 case $days <= 1:
217 $period = [
218 'from' => strtotime('-24 hours'),
219 'to' => time(),
220 ];
221 break;
222
223 default:
224 $period = [
225 'from' => time() - ($days * 86400),
226 'to' => time(),
227 ];
228 }
229
230 return $period;
231 }
232
233 /**
234 * Convert an array to a string with quotation marks.
235 *
236 * @param array $array
237 * Data array.
238 *
239 * @return string
240 * Array of data converted to string.
241 */
242 public static function convertArrayToString($array) {
243 if(empty($array)){
244 return '';
245 }
246 return '"' . implode('","', $array) . '"';
247 }
248
249 /**
250 * Converting the response to a readable form.
251 *
252 * @param string $message
253 * Message response from the API server to the request.
254 *
255 * @return string|bool
256 * Returns a message.
257 */
258 public static function messageForHuman($message) {
259
260 $definition = $message;
261
262 switch ($message) {
263 case 'HOSTS_LIMIT_EXCEEDED':
264 $definition = __('Limit of adding sites exceeded.', 'wtotem');
265 break;
266
267 case 'USER_ALREADY_REGISTERED':
268 $definition = __('A user with this email already exists.', 'wtotem');
269 break;
270
271 case 'DUPLICATE_HOST':
272 $definition = __('Duplicate host', 'wtotem');
273 break;
274
275 case 'INVALID_DOMAIN_NAME':
276 $definition = __('Invalid Domain Name', 'wtotem');
277 break;
278 default:
279 $definition = str_replace("_", " ", $definition);
280 $definition = ucfirst(strtolower($definition));
281
282 }
283 return $definition;
284 }
285
286 /**
287 * Get the data associated with the status.
288 *
289 * @param string $status
290 * Module or agent status.
291 *
292 * @return array
293 * Returns an array with status data.
294 */
295 public static function getStatusData($status) {
296 $path = self::getImagePath('');
297 $status = ($status == "installed") ? 'working' : $status;
298
299 switch ($status) {
300
301 case 'clean':
302 case 'up':
303 case 'installed':
304 case 'working':
305 $status_data = [
306 'class' => 'is--status--ok',
307 'image' => $path . 'check-mark.svg',
308 'icon' => $path . 'icon_success_status.svg',
309 ];
310 break;
311
312 case 'pending':
313 $status_data = [
314 'class' => 'is--status--pending',
315 'image' => $path . 'loading.svg',
316 'icon' => $path . 'alert-warning.svg',
317 ];
318 break;
319
320 case 'pause':
321 case 'modified':
322 $status_data = [
323 'class' => 'is--status--pending',
324 'image' => $path . 'warning.svg',
325 'icon' => $path . 'alert-warning.svg',
326 ];
327 break;
328
329 case 'expired':
330 case 'no_cert':
331 case 'expires':
332 case 'open_ports':
333 case 'not_supported':
334 case 'not_registered':
335 $status_data = [
336 'class' => 'is--status--warning',
337 'image' => $path . 'warning.svg',
338 'icon' => $path . 'alert-warning.svg',
339 ];
340 break;
341
342 case 'invalid':
343 case 'error':
344 case 'down':
345 case 'expires_today':
346 case 'infected':
347 case 'deface':
348 case 'not_installed':
349 $status_data = [
350 'class' => 'is--status--error',
351 'image' => $path . 'warning.svg',
352 'icon' => $path . 'alert-warning.svg',
353 ];
354 break;
355
356 default:
357 $status_data = [
358 'class' => 'is--status--pending',
359 'image' => $path . 'warning.svg',
360 'icon' => $path . 'alert-warning.svg',
361 ];
362 }
363 $status_data['name'] = $status;
364 $status_data['text'] = self::getStatusText($status);
365 $status_data['tooltips'] = self::getTooltips($status);
366
367 return $status_data;
368 }
369
370 /**
371 * Get a readable status text.
372 *
373 * @param string $status
374 * Module or agent status.
375 *
376 * @return string
377 * Returns the status text in the current language.
378 */
379 public static function getStatusText($status) {
380 $statuses = [
381 'warning' => __('Warning', 'wtotem'),
382 'error' => __('Error', 'wtotem'),
383 'success' => __('Success', 'wtotem'),
384 'info' => __('Info', 'wtotem'),
385 'invalid' => __('Invalid', 'wtotem'),
386 'ok' => __('Everything is OK', 'wtotem'),
387 'expired' => __('Expired', 'wtotem'),
388 'expires' => __('Expires', 'wtotem'),
389 'expires_today' => __('Expires today', 'wtotem'),
390 'missing' => __('Missing', 'wtotem'),
391 'active' => __('Active', 'wtotem'),
392 'inactive' => __('Inactive', 'wtotem'),
393 'pending' => __('Pending', 'wtotem'),
394 'pause' => __('Disabled', 'wtotem'),
395 'available' => __('Available', 'wtotem'),
396 'not_supported' => __('Not supported', 'wtotem'),
397 'not_registered' => __('Not registered', 'wtotem'),
398 'unsupported' => __('Unsupported', 'wtotem'),
399 'clean' => __('Clean', 'wtotem'),
400 'clear' => __('Clear', 'wtotem'),
401 'blacklisted' => __('Infected', 'wtotem'),
402 'miner_detected' => __('Infected', 'wtotem'),
403 'deface' => __('Deface', 'wtotem'),
404 'modified' => __('Modified', 'wtotem'),
405 'detected' => __('Detected', 'wtotem'),
406 'open_ports' => __('Open ports', 'wtotem'),
407 'blocked' => __('Blocked', 'wtotem'),
408 'connected' => __('Connected', 'wtotem'),
409 'attacks_detected' => __('Attacks detected', 'wtotem'),
410 'signature_found' => __('Signature found', 'wtotem'),
411 'file_changes' => __('File changes', 'wtotem'),
412 'no_cert' => __('No cert', 'wtotem'),
413 'down' => __('Down', 'wtotem'),
414 'up' => __('Up', 'wtotem'),
415 'infected' => __('Infected', 'wtotem'),
416 'not_installed' => __('Need to install', 'wtotem'),
417 'agent_not_available' => __('Agent not available', 'wtotem'),
418 'update_error' => __('Update error', 'wtotem'),
419 'session_error' => __('Session Error', 'wtotem'),
420 'internal_error' => __('Internal Error', 'wtotem'),
421 'installing' => __('Installing', 'wtotem'),
422 'installed' => __('Installed', 'wtotem'),
423 'working' => __('Working', 'wtotem'),
424 "critical" => __('Critical', 'wtotem'),
425 "deleted" => __('Deleted', 'wtotem'),
426 "changed" => __('Changed', 'wtotem'),
427 "new" => __('New', 'wtotem'),
428 "scanned" => __('Scanned', 'wtotem'),
429 "quarantine" => __('In quarantine', 'wtotem'),
430 ];
431
432 return (array_key_exists($status, $statuses)) ? $statuses[$status] : $status;
433 }
434
435 /**
436 * Get tooltips text for status.
437 *
438 * @param string $status
439 * Module or agent status.
440 *
441 * @return string
442 * Returns the status tooltip in the current language.
443 */
444 public static function getTooltips($status) {
445 $tooltips = [
446 'invalid' => __('Invalid -The certificate is invalid. Please, make sure that relevant certificate details filled correctly.', 'wtotem'),
447 'expired' => __('Expired - The certificate has expired. Connection is not secure. Please, renew it.', 'wtotem'),
448 'expires' => __('Expires - The certificate expires soon. Please, take actions.', 'wtotem'),
449 'expires_today' => __('Expires today - The certificate expires today. Please, take actions.', 'wtotem'),
450 'error' => __("Error - Something went wrong. Please, contact us, we'll fix the problem.", 'wtotem'),
451 'pending' => __('Pending - System processes your website. Data will be available soon.', 'wtotem'),
452 'pause' => __('Pause - The module is paused.', 'wtotem'),
453 'clean' => __('Everything is OK - Nothing to worry about. Everything is alright.', 'wtotem'),
454 'deface' => __("Deface - Website hacked. Please, contact us, we'll fix the problem.", 'wtotem'),
455 'open_ports' => __('Open ports - Open ports detected. Your website is vulnerable to attacks.', 'wtotem'),
456 'blocked' => __('Blocked - The module is blocked due to billing issues.', 'wtotem'),
457 'no_cert' => __("No cert - You don't have SSL certificate. We recommend you to install it for security concerns.", 'wtotem'),
458 'down' => __('Down - The website is not available for visitors.', 'wtotem'),
459 'up' => __('Up - The website is available for visitors.', 'wtotem'),
460 'infected' => __('Infected - The website site is blacklisted and may have infected files. Please, check antivirus module.', 'wtotem'),
461 'installing' => __('It means that the agent installation is in progress. Usually, it takes up to one hour.', 'wtotem'),
462 'agent_not_available' => __('We cannot locate the agent right now.', 'wtotem'),
463 'update_error' => __('It seems that your agent failed to update due to permissions restrictions.', 'wtotem'),
464 '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'),
465 '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'),
466 'working' => __('Everything is alright.', 'wtotem'),
467 'installed' => __('Everything is alright.', 'wtotem'),
468 'not_installed' => __('You need to install agent manager to activate antivirus and firewall.', 'wtotem'),
469 ];
470
471 return (array_key_exists($status, $tooltips)) ? $tooltips[$status] : '';
472 }
473
474 /**
475 * Converting site data.
476 *
477 * @param array $data
478 * Sites data from WebTotem.
479 *
480 * @return array
481 * Converted data.
482 */
483 public static function allSitesData($data) {
484
485 $local_sites = get_sites();
486 $main_host = WebTotemOption::getMainHost();
487 $domains = [];
488
489 foreach ($local_sites as $site){
490 $domain = untrailingslashit($site->domain . $site->path);
491 $domains[$domain] = $domain;
492 }
493
494 $sites = [];
495 if(array_key_exists('edges', $data)){
496 foreach ($data['edges'] as $site) {
497 $site = $site['node'];
498 // Take sites only from the multisite network.
499 if(array_key_exists($site['hostname'], $domains)) {
500 unset($domains[$site['hostname']]);
501 $sites[] = [
502 'hostname' => $site['hostname'],
503 'title' => $site['title'],
504 'main_host' => $main_host['id'] == $site['id'],
505 'host_id' => $site['id'],
506 'url' => admin_url('admin.php?page=wtotem_dashboard&hid=' . $site['id']),
507 'firewall' => [
508 'status' => self::getStatusData($site['firewall']['status']),
509 ],
510 'antivirus' => [
511 'status' => self::getStatusData($site['antivirus']['status']),
512 ],
513 'stacks' => self::getStacksData($site['maliciousScript']['stack']),
514 'services' => self::getSiteServicesData($site),
515 ];
516 }
517 }
518
519 }
520 return $sites;
521 }
522
523 /**
524 * Converting stacks data.
525 *
526 * @param array $stacks
527 * Stacks data from WebTotem.
528 *
529 * @return array
530 * Converted data.
531 */
532 protected static function getStacksData($stacks) {
533 $apps = file_get_contents(WEBTOTEM_PLUGIN_PATH . '/includes/js/apps.json');
534 $apps = json_decode($apps, true);
535
536 $path = 'https://assets.wtotem.net/images/apps/';
537 $defaultIcon = WEBTOTEM_URL . '/includes/img/defaultTechnologiesIcon.svg';
538
539 $stackList = array_slice($stacks, 0,3);
540 $list = [];
541 foreach ($stackList as $key => $stack){
542 $list[$key] = [
543 'name' => $stack['name'],
544 'icon' => $path . ($apps[$stack['name']]['icon'] ?: $defaultIcon),
545 ];
546 }
547
548 if(count($stacks) <= 3){
549 $other['count'] = 0;
550 $other['names'] = [];
551 } else {
552 $otherStacks = array_slice($stacks, 3);
553 $other['count'] = count($otherStacks);
554 foreach ($otherStacks as $stack){
555 $other['names'][] = $stack['name'];
556 }
557 }
558 if($other['names']){
559 $other['names'] = implode(",", $other['names']);
560 }
561 return ['list' => $list, 'other' => $other];
562 }
563
564 /**
565 * Converting services data.
566 *
567 * @param $data
568 * Site data from WebTotem.
569 *
570 * @return array
571 * Converted data.
572 */
573 protected static function getSiteServicesData($data) {
574
575 $services = [
576 'ssl' => 'ssl',
577 'availability' => 'wa',
578 'reputation' => 'rc',
579 'ports' => 'ps',
580 'deface' => 'dc',
581 'domain' => 'dec',
582 ];
583
584 $list = [];
585 $other['count'] = 0;
586 $other['names'] = [];
587
588 foreach ($services as $key => $service){
589 if(array_key_exists($key, $data) and is_array($data[$key]) and array_key_exists('status', $data[$key])){
590 $status = self::getServiceStatus($data[$key]['status']);
591
592 if(in_array($status['color'], ['red', 'yellow'])){
593 if(count($list) < 2){
594 $color = $status['color'] == 'red' ? 'white/' : '';
595
596 $list[$key] = [
597 'status' => $status,
598 'icon' => 'services/'. $color . $service . '.svg',
599 'name' => self::getServiceName( $service ),
600 ];
601 } else {
602 $other['names'][] = self::getServiceName( $service );
603 $other['count']++;
604 }
605 }
606
607 }
608 }
609 if($other['names']){
610 $other['names'] = implode(",", $other['names']);
611 }
612 return ['list' => $list, 'other' => $other];
613 }
614
615 /**
616 * Get the data associated with the status.
617 *
618 * @param string $status
619 * Module or agent status.
620 *
621 * @return array
622 * Returns an array with status data.
623 */
624 public static function getServiceStatus($status) {
625 switch ($status) {
626
627 case 'expired':
628 case 'invalid':
629 case 'error':
630 case 'expires_today':
631 case 'down':
632 case 'infected':
633 case 'deface':
634 case 'not_installed':
635 case 'quarantine':
636 $status_data = [
637 'color' => 'red',
638 ];
639 break;
640
641 case 'no_cert':
642 case 'expires':
643 case 'open_ports':
644 case 'modified':
645 case 'not_supported':
646 case 'not_registered':
647 case 'blocked':
648 case 'pause':
649 case 'internal_error':
650 case 'update_error':
651 case 'config_error':
652 case 'agent_not_available':
653 case 'session_error':
654 $status_data = [
655 'color' => 'yellow',
656 ];
657 break;
658
659 case 'clean':
660 case 'installed':
661 case 'up':
662 case 'scanned':
663 case 'working':
664 $status_data = [
665 'color' => 'green',
666 ];
667 break;
668
669 case 'deleted':
670 $status_data = [
671 'color' => 'black',
672 ];
673 break;
674
675 case 'installing':
676 case 'pending':
677 default:
678 $status_data = [
679 'color' => 'gray',
680 ];
681 break;
682
683 }
684
685 return $status_data;
686 }
687
688 /**
689 * Get the translation of service.
690 *
691 * @param $service
692 * Service short name.
693 *
694 * @return string
695 * Translation of service.
696 */
697 public static function getServiceName($service){
698 $services = [
699 "wa" => __('Availability', 'wtotem'),
700 "rc" => __('Reputation', 'wtotem'),
701 "ssl" => 'SSL',
702 "cms" => __('Technologies', 'wtotem'),
703 "dc" => __('Deface', 'wtotem'),
704 "ps" => __('Ports', 'wtotem'),
705 "waf" => __('Firewall', 'wtotem'),
706 "av" => __('Antivirus', 'wtotem'),
707 "dec" => __('Domain', 'wtotem'),
708 ];
709 return $services[$service];
710 }
711
712 /**
713 * Get reports with modules list.
714 *
715 * @param array $edges
716 * Data on generated reports.
717 *
718 * @return array
719 * Returns an array with converted data.
720 */
721 public static function getReports(array $edges) {
722 $modulesLang = [
723 'wa' => __('Availability log', 'wtotem'),
724 'dc' => __('Deface log', 'wtotem'),
725 'ps' => __('Port log', 'wtotem'),
726 'rc' => __('Reputation log', 'wtotem'),
727 'sc' => __('Evaluation log', 'wtotem'),
728 'av' => __('Antivirus log', 'wtotem'),
729 'waf' => __('Firewall log', 'wtotem'),
730 ];
731
732 $reports = [];
733
734 foreach ($edges as $edge) {
735 if (in_array(FALSE, $edge["node"])) {
736 $arr = [];
737 foreach ($edge["node"] as $module => $value) {
738 if ($value == TRUE && array_key_exists($module, $modulesLang)) {
739 $arr[] = $modulesLang[$module];
740 }
741 }
742 $modules = implode(", ", $arr);
743 }
744 else {
745 $modules = __('All modules', 'wtotem');
746 }
747
748 $reports[] = [
749 'id' => $edge["node"]['id'],
750 'modules' => $modules,
751 'created_at' => self::dateFormatter($edge["node"]['createdAt']),
752 ];
753 }
754
755 return $reports;
756 }
757
758 /**
759 * Get reputation status description.
760 *
761 * @param string $status
762 * Reputation status.
763 *
764 * @return string
765 * Returns a description of the reputation status
766 */
767 public static function getReputationInfo($status) {
768 switch ($status) {
769 case 'clean':
770 $data = __("Don't worry, your reputation is good", 'wtotem');
771 break;
772
773 case 'infected':
774 $data = __('Oh, your reputation is bad', 'wtotem');
775 break;
776
777 default:
778 $data = __('Information is being updated', 'wtotem');
779 }
780 return $data;
781 }
782
783 /**
784 * Get blacklists entries counts.
785 *
786 * @param string $status
787 * Reputation status.
788 * @param array $virus_list
789 * Sources where the site can be blacklisted.
790 *
791 * @return int
792 * Number of references in blacklists.
793 */
794 public static function blacklistsEntries($status, array $virus_list) {
795 $count = 0;
796 if ($status != "clean") {
797 foreach ($virus_list as &$list) {
798 if (!empty($list['virus']['type'])) {
799 $count++;
800 }
801 }
802 }
803 return $count;
804 }
805
806 /**
807 * Classification of the rating in the letter grades.
808 *
809 * @param int $score
810 * Site rating from 1 to 100.
811 *
812 * @return array
813 * Returns an array of data.
814 */
815 public static function scoreGrading($score) {
816 if ($score < 0 || $score > 100) {
817 return ['grade' => '', 'color' => ''];
818 }
819
820 $scores = [
821 100 => 'A+',
822 90 => 'A',
823 80 => 'A-',
824 70 => 'B+',
825 60 => 'B',
826 50 => 'B-',
827 35 => 'C+',
828 20 => 'C',
829 0 => 'C-',
830 ];
831
832 foreach ($scores as $key => $value) {
833 if ($score >= $key) {
834 $grade = $value;
835 break;
836 }
837 }
838
839 // Set a color depending on the grade.
840 switch ($score) {
841 case $score >= 80:
842 $color = 'green';
843 break;
844
845 case $score >= 50:
846 $color = 'orange';
847 break;
848
849 default:
850 $color = 'red';
851 }
852
853 return ['grade' => $grade, 'color' => $color];
854 }
855
856 /**
857 * Calculate the number of remaining days.
858 *
859 * @param string $date
860 * Expiry date.
861 *
862 * @return string
863 * Returns the number of days before the expiration date.
864 */
865 public static function daysLeft($date) {
866 if ((int) $date === 0) {
867 $days_left = 0;
868 }
869 else {
870 $now = new \DateTime();
871 $expiry_date = new \DateTime();
872 $timestamp = strtotime($date);
873 $expiry_date->setTimestamp($timestamp);
874 $days_left = $expiry_date->diff($now)->format("%a");
875 }
876 return $days_left;
877 }
878
879 /**
880 * Converting the firewall logs.
881 *
882 * @param array $logs_
883 * Firewall logs from WebTotem.
884 *
885 * @return array
886 * Converted array of logs.
887 */
888 public static function wafLogs(array $logs_) {
889 $logs = [];
890 foreach ($logs_ as $key => $log) {
891 $log = $log['node'];
892
893 $logs[$key]['ip'] = $log['ip'];
894 $logs[$key]['request'] = htmlspecialchars(urldecode($log['request']));
895 $logs[$key]['time'] = self::dateFormatter($log['time']);
896 $logs[$key]['country_code'] = strtolower($log['country']);
897 $logs[$key]['country'] = $log['location']['country']['nameEn'];
898 $logs[$key]['blocked'] = $log['blocked'] ? __('Blocked IP', 'wtotem') : __('Not blocked', 'wtotem');
899 }
900 return $logs;
901 }
902
903 /**
904 * Converting firewall data to json for a D3 chart.
905 *
906 * @param array $charts
907 * Charts data from WebTotem.
908 *
909 * @return array
910 * Returns the converted data for chart.
911 */
912 public static function generateWafChart(array $charts) {
913 $sum = 0;
914 foreach ($charts as $chart) {
915 $sum += $chart['attacks'];
916 }
917 if ($sum == 0) {
918 return ['chart' => FALSE, 'count_attacks' => 0, 'count_blocks' => 0];
919 }
920
921 // Get days count.
922 $charts_ = $charts;
923 $first = array_shift($charts_);
924 $last = array_pop($charts_);
925 $days = ceil((strtotime($last['time']) - strtotime($first['time'])) / 86400);
926
927 // Set variables.
928 $count_attacks = $count_blocks = 0;
929
930 foreach ($charts as $chart) {
931 if ($days <= 1) {
932 $time_zone = WebTotemOption::getOption('time_zone_offset');
933 $userTime = ($time_zone) ? strtotime($time_zone . ' hours', strtotime($chart['time'])) : strtotime($chart['time']);
934 }
935 if (($chart['attacks'] and $days == 2) or $days != 2) {
936 $result[] = [
937 'date' => ($days <= 1) ? date("Y-m-d H:00:00", $userTime) : date("Y-m-d", strtotime($chart['time'])),
938 'count' => $chart['blocked'],
939 'attacks' => $chart['attacks'],
940 'blocked' => $chart['blocked'],
941 ];
942 $count_attacks += $chart['attacks'];
943 $count_blocks += $chart['blocked'];
944 }
945 }
946
947 if (!isset($result)) {
948 return [
949 'chart' => FALSE,
950 'count_attacks' => 0,
951 'count_blocks' => 0,
952 ];
953 }
954
955 return [
956 'chart' => json_encode($result),
957 'count_attacks' => $count_attacks,
958 'count_blocks' => $count_blocks,
959 'days' => $days,
960 ];
961 }
962
963 /**
964 * Converting data to json for a D3 chart.
965 *
966 * @param array $charts
967 * Charts data from WebTotem.
968 * @param int $days
969 * The number of days to build the chart.
970 *
971 * @return bool|string
972 * Returns the converted data for chart.
973 */
974 public static function generateChart(array $charts, $days = 7) {
975 $sum = 0;
976 foreach ($charts as $chart) {
977 $sum += $chart['value'];
978 }
979 if ($sum == 0) {
980 return FALSE;
981 }
982
983 $result = [];
984
985 foreach ($charts as $chart) {
986 if ($days <= 1) {
987 $time_zone = WebTotemOption::getOption('time_zone_offset');
988 $userTime = ($time_zone) ? strtotime($time_zone . 'hours', strtotime($chart['time'])) : strtotime($chart['time']);
989 }
990 $result[] = [
991 'date' => ($days <= 1) ? date("Y-m-d H:00:00", $userTime) : date("Y-m-d", strtotime($chart['time'])),
992 'value' => $chart['value'],
993 ];
994 }
995
996 return json_encode($result, TRUE);
997 }
998
999 /**
1000 * Converting data to json for a D3 chart.
1001 *
1002 * @param array $data
1003 * Charts data from WebTotem.
1004 *
1005 * @return array|bool
1006 * Returns the converted data for chart.
1007 */
1008 public static function generateAttacksMapChart(array $data) {
1009 $attacks = [];
1010 $countries = [];
1011 $labels = [];
1012 foreach ($data as $value) {
1013 $attacks[] = $value['attacks'];
1014 $labels[] = self::getCountryName($value['country']);
1015 $countries[] = $value['location']['country']['nameEn'];
1016 }
1017 $result = ['attacks' => $attacks, 'countries' => $countries, 'labels' => $labels];
1018
1019 if (!$attacks) {
1020 return FALSE;
1021 }
1022
1023 return json_encode($result, TRUE);
1024 }
1025
1026 /**
1027 * Reassembling the antivirus logs.
1028 *
1029 * @param array $logs_
1030 * Antivirus logs from WebTotem.
1031 *
1032 * @return array
1033 * Reassembled array of logs.
1034 */
1035 public static function getAntivirusLogs(array $logs_) {
1036 $logs = [];
1037 foreach ($logs_ as $key => $log) {
1038 $log = $log['node'];
1039
1040 $file_info = new SplFileInfo(urldecode($log['filePath']));
1041
1042 $log['original_path'] = $log['filePath'];
1043 $log['file_path'] = $file_info->getPath() . '/';
1044 $log['file_name'] = $file_info->getFilename();
1045 $log['time'] = self::dateFormatter($log['time']);
1046 $log['permissions_changed'] = $log['permissionsChanged'];
1047 $log['status'] = self::getStatusData($log['event']);
1048 $log['class'] = 'wt-text--green';
1049
1050 switch ($log['event']) {
1051 case 'modified':
1052 case 'quarantine':
1053 $log['class'] = "wt-text--yellow";
1054 break;
1055
1056 case 'deleted':
1057 $log['class'] = "wt-text--light-gray";
1058 break;
1059
1060 case 'infected':
1061 $log['class'] = "wt-text--red";
1062 break;
1063 }
1064
1065 $logs[$key] = $log;
1066 }
1067 return $logs;
1068 }
1069
1070 /**
1071 * Reassembling the quarantine logs.
1072 *
1073 * @param array $logs_
1074 * Quarantine logs from WebTotem.
1075 *
1076 * @return array
1077 * Reassembled array of logs.
1078 */
1079 public static function getQuarantineLogs(array $logs_) {
1080 $logs = [];
1081 foreach ($logs_ as $key => $log) {
1082 $logs[$key] = $log;
1083 $logs[$key]['path'] = urldecode($log['path']);
1084 $logs[$key]['date'] = self::dateFormatter($log['date']);
1085 }
1086
1087 return $logs;
1088 }
1089
1090 /**
1091 * Generate an array of IP address data.
1092 *
1093 * @param array $data
1094 * IP addresses data from WebTotem.
1095 * @param string $list_name
1096 * Allow or deny list.
1097 *
1098 * @return array
1099 * Returns array of data.
1100 */
1101 public static function getIpList(array $data, $list_name) {
1102 $list = [];
1103 foreach ($data as $item) {
1104 $list[] = [
1105 'ip' => $item['ip'],
1106 'id' => $item['id'],
1107 'created_at' => self::dateFormatter($item['createdAt']),
1108 'list_name' => $list_name,
1109 ];
1110 }
1111 return $list;
1112 }
1113
1114 /**
1115 * Generate an array of URL address data.
1116 *
1117 * @param array $data
1118 * URL addresses data from WebTotem.
1119 *
1120 * @return array
1121 * Returns array of data.
1122 */
1123 public static function getUrlAllowList(array $data) {
1124 $list = [];
1125 foreach ($data as $item) {
1126 $list[] = [
1127 'url' => $item['url'],
1128 'id' => $item['id'],
1129 'created_at' => self::dateFormatter($item['createdAt']),
1130 'list_name' => 'url_allow',
1131 ];
1132 }
1133 return $list;
1134 }
1135
1136 /**
1137 * Convert IP list to be transferred to WebTotem.
1138 *
1139 * @param string $data
1140 * IP list.
1141 *
1142 * @return string
1143 * Returns the converted string.
1144 */
1145 public static function convertIpListForApi($data) {
1146 if (!$data) {
1147 return FALSE;
1148 }
1149
1150 $ips = preg_split("/(?(?=[\s,])[^.]|^$)/", $data);
1151
1152 if (is_array($ips)) {
1153 $ips_ = '[';
1154 foreach ($ips as $ip) {
1155 if (!empty($ip)) {
1156 $ips_ .= '"' . $ip . '",';
1157 }
1158 }
1159 $ips_ = substr($ips_, 0, -1);
1160 $ips_ .= ']';
1161 }
1162 else {
1163 $ips_ = '"' . $ips . '"';
1164 }
1165
1166 return $ips_;
1167 }
1168
1169 /**
1170 * Get data of the country with the most attacks.
1171 *
1172 * @param array $map
1173 * Map logs from WebTotem.
1174 * @param int $count_attacks
1175 * Total attacks.
1176 *
1177 * @return array
1178 * Returns array of data.
1179 */
1180 public static function getMostAttacksData($map) {
1181
1182 if ($map) {
1183 $most_attacks_key = array_search(max(array_column($map, 'attacks')), array_column($map, 'attacks'));
1184 $total_attacks = array_sum(array_column($map, 'attacks'));
1185
1186 $data['percent'] = ($total_attacks) ? round($map[$most_attacks_key]['attacks'] / $total_attacks * 100) : 0;
1187 $data['country'] = self::getCountryName($map[$most_attacks_key]['country']);
1188 $data['offset'] = 176 / 100 * (100 - $data['percent']);
1189
1190 return $data;
1191 }
1192
1193 return ['percent' => 0, 'country' => FALSE, 'offset' => 0];
1194 }
1195
1196 /**
1197 * Getting the country name by two-letter code.
1198 *
1199 * @param string $key
1200 * Two-letter code.
1201 *
1202 * @return string
1203 * Returns country name.
1204 */
1205 public static function getCountryName($key) {
1206 $countries = WebTotemCountryManager::getStandardList();
1207 $key = (string) $key;
1208
1209 return (array_key_exists($key, $countries)) ? $countries[$key] : $key;
1210 }
1211
1212
1213 /**
1214 * Get configs data.
1215 *
1216 * @param array $array
1217 * Original array.
1218 * @param string $key
1219 * The key to use as an index.
1220 *
1221 * @return array
1222 * Configs data array.
1223 */
1224 public static function getConfigsData(array $array, $key) {
1225 $configs = self::arrayMapIndex($array, $key);
1226
1227 foreach ($configs as $service => $config){
1228 $configs[$service]['checked'] = ($config['isActive']) ? 'checked' : '';
1229 $configs[$service]['notification_checked'] = (isset($config['notifications']) && $config['notifications']) ? 'checked' : '';
1230 }
1231
1232 return $configs;
1233 }
1234
1235 /**
1236 * Get waf setting data.
1237 *
1238 * @param array $settings
1239 * Original array.
1240 *
1241 * @return array
1242 * Configs data array.
1243 */
1244 public static function getWafSettingData(array $settings) {
1245 $_settings['gdn']['checked'] = (isset($settings['gdn']) && !$settings['gdn']) ? '' : 'checked';
1246 $_settings['dos'] = [
1247 'checked' => (isset($settings['dosProtection']) && !$settings['dosProtection']) ? '' : 'checked',
1248 'visually' => (isset($settings['dosProtection']) && !$settings['dosProtection']) ? 'visually-hidden' : '',
1249 ];
1250 $_settings['dos_limit'] = $settings['dosLimit'] ?: 1000;
1251
1252 $_settings['login_attempt'] = [
1253 'checked' => (isset($settings['loginAttemptsProtection']) && !$settings['loginAttemptsProtection']) ? '' : 'checked',
1254 'visually' => (isset($settings['loginAttemptsProtection']) && !$settings['loginAttemptsProtection']) ? 'visually-hidden' : '',
1255 ];
1256 $_settings['login_attempt_limit'] = $settings['loginAttemptsLimit'] ?: 20;
1257
1258 return $_settings;
1259 }
1260
1261 /**
1262 * Get plugin settings data.
1263 *
1264 * @return array
1265 * Configs data array.
1266 */
1267 public static function getPluginSettingsData() {
1268
1269 $settings = WebTotemOption::getPluginSettings();
1270 $_settings = $settings;
1271
1272 $_settings['hide_wp_version_checked'] = $settings['hide_wp_version'] ? 'checked' : '';
1273 $_settings['recaptcha_checked'] = $settings['recaptcha'] ? 'checked' : '';
1274
1275 return $_settings;
1276 }
1277
1278 /**
1279 * Replace array indexes by key.
1280 *
1281 * @param array $array
1282 * Original array.
1283 * @param string $key
1284 * The key to use as an index.
1285 *
1286 * @return array
1287 * Returns a new array.
1288 */
1289 public static function arrayMapIndex(array $array, $key) {
1290 $new_array = [];
1291 foreach ($array as $item) {
1292 if (array_key_exists($key, $item)) {
1293 $new_array[$item[$key]] = $item;
1294 }
1295 }
1296 return $new_array;
1297 }
1298
1299 /**
1300 * Generate random string.
1301 *
1302 * @param int $length
1303 * The required length of the string.
1304 *
1305 * @return string
1306 * Returns random string.
1307 */
1308 public static function generateRandomString( int $length = 10): string {
1309 $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-';
1310 $charactersLength = strlen($characters);
1311 $randomString = '';
1312 for ($i = 0; $i < $length; $i++) {
1313 $randomString .= $characters[rand(0, $charactersLength - 1)];
1314 }
1315 return $randomString;
1316 }
1317
1318 /**
1319 * Encodes the less than, greater than, ampersand,double quote
1320 * and single quote characters. Will never double encode entities.
1321 *
1322 * @see https://developer.wordpress.org/reference/functions/esc_attr/
1323 *
1324 * @param string $text
1325 * The text which is to be encoded.
1326 *
1327 * @return string
1328 * The encoded text with HTML entities.
1329 */
1330 public static function escape($text = '')
1331 {
1332 return esc_attr($text);
1333 }
1334
1335 /**
1336 * Get notifications array.
1337 *
1338 * @return array
1339 * Returns notifications array.
1340 */
1341 public static function getNotifications() {
1342
1343 $notifications_data = WebTotemOption::getNotificationsData();
1344 $notifications = [];
1345
1346 foreach ($notifications_data as $notification) {
1347 switch ($notification['type']) {
1348 case 'error':
1349 $image = 'alert-error.svg';
1350 $class = 'wtotem_alert__title_red';
1351 break;
1352
1353 case 'warning':
1354 $image = 'alert-warning.svg';
1355 $class = 'wtotem_alert__title_yellow';
1356 break;
1357
1358 case 'success':
1359 $image = 'alert-success.svg';
1360 $class = 'wtotem_alert__title_green';
1361 break;
1362
1363 case 'info':
1364 $image = 'info-blue.svg';
1365 $class = 'wtotem_alert__title_blue';
1366 break;
1367 }
1368
1369 $notifications[] = [
1370 "text" => $notification['notice'],
1371 "id" => self::generateRandomString(8),
1372 "type" => self::getStatusText($notification['type']),
1373 "image" => $image,
1374 "class" => $class,
1375 ];
1376 }
1377
1378 return $notifications;
1379 }
1380
1381 /**
1382 * Get current agent installation statuses.
1383 *
1384 * @param array $agents_statuses
1385 * Agents statuses got from the WebTotem API.
1386 *
1387 * @return array
1388 * Returns an array with agent installation status data.
1389 */
1390 public static function getAgentsStatuses(array $agents_statuses) {
1391 $agents = ['am', 'waf', 'av'];
1392 $installing_statuses = [
1393 'not_installed',
1394 'installing',
1395 'internal_error',
1396 'update_error',
1397 'config_error',
1398 'session_error',
1399 ];
1400
1401 $process_statuses = [];
1402 $option_statuses = [];
1403
1404 foreach ($agents as $agent) {
1405 $status = WebTotemAgentManager::checkInstalledService($agent);
1406 $option_statuses[$agent] = $status['option_status'] ?: FALSE;
1407
1408 if ($agent == 'am') {
1409 if ($status['file_status']) {
1410 $process_statuses[$agent] = 'installed';
1411 }
1412 else {
1413 $process_statuses[$agent] = 'failed';
1414 }
1415 }
1416 else {
1417 if ($status['file_status']) {
1418 if (in_array($agents_statuses[$agent], $installing_statuses)) {
1419 $process_statuses[$agent] = 'installing';
1420 }
1421 elseif ($agents_statuses[$agent] == 'agent_not_available') {
1422 $process_statuses[$agent] = 'failed';
1423 }
1424 else {
1425 $process_statuses[$agent] = 'installed';
1426 }
1427 }
1428 else {
1429 $process_statuses[$agent] = 'installing';
1430 }
1431 }
1432 }
1433
1434 return [
1435 'process_statuses' => $process_statuses,
1436 'option_statuses' => $option_statuses,
1437 ];
1438 }
1439
1440 }
1441