roles; if(in_array('administrator', $roles)) { $user_role = 1; } elseif(in_array('editor', $roles) or current_user_can('publish_posts')) { $user_role = 2; } else { $user_role = 0; } } define( 'WEBTOTEM_USER_ROLE', $user_role ); return true; } /** * Check whether the current site is working as a multi-site instance. * * @return bool * Either TRUE or FALSE in case WordPress is being used as a multi-site instance. */ public static function isMultiSite() { return (bool) ( (function_exists('is_multisite') && is_multisite()) || (defined('MULTISITE') && MULTISITE == true) ); } /** * Get user email. * * @return string * Returns user email. */ public static function getUserEmail() { $email = WebTotemOption::getOption( "user_email" ); if(!$email){ if(WebTotemOption::isActivated()) { // $email = WebTotemAPI::getEmail(); } WebTotemOption::setOptions(['user_email' => $email]); } return $email; } /** * Returns the md5 hash representing the content of a file. * * @param string $file * Relative path to the file. * @return string * Seven first characters in the hash of the file. */ public static function fileVersion($file = '') { return substr(md5_file(WEBTOTEM_PLUGIN_PATH . '/' . $file), 0, 7); } /** * Returns full path to image. * * @param string $image * Relative path to the file. * @return string * Full path to image. */ public static function getImagePath($image) { return WEBTOTEM_URL. '/includes/img/' . $image; } /** * Checking whether the current domain belongs to the kz domain zone. * * @return bool * true is returned if the domain belongs to the kz domain zone. */ public static function isKz() { $is_kz = json_decode(WebTotemOption::getOption('is_kz'), true); if(is_array($is_kz)){ return $is_kz['value']; } if(function_exists('idn_to_utf')){ $host = idn_to_utf8($_SERVER['HTTP_HOST']); } else { $host = $_SERVER['HTTP_HOST']; } $parts = explode('.', $host); $domain_zone = $parts[count($parts)-1]; if($domain_zone === 'kz' or $domain_zone === 'қаз'){ $is_kz['value'] = true; } else { $is_kz['value'] = false; } WebTotemOption::setOptions(['is_kz' => $is_kz]); return $is_kz['value']; } /** * Convert object to array. * * @param array $data * Array. * @return array * Returns array. */ public static function convertObjectToArray($data){ if(!is_array($data)) $data = (array)$data; array_walk_recursive($data, function(&$item){ if(is_object($item)) $item = (array)$item; }); return $data; } /** * Decodes a Base64URL-encoded string into a regular string. * * Base64URL is a URL-safe variant of Base64 commonly used in JWT and other web standards. * In Base64URL, the '+' and '/' characters are replaced with '-' and '_', and the '=' padding is often omitted. * This function converts Base64URL back to standard Base64 and then decodes it. * * @param string $data The input string encoded in Base64URL format. * * @return string|false Returns the decoded string, or false if decoding fails. */ public static function base64UrlDecode($data) { $remainder = strlen($data) % 4; if ($remainder) { $data .= str_repeat('=', 4 - $remainder); } return base64_decode(strtr($data, '-_', '+/')); } /** * Removing duplicates by one key. * * @param array $array * Array. * @param string $key * Delete duplicates with the same key. * * @return array * Returns array. */ public static function arrayUniqueKey($array, $key) { $tmp = $key_array = array(); $i = 0; foreach ($array as $val) { if (!in_array($val[$key], $key_array)) { $key_array[$i] = $val[$key]; $tmp[$i] = $val; } $i++; } return $tmp; } /** * Returns user IP address. * * @return string * Returns user IP address. */ public static function getUserIP() { $arr = [ 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_CF_CONNECTING_IP', 'REMOTE_ADDR' ]; foreach ($arr as $key){ if (array_key_exists($key, $_SERVER) === true) { foreach (explode(',', $_SERVER[$key]) as $ip) { $ip = trim($ip); $ip = filter_var($ip, FILTER_VALIDATE_IP); if (!empty($ip)) { return $ip; } } } } return false; } /** * Convert the file size to а human-readable format. * * @param string $bytes * File size in bytes. * @param string $decimals * The number of characters after the decimal point. * * @return string * Returns the file size in a human-readable format. */ public static function humanFilesize($bytes, $decimals = 2) { $factor = floor((strlen($bytes) - 1) / 3); $unit_of_measurement = ($factor > 0) ? substr("KMGT", $factor - 1, 1) : ''; $size = sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . $unit_of_measurement . 'B'; return str_replace(".00", "", $size); } /** * Check whether the file is publicly accessible. * * @param string $url * http link to the file. * @param string $path * The path to the file. * * @return bool */ public static function isPubliclyAccessible($url, $path) { $response = wp_remote_get($url); if ((int) floor(((int) wp_remote_retrieve_response_code($response) / 100)) === 2) { $handle = @fopen($path, 'r'); if ($handle) { $contents = fread($handle, 700); fclose($handle); $remoteContents = substr(wp_remote_retrieve_body($response), 0, 700); return $contents === $remoteContents; } } return false; } /** * Check that the training period has passed for the firewall. * * @return bool * Returns boolean. */ public static function isWafTraining() { $created_at = WebTotemOption::getOption('am_created_at'); if($created_at) { $when_waf_trained = strtotime('+2 day', strtotime($created_at)); $today = strtotime('today'); return ($when_waf_trained < $today) ? FALSE : TRUE; } return FALSE; } /** * Check if the data for the period is available. * * @param string $created_at * Date when the agent manager was created. * * @return array * Returns an array with periods. */ public static function isPeriodAvailable() { $created_at = WebTotemOption::getOption('am_created_at'); if(!$created_at){ return [ 'monthly' => false, 'yearly' => false, ]; } $diff = strtotime(gmdate("Y-m-d H:i:s")) - strtotime($created_at); $daysCount = floor($diff / 86400); return [ 'monthly' => $daysCount > 7, 'yearly' => $daysCount > 30, ]; } /** * Converting a date to the appropriate format. * * @param string $date * Date in any format. * @param string $format * The format to which you want to convert the date. * * @return string * Returns converted Date. * @throws Exception */ public static function dateFormatter($date, $format = 'M j, Y \/ H:i') { if (!$date) { return __('Unknown', 'wtotem'); } if ( is_numeric($date) && (int)$date == $date ){ $date = date('Y-m-d H:i', $date); } if($wp_timezone = wp_timezone()){ $UTC = new DateTimeZone("UTC"); $date = new DateTime( $date, $UTC ); $date->setTimezone( new DateTimeZone($wp_timezone->getName()) ); return date_i18n($format,strtotime($date->format('Y-m-d H:i'))); } $time_zone = WebTotemOption::getOption('time_zone_offset'); $user_time = ($time_zone) ? strtotime($time_zone . 'hours', strtotime($date)) : strtotime($date); return date_i18n($format, $user_time); } /** * Get theme mode data. * * @return array * Returns array with current theme data. */ public static function getThemeMode() { $theme_mode = WebTotemOption::getSessionOption('theme_mode'); return [ "is_dark_mode" => $theme_mode == 'dark' ? 'wtotem_theme—dark' : '', "dark_mode_checked" => $theme_mode == 'dark' ? 'checked' : '', ]; } /** * Get current user language. * * @return string * Returns current language in 2-letter abbreviations */ public static function getLanguage() { $current_language = substr(get_bloginfo('language'), 0,2); $language = (in_array($current_language,['ru','en','pl'])) ? $current_language : 'en' ; return $language; } /** * Converting a date to the appropriate format. * * @param string|array $days * Number of days or period to convert. * * @return array * Returns an array of two values "from" and "to" */ public static function getPeriod($days) { if (!$days) { $days = 30; } switch ($days) { case is_array($days): $to = $days[1] ?: $days[0]; $period = [ 'from' => date('Y-m-d 00:00:01', strtotime(self::formatDate($days[0]))), 'to' => date('Y-m-d 23:59:59', strtotime(self::formatDate($to))), ]; break; case $days <= 1: $period = [ 'from' => date('Y-m-d H:m:i', strtotime('-24 hours')), 'to' => date('Y-m-d H:m:i', time()), ]; break; default: $period = [ 'from' => date('Y-m-d H:m:i', time() - ($days * 86400)), 'to' => date('Y-m-d H:m:i'), ]; } return $period; } /** * Converting a date from "j M, Y" format to 'd-m-Y' format. * * @return string * Returns date in new format */ public static function formatDate($date){ $month = [ 'Jan' => 'Янв', 'Feb' => 'Фев', 'Mar' => 'Мар', 'Apr' => 'Апр', 'May' => 'Май', 'Jun' => 'Июн', 'Jul' => 'Июл', 'Aug' => 'Авг', 'Sep' => 'Сен', 'Oct' => 'Окт', 'Nov' => 'Ноя', 'Dec' => 'Дек', ]; foreach ($month as $key => $value) { if (strpos($date, $value)) { $date = str_replace($value, $key, $date); } } $pattern = '/^(\d{1,2})\s+([a-zA-Z]+),\s+(\d{4})$/'; if (preg_match($pattern, $date, $matches)) { $monthNumber = date_parse($matches[2])['month']; return sprintf('%02d-%02d-%04d', $matches[1], $monthNumber, $matches[3]); } return $date; } /** * Convert an array to a string with quotation marks. * * @param array $array * Data array. * * @return string * Array of data converted to string. */ public static function convertArrayToString($array) { if(empty($array)){ return ''; } return '"' . implode('","', $array) . '"'; } /** * Converting the response to a readable form. * * @param string $message * Message response from the API server to the request. * * @return string|bool * Returns a message. */ public static function messageForHuman($message) { $definition = $message; switch ($message) { case 'HOSTS_LIMIT_EXCEEDED': $definition = __('Limit of adding sites exceeded.', 'wtotem'); break; case 'USER_ALREADY_REGISTERED': $definition = __('A user with this email already exists.', 'wtotem'); break; case 'DUPLICATE_HOST': $definition = __('Duplicate host', 'wtotem'); break; case 'INVALID_DOMAIN_NAME': $definition = __('Invalid Domain Name', 'wtotem'); break; default: $definition = str_replace("_", " ", $definition); $definition = ucfirst(strtolower($definition)); } return $definition; } /** * Get the data associated with the status. * * @param string $status * Module or agent status. * * @return array * Returns an array with status data. */ public static function getStatusData($status) { $path = self::getImagePath(''); $status = ($status == "installed") ? 'working' : $status; switch ($status) { case 'clean': case 'up': case 'installed': case 'available': case 'enable': case 'working': case 'good': $status_data = [ 'class' => 'is--status--ok', 'image' => $path . 'check-mark.svg', 'icon' => $path . 'icon_success_status.svg', ]; break; case 'pending': $status_data = [ 'class' => 'is--status--pending', 'image' => $path . 'loading.svg', 'icon' => $path . 'alert-warning.svg', ]; break; case 'pause': case 'modified': $status_data = [ 'class' => 'is--status--pending', 'image' => $path . 'warning.svg', 'icon' => $path . 'alert-warning.svg', ]; break; case 'expired': case 'no_cert': case 'expires': case 'open_ports': case 'warning': case 'not_supported': case 'not_registered': $status_data = [ 'class' => 'is--status--warning', 'image' => $path . 'warning.svg', 'icon' => $path . 'alert-warning.svg', ]; break; case 'invalid': case 'revoked': case 'untrusted': case 'not_found': case 'wrong_host': case 'error': case 'down': case 'expires_today': case 'infected': case 'deface': case 'not_installed': $status_data = [ 'class' => 'is--status--error', 'image' => $path . 'warning.svg', 'icon' => $path . 'alert-warning.svg', ]; break; default: $status_data = [ 'class' => 'is--status--pending', 'image' => $path . 'warning.svg', 'icon' => $path . 'alert-warning.svg', ]; } $status_data['name'] = $status; $status_data['text'] = self::getStatusText($status); $status_data['tooltips'] = self::getTooltips($status); return $status_data; } /** * Get a readable status text. * * @param string $status * Module or agent status. * * @return string * Returns the status text in the current language. */ public static function getStatusText($status) { $statuses = [ 'warning' => __('Warning', 'wtotem'), 'error' => __('Error', 'wtotem'), 'success' => __('Success', 'wtotem'), 'info' => __('Info', 'wtotem'), 'invalid' => __('Invalid', 'wtotem'), 'ok' => __('Everything is OK', 'wtotem'), 'expired' => __('Expired', 'wtotem'), 'expires' => __('Expires', 'wtotem'), 'expires_today' => __('Expires today', 'wtotem'), 'missing' => __('Missing', 'wtotem'), 'active' => __('Active', 'wtotem'), 'inactive' => __('Inactive', 'wtotem'), 'pending' => __('Pending', 'wtotem'), 'pause' => __('Disabled', 'wtotem'), 'available' => __('Available', 'wtotem'), 'not_supported' => __('Not supported', 'wtotem'), 'not_registered' => __('Not registered', 'wtotem'), 'unsupported' => __('Unsupported', 'wtotem'), 'clean' => __('Clean', 'wtotem'), 'clear' => __('Clear', 'wtotem'), 'blacklisted' => __('Infected', 'wtotem'), 'miner_detected' => __('Infected', 'wtotem'), 'deface' => __('Deface', 'wtotem'), 'modified' => __('Modified', 'wtotem'), 'detected' => __('Detected', 'wtotem'), 'open_ports' => __('Open ports', 'wtotem'), 'blocked' => __('Blocked', 'wtotem'), 'connected' => __('Connected', 'wtotem'), 'attacks_detected' => __('Attacks detected', 'wtotem'), 'signature_found' => __('Signature found', 'wtotem'), 'file_changes' => __('File changes', 'wtotem'), 'no_cert' => __('No cert', 'wtotem'), 'down' => __('Down', 'wtotem'), 'up' => __('Up', 'wtotem'), 'infected' => __('Infected', 'wtotem'), 'not_installed' => __('Need to install', 'wtotem'), 'agent_not_available' => __('Agent not available', 'wtotem'), 'update_error' => __('Update error', 'wtotem'), 'session_error' => __('Session Error', 'wtotem'), 'internal_error' => __('Internal Error', 'wtotem'), 'installing' => __('Installing', 'wtotem'), 'installed' => __('Installed', 'wtotem'), 'working' => __('Working', 'wtotem'), "critical" => __('Critical', 'wtotem'), "deleted" => __('Deleted', 'wtotem'), "changed" => __('Changed', 'wtotem'), "new" => __('New', 'wtotem'), "scanned" => __('Scanned', 'wtotem'), "quarantine" => __('In quarantine', 'wtotem'), "good" => __('Good', 'wtotem'), "wrong_host" => __('Wrong host', 'wtotem'), "revoked" => __('Revoked', 'wtotem'), "untrusted" => __('Untrusted', 'wtotem'), "not_found" => __('Not found', 'wtotem'), "enable" => __('Enable', 'wtotem'), ]; return (array_key_exists($status, $statuses)) ? $statuses[$status] : $status; } /** * Get tooltips text for status. * * @param string $status * Module or agent status. * * @return string * Returns the status tooltip in the current language. */ public static function getTooltips($status) { $tooltips = [ 'invalid' => __('Invalid -The certificate is invalid. Please, make sure that relevant certificate details filled correctly.', 'wtotem'), 'expired' => __('Expired - The certificate has expired. Connection is not secure. Please, renew it.', 'wtotem'), 'expires' => __('Expires - The certificate expires soon. Please, take actions.', 'wtotem'), 'expires_today' => __('Expires today - The certificate expires today. Please, take actions.', 'wtotem'), 'error' => __("Error - Something went wrong. Please, contact us, we'll fix the problem.", 'wtotem'), 'pending' => __('Pending - System processes your website. Data will be available soon.', 'wtotem'), 'pause' => __('Pause - The module is paused.', 'wtotem'), 'clean' => __('Everything is OK - Nothing to worry about. Everything is alright.', 'wtotem'), 'deface' => __("Deface - Website hacked. Please, contact us, we'll fix the problem.", 'wtotem'), 'open_ports' => __('Open ports - Open ports detected. Your website is vulnerable to attacks.', 'wtotem'), 'blocked' => __('Blocked - The module is blocked due to billing issues.', 'wtotem'), 'no_cert' => __("No cert - You don't have SSL certificate. We recommend you to install it for security concerns.", 'wtotem'), 'down' => __('Down - The website is not available for visitors.', 'wtotem'), 'up' => __('Up - The website is available for visitors.', 'wtotem'), 'infected' => __('Infected - The website site is blacklisted and may have infected files. Please, check antivirus module.', 'wtotem'), 'installing' => __('It means that the agent installation is in progress. Usually, it takes up to one hour.', 'wtotem'), 'agent_not_available' => __('We cannot locate the agent right now.', 'wtotem'), 'update_error' => __('It seems that your agent failed to update due to permissions restrictions.', 'wtotem'), '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'), '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'), 'working' => __('Everything is alright.', 'wtotem'), 'installed' => __('Everything is alright.', 'wtotem'), 'available' => __('Everything is alright.', 'wtotem'), 'not_installed' => __('You need to install agent manager to activate antivirus and firewall.', 'wtotem'), 'enable' => __('Enabled — the module is active; information is being collected and updated.', 'wtotem'), ]; return (array_key_exists($status, $tooltips)) ? $tooltips[$status] : ''; } /** * Converting site data. * * @param array $data * Sites data from WebTotem. * * @return array * Converted data. */ public static function allSitesData($data) { $local_sites = get_sites(); $main_host = WebTotemOption::getMainHost(); $domains = []; foreach ($local_sites as $site){ $domain = untrailingslashit($site->domain . $site->path); $domains[$domain] = $domain; } $sites = []; if(array_key_exists('edges', $data)){ foreach ($data['edges'] as $site) { $site = $site['node']; // Take sites only from the multisite network. if(array_key_exists($site['hostname'], $domains)) { unset($domains[$site['hostname']]); $sites[] = [ 'hostname' => $site['hostname'], 'title' => $site['title'], 'main_host' => $main_host['id'] == $site['id'], 'host_id' => $site['id'], 'url' => admin_url('admin.php?page=wtotem_dashboard&hid=' . $site['id']), 'firewall' => [ 'status' => self::getStatusData($site['firewall']['status']), ], 'antivirus' => [ 'status' => self::getStatusData($site['antivirus']['status']), ], 'stacks' => self::getStacksData($site['maliciousScript']['stack']), 'services' => self::getSiteServicesData($site), ]; } } } return $sites; } /** * Converting stacks data. * * @param array $stacks * Stacks data from WebTotem. * * @return array * Converted data. */ protected static function getStacksData($stacks) { $apps = file_get_contents(WEBTOTEM_PLUGIN_PATH . '/includes/js/apps.json'); $apps = json_decode($apps, true); $path = 'https://assets.wtotem.net/images/apps/'; $defaultIcon = WEBTOTEM_URL . '/includes/img/defaultTechnologiesIcon.svg'; $stackList = array_slice($stacks, 0,3); $list = []; foreach ($stackList as $key => $stack){ $list[$key] = [ 'name' => $stack['name'], 'icon' => $path . ($apps[$stack['name']]['icon'] ?: $defaultIcon), ]; } if(count($stacks) <= 3){ $other['count'] = 0; $other['names'] = []; } else { $otherStacks = array_slice($stacks, 3); $other['count'] = count($otherStacks); foreach ($otherStacks as $stack){ $other['names'][] = $stack['name']; } } if($other['names']){ $other['names'] = implode(",", $other['names']); } return ['list' => $list, 'other' => $other]; } /** * Converting services data. * * @param $data * Site data from WebTotem. * * @return array * Converted data. */ protected static function getSiteServicesData($data) { $services = [ 'ssl' => 'ssl', 'availability' => 'wa', 'reputation' => 'rc', 'ports' => 'ps', 'deface' => 'dc', 'domain' => 'dec', ]; $list = []; $other['count'] = 0; $other['names'] = []; foreach ($services as $key => $service){ if(array_key_exists($key, $data) and is_array($data[$key]) and array_key_exists('status', $data[$key])){ $status = self::getServiceStatus($data[$key]['status']); if(in_array($status['color'], ['red', 'yellow'])){ if(count($list) < 2){ $color = $status['color'] == 'red' ? 'white/' : ''; $list[$key] = [ 'status' => $status, 'icon' => 'services/'. $color . $service . '.svg', 'name' => self::getServiceName( $service ), ]; } else { $other['names'][] = self::getServiceName( $service ); $other['count']++; } } } } if($other['names']){ $other['names'] = implode(",", $other['names']); } return ['list' => $list, 'other' => $other]; } /** * Get the data associated with the status. * * @param string $status * Module or agent status. * * @return array * Returns an array with status data. */ public static function getServiceStatus($status) { switch ($status) { case 'expired': case 'invalid': case 'error': case 'expires_today': case 'down': case 'infected': case 'deface': case 'not_installed': case 'quarantine': $status_data = [ 'color' => 'red', ]; break; case 'no_cert': case 'expires': case 'open_ports': case 'modified': case 'not_supported': case 'not_registered': case 'blocked': case 'pause': case 'internal_error': case 'update_error': case 'config_error': case 'agent_not_available': case 'session_error': $status_data = [ 'color' => 'yellow', ]; break; case 'clean': case 'installed': case 'up': case 'scanned': case 'working': $status_data = [ 'color' => 'green', ]; break; case 'deleted': $status_data = [ 'color' => 'black', ]; break; case 'installing': case 'pending': default: $status_data = [ 'color' => 'gray', ]; break; } return $status_data; } /** * Get the translation of service. * * @param $service * Service short name. * * @return string * Translation of service. */ public static function getServiceName($service){ $services = [ "wa" => __('Availability', 'wtotem'), "rc" => __('Reputation', 'wtotem'), "ssl" => 'SSL', "cms" => __('Technologies', 'wtotem'), "dc" => __('Deface', 'wtotem'), "ps" => __('Ports', 'wtotem'), "waf" => __('Firewall', 'wtotem'), "av" => __('Antivirus', 'wtotem'), "dec" => __('Domain', 'wtotem'), ]; return $services[$service]; } /** * Get reports with modules list. * * @param array $edges * Data on generated reports. * * @return array * Returns an array with converted data. */ public static function getReports(array $edges) { $modulesLang = [ 'wa' => __('Availability log', 'wtotem'), 'dc' => __('Deface log', 'wtotem'), 'ps' => __('Port log', 'wtotem'), 'rc' => __('Reputation log', 'wtotem'), 'sc' => __('Evaluation log', 'wtotem'), 'av' => __('Antivirus log', 'wtotem'), 'waf' => __('Firewall log', 'wtotem'), ]; $reports = []; foreach ($edges as $edge) { if (in_array(FALSE, $edge["node"])) { $arr = []; foreach ($edge["node"] as $module => $value) { if ($value == TRUE && array_key_exists($module, $modulesLang)) { $arr[] = $modulesLang[$module]; } } $modules = implode(", ", $arr); } else { $modules = __('All modules', 'wtotem'); } $reports[] = [ 'id' => $edge["node"]['id'], 'modules' => $modules, 'created_at' => self::dateFormatter($edge["node"]['createdAt']), ]; } return $reports; } /** * Get reputation status description. * * @param string $status * Reputation status. * * @return string * Returns a description of the reputation status */ public static function getReputationInfo($status) { switch ($status) { case 'clean': $data = __("Don't worry, your reputation is good", 'wtotem'); break; case 'infected': $data = __('Oh, your reputation is bad', 'wtotem'); break; default: $data = __('Information is being updated', 'wtotem'); } return $data; } /** * Get blacklists entries counts. * * @param string $status * Reputation status. * @param array $virus_list * Sources where the site can be blacklisted. * * @return int * Number of references in blacklists. */ public static function blacklistsEntries($status, array $virus_list) { $count = 0; if ($status != "clean") { foreach ($virus_list as &$list) { if (!empty($list['virus']['type'])) { $count++; } } } return $count; } /** * Classification of the rating in the letter grades. * * @param int $score * Site rating from 1 to 100. * * @return array * Returns an array of data. */ public static function scoreGrading($score) { if ($score < 0 || $score > 100) { return ['grade' => '', 'color' => '']; } $scores = [ 100 => 'A+', 90 => 'A', 80 => 'A-', 70 => 'B+', 60 => 'B', 50 => 'B-', 35 => 'C+', 20 => 'C', 0 => 'C-', ]; foreach ($scores as $key => $value) { if ($score >= $key) { $grade = $value; break; } } // Set a color depending on the grade. switch ($score) { case $score >= 80: $color = 'green'; break; case $score >= 50: $color = 'orange'; break; default: $color = 'red'; } return ['grade' => $grade, 'color' => $color]; } /** * Calculate the number of remaining days. * * @param string $date * Expiry date. * * @return string * Returns the number of days before the expiration date. */ public static function daysLeft($date) { if ((int) $date === 0) { $days_left = 0; } else { $now = new \DateTime(); $expiry_date = new \DateTime(); $timestamp = strtotime($date); $expiry_date->setTimestamp($timestamp); $days_left = $expiry_date->diff($now)->format("%a"); } return $days_left; } /** * Converting the firewall logs. * * @param array $logs_ * Firewall logs from WebTotem. * * @return array * Converted array of logs. */ public static function wafLogs(array $logs_) { $logs = []; foreach ($logs_ as $key => $log) { $logs[$key]['ip'] = $log['ip']; $logs[$key]['request'] = urldecode($log['request']); $logs[$key]['time'] = self::dateFormatter($log['date']); $logs[$key]['country_code'] = strtoupper($log['country']); $logs[$key]['country'] = self::getCountryName($log['country']); $logs[$key]['blocked'] = $log['blocked'] ? __('Blocked', 'wtotem') : __('Not blocked', 'wtotem'); $more = [ 'ip' => $log['ip'], 'proxy_ip' => $log['proxy_ip'], 'source' => $log['source'], 'request' => urldecode($log['request']), 'user_agent' => $log['user_agent'], 'time' => self::dateFormatter($log['date']), 'type' => $log['type'], 'category' => $log['signature_category'], 'country' => self::getCountryName($log['country']), 'payload' => urldecode($log['payload']), 'hostname' => urldecode($log['hostname']), 'is_trusted' => urldecode($log['is_trusted']), ]; $logs[$key]['more'] = json_encode($more); } return $logs; } /** * Converting firewall data to json for a D3 chart. * * @param array $charts * Charts data from WebTotem. * * @return array * Returns the converted data for chart. */ public static function generateWafChart(array $charts) { $sum = 0; foreach ($charts as $chart) { $sum += $chart['total_attacks']; } if ($sum == 0) { return [ 'chart' => FALSE, 'count_attacks' => 0, 'count_blocks' => 0, 'days' => 0, ]; } // Set variables. $count_attacks = $count_blocks = 0; foreach ($charts as $chart) { $result[$chart["type"]] = $chart["statistics"]; } if (!isset($result)) { return [ 'chart' => FALSE, 'count_attacks' => 0, 'count_blocks' => 0, 'days' => 0, ]; } return [ 'chart' => json_encode($result), 'count_attacks' => $count_attacks, 'count_blocks' => $count_blocks, ]; } /** * Converting data to json for a D3 chart. * * @param array $charts * Charts data from WebTotem. * @param int $days * The number of days to build the chart. * * @return bool|string * Returns the converted data for chart. */ public static function generateChart(array $charts, $days = 7) { $sum = 0; foreach ($charts as $chart) { $sum += $chart['value']; } if ($sum == 0) { return FALSE; } $result = []; foreach ($charts as $chart) { if ($days <= 1) { $time_zone = WebTotemOption::getOption('time_zone_offset'); $userTime = ($time_zone) ? strtotime($time_zone . 'hours', strtotime($chart['time'])) : strtotime($chart['time']); } $result[] = [ 'date' => ($days <= 1) ? date("Y-m-d H:00:00", $userTime) : date("Y-m-d", strtotime($chart['time'])), 'value' => $chart['value'], ]; } return json_encode($result, TRUE); } /** * Converting data to json for a D3 chart. * * @param array $data * Charts data from WebTotem. * * @return array|bool * Returns the converted data for chart. */ public static function generateAttacksMapChart(array $data) { $attacks = []; $countries = []; $labels = []; foreach ($data as $value) { $attacks[] = $value['total_attack']; $labels[] = self::getCountryName($value['name']); $countries[] = self::getCountryName($value['name'], 'en-US'); } $result = ['attacks' => $attacks, 'countries' => $countries, 'labels' => $labels]; if (!$attacks) { return FALSE; } return json_encode($result, TRUE); } /** * Reassembling the antivirus logs. * * @param array $logs_ * Antivirus logs from WebTotem. * * @return array * Reassembled array of logs. */ public static function getAntivirusLogsData(array $logs_) { if(!$logs_){ return []; } $logs = []; foreach ($logs_ as $key => $log) { if(isset($log['infectedNum']) and $log['infectedNum']){ $log['status_info'] = 'infected'; $log['status_name'] = __('Infected', 'wtotem'); } else { $log['status_info'] = 'clean'; $log['status_name'] = __('Clean', 'wtotem'); } $log['date'] = self::dateFormatter($log['date'], 'd M Y'); $log['data'] = json_encode($log); $logs[$key] = $log; } return $logs; } /** * Reassembling the Infected Files logs. * * @param array $logs_ * Antivirus logs from WebTotem. * * @return array * Reassembled array of Infected Files logs. */ public static function getInfectedFilesData(array $logs_) { $logs = []; foreach ($logs_ as $key => $log) { $full_path = urldecode($log['name']); $log['file_name'] = $name = basename($full_path); $log['file_path'] = str_replace($name, "", $full_path); $log['id'] = $key; $logs[$key] = $log; } return $logs; } /** * Reassembling the antivirus logs. * * @param array $logs_ * Antivirus logs from WebTotem. * * @return array * Reassembled array of logs. */ public static function getAntivirusAlerts(array $logs_) { $logs = []; foreach ($logs_ as $key => $log) { switch ($log['type']) { case 'file': $log['type_text'] = "Suspicious file"; break; case 'skippedPaths': $log['type_text'] = "Unscanned Paths"; break; default: $log['type_text'] = $log['type']; } $logs[$key] = $log; } return $logs; } /** * Get open path data. * * @param array $_ports * Open ports array. * * @return array * Reassembled array of open ports. */ public static function getOpenPortsData($ports) { if(!$ports){ return []; } foreach ($ports as $key => $port) { $summary = ''; if($port['cves']){ foreach ($port['cves'] as $item){ $summary .= '

' . $item['summary'] . '

'; } } $ports[$key]['cve_summary'] = $summary; } return $ports; } // /** // * Reassembling the quarantine logs. // * // * @param array $logs_ // * Quarantine logs from WebTotem. // * // * @return array // * Reassembled array of logs. // */ // public static function getQuarantineLogs(array $logs_) { // $logs = []; // foreach ($logs_ as $key => $log) { // $logs[$key] = $log; // $logs[$key]['path'] = urldecode($log['name']); // $logs[$key]['date'] = self::dateFormatter($log['date']); // } // // return $logs; // } /** * Reassembling the Quarantine logs. * * @param array $list_ * Quarantine path list from WebTotem. * * @return array * Reassembled array of Quarantine logs. */ public static function getQuarantineListData(array $list_) { if(!$list_){ return []; } $list = []; foreach ($list_ as $key => $log) { $full_path = urldecode($log['name']); $log['path'] = $full_path; $log['file_name'] = $name = basename($full_path); $log['file_path'] = str_replace($name, "", $full_path); $log['id'] = $key; $list[$key] = $log; } return $list; } /** * Generate an array of IP address data. * * @param array $data * IP addresses data from WebTotem. * @param string $list_name * Allow or deny list. * * @return array * Returns array of data. */ public static function getIpList(array $data, $list_name) { $list = []; foreach ($data as $item) { $list[] = [ 'ip' => $item, 'list_name' => $list_name, ]; } return $list; } /** * Convert IP list to be transferred to WebTotem. * * @param string $data * IP list. * * @return string * Returns the converted string. */ public static function convertIpListForApi($data) { if (!$data) { return FALSE; } return preg_split("/(?(?=[\s,])[^.]|^$)/", $data); } /** * Validate Ip or Cidr. * * @param string $input * IP address. (IPv4 / IPv6 + CIDR) * * @return bool * Returns the converted string. */ public static function validateIpOrCidr(string $input): bool { if (strpos($input, '/') !== false) { [$ip, $mask] = explode('/', $input, 2); if (!filter_var($ip, FILTER_VALIDATE_IP)) { return false; } if (!ctype_digit($mask)) { return false; } $mask = (int)$mask; if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { return $mask >= 0 && $mask <= 32; } if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { return $mask >= 0 && $mask <= 128; } return false; } return filter_var($input, FILTER_VALIDATE_IP) !== false; } /** * Get data of the country with the most attacks. * * @param array $map * Map logs from WebTotem. * * @return array * Returns array of data. */ public static function getMostAttacksData($map) { if ($map) { $most_attacks_key = array_search(max(array_column($map, 'total_attack')), array_column($map, 'total_attack')); $total_attacks = array_sum(array_column($map, 'total_attack')); $data['percent'] = ($total_attacks) ? round($map[$most_attacks_key]['total_attack'] / $total_attacks * 100) : 0; $data['country'] = self::getCountryName($map[$most_attacks_key]['name']); $data['offset'] = 176 / 100 * (100 - $data['percent']); return $data; } return ['percent' => 0, 'country' => FALSE, 'offset' => 0]; } /** * Get data on the three most attacking countries. * * @param array $map * Map logs from WebTotem. * * @return array * Returns array of data. */ public static function getTreeMostAttacksData($map) { $total_attacks = array_sum(array_column($map, 'attacks')); if ($map) { array_multisort (array_column($map, 'attacks'), SORT_DESC, $map); $data = array_slice($map, 0, 3); foreach ($data as $key => $value){ $data[$key]['percent'] = round($value['attacks'] / $total_attacks * 100); $data[$key]['country'] = self::getCountryName($value['country']); } return $data; } return []; } /** * Getting the country name by two-letter code. * * @param string $key * Two-letter code. * * @return string * Returns country name. */ public static function getCountryName($key, $lang = false) { if($lang == 'en-US'){ $countries = WebTotemCountryManager::getCountiesNameByCode(); } else { $countries = WebTotemCountryManager::getStandardList(); } $key = (string) $key; return (array_key_exists($key, $countries)) ? $countries[$key] : $key; } /** * Get configs data. * * @param array $array * Original array. * @param string $key * The key to use as an index. * * @return array * Configs data array. */ public static function getConfigsData(array $array, $key) { $configs = self::arrayMapIndex($array, $key); foreach ($configs as $service => $config){ $configs[$service]['checked'] = ($config['isActive']) ? 'checked' : ''; $configs[$service]['notification_checked'] = (isset($config['notifications']) && $config['notifications']) ? 'checked' : ''; } return $configs; } /** * Get waf setting data. * * @param array $settings * Original array. * * @return array * Configs data array. */ public static function getWafSettingData(array $settings) { $_settings['gdn']['checked'] = (isset($settings['global_defense_network']) && !$settings['global_defense_network']) ? '' : 'checked'; $_settings['dos'] = [ 'checked' => (isset($settings['dos_protect']) && !$settings['dos_protect']) ? '' : 'checked', 'visually' => (isset($settings['dos_protect']) && !$settings['dos_protect']) ? 'visually-hidden' : '', ]; $_settings['dos_limit'] = $settings['dos_limit'] ?: 1000; $_settings['login_attempt'] = [ 'checked' => (isset($settings['login_protect']) && !$settings['login_protect']) ? '' : 'checked', 'visually' => (isset($settings['login_protect']) && !$settings['login_protect']) ? 'visually-hidden' : '', ]; $_settings['login_attempt_limit'] = $settings['login_attempt_limit'] ?: 20; return $_settings; } /** * Get plugin settings data. * * @return array * Configs data array. */ public static function getPluginSettingsData() { $settings = WebTotemOption::getPluginSettings(); $_settings = $settings; $_settings['hide_wp_version_checked'] = (array_key_exists('hide_wp_version', $settings) and $settings['hide_wp_version']) ? 'checked' : ''; $_settings['disable_user_enumeration_checked'] = (array_key_exists('disable_user_enumeration', $settings) and $settings['disable_user_enumeration']) ? 'checked' : ''; $_settings['recaptcha_checked'] = (array_key_exists('recaptcha', $settings) and $settings['recaptcha']) ? 'checked' : ''; $_settings['two_factor_checked'] = (array_key_exists('two_factor', $settings) and $settings['two_factor']) ? 'checked' : ''; return $_settings; } /** * Replace array indexes by key. * * @param array $array * Original array. * @param string $key * The key to use as an index. * * @return array * Returns a new array. */ public static function arrayMapIndex(array $array, $key) { $new_array = []; foreach ($array as $item) { if (array_key_exists($key, $item)) { $new_array[$item[$key]] = $item; } } return $new_array; } /** * Generate random string. * * @param int $length * The required length of the string. * * @return string * Returns random string. */ public static function generateRandomString( int $length = 10): string { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-'; $charactersLength = strlen($characters); $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, $charactersLength - 1)]; } return $randomString; } /** * Encodes the less than, greater than, ampersand,double quote * and single quote characters. Will never double encode entities. * * @see https://developer.wordpress.org/reference/functions/esc_attr/ * * @param string $text * The text which is to be encoded. * * @return string * The encoded text with HTML entities. */ public static function escape($text = '') { return esc_attr($text); } /** * Throw generic exception. * * @throws Exception * * @param string $message * Error or information message. * @param string $type * Either info or error. * * @return bool * False all the time, used for debug. */ public static function throwException($message, $type = 'error') { if (defined('WTOTEM_THROW_EXCEPTIONS') && WTOTEM_THROW_EXCEPTIONS === true && is_string($message) ) { $message = str_replace( 'WebTotem:', ($type === 'error' ? __('Error:', 'wtotem') : __('Info:', 'wtotem')), $message ); throw new Exception($message, $type === 'error' ? 157 : 333); } return false; } /** * Get audit logs data * * @return array */ public static function getAuditLogs($data, $dates_count) { $logs = []; foreach ($data as $datum){ $date_time = strtotime($datum['created_at']); $date = self::dateFormatter($date_time, 'M j, Y'); $logs[$date]['date'] = $date; $logs[$date]['count'] = $dates_count[$date]; $logs[$date]['logs'][] = [ 'time' => self::dateFormatter($date_time,'H:i'), 'user_name' => $datum['user_name'], 'status' => $datum['status'], 'title' => $datum['title'], 'event' => $datum['event'], 'description' => $datum['description'], 'ip' => $datum['ip'], 'viewed' => (int) !$datum['viewed'] ]; } return $logs; } /** * Update user's plugins cve data * * @return void */ public static function updateCveData() { require_once ABSPATH . 'wp-admin/includes/plugin.php'; $all_plugins = get_plugins(); $list = []; foreach ($all_plugins as $plugin) { if($plugin['TextDomain'] and $plugin['Version']){ $list[] = '{"technology": "' . $plugin['TextDomain'] . '", "version": "' . $plugin['Version'] . '"}'; } } $list = implode(', ', $list ?? []); $response = WebTotemAPI::getCVE($list); $cve_list = $response ? WebTotem::arrayMapIndex($response, 'technology') : []; $update_plugins = get_site_transient( 'update_plugins' ); $update_plugins = WebTotem::convertObjectToArray($update_plugins->response); $values = ''; WebTotemDB::deleteData([], 'plugins_cve_list'); foreach ($all_plugins as $key => $plugin) { if(array_key_exists($plugin['TextDomain'], $cve_list)){ $new_version = $update_plugins[$key]['new_version'] ?? 0; $cves = $cve_list[$plugin['TextDomain']]['cves']; if ($cves){ foreach ($cves as $cve){ $cve['published'] = self::dateFormatter($cve['published'], 'Y-m-d'); $cve['summary'] = str_replace("'", "", $cve['summary']); $values .= sprintf("('%s','%s','%s','%s','%s','%s'),", $cve['cve_id'], $plugin['Name'], $plugin['TextDomain'], $plugin['Version'], $new_version, json_encode($cve) ); } } } } if($values){ $values = substr_replace($values, ";", -1); $columns = '(cve_id, plugin_name, slug, plugin_version, new_version, cve_data )'; WebTotemDB::setRows('plugins_cve_list', $columns, $values); } } public static function getPluginVersionFromRepository($slug) { $url = "https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slugs][]={$slug}"; $response = wp_remote_get($url); // WPOrg API call $plugins = json_decode($response['body']); // traverse $response object foreach($plugins as $key => $plugin) { $version = $plugin->version; } return $version; } /** * Update user's plugins cve data * * @return bool */ public static function updateCveDataByPluginName($plugin_data) { if(!$plugin_data['TextDomain'] or !$plugin_data['Version']){ return false; } $list = WebTotemAPI::getCVE('{"technology": "' . $plugin_data['TextDomain'] . '", "version": "' . $plugin_data['Version'] . '"}'); $cve_list = WebTotem::arrayMapIndex($list, 'technology'); $values = ''; WebTotemDB::deleteData(['slug' => $plugin_data['TextDomain']], 'plugins_cve_list'); if(array_key_exists($plugin_data['TextDomain'], $cve_list) and $cve_list[$plugin_data['TextDomain']]['cves']){ $has_new_version = WebTotem::getPluginVersionFromRepository($plugin_data['TextDomain']); foreach ($cve_list[$plugin_data['TextDomain']]['cves'] as $cve){ $cve['published'] = self::dateFormatter($cve['published'], 'Y-m-d'); $values .= sprintf("('%s','%s','%s','%s','%s','%s'),", $cve['cve_id'], $plugin_data['Name'], $plugin_data['TextDomain'], $plugin_data['Version'], ($has_new_version and $has_new_version != $plugin_data['Version']) ? $has_new_version : 0, json_encode($cve) ); } $values = substr_replace($values, ";", -1); $columns = '(cve_id, plugin_name, slug, plugin_version, new_version, cve_data)'; WebTotemDB::setRows('plugins_cve_list', $columns, $values); } return true; } public static function get_plugin_info($plugin_slug) { include_once(ABSPATH . 'wp-admin/includes/plugin.php'); $all_plugins = get_plugins(); $plugin_file = "$plugin_slug/$plugin_slug.php"; if (isset($all_plugins[$plugin_file])) { $plugin_info = $all_plugins[$plugin_file]; return $plugin_info; } else { return false; } } /** * Get confidential files data * * @return array */ public static function preparePluginsCveList($data) { foreach ($data as $key => $datum){ $data[$key]['cve_data'] = json_decode($datum['cve_data'], true); $data[$key]['cve_data']['published'] = self::dateFormatter($data[$key]['cve_data']['published'], 'M j, Y'); } return $data; } /** * Get confidential files data * * @return array */ public static function getConfidentialFiles($data) { foreach ($data as $key => $datum){ $data[$key]['modified_at'] = date_i18n('M j, Y \/ H:i', strtotime($datum['modified_at'])); $data[$key]['size'] = self::humanFilesize($datum['size']); $data[$key]['name'] = urldecode($datum['name']); $data[$key]['path'] = urldecode($datum['path']); } return $data; } /** * Get confidential files data * * @return array */ public static function prepareLinksData($data) { foreach ($data as $key => $datum){ $content = $datum['content']; $source = $datum['source']; if(strpos($content, 'http://') !== 0 and strpos($content, 'https://') !== 0 and strpos($content, '//') !== 0){ $match = substr_count($content, '../'); $content = str_replace("../", "", $content); $content = ltrim($content, '/'); for($i=0; $i < 1+$match; $i++){ $source = substr($source, 0, strrpos($source, "/")); } $data[$key]['link'] = $source . '/' . $content; } else { $data[$key]['link'] = $content; } } return $data; } /** * Building navigation and forming a template * * @param integer $limit * number of entries per 1 page * @param integer $count_all * total number of all entries * @param integer $currentPage * the number of the page being viewed * @param integer $nextPrev * Show the "Forward" and "Back" buttons * @return mixed * Generated navigation template ready for output */ public static function paginationBuild($limit, $count_all, $currentPage = 1, $nextPrev = true) { if( $limit < 1 OR $count_all <= $limit ) return ''; $count_pages = ceil( $count_all / $limit ); $spread = 3; $separator = "..."; $wrap = "
{pages}
"; $nextTitle = '←'; $prevTitle = '→'; $currentPage = intval( $currentPage ); if( $currentPage < 1 ) $currentPage = 1; $shift_start = max( $currentPage - $spread, 2 ); $shift_end = min( $currentPage + $spread, $count_pages-1 ); if( $shift_end < $spread * 2 ) { $shift_end = min( $spread * 2, $count_pages-1 ); } if( $shift_end == $count_pages - 1 AND $shift_start > 3 ) { $shift_start = max( 3, min( $count_pages - $spread * 2 + 1, $shift_start ) ); } $list = self::getPaginationItem( 1, $currentPage ); if ($shift_start == 3) { $list .= self::getPaginationItem( 2, $currentPage ); } elseif ( $shift_start > 3 ) { $list .= $separator; } for( $i = $shift_start; $i <= $shift_end; $i++ ) { $list .= self::getPaginationItem( $i, $currentPage ); } $last_page = $count_pages - 1; if( $shift_end == $last_page-1 ){ $list .= self::getPaginationItem( $last_page, $currentPage ); } elseif( $shift_end < $last_page ) { $list .= $separator; } $list .= self::getPaginationItem( $count_pages, $currentPage ); if( $nextPrev ) { $list = self::getPaginationItem( $currentPage > 1 ? $currentPage - 1 : 0, $currentPage, $nextTitle) . $list . self::getPaginationItem( $currentPage < $count_pages ? $currentPage + 1 : 0, $currentPage, $prevTitle ); } return str_replace( "{pages}", $list, $wrap ); } /** * Button/Link Formation * @param int $page_num * page number * @param string $currentPage * current page * @param string $page_name * if specified, the text will be displayed instead of the page number * @return string * span block with active page or link. */ public static function getPaginationItem( $page_num, $currentPage, $page_name = '' ) { if($page_num === 0){return '';} $page_name = $page_name ?: $page_num; if( $currentPage == $page_num ) { return "{$page_name}"; } else { return "{$page_name}"; } } /** * Get notifications array. * * @return array * Returns notifications array. */ public static function getNotifications() { $notifications_data = WebTotemOption::getNotificationsData(); $notifications = []; foreach ($notifications_data as $notification) { switch ($notification['type']) { case 'error': $image = 'alert-error.svg'; $class = 'wtotem_alert__title_red'; break; case 'warning': $image = 'alert-warning.svg'; $class = 'wtotem_alert__title_yellow'; break; case 'success': $image = 'alert-success.svg'; $class = 'wtotem_alert__title_green'; break; case 'info': $image = 'info-blue.svg'; $class = 'wtotem_alert__title_blue'; break; } $notifications[] = [ "text" => $notification['notice'], "id" => self::generateRandomString(8), "type" => self::getStatusText($notification['type']), "type_raw" => $notification['type'], "image" => $image, "class" => $class, ]; } return $notifications; } /** * Get current agent installation statuses. * * @param array $agents_statuses * Agents statuses got from the WebTotem API. * * @return array * Returns an array with agent installation status data. */ public static function getAgentsStatuses(array $agents_statuses) { $agents = ['am', 'waf', 'av']; $installing_statuses = [ 'not_available', 'internal_error', 'update_error', 'config_error', 'session_error', ]; $process_statuses = []; $option_statuses = []; foreach ($agents as $agent) { $status = WebTotemAgentManager::checkInstalledService($agent); $option_statuses[$agent] = $status['option_status'] ?: FALSE; if ($agent == 'am') { if ($status['file_status']) { $process_statuses[$agent] = 'available'; } else { $process_statuses[$agent] = 'failed'; } } else { if ($status['file_status']) { if (in_array($agents_statuses[$agent], $installing_statuses)) { $process_statuses[$agent] = 'installing'; } elseif ($agents_statuses[$agent] == 'not_available') { $process_statuses[$agent] = 'failed'; } else { $process_statuses[$agent] = 'available'; } } else { $process_statuses[$agent] = 'installing'; } } } return [ 'process_statuses' => $process_statuses, 'option_statuses' => $option_statuses, ]; } }