PluginProbe
WebTotem Security / 3.0.0
WebTotem Security v3.0.0
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 3.0.0, at lib/Helper.php

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