OS : Linux
PHP Version : 7.4.33
Software : Apache/2.4.6 (CentOS) PHP/7.4.33
Information System : Linux apprendre2 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64
Disable Function : 0, // 0
'filter_user' => 0, // 1
'begin' => 0, // 2 - IP
'end' => 0, // 3 - IP
'filter_ip' => 0, // 4 - IP
'filter_login' => 0, // 5
'search_activity' => 0, // 6
'filter_role' => 0, // 7
'user_ids' => 0, // 8
// @since 8.9.6
'search_url' => 0, // 9
'filter_status' => 0, // 10
// Non-query alert settings, see CRB_NON_ALERT_PARAMS
'al_limit' => 0, // 11
'al_count' => 0, // 12
'al_expires' => 0, // 13
'al_ignore_rate' => 0, // 14
// @since 8.9.7
'al_send_emails' => 0, // 15
'al_send_pushbullet' => 0, // 16
);
const CRB_NON_ALERT_PARAMS = array(
'al_limit',
'al_count',
'al_expires',
'al_ignore_rate',
'al_send_emails',
'al_send_pushbullet'
);
const CRB_ALERTZ = '_cerber_subs';
// Known alert channels
const CRB_CHANNELS = array( 'email' => 1, 'pushbullet' => 1 );
const CRB_EV_LIN = 5;
const CRB_EV_LFL = 7;
const CRB_EV_PUR = 50;
const CRB_EV_LDN = 53;
const CRB_EV_PRS = 21;
const CRB_EV_UST = 22;
const CRB_EV_PRD = 25;
const CRB_STS_25 = 25;
const CRB_STS_29 = 29;
const CRB_STS_30 = 30;
const CRB_STS_11 = 11;
const CRB_STS_51 = 51;
const CRB_STS_52 = 52;
const CRB_STS_532 = 532;
/**
* Known WP scripts
* @since 6.0
*
*/
function cerber_get_wp_scripts() {
$scripts = array( WP_LOGIN_SCRIPT, WP_REG_URI, WP_XMLRPC_SCRIPT, WP_TRACKBACK_SCRIPT, WP_PING_SCRIPT, WP_SIGNUP_SCRIPT );
if ( ! cerber_is_custom_comment() ) {
$scripts[] = WP_COMMENT_SCRIPT;
}
return array_map( function ( $e ) {
return '/' . $e;
}, $scripts );
}
/**
* Returns WP Cerber nonce
*
* @param string $action
*
* @return false|string
*
* @since 8.9.5.3
*/
function crb_create_nonce( $action = 'control' ) {
static $cache = array();
if ( ! isset( $cache[ $action ] ) ) {
if ( ! function_exists( 'wp_create_nonce' ) ) {
cerber_load_wp_constants();
require_once( ABSPATH . WPINC . '/pluggable.php' );
}
$cache[ $action ] = wp_create_nonce( $action );
}
return $cache[ $action ];
}
/**
* Returns Site URL + /wp-admin/admin.php
*
* @return string
*
* @since 8.9.5.3
*/
function crb_get_admin_base(){
static $adm_base;
if ( ! isset( $adm_base ) ) {
if ( nexus_is_valid_request() ) {
$base = nexus_request_data()->base;
}
else {
$base = ( ! is_multisite() ) ? admin_url() : network_admin_url();
}
$adm_base = rtrim( $base, '/' ) . '/admin.php';
}
return $adm_base;
}
/**
* Return a link (full URL) to a Cerber admin page.
* Add a particular tab and GET parameters if they are specified
*
* @param string $tab Tab on the page
* @param array $args GET arguments to add to the URL
* @param bool $add_nonce If true, adds the nonce
* @param bool $encode
*
* @return string Full URL
*/
function cerber_admin_link( $tab = '', $args = array(), $add_nonce = false, $encode = true ) {
$page = 'cerber-security';
if ( empty( $args['page'] ) ) {
// TODO: look up the page in tabs config
//$config = cerber_get_admin_page_config();
if ( in_array( $tab, array( 'antispam', 'captcha' ) ) ) {
$page = 'cerber-recaptcha';
}
elseif ( in_array( $tab, array( 'imex', 'diagnostic', 'license', 'diag-log', 'change-log' ) ) ) {
$page = 'cerber-tools';
}
elseif ( in_array( $tab, array( 'traffic', 'ti_settings' ) ) ) {
$page = 'cerber-traffic';
}
elseif ( in_array( $tab, array( 'user_shield', 'opt_shield' ) ) ) {
$page = 'cerber-shield';
}
elseif ( in_array( $tab, array( 'geo' ) ) ) {
$page = 'cerber-rules';
}
elseif ( in_array( $tab, array( 'role_policies', 'global_policies' ) ) ) {
$page = 'cerber-users';
}
else {
if ( list( $prefix ) = explode( '_', $tab, 2 ) ) {
if ( $prefix == 'scan' ) {
$page = 'cerber-integrity';
}
elseif ( $prefix == 'nexus' ) {
$page = 'cerber-nexus';
}
}
}
}
else {
$page = $args['page'];
unset( $args['page'] );
}
$link = crb_get_admin_base() . '?page=' . $page;
$amp = ( $encode ) ? '&' : '&';
if ( $tab ) {
$link .= $amp . 'tab=' . preg_replace( '/[^\w\-]+/', '', $tab );
}
if ( $args ) {
foreach ( $args as $arg => $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $val ) {
$link .= $amp . $arg . '[]=' . urlencode( $val );
}
}
else {
$link .= $amp . $arg . '=' . urlencode( $value );
}
}
}
if ( $add_nonce ) {
$link .= $amp . 'cerber_nonce=' . crb_create_nonce();
}
return $link;
}
/**
* Return modified link to the currently displaying page
*
* @param array $args Arguments to add to the link to the currently displayed page
* @param bool $preserve Save GET parameters of the current request other than 'page' and 'tab'
* @param bool $add_nonce Add Cerber's nonce
*
* @return string
*/
function cerber_admin_link_add( $args = array(), $preserve = false, $add_nonce = true ) {
$link = cerber_admin_link( crb_admin_get_tab(), array( 'page' => crb_admin_get_page() ), $add_nonce );
if ( $preserve ) {
$get = crb_get_query_params();
unset( $get['page'], $get['tab'] );
}
else {
$get = array();
}
if ( $args ) {
$get = array_merge( $get, $args );
}
if ( $get ) {
foreach ( $get as $arg => $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $key => $val ) {
$link .= '&' . $arg . '[' . $key . ']=' . urlencode( $val );
}
}
else {
$link .= '&' . $arg . '=' . urlencode( $value );
}
}
}
return esc_url( $link );
}
/**
* @param array $set
*
* @return string
*/
function cerber_activity_link( $set = array() ) {
static $link;
if ( ! $link ) {
$link = cerber_admin_link( 'activity' );
}
$filter = '';
$c = count( $set );
if ( 1 == $c ) {
$filter .= '&filter_activity=' . absint( array_shift( $set ) );
}
elseif ( $c ) {
foreach ( $set as $key => $item ) {
$filter .= '&filter_activity[' . $key . ']=' . absint( $item );
}
}
return $link . $filter;
}
function cerber_traffic_link( $set = array(), $format = 1 ) {
$ret = cerber_admin_link( 'traffic', $set );
if ( $format ) {
$class = ( $format == 1 ) ? 'class="crb-button-tiny"' : '';
$ret = '' . __( 'Check for requests', 'wp-cerber' ) . '';
}
return $ret;
}
function cerber_get_login_url() {
$ret = '';
if ( $path = crb_get_settings( 'loginpath' ) ) {
$ret = cerber_get_home_url() . '/' . $path . '/';
}
return $ret;
}
/**
* @return array
* @since 8.6.6.1
*
*/
function crb_parse_site_url() {
static $result;
if ( isset( $result ) ) {
return $result;
}
$site_url = cerber_get_site_url();
$p1 = strpos( $site_url, '//' );
$p2 = strpos( $site_url, '/', $p1 + 2 );
if ( $p2 !== false ) {
$site_root = substr( $site_url, 0, $p2 );
$sub_folder = substr( $site_url, $p2 );
}
else {
$site_root = $site_url;
$sub_folder = '';
}
$result = array( $site_root, $sub_folder );
return $result;
}
/**
* Always includes the path to the current WP installation
*
* @return string
* @since 7.9.4
*
*/
function cerber_get_site_url() {
static $url;
if ( isset( $url ) ) {
return $url;
}
$url = trim( get_site_url(), '/' );
return $url;
}
/**
* Might NOT include the path to the current WP installation in some cases
* See: https://wordpress.org/support/article/giving-wordpress-its-own-directory/
*
* @return string
* @since 7.9.4
*
*/
function cerber_get_home_url() {
static $url;
if ( ! isset( $url ) ) {
$url = trim( get_home_url(), '/' );
}
return $url;
}
/**
* For non-HTML cases. Not suitable for HTML rendering.
*
* @return string
*/
function crb_get_blogname_decoded() {
return wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
}
function cerber_calculate_kpi( $period = 1 ) {
$period = absint( $period );
if ( ! $period ) {
$period = 1;
}
// TODO: Add spam performance as percentage Denied / Allowed comments
return array(
array(
__( 'Malicious activities mitigated', 'wp-cerber' ) . '',
cerber_count_log( crb_get_activity_set( 'malicious' ), $period )
),
array( __( 'Spam comments denied', 'wp-cerber' ), cerber_count_log( array( 16 ), $period ) ),
array( __( 'Spam form submissions denied', 'wp-cerber' ), cerber_count_log( array( 17 ), $period ) ),
array( __( 'Malicious IP addresses detected', 'wp-cerber' ), cerber_count_log( crb_get_activity_set( 'malicious' ), $period, 'ip', true ) ),
array( __( 'Lockouts occurred', 'wp-cerber' ), cerber_count_log( array( 10, 11 ), $period ) ),
);
}
/**
* The list devices available to send notifications
*
* @param string $token
*
* @return array|false
*/
function cerber_pb_get_devices( $token = '' ) {
cerber_delete_set( 'pushbullet_name' );
if ( ! $token ) {
if ( ! $token = crb_get_settings( 'pbtoken' ) ) {
return false;
}
}
$ret = array();
$curl = @curl_init();
if ( ! $curl ) {
return false;
}
$headers = array(
'Authorization: Bearer ' . $token
);
curl_setopt_array( $curl, array(
CURLOPT_URL => 'https://api.pushbullet.com/v2/devices',
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 2,
CURLOPT_TIMEOUT => 4, // including CURLOPT_CONNECTTIMEOUT
CURLOPT_DNS_CACHE_TIMEOUT => 4 * 3600,
) );
$result = @curl_exec( $curl );
$curl_error = curl_error( $curl );
curl_close( $curl );
$response = json_decode( $result, true );
if ( JSON_ERROR_NONE == json_last_error() && isset( $response['devices'] ) ) {
foreach ( $response['devices'] as $device ) {
$ret[ $device['iden'] ] = $device['nickname'];
}
}
else {
if ( $response['error'] ) {
$e = 'Pushbullet ' . $response['error']['message'];
}
elseif ( $curl_error ) {
$e = $curl_error;
}
else {
$e = 'Unknown cURL error';
}
cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' ' . $e );
}
return $ret;
}
/**
* Send push message via Pushbullet
*
* @param string $title
* @param string $text
* @param string $more Additional text (links)
* @param string $footer
*
* @return bool|WP_Error True on success
*/
function cerber_pb_send( $title, $text, $more = '', $footer = '' ) {
if ( ! $text ) {
return false;
}
if ( ! $token = crb_get_settings( 'pbtoken' ) ) {
return false;
}
$body = $text;
if ( $format = crb_get_settings( 'pb_format' ) ) {
if ( $format == 1 ) {
$body .= $more;
}
}
else {
$body .= $more . $footer;
}
$params = array( 'type' => 'note', 'title' => $title, 'body' => $body, 'sender_name' => 'WP Cerber' );
if ( $device = crb_get_settings( 'pbdevice' ) ) {
if ( $device != 'all' && $device != 'N' ) {
$params['device_iden'] = $device;
}
}
$headers = array( 'Access-Token: ' . $token, 'Content-Type: application/json' );
$curl = @curl_init();
if ( ! $curl ) {
return false;
}
curl_setopt_array( $curl, array(
CURLOPT_URL => 'https://api.pushbullet.com/v2/pushes',
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => json_encode( $params ),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 2,
CURLOPT_TIMEOUT => 4, // including CURLOPT_CONNECTTIMEOUT
CURLOPT_DNS_CACHE_TIMEOUT => 4 * 3600,
) );
$result = @curl_exec( $curl );
if ( $curl_error = curl_error( $curl ) ) {
$ret = new WP_Error( 'cerber_pb_error', $curl_error );
}
else {
$ret = true;
}
curl_close( $curl );
return $ret;
}
/**
* Returns the name of the selected Pushbullet device
*
* @return string Sanitized name of the device retrieved from Pushbullet
*
* @since 8.9.5.3
*/
function cerber_pb_get_active() {
if ( false !== $name = cerber_get_set( 'pushbullet_name', null, false ) ) {
return $name;
}
// Updating the cache
$device = crb_get_settings( 'pbdevice' );
$name = '';
if ( $device == 'all' ) {
$name = 'all connected devices';
}
elseif ( $device && $list = cerber_pb_get_devices() ) {
$name = htmlspecialchars( $list[ $device ] ) ?? '';
}
cerber_update_set( 'pushbullet_name', $name, null, false, time() + 3600 );
return $name;
}
/**
* Informs admin if something wrong with the website or its configuration
*
*/
function cerber_check_environment() {
static $done;
if ( $done ) {
return;
}
$done = true;
if ( version_compare( CERBER_REQ_PHP, phpversion(), '>' ) ) {
cerber_admin_notice( sprintf( __( 'WP Cerber requires PHP %s or higher. You are running %s.', 'wp-cerber' ), CERBER_REQ_PHP, phpversion() ) );
}
if ( ! crb_wp_version_compare( CERBER_REQ_WP ) ) {
cerber_admin_notice( sprintf( __( 'WP Cerber requires WordPress %s or higher. You are running %s.', 'wp-cerber' ), CERBER_REQ_WP, cerber_get_wp_version() ) );
}
if ( defined( 'CERBER_CLOUD_DEBUG' ) ) {
cerber_admin_notice( 'Warning: Diagnostic logging of cloud requests is enabled.' );
}
if ( ( time() - 120 ) < cerber_get_set( '_check_env', 0, false ) ) {
return;
}
cerber_update_set( '_check_env', time(), 0, false );
if ( ! crb_get_settings( 'tienabled' ) ) {
cerber_admin_notice( 'Warning: Traffic Inspector is disabled' );
}
if ( cerber_is_admin_page( array( 'page' => 'cerber-shield' ) ) ) {
if ( CRB_DS::check_errors( $msg ) ) {
cerber_admin_notice( $msg );
}
}
$ex_list = get_loaded_extensions();
if ( ! in_array( 'curl', $ex_list ) ) {
cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' cURL PHP library is not enabled on this website.' );
}
else {
$curl = @curl_init();
if ( ! $curl && ( $err_msg = curl_error( $curl ) ) ) {
cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' ' . $err_msg );
}
curl_close( $curl );
}
if ( ! in_array( 'mbstring', $ex_list ) || ! function_exists( 'mb_convert_encoding' ) ) {
cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' Required PHP extension mbstring is not enabled on this website. Some plugin features do work properly. Please enable the PHP mbstring extension (multibyte string support) in your hosting control panel.' );
}
if ( cerber_get_mode() != crb_get_settings( 'boot-mode' ) ) {
cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' ' . 'The plugin is initialized in a different mode that does not match the settings. Check the "Load security engine" setting.' );
}
}
/**
* Register an issue to be used in the "trouble solving" functionality
*
* @param string $code
* @param string $text
* @param string $related_setting
*/
function cerber_add_issue( $code, $text, $related_setting = '' ) {
// TODO: implement this reporting feature
static $issues = array();
if ( ! isset( $issues[ $code ] ) ) {
$issues[ $code ] = array( $text, $related_setting );
if ( is_admin() ) {
// There will be a separate list of issues that is displayed separately.
// cerber_admin_notice( __( 'Warning:', 'wp-cerber' ) . ' ' . $text );
}
}
}
/**
* Health check-up and self-repairing for vital parts
*
*/
function cerber_watchdog( $full = false ) {
if ( $full ) {
cerber_create_db( false );
cerber_upgrade_db();
return;
}
if ( ! cerber_is_table( CERBER_LOG_TABLE )
|| ! cerber_is_table( CERBER_BLOCKS_TABLE )
|| ! cerber_is_table( CERBER_LAB_IP_TABLE )
) {
cerber_create_db( false );
cerber_upgrade_db();
}
}
/**
* Detect and return remote client IP address
*
* @return string Valid IP address
* @since 6.0
*/
function cerber_get_remote_ip() {
static $remote_ip;
if ( isset( $remote_ip ) ) {
return $remote_ip;
}
if ( defined( 'CERBER_IP_KEY' ) ) {
$remote_ip = filter_var( $_SERVER[ CERBER_IP_KEY ], FILTER_VALIDATE_IP );
}
elseif ( crb_get_settings( 'proxy' ) && isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
$list = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] );
foreach ( $list as $maybe_ip ) {
$remote_ip = filter_var( trim( $maybe_ip ), FILTER_VALIDATE_IP );
if ( $remote_ip ) {
break;
}
}
if ( ! $remote_ip && isset( $_SERVER['HTTP_X_REAL_IP'] ) ) {
$remote_ip = filter_var( $_SERVER['HTTP_X_REAL_IP'], FILTER_VALIDATE_IP );
}
}
else {
if ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
$remote_ip = $_SERVER['REMOTE_ADDR'];
}
elseif ( ! empty( $_SERVER['HTTP_X_REAL_IP'] ) ) {
$remote_ip = $_SERVER['HTTP_X_REAL_IP'];
}
elseif ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
$remote_ip = $_SERVER['HTTP_CLIENT_IP'];
}
$remote_ip = filter_var( $remote_ip, FILTER_VALIDATE_IP );
}
if ( ! $remote_ip ) { // including WP-CLI, other way is: if defined('WP_CLI')
$remote_ip = CERBER_NO_REMOTE_IP;
}
if ( cerber_is_ipv6( $remote_ip ) ) {
$remote_ip = cerber_ipv6_short( $remote_ip );
}
return $remote_ip;
}
/**
* Get ip_id for IP.
* The ip_id can be safely used for array indexes and in any HTML code
*
* @param $ip string IP address
*
* @return string ID for given IP
* @since 2.2
*
*/
function cerber_get_id_ip( $ip ) {
$ip_id = str_replace( '.', '-', $ip, $count );
$ip_id = str_replace( ':', '_', $ip_id );
return $ip_id;
}
/**
* Get IP from ip_id
*
* @param $ip_id string ID for an IP
*
* @return string IP address for given ID
* @since 2.2
*
*/
function cerber_get_ip_id( $ip_id ) {
$ip = str_replace( '-', '.', $ip_id, $count );
$ip = str_replace( '_', ':', $ip );
return $ip;
}
/**
* Check if given IP address is a valid single IP v4 address
*
* @param $ip
*
* @return bool
*/
function cerber_is_ipv4( $ip ) {
return ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) ? true : false;
}
function cerber_is_ipv6( $ip ) {
return ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) ? true : false;
}
/**
* Check if a given IP address belongs to a private network (private IP).
* Universal: support IPv6 and IPv4.
*
* @param $ip string An IP address to check
*
* @return bool True if IP is in the private range, false otherwise
*/
function is_ip_private( $ip ) {
if ( ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE ) ) {
return true;
}
elseif ( ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE ) ) {
return true;
}
return false;
}
function cerber_is_ip( $ip ) {
return filter_var( $ip, FILTER_VALIDATE_IP );
}
/**
* Expands shortened IPv6 to full IPv6 address
*
* @param $ip string IPv6 address
*
* @return string Full IPv6 address
*/
function cerber_ipv6_expand( $ip ) {
$full_hex = (string) bin2hex( inet_pton( $ip ) );
return implode( ':', str_split( $full_hex, 4 ) );
}
/**
* Compress full IPv6 to shortened
*
* @param $ip string IPv6 address
*
* @return string Full IPv6 address
*/
function cerber_ipv6_short( $ip ) {
if ( ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
return $ip;
}
return inet_ntop( inet_pton( $ip ) );
}
/**
* Convert multilevel object or array of objects to associative array recursively
*
* @param $var object|array
*
* @return array result of conversion
* @since 3.0
*/
function obj_to_arr_deep( $var ) {
if ( is_object( $var ) ) {
$var = get_object_vars( $var );
}
if ( is_array( $var ) ) {
return array_map( __FUNCTION__, $var );
}
return $var;
}
/**
* Search for a string key in a given multidimensional array
*
* @param array $array
* @param string $needle
*
* @return bool
*/
function crb_multi_search_key( $array, $needle ) {
foreach ( $array as $key => $value ) {
if ( (string) $key == (string) $needle ) {
return true;
}
if ( is_array( $value ) ) {
$ret = crb_multi_search_key( $value, $needle );
if ( $ret == true ) {
return true;
}
}
}
return false;
}
/**
* array_column() implementation for PHP < 5.5
*
* @param array $arr Multidimensional array
* @param string $column Column key
*
* @return array
*/
function crb_array_column( $arr = array(), $column = '' ) {
global $x_column;
$x_column = $column;
$ret = array_map( function ( $element ) {
global $x_column;
return $element[ $x_column ];
}, $arr );
$ret = array_values( $ret );
return $ret;
}
/**
* @param $arr array
* @param $key string|integer|array
* @param $default mixed
* @param $pattern string REGEX pattern for value validation, UTF is not supported
*
* @return mixed
*/
function crb_array_get( &$arr, $key, $default = false, $pattern = '' ) {
if ( ! is_array( $arr ) || empty( $arr ) ) {
return $default;
}
if ( is_array( $key ) ) {
$ret = crb_array_get_deep( $arr, $key );
if ( $ret === null ) {
$ret = $default;
}
}
else {
$ret = ( isset( $arr[ $key ] ) ) ? $arr[ $key ] : $default;
}
if ( ! $pattern ) {
return $ret;
}
if ( ! is_array( $ret ) ) {
if ( @preg_match( '/^' . $pattern . '$/i', $ret ) ) {
return $ret;
}
return $default;
}
global $cerber_temp;
$cerber_temp = $pattern;
array_walk( $ret, function ( &$item ) {
global $cerber_temp;
if ( ! @preg_match( '/^' . $cerber_temp . '$/i', $item ) ) {
$item = '';
}
} );
return array_filter( $ret );
}
/**
* Retrieve element from multi-dimensional array
*
* @param array $arr
* @param array $keys Keys (dimensions)
*
* @return mixed|null Value of the element if it's defined, null otherwise
*/
function crb_array_get_deep( &$arr, $keys ) {
if ( ! is_array( $arr ) ) {
return null;
}
$key = array_shift( $keys );
if ( isset( $arr[ $key ] ) ) {
if ( empty( $keys ) ) {
return $arr[ $key ];
}
return crb_array_get_deep( $arr[ $key ], $keys );
}
return null;
}
/**
* Compare two arrays by using keys: check if two arrays have different set of keys
*
* @param $arr1 array
* @param $arr2 array
*
* @return bool true if arrays have different set of keys
*/
function crb_array_diff_keys( &$arr1, &$arr2 ) {
if ( count( $arr1 ) != count( $arr2 ) ) {
return true;
}
if ( array_diff_key( $arr1, $arr2 ) ) {
return true;
}
if ( array_diff_key( $arr2, $arr1 ) ) {
return true;
}
return false;
}
/**
* Compares two elements of two arrays
*
* @param $arr1 array
* @param $arr2 array
* @param $key1 string|int
* @param $key2 string|int
*
* @return bool True if elements are equal or absent in two arrays
*/
function crb_array_cmp_val( &$arr1, &$arr2, $key1, $key2 = null ) {
if ( ! $key2 ) {
$key2 = $key1;
}
if ( ( $set = isset( $arr1[ $key1 ] ) ) !== isset( $arr2[ $key2 ] ) ) {
return false;
}
if ( ! $set ) {
return true;
}
return ( $arr1[ $key1 ] === $arr2[ $key2 ] );
}
/**
* Changes the case of all keys in an array.
* Supports multi-dimensional arrays.
*
* @param array $arr
* @param int $case CASE_LOWER | CASE_UPPER
*
* @return array
*/
function crb_array_change_key_case( $arr, $case = CASE_LOWER ) {
return array_map( function ( $item ) use ( $case ) {
if ( is_array( $item ) ) {
$item = crb_array_change_key_case( $item, $case );
}
return $item;
}, array_change_key_case( $arr, $case ) );
}
/**
* @param string|array $val
*
* for objects see map_deep();
*/
function crb_trim_deep( &$val ) {
if ( ! is_array( $val ) ) {
$val = trim( $val );
}
array_walk_recursive( $val, function ( &$v ) {
$v = trim( $v );
} );
}
/**
* @param string|array $val
*
* Note: _sanitize_text_fields removes HTML tags
*
*/
function crb_sanitize_deep( &$val ) {
if ( ! is_array( $val ) ) {
$val = _sanitize_text_fields( $val, true );
}
array_walk_recursive( $val, function ( &$v ) {
$v = _sanitize_text_fields( $v, true );
} );
}
/**
* Sanitizes integer values
*
* @param array|int $val Input value
* @param bool $make_list If true, returns result as an array
* @param bool $keep_empty If false, removes empty elements from the resulting array
*
* @return array|int
*
* @since 8.9.5.2
*/
function crb_sanitize_int( $val, $make_list = true, $keep_empty = false ) {
if ( ! is_array( $val ) ) {
if ( $make_list ) {
$val = array( $val );
}
else {
return absint( $val );
}
}
array_walk_recursive( $val, function ( &$v ) {
$v = absint( $v );
} );
if ( ! $keep_empty ) {
$val = array_filter( $val );
}
return $val;
}
/**
* Return true if a REST API URL has been requested
*
* @return bool
* @since 3.0
*/
function cerber_is_rest_url() {
global $wp_rewrite;
static $ret = null;
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
return true;
}
if ( isset( $_REQUEST['rest_route'] ) ) {
return true;
}
if ( ! $wp_rewrite ) { // see get_rest_url() in the multisite mode
return false;
}
if ( isset( $ret ) ) {
return $ret;
}
$ret = false;
if ( crb_get_rest_path() ) {
$ret = true;
}
return $ret;
}
/**
* @return bool
*
* @since 8.8
*/
function cerber_is_api_request() {
return ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) );
}
/**
* Check if the current query is HTTP GET
*
* @return bool true if request method is GET
*/
function cerber_is_http_get() {
if ( nexus_is_valid_request() ) {
return ! nexus_request_data()->is_post;
}
if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] == 'GET' ) {
return true;
}
return false;
}
/**
* Check if the current query is HTTP POST
*
* @return bool true if request method is POST
*/
function cerber_is_http_post() {
if ( nexus_is_valid_request() ) {
return nexus_request_data()->is_post;
}
if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] == 'POST' ) {
return true;
}
return false;
}
/**
* Checks if it's a wp cron request
*
* @return bool
*/
function cerber_is_wp_cron() {
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
return true;
}
if ( CRB_Request::is_script( '/wp-cron.php' ) ) {
return true;
}
return false;
}
function cerber_is_wp_ajax( $use_filter = false ) {
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return true;
}
// @since 8.1.3
if ( $use_filter && function_exists( 'wp_doing_ajax' ) ) {
return wp_doing_ajax();
}
return false;
}
/**
* Checks whether the given variable is a WordPress Error
*
* @param $thing
*
* @return bool True if the variable is an instance of WP_Error
*
* @since 9.0.4
*/
function crb_is_wp_error( $thing ) {
return ( $thing instanceof WP_Error );
}
/**
* @return bool True if it's the user edit/profile WordPress admin page
*/
function is_admin_user_edit() {
if ( ( defined( 'IS_PROFILE_PAGE' ) && IS_PROFILE_PAGE )
|| CRB_Request::is_script( array( '/wp-admin/user-edit.php', '/wp-admin/profile.php' ) ) ) {
return true;
}
return false;
}
/**
* Returns a $_GET parameter with a given key
*
* @param $key string
* @param $pattern string
* @param $filter_var integer filter_var() filter ID
*
* @return bool|array|string
*/
function cerber_get_get( $key, $pattern = '', $filter_var = null ) {
$ret = crb_array_get( $_GET, $key, false, $pattern );
if ( $filter_var ) {
return filter_var( $ret, FILTER_VALIDATE_IP );
}
return $ret;
}
/**
*
* @param $key string
* @param $pattern string
*
* @return bool|array|string
*/
function cerber_get_post( $key, $pattern = '' ) {
return crb_array_get( $_POST, $key, false, $pattern );
}
/**
* Returns values of $_GET parameters (query string)
*
* @param string $key
* @param string $pattern
*
* @return array|string|mixed
*/
function crb_get_query_params( $key = null, $pattern = '' ) {
if ( nexus_is_valid_request() ) {
if ( $key ) {
return crb_array_get( nexus_request_data()->get_params, $key, false, $pattern );
}
return (array) nexus_request_data()->get_params;
}
// Local context
if ( $key ) {
return cerber_get_get( $key, $pattern );
}
return (array) $_GET;
}
function crb_get_post_fields( $key = null, $default = false, $pattern = '' ) {
if ( nexus_is_valid_request() ) {
if ( nexus_request_data()->is_post ) {
return nexus_request_data()->get_post_fields( $key, $default, $pattern );
}
return array();
}
if ( $key ) {
return crb_array_get( $_POST, $key, $default, $pattern );
}
return $_POST;
}
function crb_get_request_field( $field, $default = false ) {
$fields = crb_get_request_fields();
return crb_array_get( $fields, $field, $default );
}
function crb_get_request_fields() {
if ( nexus_is_valid_request() ) {
$ret = nexus_request_data()->get_params;
if ( nexus_request_data()->is_post ) {
$ret = array_merge( $ret, nexus_request_data()->get_post_fields() );
}
return $ret;
}
return $_REQUEST;
}
/**
* Is requested REST API namespace whitelisted
*
* @return bool
*/
function cerber_is_route_allowed() {
$allowed = crb_get_settings( 'restwhite' );
if ( empty( $allowed ) ) {
return false;
}
$rest_path = crb_get_rest_path();
$namespace = substr( $rest_path, 0, strpos( $rest_path, '/' ) );
if ( in_array( $namespace, (array) $allowed ) ) {
return true;
}
return false;
}
function cerber_is_rest_permitted() {
// Exception: application passwords route @since WP Cerber 8.8 & WP 5.6 -> permissions are checked in the WP core
if ( preg_match( '#^wp/v\d+/users/\d+/application-passwords#', crb_get_rest_path() ) ) {
return true;
}
$opt = crb_get_settings();
if ( ! empty( $opt['norestuser'] ) ) {
$path = explode( '/', crb_get_rest_path() );
if ( $path && count( $path ) > 2 && $path[0] == 'wp' && $path[2] == 'users' ) {
if ( is_super_admin() ) {
return true; // @since 8.3.4
}
return false;
}
}
if ( empty( $opt['norest'] ) ) {
return true;
}
CRB_Globals::$user_id = get_current_user_id();
if ( $opt['restauth'] && is_user_logged_in() ) {
return true;
}
if ( ! empty( $opt['restwhite'] ) || is_array( $opt['restwhite'] ) ) {
$rest_path = crb_get_rest_path();
$namespace = substr( $rest_path, 0, strpos( $rest_path, '/' ) );
foreach ( $opt['restwhite'] as $exception ) {
if ( $exception == $namespace ) {
CRB_Globals::$req_status = 503;
return true;
}
}
}
if ( ! empty( $opt['restroles'] ) || is_array( $opt['restroles'] ) ) {
if ( cerber_user_has_role( $opt['restroles'] ) ) {
return true;
}
}
return false;
}
/**
* Requested REST API route without leading and trailing slashes
*
* @return string Empty string for any non REST API request
*/
function crb_get_rest_path() {
global $wp_rewrite;
static $ret;
if ( isset( $ret ) ) {
return $ret;
}
$ret = '';
if ( isset( $_REQUEST['rest_route'] ) ) {
$ret = trim( $_REQUEST['rest_route'], '/' );
}
elseif ( cerber_is_permalink_enabled() ) {
$path = CRB_Request::get_relative_path();
$path = trim( $path, '/' );
$prefix = rest_get_url_prefix() . '/';
// See get_rest_url() ----------------
if ( ( $wp_rewrite instanceof WP_Rewrite ) && $wp_rewrite->using_index_permalinks() ) {
$prefix = $wp_rewrite->index . '/' . $prefix;
}
// -----------------------------------
if ( 0 === strpos( $path, $prefix ) ) {
$ret = substr( $path, strlen( $prefix ) );
}
}
return $ret;
}
/**
*
* @return string Full URL including scheme, host, path and trailing slash
*
*/
function crb_get_rest_url() {
static $ret;
if ( ! isset( $ret ) ) {
$ret = get_rest_url();
}
return $ret;
}
/**
* Check if a user has at least one role from the list
*
* @param array $roles
* @param null $user_id
*
* @return bool
*/
function cerber_user_has_role( $roles = array(), $user_id = null ) {
if ( ! $roles ) {
return false;
}
if ( ! $user_id ) {
$user = wp_get_current_user();
}
else {
$user = get_userdata( $user_id );
}
if ( ! $user || empty( $user->roles ) ) {
return false;
}
if ( ! is_array( $roles ) ) {
$roles = array( $roles );
}
if ( array_intersect( $user->roles, $roles ) ) {
return true;
}
return false;
}
/**
* Check if all user roles are in the list
*
* @param array|string $roles
* @param int $user_id
*
* @return bool false if the user has role(s) other than listed in $roles
*/
function crb_user_has_role_strict( $roles, $user_id ) {
if ( ! $user_id || ! $user = get_userdata( $user_id ) ) {
return false;
}
if ( ! is_array( $roles ) ) {
$roles = array( $roles );
}
$user_roles = ( is_array( $user->roles ) ) ? $user->roles : array();
return ( ! array_diff( $user_roles, $roles ) );
}
/**
* Returns metadata if the user is blocked by admin
*
* @param int $uid User ID
*
* @return false|array
*/
function crb_is_user_blocked( $uid ) {
if ( $uid
&& ( $m = get_user_meta( $uid, CERBER_BUKEY, 1 ) )
&& ! empty( $m['blocked'] )
&& $m[ 'u' . $uid ] == $uid ) {
return $m;
}
return false;
}
/**
* Returns textual info on who and when blocked a user
*
* @param array $meta
*
* @return string
*
* @since 9.0.2
*/
function crb_user_blocked_by( $meta ) {
if ( empty( $meta['blocked_by'] ) ) {
return '';
}
if ( $meta['blocked_by'] == get_current_user_id() ) {
$who = __( 'You', 'wp-cerber' );
}
else {
$user = get_userdata( $meta['blocked_by'] );
$who = '' . $user->display_name . '';
}
return sprintf( _x( 'blocked by %s at %s', 'e.g. blocked by John at 11:00', 'wp-cerber' ), $who, cerber_date( $meta['blocked_time'] ) );
}
/**
* @return bool
*
* @since 8.8
*
*/
function crb_is_user_logged_in() {
if ( ! function_exists( 'is_user_logged_in' ) ) {
cerber_load_wp_constants(); // The reason is, we need auth constants before the use of is_user_logged_in() in "Standard mode".
require_once( ABSPATH . WPINC . '/pluggable.php' );
}
return is_user_logged_in();
}
/**
* Returns user session token.
*
* OMG: WordPress stores the same token in two different cookies.
*
* @return string
*
* @since 8.9.1
*/
function crb_get_session_token() {
// First, trying the default cookie: LOGGED_IN_COOKIE
if ( ! $token = wp_get_session_token() ) {
// Trying another, backup cookie: SECURE_AUTH_COOKIE or AUTH_COOKIE
$cookie = wp_parse_auth_cookie();
$token = crb_array_get( $cookie, 'token', '' );
}
return $token;
}
/**
* Checks role-based user limits
*
* @param $user_id
*
* @return false|string Returns false if no restrictions, an error message otherwise.
*/
function crb_check_user_limits( $user_id ) {
if ( ! $user_id ) {
return false;
}
// Sessions
if ( ! $limit = absint( cerber_get_user_policy( 'sess_limit', $user_id ) ) ) {
return false;
}
$list = cerber_db_get_results( 'SELECT started, wp_session_token FROM ' . cerber_get_db_prefix() . CERBER_USS_TABLE . ' WHERE user_id = ' . absint( $user_id ) );
if ( $list && ( count( $list ) >= $limit ) ) {
if ( cerber_get_user_policy( 'sess_limit_policy', $user_id ) ) {
if ( $msg = cerber_get_user_policy( 'sess_limit_msg', $user_id ) ) {
return $msg;
}
return get_wp_cerber()->getErrorMsg();
}
else {
$started = array_column( $list, 'started' );
array_multisort( $started, SORT_ASC, SORT_NUMERIC, $list );
CRB_Globals::$session_status = 38;
crb_sessions_kill( $list[0]['wp_session_token'], $user_id, false );
}
}
return false;
}
/**
* Return the last element in the path of the requested URI.
*
* @return bool|string
*/
function cerber_last_uri() {
static $ret;
if ( isset( $ret ) ) {
return $ret;
}
$ret = strtolower( $_SERVER['REQUEST_URI'] );
if ( $pos = strpos( $ret, '?' ) ) {
$ret = substr( $ret, 0, $pos );
}
if ( $pos = strpos( $ret, '#' ) ) {
$ret = substr( $ret, 0, $pos ); // @since 8.1 - malformed request URI
}
$ret = rtrim( $ret, '/' );
$ret = substr( strrchr( $ret, '/' ), 1 );
return $ret;
}
/**
* Return the name of an executable script in the requested URI if it's present
*
* @return bool|string The script name or false if executable script is not detected
*/
function cerber_get_uri_script() {
static $ret;
if ( isset( $ret ) ) {
return $ret;
}
$last = cerber_last_uri();
if ( cerber_detect_exec_extension( $last ) ) {
$ret = $last;
}
else {
$ret = false;
}
return $ret;
}
/**
* Detects an executable extension in a filename.
* Supports double and N fake extensions.
*
* @param $line string Filename
* @param array $extra A list of additional extensions to detect
*
* @return bool|string An extension if it's found, false otherwise
*/
function cerber_detect_exec_extension( $line, $extra = array() ) {
static $executable = array( 'php', 'phtm', 'phtml', 'phps', 'shtm', 'shtml', 'jsp', 'asp', 'aspx', 'axd', 'exe', 'com', 'cgi', 'pl', 'py', 'pyc', 'pyo' );
static $not_exec = array( 'jpg', 'png', 'svg', 'css', 'txt' );
if ( empty( $line ) || ! strrpos( $line, '.' ) ) {
return false;
}
if ( $extra ) {
$ex_list = array_merge( $executable, $extra );
}
else {
$ex_list = $executable;
}
$line = trim( $line );
$line = trim( $line, '/' );
$parts = explode( '.', $line );
array_shift( $parts );
// First and last are critical for most server environments
$first_ext = array_shift( $parts );
$last_ext = array_pop( $parts );
if ( $first_ext ) {
$first_ext = strtolower( $first_ext );
if ( ! in_array( $first_ext, $not_exec ) ) {
if ( in_array( $first_ext, $ex_list ) ) {
return $first_ext;
}
if ( preg_match( '/php\d+/', $first_ext ) ) {
return $first_ext;
}
}
}
if ( $last_ext ) {
$last_ext = strtolower( $last_ext );
if ( in_array( $last_ext, $ex_list ) ) {
return $last_ext;
}
if ( preg_match( '/php\d+/', $last_ext ) ) {
return $last_ext;
}
}
return false;
}
/**
* Remove extra slashes \ / from a script file name
*
* @return string|bool
*/
function cerber_script_filename() {
return preg_replace( '/[\/\\\\]+/', '/', $_SERVER['SCRIPT_FILENAME'] ); // Windows server
}
function cerber_script_exists( $uri ) {
$script_filename = cerber_script_filename();
if ( is_multisite() && ! is_subdomain_install() ) {
$path = explode( '/', $uri );
if ( count( $path ) > 1 ) {
$last = array_pop( $path );
$virtual_sub_folder = array_pop( $path );
$uri = implode( '/', $path ) . '/' . $last;
}
}
if ( false === strrpos( $script_filename, $uri ) ) {
return false;
}
return true;
}
/**
* Activity labels and statues
*
* @param string $type
* @param int $id
*
* @return array|string
*/
function cerber_get_labels( $type = 'activity', $id = 0 ) {
if ( ! $labels = cerber_cache_get( 'labels' ) ) {
// Initialize it
$labels = array(
'activity' => array(),
'activity_by' => array(),
'status' => array(),
);
$act = &$labels['activity'];
$act_by = &$labels['activity_by'];
// User actions
$act[1] = __( 'User created', 'wp-cerber' );
/* translators: %s is the name of a website administrator who created the user. */
$act_by[1] = __( 'User created by %s', 'wp-cerber' );
$act[2] = __( 'User registered', 'wp-cerber' );
$act[3] = __( 'User deleted', 'wp-cerber' );
/* translators: %s is the name of a website administrator who deleted the user. */
$act_by[3] = __( 'User deleted by %s', 'wp-cerber' );
$act[ CRB_EV_LIN ] = __( 'Logged in', 'wp-cerber' );
$act[6] = __( 'Logged out', 'wp-cerber' );
$act[ CRB_EV_LFL ] = __( 'Login failed', 'wp-cerber' );
// Cerber actions - IP specific - lockouts
$act[10] = __( 'IP blocked', 'wp-cerber' );
$act[11] = __( 'IP subnet blocked', 'wp-cerber' );
// WP Cerber's actions - denied
$act[12] = __( 'Citadel activated!', 'wp-cerber' );
$act[16] = __( 'Spam comment denied', 'wp-cerber' );
$act[17] = __( 'Spam form submission denied', 'wp-cerber' );
$act[18] = __( 'Form submission denied', 'wp-cerber' );
$act[19] = __( 'Comment denied', 'wp-cerber' );
// Cerber status now
//$act[13]=__('Locked out','wp-cerber');
//$act[14]=__('IP blacklisted','wp-cerber');
//$act[15]=__('Malicious activity detected','wp-cerber');
// --------------------------------------------------------------
// Other events
$act[20] = __( 'Password changed', 'wp-cerber' );
/* translators: %s is the name of a website administrator who changed the password. */
$act_by[20] = __( 'Password changed by %s', 'wp-cerber' );
$act[ CRB_EV_PRS ] = __( 'Password reset requested', 'wp-cerber' );
$act[ CRB_EV_UST ] = __( 'User session terminated', 'wp-cerber' );
/* translators: %s is the name of a website administrator who terminated the session. */
$act_by[ CRB_EV_UST ] = __( 'User session terminated by %s', 'wp-cerber' );
$act[ CRB_EV_PRD ] = __( 'Password reset request denied', 'wp-cerber' );
// Not in use and replaced by statuses 532 - 534 since 8.9.4.
$act[40] = __( 'reCAPTCHA verification failed', 'wp-cerber' );
$act[41] = __( 'reCAPTCHA settings are incorrect', 'wp-cerber' );
$act[42] = __( 'Request to the Google reCAPTCHA service failed', 'wp-cerber' );
// --------------------------
$act[CRB_EV_PUR] = __( 'Attempt to access prohibited URL', 'wp-cerber' );
$act[51] = __( 'Attempt to log in with non-existing username', 'wp-cerber' );
$act[52] = __( 'Attempt to log in with prohibited username', 'wp-cerber' );
// WP Cerber's actions - denied
$act[ CRB_EV_LDN ] = __( 'Attempt to log in denied', 'wp-cerber' );
$act[54] = __( 'Attempt to register denied', 'wp-cerber' );
$act[55] = __( 'Probing for vulnerable code', 'wp-cerber' );
$act[56] = __( 'Attempt to upload malicious file denied', 'wp-cerber' );
$act[57] = __( 'File upload denied', 'wp-cerber' );
$act[70] = __( 'Request to REST API denied', 'wp-cerber' );
$act[71] = __( 'Request to XML-RPC API denied', 'wp-cerber' );
$act[72] = __( 'User creation denied', 'wp-cerber' );
$act[73] = __( 'User row update denied', 'wp-cerber' );
$act[74] = __( 'Role update denied', 'wp-cerber' );
$act[75] = __( 'Setting update denied', 'wp-cerber' );
$act[76] = __( 'User metadata update denied', 'wp-cerber' );
$act[100] = __( 'Malicious request denied', 'wp-cerber' );
// APIs
$act[149] = __( 'User application password updated', 'wp-cerber' );
$act[150] = __( 'User application password created', 'wp-cerber' );
/* translators: %s is the name of a website administrator who created the password. */
$act_by[150] = __( 'User application password created by %s', 'wp-cerber' );
$act[151] = __( 'API request authorized', 'wp-cerber' );
$act[152] = __( 'API request authorization failed', 'wp-cerber' );
$act[153] = __( 'User application password deleted', 'wp-cerber' );
/* translators: %s is the name of a website administrator who deleted the password. */
$act_by[153] = __( 'User application password deleted by %s', 'wp-cerber' );
// BuddyPress
$act[200] = __( 'User activated', 'wp-cerber' );
// Nexus slave
$act[300] = __( 'Invalid master credentials', 'wp-cerber' );
$act[400] = 'Two-factor authentication enforced';
// Statuses
$sts = &$labels['status'];
$sts[10] = __( 'Denied', 'wp-cerber' ); // @since 8.9.5.6
$sts[ CRB_STS_11 ] = __( 'Bot detected', 'wp-cerber' );
$sts[12] = __( 'Citadel mode is active', 'wp-cerber' );
$sts[13] = __( 'IP address is locked out', 'wp-cerber' );
$sts[14] = __( 'IP blacklisted', 'wp-cerber' );
$sts[15] = __( 'Malicious activity detected', 'wp-cerber' );
$sts[16] = __( 'Blocked by country rule', 'wp-cerber' );
$sts[17] = __( 'Limit reached', 'wp-cerber' );
$sts[18] = __( 'Multiple suspicious activities', 'wp-cerber' );
$sts[19] = __( 'Denied', 'wp-cerber' ); // @since 6.7.5
$sts[20] = __( 'Suspicious number of fields', 'wp-cerber' );
$sts[21] = __( 'Suspicious number of nested values', 'wp-cerber' );
$sts[22] = __( 'Malicious code detected', 'wp-cerber' );
$sts[23] = __( 'Suspicious SQL code detected', 'wp-cerber' );
$sts[24] = __( 'Suspicious JavaScript code detected', 'wp-cerber' );
$sts[ CRB_STS_25 ] = __( 'Blocked by administrator', 'wp-cerber' );
$sts[26] = __( 'Site policy enforcement', 'wp-cerber' );
$sts[27] = __( '2FA code verified', 'wp-cerber' );
$sts[28] = __( 'Initiated by the user', 'wp-cerber' );
$sts[ CRB_STS_29 ] = __( 'User blocked by administrator', 'wp-cerber' );
$sts[ CRB_STS_30 ] = __( 'Username is prohibited', 'wp-cerber' );
$sts[31] = __( 'Email address is prohibited', 'wp-cerber' );
$sts[32] = 'User role is prohibited';
$sts[33] = __( 'Permission denied', 'wp-cerber' );
$sts[34] = 'Unauthorized access denied';
$sts[35] = __( 'Invalid user', 'wp-cerber' );
$sts[36] = __( 'Incorrect password', 'wp-cerber' );
$sts[37] = __( 'IP address is not allowed', 'wp-cerber' );
$sts[38] = __( 'Limit on concurrent user sessions', 'wp-cerber' );
$sts[39] = __( 'Invalid cookies', 'wp-cerber' );
$sts[40] = __( 'Invalid cookies cleared', 'wp-cerber' );
$sts[50] = __( 'Forbidden URL', 'wp-cerber' );
$sts[ CRB_STS_51 ] = __( 'Executable file extension detected', 'wp-cerber' );
$sts[ CRB_STS_52 ] = __( 'Filename is prohibited', 'wp-cerber' );
// @since 8.6.4
$sts[500] = __( 'IP whitelisted', 'wp-cerber' );
$sts[501] = 'URL whitelisted';
$sts[502] = 'Query whitelisted';
$sts[503] = 'Namespace whitelisted';
// IP is in the whitelist, but "use whitelist" setting is not enabled
$sts[510] = $sts[500]; // TI
$sts[511] = $sts[500]; // DS
$sts[512] = $sts[500]; // DS
// @since 8.9.4
$sts[530] = __( 'Logged out everywhere', 'wp-cerber' );
$sts[531] = __( 'reCAPTCHA verified', 'wp-cerber' );
$sts[ CRB_STS_532 ] = __( 'reCAPTCHA verification failed', 'wp-cerber' );
$sts[533] = __( 'reCAPTCHA settings are incorrect', 'wp-cerber' );
$sts[534] = __( 'Request to the Google reCAPTCHA service failed', 'wp-cerber' );
// @since 9.3.2
$sts[540] = __( "User's IP address does not match the one used to log in", 'wp-cerber' );
$sts[541] = __( "User's browser does not match the one used to log in", 'wp-cerber' );
$sts[542] = __( 'Exceeded the limit on the number of attempts to enter 2FA code', 'wp-cerber' );
// Note: 7xx is in use in cerber_get_reason()
cerber_cache_set( 'labels', $labels );
}
if ( $id ) {
if ( isset( $labels[ $type ][ $id ] ) ) {
return $labels[ $type ][ $id ];
}
return __( 'Unknown label', 'wp-cerber' ) . ' (' . absint( $id ) . '/' . $type . ')';
}
return $labels[ $type ];
}
/**
* Returns a label to be displayed in the logs
*
* @param int $activity
* @param int $user
* @param int $by_user
* @param bool $link
*
* @return string
*
* @since 8.9.5.1
*/
function crb_get_label( $activity, $user = null, $by_user = null, $link = true ) {
static $user_link = array();
if ( $by_user
&& $user != $by_user
&& $user_data = crb_get_userdata( $by_user ) ) {
if ( $link ) {
if ( empty( $user_link[ $by_user ] ) ) {
$user_link[ $by_user ] = get_edit_user_link( $by_user );
}
$user_name = '' . $user_data->display_name . '';
}
else {
$user_name = $user_data->display_name;
}
return sprintf( cerber_get_labels( 'activity_by', $activity ), $user_name );
}
return cerber_get_labels( 'activity', $activity );
}
function crb_get_filter_set( $set_id ) {
static $list = array( 1 => 'suspicious', 2 => 'login_issues' );
if ( ! isset( $list[ $set_id ] ) ) {
return array();
}
return crb_get_activity_set( $list[ $set_id ] );
}
function crb_get_activity_set( $slice = 'malicious', $implode = false ) {
$ret = array();
switch ( $slice ) {
case 'malicious':
$ret = array( 16, 17, CRB_EV_PRD, 40, 50, 51, 52, CRB_EV_LDN, 54, 55, 56, 100 );
break;
case 'black': // Like 'malicious' but will cause an IP lockout when hit the limit
$ret = array( 16, 17, 40, 50, 51, 52, CRB_EV_LDN, 55, 56, 100, 300 );
break;
case 'suspicious': // Uses when an admin inspects logs with filter_set = 1
$ret = array( 10, 11, 16, 17, CRB_EV_PRD, 40, 50, 51, 52, CRB_EV_LDN, 54, 55, 56, 57, 100, 70, 71, 72, 73, 74, 75, 76, 300 );
break;
case 'dashboard': // Important events for the plugin dashboard
$ret = array( 1, 2, 3, CRB_EV_LIN, 12, 16, 17, 18, 19, CRB_EV_UST, 40, 41, 42, 50, 51, 52, CRB_EV_LDN, 54, 55, 56, 57, 72, 73, 74, 75, 76, 100, 149, 150, 200, 300, 400 );
break;
case 'login_issues':
$ret = array( CRB_EV_LFL, CRB_EV_PRS, CRB_EV_PRD, 51, 52, CRB_EV_LDN, 152 );
break;
case 'blocked': // IP or subnet was blocked
$ret = array( 10, 11 );
}
if ( $implode ) {
return implode( ',', $ret );
}
return $ret;
}
function cerber_get_reason( $reason_id = null ) {
if ( ! $labels = cerber_cache_get( 'reasons' ) ) {
$labels = array();
$labels[701] = __( 'Limit on login attempts is reached', 'wp-cerber' );
$labels[702] = __( 'Attempt to access', 'wp-cerber' );
$labels[702] = __( 'Attempt to access prohibited URL', 'wp-cerber' );
$labels[703] = __( 'Attempt to log in with non-existing username', 'wp-cerber' );
$labels[704] = __( 'Attempt to log in with prohibited username', 'wp-cerber' );
$labels[705] = __( 'Limit on failed reCAPTCHA verifications is reached', 'wp-cerber' );
$labels[706] = __( 'Bot activity is detected', 'wp-cerber' );
$labels[707] = __( 'Multiple suspicious activities were detected', 'wp-cerber' );
$labels[708] = __( 'Probing for vulnerable code', 'wp-cerber' );
$labels[709] = __( 'Malicious code detected', 'wp-cerber' );
$labels[710] = __( 'Attempt to upload a file with malicious code', 'wp-cerber' );
$labels[711] = __( 'Multiple erroneous requests', 'wp-cerber' );
$labels[712] = __( 'Multiple suspicious requests', 'wp-cerber' );
//$labels[721] = 'Limit on 2FA verifications has reached';
$labels[721] = __( 'Exceeded the limit on the number of attempts to enter 2FA code', 'wp-cerber' );
cerber_cache_set( 'reasons', $labels );
}
if ( $reason_id ) {
if ( isset( $labels[ $reason_id ] ) ) {
return $labels[ $reason_id ];
}
return __( 'Unknown', 'wp-cerber' );
}
return $labels;
}
function cerber_db_error_log( $errors = array() ) {
global $wpdb;
if ( ! $errors ) {
$errors = array();
if ( ! empty( $wpdb->last_error ) ) {
$errors = array( array( $wpdb->last_error, $wpdb->last_query, microtime( true ) ) );
}
if ( $others = cerber_db_get_errors( true, false ) ) {
$errors = array_merge( $errors, $others );
}
}
if ( ! $errors ) {
return;
}
if ( ! $old = get_site_option( '_cerber_db_errors' ) ) {
$old = array();
}
update_site_option( '_cerber_db_errors', array_merge( $old, $errors ) );
}
/**
* Add admin error message(s) to be displayed in the dashboard
*
* @param string|array $msg
*/
function cerber_admin_notice( $msg ) {
crb_admin_add_msg( $msg, 'admin_notice' );
}
/**
*
* @param string|array $msg
*/
function cerber_admin_message( $msg ) {
crb_admin_add_msg( $msg );
}
function crb_admin_add_msg( $msg, $type = 'admin_message' ) {
if ( ! $msg || CRB_Globals::$doing_upgrade ) {
return;
}
if ( ! is_array( $msg ) ) {
$msg = array( $msg );
}
$set = cerber_get_set( $type );
if ( ! $set || ! is_array( $set ) ) {
$set = array();
}
cerber_update_set( $type, array_merge( $set, $msg ) );
}
function crb_clear_admin_msg() {
cerber_update_set( 'admin_notice', array() );
cerber_update_set( 'admin_message', array() );
cerber_update_set( 'cerber_admin_wide', '' );
}
/*
Check if currently displayed page is a Cerber admin dashboard page with optional checking a set of GET params
*/
function cerber_is_admin_page( $params = array(), $force = false ) {
if ( ! is_admin()
&& ! nexus_is_valid_request() ) {
return false;
}
$get = crb_get_query_params();
$ret = false;
if ( isset( $get['page'] ) && false !== strpos( $get['page'], 'cerber-' ) ) {
$ret = true;
if ( $params ) {
foreach ( $params as $param => $value ) {
if ( ! isset( $get[ $param ] ) ) {
$ret = false;
break;
}
if ( ! is_array( $value ) ) {
if ( $get[ $param ] != $value ) {
$ret = false;
break;
}
}
elseif ( ! in_array( $get[ $param ], $value ) ) {
$ret = false;
break;
}
}
}
}
if ( $ret || ! $force ) {
return $ret;
}
if ( ! function_exists( 'get_current_screen' ) || ! $screen = get_current_screen() ) {
return false;
}
if ( $screen->base == 'plugins' ) {
return true;
}
/*
if ($screen->parent_base == 'options-general') return true;
if ($screen->parent_base == 'settings') return true;
*/
return false;
}
/**
* Return human readable "ago" time
*
* @param $time integer Unix timestamp - time of an event
*
* @return string
*/
function cerber_ago_time( $time ) {
$diff = abs( time() - (int) $time );
if ( $diff < MINUTE_IN_SECONDS ) {
$secs = ( $diff <= 1 ) ? 1 : $diff;
/* translators: Time difference between two dates, in seconds (sec=second). 1: Number of seconds */
$dt = sprintf( _n( '%s sec', '%s secs', $secs, 'wp-cerber' ), $secs );
}
else {
$dt = human_time_diff( $time );
}
// _x( 'at', 'preposition of time',
return ( $time <= time() ) ? sprintf( __( '%s ago' ), $dt ) : sprintf( _x( 'in %s', 'preposition of a period of time like: in 6 hours', 'wp-cerber' ), $dt );
}
function cerber_auto_date( $time, $purify = true ) {
if ( ! $time ) {
return __( 'Never', 'wp-cerber' );
}
return $time < ( time() - DAY_IN_SECONDS ) ? cerber_date( $time, $purify ) : cerber_ago_time( $time );
}
/**
* Format date according to user settings and timezone
*
* @param $timestamp int Unix timestamp
* @param $purify boolean If true adds html to have a better look on a web page
*
* @return string
*/
function cerber_date( $timestamp, $purify = true ) {
static $gmt_offset;
if ( $gmt_offset === null ) {
$gmt_offset = get_option( 'gmt_offset' ) * 3600;
}
$timestamp = $gmt_offset + absint( $timestamp );
// @since 8.6.4: snippet is taken from new date_i18n()
if ( function_exists( 'wp_date' ) ) { // wp_date() introduced in WP 5.3
$local_time = gmdate( 'Y-m-d H:i:s', $timestamp );
$timezone = wp_timezone();
$datetime = date_create( $local_time, $timezone );
$date = wp_date( cerber_get_dt_format(), $datetime->getTimestamp(), $timezone );
}
else { // Older WP
$date = date_i18n( cerber_get_dt_format(), $timestamp );
}
if ( $purify ) {
$date = str_replace( array( ',', ' am', ' pm', ' AM', ' PM' ), array( ',