PluginProbe
WebTotem Security / 2.4.14
WebTotem Security v2.4.14
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.14, at lib/Helper.php

1,456 lines 38.0 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 $more = [
901 'ip' => $log['ip'],
902 'proxy_ip' => $log['proxyIp'],
903 'source' => $log['source'],
904 'request' => htmlspecialchars(urldecode($log['request'])),
905 'user_agent' => $log['userAgent'],
906 'time' => self::dateFormatter($log['time']),
907 'type' => $log['type'],
908 'category' => $log['category'],
909 'country' => $log['location']['country']['nameEn'],
910 'payload' => htmlspecialchars(urldecode($log['payload'])),
911 ];
912
913 $logs[$key]['more'] = json_encode($more);
914 }
915 return $logs;
916 }
917
918 /**
919 * Converting firewall data to json for a D3 chart.
920 *
921 * @param array $charts
922 * Charts data from WebTotem.
923 *
924 * @return array
925 * Returns the converted data for chart.
926 */
927 public static function generateWafChart(array $charts) {
928 $sum = 0;
929 foreach ($charts as $chart) {
930 $sum += $chart['attacks'];
931 }
932 if ($sum == 0) {
933 return ['chart' => FALSE, 'count_attacks' => 0, 'count_blocks' => 0];
934 }
935
936 // Get days count.
937 $charts_ = $charts;
938 $first = array_shift($charts_);
939 $last = array_pop($charts_);
940 $days = ceil((strtotime($last['time']) - strtotime($first['time'])) / 86400);
941
942 // Set variables.
943 $count_attacks = $count_blocks = 0;
944
945 foreach ($charts as $chart) {
946 if ($days <= 1) {
947 $time_zone = WebTotemOption::getOption('time_zone_offset');
948 $userTime = ($time_zone) ? strtotime($time_zone . ' hours', strtotime($chart['time'])) : strtotime($chart['time']);
949 }
950 if (($chart['attacks'] and $days == 2) or $days != 2) {
951 $result[] = [
952 'date' => ($days <= 1) ? date("Y-m-d H:00:00", $userTime) : date("Y-m-d", strtotime($chart['time'])),
953 'count' => $chart['blocked'],
954 'attacks' => $chart['attacks'],
955 'blocked' => $chart['blocked'],
956 ];
957 $count_attacks += $chart['attacks'];
958 $count_blocks += $chart['blocked'];
959 }
960 }
961
962 if (!isset($result)) {
963 return [
964 'chart' => FALSE,
965 'count_attacks' => 0,
966 'count_blocks' => 0,
967 ];
968 }
969
970 return [
971 'chart' => json_encode($result),
972 'count_attacks' => $count_attacks,
973 'count_blocks' => $count_blocks,
974 'days' => $days,
975 ];
976 }
977
978 /**
979 * Converting data to json for a D3 chart.
980 *
981 * @param array $charts
982 * Charts data from WebTotem.
983 * @param int $days
984 * The number of days to build the chart.
985 *
986 * @return bool|string
987 * Returns the converted data for chart.
988 */
989 public static function generateChart(array $charts, $days = 7) {
990 $sum = 0;
991 foreach ($charts as $chart) {
992 $sum += $chart['value'];
993 }
994 if ($sum == 0) {
995 return FALSE;
996 }
997
998 $result = [];
999
1000 foreach ($charts as $chart) {
1001 if ($days <= 1) {
1002 $time_zone = WebTotemOption::getOption('time_zone_offset');
1003 $userTime = ($time_zone) ? strtotime($time_zone . 'hours', strtotime($chart['time'])) : strtotime($chart['time']);
1004 }
1005 $result[] = [
1006 'date' => ($days <= 1) ? date("Y-m-d H:00:00", $userTime) : date("Y-m-d", strtotime($chart['time'])),
1007 'value' => $chart['value'],
1008 ];
1009 }
1010
1011 return json_encode($result, TRUE);
1012 }
1013
1014 /**
1015 * Converting data to json for a D3 chart.
1016 *
1017 * @param array $data
1018 * Charts data from WebTotem.
1019 *
1020 * @return array|bool
1021 * Returns the converted data for chart.
1022 */
1023 public static function generateAttacksMapChart(array $data) {
1024 $attacks = [];
1025 $countries = [];
1026 $labels = [];
1027 foreach ($data as $value) {
1028 $attacks[] = $value['attacks'];
1029 $labels[] = self::getCountryName($value['country']);
1030 $countries[] = $value['location']['country']['nameEn'];
1031 }
1032 $result = ['attacks' => $attacks, 'countries' => $countries, 'labels' => $labels];
1033
1034 if (!$attacks) {
1035 return FALSE;
1036 }
1037
1038 return json_encode($result, TRUE);
1039 }
1040
1041 /**
1042 * Reassembling the antivirus logs.
1043 *
1044 * @param array $logs_
1045 * Antivirus logs from WebTotem.
1046 *
1047 * @return array
1048 * Reassembled array of logs.
1049 */
1050 public static function getAntivirusLogs(array $logs_) {
1051 $logs = [];
1052 foreach ($logs_ as $key => $log) {
1053 $log = $log['node'];
1054
1055 $file_info = new SplFileInfo(urldecode($log['filePath']));
1056
1057 $log['original_path'] = $log['filePath'];
1058 $log['file_path'] = $file_info->getPath() . '/';
1059 $log['file_name'] = $file_info->getFilename();
1060 $log['time'] = self::dateFormatter($log['time']);
1061 $log['permissions_changed'] = $log['permissionsChanged'];
1062 $log['status'] = self::getStatusData($log['event']);
1063 $log['class'] = 'wt-text--green';
1064
1065 switch ($log['event']) {
1066 case 'modified':
1067 case 'quarantine':
1068 $log['class'] = "wt-text--yellow";
1069 break;
1070
1071 case 'deleted':
1072 $log['class'] = "wt-text--light-gray";
1073 break;
1074
1075 case 'infected':
1076 $log['class'] = "wt-text--red";
1077 break;
1078 }
1079
1080 $logs[$key] = $log;
1081 }
1082 return $logs;
1083 }
1084
1085 /**
1086 * Reassembling the quarantine logs.
1087 *
1088 * @param array $logs_
1089 * Quarantine logs from WebTotem.
1090 *
1091 * @return array
1092 * Reassembled array of logs.
1093 */
1094 public static function getQuarantineLogs(array $logs_) {
1095 $logs = [];
1096 foreach ($logs_ as $key => $log) {
1097 $logs[$key] = $log;
1098 $logs[$key]['path'] = urldecode($log['path']);
1099 $logs[$key]['date'] = self::dateFormatter($log['date']);
1100 }
1101
1102 return $logs;
1103 }
1104
1105 /**
1106 * Generate an array of IP address data.
1107 *
1108 * @param array $data
1109 * IP addresses data from WebTotem.
1110 * @param string $list_name
1111 * Allow or deny list.
1112 *
1113 * @return array
1114 * Returns array of data.
1115 */
1116 public static function getIpList(array $data, $list_name) {
1117 $list = [];
1118 foreach ($data as $item) {
1119 $list[] = [
1120 'ip' => $item['ip'],
1121 'id' => $item['id'],
1122 'created_at' => self::dateFormatter($item['createdAt']),
1123 'list_name' => $list_name,
1124 ];
1125 }
1126 return $list;
1127 }
1128
1129 /**
1130 * Generate an array of URL address data.
1131 *
1132 * @param array $data
1133 * URL addresses data from WebTotem.
1134 *
1135 * @return array
1136 * Returns array of data.
1137 */
1138 public static function getUrlAllowList(array $data) {
1139 $list = [];
1140 foreach ($data as $item) {
1141 $list[] = [
1142 'url' => $item['url'],
1143 'id' => $item['id'],
1144 'created_at' => self::dateFormatter($item['createdAt']),
1145 'list_name' => 'url_allow',
1146 ];
1147 }
1148 return $list;
1149 }
1150
1151 /**
1152 * Convert IP list to be transferred to WebTotem.
1153 *
1154 * @param string $data
1155 * IP list.
1156 *
1157 * @return string
1158 * Returns the converted string.
1159 */
1160 public static function convertIpListForApi($data) {
1161 if (!$data) {
1162 return FALSE;
1163 }
1164
1165 $ips = preg_split("/(?(?=[\s,])[^.]|^$)/", $data);
1166
1167 if (is_array($ips)) {
1168 $ips_ = '[';
1169 foreach ($ips as $ip) {
1170 if (!empty($ip)) {
1171 $ips_ .= '"' . $ip . '",';
1172 }
1173 }
1174 $ips_ = substr($ips_, 0, -1);
1175 $ips_ .= ']';
1176 }
1177 else {
1178 $ips_ = '"' . $ips . '"';
1179 }
1180
1181 return $ips_;
1182 }
1183
1184 /**
1185 * Get data of the country with the most attacks.
1186 *
1187 * @param array $map
1188 * Map logs from WebTotem.
1189 * @param int $count_attacks
1190 * Total attacks.
1191 *
1192 * @return array
1193 * Returns array of data.
1194 */
1195 public static function getMostAttacksData($map) {
1196
1197 if ($map) {
1198 $most_attacks_key = array_search(max(array_column($map, 'attacks')), array_column($map, 'attacks'));
1199 $total_attacks = array_sum(array_column($map, 'attacks'));
1200
1201 $data['percent'] = ($total_attacks) ? round($map[$most_attacks_key]['attacks'] / $total_attacks * 100) : 0;
1202 $data['country'] = self::getCountryName($map[$most_attacks_key]['country']);
1203 $data['offset'] = 176 / 100 * (100 - $data['percent']);
1204
1205 return $data;
1206 }
1207
1208 return ['percent' => 0, 'country' => FALSE, 'offset' => 0];
1209 }
1210
1211 /**
1212 * Getting the country name by two-letter code.
1213 *
1214 * @param string $key
1215 * Two-letter code.
1216 *
1217 * @return string
1218 * Returns country name.
1219 */
1220 public static function getCountryName($key) {
1221 $countries = WebTotemCountryManager::getStandardList();
1222 $key = (string) $key;
1223
1224 return (array_key_exists($key, $countries)) ? $countries[$key] : $key;
1225 }
1226
1227
1228 /**
1229 * Get configs data.
1230 *
1231 * @param array $array
1232 * Original array.
1233 * @param string $key
1234 * The key to use as an index.
1235 *
1236 * @return array
1237 * Configs data array.
1238 */
1239 public static function getConfigsData(array $array, $key) {
1240 $configs = self::arrayMapIndex($array, $key);
1241
1242 foreach ($configs as $service => $config){
1243 $configs[$service]['checked'] = ($config['isActive']) ? 'checked' : '';
1244 $configs[$service]['notification_checked'] = (isset($config['notifications']) && $config['notifications']) ? 'checked' : '';
1245 }
1246
1247 return $configs;
1248 }
1249
1250 /**
1251 * Get waf setting data.
1252 *
1253 * @param array $settings
1254 * Original array.
1255 *
1256 * @return array
1257 * Configs data array.
1258 */
1259 public static function getWafSettingData(array $settings) {
1260 $_settings['gdn']['checked'] = (isset($settings['gdn']) && !$settings['gdn']) ? '' : 'checked';
1261 $_settings['dos'] = [
1262 'checked' => (isset($settings['dosProtection']) && !$settings['dosProtection']) ? '' : 'checked',
1263 'visually' => (isset($settings['dosProtection']) && !$settings['dosProtection']) ? 'visually-hidden' : '',
1264 ];
1265 $_settings['dos_limit'] = $settings['dosLimit'] ?: 1000;
1266
1267 $_settings['login_attempt'] = [
1268 'checked' => (isset($settings['loginAttemptsProtection']) && !$settings['loginAttemptsProtection']) ? '' : 'checked',
1269 'visually' => (isset($settings['loginAttemptsProtection']) && !$settings['loginAttemptsProtection']) ? 'visually-hidden' : '',
1270 ];
1271 $_settings['login_attempt_limit'] = $settings['loginAttemptsLimit'] ?: 20;
1272
1273 return $_settings;
1274 }
1275
1276 /**
1277 * Get plugin settings data.
1278 *
1279 * @return array
1280 * Configs data array.
1281 */
1282 public static function getPluginSettingsData() {
1283
1284 $settings = WebTotemOption::getPluginSettings();
1285 $_settings = $settings;
1286
1287 $_settings['hide_wp_version_checked'] = (array_key_exists('hide_wp_version', $settings) and $settings['hide_wp_version']) ? 'checked' : '';
1288 $_settings['recaptcha_checked'] = (array_key_exists('recaptcha', $settings) and $settings['recaptcha']) ? 'checked' : '';
1289
1290 return $_settings;
1291 }
1292
1293 /**
1294 * Replace array indexes by key.
1295 *
1296 * @param array $array
1297 * Original array.
1298 * @param string $key
1299 * The key to use as an index.
1300 *
1301 * @return array
1302 * Returns a new array.
1303 */
1304 public static function arrayMapIndex(array $array, $key) {
1305 $new_array = [];
1306 foreach ($array as $item) {
1307 if (array_key_exists($key, $item)) {
1308 $new_array[$item[$key]] = $item;
1309 }
1310 }
1311 return $new_array;
1312 }
1313
1314 /**
1315 * Generate random string.
1316 *
1317 * @param int $length
1318 * The required length of the string.
1319 *
1320 * @return string
1321 * Returns random string.
1322 */
1323 public static function generateRandomString( int $length = 10): string {
1324 $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-';
1325 $charactersLength = strlen($characters);
1326 $randomString = '';
1327 for ($i = 0; $i < $length; $i++) {
1328 $randomString .= $characters[rand(0, $charactersLength - 1)];
1329 }
1330 return $randomString;
1331 }
1332
1333 /**
1334 * Encodes the less than, greater than, ampersand,double quote
1335 * and single quote characters. Will never double encode entities.
1336 *
1337 * @see https://developer.wordpress.org/reference/functions/esc_attr/
1338 *
1339 * @param string $text
1340 * The text which is to be encoded.
1341 *
1342 * @return string
1343 * The encoded text with HTML entities.
1344 */
1345 public static function escape($text = '')
1346 {
1347 return esc_attr($text);
1348 }
1349
1350 /**
1351 * Get notifications array.
1352 *
1353 * @return array
1354 * Returns notifications array.
1355 */
1356 public static function getNotifications() {
1357
1358 $notifications_data = WebTotemOption::getNotificationsData();
1359 $notifications = [];
1360
1361 foreach ($notifications_data as $notification) {
1362 switch ($notification['type']) {
1363 case 'error':
1364 $image = 'alert-error.svg';
1365 $class = 'wtotem_alert__title_red';
1366 break;
1367
1368 case 'warning':
1369 $image = 'alert-warning.svg';
1370 $class = 'wtotem_alert__title_yellow';
1371 break;
1372
1373 case 'success':
1374 $image = 'alert-success.svg';
1375 $class = 'wtotem_alert__title_green';
1376 break;
1377
1378 case 'info':
1379 $image = 'info-blue.svg';
1380 $class = 'wtotem_alert__title_blue';
1381 break;
1382 }
1383
1384 $notifications[] = [
1385 "text" => $notification['notice'],
1386 "id" => self::generateRandomString(8),
1387 "type" => self::getStatusText($notification['type']),
1388 "image" => $image,
1389 "class" => $class,
1390 ];
1391 }
1392
1393 return $notifications;
1394 }
1395
1396 /**
1397 * Get current agent installation statuses.
1398 *
1399 * @param array $agents_statuses
1400 * Agents statuses got from the WebTotem API.
1401 *
1402 * @return array
1403 * Returns an array with agent installation status data.
1404 */
1405 public static function getAgentsStatuses(array $agents_statuses) {
1406 $agents = ['am', 'waf', 'av'];
1407 $installing_statuses = [
1408 'not_installed',
1409 'installing',
1410 'internal_error',
1411 'update_error',
1412 'config_error',
1413 'session_error',
1414 ];
1415
1416 $process_statuses = [];
1417 $option_statuses = [];
1418
1419 foreach ($agents as $agent) {
1420 $status = WebTotemAgentManager::checkInstalledService($agent);
1421 $option_statuses[$agent] = $status['option_status'] ?: FALSE;
1422
1423 if ($agent == 'am') {
1424 if ($status['file_status']) {
1425 $process_statuses[$agent] = 'installed';
1426 }
1427 else {
1428 $process_statuses[$agent] = 'failed';
1429 }
1430 }
1431 else {
1432 if ($status['file_status']) {
1433 if (in_array($agents_statuses[$agent], $installing_statuses)) {
1434 $process_statuses[$agent] = 'installing';
1435 }
1436 elseif ($agents_statuses[$agent] == 'agent_not_available') {
1437 $process_statuses[$agent] = 'failed';
1438 }
1439 else {
1440 $process_statuses[$agent] = 'installed';
1441 }
1442 }
1443 else {
1444 $process_statuses[$agent] = 'installing';
1445 }
1446 }
1447 }
1448
1449 return [
1450 'process_statuses' => $process_statuses,
1451 'option_statuses' => $option_statuses,
1452 ];
1453 }
1454
1455 }
1456