Orderadmin/cerber-admin-settings.php 0000644 00000121422 14757753175 0012562 0 ustar 00 $screen_id ) ) ) {
return;
}
$option = 'cerber-' . $screen_id;
register_setting( 'cerberus-' . $screen_id, $option );
global $tmp;
foreach ( $sections as $section_id => $section_config ) {
$desc = crb_array_get( $section_config, 'desc' );
if ( $links = crb_array_get( $section_config, 'seclinks' ) ) {
foreach ( $links as $link ) {
$desc .= '[ ' . $link[0] . ' ] ';
}
}
if ( $doclink = crb_array_get( $section_config, 'doclink' ) ) {
$desc .= '[ ' . __( 'Know more', 'wp-cerber' ) . ' ] ';
}
$tmp[ $section_id ] = '' . $desc . ' ';
add_settings_section( $section_id, crb_array_get( $section_config, 'name', '' ), function ( $sec ) {
global $tmp;
if ( $tmp[ $sec['id'] ] ) {
echo $tmp[ $sec['id'] ];
}
}, $option );
foreach ( $section_config['fields'] as $field => $config ) {
if ( ( $req_wp = $config['requires_wp'] ?? false )
&& ! crb_wp_version_compare( (string) $req_wp ) ) {
continue;
}
if ( ( $cb = $config['requires_true'] ?? false )
&& is_callable( $cb )
&& ! $cb() ) {
continue;
}
if ( in_array( $field, CRB_PRO_SETTINGS ) && ! lab_lab() ) {
continue;
}
$config['setting'] = $field;
$config['group'] = $screen_id;
$config['type'] = $config['type'] ?? 'text';
// Setting row (tr) class, to specify the input class use 'input_class'
$config['class'] = '';
if ( $config['type'] == 'hidden' ) {
$config['class'] .= ' crb-display-none';
}
if ( isset( $config['enabler'] ) ) {
$config['class'] .= crb_check_enabler( $config, crb_get_settings( $config['enabler'][0] ) );
}
add_settings_field( $field, crb_array_get( $config, 'title', '' ), 'cerber_field_show', $option, $section_id, $config );
}
}
}
function cerber_get_setting_id( $tab = null ) {
$id = ( ! $tab ) ? cerber_get_get( 'tab', CRB_SANITIZE_ID ) : $tab;
if ( ! $id ) {
$id = cerber_get_wp_option_id();
}
if ( ! $id ) {
$id = crb_admin_get_page();
}
// Mapping: some tab names (or page id) doesn't match WP setting names
// tab => settings id
$map = array(
'scan_settings' => 'scanner', // define('CERBER_OPT_S','cerber-scanner');
'scan_schedule' => 'schedule', // define('CERBER_OPT_E','cerber-schedule');
'scan_policy' => 'policies',
'ti_settings' => 'traffic',
'captcha' => 'recaptcha',
'cerber-recaptcha' => 'antispam',
'global_policies' => 'users',
'cerber-shield' => 'user_shield',
'cerber-nexus' => 'nexus-slave',
'nexus_slave' => 'nexus-slave',
);
crb_addon_settings_mapper( $map );
if ( isset( $map[ $id ] ) ) {
return $map[ $id ];
}
return $id;
}
/**
* Works when updating WP options
*
* @return bool|string
*/
function cerber_get_wp_option_id( $option_page = null ) {
if ( ! $option_page ) {
$option_page = crb_array_get( $_POST, 'option_page' );
}
if ( $option_page && ( 0 === strpos( $option_page, 'cerberus-' ) ) ) {
return substr( $option_page, 9 ); // 8 = length of 'cerberus-'
}
return false;
}
/*
* Display a settings form on an admin page
*
*/
function cerber_show_settings_form( $group = null ) {
$action = '';
if ( is_multisite() ) {
$action = ''; // Settings API doesn't work in multisite. Post data will be handled in the cerber_ms_update()
}
else {
if ( nexus_is_valid_request() ) {
//$action = cerber_admin_link();
}
else {
$action = 'options.php'; // Standard way
}
}
?>
';
?>
'; ?>
[ ' . __( 'Know more', 'wp-cerber' ) . ' ]';
}
// Unconditionally required
$req = $config['required'] ?? false;
if ( $req ) {
$atts .= ' required="required" ';
}
// Conditionally (when enabled) required
$required = $config['validate']['required'] ?? false;
if ( $required ) {
$atts .= ' data-input_required="1" ';
}
$placeholder = $config['placeholder'] ?? '';
if ( ! $placeholder && ( $required || $req ) ) {
$placeholder = __( 'Required', 'wp-cerber' );
}
if ( $placeholder ) {
$atts .= ' placeholder="' . crb_attr_escape( $placeholder ) . '" ';
}
if ( isset( $config['disabled'] ) ) {
$atts .= ' disabled="disabled" ';
}
$value = $config['value'] ?? '';
$setting = $config['setting'] ?? '';
if ( $setting ) {
if ( ! $value && isset( $settings[ $setting ] ) ) {
$value = $settings[ $setting ];
}
if ( ( $setting == 'loginnowp' || $setting == 'loginpath' ) && ! cerber_is_permalink_enabled() ) {
$atts .= ' disabled="disabled" ';
}
if ( $setting == 'loginpath' ) {
$pre = cerber_get_home_url() . '/';
$value = urldecode( $value );
}
}
$value = crb_attr_escape( $value );
$value = crb_format_field_value( $value, $config );
$name_prefix = 'cerber-' . $config['group'];
$name = $name_prefix . '[' . $setting . ']';
$id = $config['id'] ?? CRB_FIELD_PREFIX . $setting;
$class = $config['input_class'] ?? '';
$data_atts = '';
$ena_atts = array();
if ( isset( $config['enabler'] ) ) {
$ena_atts['input_enabler'] = CRB_FIELD_PREFIX . $config['enabler'][0];
if ( isset( $config['enabler'][1] ) ) {
$ena_atts['input_enabler_value'] = $config['enabler'][1];
}
foreach ( $ena_atts as $att => $val ) {
$data_atts .= ' data-' . $att . '="' . $val . '"';
}
}
switch ( $config['type'] ) {
case 'limitz':
$s1 = $config['group'] . '-period';
$s2 = $config['group'] . '-number';
$s3 = $config['group'] . '-within';
$html = sprintf( $label,
cerber_digi_field( $name_prefix . '[' . $s1 . ']', $settings[ $s1 ] ),
cerber_digi_field( $name_prefix . '[' . $s2 . ']', $settings[ $s2 ] ),
cerber_digi_field( $name_prefix . '[' . $s3 . ']', $settings[ $s3 ] ) );
break;
case 'attempts':
$html = sprintf( __( '%s retries are allowed within %s minutes', 'wp-cerber' ),
cerber_digi_field( $name_prefix . '[attempts]', $settings['attempts'] ),
cerber_digi_field( $name_prefix . '[period]', $settings['period'] ) );
break;
case 'reglimit':
$html = sprintf( __( '%s registrations are allowed within %s minutes from one IP address', 'wp-cerber' ),
cerber_digi_field( $name_prefix . '[reglimit_num]', $settings['reglimit_num'] ),
cerber_digi_field( $name_prefix . '[reglimit_min]', $settings['reglimit_min'], 4, 4 ) );
break;
case 'aggressive':
$html = sprintf( __( 'Increase lockout duration to %s hours after %s lockouts in the last %s hours', 'wp-cerber' ),
cerber_digi_field( $name_prefix . '[agperiod]', $settings['agperiod'] ),
cerber_digi_field( $name_prefix . '[aglocks]', $settings['aglocks'] ),
cerber_digi_field( $name_prefix . '[aglast]', $settings['aglast'] ) );
break;
case 'notify':
$html = ' '
. __( 'Send notification if the number of active lockouts above', 'wp-cerber' ) . ' ' .
cerber_digi_field( $name_prefix . '[above]', $settings['above'] ) .
crb_test_notify_link( array( 'channel' => 'email' ) );
break;
case 'citadel':
$html = sprintf( __( 'Enable after %s failed login attempts in the last %s minutes', 'wp-cerber' ),
cerber_digi_field( $name_prefix . '[cilimit]', $settings['cilimit'] ),
cerber_digi_field( $name_prefix . '[ciperiod]', $settings['ciperiod'] ) . ' ' );
break;
case 'checkbox':
$html = '
';
//$html .= '' . $label . '
';
if ( $label ) {
$html .= '' . $label . '
';
}
if ( $data_atts ) {
$html .= ' ';
}
break;
case 'textarea':
$html = '';
if ( $label ) {
$html .= '' . $label . ' ';
}
break;
case 'select':
$html = cerber_select( $name, $config['set'], $value, $class, $id, '', $placeholder, $ena_atts );
if ( $label ) {
$html .= '' . $label . ' ';
}
break;
case 'role_select':
if ( $label ) {
$label = '' . $label . '
';
}
$html = $label . '' . cerber_role_select( $name . '[]', $value, '', true, '' ) . '
';
break;
case 'checkbox_set':
if ( $label ) {
$label = '' . $label . '
';
}
$html = '' . $label;
foreach ( $config['set'] as $key => $item ) {
$v = ( ! empty( $value[ $key ] ) ) ? $value[ $key ] : 0;
$html .= ' ' . $item . ' ';
}
$html .= '
';
break;
case 'reptime':
$html = cerber_time_select( $config, $settings ) . ' ';
break;
case 'timepicker':
$html = ' ';
$html .= ' ' . $label . ' ';
break;
case 'hidden':
$html = ' ';
break;
case 'text':
default:
$type = $config['type'] ?? 'text';
if ( in_array( $type, array( 'url', 'number', 'email' ) ) ) {
$input_type = $type;
}
else {
$input_type = 'text';
}
$size = '';
$class = '';
if ( $type == 'digits' ) {
$size = '3';
$class = 'crb-digits';
}
$size = $config['size'] ?? $size;
$maxlength = $config['maxlength'] ?? $size;
if ( $maxlength ) {
$maxlength = ' maxlength="' . $maxlength . '" ';
}
if ( $size ) {
$size = ' size="' . $size . '"';
}
else {
$class = 'crb-wide';
}
if ( isset( $config['pattern'] ) ) {
$atts .= ' pattern="' . $config['pattern'] . '"';
}
if ( isset( $config['attr'] ) ) {
foreach ( $config['attr'] as $at_name => $at_value ) {
$atts .= ' ' . $at_name . ' ="' . $at_value . '" ';
}
}
else {
if ( isset( $config['title'] ) ) {
$atts .= ' title="' . $config['title'] . '"';
}
}
$html = $pre . ' ';
if ( $label ) {
if ( ! $size || crb_array_get( $config, 'label_pos' ) == 'below' ) {
$label = '' . $label . ' ';
}
else {
$label = ' ' . $label . ' ';
}
}
$html .= $label;
break;
}
if ( $loh = $config['act_relation'] ?? false ) {
foreach ( $loh as $item ) {
if ( in_array( $value, $item[0] ) ) {
$html .= '[ ' . $item[2] . ' ] ';
}
}
}
if ( ! empty( $config['field_switcher'] ) ) {
$name = 'cerber-' . $config['group'] . '[' . $setting . '-enabled]';
$value = $settings[ $setting . '-enabled' ] ?? 0;
$checkbox = '' . $config['field_switcher'] . ' ';
$html = $checkbox . ' ' . $html;
}
echo '';
echo $html;
if ( ! empty( $config['callback_under'] )
&& $content = call_user_func( $config['callback_under'] ) ) {
echo '
';
echo $content;
echo '
';
}
echo "
\n";
}
function cerber_role_select( $name = 'cerber-roles', $selected = array(), $class = '', $multiple = '', $placeholder = '', $width = '75%' ) {
if ( ! is_array( $selected ) ) {
$selected = array( $selected );
}
if ( ! $placeholder ) {
$placeholder = __( 'Select one or more roles', 'wp-cerber' );
}
$roles = wp_roles();
$options = array();
foreach ( $roles->get_names() as $key => $title ) {
$s = ( in_array( $key, $selected ) ) ? 'selected' : '';
$options[] = '' . $title . ' ';
}
$m = ( $multiple ) ? 'multiple="multiple"' : '';
// Setting width via class is not working
$style = '';
if ( $width ) {
$style = 'width: ' . $width.';';
}
return ' ' . implode( "\n", $options ) . ' ';
}
function cerber_time_select($args, $settings){
// Week
$php_week = array(
__( 'Sunday' ),
__( 'Monday' ),
__( 'Tuesday' ),
__( 'Wednesday' ),
__( 'Thursday' ),
__( 'Friday' ),
__( 'Saturday' ),
);
$field = $args['setting'].'-day';
$selected = $settings[ $field ] ?? '';
$ret = cerber_select( 'cerber-' . $args['group'] . '[' . $field . ']', $php_week, $selected );
$ret .= ' ' . _x( 'at', 'preposition of time like: at 11:00', 'wp-cerber' ) . ' ';
// Hours
$hours = array();
for ( $i = 0; $i <= 23; $i ++ ) {
$hours[] = str_pad( $i, 2, '0', STR_PAD_LEFT ) . ':00';
}
$field = $args['setting'] . '-time';
$selected = $settings[ $field ] ?? '';
$ret .= cerber_select( 'cerber-' . $args['group'] . '[' . $field . ']', $hours, $selected );
return $ret . crb_test_notify_link( array( 'type' => 'report' ), __( 'Click to send now', 'wp-cerber' ) );
}
function cerber_checkbox( $name, $value, $label = '', $id = '', $atts = '' ) {
if ( ! $id ) {
$id = CRB_FIELD_PREFIX . $name;
}
return '
' . $label . '
';
}
function cerber_digi_field( $name, $value = '', $size = '3', $maxlength = '3', $id = '' ) {
return cerber_txt_field( $name, $value, $id, $size, $maxlength, '\d+', 'crb-digits' );
}
function cerber_txt_field( $name, $value = '', $id = '', $size = '', $maxlength = '', $pattern = '', $class = '' ) {
$atts = '';
if ( $id ) {
$atts .= ' id="' . $id . '" ';
}
if ( $class ) {
$atts .= ' class="' . $class . '" ';
}
if ( $size ) {
$atts .= ' size="' . $size . '" ';
}
if ( $maxlength ) {
$atts .= ' maxlength="' . $maxlength . '" ';
}
if ( $pattern ) {
$atts .= ' pattern="' . $pattern . '" ';
}
return ' ';
}
function cerber_nonce_field( $action = 'control', $echo = false ) {
$sf = '';
if ( nexus_is_valid_request() ) {
$sf = ' ';
}
$nf = wp_nonce_field( $action, 'cerber_nonce', false, false );
if ( ! $echo ) {
return $nf . $sf;
}
echo $nf . $sf;
}
function crb_admin_submit_button( $text = '', $echo = false ) {
if ( ! $text ) {
$text = __( 'Save Changes' );
}
$d = '';
$hint = '';
if ( nexus_is_valid_request() && ! nexus_is_granted( 'submit' ) ) {
$d = 'disabled="disabled"';
$hint = ' not available in the read-only mode';
}
$html = ' ' . $hint . '
';
if ( $echo ) {
echo $echo;
}
return $html;
}
/*
Sanitizing users input for Main Settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT, function ($new, $old, $option) {
$ret = cerber_set_boot_mode( $new['boot-mode'], $old['boot-mode'] );
if ( crb_is_wp_error( $ret ) ) {
cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' ' . $ret->get_error_message() );
cerber_admin_notice( __( 'Plugin initialization mode has not been changed', 'wp-cerber' ) );
$new['boot-mode'] = $old['boot-mode'];
}
$new['attempts'] = absint( $new['attempts'] );
$new['period'] = absint( $new['period'] );
$new['lockout'] = absint( $new['lockout'] );
$new['agperiod'] = absint( $new['agperiod'] );
$new['aglocks'] = absint( $new['aglocks'] );
$new['aglast'] = absint( $new['aglast'] );
if ( cerber_is_permalink_enabled() ) {
$new['loginpath'] = urlencode( str_replace( '/', '', $new['loginpath'] ) );
$new['loginpath'] = sanitize_text_field( $new['loginpath'] );
if ( $new['loginpath'] ) {
if ( $new['loginpath'] == 'wp-admin'
|| preg_match( '/[#|\.\!\?\:\s]/', $new['loginpath'] ) ) {
cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' You may not set this value for Custom login URL. Please specify another one.' );
$new['loginpath'] = $old['loginpath'];
}
elseif ( $new['loginpath'] != $old['loginpath'] ) {
$href = cerber_get_home_url() . '/' . $new['loginpath'] . '/';
$url = urldecode( $href );
$msg = array();
$msg_e = array();
$msg[] = __( 'Attention! You have changed the login URL! The new login URL is', 'wp-cerber' ) . ': ' . $url . ' ';
$msg_e[] = __( 'Attention! You have changed the login URL! The new login URL is', 'wp-cerber' ) . ': ' . $url;
$msg[] = __( 'If you use a caching plugin, you have to add your new login URL to the list of pages not to cache.', 'wp-cerber' );
$msg_e[] = __( 'If you use a caching plugin, you have to add your new login URL to the list of pages not to cache.', 'wp-cerber' );
cerber_admin_notice( $msg );
cerber_send_notification( 'newlurl', array( 'text' => $msg_e ) );
}
}
}
else {
$new['loginpath'] = '';
$new['loginnowp'] = 0;
}
if ( $new['loginnowp'] && empty( $new['loginpath'] ) && ! class_exists( 'WooCommerce' ) ) {
cerber_admin_notice( array(
'' . __( 'Heads up!' ) . ' ',
__( 'You have disabled the default login page. Ensure that you have configured an alternative login page. Otherwise, you will not be able to log in.', 'wp-cerber' )
) );
}
$new['ciduration'] = absint( $new['ciduration'] );
$new['cilimit'] = absint( $new['cilimit'] );
$new['cilimit'] = $new['cilimit'] == 0 ? '' : $new['cilimit'];
$new['ciperiod'] = absint( $new['ciperiod'] );
$new['ciperiod'] = $new['ciperiod'] == 0 ? '' : $new['ciperiod'];
if ( ! $new['cilimit'] ) {
$new['ciperiod'] = '';
}
if ( ! $new['ciperiod'] ) {
$new['cilimit'] = '';
}
$new['keeplog'] = absint( $new['keeplog'] );
if ( $new['keeplog'] == 0 ) {
$new['keeplog'] = 1;
}
if ( $new['cookiepref'] != $old['cookiepref'] ) {
crb_update_cookie_dependent();
}
return $new;
}, 10, 3 );
/*
Sanitizing/checking user input for anti-spam tab settings
*/
add_filter( 'pre_update_option_' . CERBER_OPT_A, function ( $new, $old, $option ) {
if ( empty( $new['botsany'] ) && empty( $new['botscomm'] ) && empty( $new['botsreg'] ) ) {
update_site_option( 'cerber-antibot', '' );
}
$warn = false;
if ( ! empty( $new['botsany'] )
&& crb_array_get( $new, 'botsany' ) != crb_array_get( $old, 'botsany' ) ) {
$warn = true;
}
if ( ! empty( $new['botscomm'] )
&& crb_array_get( $new, 'botscomm' ) != crb_array_get( $old, 'botscomm' ) ) {
$warn = true;
}
if ( ! empty( $new['customcomm'] ) ) {
if ( ! crb_get_compiled( 'custom_comm_slug' ) ) {
crb_update_compiled( 'custom_comm_slug', crb_random_string( 20, 30 ) );
crb_update_compiled( 'custom_comm_mark', crb_random_string( 20, 30 ) );
$warn = true;
}
}
else {
if ( crb_get_compiled( 'custom_comm_slug' ) ) {
crb_update_compiled( 'custom_comm_slug', '' );
$warn = true;
}
}
if ( $warn ) {
cerber_admin_notice( array(
'' . __( 'Important note if you have a caching plugin in place', 'wp-cerber' ) . ' ',
__( 'To avoid false positives and get better anti-spam performance, please clear the plugin cache.', 'wp-cerber' )
) );
}
return $new;
}, 10, 3 );
/*
Sanitizing/checking user input for reCAPTCHA tab settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT_C, function ($new, $old, $option) {
// Check ability to make external HTTP requests
if ( ! empty( $new['sitekey'] ) && ! empty( $new['secretkey'] ) ) {
if ( ( ! $goo = get_wp_cerber()->reCaptchaRequest( '1' ) )
|| ! isset( $goo['success'] ) ) {
cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' ' . cerber_get_labels( 'status', 534 ) );
}
}
$new['recaptcha-period'] = absint( $new['recaptcha-period'] );
$new['recaptcha-number'] = absint( $new['recaptcha-number'] );
$new['recaptcha-within'] = absint( $new['recaptcha-within'] );
return $new;
}, 10, 3 );
/*
Sanitizing/checking user input for Notifications tab settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT_N, function ($new, $old, $option) {
$emails = cerber_text2array( $new['email'], ',' );
$new['email'] = array();
foreach ( $emails as $item ) {
if ( is_email( $item ) ) {
$new['email'][] = $item;
}
else {
cerber_admin_notice( __( 'ERROR : please enter a valid email address.' ) );
}
}
$emails = cerber_text2array( $new['email-report'], ',' );
$new['email-report'] = array();
foreach ( $emails as $item ) {
if ( is_email( $item ) ) {
$new['email-report'][] = $item;
}
else {
cerber_admin_notice( __( 'ERROR : please enter a valid email address.' ) );
}
}
$new['emailrate'] = absint( $new['emailrate'] );
// When we install a new token, we set proper default value for the device setting
if ( $new['pbtoken'] != $old['pbtoken'] ) {
if ( ! $new['pbtoken'] ) {
$new['pbdevice'] = '';
}
else {
$list = cerber_pb_get_devices( $new['pbtoken'] );
if ( is_array( $list ) && ! empty( $list ) ) {
$new['pbdevice'] = 'all';
}
else {
$new['pbdevice'] = '';
}
}
}
return $new;
}, 10, 3 );
/*
Sanitizing/checking user input for Hardening tab settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT_H, function ($new, $old, $option) {
$new['restwhite'] = cerber_text2array( $new['restwhite'], "\n", function ( $v ) {
$v = preg_replace( '/[^a-z_\-\d\/]/i', '', $v );
return trim( $v, '/' );
} );
$result = cerber_htaccess_sync( 'main', $new );
if ( crb_is_wp_error( $result ) ) {
$new['adminphp'] = $old['adminphp'];
cerber_admin_notice( $result->get_error_message() );
}
$result = cerber_htaccess_sync( 'media', $new );
if ( crb_is_wp_error( $result ) ) {
$new['phpnoupl'] = $old['phpnoupl'];
cerber_admin_notice( $result->get_error_message() );
}
return $new;
}, 10, 3 );
/*
Sanitizing/checking user input for Traffic Inspector tab settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT_T, function ($new, $old, $option) {
$new['tiwhite'] = cerber_text2array( $new['tiwhite'], "\n" );
foreach ( $new['tiwhite'] as $item ) {
if ( strrpos( $item, '?' ) ) {
cerber_admin_notice( 'You may not specify the query string with a question mark: ' . htmlspecialchars( $item, ENT_SUBSTITUTE ) );
}
if ( strrpos( $item, '://' ) ) {
cerber_admin_notice( 'You may not specify the full URL: ' . htmlspecialchars( $item, ENT_SUBSTITUTE ) );
}
}
if ( $new['tithreshold'] ) {
$new['tithreshold'] = absint( $new['tithreshold'] );
}
$new['tikeeprec'] = absint( $new['tikeeprec'] );
if ( $new['tikeeprec'] == 0 ) {
$new['tikeeprec'] = 1;
cerber_admin_notice( 'You may not set Keep records for to 0 days. To completely disable logging set Logging mode to Logging disabled.' );
}
return $new;
}, 10, 3 );
add_filter( 'pre_update_option_' . CERBER_OPT_US, function ( $new, $old, $option ) {
if ( ! empty( $new['ds_4acc'] ) ) {
CRB_DS::enable_shadowing( 1 );
}
else {
CRB_DS::disable_shadowing( 1 );
}
if ( ! empty( $new['ds_4roles'] ) ) {
CRB_DS::enable_shadowing( 2 );
}
else {
CRB_DS::disable_shadowing( 2 );
}
return $new;
}, 10, 3 );
add_filter( 'pre_update_option_' . CERBER_OPT_OS, function ( $new, $old, $option ) {
if ( ! empty( $new['ds_4opts'] ) ) {
CRB_DS::enable_shadowing( 3 );
}
else {
CRB_DS::disable_shadowing( 3 );
}
return $new;
}, 10, 3 );
/*
Sanitizing/checking user input for Security Scanner settings
*/
add_filter( 'pre_update_option_' . CERBER_OPT_S, function ( $new, $old, $option ) {
$new['scan_exclude'] = cerber_normal_dirs( $new['scan_exclude'] );
return $new;
}, 10, 3 );
/*
Sanitizing/checking user input for Scanner Schedule settings
*/
add_filter( 'pre_update_option_' . CERBER_OPT_E, function ( $new, $old, $option ) {
$new['scan_aquick'] = absint( $new['scan_aquick'] );
$new['scan_afull-enabled'] = ( empty( $new['scan_afull-enabled'] ) ) ? 0 : 1;
$sec = cerber_sec_from_time( $new['scan_afull'] );
if ( ! $sec || ! ( $sec >= 0 && $sec <= 86400 ) ) {
$new['scan_afull'] = '01:00';
}
$emails = cerber_text2array( $new['email-scan'], ',' );
$new['email-scan'] = array();
foreach ( $emails as $item ) {
if ( is_email( $item ) ) {
$new['email-scan'][] = $item;
}
else {
cerber_admin_notice( __( 'ERROR : please enter a valid email address.' ) );
}
}
if ( lab_lab() ) {
if ( cerber_cloud_sync( $new ) ) {
cerber_admin_message( __( 'The schedule has been updated', 'wp-cerber' ) );
}
else {
cerber_admin_message( __( 'Unable to update the schedule', 'wp-cerber' ) );
}
}
return $new;
}, 10, 3 );
add_filter( 'pre_update_option_' . CERBER_OPT_P, function ( $new, $old, $option ) {
$new['scan_delexdir'] = cerber_normal_dirs($new['scan_delexdir']);
return $new;
}, 10, 3 );
/**
* Let's sanitize and normalize them all
* @since 4.1
*
*/
add_filter( 'pre_update_option', 'cerber_o_o_sanitizer', 10, 3 );
function cerber_o_o_sanitizer( $value, $option, $old_value ) {
if ( ! in_array( $option, cerber_get_setting_list() ) ) {
return $value;
}
$pre_sanitize = $value;
$defs = crb_get_settings_def();
// TODO: Is there any case when $value is not array???
if ( is_array( $value ) ) {
// Parsing settings, applying formatting, etc.
foreach ( $value as $setting => &$setting_val ) {
if ( ! $conf = crb_array_get( $defs, $setting ) ) {
continue;
}
if ( $enabler = $conf['enabler'] ?? '' ) {
if ( crb_check_enabler( $conf, $value[ $enabler[0] ] ) ) {
continue;
}
}
$callback = crb_array_get( $conf, 'apply' );
$regex = crb_array_get( $conf, 'regex_filter' ); // Filtering out not allowed chars
$validate = crb_array_get( $conf, 'validate' );
if ( isset( $conf['list'] ) ) {
// Process the values
$setting_val = cerber_text2array( $setting_val, $conf['delimiter'], $callback, $regex );
// Remove not allowed values
global $_deny;
if ( $_deny = crb_array_get( $conf, 'deny_filter' ) ) {
$setting_val = array_filter( $setting_val, function ( $e ) {
global $_deny;
return ! in_array( $e, $_deny );
} );
}
}
else {
// Process the value
if ( $callback && is_callable( $callback ) ) {
$setting_val = call_user_func( $callback, $setting_val );
}
if ( $regex ) {
$setting_val = mb_ereg_replace( $regex, '', $setting_val );
}
// Validating the value
if ( $validate ) {
$field_name = $conf['title'] ?? $conf['label'] ?? 'Unknown field';
$error_msg = '';
if ( ! empty( $validate['required'] )
&& ! $setting_val ) {
$error_msg = sprintf( __( 'Field %s may not be empty', 'wp-cerber' ), '' . $field_name . ' ' );
}
elseif ( $setting_val
&& ( $sat = $validate['satisfy'] ?? false )
&& is_callable( $sat )
&& ! call_user_func( $sat, $setting_val ) ) {
$error_msg = sprintf( __( 'Field %s contains an invalid value', 'wp-cerber' ), '' . $field_name . ' ' );
}
if ( $error_msg ) {
cerber_admin_notice( '' . __( 'ERROR:' ) . ' ' . $error_msg );
}
}
}
}
}
if ( is_array( $value ) ) {
array_walk_recursive( $value, function ( &$element, $key ) {
if ( ! is_array( $element ) ) {
$element = sanitize_text_field( (string) $element );
}
} );
}
else {
$value = sanitize_text_field( (string) $value );
}
$value = cerber_normalize( $value, $option );
// Warn the user if we have altered some values the user has entered
$changed = array();
foreach ( $pre_sanitize as $setting => $pre_val ) {
if ( empty( $pre_val ) ) {
continue;
}
if ( is_array( $pre_val ) ) { // Usually checkboxes
if ( json_encode( $pre_val ) !== json_encode( $value[ $setting ] ) ) {
$changed[] = $setting;
}
}
elseif ( is_array( $value[ $setting ] ) ) {
$pre_val = preg_replace( '/\s+/', '', $pre_val );
$san_val = preg_replace( '/\s+/', '', crb_format_field_value( $value[ $setting ], crb_array_get( $defs, $setting ) ) );
if ( $pre_val != $san_val ) {
$changed[] = $setting;
}
}
elseif ( $pre_val != $value[ $setting ] ) {
$changed[] = $setting;
}
}
if ( $changed ) {
$list = array_intersect_key( $defs, array_flip( $changed ) );
$msg = '' . implode( '
', array_column( $list, 'title' ) ) . '
';
cerber_admin_notice( __( 'For safety reasons, prohibited symbols and invalid values have been removed from the following settings. Please check their values.', 'wp-cerber' ) . $msg );
}
return $value;
}
function cerber_normal_dirs( $list = array() ) {
if ( ! is_array( $list ) ) {
$list = cerber_text2array( $list, "\n" );
}
$ready = array();
foreach ( $list as $item ) {
$item = rtrim( cerber_normal_path( $item ), '/\\' ) . DIRECTORY_SEPARATOR;
if ( ! @is_dir( $item ) ) {
$dir = cerber_get_abspath() . ltrim( $item, DIRECTORY_SEPARATOR );
if ( ! @is_dir( $dir ) ) {
cerber_admin_notice( 'Directory does not exist: ' . htmlspecialchars( $item, ENT_SUBSTITUTE ) );
continue;
}
$item = $dir;
}
$ready[] = $item;
}
return $ready;
}
/*
* Save settings on the multisite WP.
* Process POST Form for settings screens.
* Because Settings API doesn't work in multisite mode!
*
*/
function cerber_ms_update() {
if ( ! cerber_is_http_post() || ! isset( $_POST['action'] ) || $_POST['action'] != 'update' ) {
return;
}
if ( ! $wp_id = cerber_get_wp_option_id() ) { // 7.9.7
return;
}
if ( ! cerber_user_can_manage() ) {
return;
}
// See wp_nonce_field() in the settings_fields() function
check_admin_referer($_POST['option_page'].'-options');
$opt_name = 'cerber-' . $wp_id;
$old = (array) get_site_option( $opt_name );
$new = $_POST[ $opt_name ];
$new = apply_filters( 'pre_update_option_' . $opt_name, $new, $old, $opt_name );
$new = cerber_normalize( $new, $opt_name ); // @since 8.5.1
cerber_update_site_option( $opt_name, $new );
}
/**
* An intermediate level for update_site_option() for Cerber's settings.
* Goal: have a more granular control over processing settings.
*
* @since 8.5.9.1
*
* @param string $option_name
* @param $value
*
* @return bool
*/
function cerber_update_site_option( $option_name, $value ) {
$result = update_site_option( $option_name, $value );
cerber_settings_update();
crb_purge_settings_cache();
return $result;
}
/**
* Updates Cerber's settings in a new way
*
* @since 8.6
*
*/
function cerber_settings_update() {
if ( ! cerber_is_http_post()
|| ! $group = crb_get_post_fields( CRB_SETTINGS_GROUP ) ) {
return;
}
// We do not process some specific cases - not a real settings form
if ( defined( 'CRB_NX_SLAVE' ) && $group == CRB_NX_SLAVE ) {
return;
}
if ( ! $remote = nexus_is_valid_request() ) {
if ( ! cerber_user_can_manage() ) {
return;
}
// See wp_nonce_field() in the settings_fields() function
check_admin_referer( $_POST['option_page'] . '-options' );
}
$defs = crb_get_settings_def( $group );
$field_keys = array_keys( $defs );
$diag_log = array();
foreach ( $defs as $id => $config ) {
if ( $msg = crb_array_get( $config, 'diag_log' ) ) {
$diag_log[ $id ] = $msg;
}
}
$fields = array_fill_keys( $field_keys, '' );
$post_fields = crb_get_post_fields( 'cerber-' . $group, array() );
crb_trim_deep( $post_fields );
$post_fields = stripslashes_deep( $post_fields );
crb_sanitize_deep( $post_fields ); // removes all tags
$new_settings = array_merge( $fields, $post_fields );
if ( ( ! $old_settings = get_site_option( CERBER_CONFIG ) )
|| ! is_array( $old_settings ) ) {
$old_settings = array();
}
$settings = array_merge( $old_settings, $new_settings );
$changed = array();
foreach ( $new_settings as $key => $val ) {
if ( ! isset( $old_settings[ $key ] ) || $old_settings[ $key ] !== $val ) {
$changed[] = $key;
}
}
$equal = empty( $changed );
$result = null;
if ( ! $equal ) {
$result = update_site_option( CERBER_CONFIG, $settings );
}
$data = array(
'group' => $group,
'equal' => $equal,
'result' => $result,
'remote' => $remote,
'changed' => $changed,
'new_values' => $new_settings,
'old_values' => $old_settings,
);
if ( $result ) {
crb_journaling( $data, $diag_log );
}
crb_event_handler( 'update_settings', $data );
}
/**
* Logs changes of the plugin settings to the diagnostic log.
* To enable logging, a setting has to have a 'diag_log' field in the setting config.
*
* @param array $data Information about updated settings.
* @param array $list The list of settings that have to be logged to the diagnostic log.
*
* @return void
*
* @since 9.0.1
*/
function crb_journaling( $data, $list ) {
if ( $data['equal'] ) {
return;
}
if ( ! $changed = array_intersect( array_keys( $list ), $data['changed'] ) ) {
return;
}
foreach ( $changed as $key ) {
$what = $list[ $key ];
if ( empty( $data['new_values'][ $key ] ) ) {
$msg = 'Disabled: ' . $what;
}
else {
$msg = 'Enabled: ' . $what;
}
cerber_diag_log( $msg, '*' );
}
}
/**
* Escaping attributes (values) for forms
*
* @param array|string $value
*
* @return array|string
*/
function crb_attr_escape( $value ) {
if ( is_array( $value ) ) {
array_walk_recursive( $value, function ( &$element ) {
$element = crb_escape( $element );
} );
}
else {
$value = crb_escape( $value );
}
return $value;
}
/**
* Helper
*
* @param string $val
*
* @return string Escaped string
*/
function crb_escape( $val ) {
if ( ! $val
|| is_numeric( $val ) ) {
return $val;
}
// the same way as in esc_attr();
return _wp_specialchars( $val, ENT_QUOTES );
}
/**
* Check setting field enabler and returns conditional inputs CSS class
*
* @param array $config The config of a setting field
* @param mixed $enab_val The value of the enabler field
*
* @return string CSS class to be used
*/
function crb_check_enabler( $config, $enab_val ) {
if ( ! isset( $config['enabler'] ) ) {
return '';
}
$enabled = true;
if ( isset( $config['enabler'][1] ) ) {
$target_val = $config['enabler'][1];
if ( 0 === strpos( $target_val, '[' ) ) {
$list = json_decode( $target_val );
if ( ! in_array( $enab_val, $list ) ) {
$enabled = false;
}
}
else {
if ( $enab_val != $target_val ) {
$enabled = false;
}
}
}
else {
if ( empty( $enab_val ) ) {
$enabled = false;
}
}
return ( ! $enabled ) ? ' crb-disable-this' : '';
}
/**
* @param mixed $value
* @param array $config
*
* @return string
*
* @since 9.1.5
*/
function crb_format_field_value( $value, $config ) {
if ( isset( $config['list'] ) ) {
$dlt = $config['delimiter_show'] ?? $config['delimiter'];
$value = cerber_array2text( $value, $dlt );
}
return $value;
} admin/cerber-tools.php 0000644 00000102275 14757753175 0011001 0 ustar 00 ' . __( 'Export settings to the file', 'wp-cerber' ) . '';
$form .= '' . __( 'When you click the button below you will get a configuration file, which you can upload on another site.', 'wp-cerber' ) . '
';
$form .= '' . __( 'What do you want to export?', 'wp-cerber' ) . '
';
$nf = wp_nonce_field( 'crb_import', 'crb_field' );
$form .= '' . __( 'Import settings from the file', 'wp-cerber' ) . ' ';
$form .= '' . __( 'When you click the button below, file will be uploaded and all existing settings will be overridden.', 'wp-cerber' ) . '
';
$form .= '' . __( 'Select file to import.', 'wp-cerber' ) . ' ' . sprintf( __( 'Maximum upload file size: %s.' ), esc_html( size_format( wp_max_upload_size() ) ) );
$form .= '
';
$form .= '' . __( 'Load the default plugin settings', 'wp-cerber' ) . ' ';
$form .= '' . __( 'When you click the button below, the default WP Cerber settings will be loaded. The Custom login URL and Access Lists will not be changed.', 'wp-cerber' ) . '
';
$form .= '' . __( 'To get the most out of WP Cerber, follow these steps:', 'wp-cerber' ) . ' Getting Stared Guide
';
$form .= '
';
$form .= 'Bulk load access list entries ';
$form .= '';
echo $form;
}
/*
Create export file
*/
function crb_do_export() {
global $wpdb;
if ( ! cerber_is_http_get() || ! isset( $_GET['cerber_export'] ) ) {
return;
}
if ( ! cerber_user_can_manage() ) {
wp_die( 'Error!' );
}
$p = cerber_plugin_data();
$data = array( 'cerber_version' => $p['Version'], 'home' => cerber_get_home_url(), 'date' => date( 'd M Y H:i:s' ) );
if ( ! empty( $_GET['exportset'] ) ) {
$data ['options'] = crb_get_settings();
$data ['geo-rules'] = cerber_get_geo_rules();
}
if ( ! empty( $_GET['exportacl'] ) ) {
//$data ['acl'] = cerber_acl_all( 'ip, tag, comments, acl_slice' );
$data ['acl'] = $wpdb->get_results( 'SELECT ip, tag, comments, acl_slice FROM ' . CERBER_ACL_TABLE, ARRAY_N );
}
$file = json_encode( $data );
$file .= '==/' . strlen( $file ) . '/' . crc32( $file ) . '/EOF';
crb_file_headers( 'wpcerber.config', 'text/plain' );
echo $file;
exit;
}
/**
* Import plugin settings from a file
*
*/
function crb_do_import() {
global $wpdb;
if ( ! isset( $_POST['cerber_import'] ) || ! cerber_is_http_post() ) {
return;
}
check_admin_referer( 'crb_import', 'crb_field' );
if ( ! cerber_user_can_manage() ) {
wp_die( 'Import failed.' );
}
// Bulk load ACL
if ( isset( $_POST['acl_text'] ) ) {
if ( ! ( $text = crb_get_post_fields( 'import_acl_entries' ) )
|| ! ( $tag = crb_get_post_fields( 'target_acl', false, 'W|B' ) ) ) {
cerber_admin_notice( 'No data provided' );
return;
}
$text = sanitize_textarea_field( $text );
$list = explode( PHP_EOL, $text );
$count = 0;
foreach ( $list as $line ) {
if ( ! $line ) {
continue;
}
list( $ip, $comment ) = explode( ',', $line . ',', 3 );
$ip = preg_replace( CRB_IP_NET_RANGE, ' ', $ip );
$ip = preg_replace( '/\s+/', ' ', $ip );
if ( ! $ip ) {
continue;
}
if ( $tag == 'B' ) {
if ( ! cerber_can_be_listed( $ip ) ) {
cerber_admin_notice( 'Cannot be blacklisted: ' . $ip );
continue;
}
}
$comment = trim( strip_tags( stripslashes( $comment ) ) );
$result = cerber_acl_add( $ip, $tag, $comment );
if ( $result !== true ) {
$msg = 'SKIPPED: ' . $ip . ' ' . $comment;
if ( crb_is_wp_error( $result ) ) {
$msg .= ' - ' . $result->get_error_message();
}
cerber_admin_notice( $msg );
}
else {
$count ++;
}
}
if ( $count ) {
$msg = $count . ' access list entries were loaded. Manage access lists .';
}
else {
$msg = 'No entries were loaded';
}
cerber_admin_message( $msg );
return;
}
// Import from a file
$ok = true;
if ( ! is_uploaded_file( $_FILES['ifile']['tmp_name'] ) ) {
cerber_admin_notice( __( 'No file was uploaded or file is corrupted', 'wp-cerber' ) );
return;
}
elseif ( $file = file_get_contents( $_FILES['ifile']['tmp_name'] ) ) {
$p = strrpos( $file, '==/' );
$data = substr( $file, 0, $p );
$sys = explode( '/', substr( $file, $p ) );
if ( $sys[3] == 'EOF' && crc32( $data ) == $sys[2] && ( $data = json_decode( $data, true ) ) ) {
if ( isset( $_POST['importset'] ) && $data['options'] && ! empty( $data['options'] ) && is_array( $data['options'] ) ) {
$data['options']['loginpath'] = urldecode( $data['options']['loginpath'] ); // needed for filter cerber_sanitize_m()
if ( $data['home'] != cerber_get_home_url() ) {
$data['options']['sitekey'] = crb_get_settings( 'sitekey' );
$data['options']['secretkey'] = crb_get_settings( 'secretkey' );
}
cerber_save_settings( $data['options'] ); // @since 2.0
if ( isset( $data['geo-rules'] ) ) {
update_site_option( CERBER_GEO_RULES, $data['geo-rules'] );
}
if ( ! empty( $data['options']['crb_role_policies'] ) ) {
update_site_option( CERBER_SETTINGS, array( 'crb_role_policies' => $data['options']['crb_role_policies'] ) );
}
}
if ( isset( $_POST['importacl'] )
&& ! empty( $data['acl'] )
&& is_array( $data['acl'] ) ) {
$acl_ok = true;
if ( false === $wpdb->query( "DELETE FROM " . CERBER_ACL_TABLE ) ) {
$acl_ok = false;
}
foreach ( $data['acl'] as $row ) {
if ( ! cerber_acl_add( $row[0], $row[1], crb_array_get( $row, 2, '' ), crb_array_get( $row, 3, 0 ) ) ) {
$acl_ok = false;
break;
}
}
if ( ! $acl_ok ) {
cerber_admin_notice( __( 'A database error occurred while importing access list entries', 'wp-cerber' ) );
}
cerber_acl_fixer();
}
cerber_upgrade_settings(); // In case it was settings from an older version
cerber_admin_message( __( 'Settings has imported successfully from', 'wp-cerber' ) . ' ' . $_FILES['ifile']['name'] );
}
else {
$ok = false;
}
}
if ( ! $ok ) {
cerber_admin_notice( __( 'Error while parsing file', 'wp-cerber' ) );
}
}
/**
* @return void
*
* @since 8.9.6.3
*/
function crb_show_phpinfo() {
if ( ! cerber_is_admin_page( array( 'tab' => 'diagnostic', 'cerber-show' => 'php_info' ) )
|| ! is_super_admin() ) {
return;
}
phpinfo();
exit();
}
/**
* Displays admin diagnostic page
*/
function cerber_show_diag(){
$sections = array();
cerber_cache_enable();
if ( $d = cerber_environment_diag() ) {
$sections [] = $d;
}
?>
';
echo ''.$section[0].' ';
echo $section[1];
echo '';
}
?>
Repair Cerber\'s Tables ';
crb_show_diag_section( 'Database Info', cerber_db_diag() . $button );
$server = $_SERVER;
if ( ! empty( $server['HTTP_COOKIE'] ) ) {
unset( $server['HTTP_COOKIE'] );
}
if ( ! empty( $server['HTTP_X_COOKIES'] ) ) {
unset( $server['HTTP_X_COOKIES'] );
}
ksort( $server );
$se = array();
foreach ( $server as $key => $value ) {
if ( is_array( $value ) ) {
$se[] = array( $key, cerber_table_view( $key, $value ) );
}
else {
$se[] = array( $key, @strip_tags( $value ) );
}
}
crb_show_diag_section( 'Server Environment Variables', cerber_make_plain_table( $se ) );
$buttons = '
Clear Cache
Recheck Status
';
crb_show_diag_section( 'Cerber Security Cloud Status', lab_status() . $buttons );
crb_show_diag_section( 'Maintenance Tasks', cerber_cron_diag() );
if ( $report = get_site_option( '_cerber_report' ) ) {
$rep = cerber_ago_time( $report[0] ) . ' (' . cerber_date( $report[0] ) . ')';
if ($report[1]) {
$rep .= ' OK | '.get_site_transient( 'crb_hourly_2' );
}
else {
$rep .= ' Unable to send email';
}
crb_show_diag_section( 'Weekly Reports', $rep );
}
if ( $alerts = get_site_option( CRB_ALERTZ ) ) {
$rep = '';
foreach ( $alerts as $hash => $alert ) {
$al_info = array();
if ( ! empty( $alert[13] ) ) {
if ( $alert[13] < time() ) {
$al_info [] = 'Expired';
}
else {
$al_info [] = 'Expires on ' . cerber_date( $alert[13] );
}
}
if ( ! empty( $alert[11] ) ) {
if ( $alert[11] <= $alert[12] ) {
$al_info [] = 'Inactive (limit has reached)';
}
else {
$al_info [] = 'Remains ' . ( $alert[11] - $alert[12] );
}
}
if ( ! empty( $alert[14] ) ) {
$al_info [] = 'Ignore rate limiting';
}
if ( ! empty( $alert[15] ) ) {
$al_info [] = 'Email';
}
if ( ! empty( $alert[16] ) ) {
$al_info [] = 'Mobile';
}
if ( $al_info = implode( ' | ', $al_info ) ) {
$al_info = ' | ' . $al_info;
}
$rep .= 'ID: ' . $hash . ' ' . $al_info . ' | ' . __( 'Delete', 'wp-cerber' ) . ' ';
}
$rep .= ' ';
$rep .= 'Read more on alerts and notifications
';
crb_show_diag_section( 'Alerts', $rep );
}
if ( $status = CRB_DS::get_status() ) {
crb_show_diag_section( 'Data Shield Status', '' . implode( ' ', $status ) . ' ' );
}
crb_show_diag_section( 'WP Cerber Cache', 'Clear
' );
?>
' . $title . ' ' . $content . '
';
}
function cerber_show_lic() {
$key = lab_get_key();
$valid = '';
$site_ip_row = '';
if ( ! empty( $key[2] ) ) {
$lic = $key[2];
if ( lab_validate_lic( $lic, $message, $site_ip ) ) {
$valid = '
This key is valid until ' . $message . '
To move the key to another website or web server, please follow these steps: https://my.wpcerber.com/how-to-move-license-key/
';
}
else {
$message = htmlspecialchars( $message );
$valid = 'This license key is invalid or expired [ i ]
If you believe this key is valid, please follow these steps: https://my.wpcerber.com/how-to-fix-invalid-or-expired-key/
';
}
if ( $site_ip ) {
$site_ip_row = '
Site IP Address
' . $site_ip . '
';
}
}
else {
$lic = '';
}
?>
' . $tz . '!' : $tz;
if ( $c = CRB_Cache::checker() ) {
$c = 'Yes | ' . cerber_date( $c ) . ' (' . cerber_ago_time( $c ) . ') ';
if ( $stat = CRB_Cache::get_stat( true ) ) {
$c .= ' | Cerber\'s entries: ' . count( $stat[1] );
$c .= ' | '.crb_confirmation_link( cerber_admin_link_add( array(
'cerber_admin_do' => 'clear_cache',
) ), 'Clear the cache' );
}
}
else {
$c = 'Not detected';
}
if ( $disabled = @ini_get( 'disable_functions' ) ) {
$disabled = str_replace( ',', ', ', $disabled );
}
$opt = ( is_multisite() ) ? $wpdb->sitemeta : $wpdb->options;
$sys = array(
array( 'Web Server', $_SERVER['SERVER_SOFTWARE'] ),
array( 'PHP version', phpversion() ),
//array( 'Server API', php_sapi_name() ),
array( 'Server API', PHP_SAPI ),
array( 'Server platform', PHP_OS ),
array( 'Memory limit', @ini_get( 'memory_limit' ) ),
array( 'Default PHP timezone', $tz ),
array( 'Disabled PHP functions', $disabled ),
array( 'WordPress version', cerber_get_wp_version() ),
array( 'WordPress locale', cerber_get_wp_locale() ),
array( 'WordPress options DB table', $opt ),
array( 'MySQLi', ( function_exists( 'mysqli_connect' ) ) ? 'YES ' : 'NO ' ),
array( 'MySQL Native Driver (mysqlnd)', ( function_exists( 'mysqli_fetch_all' ) ) ? 'YES ' : 'NO' ),
array( 'PHP allow_url_fopen', ( ini_get( 'allow_url_fopen' ) ) ? 'Enabled ' : 'Disabled ' ),
array( 'PHP allow_url_include', ( ini_get( 'allow_url_include' ) ) ? 'Enabled ' : 'Disabled ' ),
array( 'Persistent object cache', $c ),
array( 'Loaded php.ini file', php_ini_loaded_file() ?: 'Unknown' ),
array( 'Detailed PHP information', 'View phpinfo() ' ),
);
if ( 2 < substr_count( cerber_get_site_url(), '/' ) ) {
$sys[] = array( 'Subfolder WP installation', 'YES' );
$sys[] = array( 'Site URL', cerber_get_site_url() );
$sys[] = array( 'Home URL', cerber_get_home_url() );
}
if ( nexus_is_valid_request() ) {
$sys[] = array( 'The IP address of the master is detected as', cerber_get_remote_ip() );
}
else {
$sys[] = array( 'Your IP address is detected as', cerber_get_remote_ip() . ' (check it on the What Is My IP Address page)' );
}
crb_show_diag_section( 'System Info', cerber_make_plain_table( $sys ) );
$folder = cerber_get_my_folder();
if ( crb_is_wp_error( $folder ) ) {
$folder = $folder->get_error_message();
}
else {
$folder .= 'quarantine' . DIRECTORY_SEPARATOR;
}
if ( file_exists( ABSPATH . 'wp-config.php' )) {
$config = ABSPATH . 'wp-config.php';
}
elseif ( file_exists( dirname( ABSPATH ) . '/wp-config.php' ) ) {
$config = dirname( ABSPATH ) . '/wp-config.php';
}
else {
$config = 'Error. No config file found.';
}
$folders = array(
array( 'WordPress root folder (ABSPATH) ', ABSPATH ),
array( 'WordPress uploads folder', cerber_get_upload_dir() ),
array( 'WordPress content folder', dirname( cerber_get_plugins_dir() ) ),
array( 'WordPress plugins folder', cerber_get_plugins_dir() ),
array( 'WordPress themes folder', cerber_get_themes_dir() ),
array( 'WordPress must-use plugin folder (WPMU_PLUGIN_DIR) ', WPMU_PLUGIN_DIR ),
array( 'WordPress config file', $config ),
array( 'Server folder for temporary files', sys_get_temp_dir() ),
array( 'PHP folder for uploading files', ini_get( 'upload_tmp_dir' ) ),
array( 'PHP folder for user session data', session_save_path() ),
array( 'WP Cerber\'s quarantine folder', $folder ),
array( 'WP Cerber\'s diagnostic log', cerber_get_diag_log() )
);
//$folders[] = array( 'WordPress config file', $config );
if ( file_exists( ABSPATH . '.htaccess' ) ) {
$folders[] = array( 'Main .htaccess file', ABSPATH . '.htaccess' );
}
foreach ( $folders as &$folder ) {
$folder[2] = '';
$folder[3] = '';
if ( @file_exists( $folder[1] ) ) {
if ( wp_is_writable( $folder[1] ) ) {
$folder[2] = 'Writable';
}
else {
$folder[2] = 'Write protected';
}
$folder[3] = cerber_get_chmod( $folder[1] );
}
else {
$folder[2] = 'Not found (no access)';
}
}
$folders[] = array( 'Directory separator', DIRECTORY_SEPARATOR );
crb_show_diag_section( 'File system', cerber_make_plain_table( $folders ) );
if ( is_multisite() ) {
$mu = array();
if ( defined( 'UPLOADS' ) ) {
$mu[] = array( 'UPLOADS', UPLOADS );
}
if ( defined( 'BLOGUPLOADDIR' ) ) {
$mu[] = array( 'BLOGUPLOADDIR', BLOGUPLOADDIR );
}
if ( defined( 'UPLOADBLOGSDIR' ) ) {
$mu[] = array( 'UPLOADBLOGSDIR', UPLOADBLOGSDIR );
}
$mu[] = array( 'Uploads folder for sites', cerber_get_upload_dir_mu() );
crb_show_diag_section( 'Multisite Constants', cerber_make_plain_table( $mu ) );
}
$pls = array();
$list = get_option('active_plugins');
foreach($list as $plugin) {
$data = get_plugin_data(WP_PLUGIN_DIR.'/'.$plugin);
$pls[] = array($data['Name'], $data['Version']);
}
crb_show_diag_section( 'Active Plugins', cerber_make_plain_table( $pls ) );
}
function cerber_make_plain_table( $data, $header = null, $first_header = false, $eq = false ) {
$class = 'crb-monospace ';
if ( $first_header ) {
$class .= ' crb-plain-fh ';
}
if ( ! $eq ) {
$class .= ' crb-plain-fcw ';
}
$ret = '';
if ( $header ) {
$ret .= '';
}
foreach ( $data as $row ) {
$ret .= '' . implode( ' ', $row ) . ' ';
}
$ret .= '
';
return $ret;
}
/*
* Create database diagnostic report
*
*
*/
function cerber_db_diag(){
global $wpdb;
$ret = array();
$db_info = array();
$db_info[] = array( 'Database server', ( $v = cerber_db_get_var( "SELECT VERSION()" ) ) ? $v : 'Unknown' );
$db_info[] = array( 'Database name', DB_NAME );
$var = crb_get_mysql_var( 'innodb_buffer_pool_size' );
$pool_size = round( $var / 1048576 );
$inno = $pool_size . ' MB';
if ( $pool_size < 16 ) {
$inno .= ' Your pool size is extremely small!';
}
elseif ( $pool_size < 64 ) {
$inno .= ' It seems your pool size is too small.';
}
$db_info[] = array( 'InnoDB buffer pool size', $inno );
$var = crb_get_mysql_var( 'max_allowed_packet' );
$db_info[] = array( 'Max allowed packet size', round( $var / 1048576 ) . ' MB' );
$db_info[] = array( 'Charset', $wpdb->charset );
$db_info[] = array( 'Collate', $wpdb->collate );
$ret[] = cerber_make_plain_table($db_info);
/*$tables_info = array();
foreach ( cerber_get_tables() as $table ) {
$tables_info[] = array( $table, $table, 123, 56, 'Details' );
//$ret[] = cerber_table_info( $table );
}
$ret[] = cerber_make_plain_table( $tables_info );*/
$ret[] = cerber_table_info( CERBER_LOG_TABLE );
$ret[] = cerber_table_info( CERBER_ACL_TABLE );
$ret[] = cerber_table_info( CERBER_BLOCKS_TABLE );
$ret[] = cerber_table_info( CERBER_TRAF_TABLE );
$err = '';
if ( $errors = get_site_option( '_cerber_db_errors' ) ) {
$err = 'Some minor DB errors were detected
';
foreach ( $errors as $error ) {
$err .= $error[0] . "\n" . $error[1] . "\n" . cerber_auto_date( $error[2], false ) . "\n------------------------\n";
}
$err .= ' ';
update_site_option( '_cerber_db_errors', '' );
}
return $err . implode( ' ', $ret );
}
/**
* Creates mini report about given database table
*
* @param $table
*
* @return string
*/
function cerber_table_info( $table ) {
global $wpdb;
if (!cerber_is_table($table)){
return 'ERROR. Database table ' . $table . ' not found! Click repair button below.
';
}
$cols = $wpdb->get_results( "SHOW FULL COLUMNS FROM " . $table );
$tb = array();
//$columns = 'Field Type Collation ';
foreach ( $cols as $column ) {
$column = obj_to_arr_deep( $column );
$field = array_shift( $column );
$type = array_shift( $column );
$collation = array_shift( $column );
$tb[] = array( $field, $type, $collation );
//$columns .= '' . $field . ' ' . $type . ' ' . $collation . ' ';
}
//$columns .= '
';
$columns = cerber_make_plain_table( $tb, array( 'Field', 'Type', 'Collation' ) );
$rows = absint( cerber_db_get_var( 'SELECT COUNT(*) FROM ' . $table ) );
$sts = $wpdb->get_row( 'SHOW TABLE STATUS WHERE NAME = "' . $table .'"');
$tb = array();
foreach ( $sts as $key => $value ) {
$tb[] = array( $key, $value );
}
$status = cerber_make_plain_table( $tb, null, true );
$truncate = '';
if ($rows) {
$truncate = ' Delete all rows ';
}
return 'Table: ' . $table . ' , rows: ' . $rows . $truncate. '
' . $columns . ' '. $status.'
';
}
function cerber_environment_diag() {
$issues = array();
if ( version_compare( '7.0', phpversion(), '>' ) ) {
$issues[] = 'Your website runs on an outdated (unsupported) version of PHP which is ' . phpversion() . '. We strongly encourage you to upgrade PHP to a newer version. See more at: http://php.net/supported-versions.php ';
}
if ( ! function_exists( 'http_response_code' ) ) {
$issues[] = 'The PHP function http_response_code() is not found or disabled.';
}
if ( ! function_exists( 'mb_convert_encoding' ) ) {
$issues[] = 'A PHP extension mbstring is not enabled on your website. Some plugin features will not work properly.
You need to enable the PHP mbstring extension (multibyte strings support) in your hosting control panel.';
}
if ( ! is_numeric( $_SERVER['REQUEST_TIME_FLOAT'] ) ) {
$issues[] = 'The server environment variable $_SERVER[\'REQUEST_TIME_FLOAT\'] is not set correctly.';
}
if ( cerber_get_remote_ip() === CERBER_NO_REMOTE_IP ) {
$issues[] = 'WP Cerber is unable to detect IP addresses correctly. How to fix: Solving problem with incorrect IP address detection ';
}
$ret = null;
if ( $issues ) {
$issues = '' . implode( '
', $issues ) . '
';
$ret = array(
' Some issues have been detected. They can affect plugin functionality. ',
$issues
);
}
return $ret;
}
function cerber_cron_diag() {
$planned = array();
$crb_crons = array(
'cerber_hourly_1' => 'Hourly task #1',
'cerber_hourly_2' => 'Hourly task #2',
'cerber_daily' => 'Daily task',
//'cerber_bg_launcher' => 'Background tasks'
);
foreach ( _get_cron_array() as $time => $item ) {
foreach ( $crb_crons as $key => $val ) {
if ( ! empty( $item[ $key ] ) ) {
$planned[ $key ] = $val . ' scheduled for ' . cerber_date( $time ) . ' (' . cerber_ago_time( $time ) . ')';
}
}
}
unset( $crb_crons['cerber_daily'] );
$crb_crons['cerber_daily_1'] = 'Daily task';
$errors = array();
$ok = array();
$no_cron = false;
foreach ( $crb_crons as $key => $task ) {
$h = get_site_transient( $key );
if ( ! $h || ! is_array( $h ) ) {
$errors[] = $task . ' has never been executed';
if ( $oldest = cerber_db_get_var( 'SELECT MIN(stamp) FROM ' . CERBER_LOG_TABLE ) ) {
if ( $oldest < ( time() - 24 * 3600 ) ) {
$no_cron = true;
}
}
continue;
}
if ( empty( $h[1] ) ) {
$errors[] = $task . ' has not finished correctly';
continue;
}
$end = $h[1];
/*
if ( $end < ( time() - 2 * 3600 ) ) {
$errors[] = $val . ' has been executed ' . cerber_ago_time( $end );
}
else {
$ok[] = $val . ' has been executed ' . cerber_ago_time( $end );
}
*/
$dur = $end - $h[0];
if ( $dur > 60 ) {
$errors[] = $task . ' has been executed ' . cerber_ago_time( $end ) . ' and it took ' . $dur . ' seconds.';
}
else {
$ok[] = $task . ' has been executed ' . cerber_ago_time( $end ) . ' and it took ' . $dur . ' seconds.';
}
}
$ret = '';
if ( $errors ) {
$ret .= '' . implode( ' ', $errors ) . '
';
}
if ( $ok ) {
$ret .= '' . implode( ' ', $ok ) . '
';
}
if ( $planned ) {
$ret .= '' . implode( ' ', $planned ) . '
';
}
$num = 0;
if ( $bg = cerber_bg_task_get_all() ) {
$num = count( $bg );
}
$ret .= 'Background tasks: ' . $num . '
';
if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
$ret .= 'Note: the internal WordPress cron launcher is disabled on this site.
';
if ( $no_cron ) {
$ret .= 'An external cron launcher has not been configured or does not work properly.
';
}
}
return $ret;
}
function cerber_show_diag_log() {
$file = cerber_get_diag_log();
if ( ! is_file( $file ) ) {
echo 'The log file has not been created yet.
';
return;
}
if ( ! $fs = filesize( $file ) ) {
echo 'The diagnostic log file is empty.
';
return;
}
$reverse_log = crb_get_query_params( 'reverse_log', '\d' );
$clear = crb_confirmation_link( cerber_admin_link_add( array(
'cerber_admin_do' => 'manage_diag_log',
'do_this' => 'clear_it',
) ), 'Clear the log' );
$dnl = 'Download as a file ';
$reverse = 'Reverse the order ';
// Log file changes
$mtime = cerber_get_date( $file );
$meta = get_user_meta( get_current_user_id(), 'clast_log_view', true );
if ( ! is_array( $meta ) ) {
$meta = array();
}
$change = $meta['last_change'][ $mtime ] ?? '';
if ( ! $change ) {
$bytes = (int) ( $fs - ( $meta['size'] ?? $fs ) );
$change = ( 0 != $bytes ) ? '( ' . sprintf( "%+d", $bytes ) . ' bytes)' : '';
}
$lupd = cerber_auto_date( $mtime ) . ' ' . $change;
unset( $meta['last_change'] ); // Delete outdated entries
$meta['last_change'][ $mtime ] = $change;
$meta['size'] = $fs;
update_user_meta( get_current_user_id(), 'clast_log_view', $meta );
echo 'Size: ' . number_format( $fs, 0, ' ', ' ' ) . ' bytes | Last update: ' . $lupd . '
[ ' . $reverse . ' | ' . $dnl . ' | ' . $clear . ' ]
';
if ( empty( $reverse_log ) ) {
$log = @fopen( $file, 'r' );
$text = fread( $log, 10000000 );
if ( ! $text ) {
return;
}
fclose( $log );
/*$p = strpos( $text, PHP_EOL );
$text = substr( $text, $p + 1 );*/
echo '' . nl2br( htmlspecialchars( $text, ENT_SUBSTITUTE ) ) . ' ';
}
else {
$lines = file( $file );
if ( ! $lines ) {
return;
}
echo '';
for ( $i = count( $lines ) - 1; $i >= 0; $i -- ) {
echo htmlspecialchars( $lines[ $i ], ENT_SUBSTITUTE ) . ' ';
}
echo ' ';
}
}
function cerber_manage_diag_log( $v ) {
if ( $v == 'clear_it' ) {
cerber_truncate_log( 0 );
}
elseif ( $v == 'download' ) {
crb_file_headers( 'wpcerber.log', 'text/plain' );
readfile( cerber_get_diag_log() );
exit;
}
}
function cerber_show_change_log() {
echo '';
if ( ! $log = cerber_parse_change_log() ) {
echo 'File changelog.txt not found';
}
echo implode( ' ', $log );
echo '
';
}
admin/cerber-users.php 0000644 00000114463 14757753175 0011004 0 ustar 00
ID );
$selected = ( empty( $cus['tfm'] ) ) ? 0 : $cus['tfm'];
echo cerber_select( 'cerber_user_2fa', array(
0 => __( 'Determined by user role policies', 'wp-cerber' ),
1 => __( 'Always enabled', 'wp-cerber' ),
2 => __( 'Disabled', 'wp-cerber' ),
), $selected );
?>
ID );
if ( $pin ) :
?>
ID );
$b_msg = ( ! empty( $b['blocked_msg'] ) ) ? $b['blocked_msg'] : '';
$b_note = ( ! empty( $b['blocked_note'] ) ) ? $b['blocked_note'] : '';
$dsp = ( ! $b ) ? 'display:none;' : '';
?>
/>
' . $by_who . '';
}
?>
add( 'invalid-email', 'Invalid email address for Two-factor authentication' );
} );
}
}
else {
$cus['tfemail'] = '';
}
cerber_update_set( CRB_USER_SET, $cus, $user_id );
if ( $cus['tfm'] == 2 ) {
CRB_2FA::delete_2fa( $user_id, true );
}
}
add_filter( 'user_row_actions', 'crb_collect_uids', 10, 2 );
add_filter( 'ms_user_row_actions', 'crb_collect_uids', 10, 2 );
function crb_collect_uids( $actions, $user_object ) {
crb_users_on_the_page( $user_object );
return $actions;
}
function crb_users_on_the_page( $user_object = null ) {
static $list = array();
if ( $user_object ) {
$list[ $user_object->ID ] = $user_object->user_login;
}
else {
return $list;
}
}
add_filter( 'views_users', function ( $views ) {
global $wpdb;
$c = cerber_db_get_var( 'SELECT COUNT(meta_key) FROM ' . $wpdb->usermeta . ' WHERE meta_key = "' . CERBER_BUKEY . '"' );
$t = __( 'Blocked Users', 'wp-cerber' );
if ( $c ) {
$t = '' . $t . ' ';
}
$views['cerber_blocked'] = $t . ' (' . $c . ')';
return $views;
} );
add_filter( 'users_list_table_query_args', function ( $args ) {
if ( isset( $_REQUEST['crb_filter_users'] ) ) {
$args['meta_key'] = CERBER_BUKEY;
$args['meta_compare'] = 'EXISTS';
}
return $args;
} );
function crb_format_user_name( $user ) {
if ( is_integer( $user ) ) {
$user = get_userdata( $user );
}
if ( ! $user ) {
return 'Unknown user';
}
if ( $user->first_name ) {
$ret = $user->first_name . ' ' . $user->last_name;
}
else {
$ret = $user->display_name;
}
return $ret . ' (' . $user->user_login . ')';
}
// Bulk actions
add_filter( "bulk_actions-users", function ( $actions ) {
$actions['cerber_block_users'] = __( 'Block', 'wp-cerber' );
return $actions;
} );
add_filter( "handle_bulk_actions-users", function ( $url ) {
if ( cerber_get_bulk_action() == 'cerber_block_users' ) {
if ( $users = cerber_get_get( 'users', '\d+' ) ) {
foreach ( $users as $user_id ) {
cerber_block_user( absint( $user_id ) );
}
}
else {
// 'No users selected';
}
$preserve = array( 's', 'paged', 'role', 'crb_filter_users' );
$remove = array_diff(
array_keys( crb_get_query_params() ),
$preserve );
$url = remove_query_arg( $remove, $url );
}
return $url;
} );
function cerber_block_user( $user_id, $msg = '', $note = '' ) {
if ( ! is_super_admin() ) {
return;
}
if ( $user_id == get_current_user_id() ) {
return;
}
if ( ( $m = get_user_meta( $user_id, CERBER_BUKEY, true ) )
&& ! empty( $m['blocked'] )
&& $m[ 'u' . $user_id ] == $user_id
&& $m['blocked_msg'] == $msg
&& $m['blocked_note'] == $note ) {
return;
}
if ( ! $m || ! is_array( $m ) ) {
$m = array();
}
if ( empty( $m['blocked'] ) ) {
$m['blocked_time'] = time();
$m['blocked'] = 1;
$m[ 'u' . $user_id ] = $user_id;
$m['blocked_by'] = get_current_user_id();
$m['blocked_ip'] = cerber_get_remote_ip();
}
$m['blocked_msg'] = $msg;
$m['blocked_note'] = $note;
update_user_meta( $user_id, CERBER_BUKEY, $m );
crb_destroy_user_sessions( $user_id );
}
function crb_admin_show_role_policies() {
$roles = wp_roles();
$tabs_config = array();
$policies = crb_get_settings( 'crb_role_policies' );
foreach ( $roles->role_names as $role_id => $name ) {
$tabs_config[ $role_id ] = array(
'title' => $name,
//'desc' => $info,
'content' => crb_admin_role_form( $role_id, crb_array_get( $policies, $role_id ) ),
);
}
crb_admin_show_vtabs( $tabs_config, __( 'Save All Changes', 'wp-cerber' ), array( 'cerber_admin_do' => 'update_role_policies' ) );
}
function crb_admin_role_form( $role_id, $values ) {
$html = '';
return $html;
}
function crb_admin_form_field( $field, $name, $value, $id = '' ) {
$value = crb_attr_escape( $value );
$label = crb_array_get( $field, 'label' );
if ( ! $id ) {
$id = CRB_FIELD_PREFIX . $name;
}
$atts = '';
if ( $field['disabled'] ) {
// || ( ! empty( $field['pro'] ) && ! lab_lab() ) ) {
$atts = ' disabled ';
}
if ( $field['disabled'] ) {
$value = '';
}
if ( isset( $field['placeholder'] ) ) {
$atts .= ' placeholder="' . $field['placeholder'] . '"';
}
$style = '';
if ( isset( $field['width'] ) ) {
$style .= ' width:' . $field['width'];
}
switch ( $field['type'] ) {
case 'checkbox':
return cerber_checkbox( $name, $value, $label, $id, $atts );
break;
case 'select':
return cerber_select( $name, $field['set'], $value, '', $id, '', '', null, $atts );
break;
case 'textarea':
$html = '' . $value . ' ';
if ( $label ) {
$html .= '' . $label . ' ';
}
return $html;
break;
case 'text':
default:
$type = crb_array_get( $field, 'type', 'text' );
//return $pre . ' ';
return ' ';
break;
}
}
function crb_admin_role_config() {
return array(
'access' => array(
'name' => '',
'desc' => '',
'fields' => array(
'nodashboard' => array(
'title' => __( 'Block access to WordPress Dashboard', 'wp-cerber' ),
'type' => 'checkbox',
'disable_role' => 'administrator',
),
'notoolbar' => array(
'title' => __( 'Hide Toolbar when viewing site', 'wp-cerber' ),
'type' => 'checkbox',
),
)
),
'redirect' => array(
'name' => __( 'Redirection rules', 'wp-cerber' ),
'desc' => '',
'fields' => array(
'rdr_login' => array(
'title' => __( 'Redirect user after login', 'wp-cerber' ),
'type' => 'text',
'width' => '100%',
),
'rdr_logout' => array(
'title' => __( 'Redirect user after logout', 'wp-cerber' ),
'type' => 'text',
'width' => '100%',
),
)
),
'misc' => array(
'name' => '',
'desc' => '',
'fields' => array(
'auth_expire' => array(
'title' => __( 'User session expiration time', 'wp-cerber' ),
//'label' => 'minutes',
'placeholder' => 'minutes',
'type' => 'number',
),
'sess_limit' => array(
'title' => __( 'Number of allowed concurrent user sessions', 'wp-cerber' ),
'type' => 'number',
'pro' => 2
),
'sess_limit_policy' => array(
'title' => __( 'When the limit on concurrent user sessions is reached', 'wp-cerber' ),
'type' => 'select',
'set' => array(
0 => __( 'Terminate the oldest user session on a new login', 'wp-cerber' ),
1 => __( 'Deny further login attempts', 'wp-cerber' ),
),
'pro' => 2
),
'sess_limit_msg' => array(
//'title' => __( 'User message', 'wp-cerber' ),
'label' => __( 'Display this message if an attempt to log in is denied because the limit on concurrent user sessions has been reached', 'wp-cerber' ),
'type' => 'textarea',
'placeholder' => __( 'You are not allowed to log in. Ask your administrator for assistance.', 'wp-cerber' ),
'enabler' => array( 'sess_limit_policy', 1 ),
'pro' => 2
),
'app_pwd' => array(
'title' => __( 'Application Passwords', 'wp-cerber' ),
'type' => 'select',
'set' => array(
0 => __( 'Use global policies', 'wp-cerber' ),
1 => __( 'Enabled, access to API using standard user passwords is allowed', 'wp-cerber' ),
2 => __( 'Enabled, no access to API using standard user passwords', 'wp-cerber' ),
3 => __( 'Disabled', 'wp-cerber' ),
),
'pro' => 2
),
)
),
'twofactor' => array(
'name' => __( 'Two-Factor Authentication', 'wp-cerber' ),
'desc' => '',
'fields' => array(
'2famode' => array(
'title' => __( 'Two-factor authentication', 'wp-cerber' ),
'type' => 'select',
'set' => array(
0 => __( 'Disabled', 'wp-cerber' ),
1 => __( 'Always enabled', 'wp-cerber' ),
2 => __( 'Advanced mode', 'wp-cerber' )
),
),
'2fasmart' => array(
'title' => __( 'Enforce two-factor authentication if any of the following conditions is true', 'wp-cerber' ),
'type' => 'html',
'enabler' => array( '2famode', 2 ),
'pro' => 1
),
'2fanewcountry' => array(
'title' => __( 'Login from a different country', 'wp-cerber' ),
'type' => 'checkbox',
'enabler' => array( '2famode', 2 ),
'pro' => 1
),
'2fanewnet4' => array(
'title' => __( 'Login from a different network Class C', 'wp-cerber' ),
'type' => 'checkbox',
'enabler' => array( '2famode', 2 ),
'pro' => 1
),
'2fanewip' => array(
'title' => __( 'Login from a different IP address', 'wp-cerber' ),
'type' => 'checkbox',
'enabler' => array( '2famode', 2 ),
'pro' => 1
),
'2fanewua' => array(
'title' => __( 'Login from a different browser or device', 'wp-cerber' ),
'type' => 'checkbox',
'enabler' => array( '2famode', 2 ),
'pro' => 1
),
'2fasessions' => array(
'title' => __( 'If the number of concurrent user sessions is greater', 'wp-cerber' ),
'type' => 'number',
'enabler' => array( '2famode', 2 ),
'pro' => 1
),
'note2' => array(
'title' => __( 'Enforce two-factor authentication with fixed intervals', 'wp-cerber' ),
'type' => 'html',
'enabler' => array( '2famode', 2 ),
'pro' => 1
),
'2fadays' => array(
'title' => __( 'Regular time intervals (days)', 'wp-cerber' ),
'type' => 'number',
//'label' => __( 'days interval', 'wp-cerber' ),
'enabler' => array( '2famode', 2 ),
'pro' => 1
),
'2falogins' => array(
'title' => __( 'Fixed number of logins', 'wp-cerber' ),
'type' => 'number',
'label' => __( 'number of logins', 'wp-cerber' ),
'enabler' => array( '2famode', 2 ),
'pro' => 1
),
)
),
);
}
function crb_admin_save_role_policies( $post ) {
$roles = wp_roles();
$policies = array();
foreach ( $roles->role_names as $role_id => $name ) {
$policies[ $role_id ] = $post[ $role_id ];
}
array_walk_recursive( $policies, function ( &$element, $key ) {
$element = trim( $element );
if ( $key == 'rdr_logout' ) {
if ( false !== strrpos( $element, 'wp-admin' ) ) {
$element = '';
}
}
if ( $element && in_array( $key, array( 'rdr_login', 'rdr_logout' ) ) ) {
if ( substr( $element, 0, 4 ) != 'http'
&& $element[0] != '/' ) {
$element = '/' . $element;
}
}
if ( ! is_array( $element )
&& ! is_numeric( $element ) ) {
$element = sanitize_text_field( (string) $element );
}
} );
if ( ! $settings = get_site_option( CERBER_SETTINGS ) ) {
$settings = array();
}
$settings['crb_role_policies'] = $policies;
if ( cerber_update_site_option( CERBER_SETTINGS, $settings ) ) {
cerber_admin_message( __( 'Policies have been updated', 'wp-cerber' ) );
}
}
function crb_destroy_user_sessions( $user_id ) {
if ( ! $user_id || get_current_user_id() == $user_id ) {
return;
}
$manager = WP_Session_Tokens::get_instance( $user_id );
$manager->destroy_all();
}
function crb_admin_is_current_session( $session_id ) {
static $st = null;
if ( $st === null ) {
$st = crb_get_session_token();
}
return ( $session_id === cerber_hash_token( $st ) );
}
function crb_admin_get_user_cell( $user_id = null, $base_url = '', $text = '', $label = '' ) {
static $wp_roles, $user_cache = array();
if ( ! $user_id ) {
return '';
}
$key = $user_id . '-' . sha1( (string) $text . ' ' . (string) $label );
if ( isset( $user_cache[ $key ] ) ) {
return $user_cache[ $key ];
}
if ( ! $user = get_userdata( $user_id ) ) {
if ( ! $user_data = cerber_get_set( 'user_deleted', $user_id ) ) {
$user_cache[ $key ] = 'UID ' . $user_id;
return '';
}
}
else {
$user_data = array( 'roles' => $user->roles, 'display_name' => $user->display_name );
}
if ( ! isset( $wp_roles ) ) {
$wp_roles = wp_roles()->roles;
}
$roles = '';
if ( ! is_multisite() && $user_data['roles'] ) {
$r = array();
foreach ( $user_data['roles'] as $role ) {
$r[] = $wp_roles[ $role ]['name'];
}
$roles = '' . implode( ', ', $r ) . ' ';
}
$lbl = ( $label ) ? '' . $label . ' ' : '';
if ( $base_url ) {
$ret = '' . $user_data['display_name'] . ' ' . $lbl . '' . $roles . '
';
}
else {
$ret = '' . $user_data['display_name'] . ' ' . $lbl . '' . $roles . '
';
}
$ret = '' . $ret . '
';
if ( $avatar = get_avatar( $user_id, 32 ) ) {
$avatar = '' . $avatar . ' ';
}
else {
$avatar = '';
}
$user_cache[ $key ] = '' . $avatar . '' . $ret . $text . '
';
return $user_cache[ $key ];
}
function crb_admin_show_sessions() {
// Helper for WP_List_Table URLs and navigation links
if ( nexus_is_valid_request() ) {
// Add parameters
$add = array( 'paged', 'order', 'orderby' );
foreach ( $add as $param ) {
//if ( $val = crb_array_get( crb_get_query_params(), $param ) ) {
if ( $val = crb_get_query_params( $param ) ) {
$_REQUEST[ $param ] = $val;
$_GET[ $param ] = $val;
}
}
// Correct URL
add_filter( 'set_url_scheme', function ( $url, $scheme, $orig_scheme ) {
return cerber_admin_link( 'sessions' );
}, 10, 3 );
}
echo '';
cerber_nonce_field( 'control', true );
echo ' ';
echo ' ';
echo ' ';
$slaves = new CRB_Sessions_Table();
$slaves->prepare_items();
//$slaves->search_box( 'Search', 'search_id' );
$slaves->display();
echo ' ';
}
// Personal data exporters ----------------------------------------
function crb_pdata_exporter_act( $email_address, $page = 1 ) {
$per_page = 1000; // Rows per step (SQL query)
$limit = ( $per_page * ( absint( $page ) - 1 ) ) . ',' . $per_page;
$data = array();
if ( ( ! $user = get_user_by( 'email', $email_address ) )
|| ! $user->ID
|| ! $rows = cerber_get_log( null, array( 'id' => $user->ID ), null, $limit ) ) {
$done = true;
if ( $page == 1 ) { // Nothing was logged at all
$data[] = array( 'name' => 'Events', 'value' => 'None logged' );
}
}
else {
$done = false; // There are rows to be exported
$labels = cerber_get_labels( 'activity' );
foreach ( $rows as $row ) {
//$value = 'IP: ' . $row->ip . ' | ' . $labels[ $row->activity ];
$value = array( 'IP_ADDRESS' => $row->ip, 'EVENT' => $labels[ $row->activity ] );
if ( $row->user_login ) {
$value['USERNAME'] = $row->user_login;
}
$value = json_encode( $value, JSON_UNESCAPED_UNICODE );
// Format is defined by WordPress
$data[] = array( 'name' => cerber_date( $row->stamp, false ), // First column
'value' => $value // Second column
);
}
}
return crb_pdata_formater( $data, 'cerber-activity', 'Activity Log', $done );
}
function crb_pdata_exporter_trf( $email_address, $page = 1 ) {
$per_page = 500; // Rows per step (SQL query)
$limit = ( $per_page * ( absint( $page ) - 1 ) ) . ',' . $per_page;
$data = array();
if ( ( ! $user = get_user_by( 'email', $email_address ) )
|| ! $user->ID
|| ! $rows = cerber_db_get_results( 'SELECT ip, uri, stamp, request_fields, request_details FROM ' . CERBER_TRAF_TABLE . ' WHERE user_id = ' . $user->ID . ' LIMIT ' . $limit, MYSQL_FETCH_OBJECT ) ) {
$done = true;
if ( $page == 1 ) { // Nothing was logged at all
$data[] = array( 'name' => 'Events', 'value' => 'None logged' );
}
}
else {
$done = false; // There are rows to be exported
$what = crb_get_settings( 'pdata_trf' );
foreach ( $rows as $row ) {
$value = array( 'IP_ADDRESS' => $row->ip );
if ( isset( $what[1] ) ) {
$value['URL'] = $row->uri;
}
if ( isset( $what[2] ) ) {
$fields = crb_auto_decode( $row->request_fields );
if ( ! empty( $fields[1] ) ) {
$value['FORM_FIEDLS'] = $fields[1];
}
}
if ( isset( $what[3] ) ) {
$dets = crb_auto_decode( $row->request_details );
if ( ! empty( $dets[8] ) ) {
$value['COOKIES'] = $dets[8];
}
}
$value = json_encode( $value, JSON_UNESCAPED_UNICODE );
// Format is defined by WordPress
$data[] = array( 'name' => cerber_date( $row->stamp, false ), // First column
'value' => $value // Second column
);
}
}
return crb_pdata_formater( $data, 'cerber-traffic', 'Traffic Log', $done );
}
function crb_pdata_formater( $data = array(), $exp_id = '', $label = '', $done = true ) {
$export_items[] = array(
'group_id' => $exp_id,
'group_label' => $label,
'item_id' => $exp_id,
'data' => $data,
);
return array(
'data' => $export_items,
'done' => $done,
);
}
if ( crb_get_settings( 'pdata_export' ) ) {
add_filter( 'wp_privacy_personal_data_exporters', 'crb_pdata_register_exporters' );
}
function crb_pdata_register_exporters( $exporters ) {
if ( crb_get_settings( 'pdata_act' ) ) {
$exporters['cerber-security-act'] = array(
'exporter_friendly_name' => 'WP Cerber Activity',
'callback' => 'crb_pdata_exporter_act',
);
}
if ( crb_get_settings( 'pdata_trf' ) ) {
$exporters['cerber-security-trf'] = array(
'exporter_friendly_name' => 'WP Cerber Traffic',
'callback' => 'crb_pdata_exporter_trf',
);
}
return $exporters;
}
// Personal data erasers ----------------------------------------
function crb_pdata_eraser( $email_address, $page = 1 ) {
$removed = false;
$retained = false;
$done = true;
$msg = array();
if ( is_super_admin()
&& ( $user = get_user_by( 'email', $email_address ) )
&& $user->ID ) {
cerber_db_query( 'DELETE FROM ' . CERBER_LOG_TABLE . ' WHERE user_id = ' . $user->ID );
cerber_db_query( 'DELETE FROM ' . CERBER_TRAF_TABLE . ' WHERE user_id = ' . $user->ID );
if ( ( $reg = get_user_meta( $user->ID, '_crb_reg_', true ) )
&& ( empty( $reg['user'] ) || $reg['user'] == $user->ID ) ) {
delete_user_meta( $user->ID, '_crb_reg_' );
}
cerber_delete_set( 'user_deleted', $user->ID );
if ( crb_get_settings( 'pdata_sessions' ) ) {
update_user_meta( $user->ID , 'session_tokens', array() );
}
$removed = true;
// Check if removing is OK
if ( cerber_get_log( null, array( 'id' => $user->ID ), null, 1 )
|| cerber_db_get_var( 'SELECT user_id FROM ' . CERBER_TRAF_TABLE . ' WHERE user_id = ' . $user->ID . ' LIMIT 1' ) ) {
$removed = false;
$retained = true;
$done = false;
if ( $page >= 3 ) { // We failed after three attempts
$msg[] = 'WP Cerber is unable to delete rows in its log tables due to a database error. Check the server error log.';
$done = true;
}
}
}
return array(
'items_removed' => $removed,
'items_retained' => $retained,
'messages' => $msg,
'done' => $done,
);
}
if ( crb_get_settings( 'pdata_erase' ) ) {
add_filter( 'wp_privacy_personal_data_erasers', 'crb_pdata_register_eraser' );
}
function crb_pdata_register_eraser( $erasers ) {
$erasers['cerber-security-erase'] = array(
'eraser_friendly_name' => __( 'WP Cerber Personal Data Eraser' ),
'callback' => 'crb_pdata_eraser',
);
return $erasers;
}
/**
* Quick analysis - returns textual info if the user has any issue with logging in (from the WP Cerber point of view).
* Helps to troubleshot user logging in issues.
*
* @param WP_User $user
*
* @return array|false
*
* @since 9.0.2
*/
function crb_get_user_auth_status( $user ) {
$nope = '';
$nope_more = '';
if ( $b = crb_is_user_blocked( $user->ID ) ) {
$nope = crb_user_blocked_by( $b );
$nope_more = htmlspecialchars( $b['blocked_note'] );
return array( $nope, $nope_more, true );
}
if ( crb_is_username_prohibited( $user->user_login ) ) {
$nope = __( 'username is prohibited', 'wp-cerber' );
$nope_more = '' . __( "Check users' settings", 'wp-cerber' ) . ' ';
return array( $nope, $nope_more, true );
}
// Is user's IP blocked?
if ( $user_ip = crb_is_user_ip_blocked( $user ) ) {
$nope = __( 'The IP address of the last failed attempt to log in is blocked', 'wp-cerber' );
$remove = cerber_admin_link_add( array( 'cerber_admin_do' => 'lockdel', 'ip' => $user_ip ), true );
$nope_more = array( $user_ip, sprintf( __( 'If necessary, <%s>unblock the IP address<%s>.', 'wp-cerber' ), 'a href="' . $remove . '" onclick="return confirm(\'' . __( 'Are you sure?', 'wp-cerber' ) . '\');"', '/a' ) );
return array( $nope, $nope_more, false );
}
// Was the attempt to log in denied by WP Cerber?
$last_login = cerber_get_last_login( $user->ID );
$last_denied = crb_get_last_failed( $user->user_login, $user->user_login, true );
if ( $last_denied->stamp < $last_login->stamp ) {
return false; // Not relevant anymore, user has logged in
}
if ( $reason = cerber_get_labels( 'status', $last_denied->ac_status ) ) {
$nope = __( 'The last attempt to log in was denied due to the following reason', 'wp-cerber' );
$nope_more = array( $reason );
$knowledge_base = array( CRB_STS_11 => 'antispam', 14 => 'acl', 16 => 'geo' );
if ( $go = crb_array_get( $knowledge_base, $last_denied->ac_status ) ) {
$nope_more[] = sprintf( __( 'If necessary, <%s>check and update settings<%s>.', 'wp-cerber' ), 'a href="' . cerber_admin_link( $go ) . '" target="_blank"', '/a' );
}
}
if ( $nope ) {
return array( $nope, $nope_more, false );
}
return false;
}
/**
* @param string $nope
* @param string|array $nope_more
*
* @return string
*
* @since 9.0.2
*/
function crb_format_user_status( $nope, $nope_more, $prefix = true ) {
$p = ( $prefix ) ? __( 'User is not allowed to log in', 'wp-cerber' ) . ' - ' : '';
$more = '';
if ( $nope_more ) {
if ( ! is_array( $nope_more ) ) {
$nope_more = array( $nope_more );
}
$more = '' . implode( '
', $nope_more ) . '
';
}
return '' . $p . $nope . ' ' . $more;
}
/**
* Detects if the user's IP is blocked due to multiple failed attempts to log in
*
* @param WP_User $user
*
* @return false|string The IP address of the last failed attempt to log in if the IP is blocked
*
* @since 9.0.2
*/
function crb_is_user_ip_blocked( $user ) {
// No blocked IP, no failed attempts
if ( ! cerber_db_get_row( 'SELECT * FROM ' . CERBER_BLOCKS_TABLE . ' LIMIT 1' )
//|| ! $last_failed = cerber_db_get_row( 'SELECT * FROM ' . CERBER_LOG_TABLE . ' WHERE ( user_login = "' . $user->user_login . '" OR user_login = "' . $user->user_email . '" ) AND activity = ' . CRB_EV_LFL . ' ORDER BY stamp DESC LIMIT 1', MYSQL_FETCH_OBJECT ) ) {
|| ! $last_failed = crb_get_last_failed( $user->user_login, $user->user_email ) ) {
return false;
}
// User logged in after several failed attempts - OK
if ( ( $last_login = cerber_get_last_login( $user->ID ) )
&& ( $last_failed->stamp < $last_login->stamp ) ) {
return false;
}
// Is user's IP locked?
if ( ( $block = cerber_get_block( $last_failed->ip ) )
&& $block->reason_id == 701 ) {
return $last_failed->ip;
}
return false;
}
class CRB_Sessions_Table extends WP_List_Table {
private $base_admin;
private $geo;
public function __construct() {
parent::__construct( array(
'singular' => 'Session',
'plural' => 'Sessions',
'ajax' => false,
'screen' => 'cerber_user_sessions' // Without this it does not work
) );
$this->geo = lab_lab();
$this->base_admin = wp_nonce_url( cerber_admin_link( 'sessions' ), 'control', 'cerber_nonce' );
}
// Columns definition
function get_columns() {
return array(
'cb' => ' ', //Render a checkbox instead of text
'ses_user' => __( 'User', 'wp-cerber' ),
//'ses_role' => __( 'Role', 'wp-cerber' ),
'ses_started' => __( 'Created', 'wp-cerber' ),
'ses_expires' => __( 'Expires', 'wp-cerber' ),
'ses_ip' => '
' . __( 'IP Address', 'wp-cerber' ),
'ses_host' => __( 'Host Info', 'wp-cerber' ),
'ses_action' => __( 'Action', 'wp-cerber' ),
);
}
// Sortable columns
function get_sortable_columns() {
return array(
'ses_user' => array( 'user_id', false ), // true means dataset is already sorted by ASC
'ses_started' => array( 'started', false ),
'ses_expires' => array( 'expires', false ),
'ses_ip' => array( 'ip', false ),
);
}
// Bulk actions
function get_bulk_actions() {
return array(
'bulk_session_terminate' => __( 'Terminate session', 'wp-cerber' ),
'bulk_block_user' => __( 'Block user', 'wp-cerber' ),
);
}
protected function extra_tablenav( $which ) {
if ( $which == 'top' ) {
?>
get_pagenum();
if ( $current_page > 1 ) {
$offset = ( $current_page - 1 ) * $per_page;
$limit = ' LIMIT ' . $offset . ',' . $per_page;
}
else {
$limit = 'LIMIT ' . $per_page;
}
// Search
if ( $user_id = crb_array_get( $get, 'filter_user', 0, '\d+' ) ) {
$where[] = 'user_id = ' . $user_id;
}
if ( $ip = stripslashes( crb_array_get( $get, 'search_ip' ) ) ) {
$where[] = 'ip LIKE "%' . preg_replace( '/[^:.\d]/', '', $ip ) . '%"';
}
$where = ( ! empty( $where ) ) ? ' WHERE ' . implode( ' AND ', $where ) : '';
// Retrieving data
$query = 'SELECT SQL_CALC_FOUND_ROWS * FROM ' . cerber_get_db_prefix() . CERBER_USS_TABLE . ' ses JOIN ' . $wpdb->users . ' us ON (ses.user_id = us.ID) ' . $where . $orderby . $limit;
if ( $this->items = cerber_db_get_results( $query ) ) {
$total_items = cerber_db_get_var( 'SELECT FOUND_ROWS()' );
}
if ( ! empty( $term ) ) {
echo '' . __( 'Search results for:', 'wp-cerber' ) . ' “' . htmlspecialchars( $term, ENT_SUBSTITUTE ) . '”
';
}
// Pagination, part 2
$this->set_pagination_args( array(
'total_items' => $total_items,
'per_page' => $per_page,
'total_pages' => ceil( $total_items / $per_page )
) );
}
public function single_row( $item ) {
//$item['user_data'] = get_userdata( $item['user_id'] );
parent::single_row( $item );
}
function column_cb( $item ) {
if ( ! crb_admin_is_current_session( $item['wp_session_token'] ) ) {
return ' ';
}
return '';
}
/**
* @param array $item // not object!
* @param string $column_name
*
* @return string
*/
function column_default( $item, $column_name ) {
//return $item[ $column_name ]; // raw output as is
switch ( $column_name ) {
case 'ses_user':
$label = '';
$links = '';
if ( ! nexus_is_valid_request() ) {
$links = $this->row_actions( array(
'' . __( 'Profile', 'wp-cerber' ) . ' ',
) );
$label = ( crb_admin_is_current_session( $item['wp_session_token'] ) ) ? __( 'You', 'wp-cerber' ) : '';
}
return crb_admin_get_user_cell( $item['user_id'], cerber_admin_link( 'sessions' ), $links, $label );
break;
case 'ses_started':
$logins = cerber_activity_link( array( CRB_EV_LIN ) ) . '&filter_user=' . $item['user_id'];
$set = array(
'' . __( 'All Logins', 'wp-cerber' ) . ' ',
'' . __( 'User Activity', 'wp-cerber' ) . ' '
);
$url = '';
// TODO: make it via AJAX per user row with a click "Details"
if ( $item['session_id'] ) {
/*$log = cerber_db_get_row( 'SELECT * FROM ' . CERBER_LOG_TABLE . ' WHERE session_id = "' . $item['session_id'] . '"' );
if ( $log ) {
$det = explode( '|', $log['details'] );
$url = $det[4];
}*/
}
return '' . cerber_date( $item['started'] ) . ' ' . $this->row_actions( $set );
break;
case 'ses_expires':
return cerber_date( $item['expires'] );
break;
case 'ses_ip':
$set = array(
'' . __( 'Activity', 'wp-cerber' ) . ' ',
'' . __( 'Traffic', 'wp-cerber' ) . ' '
);
return crb_admin_ip_cell( $item['ip'], '', $this->row_actions( $set ) );
break;
case 'ses_host':
$ip_id = cerber_get_id_ip( $item['ip'] );
$ip_info = cerber_get_ip_info( $item['ip'], true );
if ( ! $hostname = crb_array_get( $ip_info, 'hostname_html' ) ) {
$hostname = crb_get_ajax_placeholder( 'hostname', $ip_id );
}
$country = ( $this->geo ) ? '' . crb_country_html( $item['country'], $item['ip'] ) . '
' : '';
return $hostname . $country;
break;
case 'ses_action':
if ( crb_admin_is_current_session( $item['wp_session_token'] ) ) {
return '';
}
$href = $this->base_admin . '&cerber_admin_do=terminate_session&id=' . $item['wp_session_token'] . '&user_id=' . $item['user_id'];
return crb_confirmation_link( $href, __( 'Terminate', 'wp-cerber' ) );
break;
}
return '';
}
function no_items() {
if ( ! empty( $_GET['s'] ) ) {
parent::no_items();
}
else {
echo 'No user sessions found.';
}
}
} admin/cerber-admin.php 0000644 00000131203 14757753175 0010722 0 ustar 00
';
add_action( 'admin_init', function () {
CRB_Globals::admin_init();
if ( cerber_is_wp_ajax() ) {
return;
}
crb_show_phpinfo();
crb_settings_processor();
crb_do_export();
crb_do_import();
crb_delete_alert();
crb_admin_headers();
// @since 8.8.2.3 Workaround: if scheduled (cron) tasks are not executed on the website
$last = get_site_transient( 'cerber_daily_1' );
if ( ! $last
|| ! is_array( $last )
|| $last[0] < ( time() - DAY_IN_SECONDS ) ) {
cerber_bg_task_add( 'cerber_do_hourly_2' );
cerber_bg_task_add( 'cerber_daily_run' );
}
} );
function cerber_assets_dir() {
return cerber_plugin_dir() . '/assets';
}
// Scan dashboard ===========================================================
function cerber_scanner_show_dashboard( $msg = '', $status = 0 ) {
$loader = ( $status ) ? UIS_LOADER_HTML : '';
$stats = cerber_get_stats_html();
$stats = array_shift( $stats );
$note = ( $status ) ? '' : __( 'It seems this website has never been scanned. To start scanning click the button below.', 'wp-cerber' );
?>
' . $note . '
';
}
?>
WordPress ';
$rows[] = 'Must use plugins ';
$rows[] = 'Drop-ins ';
$rows[] = 'Plugins ';
$rows[] = 'Themes ';
$rows[] = 'Uploads folder ';
$rows[] = 'Unattended files ';
echo implode( "\n", $rows );
?>
( time() - 900 )
) {
$msg = __( 'Currently a scheduled scan in progress. Please wait until it is finished.', 'wp-cerber' );
$status = 1;
}
else {
$msg = sprintf( __( 'Previous scan started %s has not been completed. Continue scanning?', 'wp-cerber' ), cerber_date( $scan['started'], false ) );
$status = 2;
}
}
else {
$status = 3;
}
}
$start_quick = ' ';
$start_full = ' ';
$stop = ' ';
$continue = ' ';
$controls = '';
switch ( $status ) {
case 0:
case 3:
$controls = $start_quick . $start_full;
break;
case 1:
$controls = '';
break;
case 2:
$controls = $start_quick . $start_full . $continue;
break;
}
$controls .= $stop;
$class = ( $status ) ? '' : 'crb-scanner-has-data';
echo '';
cerber_scanner_show_dashboard( $msg, $status );
$d = '';
if ( nexus_is_valid_request() && ! nexus_is_granted( 'submit' ) ) {
$d = 'disabled="disabled"';
}
?>
';
}
function cerber_ref_upload_form() {
?>
$console_log,
'cerber_scan_do' => $next_do,
'cerber_scanner' => $scanner,
//'scan' => cerber_get_scan(), // debug only
);
if ( $scan_do != 'continue_scan' ) {
$ret['strings'] = cerber_get_strings();
}
ob_end_clean();
echo json_encode( $ret );
crb_admin_stop_ajax();
}
/**
* File viewer, server side AJAX
*
*/
add_action( 'wp_ajax_cerber_view_file', function () {
cerber_check_ajax_permissions();
$get = crb_get_query_params();
if ( ! $file_name = $get['file'] ) {
crb_admin_stop_ajax( 'Error: Filename not specified. Please run a new malware scan.' );
}
if ( ! @file_exists( $file_name ) ) {
crb_admin_stop_ajax( 'Error: The requested file doesn\'t exist or has been deleted: ' . htmlspecialchars( $file_name ). '. Please run a new malware scan.' );
return;
}
if ( ! @is_file( $file_name ) ) {
crb_admin_stop_ajax( 'Error: The requested file is not an ordinary file: ' . htmlspecialchars( $file_name ) );
return;
}
$file_size = filesize( $file_name );
if ( $file_size > 8000000 ) {
crb_admin_stop_ajax( 'Error: The requested is too large to display: ' . crb_size_format( $file_size ) );
return;
}
if ( $file_size <= 0 ) {
crb_admin_stop_ajax( 'The requested file is empty.' );
return;
}
$scan_id = absint( $get['scan_id'] );
$the_file = cerber_db_get_row( 'SELECT * FROM ' . cerber_get_db_prefix() . CERBER_SCAN_TABLE . ' WHERE scan_id = ' . $scan_id . ' AND file_name = "' . $file_name . '"' );
if ( ! $the_file ) {
crb_admin_stop_ajax( __( 'File access error. Possibly scan results are outdated. Please run Quick or Full Scan.', 'wp-cerber' ) );
return;
}
if ( ! $source = file_get_contents( $file_name ) ) {
crb_admin_stop_ajax( 'Error: Unable to load file.' );
return;
}
$source = htmlspecialchars( $source, ENT_SUBSTITUTE );
if ( ! $source ) {
$source = 'Unable to display the contents of the file. This file contains non-printable characters.';
}
if ( cerber_detect_exec_extension( $file_name )
|| cerber_check_extension( $file_name, array( 'js', 'css', 'inc' ) )
|| cerber_is_htaccess( $file_name )
) {
$paint = true;
}
else {
$paint = false;
}
$overlay = '';
if ( $paint ) {
$overlay = '
Loading, please wait...
';
}
//$sh_url = plugin_dir_url( __FILE__ ) . 'assets/sh/';
$sh_url = CRB_Globals::$assets_url . 'sh/';
$sheight = absint( $get['sheight'] ) - 100; // highlighter is un-responsible, so we need tell him the real height
?>
' . $source . '';
if ( $the_file ) {
echo '
Issue: ' . cerber_get_issue_label( $the_file['scan_status'] ) . '
';
}
if ( $paint ) :
?>
$folder->get_error_message() ) );
}
if ( isset( $_FILES['refile'] ) ) {
// Step 1, saving file
if ( ! is_uploaded_file( $_FILES['refile']['tmp_name'] ) ) {
$error = 'Unable to read uploaded file';
}
if ( ! cerber_check_extension( $_FILES['refile']['name'], array( 'zip' ) ) ) {
$error = __( 'This type of file is not supported. Please upload a ZIP archive.', 'wp-cerber' );
}
if ( cerber_detect_exec_extension( $_FILES['refile']['name'] ) ) {
$error = __( 'Executable files are not supported. Please upload a ZIP archive.', 'wp-cerber' );
}
if ( false !== strpos( $_FILES['refile']['name'], '/' ) ) {
$error = 'Incorrect filename';
}
if ( $error ) {
cerber_end_ajax( array( 'error' => $error ) );
}
if ( false === @move_uploaded_file( $_FILES['refile']['tmp_name'], $folder . $_FILES['refile']['name'] ) ) {
cerber_end_ajax( array( 'error' => 'Unable to copy file to ' . $folder ) );
}
}
else {
// Step 2, creating hash
$result = cerber_need_for_hash();
if ( crb_is_wp_error( $result ) ) {
cerber_end_ajax( array( 'error' => $result->get_error_message() ) );
}
}
cerber_end_ajax();
} );
/**
* Deleting files, server side AJAX
*
*/
add_action( 'wp_ajax_cerber_scan_bulk_files', function () {
cerber_check_ajax_permissions();
$post = crb_get_post_fields();
if ( empty( $post['files'] ) || empty( $post['scan_id'] ) ) {
crb_admin_stop_ajax( 'Error!' );
return;
}
$scan_id = absint( $post['scan_id'] );
if ( ! cerber_get_scan( $scan_id ) ) {
crb_admin_stop_ajax( 'Error!' );
return;
}
$operation = $post['scan_file_operation'];
if ( ( ! $ignore = cerber_get_set( 'ignore-list' ) ) || ! is_array( $ignore ) ) {
$ignore = array();
}
global $crb_list;
$crb_list = array();
$i = 0;
$errors = array();
$time = time();
$user_id = get_current_user_id();
foreach ( $post['files'] as $file_name ) {
if ( ! is_file( $file_name ) ) {
continue;
}
$the_file = cerber_db_get_row( 'SELECT * FROM ' . cerber_get_db_prefix() . CERBER_SCAN_TABLE . ' WHERE scan_id = ' . $scan_id . ' AND file_name = "' . $file_name . '"', MYSQL_FETCH_OBJECT );
if ( ! $the_file || ! is_file( $the_file->file_name ) ) {
$errors[] = 'Unknown file: ' . $file_name;
continue;
}
switch ( $operation ) {
case 'delete_file':
$result = cerber_quarantine_file( $file_name, $scan_id );
break;
case 'ignore_add_file':
$ignore[ $the_file->file_name_hash ] = array(
$the_file->file_name,
@hash_file( 'sha256', $the_file->file_name ),
$user_id,
$time,
);
$result = true;
break;
}
if ( crb_is_wp_error( $result ) ) {
$errors[] = $result->get_error_message();
}
elseif ( ! $result ) {
$errors[] = 'Unknown error 55';
}
else {
$i ++;
$crb_list[] = $file_name;
}
}
if ( $operation == 'ignore_add_file' ) {
// Update the last scan results to keep it up to date and avoid user confusing
if ( $scan = cerber_get_scan() ) {
crb_file_filter( $scan['issues'], function ( $file_name ) {
global $crb_list;
if ( in_array( $file_name, $crb_list ) ) {
return false;
}
return true;
} );
cerber_update_scan( $scan );
}
if ( ! cerber_update_set( 'ignore-list', $ignore ) ) {
$errors [] = 'Unable to update the ignore list';
}
}
crb_scan_debug( $errors );
cerber_end_ajax( array( 'errors' => $errors, 'number' => $i, 'processed' => $crb_list ) );
} );
/**
* Finalizes current AJAX request and sends data to the client
*
* @param $data array
*/
function cerber_end_ajax( $data = array() ) {
if ( ! $data ) {
$data = array();
}
$data['cerber_db_errors'] = cerber_db_get_errors();
if ( ! $data['cerber_db_errors'] ) {
$data['OK'] = 'OK!';
}
echo json_encode( $data );
if ( ! nexus_is_valid_request() ) {
wp_die();
}
}
function crb_admin_stop_ajax( $msg = '' ) {
if ( $msg ) {
echo $msg;
}
if ( ! nexus_is_valid_request() ) {
exit;
}
}
function cerber_show_quarantine() {
$folder = cerber_get_the_folder( true );
if ( crb_is_wp_error( $folder ) ) {
echo $folder->get_error_message();
return;
}
$no_files = '
' . __( 'There are no files in the quarantine at the moment.', 'wp-cerber' ) . '
';
$per_page = crb_admin_get_per_page();
$first = ( cerber_get_pn() - 1 ) * $per_page;
$last = $first + $per_page;
$list = array();
$filter_scan = crb_get_query_params( 'scan', '\d+' );
list ( $list, $count, $scan_list ) = cerber_quarantine_get_files( $first, $last, $filter_scan );
_crb_qr_total_sync( $count );
if ( ! $list ) {
if ( ! $filter_scan ) {
echo $no_files;
}
else {
echo __( 'No files match the specified filter.', 'wp-cerber' ) . '
' . __( 'Click here to see the full list of files', 'wp-cerber' ) . ' .';
}
return;
}
$rows = array();
$ofs = get_option( 'gmt_offset' ) * 3600;
$confirm = ' onclick="return confirm(\'' . __( 'Are you sure?', 'wp-cerber' ) . '\');"';
foreach ( $list as $file ) {
$p = array(
'cerber_admin_do' => 'scan_tegrity',
'crb_scan_id' => $file['scan_id'],
'crb_file_id' => $file['qfile']
);
$p['crb_scan_adm'] = 'delete';
$delete = '
' . __( 'Delete permanently', 'wp-cerber' ) . ' ';
$p['crb_scan_adm'] = 'restore';
$restore = ( ! $file['can'] ) ? '' : ' |
' . __( 'Restore', 'wp-cerber' ) . ' ';
$moved = strtotime( $file['date'] ) - $ofs;
$will = cerber_auto_date( $file['scan_id'] + DAY_IN_SECONDS * crb_get_settings( 'scan_qcleanup' ) );
$file_name = str_replace( DIRECTORY_SEPARATOR, '
' . DIRECTORY_SEPARATOR, $file['source'] );
$rows[] = array(
'' . cerber_auto_date( $file['scan_id'] ) . ' ',
'' . cerber_auto_date( $moved ) . ' ',
$will,
$file['size'],
$file_name,
'' . $delete . $restore . ' '
);
}
$heading = array(
__( 'Scanned', 'wp-cerber' ),
__( 'Quarantined', 'wp-cerber' ),
__( 'Automatic deletion', 'wp-cerber' ),
__( 'Size', 'wp-cerber' ),
__( 'File', 'wp-cerber' ),
__( 'Action', 'wp-cerber' ),
);
$table = cerber_make_table( $rows, $heading, 'crb-quarantine' );
$table .= cerber_page_navi( $count, $per_page );
$filter = '';
if ( count( $scan_list ) > 1 ) {
krsort( $scan_list );
$list = array( 0 => __( 'All scans', 'wp-cerber' ) );
foreach ( $scan_list as $s ) {
$list[ $s ] = cerber_date( $s, false );
}
$filter = '
' . cerber_select( 'scan', $list, $filter_scan ) . ' ';
}
echo $filter . $table;
}
function cerber_quarantine_do( $what, $scan_id, $qfile ) {
$scan_id = absint( $scan_id );
if ( ! $scan_id ) {
cerber_admin_notice( 'Error: Wrong scan parameters.' );
return;
}
//$dir = cerber_get_the_folder() . 'quarantine' . DIRECTORY_SEPARATOR . $scan_id;
$dir = cerber_get_the_folder( true );
if ( crb_is_wp_error( $dir ) ) {
cerber_admin_notice( $dir->get_error_message() );
return;
}
$dir .= 'quarantine' . DIRECTORY_SEPARATOR . $scan_id;
$file = $dir . DIRECTORY_SEPARATOR . $qfile;
if ( ! @is_file( $file ) || is_link( $file ) ) {
cerber_admin_notice( 'Error: No file to process' );
return;
}
$rst = $dir . '/.restore';
if ( ! file_exists( $rst ) || ! $handle = @fopen( $rst, 'r' ) ) {
cerber_admin_notice( 'Error: A restore registry file is corrupt or missing.' );
return;
}
$data = null;
while ( ( $line = fgets( $handle ) ) !== false ) {
if ( $p = crb_parse_qline( $dir, $line ) ) {
if ( $p['qfile'] == $qfile ) {
$data = $p;
break;
}
}
}
if ( ! $data ) {
cerber_admin_notice( 'Error: No information about this file. Unable to proceed.' );
return;
}
$err = null;
$msg = null;
switch ( $what ) {
case 'delete':
if ( unlink( $file ) ) {
$msg = __( 'The file has been deleted permanently.', 'wp-cerber' );
crb_qr_total_update( -1 );
}
else {
$err = 'Unable to delete the file: ' . $file;
}
break;
case 'restore':
if ( $data['can'] ) {
$target_dir = dirname( $data['source'] );
if ( ! file_exists( $target_dir ) && ! mkdir( $target_dir, 0755, true ) ) {
$err = 'Unable to create the folder ' . $target_dir . ' . Check permissions of parent folders.';
}
if ( ! $err ) {
if ( @rename( $file, $data['source'] ) ) {
$msg = __( 'The file has been restored to its original location.', 'wp-cerber' );
crb_qr_total_update( -1 );
}
else {
$err = 'A file error occurred while restoring the file. Check permissions of folders.';
}
}
}
else {
$err = 'This file cannot be restored and needs to be manually copied. See instructions in this file: ' . $rst . '
';
}
break;
}
if ( $err ) {
cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' ' . $err );
}
if ( $msg ) {
cerber_admin_message( $msg );
}
}
function cerber_show_ignore() {
// For translators
__( 'Apply', 'wp-cerber' );
__( 'Remove from the list', 'wp-cerber' );
__( 'User Insights', 'wp-cerber' );
__( 'Traffic Insights', 'wp-cerber' );
__( 'Activity Insights', 'wp-cerber' );
$no_files = __( 'The list is empty.', 'wp-cerber' );
$per_page = crb_admin_get_per_page();
$first = ( cerber_get_pn() - 1 ) * $per_page;
if ( ! $list = cerber_get_set( 'ignore-list' ) ) {
echo '' . $no_files . '
';
return;
}
$count = count( $list );
$list = array_slice( $list, $first, $per_page );
$rows = array();
$confirm = ' onclick="return confirm(\'' . __( 'Are you sure?', 'wp-cerber' ) . '\');"';
foreach ( $list as $key => $file ) {
$delete = '' . __( 'Remove from the list', 'wp-cerber' ) . ' ';
$rows[] = array(
cerber_date( $file[3] ),
cerber_date( cerber_get_date( $file[0] ) ),
crb_size_format( cerber_get_size( $file[0] ) ),
$file[0],
'' . $delete . ' '
);
}
$heading = array(
__( 'Added', 'wp-cerber' ),
__( 'Modified', 'wp-cerber' ),
__( 'Size', 'wp-cerber' ),
__( 'File', 'wp-cerber' ),
__( 'Action', 'wp-cerber' ),
);
$table = cerber_make_table( $rows, $heading );
$table .= cerber_page_navi( $count, $per_page );
echo $table;
}
function crb_remove_ignore( $id ) {
if ( ! $list = cerber_get_set( 'ignore-list' ) ) {
return false;
}
if ( ! isset( $list[ $id ] ) ) {
return false;
}
unset( $list[ $id ] );
return cerber_update_set( 'ignore-list', $list );
}
// Scan analytics ===========================================================
function crb_scan_no_message() {
return '' .
__( 'No data for generating reports', 'wp-cerber' ) . '
' .
__( ' Please run the Full Scan. After the scan is completed, analytics reports will be generated.', 'wp-cerber' ) . '
';
}
function cerber_scan_insights() {
if ( ! $scan_id = crb_scan_last_full() ) {
echo crb_scan_no_message();
return;
}
if ( $ext = crb_get_query_params( 'fext' ) ) {
cerber_show_files( $ext, $scan_id );
return;
}
?>
No files found. Please run the Full Scan.';
return;
}
$title = ( $ext == '.' ) ? __( 'Files without extension', 'wp-cerber' ) : 'Files with the "' . $ext . '" extension';
echo '
' . $title . ' ';
echo crb_make_file_table( $files ) . cerber_page_navi( $total, $per_page );
}
/**
* @param string $type
*
* @return string[]
*
* @since 8.6.4
*/
function cerber_generate_insights( $type ) {
if ( ! $scan_id = crb_scan_last_full() ) {
return array( 'html' => crb_scan_no_message() );
}
$key = 'scan_insights_' . $type;
// Cache
if ( $report = cerber_get_set( $key, $scan_id, false ) ) {
return array( 'html' => $report );
}
switch ( $type ) {
case 1:
$report = crb_scan_insights_brief( $scan_id );
break;
case 2:
$report = crb_scan_insights_lrgst( $scan_id );
break;
case 3:
$report = crb_scan_insights_exts( $scan_id );
break;
}
// Cache
if ( $report ) {
cerber_update_set( $key, $report, $scan_id, false, time() + 24 * 3600 );
}
else {
$report = 'ERROR: Unknown report';
}
$response = array( 'html' => $report, 'error' => '' );
/*if ( $_REQUEST['request'] < 2 ) {
$response['continue'] = 1;
}*/
return $response;
}
function crb_scan_insights_brief( $scan_id ) {
$scan_id = absint( $scan_id );
$table = cerber_get_db_prefix() . CERBER_SCAN_TABLE;
$result = '' . __( 'Brief summary', 'wp-cerber' ) . ' ';
$list = array(
array( 'WordPress root folder (ABSPATH) ', ABSPATH ),
array( 'WordPress uploads folder', cerber_get_upload_dir() ),
array( 'WordPress content folder', dirname( cerber_get_plugins_dir() ) ),
array( 'WordPress plugins folder', cerber_get_plugins_dir() ),
array( 'WordPress themes folder', cerber_get_themes_dir() ),
array( 'WordPress must-use plugin folder (WPMU_PLUGIN_DIR) ', WPMU_PLUGIN_DIR ),
array( 'PHP folder for uploading files', ini_get( 'upload_tmp_dir' ) ),
array( 'Server folder for temporary files', sys_get_temp_dir() ),
array( 'Server folder for users\' session data', session_save_path() ),
);
/*
It's not scanned by the scanner
$cerber_folder = cerber_get_my_folder();
if ( ! crb_is_wp_error( $cerber_folder ) ) {
$list[] = array( 'WP Cerber\'s folder', $cerber_folder );
}*/
$folders = array();
foreach ( $list as $item ) {
if ( ! $item[1] || ! @file_exists( $item[1] ) ) {
continue;
}
$item[2] = 0;
$item[3] = 0;
$item[1] = rtrim( $item[1], DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
if ( $files = cerber_db_get_col( 'SELECT file_size FROM ' . $table . ' WHERE scan_id = ' . $scan_id . ' AND file_name LIKE "' . $item[1] . '%"' ) ) {
$item[2] = count( $files );
if ( $sum = array_sum( $files ) ) {
$item[3] = crb_size_format( $sum );
}
}
$folders[] = $item;
}
$sql = 'SELECT file_size FROM ' . $table . ' WHERE scan_id = ' . $scan_id . ' AND file_name NOT LIKE "' . ABSPATH . '%"';
$files = cerber_db_get_col( $sql );
if ( $files ) {
if ( $sum = array_sum( $files ) ) {
$sum = crb_size_format( $sum );
}
$folders[] = array( 'Above the WordPress installation folder', '', count( $files ), $sum );
}
$column = array_column( $folders, 2 );
array_multisort( $column, SORT_DESC, $folders );
return $result . cerber_make_table( $folders, array(
__( 'Folder', 'wp-cerber' ),
__( 'Path', 'wp-cerber' ),
__( 'Files', 'wp-cerber' ),
__( 'Space Occupied', 'wp-cerber' )
), '', 'crb_align_right crb_align_left_2', 'crb-monospace' );
}
function crb_scan_insights_exts( $scan_id ) {
$scan_id = absint( $scan_id );
$table = cerber_get_db_prefix() . CERBER_SCAN_TABLE;
$list = array();
$offset = 0;
$total = 0;
$done = false;
//cerber_exec_timer( 15 );
while ( true ) {
if ( ! $files = cerber_db_get_results( 'SELECT file_name, file_name_hash, file_mtime, file_size, file_ext FROM ' . $table . ' WHERE scan_id = ' . $scan_id . ' LIMIT ' . CRB_SQL_CHUNK . ' OFFSET ' . $offset ) ) {
$done = true;
break;
}
$offset += CRB_SQL_CHUNK;
foreach ( $files as $file ) {
if ( ! $ext = crb_get_extension( $file['file_name'] ) ) {
$ext = '.';
}
if ( empty( $file['file_ext'] ) ) {
cerber_db_query( 'UPDATE ' . $table . ' SET file_ext = "' . $ext . '" WHERE scan_id = ' . $scan_id . ' AND file_name_hash = "' . $file['file_name_hash'] . '"' );
}
if ( ! isset( $list[ $ext ] ) ) {
$list[ $ext ] = array( 0, 0, array(), array() );
}
$list[ $ext ][0] ++;
$list[ $ext ][1] += $file['file_size'];
$list[ $ext ][2][] = $file['file_size'];
$list[ $ext ][3][] = $file['file_mtime'];
$total ++;
}
}
if ( ! $done && ( count( $files ) < CRB_SQL_CHUNK ) ) {
$done = true;
}
if ( ! $done ) {
return array( 'continue' => 1 );
}
if ( ! $list ) {
return crb_scan_no_message();
}
$base = cerber_admin_link( 'scan_insights' );
$none = __( 'No extension', 'wp-cerber' );
$result = array();
foreach ( $list as $ext => $data ) {
$text = ( $ext == '.' ) ? $none : $ext;
$result[] = array(
'' . $text . ' ',
$data[0],
crb_size_format( $data[1] ),
crb_size_format( min( $data[2] ) ),
crb_size_format( max( $data[2] ) ),
crb_size_format( round( $data[1] / $data[0], 0 ) ),
cerber_date( min( $data[3] ) ),
cerber_date( max( $data[3] ) ),
);
}
// Sorting by a column in a multidimensional array
$column = array_column( $result, 1 );
array_multisort( $column, SORT_DESC, $result );
// Create table
//$report = 'The file system report is based on the last full scan. ';
$report = '' . __( 'File extensions statistics', 'wp-cerber' ) . ' ';
$report .= cerber_make_table( $result, array(
__( 'Extension', 'wp-cerber' ),
__( 'Files', 'wp-cerber' ),
__( 'Space Occupied', 'wp-cerber' ),
__( 'Smallest', 'wp-cerber' ),
__( 'Largest', 'wp-cerber' ),
__( 'Average Size', 'wp-cerber' ),
__( 'Oldest', 'wp-cerber' ),
__( 'Newest', 'wp-cerber' ),
), 'crb-ext-statistics', 'crb_align_right', 'crb-monospace crb-anchor-decorated' );
return $report;
}
function crb_scan_insights_lrgst( $scan_id ) {
$scan_id = absint( $scan_id );
$table = cerber_get_db_prefix() . CERBER_SCAN_TABLE;
if ( ! $files = cerber_db_get_results( 'SELECT file_name, file_mtime, file_size FROM ' . $table . ' WHERE scan_id = ' . $scan_id . ' ORDER BY file_size DESC LIMIT 10' ) ) {
return crb_scan_no_message();
}
$title = '' . __( 'Top 10 largest files', 'wp-cerber' ) . ' ';
return $title . crb_make_file_table( $files );
}
function crb_scan_insights_duplicates( $scan_id ) {
$scan_id = absint( $scan_id );
// SQL Doesn't work
/* ( ! $files = cerber_db_get_results( 'SELECT file_name, file_mtime, file_size, file_hash FROM ' . cerber_get_db_prefix() . CERBER_SCAN_TABLE . ' WHERE scan_id = ' . $scan_id . ' GROUP BY file_hash HAVING COUNT(*) > 1 LIMIT 100' ) ) {
return '';
}*/
if ( ! $files = cerber_db_get_col( 'SELECT file_hash FROM ' . cerber_get_db_prefix() . CERBER_SCAN_TABLE . ' WHERE scan_id = ' . $scan_id . ' AND file_size !=0 AND file_hash !="" ' ) ) {
return crb_scan_no_message();
}
$dup = array_unique( array_diff_assoc( $files, array_unique( $files ) ) );
rsort( $dup );
$list = array();
foreach ( $dup as $hash ) {
if ( $fl = cerber_db_get_results( 'SELECT file_name, file_mtime, file_size FROM ' . cerber_get_db_prefix() . CERBER_SCAN_TABLE . ' WHERE scan_id = ' . $scan_id . ' AND file_hash = "' . $hash . '"' ) ) {
$list = array_merge( $list, $fl );
}
}
$result = 'Duplicate files ';
return $result . crb_make_file_table( $list );
}
function crb_scan_last_full() {
$scans = cerber_db_get_col( 'SELECT DISTINCT scan_id FROM ' . cerber_get_db_prefix() . CERBER_SCAN_TABLE . ' WHERE scan_mode = 1 ORDER BY scan_id DESC LIMIT 1' );
if ( $scans ) {
$scan_id = $scans[0];
$scan = cerber_get_scan( $scan_id );
if ( $scan['finished']
|| ( $scan['aborted'] && $scan['next_step'] > 5 ) ) { // Step 5 is enough for analysis
return $scan_id;
}
}
return false;
}
/**
* @param int $user_id
* @param string $tab
* @param bool $cache_only
*
* @return string
*/
function crb_generate_user_insights( $user_id, $tab, $cache_only = false ) {
if ( $tab != 'activity' ) {
$tab = 'traffic';
}
$result = '';
$links = array();
if ( $user = get_userdata( $user_id ) ) {
$labels = cerber_get_labels();
$list = array();
if ( $data = crb_q_cache_get( 'SELECT COUNT(activity) as cnt, activity FROM ' . CERBER_LOG_TABLE . ' WHERE user_id = ' . $user_id . ' GROUP BY activity', CERBER_LOG_TABLE, $cache_only ) ) {
foreach ( $data as $item ) {
$list[ $item[1] ] = 1;
$links[] = array( array( 'filter_activity' => $item[1], 'filter_user' => $user_id ), crb_array_get( $labels, $item[1], 'Unknown' ) . ' - ' . $item[0] );
}
}
if ( $tab == 'activity' ) {
if ( $data = crb_q_cache_get( 'SELECT COUNT(activity) as cnt, activity FROM ' . CERBER_LOG_TABLE . ' WHERE user_login = "' . $user->user_login . '" OR user_login = "' . $user->user_email . '" GROUP BY activity', CERBER_LOG_TABLE, $cache_only ) ) {
foreach ( $data as $item ) {
if ( isset( $list[ $item[1] ] ) ) {
continue;
}
$links[] = array( array( 'filter_activity' => $item[1], 'filter_login' => $user->user_login . '|' . $user->user_email ), crb_array_get( $labels, $item[1], 'Unknown' ) . ' - ' . $item[0] );
}
}
}
if ( $links ) {
array_unshift( $links, array( array( 'filter_user' => $user_id ), __( 'All' ) ) );
$result = '' . crb_make_nav_links( $links, $tab ) . '
';
}
}
if ( ! $result ) {
if ( $cache_only ) {
return '';
}
$result = __( 'No activity has been logged yet.', 'wp-cerber' );
}
return $result;
}
// Miscellaneous admin routines ===========================================================
/**
* Detects file extension
*
* @param string $file_name
*
* @return string
*/
function crb_get_extension( $file_name ) {
$name = basename( $file_name );
if ( $name == '.htaccess' ) {
return '';
}
if ( false === ( $last_dot = strrpos( $name, '.' ) ) ) {
return '';
}
/*$first_dot = strpos( $name, '.' );
if ( $last_dot !== $first_dot
&& cerber_detect_exec_extension( $name ) ) {
$ext = substr( $name, $first_dot + 1 );
}
else {
$ext = substr( $name, $last_dot + 1 );
}*/
$ext = substr( $name, $last_dot + 1 );
if ( ! $ext ) {
$ext = '';
}
return $ext;
}
function crb_make_file_table( $files ) {
$result = array();
foreach ( $files as $file ) {
$result[] = array(
$file['file_name'],
crb_size_format( $file['file_size'] ),
cerber_date( $file['file_mtime'] ),
);
}
return cerber_make_table( $result, array(
__( 'File Name', 'wp-cerber' ),
__( 'Size', 'wp-cerber' ),
__( 'Modified', 'wp-cerber' ),
), '', 'crb_align_right', 'crb-monospace crb-anchor-decorated' );
}
/**
* Generates an HTML table
*
* @param $heading
* @param $rows
* @param string $id
* @param $class
* @param string $body_class
* @param string $head_class
* @param bool $show_footer
*
* @return string
* @since 8.6.4
*
*/
function cerber_make_table( $rows, $heading = array(), $id = '', $class = '', $body_class = '', $head_class = '', $show_footer = true ) {
$tr = array();
foreach ( $rows as $row ) {
$tr[] = '' . implode( ' ', $row ) . ' ';
}
$tfoot = '';
$thead = '';
if ( $heading ) {
$titles = '' . implode( ' ', $heading ) . ' ';
$thead = '' . $titles . ' ';
if ( $show_footer ) {
$tfoot = '' . $titles . ' ';
}
}
$id = ( $id ) ? 'id="' . $id . '"' : '';
return '' . $thead . $tfoot . '' . implode( ' ', $tr ) . '
';
}
function cerber_get_chmod( $file ) {
return substr( sprintf( '%o', @fileperms( $file ) ), - 4 );
}
function cerber_get_size( $file ) {
$size = @filesize( $file );
return ( is_numeric( $size ) ) ? $size : 0;
}
function cerber_get_date( $file ) {
$mtime = @filemtime( $file );
return ( is_numeric( $mtime ) ) ? $mtime : 0;
}
function crb_show_admin_announcement( $text = '', $big = true ) {
$class = ( $big ) ? 'crb-cerber-logo-big' : 'crb-cerber-logo-small';
echo '';
}
/**
* Returns GET (query string) params of the currently displayed page
*
* @return array
*/
function crb_get_referrer_params() {
if ( cerber_is_wp_ajax() ) {
$referrer = array();
if ( $ref_url = crb_get_request_field( 'referrer_page_url' ) ) {
if ( $temp = parse_url( $ref_url, PHP_URL_QUERY ) ) {
parse_str( $temp, $referrer );
}
}
}
else {
$referrer = crb_get_query_params();
}
if ( ! is_array( $referrer ) ) {
$referrer = array();
}
array_walk_recursive( $referrer, function ( &$item ) {
$item = preg_replace( '/[^\w\-.:*|@]/i', '', $item ); // Some sanitization
} );
return $referrer;
}
add_filter( 'manage_application-passwords-user_columns', function ( $columns ) {
end( $columns );
$key = key( $columns );
$last = array_pop( $columns );
return array_merge( $columns,
array(
'app_cerber_log_ok' => __( 'Authorized', 'wp-cerber' ),
'app_cerber_log_fail' => __( 'Authorization Failed', 'wp-cerber' ),
$key => $last
) );
} );
add_action( 'manage_application-passwords-user_custom_column', function ( $column_name, $item ) {
global $user_id;
if ( $column_name == 'app_cerber_log_ok' && ! empty( $item['last_ip'] ) ) {
$link = cerber_activity_link( array( 4 ) ) . '&filter_user=' . $user_id;
echo 'View Log ';
}
if ( $column_name == 'app_cerber_log_fail' ) {
$user = get_user_by( 'ID', $user_id );
$link = cerber_activity_link( array( 8 ) ) . '&filter_login=' . $user->user_email . '|' . $user->user_login;
echo 'View Log ';
}
}, 10, 2 );
/**
* @param string $fname
* @param string $ct Content-Type @since 8.6.9
*/
function crb_file_headers( $fname, $ct = 'text/csv' ) {
$fname = rawurlencode( $fname ); // encode non-ASCII symbols
@ob_clean(); // This trick is crucial for some servers/environments (e.g. some IIS)
header( "Content-Type: " . $ct );
header( "Content-Disposition: attachment; filename*=UTF-8''{$fname}" );
} admin/cerber-dashboard.php 0000644 00000625317 14757753175 0011577 0 ustar 00 'network-admin',
'id' => 'cerber_admin',
'title' => 'WP Cerber',
'href' => cerber_admin_link(),
);
$wp_admin_bar->add_node( $args );
}
/**
* Outputs WP Cerber admin headers
*
* @return void
*
* @since 9.1.1
*/
function crb_admin_headers() {
if ( ! cerber_is_admin_page()
|| cerber_is_wp_ajax() ) {
return;
}
header( "Content-Security-Policy: base-uri 'self'; default-src 'self' 'unsafe-inline'; img-src https: data:; font-src https: data:; connect-src 'self'; object-src 'none';" );
}
/**
* Wrapper for all admin pages
*
* @param $title string
* @param $tabs array
* @param $active_tab string
* @param $renderer callable
*/
function cerber_show_admin_page( $title, $tabs = array(), $active_tab = null, $renderer = null ) {
if ( ! $active_tab ) {
$active_tab = crb_admin_get_tab( $tabs );
}
if ( nexus_is_valid_request() ) {
$title .= nexus_request_data()->at_site;
}
?>
';
echo '
';
if ( $active_tab == 'help' ) {
cerber_show_help();
}
elseif ( is_callable( $renderer ) ) {
call_user_func( $renderer, $active_tab );
}
echo '
';
cerber_show_aside( $active_tab );
echo '
';
?>
deleteGarbage();
$per_page = ( ! empty( $args['per_page'] ) ) ? $args['per_page'] : crb_admin_get_per_page();
$limit = cerber_get_sql_limit( $per_page );
$controls = empty( $args['no_navi'] );
$table = '';
$hint = '';
if ( $rows = cerber_db_get_results( 'SELECT * FROM ' . CERBER_BLOCKS_TABLE . ' ORDER BY block_until DESC ' . $limit, MYSQL_FETCH_OBJECT ) ) {
if ( $controls ) {
$total = cerber_blocked_num();
}
$table_rows = array();
$base_url = cerber_admin_link( 'activity' );
$remove_base = cerber_admin_link( crb_admin_get_tab(), null, true );
foreach ( $rows as $row ) {
$ip = '' . $row->ip . ' ';
$ip_info = cerber_get_ip_info( $row->ip, true );
if ( isset( $ip_info['hostname_html'] ) ) {
$hostname = $ip_info['hostname_html'];
}
else {
$ip_id = cerber_get_id_ip( $row->ip );
$hostname = crb_get_ajax_placeholder( 'hostname', $ip_id );
}
if ( lab_lab() ) {
$single_ip = str_replace( '*', '1', $row->ip );
$country = '' . crb_country_html( null, $single_ip );
}
else {
$country = '';
}
$the_row = ' ' . $ip . ' ' . $hostname . $country . ' ' . cerber_date( $row->block_until ) . ' ' . $row->reason . ' ';
if ( $controls ) {
$the_row .= '' . __( 'Remove', 'wp-cerber' ) . ' ';
}
$table_rows[] = $the_row;
}
$heading = array(
__( 'IP Address', 'wp-cerber' ),
__( 'Hostname', 'wp-cerber' ),
__( 'Country', 'wp-cerber' ),
__( 'Expires', 'wp-cerber' ),
__( 'Reason', 'wp-cerber' ),
__( 'Action', 'wp-cerber' ),
);
if ( ! lab_lab() ) {
unset( $heading[2] );
}
if ( $controls ) {
$navi = cerber_page_navi( $total, $per_page );
$hint = '' . __( 'Click the IP address to see its activity', 'wp-cerber' ) . '
';
}
else {
$navi = '';
unset( $heading[5] );
}
$titles = '' . implode( ' ', $heading ) . ' ';
$table = '' . $titles . ' ' . $titles . ' ' . implode( '', $table_rows ) . '
' . $navi;
/*if ( empty( $args['no_navi'] ) ) {
$table .= cerber_page_navi( $total, $per_page );
}*/
}
else {
$hint = '' . sprintf( __( 'No lockouts at the moment. The sky is clear.', 'wp-cerber' ) ) . '
';
}
$ret = $table . '' . $hint . '
';
if ( $echo ) {
echo $ret;
return;
}
return $ret;
}
/**
* @param string $ip
*
* @return int
*/
function cerber_block_delete( $ip ) {
$result = 0;
if ( cerber_db_query( 'DELETE FROM ' . CERBER_BLOCKS_TABLE . ' WHERE ip = "' . cerber_real_escape( $ip ) . '"' ) ) {
$result = cerber_db_get_affected();
}
crb_event_handler( 'ip_event', array(
'e_type' => 'unlocked',
'ip' => $ip,
'result' => $result
) );
return $result;
}
/**
* ACL management forms
*
* @return void
*/
function cerber_acl_form(){
echo '
' . __( 'White IP Access List', 'wp-cerber' ) . ' ';
echo '
';
echo cerber_acl_get_table( 'W' );
echo '
' . __( 'Black IP Access List', 'wp-cerber' ) . ' ';
echo '
';
echo cerber_acl_get_table( 'B' );
$user_ip = cerber_get_remote_ip();
$link = cerber_admin_link( 'activity' ) . '&filter_ip=' . $user_ip;
$name = crb_country_html( null, $user_ip );
echo ' ' . __( 'Your IP', 'wp-cerber' ) . ': ' . $user_ip . ' ' . $name . '
';
?>
Use the following formats to add entries to the access lists
IPv4 Single IPv4 address 192.168.5.22
IPv4 Range specified with hyphen (dash) 192.168.1.45 - 192.168.22.165
IPv4 Range specified with CIDR 192.168.128.0/20
IPv4 Subnet Class C specified with CIDR 192.168.77.0/24
IPv4 Any IPv4 address specified with CIDR 0.0.0.0/0
IPv4 Subnet Class C specified with wildcard 192.168.77.*
IPv4 Subnet Class B specified with wildcard 192.168.*.*
IPv4 Subnet Class A specified with wildcard 192.*.*.*
IPv4 Any IPv4 address specified with wildcard *.*.*.*
IPv6 Single IPv6 address 2001:0db8:85a3:0000:0000:8a2e:0370:7334
IPv6 Range specified with hyphen (dash) 2001:db8::ff00:41:0 - 2001:db8::ff00:41:12ff
IPv6 Range specified with CIDR 2001:db8::/46
IPv6 Range specified with wildcard 2001:db8::ff00:41:*
IPv6 Any IPv6 address ::/0
get_results( 'SELECT * FROM ' . CERBER_ACL_TABLE . " WHERE acl_slice = '.$acl_slice.' AND tag = '" . $tag . "' ORDER BY ip_long_begin, ip" ) ) {
foreach ( $rows as $row ) {
$links = '';
if ( ! $row->ver6 || cerber_is_ipv6( $row->ip ) ) {
$links = '' . __( 'Check for activities', 'wp-cerber' ) . ' ' . cerber_traffic_link( array( 'filter_ip' => $row->ip ) );
}
$list[] = '' . $row->ip . ' ' . $row->comments . ' ' . $links . '
' . __( 'Remove', 'wp-cerber' ) . '
';
}
$ret = '' . implode( ' ', $list ) . '
';
}
else {
$ret = '' . __( 'List is empty', 'wp-cerber' ) . '
';
}
$ret = ''
. $ret . '
'
. cerber_nonce_field()
. ' ';
return $ret;
}
/*
Handle actions with items in ACLs in the dashboard
*/
function cerber_acl_form_process( $post = array() ) {
$tag = crb_array_get( $post, 'acl_tag', '', 'W|B' );
$ip = trim( crb_array_get( $post, 'add_acl' ) );
$ip = preg_replace( CRB_IP_NET_RANGE, ' ', $ip );
$ip = preg_replace( '/\s+/', ' ', $ip );
$comment = strip_tags( stripslashes( crb_array_get( $post, 'add_acl_comment', '' ) ) );
if ( $tag == 'B' ) {
if ( ! cerber_can_be_listed( $ip ) ) {
cerber_admin_notice( __( "You cannot add your IP address or network", 'wp-cerber' ) );
return;
}
$ok = sprintf( __( 'IP address %s has been added to Black IP Access List', 'wp-cerber' ), $ip );
}
else {
$ok = sprintf( __( 'IP address %s has been added to White IP Access List', 'wp-cerber' ), $ip );
}
$result = cerber_acl_add( $ip, $tag, $comment );
if ( crb_is_wp_error( $result ) ) {
cerber_admin_notice( $result->get_error_message() );
}
else {
cerber_admin_message( $ok );
}
return;
}
/*
AJAX admin requests is landing here
*/
add_action('wp_ajax_cerber_ajax', 'cerber_admin_ajax');
function cerber_admin_ajax() {
$go = cerber_check_ajax_permissions( false );
if ( $go === false ) {
return false;
}
$admin = $go === true;
$response = array();
$request = crb_get_request_fields();
if ( $admin && ( $ip = crb_array_get( $request, 'acl_delete' ) ) ) {
if ( cerber_acl_remove( $ip, crb_array_get( $request, 'slice' ) ) ) {
$response['deleted_ip'] = $ip;
}
else {
$response['error'] = 'Unable to delete the entry';
}
}
elseif ( ( $slug = crb_array_get( $request, 'crb_ajax_slug' ) )
&& $list = crb_array_get( $request, 'crb_ajax_list' ) ) {
$response['slug'] = $slug;
$list = array_unique( $list );
$response_data = array();
/*
$list = array_map(function ($ip_id){
return cerber_get_ip_id( $ip_id );
}, $list);
$list = array_filter( $list, function ( $ip ) {
if (filter_var( $ip, FILTER_VALIDATE_IP )){
return true;
}
});*/
if ( $slug == 'hostname' || $slug == 'country' ) {
$ip_list = array();
foreach ( $list as $ip_id ) {
if ( $ip = filter_var( cerber_get_ip_id( $ip_id ), FILTER_VALIDATE_IP ) ) {
$ip_list[ $ip_id ] = $ip;
$response_data[ $ip_id ] = '';
}
else {
$response_data[ $ip_id ] = '-';
}
}
}
switch ( $slug ) {
case 'hostname':
foreach ( $ip_list as $ip_id => $ip ) {
$ip_info = cerber_get_ip_info( $ip );
$response_data[ $ip_id ] = $ip_info['hostname_html'];
}
break;
case 'country':
if ( $country_list = lab_get_country( $ip_list, false ) ) {
foreach ( $country_list as $ip_id => $country ) {
if ( $country ) {
$response_data[ $ip_id ] = cerber_get_flag_html( $country, cerber_country_name( $country ) );
}
else {
$response_data[ $ip_id ] = __( 'Unknown', 'wp-cerber' );
}
}
}
break;
case 'cbcc':
foreach ( $list as $user_id ) {
$response_data[ $user_id ] = 0;
$base = admin_url( 'edit-comments.php' );
// to get this working we added filter 'preprocess_comment'
if ( $com = get_comments( array( 'author__in' => $user_id ) ) ) {
$response_data[ $user_id ] = '' . count( $com ) . ' ';
}
}
break;
case 'cbla':
$base = cerber_activity_link( array( CRB_EV_LIN ) );
foreach ( $list as $user_id ) {
if ( $row = cerber_get_last_login( $user_id ) ) {
if ( $country = crb_country_html( $row->country, $row->ip, false ) ) {
$country = ' ' . $country;
}
else {
$country = '';
}
$val = '' . cerber_date( $row->stamp ) . ' ' . $country;
}
else {
$val = __( 'Never', 'wp-cerber' );
}
$response_data[ $user_id ] = $val;
}
break;
case 'cbfl':
$base = cerber_activity_link( array( CRB_EV_LFL ) );
foreach ( $list as $user_id ) {
$u = get_userdata( $user_id );
$val = 0;
$failed = cerber_db_get_var( 'SELECT COUNT(user_id) FROM ' . CERBER_LOG_TABLE . ' WHERE ( user_login = "' . $u->user_login . '" OR user_login = "' . $u->user_email . '" ) AND activity = ' . CRB_EV_LFL . ' AND stamp > ' . ( time() - 24 * 3600 ) );
if ( $failed ) {
$val = '' . $failed . ' ';
}
$response_data[ $user_id ] = $val;
}
break;
}
$response['data'] = $response_data;
}
elseif ( $admin && isset( $request['dismiss_info'] ) ) {
if ( isset( $request['button_id'] ) && ( $request['button_id'] == 'lab_ok' || $request['button_id'] == 'lab_no' ) ) {
lab_user_opt_in( $request['button_id'] );
}
else {
delete_site_option( 'cerber_admin_info' );
}
}
elseif ( $admin && ( $ajax_route = crb_array_get( $request, 'ajax_route' ) ) ) {
$ref_params = crb_get_referrer_params();
switch ( $ajax_route ) {
case 'scanner_analytics':
$response = cerber_generate_insights( $request['itype'] );
break;
case 'user_activity_analytics':
$data = crb_generate_user_insights( absint( $request['user_id'] ), $ref_params['tab'] );
$response = array( 'html' => $data, 'error' => '' );
break;
case 'ip_extra_info':
$response = cerber_ip_extra_view_ajax( $request, $ref_params );
break;
case 'ip_quick_analytics':
$data = crb_generate_ip_insights( $request['ip_address'], $ref_params['tab'] );
$response = array( 'html' => $data, 'error' => '' );
break;
}
}
elseif ( $admin && ( $usearch = crb_array_get( $request, 'user_search' ) ) ) {
$users = get_users( array( 'search' => '*' . esc_attr( $usearch ) . '*', ) );
$response = array();
if ( $users ) {
foreach ( $users as $user ) {
$data = get_userdata( $user->ID );
$response[] = array(
'id' => $user->ID,
'text' => crb_format_user_name( $data )
);
}
}
}
echo json_encode( $response );
if ( ! nexus_is_valid_request() ) {
wp_die();
}
}
add_action( 'wp_ajax_cerber_local_ajax', function () {
check_ajax_referer( 'crb-ajax-admin', 'ajax_nonce' );
if ( ! is_super_admin() ) {
wp_die( 'Oops! Access denied.' );
}
$done = array();
switch ( crb_array_get( $_REQUEST, 'crb_ajax_do' ) ) {
case 'bg_tasks_run':
$done = cerber_bg_task_launcher( array_flip( crb_array_get( $_REQUEST, 'tasks', array() ) ) );
break;
}
echo json_encode( array( 'done' => $done ) );
exit;
} );
/**
* @param string $group
* @param string $item_id
*
* @return string
*/
function crb_get_ajax_placeholder( $group, $item_id ) {
return ' ';
}
/*
* Retrieve extended IP information
* @since 2.2
*
*/
function cerber_get_ip_info( $ip, $cache_only = false ) {
$ip_id = cerber_get_id_ip( $ip );
$var = get_transient( $ip_id );
$ip_info = crb_unserialize( $var ); // lazy way
if ( $cache_only ) {
return $ip_info;
}
if ( empty( $ip_info['hostname_html'] ) ) {
$ip_info = array();
$hostname = @gethostbyaddr( $ip );
if ( ! $hostname ) {
$hostname = __( 'unknown', 'wp-cerber' );
}
$ip_info['hostname'] = $hostname;
if ( ! filter_var( $hostname, FILTER_VALIDATE_IP ) ) {
$hostname = str_replace( '.', '.', $hostname );
}
$ip_info['hostname_html'] = $hostname;
set_transient( $ip_id, serialize( $ip_info ), 24 * 3600 );
}
return $ip_info;
}
/*
Admin dashboard actions
*/
add_action( 'wp_loaded', function () { // 'wp_loaded' @since 5.6
if ( ! is_admin() ) {
return;
}
cerber_admin_request();
}, 0 );
/**
* @param bool $is_post
*
* @return mixed
*
*/
function cerber_admin_request( $is_post = false ) {
global $wpdb;
if ( ! nexus_is_valid_request()
&& ! cerber_user_can_manage() ) {
return;
}
$get = crb_get_query_params();
if ( ( ! $nonce = crb_array_get( $get, 'cerber_nonce' ) )
&& ( ! $nonce = crb_get_post_fields( 'cerber_nonce' ) ) ) {
return;
}
if ( ! wp_verify_nonce( $nonce, 'control' ) ) {
// TODO: Investigate why is this fires while managing a slave website via Cerber.Hub
//cerber_admin_notice( 'Nonce verification failed.' );
return;
}
$post = crb_get_post_fields();
//$q = crb_admin_parse_query( array( 'cerber_admin_do', 'ip' ) );
$remove_args = array();
if ( cerber_is_http_get() ) {
if ( ( $do = crb_array_get( $get, 'cerber_admin_do' ) ) ) {
switch ( $do ) {
case 'lockdel':
$err = '';
$ip = crb_array_get( $get, 'ip' );
if ( cerber_is_ip_or_net( $ip ) ) {
if ( cerber_block_delete( $ip ) ) {
cerber_admin_message( sprintf( __( 'Lockout for %s was removed', 'wp-cerber' ), $ip ) );
}
else {
$err = 'No lockout for the specified IP address ' . $ip . ' found.';
}
}
else {
$err = 'An invalid IP address has been specified.';
}
if ( $err ) {
cerber_admin_notice( 'Request failed. ' . $err );
}
$remove_args[] = 'ip';
break;
case 'testnotify':
$test_type = crb_array_get( $get, 'type', '', '\w+' );
$test_chan = crb_array_get( $get, 'channel', '', '\w+' );
$channels = array();
if ( $test_chan && ( $test_chan != 'email' || lab_lab() ) ) {
$channels = array_fill_keys( array_keys( CRB_CHANNELS ), 0 );
$channels = array_merge( $channels, array( $test_chan => 1 ) );
}
$sent = cerber_send_notification( $test_type, array( 'subj' => ' *** ' . __( 'TEST MESSAGE', 'wp-cerber' ) . ' *** ' ), '', $channels, true );
if ( $sent !== false ) {
$to = ' ' . implode( ', ', $sent);
/* translators: %s is the name of a mobile device or/and email addresses. */
cerber_admin_message( sprintf( __( 'A test message has been sent to %s', 'wp-cerber' ), $to ) );
}
else {
cerber_admin_notice( __( 'Unable to send a test message', 'wp-cerber' ) );
}
$remove_args[] = 'type';
$remove_args[] = 'channel';
break;
case 'subscribe':
$m = crb_array_get( $get, 'mode' );
$mode = ( 'on' == $m ) ? 'on' : 'off';
crb_admin_alerts_do( $mode );
$remove_args = array_merge( $remove_args, CRB_NON_ALERT_PARAMS );
$remove_args[] = 'subscribe';
$remove_args[] = 'mode';
break;
case 'nexus_set_role':
nexus_enable_role();
wp_safe_redirect( cerber_admin_link( null, array( 'page' => 'cerber-nexus' ) ) );
exit();
break;
case 'nexus_delete_slave':
//nexus_delete_slave( cerber_get_get( 'site_id' ) );
wp_safe_redirect( cerber_admin_link( 'nexus_sites' ) );
exit();
break;
case 'nexus_site_table':
if ( cerber_get_bulk_action() ) {
nexus_do_bulk();
}
break;
case 'scan_tegrity':
$adm = crb_array_get( $get, 'crb_scan_adm' );
$file = crb_array_get( $get, 'crb_file_id' );
if ( in_array( $adm, array( 'delete', 'restore' ) ) ) {
cerber_quarantine_do( $adm, crb_array_get( $get, 'crb_scan_id' ), crb_array_get( $get, 'crb_file_id' ) );
}
elseif ( $adm == 'remove_ignore' ) {
if ( crb_remove_ignore( $file ) ) {
cerber_admin_message( 'The file has been removed from the list' );
}
}
$remove_args = array( 'crb_scan_adm', 'crb_scan_id', 'crb_file_id' );
break;
case 'manage_diag_log':
cerber_manage_diag_log( crb_array_get( $get, 'do_this' ) );
$remove_args[] = 'do_this';
break;
case 'terminate_session':
crb_sessions_kill( crb_array_get( $get, 'id', null, '\w+' ), crb_array_get( $get, 'user_id' ) );
$remove_args = array( 'user_id', 'id' );
break;
case 'crb_manage_sessions':
if ( cerber_get_bulk_action() == 'bulk_session_terminate' ) {
crb_sessions_kill( crb_array_get( $get, 'ids', array(), '\w+' ) );
}
elseif ( cerber_get_bulk_action() == 'bulk_block_user' ) {
if ( ( $sids = crb_array_get( $get, 'ids', array(), '\w+' ) )
&& ( $users = cerber_db_get_col( 'SELECT user_id FROM ' . cerber_get_db_prefix() . CERBER_USS_TABLE . ' WHERE wp_session_token IN ("' . implode( '","', $sids ) . '")' ) ) ) {
array_walk( $users, 'cerber_block_user' );
}
}
break;
case 'export':
if ( nexus_is_valid_request() ) {
return crb_admin_get_tokenized_link();
}
crb_admin_download_file( crb_array_get( $get, 'type' ) );
break;
case 'load_defaults':
cerber_load_defaults();
cerber_admin_message( __( 'Default settings have been loaded', 'wp-cerber' ) );
break;
case 'clear_cache':
CRB_Cache::reset();
break;
default:
return;
}
if ( nexus_is_valid_request() ) {
return array( 'redirect' => true, 'remove_args' => $remove_args );
}
else {
cerber_safe_redirect( $remove_args );
}
}
// TODO: move to the switch above
if ( cerber_get_get( 'citadel' ) == 'deactivate' ) {
cerber_disable_citadel();
}
elseif ( isset( $_GET['force_repair_db'] ) ) {
cerber_create_db();
cerber_upgrade_db( true );
cerber_admin_message( 'Cerber\'s database tables have been repaired and upgraded' );
cerber_safe_redirect('force_repair_db');
}
elseif ( $table = cerber_get_get( 'truncate', '[a-z_]+' ) ) {
if ( 0 === strpos( $table, 'cerber_' ) ) {
if ( $wpdb->query( 'TRUNCATE TABLE ' . $table ) ) {
cerber_admin_message( 'Table ' . $table . ' has been truncated' );
}
else {
cerber_admin_notice( $wpdb->last_error );
}
}
cerber_safe_redirect( 'truncate' );
}
elseif ( isset( $_GET['force_check_nodes'] ) ) {
$best = lab_check_nodes( true );
cerber_admin_message( 'Connectivity to all Cerber Lab\'s nodes has been checked. The closest node: ' . $best );
cerber_safe_redirect( 'force_check_nodes' );
}
elseif ( isset( $_GET['clear_up_lab_cache'] ) ) {
lab_cleanup_cache();
cerber_admin_message( 'The Cerber Lab cache has been cleared' );
cerber_safe_redirect( 'clear_up_lab_cache' );
}
elseif ( isset( $_GET['clear_up_the_cache'] ) ) {
lab_cleanup_cache();
cerber_delete_expired_set( true );
cerber_admin_message( 'The plugin cache has been cleared' );
cerber_safe_redirect( 'clear_up_the_cache' );
}
}
if ( cerber_is_http_post() ) {
$redirect = false;
if ( ( $do = crb_array_get( $post, 'cerber_admin_do' ) ) ) {
switch ( $do ) {
case 'update_role_policies':
crb_admin_save_role_policies( $post );
$redirect = true;
break;
case 'update_geo_rules':
crb_admin_save_geo_rules( $post );
break;
case 'add2acl':
cerber_acl_form_process( $post );
break;
case 'add_slave':
nexus_add_slave( crb_array_get( $post, 'new_slave_token' ) );
break;
case 'install_key':
$lic = preg_replace( "/[^A-Z0-9]/i", '', crb_array_get( $post, 'cerber_license' ) );
if ( ( strlen( $lic ) == LAB_KEY_LENGTH ) || empty( $lic ) ) {
lab_cleanup_cache();
cerber_delete_expired_set( true );
lab_get_site_meta();
lab_update_key( $lic );
if ( $lic ) {
if ( lab_validate_lic() ) {
cerber_admin_message( 'Great! You have entered the valid license key. Now, whenever you see a green shield icon in the top right-hand corner of any Cerber\'s admin page, it means the professional version works as intended, and your website is protected by WP Cerber Security Cloud.
Please use our client portal to manage your subscription, license keys and get support at https://my.wpcerber.com
Thanks for being our client.
' );
}
else {
cerber_admin_notice( 'Error! You have entered an invalid or expired license key.' );
}
}
$redirect = true;
}
break;
}
if ( $redirect ) {
if ( nexus_is_valid_request() ) {
// No redirection is needed so far, we use a second 'get_page' request
//return array( 'redirect' => true, 'remove_args' => $remove_args );
}
else {
cerber_safe_redirect( $remove_args );
}
}
}
}
}
function crb_admin_download_file( $t, $query = array() ) {
switch ( $t ) {
case 'activity':
cerber_export_activity( $query );
break;
case 'traffic':
cerber_export_traffic( $query );
break;
case 'get_diag_log':
cerber_manage_diag_log( 'download' );
break;
}
}
function cerber_safe_redirect( $rem_args ) {
if ( empty( $rem_args ) ) {
$rem_args = array();
}
elseif ( ! is_array( $rem_args ) ) {
$rem_args = array( $rem_args );
}
// Most used common args to remove
$rem_args = array_merge( $rem_args, array(
'_wp_http_referer',
'_wpnonce',
'cerber_nonce',
'ids',
'cerber_admin_do',
'action',
'action2'
) );
wp_safe_redirect( remove_query_arg( $rem_args ) );
exit(); // mandatory!
}
function crb_admin_get_tokenized_link() {
if ( empty( nexus_request_data()->get_params ) ) {
return false;
}
$key = crb_random_string( 26 );
if ( cerber_update_set( '_the_key_' . $key, array( 'query' => nexus_request_data()->get_params ), 1, true, time() + 60 ) ) {
return array( 'redirect' => true, 'redirect_url' => home_url( '?cerber_magic_key=' . $key ) );
}
cerber_admin_notice( 'Unable to generate a tokenized URL' );
return false;
}
function cerber_export_activity( $params = array() ) {
crb_raise_limits( 512 );
$args = array( 'per_page' => 0 );
if ( $params ) {
$args = array_merge( $params, $args );
}
list( $query, $per_page, $falist, $ip, $filter_login, $user_id, $search, $sid, $in_url ) = cerber_activity_query( $args );
// We split into several requests to avoid PHP and MySQL memory limitations
if ( defined( 'CERBER_EXPORT_CHUNK' ) && is_numeric( CERBER_EXPORT_CHUNK ) ) {
$per_chunk = CERBER_EXPORT_CHUNK;
}
else {
$per_chunk = 1000; // Rows per SQL request: a compromise between the size of SQL data at each iteration and script execution time
}
if ( ! $result = cerber_db_query( $query . ' LIMIT ' . $per_chunk ) ) {
wp_die( 'Nothing to export' );
}
$total = cerber_db_get_var( "SELECT FOUND_ROWS()" );
$info = array();
if ( $ip ) {
$info[] = '"Filter by IP:","' . $ip . '"';
}
if ( $user_id ) {
$user = get_userdata( $user_id );
$info[] = '"Filter by user:","' . $user->display_name . '"';
}
if ( $search ) {
$info[] = '"Search results for:","' . $search . '"';
}
$heading = array(
__( 'IP Address', 'wp-cerber' ),
__( 'Date', 'wp-cerber' ),
__( 'Event', 'wp-cerber' ),
__( 'Additional Details', 'wp-cerber' ),
__( 'Local User', 'wp-cerber' ),
__( 'User login', 'wp-cerber' ),
__( 'User ID', 'wp-cerber' ),
__( 'Username', 'wp-cerber' ),
'Unix Timestamp',
'Request ID',
'URL',
);
cerber_send_csv_header( 'wp-cerber-activity', $total, $heading, $info );
$labels = cerber_get_labels( 'activity' );
$status = cerber_get_labels( 'status' ) + cerber_get_reason();
$placeholder = array( 0, '', '', '', '' );
if ( crb_get_settings( 'plain_date' ) ) {
function _crb_csv_date( $timestamp ) {
static $gmt_offset;
if ( $gmt_offset === null ) {
$gmt_offset = get_option( 'gmt_offset' ) * 3600;
}
return date( 'Y-m-d H:i:s', $timestamp + $gmt_offset );
}
}
else {
function _crb_csv_date( $timestamp ) {
return cerber_date( $timestamp, false );
}
}
// The loop
$i = 0;
do {
while ( $row = mysqli_fetch_object( $result ) ) {
$values = array();
if ( ! empty( $row->details ) ) {
$details = explode( '|', $row->details );
}
else {
$details = $placeholder;
}
$values[] = $row->ip;
$values[] = _crb_csv_date( $row->stamp );
$values[] = $labels[ $row->activity ];
$s = $details[0];
$values[] = ( isset( $status[ $s ] ) ) ? $status[ $s ] : '';
$values[] = $row->display_name;
$values[] = $row->ulogin;
$values[] = $row->user_id;
$values[] = $row->user_login;
$values[] = $row->stamp;
$values[] = $row->session_id;
$values[] = $details[4];
cerber_send_csv_line( $values );
}
mysqli_free_result( $result );
$i ++;
$offset = $per_chunk * $i;
} while ( ( $result = cerber_db_query( $query . ' LIMIT ' . $offset . ', ' . $per_chunk ) )
&& $result->num_rows );
exit;
}
function cerber_send_csv_header( $f_name, $total, $heading = array(), $info = array() ) {
$fname = $f_name . '.csv';
crb_file_headers( $fname );
$info[] = '"Generated by:","WP Cerber Security ' . CERBER_VER . '"';
$info[] = '"Website:","' . crb_get_blogname_decoded() . '"';
$info[] = '"Date:","' . cerber_date( time(), false ) . '"';
$info[] = '"Rows in this file:","' . $total . '"';
echo implode( "\r\n", $info ) . "\r\n\r\n";
foreach ( $heading as &$item ) {
$item = '"' . str_replace( '"', '""', trim( $item ) ) . '"';
}
echo implode( ',', $heading ) . "\r\n";
}
function cerber_send_csv_line( $values ) {
foreach ( $values as &$value ) {
$value = '"' . str_replace( '"', '""', trim( $value ) ) . '"';
}
$line = implode( ',', $values ) . "\r\n";
echo $line;
}
function crb_admin_activity_nav_links( $context = '' ) {
$links = array();
if ( ! $context ) {
$links[] = array( array(), __( 'View all', 'wp-cerber' ) );
$labels = cerber_get_labels( 'activity' );
$set = array( CRB_EV_LIN );
foreach ( $set as $item ) {
$links[] = array( array( 'filter_activity' => $item ), $labels[ $item ] );
}
}
if ( $context == 'users' ) {
$links[] = array( array( 'filter_user' => '*' ), __( 'View all', 'wp-cerber' ) );
}
if ( $context != 'suspicious' ) {
$links[] = array( array( 'filter_activity' => array( 1, 2 ) ), __( 'New users', 'wp-cerber' ) );
$links[] = array( array( 'filter_set' => 2 ), __( 'Login issues', 'wp-cerber' ) );
}
if ( $context != 'users' ) {
if ( ! $context ) {
$txt = __( 'Suspicious activity', 'wp-cerber' );
}
else {
$txt = __( 'View all', 'wp-cerber' );
}
$links[] = array( array( 'filter_set' => 1 ), $txt );
}
if ( ! $context ) {
$links[] = array( array( 'filter_activity' => array( 10, 11 ) ), __( 'IP blocked', 'wp-cerber' ) );
$links[] = array( array( 'filter_user' => '*' ), __( 'Users', 'wp-cerber' ) );
$links[] = array( array( 'filter_user' => 0 ), __( 'Non-authenticated', 'wp-cerber' ) );
if ( ! nexus_is_valid_request() ) {
$links[] = array( array( 'filter_user' => get_current_user_id() ), __( 'My activity', 'wp-cerber' ) );
$links[] = array( array( 'filter_ip' => cerber_get_remote_ip() ), __( 'My IP', 'wp-cerber' ) );
}
}
return crb_make_nav_links( $links, 'activity' );
}
/**
* @param array $link_list A list of links
* @param string $tab Admin page tab to generated links for
* @param string $class CSS class
*
* @return string
* @since 8.8.3.2
*/
function crb_make_nav_links( $link_list, $tab = 'activity', $class = '' ) {
$params = crb_get_referrer_params();
unset( $params['page'], $params['tab'], $params['pagen'] );
$base_url = cerber_admin_link( $tab );
$ret = '';
foreach ( $link_list as $link ) {
$query_string = '';
$selected = false;
$other = $params;
if ( ! empty( $link[0] ) ) {
$selected = true;
foreach ( $link[0] as $name => $value ) {
if ( ! is_array( $value ) ) {
$query_string .= '&' . $name . '=' . $value;
if ( crb_array_get( $params, $name ) !== (string) $value ) {
$selected = false;
}
else {
unset( $other[ $name ] );
}
}
else {
foreach ( $value as $key => $val ) {
$query_string .= '&' . $name . '[' . $key . ']=' . $val;
if ( crb_array_get( $params, array( $name, $key ) ) !== (string) $val ) {
$selected = false;
}
else {
unset( $other[ $name ][ $key ] );
}
}
}
}
}
$other = array_filter( $other, function ( $val ) {
return $val === 0 || ! empty( $val );
} );
if ( $selected & empty( $other ) ) {
$ret .= '' . $link[1] . ' ';
}
else {
$ret .= '' . $link[1] . ' ';
}
}
return '' . $ret . '
';
}
/*
* Display activities in the WP Dashboard
*
*
*/
function cerber_show_activity( $args = array(), $echo = true ) {
$status_labels = cerber_get_labels( 'status' ) + cerber_get_reason();
$base_url = cerber_activity_link();
$is_log_empty = ! ( (bool) cerber_db_get_var( 'SELECT ip FROM ' . CERBER_LOG_TABLE . ' LIMIT 1' ) );
$export_link = '';
$ret = '';
list( $query, $per_page, $falist, $filter_ip, $filter_login, $user_id, $search, $sid, $in_url ) = cerber_activity_query( $args );
$sname = '';
$info = '';
$_find = array();
if ( $filter_login ) {
$login = explode( '|', $filter_login );
if ( is_email( $login[0] ) ) {
$_find[] = array( 'email', $login[0] );
}
else {
$_find[] = array( 'login', $login[0] );
}
if ( isset( $login[1] ) ) {
if ( is_email( $login[1] ) ) {
$_find[] = array( 'email', $login[1] );
}
else {
$_find[] = array( 'login', $login[1] );
}
}
}
if ( $search ) {
if ( cerber_is_ip( $search ) ) {
$filter_ip = $search;
}
else {
if ( is_email( $search ) ) {
$_find[] = array( 'email', $search );
}
else {
$_find[] = array( 'login', $search );
}
}
}
if ( ! $user_id && $_find ) {
foreach ( $_find as $item ) {
if ( $user = get_user_by( $item[0], $item[1] ) ) {
$user_id = $user->ID;
break;
}
}
}
if ( $user_id ) {
$sname = crb_format_user_name( $user_id );
$info .= cerber_user_extra_view( $user_id );
}
if ( $filter_ip ) {
$info .= cerber_ip_extra_view( $filter_ip );
}
// No cache
//$rows = cerber_db_get_results( $query, MYSQL_FETCH_OBJECT );
if ( empty( $args['no_navi'] ) ) {
list( $rows, $total ) = crb_q_cache_get( array( array( $query, MYSQL_FETCH_OBJECT ), array( "SELECT FOUND_ROWS()" ) ), CERBER_LOG_TABLE );
$total = $total[0][0];
}
else {
$rows = crb_q_cache_get( array( array( $query, MYSQL_FETCH_OBJECT ) ), CERBER_LOG_TABLE );
}
if ( $rows ) {
$no_results = false;
if ( empty( $args['no_navi'] ) ) {
// No cache
//$total = cerber_db_get_var( "SELECT FOUND_ROWS()" );
}
$tbody = '';
$country = '';
$geo = lab_lab();
if ( ! defined( 'CERBER_FULL_URI' ) || ! CERBER_FULL_URI ) {
$short = true;
$site_url = cerber_get_site_url();
$site_url = substr( $site_url, strpos( $site_url, '://' ) + 3 );
$start = strlen( rtrim( $site_url, '/' ) );
}
else {
$short = false;
$start = 0;
}
foreach ( $rows as $row ) {
$ip_id = cerber_get_id_ip( $row->ip );
$activity = '' . crb_get_label( $row->activity, $row->user_id, $row->ac_by_user ) . ' ';
if ( empty( $args['no_details'] ) && $row->details ) {
$details = explode( '|', $row->details );
$status = (int) crb_array_get( $details, 0, 0 );
if ( ! empty( $status ) && $sts_label = crb_array_get( $status_labels, $status ) ) {
$activity .= ' ' . $sts_label . ' ';
}
if ( ( $uri = crb_array_get( $details, 4 ) )
&& ( $row->activity < 10 || $row->activity > 12 ) ) {
if ( $short && 0 === strpos( $uri, $site_url ) ) {
$ac_uri = substr( $uri, $start );
}
else {
$ac_uri = $uri;
}
$activity .= '' . str_replace( array( '-', '/' ), array(
'-',
'/'
), htmlspecialchars( urldecode( $ac_uri ) ) ) . '
';
}
}
$activity = '' . $activity . '
';
$name = crb_admin_get_user_cell( $row->user_id, $base_url );
$username = '';
if ( $row->user_login ) {
$login_used = htmlspecialchars( $row->user_login );
$username = '' . $login_used . ' ';
}
$ip_info = cerber_get_ip_info( $row->ip, true );
$hostname = $ip_info['hostname_html'] ?? crb_get_ajax_placeholder( 'hostname', $ip_id );
if ( ! empty( $args['date'] ) && $args['date'] == 'ago' ) {
$date = cerber_ago_time( $row->stamp );
}
else {
$date = '' . cerber_date( $row->stamp ) . ' ';
}
if ( $geo ) {
$country = '' . crb_country_html( $row->country, $row->ip );
}
$tbody .= ' ' . crb_admin_ip_cell( $row->ip, $base_url . '&filter_ip=' . $row->ip ) . ' ' . $hostname . $country . ' ' . $date . ' ' . $activity . ' ' . $name . ' ' . $username . ' ';
}
$heading = array(
'crb_ip_col' => '
' . __( 'IP Address', 'wp-cerber' ),
'crb_hostname_col' => __( 'Hostname', 'wp-cerber' ),
'crb_country_col' => __( 'Country', 'wp-cerber' ),
'crb_date_col' => __( 'Date', 'wp-cerber' ),
'crb_event_col' => __( 'Event', 'wp-cerber' ),
'crb_user_col' => __( 'Local User', 'wp-cerber' ),
'crb_username_col' => __( 'Username', 'wp-cerber' )
);
if ( ! $geo ) {
unset( $heading['crb_country_col'] );
}
$titles_head = '';
foreach ( $heading as $id => $title ) {
$titles_head .= '' . $title . ' ';
}
$titles_foot = '' . implode( ' ', $heading ) . ' ';
$class = 'widefat crb-table cerber-margin crb-' . count( $heading ) . '-columns';
$table = '' . $titles_head . ' ' . $titles_foot . ' ' . $tbody . '
';
if ( empty( $args['no_navi'] ) ) {
$table .= cerber_page_navi( $total, $per_page );
}
//$legend = ''.sprintf(__('Showing last %d records from %d','wp-cerber'),count($rows),$total);
if ( empty( $args['no_export'] ) ) {
$export_link .= ' ' . __( 'Export', 'wp-cerber' ) . ' ';
//) ) . '"> ' . __( 'Export', 'wp-cerber' ) . '';
}
}
else {
// No results found -------------------------------------------------------------
$no_results = true;
// Is it a search (filter) request?
$is_search = ( array_intersect_key( CRB_ACT_PARAMS, crb_get_query_params() ) );
$hints = array();
if ( ! $is_search ) {
$hints[] = __( 'No activity has been logged yet.', 'wp-cerber' );
}
else {
$hints[] = __( 'No events found using the given search criteria', 'wp-cerber' );
if ( crb_admin_alert_exists() ) {
$hints[] = __( 'You will be notified when such an event occurs', 'wp-cerber' ) . ' [ ' . crb_admin_alert_dialog( false, '', __( 'Delete', 'wp-cerber' ) ) . ' ]';
}
elseif ( $alert_link = crb_admin_alert_dialog( false, __( 'Get me notified when such an event occurs', 'wp-cerber' ) ) ) {
$hints[] = $alert_link;
}
if ( ! $is_log_empty ) {
$hints[] = '' . __( 'View all logged events', 'wp-cerber' ) . ' ';
}
$trf_params = array();
if ( cerber_is_ip_or_net( $search ) ) {
$trf_params['filter_ip'] = $search;
}
if ( $trf_params ) {
$hints[] = '
' . __( 'Check for requests from the IP address', 'wp-cerber' ) . ' ' . $trf_params['filter_ip'] . '
';
}
}
$table = '' . implode( '
', $hints ) . '
';
}
if ( empty( $args['no_navi'] ) ) {
$display = ( $sid || $in_url ) ? '' : 'display: none;';
$filters = '
' . __( 'Filter', 'wp-cerber' ) . '
';
$right_links = '';
if ( ! $no_results ) {
$right_links = crb_admin_alert_dialog();
}
$right_links .= $export_link;
$top_bar = '' . $filters . '
' . $right_links . '
';
$quick = '';
if ( ! $is_log_empty ) {
$quick = crb_admin_activity_nav_links();
}
$ret = '' . $quick . '
' . $top_bar . $info . '
' . $ret;
}
$ret .= $table;
if ( $echo ) {
echo $ret;
}
else {
return $ret;
}
}
/**
* Parse arguments and create SQL query for retrieving rows from activity log
*
* @param array $args Optional arguments to use them instead of using $_GET
*
* @return array
* @since 4.16
*/
function cerber_activity_query( $args = array() ) {
global $wpdb;
$ret = array_fill( 0, 9, '' );
$where = array();
$q = crb_admin_parse_query( array_keys( CRB_ACT_PARAMS ), $args );
$falist = array();
if ( ! empty( $q->filter_set ) ) {
$falist = crb_get_filter_set( $q->filter_set );
}
elseif ( $q->filter_activity ) { // Multiple activities can be requested this way: &filter_activity[]=11&filter_activity[]=7
$falist = crb_sanitize_int( $q->filter_activity );
}
if ( $falist ) {
$where[] = 'log.activity IN (' . implode( ',', $falist ) . ')';
}
$ret[2] = $falist;
if ( $q->filter_status ) {
if ( $status = crb_sanitize_int( $q->filter_status ) ) {
$where[] = 'log.ac_status IN (' . implode( ',', $status ) . ')';
}
}
if ( $q->filter_ip ) {
$range = cerber_any2range( $q->filter_ip );
if ( is_array( $range ) ) {
$where[] = '(log.ip_long >= ' . $range['begin'] . ' AND log.ip_long <= ' . $range['end'] . ')';
}
elseif ( cerber_is_ip_or_net( $q->filter_ip ) ) {
$where[] = 'log.ip = "' . $q->filter_ip . '"';
}
else {
$where[] = "ip = 'produce-no-result'";
}
$ret[3] = preg_replace( CRB_IP_NET_RANGE, ' ', $q->filter_ip );
}
if ( $q->filter_login ) {
if ( strpos( $q->filter_login, '|' ) ) {
$sanitize = preg_replace( '/["\'<>\/]+/', '', $q->filter_login );
$logins = explode( '|', $sanitize );
$where[] = '( log.user_login = "' . implode( '" OR log.user_login = "', $logins ) . '" )';
}
else {
$where[] = $wpdb->prepare( 'log.user_login = %s', $q->filter_login );
}
$ret[4] = htmlspecialchars( $q->filter_login );
}
if ( isset( $q->filter_user ) ) {
if ( $q->filter_user == '*' ) {
$where[] = 'log.user_id != 0';
$ret[5] = '';
}
elseif ( is_numeric( $q->filter_user ) ) {
$user_id = absint( $q->filter_user );
//$where[] = 'log.user_id = ' . $user_id;
$where[] = '( log.user_id = ' . $user_id . ' OR log.ac_by_user =' . $user_id . ')';
$ret[5] = $user_id;
}
}
if ( $q->search_activity ) {
$search = stripslashes( $q->search_activity );
$ret[6] = htmlspecialchars( $search );
$search = '%' . $search . '%';
$escaped = cerber_real_escape( $search );
$w = array();
$w[] = 'log.user_login LIKE "' . $escaped . '"';
$w[] = 'log.ip LIKE "' . $escaped . '"';
$where[] = '(' . implode( ' OR ', $w ) . ')';
}
if ( $q->filter_sid ) {
$ret[7] = $sid = preg_replace( '/[^\w]/', '', $q->filter_sid );
$where[] = 'log.session_id = "' . $sid . '"';
}
if ( $q->search_url ) {
$search = stripslashes( $q->search_url );
$ret[8] = htmlspecialchars( $search );
$where[] = 'log.details LIKE "' . cerber_real_escape( '%' . $search . '%' ) . '"';
}
if ( $q->filter_country ) {
$country = substr( $q->filter_country, 0, 3 );
$ret[9] = htmlspecialchars( $country );
$where[] = 'log.country = "' . cerber_real_escape( $country ) . '"';
}
$where = ( ! empty( $where ) ) ? 'WHERE ' . implode( ' AND ', $where ) : '';
// Limits, if specified
$per_page = ( isset( $args['per_page'] ) ) ? absint( $args['per_page'] ) : crb_admin_get_per_page();
$ret[1] = $per_page;
$calc = ( empty( $args['no_navi'] ) ) ? 'SQL_CALC_FOUND_ROWS' : '';
if ( $per_page ) {
$limit = cerber_get_sql_limit( $per_page );
$sql = 'SELECT ' . $calc . ' * FROM ' . CERBER_LOG_TABLE . " log {$where} ORDER BY stamp DESC {$limit}";
}
else {
$sql = 'SELECT ' . $calc . ' log.*,u.display_name,u.user_login ulogin FROM ' . CERBER_LOG_TABLE . ' log LEFT JOIN ' . $wpdb->users . " u ON (log.user_id = u.ID) {$where} ORDER BY stamp DESC"; // "ORDER BY" is mandatory!
}
$ret[0] = $sql;
return $ret;
}
function crb_admin_ip_cell( $ip, $ip_link = '', $text = '' ) {
static $cache = array();
if ( isset( $cache[ $ip ] ) ) {
return $cache[ $ip ];
}
$row = true;
$acl = cerber_acl_check( $ip, '', 0, $row );
$tip = '';
if ( ! empty( $row->comments ) ) {
$tip = $row->comments;
}
else {
if ( $acl == 'W' ) {
$tip = __( 'White IP Access List', 'wp-cerber' );
}
elseif ( $acl == 'B' ) {
$tip = __( 'Black IP Access List', 'wp-cerber' );
}
}
if ( $b = cerber_block_check( $ip ) ) {
$block = ' crb_color_blocked ';
$tip = ( $tip ) ? ' / ' . $b->reason : $b->reason;
}
else {
$block = '';
}
if ( $ip_link ) {
$ip = '' . $ip . ' ';
}
$cache[ $ip ] = '';
return $cache[ $ip ];
}
/*
* Additional information about IP address
*/
function cerber_ip_extra_view( $ip, $context = 'activity' ) {
if ( ! $ip || ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
return '';
}
$html = crb_generate_ip_extra_view( $ip, $context, true );
$class = 'crb-extra-info';
$ajax = '';
if ( ! $html ) {
$ajax = ' data-ajax_route="ip_extra_info" data-ip="' . $ip . '"';
$class .= ' crb_async_content';
$html = UIS_LOADER_HTML;
}
return '';
}
/**
* @param array $request
* @param array $ref_params
*
* @return array
*/
function cerber_ip_extra_view_ajax( $request, $ref_params ) {
$ip = filter_var( $request['ip'], FILTER_VALIDATE_IP );
$tab = $ref_params['tab'] ?? '';
$ret = crb_generate_ip_extra_view( $ip, $tab );
return array( 'html' => $ret, 'error' => '' );
}
function crb_generate_ip_extra_view( $ip, $context = 'activity', $cache_only = false ) {
if ( ! $ip || ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
return '';
}
$ip_navs = '';
if ( $context == 'activity' ) {
$ip_navs .= cerber_traffic_link( array( 'filter_ip' => $ip ) );
}
else {
$ip_navs .= ' ' . __( 'Check for activities', 'wp-cerber' ) . ' ';
}
$row = true;
$acl = cerber_acl_check( $ip, '', 0, $row );
$comments = crb_attr_escape( $row->comments );
$ip_status = '';
if ( $acl == 'W' ) {
$ip_status .= '' . __( 'White IP Access List', 'wp-cerber' ) . ' ';
}
elseif ( $acl == 'B' ) {
$ip_status .= '' . __( 'Black IP Access List', 'wp-cerber' ) . ' ';
}
$key_cache = '_cache_ipx_' . $ip . $context . (string) $acl;
if ( $block = cerber_block_check( $ip ) ) {
$ip_status .= '' . __( 'Locked out', 'wp-cerber' ) . ' ' . $block->reason . ' ';
$key_cache .= '_blocked';
}
if ( $cache = cerber_get_set( $key_cache, null, false, true ) ) {
return $cache;
}
elseif ( $cache_only ) {
return '';
}
$whois = '';
$country = '';
$abuse = '';
$network = '';
$network_info = '';
$network_navs = '';
if ( crb_get_settings( 'ip_extra' ) ) {
$ip_data = cerber_ip_whois_info( $ip );
if ( isset( $ip_data['error'] ) ) {
$whois = $ip_data['error'];
}
elseif ( isset( $ip_data['whois'] ) ) {
$whois = $ip_data['whois'];
}
if ( isset( $ip_data['country'] ) ) {
$country = $ip_data['country'];
}
if ( ! empty( $ip_data['data']['abuse-mailbox'] ) ) {
$abuse = '' . __( 'Abuse email:', 'wp-cerber' ) . ' ' . $ip_data['data']['abuse-mailbox'] . '
';
}
if ( ! empty( $ip_data['data']['network'] ) ) {
$network = $ip_data['data']['network'];
$range = cerber_any2range( $network );
//$network_info = '' . __( 'Network:', 'wp-cerber' ) . ' ' . $network . ' ' . __( 'Check for activities', 'wp-cerber' ) . ' ' . cerber_traffic_link( array( 'filter_ip' => $range['range'] ) );
$network_info = __( 'Network:', 'wp-cerber' ) . ' ' . $network;
$network_navs = '' . __( 'Check for activities', 'wp-cerber' ) . ' ' . cerber_traffic_link( array( 'filter_ip' => $range['range'] ) );
}
}
$form = '';
if ( ! cerber_is_myip( $ip ) && ! cerber_acl_check( $ip ) ) {
if ( $network ) {
$net_button = '';
}
else {
$net_button = '';
}
$net_button .= __( 'Add network to the Black List', 'wp-cerber' ) . ' ';
$form = '
' . __( 'Add IP to the Black List', 'wp-cerber' ) . ' ' .
'' .
cerber_nonce_field( 'control' ) .
' ';
}
$ip_info = '' . $ip . ' ' . $country . ' ';
$ip_insights = crb_generate_ip_insights( $ip, $context );
$ret = '
' . $whois . '
';
cerber_update_set( $key_cache, $ret, 0, false, time() + 7200, true );
return $ret;
}
/**
* @param string $ip
* @param string $tab
* @param false $cache_only
*
* @return string|false
*/
function crb_generate_ip_insights( $ip, $tab = 'activity', $cache_only = false ) {
if ( $tab != 'activity' ) {
$tab = 'traffic';
}
$result = '';
$links = array();
$labels = cerber_get_labels();
if ( $data = crb_q_cache_get( 'SELECT COUNT(activity) as cnt, activity FROM ' . CERBER_LOG_TABLE . ' WHERE ip = "' . $ip . '" GROUP BY activity', CERBER_LOG_TABLE, $cache_only ) ) {
$links[] = array( array( 'filter_ip' => $ip ), __( 'All' ) );
foreach ( $data as $item ) {
$links[] = array( array( 'filter_activity' => $item[1], 'filter_ip' => $ip ), crb_array_get( $labels, $item[1], 'Unknown' ) . ' - ' . $item[0] );
}
}
if ( $links ) {
$result = '' . crb_make_nav_links( $links, $tab ) . '
';
}
if ( ! $result ) {
if ( $cache_only ) {
return false;
}
$result = __( 'No activity has been logged yet.', 'wp-cerber' );
}
return $result;
}
/**
* Prepare a short report (extra information) about a given user
*
* @param int $user_id
* @param string $context
*
* @return string
*/
function cerber_user_extra_view( $user_id, $context = 'activity' ) {
global $wpdb;
if ( ! $u = get_userdata( $user_id ) ) {
return '';
}
$ret = '';
$class = '';
$user_profile = '';
$user_summary = array();
if ( $avatar = (string) get_avatar( $user_id, 96 ) ) {
$user_profile = '' . $avatar . '
';
}
if ( ! is_multisite() && $u->roles ) {
$roles = array();
$wp_roles = wp_roles();
foreach ( $u->roles as $role ) {
$roles[] = $wp_roles->roles[ $role ]['name'];
}
$roles = '' . implode( ', ', $roles ) . ' ';
}
else {
$roles = '';
}
if ( ! nexus_is_valid_request() ) {
$name = '' . $u->display_name . ' ';
}
else {
$name = $u->display_name;
}
$name = '' . $name . ' ' . $roles . '
';
$user_profile .= '' . $name . '
';
// Last seen
$seen = array();
$seen[0] = $wpdb->get_row( 'SELECT stamp,ip FROM ' . CERBER_TRAF_TABLE . ' WHERE user_id = ' . $user_id . ' ORDER BY stamp DESC LIMIT 1' );
$seen[1] = $wpdb->get_row( 'SELECT stamp,ip FROM ' . CERBER_LOG_TABLE . ' WHERE user_id = ' . $user_id . ' AND ac_by_user = 0 ORDER BY stamp DESC LIMIT 1' );
$seen[2] = $wpdb->get_row( 'SELECT stamp,ip FROM ' . CERBER_LOG_TABLE . ' WHERE ac_by_user = ' . $user_id . ' ORDER BY stamp DESC LIMIT 1' );
$tmp = array();
foreach ( $seen as $key => $log_row ) {
$tmp[ $key ] = ( $log_row ) ? $log_row->stamp : 0;
}
if ( $max = max( $tmp ) ) {
$max_keys = array_keys( $tmp, $max );
$last_log = $seen[ $max_keys[0] ];
$last_seen = '' . cerber_ago_time( $last_log->stamp ) . ' ';
$country = crb_country_html( null, $last_log->ip );
$user_summary[] = array( __( 'Last seen', 'wp-cerber' ), $last_seen, $country );
}
// Last login
$last_login = cerber_get_last_login( $user_id );
if ( ! empty( $last_login ) ) {
$country = crb_country_html( null, $last_login->ip );
$user_summary[] = array( __( 'Last login', 'wp-cerber' ), cerber_date( $last_login->stamp, false ), $country );
}
// Registered (created)
if ( $time = strtotime( cerber_db_get_var( "SELECT user_registered FROM {$wpdb->users} WHERE id = " . $user_id ) ) ) {
$reg_date = cerber_auto_date( $time, false );
$reg_event = __( 'Registered', 'wp-cerber' );
$country = '';
if ( $reg_meta = get_user_meta( $user_id, '_crb_reg_', true ) ) {
if ( $reg_meta['IP'] ?? false ) {
$country = crb_country_html( null, $reg_meta['IP'] );
}
if ( $reg_meta['user'] ?? false ) {
$reg_event = crb_get_label( 1, $user_id, $reg_meta['user'] );
}
}
$user_summary[] = array( $reg_event, $reg_date, $country );
}
// Activated - BuddyPress
if ( $log = cerber_get_log( array( 200 ), array( 'id' => $user_id ) ) ) {
$acted = $log[0];
$activated = cerber_auto_date( $acted->stamp );
$country = crb_country_html( null, $acted->ip );
$user_summary[] = array( __( 'Activated', 'wp-cerber' ), $activated, $country );
}
$usn = crb_sessions_get_num( $user_id );
$sess_link = $usn ? '' . $usn . ' ' : '0';
$user_summary[] = array(
'' . __( 'Active sessions', 'wp-cerber' ) . '
',
$sess_link
);
// Make a link to switch to other log
if ( $context == 'activity' ) {
$link = cerber_traffic_link( array( 'filter_user' => $user_id ) , 2 );
}
else {
$link = ' ' . __( 'Check for activities', 'wp-cerber' ) . ' ';
}
$user_summary[] = array( $link );
$summary = '';
foreach ( $user_summary as $row ) {
$row = array_pad( $row, 3, '' );
$summary .= '' . implode( ' ', $row ) . ' ';
}
if ( $us_sts = crb_get_user_auth_status( $u ) ) {
$summary .= '' . crb_format_user_status( $us_sts[0], $us_sts[1], $us_sts[2] ) . '
';
}
$summary = '';
$ins_class = '';
if ( ! $ins = crb_generate_user_insights( $user_id, $context, true ) ) {
$ins_class = 'crb_async_content';
}
$ret .= '';
return '';
}
// Users -------------------------------------------------------------------------------------
add_filter('users_list_table_query_args' , function ($args) {
if ( crb_get_settings( 'usersort' ) && empty( $args['orderby'] ) ) {
$args['orderby'] = 'user_registered';
$args['order'] = 'desc';
}
return $args;
});
/*
Add custom columns to the Users admin screen
*/
add_filter( 'manage_users_columns', function ( $columns ) {
return array_merge( $columns,
array(
'cbcc' => __( 'Comments', 'wp-cerber' ),
'cbla' => __( 'Last login', 'wp-cerber' ),
'cbfl' => '' . __( 'Failed login attempts', 'wp-cerber' ) . ' ',
'cbdr' => __( 'Registered', 'wp-cerber' )
) );
} );
add_filter( 'manage_users_sortable_columns', function ( $sortable_columns ) {
$sortable_columns['cbdr'] = 'user_registered';
return $sortable_columns;
} );
/*
Display custom columns on the Users screen
*/
add_filter( 'manage_users_custom_column', function ( $value, $column, $user_id ) {
global $wpdb, $user_ID;
$ret = $value;
switch ( $column ) {
case 'cbcc' :
case 'cbla' :
case 'cbfl' :
$ret = crb_get_ajax_placeholder( $column, $user_id );
break;
case 'cbdr' :
$time = strtotime( cerber_db_get_var( "SELECT user_registered FROM $wpdb->users WHERE id = " . $user_id ) );
$ret = cerber_auto_date( $time );
if ( $reg_data = get_user_meta( $user_id, '_crb_reg_', true ) ) {
if ( $ip = filter_var( $reg_data['IP'], FILTER_VALIDATE_IP ) ) {
$act_link = cerber_admin_link( 'activity', array( 'filter_ip' => $ip ) );
$ret .= '' . $ip . ' ';
if ( $country = crb_country_html( null, $ip ) ) {
$ret .= ' ' . $country;
}
}
if ( $uid = absint( $reg_data['user'] ) ) {
$name = cerber_db_get_var( 'SELECT meta_value FROM ' . $wpdb->usermeta . ' WHERE user_id = ' . $uid . ' AND meta_key = "nickname"' );
if ( ! $user_ID ) {
$user_ID = get_current_user_id();
}
if ( $user_ID == $uid ) {
$name .= ' (' . __( 'You', 'wp-cerber' ) . ')';
}
$ret .= ' ' . $name;
}
}
break;
}
return $ret;
}, 10, 3 );
/*
Registering admin widgets
*/
if ( ! is_multisite() ) {
add_action( 'wp_dashboard_setup', 'cerber_widgets' );
}
else {
add_action( 'wp_network_dashboard_setup', 'cerber_widgets' );
}
function cerber_widgets() {
if ( cerber_user_can_manage() ) {
wp_add_dashboard_widget( 'cerber_quick', __( 'Cerber Quick View', 'wp-cerber' ), 'cerber_quick_w' );
}
}
/*
Cerber Quick View widget
*/
function cerber_quick_w(){
$dash = cerber_admin_link();
$act = cerber_admin_link( 'activity' );
$traf = cerber_admin_link( 'traffic' );
$scanner = cerber_admin_link( 'scan_main' );
$acl = cerber_admin_link( 'acl' );
$sess = cerber_admin_link( 'sessions' );
$locks = cerber_admin_link( 'lockouts' );
$s_count = cerber_db_get_var('SELECT COUNT(DISTINCT user_id) FROM '. cerber_get_db_prefix() . CERBER_USS_TABLE );
$failed = cerber_db_get_var( 'SELECT count(ip) FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . CRB_EV_LFL . ') AND stamp > ' . ( time() - 24 * 3600 ) );
$failed_prev = cerber_db_get_var( 'SELECT count(ip) FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . CRB_EV_LFL . ') AND stamp > ' . ( time() - 48 * 3600 ) . ' AND stamp < ' . ( time() - 24 * 3600 ) );
$failed_ch = cerber_percent($failed_prev,$failed);
$locked = cerber_db_get_var('SELECT count(ip) FROM '. CERBER_LOG_TABLE .' WHERE activity IN (10,11) AND stamp > '.(time() - 24 * 3600));
$locked_prev = cerber_db_get_var('SELECT count(ip) FROM '. CERBER_LOG_TABLE .' WHERE activity IN (10,11) AND stamp > '.(time() - 48 * 3600).' AND stamp < '.(time() - 24 * 3600));
$locked_ch = cerber_percent($locked_prev,$locked);
//$lockouts = $wpdb->get_var('SELECT count(ip) FROM '. CERBER_BLOCKS_TABLE);
$lockouts = cerber_blocked_num();
if ($last = cerber_db_get_var('SELECT MAX(stamp) FROM '.CERBER_LOG_TABLE.' WHERE activity IN (10,11)')) {
$last = cerber_ago_time( $last );
}
else $last = __('Never','wp-cerber');
$w_count = cerber_db_get_var('SELECT count(ip) FROM '. CERBER_ACL_TABLE .' WHERE tag ="W"' );
$b_count = cerber_db_get_var('SELECT count(ip) FROM '. CERBER_ACL_TABLE .' WHERE tag ="B"' );
if ( cerber_is_citadel() ) {
$citadel = '' . __( 'active', 'wp-cerber' ) . ' (' . __( 'deactivate', 'wp-cerber' ) . ' )';
}
else {
if ( crb_get_settings( 'citadel_on' ) && crb_get_settings( 'ciperiod' ) ) {
$citadel = __( 'not active', 'wp-cerber' );
}
else {
$citadel = __( 'disabled', 'wp-cerber' );
}
}
echo '';
echo '';
if ( cerber_check_for_newer() ) {
echo '' . __( 'A new version is available', 'wp-cerber' ) . '
';
}
}
/*
Show Help tab screen
*/
function cerber_show_help() {
switch ( crb_admin_get_page()){
case 'cerber-integrity':
cerber_show_scan_help();
break;
case 'cerber-nexus':
cerber_show_nexus_help();
break;
case 'cerber-recaptcha':
cerber_show_anti_help();
break;
default:
cerber_show_general_help();
}
}
function cerber_show_nexus_help() {
?>
How remote management works
Our technology enables you to manage WP Cerber plugins, monitor activity, and upgrade plugins on multiple WordPress powered websites from a main Cerber.Hub website which is also called a main website.
To activate this technology, you need to enable a Cerber.Hub main mode on the main website and a managed mode on each website you want to connect to the main website.
Read more:
Manage multiple WP Cerber instances from one dashboard
A safety note
All access tokens are stored in the databases of the main and managed websites in unencrypted form (plaintext). Store a backup copy of all websites in a safe and trusted place.
Troubleshooting
If you’re unable to get it working, that may be caused by a number of reasons. Enable the diagnostic log on the main website and on the managed one to obtain more information. You can view the diagnostic log on the Tools admin page. Here is a list of the most common causes of issues on the managed website.
A security plugin on the managed website is interfering with the WP Cerber plugin
A security directive in the .htaccess file on the managed website is blocking incoming requests as suspicious
A firewall or a proxy service (like Cloudflare) is blocking (filtering out) incoming requests to the managed website
The IP address of the main website is locked out or in the Black Access List on the managed website
The managed mode on the remote website has been re-enabled making the security token saved on the main website invalid
The IP address of the main website does not match the one set in the access settings on the managed website
Getting started
Start with activating main Cerber.Hub website. Go to the Cerber.Hub admin page and enable main Cerber.Hub mode. Once you’ve done this you can connect managed websites by using security tokens generated on managed websites.
Connecting managed websites
To connect a remote website to the main website as a remote managed website, you need to enable the managed mode on that website. Go to the Cerber.Hub admin page and enable the managed mode. During the activation of the managed mode, a unique security access token is generated and saved into the database of the managed website. Keep the token secret.
Now, go to your main Cerber.Hub website and click the “Add” button on the “My Websites” admin page. Copy the token and paste it in the “Add a remote website” popup window.
Manage websites remotely
Once you’ve connected all your remote websites to the main website, you can easily switch between them with a single click in the top navigation menu on the admin bar or by clicking the name of a remote website on the “My Websites” page. Once you’ve switched to a remote managed website, use the plugin menu and admin links the way like you do this normally. To switch back to the main website, click a X icon on the admin bar.
Note: when you’re managing remote website, the color of the admin bar is blue and the left admin menu on the main website is dimmed.
Using the malware scanner
To start scanning, click either the Start Quick Scan button or the Start Full Scan button. Do
not close the browser window while the scan is in progress. You may just open a new browser
tab to do something else on the website. Once the scan is finished you can close the window,
the results are stored in the DB until the next scan.
Depending on server performance and the number of files, the Quick scan may take about 3-5
minutes and the Full scan can take about ten minutes or less.
During the scan, the plugin verifies plugins, themes, and WordPress by trying to retrieve
checksum data from wordpress.org. If the integrity data is not available, you can upload an
appropriate source ZIP archive for a plugin or a theme. The plugin will use it to detect
changes in files. You need to do it once, after the first scan.
Read more: Cerber
Security Scanner Settings
Interpreting scan results
The scanner shows you a list of issues and possible actions you can take. If the integrity of
an object has been verified, you see a green mark Verified. If you see the “Integrity data
not found” message, you need to upload a reference ZIP archive by clicking “Resolve issue”.
For all other issues, click on an appropriate issue link. To view the content of a file,
click on its name.
Troubleshooting
If the scanner window stops responding or updating, it usually means the process of scanning on the server is hung. This might happen due to a number of reasons but typically this happens due to a misconfigured server or it’s caused by some hosting limitations. Do the following:
Open the browser console (use F12 key on PC or Cmd + Option + J on Mac) and check it for CERBER ERROR messages
Try to disable scanning the session directory or the temp directory (or both) in the scanner settings
Enable diagnostic logging in the scanner settings and check the log after scanning
Note: The scanner requires the cURL library to be enabled for PHP scripts. Usually, it's enabled by
default.
Read more: Malware
Scanner & Integrity Checker
What's the Quick Scan?
During the Quick Scan, the scanner verifies the integrity and inspects the code of all files
with executable extensions only.
What's the Full Scan?
During the Full Scan, the scanner verifies the integrity and inspects the content of all
files on the website. All media files are scanned for malicious payload.
Read more: What Cerber Security Scanner scans and detects
Configuring scheduled scans
In the Automated recurring scan schedule section you set up your schedule. Select the desired
frequency of the Quick Scan and specify the time of the Full Scan. By default, all automated
recurring scans are turned off.
The Scan results reporting section is about reporting. Here you can easily and flexibly
configure conditions for generating and sending reports.
The email report will only include issues that match conditions in the Report an issue if any
of the following is true filter. So this setting works as a filter for issues you want to
get in a email report. The report will only be sent if there are issues to report and the
following condition is true.
The second condition is configured with Send email report. The report will be sent if a
selected condition is true. The last option is the most restrictive.
Read more: Automated
recurring scans and email reporting
Automatic cleanup of malware
The plugin can automatically delete malicious and suspicious files. Automatic removal
policies are enforced at the end of every scheduled scan based on its results. The list of
files to be deleted depends on the scanner settings. By default automatic removal is
disabled. It's advised to enable it at least for unattended files and files in the media
uploads folder for files with the High severity risk. The plugin deletes only files that
have malicious or suspicious code payload. All detected malicious and suspicious files are
moved to the quarantine.
Read more:
Automatic cleanup of malware and suspicious files
Deleting files
Usually, you can delete any suspicious or malicious file if it has a checkbox in its row in
the leftmost cell. Before deleting a file, click the issue link in its row to see an
explanation. When you delete a file WP Cerber moves it to a quarantine folder.
Restoring deleted files
If you delete an important file by chance, you can restore the file from the quarantine. To
restore one or more files from within the WordPress dashboard, click the Quarantine tab.
Find the filename in the File column and click Restore in the Action column. The file will
be restored to its original location.
To restore a file manually, you need to use a file manager in your hosting control panel. All
deleted files are stored in a special quarantine folder. The location of the folder is shown
on the Tools / Diagnostic admin page. The original name and location of a deleted file are
saved in a .restore file. It’s a text file. Open it in a browser or a file viewer, find the
filename you need to restore in a list of deleted files and copy the file back to its
location by using the original name and location of the file.
Setting up anti-spam protection
The Cerber anti-spam and bot detection engine is capable to protect virtually any form on a website. It’s a great alternative to reCAPTCHA.
Tested with Caldera Forms, Gravity Forms, Contact Form 7, Ninja Forms, Formidable Forms, Fast Secure Contact Form, Contact Form by WPForms and WooCommerce forms.
How to stop spam user registrations on your WordPress
How to stop spam form submissions on your WordPress
Configuring exceptions for the anti-spam engine
Usually, you need to specify an exception if you use a plugin or some technology that communicates with your website by submitting forms or sending POST requests programmatically. In this case, Cerber can block these legitimate requests because it recognizes them as generated by bots. This may lead to multiple false positives which you can see on the Activity tab. These entries are marked as Spam form submission denied .
Configuring exceptions for the anti-spam engine
How to set up reCAPTCHA
Before you can start using reCAPTCHA on the website, you have to obtain a Site key and a Secret key on the Google website. To get the keys you have to have Google account.
Register your website and get both keys here: https://www.google.com/recaptcha/admin
Note: If you are going to use an invisible version, you must get and use Site key and a Secret key for the invisible version only.
How to set up reCAPTCHA in details
Why does reCAPTCHA not protect WordPress against bots and brute-force attacks?
What is Drill down IP?
To get extra information like country, company, network info, abuse contact etc. for a specific IP address,
the plugin makes requests to a limited set of external WHOIS servers which are maintained by appropriate
Registry. All Registry are accredited by ICANN, so there are no reasons for security concerns. Retrieved
information isn't storing in the database, but it is caching for up to 24 hours to avoid excessive requests and
get faster response.
Read more in the Security Blog
What is Cerber Lab?
Cerber Laboratory is a forensic team at Cerber Tech Inc. The team studies and analyzes
patterns of hacker and botnet attacks, malware, vulnerabilities in major plugins and how they are
exploitable on WordPress powered websites.
Know more
Do you have an idea for a cool new feature that you would love to see in WP Cerber?
Feel free to submit your ideas here: New Feature
Request .
Are you ready to translate this plugin into your language?
I would appreciate that! Please, notify me
Check out other plugins from the trusted author
Plugin for inspecting code of plugins on your site: Plugin Inspector
The Plugin Inspector plugin is an easy way to check plugins installed on your
WordPress and make sure
that plugins does not use deprecated WordPress functions and some unsafe functions like eval,
base64_decode, system, exec etc. Some of those functions may be used to load malicious code (malware)
from the external source directly to the site or WordPress database.
Plugin Inspector allows you to view all the deprecated functions complete with
path, line number,
deprecation function name, and the new recommended function to use. The checks are run through a simple
admin page and all results are displayed at once. This is very handy for plugin developers or anybody
who want to know more about installed plugins.
Google Translate Widget expands your global reach quickly and easily. Google Translate is a free
multilingual machine translation service provided by Google to translate websites. And now you can allow
visitors around of the world to get your site in their native language. Just put widget on the sidebar
with one click.
Submit a support ticket on our Help Desk: https://my.wpcerber.com
';
}
else {
$support = 'Support for the free version is provided on the WordPress forum only , though, please note, that it is free support hence it is
not always possible to answer all questions on a timely manner, although we do try.
If you need professional and priority support 24/7/365, please buy a PRO license
';
}
?>
How to configure the plugin
To get the most out of WP Cerber Security, you need to configure the plugin properly
Please read this first: Getting Started Guide
Do you have a question or need help?
Get answer on the support forum
Search plugin documentation on wpcerber.com
' . $kpi[1] . '' . $kpi[0] . ' ';
}
$kpi_show = '';
// TODO: add link "send daily report to my email"
// $kpi_show .= '' . __( 'in the last 24 hours', 'wp-cerber' ) . '
';
echo '' . $kpi_show . '
';
$placeholder = '' . __( 'No activity has been logged yet.', 'wp-cerber' ) . '
';
$user_logged = cerber_db_get_var( 'SELECT ip FROM ' . CERBER_LOG_TABLE . ' WHERE user_id !=0 LIMIT 1' );
if ( ! $user_logged ) {
$nav_links = '';
$user_act = $placeholder;
}
else {
$nav_links = crb_admin_activity_nav_links( 'users' );
$user_act = cerber_show_activity( array(
'filter_user' => '*',
'per_page' => 10,
'no_navi' => true,
'no_export' => true,
'no_details' => true,
'date' => 'ago'
), false );
}
$dash_widgets[] = array( __( "Users' Activity", 'wp-cerber' ), $user_act, $nav_links );
$in = implode( ',', crb_get_filter_set( 1 ) );
$bad_logged = cerber_db_get_var( 'SELECT ip FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . $in . ') LIMIT 1' );
if ( ! $bad_logged ) {
$susp_act = $placeholder;
}
else {
$susp_act = cerber_show_activity( array(
'filter_set' => 1,
'per_page' => 10,
'no_navi' => true,
'no_export' => true,
'no_details' => true,
'date' => 'ago'
), false );
}
$nav_links = ( $bad_logged ) ? crb_admin_activity_nav_links( 'suspicious' ) : '';
$dash_widgets[] = array( __( 'Malicious Activity', 'wp-cerber' ), $susp_act, $nav_links );
/*$total = cerber_db_get_var( 'SELECT count(ip) FROM ' . CERBER_LOG_TABLE );
$nav_links = ( $total ) ? crb_admin_quick_nav( 'dashboard' ) : '';
echo '' . __( 'Activity', 'wp-cerber' ) . ' ' . $nav_links . '
';
/*cerber_show_activity( array(
'filter_activity' => crb_get_activity_set( 'dashboard' ),
'per_page' => 10,
'no_navi' => true,
'no_export' => true,
'no_details' => true,
'date' => 'ago'
) );*/
$locked = cerber_blocked_num();
if ( ! $locked ) {
$view_all = '';
$locked_ips = '' . __( 'No lockouts at the moment. The sky is clear.', 'wp-cerber' ) . '
';
}
else {
$view_all = '' . __( 'View all', 'wp-cerber' ) . ' ';
$locked_ips = cerber_show_lockouts( array(
'per_page' => 10,
'no_navi' => true
,
), false );
}
$dash_widgets[] = array( __( 'Recently locked out IP addresses', 'wp-cerber' ), $locked_ips, $view_all );
foreach ( $dash_widgets as $dash_widget ) {
$heading = '' . $dash_widget[0] . ' ' . $dash_widget[2] . '
';
echo '' . $heading . $dash_widget[1] . '
';
}
}
/*
Admin aside bar
*/
function cerber_show_aside( $tab ) {
if ( in_array( $tab, array( 'nexus_sites', 'activity', 'lockouts', 'traffic' ) ) ) {
return;
}
$aside = array();
if ( $tab != 'scan_main' && ! lab_lab() && crb_was_activated( 1 * DAY_IN_SECONDS ) ) {
/*$aside[] =
' Follow Cerber on Twitter ';
*/
// Subscribe to Cerber\'s newsletter
// Follow Cerber on Facebook
//';
$images = array( 'bn4ra.png', 'bn5ra.png' );
$d = (int) date( 'z' );
$n = ( $d & 1 ) ? 1 : 0;
$ban = CRB_Globals::$assets_url . $images[ $n ];
$aside[] = ' ';
}
$context_links = '';
$docs = crb_get_doc_links();
foreach ( $docs as $doc ) {
$context_links .= '' . $doc[1] . '
';
}
$aside[] = '
Documentation & How To
' . $context_links . ' ';
if ( ! lab_lab() && crb_was_activated( 2 * WEEK_IN_SECONDS ) ) {
$r = array();
$r[0] = crb_get_review_url( 'tpilot' );
$r[1] = crb_get_review_url( 'wp' );
shuffle( $r );
$aside[] = ' ';
}
echo '' . implode( ' ', $aside ) . '
';
}
function crb_get_doc_links() {
static $links = array(
'cerber-integrity' => array(
array( 'https://wpcerber.com/wordpress-security-scanner/', 'How to use the integrity checker and malware scanner' ),
array( 'https://wpcerber.com/wordpress-security-scanner-scan-malware-detect/', 'What the scanner scans and detects' ),
array( 'https://wpcerber.com/malware-scanner-settings/', 'Configuring the scanner settings' ),
array( 'https://wpcerber.com/troubleshooting-malware-scanner/', 'Troubleshooting malware scanner issues' ),
array( 'https://wpcerber.com/automatic-malware-removal-wordpress/', 'Automatic cleanup of malware and suspicious files' ),
array( 'https://wpcerber.com/automated-recurring-malware-scans/', 'Automated recurring scans and email reporting' )
),
'*' => array(
array( 'https://wpcerber.com/wordpress-application-passwords-how-to/', 'Managing WordPress application passwords a hassle-free way' ),
array( 'https://wpcerber.com/how-to-protect-wordpress-checklist/', 'How to protect WordPress effectively: a must-do list' ),
array( 'https://wpcerber.com/manage-multiple-websites/', 'Managing multiple WP Cerber instances from one dashboard' ),
array( 'https://wpcerber.com/wordpress/gdpr/', 'Stay in compliance with GDPR' ),
array( 'https://wpcerber.com/two-factor-authentication-for-wordpress/', 'How to protect user accounts with Two-Factor Authentication' ),
array( 'https://wpcerber.com/limiting-concurrent-user-sessions-in-wordpress/', 'How to stop your users from sharing their WordPress accounts' ),
array( 'https://wpcerber.com/wordpress-mobile-and-browser-notifications-pushbullet/', 'Be notified with mobile and browser notifications' ),
array( 'https://wpcerber.com/wordpress-notifications-made-easy/', 'WordPress notifications made easy' ),
array( 'https://wpcerber.com/restrict-access-to-wordpress-rest-api/', 'How to restrict access to REST API' ),
array( 'https://wpcerber.com/automatic-malware-removal-wordpress/', 'Automatic cleanup of malware and suspicious files' ),
array( 'https://wpcerber.com/automated-recurring-malware-scans/', 'Automated recurring scans and email reporting' ),
),
);
static $get_started = array( 'https://wpcerber.com/getting-started/', 'Getting Started Guide ' );
$ret = crb_array_get( $links, crb_admin_get_page() );
$common = crb_array_get( $links, '*' );
if ( ! $ret ) {
array_unshift( $common, $get_started );
return $common;
}
$ret = array_merge( $ret, array_slice( $common, 0, 5 ) );
$ret[] = $get_started;
return $ret;
}
/*
Displaying notices in the dashboard
*/
add_action( 'load-plugins.php', function () {
add_action( 'admin_notices', 'cerber_show_admin_notice', 999 );
add_action( 'network_admin_notices', 'cerber_show_admin_notice', 999 );
} );
function cerber_show_admin_notice() {
global $cerber_shown;
$cerber_shown = false;
if ( nexus_get_context() ) {
return;
}
if ( cerber_is_citadel() && cerber_user_can_manage() ) {
echo '';
}
if ( ! nexus_is_valid_request() && ! cerber_is_admin_page( null, true ) ) {
return;
}
//if ($notices = get_site_option('cerber_admin_notice'))
// echo ''; // class="updated" - green, class="update-nag" - yellow and above the page title,
//if ($notices = get_site_option('cerber_admin_message'))
// echo ''; // class="updated" - green, class="update-nag" - yellow and above the page title,
$all = array();
if ( ! empty( $_GET['settings-updated'] ) ) {
$all[] = array( __( 'Settings saved', 'wp-cerber' ), 'updated' );
}
if ( $notice = cerber_get_set( 'admin_notice' ) ) {
if ( is_array( $notice ) ) {
$notice = '' . implode( '
', $notice ) . '
';
}
$all[] = array( $notice, 'error' ); // red
cerber_update_set( 'admin_notice', array() );
}
if ( $message = cerber_get_set( 'admin_message' ) ) {
if ( is_array( $message ) ) {
$message = '' . implode( '
', $message ) . '
';
}
$all[] = array( $message, 'updated' ); // green
cerber_update_set( 'admin_message', array() );
}
if ( $all ) {
$cerber_shown = true;
foreach ( $all as $notice ) {
echo '
' . $notice[0] . 'Dismiss this notice.
';
}
}
if ( $wide = cerber_get_set( 'cerber_admin_wide', null, false ) ) {
crb_show_admin_announcement( $wide, false );
$cerber_shown = true;
cerber_update_set( 'cerber_admin_wide', '' );
}
if ( $cerber_shown || ! cerber_is_admin_page() ) {
return;
}
if ( $msg = get_site_option( 'cerber_admin_info' ) ) {
crb_show_admin_announcement( $msg );
$cerber_shown = true;
return;
}
lab_opt_in();
}
/**
* Check if an alert with a given parameters exists
*
* @param array $params
*
* @return bool
*
* @since 8.9.5.6
*/
function crb_admin_alert_exists( $params = null ) {
$alerts = cerber_get_site_option( CRB_ALERTZ );
if ( ! $alerts ) {
return false;
}
if ( ! $params ) {
$params = crb_get_alert_params();
}
$hash = crb_get_alert_id( $params );
if ( isset( $alerts[ $hash ] )
&& ! crb_is_alert_expired( $alerts[ $hash ] ) ) {
return true;
}
return false;
}
/**
* Generates a link/button to display a dialog for creating an alert on the currently displaying Activity page
*
* @return string HTML code for using in the Dashboard HTML
*
* @since 8.9.6
*/
function crb_admin_alert_dialog( $button = true, $create_txt = null, $delete_txt = null ) {
$params = crb_get_alert_params();
// Check if query parameters that are used in alerts are set and not empty.
// All activities, without any filter are not allowed
$empty = array_filter( array_values( $params ) );
if ( empty( $empty ) ) {
return '';
}
$alerts = cerber_get_site_option( CRB_ALERTZ );
// Limit to the number of subscriptions
if ( $alerts && count( $alerts ) > 50 ) {
return '';
}
$mode = ( crb_admin_alert_exists( $params ) ) ? 'off' : 'on';
if ( $mode == 'on' ) {
$label = $create_txt ?? __( 'Create Alert', 'wp-cerber' );
$icon = ( $button ) ? 'crb-icon-bx-bell' : '';
}
else {
$label = $delete_txt ?? __( 'Delete Alert', 'wp-cerber' );
$icon = ( $button ) ? 'crb-icon-bx-bell-off' : '';
$link = cerber_admin_link_add( array( 'cerber_admin_do' => 'subscribe', 'mode' => 'off' ), true );
if ( $button ) {
return ' ' . $label . ' ';
}
return '' . $label . ' ';
}
// Create the alert dialog
$em_list = cerber_get_email( 'send_alert' );
$mob_name = cerber_pb_get_active();
if ( $mob_name && lab_lab() ) {
$channels = array(
'title' => __( 'Channels to send alerts', 'wp-cerber' ),
'visible' =>
array(
'al_send_emails' => array(
/* translators: %s is the email address(es). */
'label' => sprintf( __( 'Send email alerts to %s', 'wp-cerber' ), implode( ', ', $em_list ) ),
'value' => '1',
'atts' => array( 'type' => 'checkbox', 'data-validation_group' => 'required_min_one', 'checked' => true ),
),
'al_send_pushbullet' => array(
/* translators: %s is the name of a mobile device. */
'label' => sprintf( __( 'Send mobile alerts to %s', 'wp-cerber' ), $mob_name ),
'value' => '1',
'atts' => array( 'type' => 'checkbox', 'data-validation_group' => 'required_min_one', 'checked' => true ),
),
),
'error_messages' => array( 'required_min_one' => __( 'Please select at least one channel', 'wp-cerber' ) )
);
}
else {
if ( $mob_name ) {
/* translators: %s is the name of a mobile device. */
$mobile = sprintf( __( 'Mobile alerts will be sent to %s', 'wp-cerber' ), $mob_name );
}
else {
$mobile = __( 'Mobile alerts are not configured', 'wp-cerber' );
}
$recipients = ( 1 < count( $em_list ) ) ? __( 'Email alerts will be sent to these emails:', 'wp-cerber' ) : __( 'Email alerts will be sent to this email:', 'wp-cerber' );
$recipients .= ' ' . implode( ', ', $em_list );
$recipients = '' . $recipients . '
' . $mobile . '
';
$recipients .= '' . __( 'Documentation', 'wp-cerber' ) . '
';
$channels = array(
'html' => $recipients,
);
}
$query_params = array_intersect_key( $params, $_GET );
$limits = array(
'title' => __( 'Optional alert limits', 'wp-cerber' ),
'hidden' => array_merge( $query_params, array(
'page' => crb_admin_get_page(),
'tab' => crb_admin_get_tab(),
'cerber_nonce' => crb_create_nonce(),
'cerber_admin_do' => 'subscribe',
'mode' => $mode
) ),
'visible' => array(
'al_limit' => array(
'label' => __( 'Maximum number of alerts to send', 'wp-cerber' ),
'value' => '',
'atts' => array(
'type' => 'text',
'pattern' => '\d+',
'placeholder' => __( 'No limit', 'wp-cerber' ),
),
),
'al_expires' => array(
'label' => __( 'Do not send alerts after this date', 'wp-cerber' ),
'value' => '',
'atts' => array( 'type' => 'date', 'min' => date( 'Y-m-d' ) ),
),
'al_ignore_rate' => array(
'label' => __( 'Ignore global rate limits', 'wp-cerber' ),
'value' => '1',
'atts' => array( 'type' => 'checkbox' ),
),
),
);
return crb_create_popup_dialog( $limits, $channels, $label, $icon );
}
/**
* Managing the list of admin alerts
*
* @param string $mode Add or delete an alert
* @param string $hash If specified, an alert with a given hash will be removed
*/
function crb_admin_alerts_do( $mode = 'on', $hash = null ) {
if ( $hash ) {
$mode = 'off';
}
else {
$params = crb_get_alert_params();
$values = array_values( $params );
$hash = crb_get_alert_id( $params );
}
$alerts = get_site_option( CRB_ALERTZ );
if ( ! $alerts || ! is_array( $alerts ) ) {
$alerts = array();
}
if ( $mode == 'on' ) {
if ( crb_admin_alert_exists() ) {
cerber_admin_notice( 'An alert with given parameters exists.' );
return;
}
$alerts[ $hash ] = $values;
$msg = __( 'The alert has been created', 'wp-cerber' );
}
else {
if ( ! isset( $alerts[ $hash ] ) ) {
cerber_admin_notice( 'No alert with the given ID found' );
return;
}
unset( $alerts[ $hash ] );
$msg = __( 'The alert has been deleted', 'wp-cerber' );
}
if ( update_site_option( CRB_ALERTZ, $alerts ) ) {
cerber_admin_message( $msg );
}
else {
cerber_admin_notice( 'Unable to update alerts!' );
}
}
/**
* Deletes an alert using an alert hash from the $_GET parameter
*
*/
function crb_delete_alert() {
if ( ( $hash = cerber_get_get( 'unsubscribeme', '[a-z\d]+' ) )
&& cerber_user_can_manage() ) {
crb_admin_alerts_do( 'off', $hash );
wp_safe_redirect( remove_query_arg( 'unsubscribeme' ) );
exit;
}
}
/*
Pagination
*/
function cerber_page_navi( $total, $per_page ) {
$max_links = 10;
if ( ! $per_page ) {
$per_page = 25;
}
$page = cerber_get_pn();
$base_url = esc_url( remove_query_arg( 'pagen', add_query_arg( crb_get_query_params(), cerber_admin_link() ) ) );
$last_page = ceil( $total / $per_page );
$ret = '';
if ( $last_page > 1 ) {
$start = 1 + $max_links * intval( ( $page - 1 ) / $max_links );
$end = $start + $max_links - 1;
if ( $end > $last_page ) {
$end = $last_page;
}
if ( $start > $max_links ) {
$links[] = '« ';
}
for ( $i = $start; $i <= $end; $i ++ ) {
if ( $page != $i ) {
$links[] = '' . $i . ' ';
}
else {
$links[] = '' . $i . ' ';
}
}
if ( $end < $last_page ) {
$links[] = '» '; // ➝
}
$ret = '' . $total . ' ' . _n( 'entry', 'entries', $total, 'wp-cerber' ) . '
';
}
return $ret;
}
function cerber_get_pn() {
$q = crb_admin_parse_query( array( 'pagen' ) );
return ( ! $q->pagen ) ? 1 : absint( $q->pagen );
}
function cerber_get_sql_limit( $per_page = 25 ) {
return ' LIMIT ' . ( cerber_get_pn() - 1 ) * $per_page . ',' . $per_page;
}
/*
Plugins screen links
*/
add_filter('plugin_action_links','cerber_action_links',10,4);
function cerber_action_links($actions, $plugin_file, $plugin_data, $context){
if($plugin_file == CERBER_PLUGIN_ID){
$link[] = '' . __('Dashboard','wp-cerber') . ' ';
$link[] = '' . __('Main settings','wp-cerber') . ' ';
$actions = array_merge ($link,$actions);
}
return $actions;
}
/*
function add_some_pointers() {
?>
'cerber-integrity' ) ) ) {
wp_enqueue_script( 'cerber_scan', $crb_assets_url . 'scanner.js', array( 'jquery' ), CERBER_VER, true );
}
}
wp_register_style( 'crb_icons_css', $crb_assets_url . 'icons/style.css', null, CERBER_VER );
wp_enqueue_style( 'crb_icons_css' );
wp_register_style( 'cerber_css', $crb_assets_url . 'admin.css', null, CERBER_VER );
wp_enqueue_style( 'cerber_css' );
}
/*
* JS & CSS for admin head
*
*/
add_action('admin_head', 'cerber_admin_head' );
add_action('customize_controls_print_scripts', 'cerber_admin_head' ); // @since 5.8.1
function cerber_admin_head() {
$crb_ajax_nonce = wp_create_nonce( 'crb-ajax-admin' );
$crb_lab_available = ( lab_lab() ) ? 'true' : 'false';
?>
user_login ) ) {
?>
$uid ) ) . '" class="page-title-action crb-title-button">' . __( 'View Activity', 'wp-cerber' ) . '';
if ( $uss = crb_sessions_get_num( $uid ) ) {
$user_links .= '' . __( 'Sessions', 'wp-cerber' ) . ' ' . $uss . ' ';
}
?>
$login ) {
if ( crb_is_username_prohibited( $login ) ) {
$blocked[] = $uid;
}
elseif ( crb_is_user_blocked( $uid ) ) {
$blocked[] = $uid;
}
}
}
?>
array( 'scan_schedule', 'scan_policy' ) ) ) ) :
?>
';
}
add_filter( 'admin_footer_text', function ( $text ) {
if ( ! cerber_is_admin_page() ) {
return $text;
}
return 'If you like WP Cerber , please give it a ★★★★★ review . Thanks!';
}, PHP_INT_MAX );
add_filter( 'update_footer', function ( $text ) {
if ( ! cerber_is_admin_page() ) {
return $text;
}
$pr = '';
$support = '';
$mode = '';
$ver = '';
if ( $remote = nexus_get_context() ) {
$ver = $remote->plugin_v;
if ( $data = nexus_get_remote_data() ) {
$pr = $data['extra']['versions'][6] ?? '';
}
}
else {
$ver = CERBER_VER;
if ( lab_lab() ) {
$pr = 'PRO';
$support = '| Get Support ';
}
else {
$pr = '';
$support = '| Support Forum ';
}
if ( 1 == cerber_get_mode() ) {
$mode = 'in the Standard mode';
}
else {
$mode = 'in the Legacy mode';
}
}
return 'WP Cerber Security ' . $pr . ' ' . $ver . '. ' . $mode . ' ' . $support;
}, PHP_INT_MAX );
/*
* Add per screen user settings
*/
function crb_admin_screen_options() {
if ( ! $id = crb_get_configurable_screen() ) {
return;
}
add_screen_option( 'per_page', array(
//'label' => __( 'Number of items per page:' ),
'default' => 25,
'option' => 'cerber_screen_' . $id . '_page', // '_page' is mandatory since WP 5.4.2
) );
}
/*
* Enables saving screen options to the user's meta
*/
add_filter( 'set-screen-option', function ( $status, $option, $value ) {
if ( $id = crb_get_configurable_screen() ) {
if ( 'cerber_screen_' . $id . '_page' == $option ) {
return $value;
}
}
return $status;
}, 10, 3 );
/*
* Returns false if the admin page has no screen options to configure
*
* @return false|string
*/
function crb_get_configurable_screen() {
if ( ! $id = crb_admin_get_tab() ) {
$id = crb_admin_get_page();
}
if ( $id == 'cerber-traffic' ) {
$id = 'traffic';
}
if ( ( $id == 'cerber-nexus' ) && nexus_is_master() ) {
return 'nexus_sites';
}
$ids = array( 'sessions', 'lockouts', 'activity', 'traffic', 'scan_quarantine', 'scan_ignore', 'nexus_sites' );
if ( ! in_array( $id, $ids ) ) {
return false;
}
return $id;
}
/*
* Retrieve settings for the current screen
*
*/
function crb_admin_get_per_page() {
if ( is_multisite() ) {
return 50; // temporary workaround
}
if ( nexus_is_valid_request() ) {
$per_page = nexus_request_data()->screen['per_page'];
}
elseif ( function_exists( 'get_current_screen' ) ) {
set_current_screen();
$screen = get_current_screen();
if ( $screen_option = $screen->get_option( 'per_page', 'option' ) ) {
$per_page = absint( get_user_meta( get_current_user_id(), $screen_option, true ) );
if ( empty ( $per_page ) ) {
$per_page = absint( $screen->get_option( 'per_page', 'default' ) );
}
}
}
if ( empty ( $per_page ) ) {
$per_page = 25;
}
return absint( $per_page );
}
/**
* @param array $tabs_config array of tabs: title, desc, contents and optional JS callback
* @param string $submit Text for the submit button
* @param array $hidden Hidden form fields
* @param string $form_id
*
*/
function crb_admin_show_vtabs( $tabs_config, $submit = '', $hidden = array(), $form_id = 'cerber_tabs' ) {
$tablinks = '';
$tabs = '';
foreach ( $tabs_config as $tab_id => $tab ) {
$tab_id = str_replace( '-', '_', $tab_id );
$tablinks .= '' . $tab['title'] . '' . crb_array_get( $tab, 'desc', '' ) . '
';
$tabs .= '' . $tab['content'] . '
';
}
echo '';
if ( ! $submit ) {
$submit = __( 'Save Changes' );
}
echo '' . $tablinks . '
' . $tabs . '
' . crb_admin_submit_button( $submit ) . '
';
cerber_nonce_field( 'control', true );
if ( $hidden ) {
foreach ( $hidden as $name => $val ) {
echo ' ';
}
}
echo ' ';
}
function crb_admin_show_geo_rules(){
$rules = cerber_geo_rule_set();
$tabs_config = array();
foreach ( $rules as $rule_id => $rule ) {
list( $desc, $content ) = crb_admin_geo_selector( $rule_id, $rule );
if ( isset( $rule['multi_set'] ) ) {
$names = array( '---first' => $rule['multi_top'] . ' — ' . $desc );
foreach ( $rule['multi_set'] as $item_id => $item_title ) {
$id = $rule_id . '_' . $item_id;
list( $d, $c ) = crb_admin_geo_selector( $id, $rule, 'crb-display-none' );
$content .= $c;
if ( cerber_get_geo_rules( $id ) ) {
$desc = __( 'Role-based rules are configured', 'wp-cerber' );
$names[ $item_id ] = $item_title . ' — ' . $d;
}
else {
$names[ $item_id ] = $item_title;
}
}
$content = '' . cerber_select( 'sw_' . $rule_id, $names, null, 'crb-geo-switcher', null, null, null, array( 'rule-id' => $rule_id ) ) . '
' . $content;
}
$tabs_config[ $rule_id ] = array(
'title' => $rule['name'],
'desc' => $desc,
'content' => '' . $content . '
',
'callback' => 'geo_rules_activator',
);
}
?>
'update_geo_rules' ), 'crb-geo-rules' );
}
function crb_admin_geo_selector( $rule_id, $rule, $class = '' ) {
$config = cerber_get_geo_rules( $rule_id );
$selector = crb_geo_country_selector( $config, $rule_id, $rule );
$opt = crb_get_settings();
if ( ! empty( $config['list'] ) ) {
$num = count( $config['list'] );
if ( $config['type'] == 'W' ) {
$info = sprintf( _n( 'Permitted for one country', 'Permitted for %d countries', $num, 'wp-cerber' ), $num );
}
else {
$info = sprintf( _n( 'Not permitted for one country', 'Not permitted for %d countries', $num, 'wp-cerber' ), $num );
}
if ( $num == 1 ) {
$info .= ' (' . current( $config['list'] ) . ')';
//$info .= ' (' . cerber_get_flag_html($c) . $c . ')';
}
}
else {
$info = __( 'No rule', 'wp-cerber' );
$info = __( 'Any country is permitted', 'wp-cerber' );
}
$note = '';
switch ( $rule_id ) {
case 'geo_register':
if ( ! get_option( 'users_can_register' ) ) {
$note = 'Registration is disabled in the General WordPress Settings.';
}
break;
case 'geo_restapi':
if ( $opt['norest'] ) {
$note = 'REST API is disabled in the Hardening settings of WP Cerber.';
}
break;
case 'geo_xmlrpc':
if ( $opt['xmlrpc'] ) {
$note = 'XML-RPC is disabled in the Hardening settings of WP Cerber.';
}
break;
}
if ( $note ) {
$note = ' Heads up! ' . $note . '
';
}
return array(
$info,
$note . '' . $selector . '
'
);
}
/**
* Generates GEO rule selector
*
* @param array $config Rule configuration
* @param string $rule_id
* @param array $rule
*
* @return string HTML code of form
*/
function crb_geo_country_selector( $config = array(), $rule_id = '', $rule = array() ) {
$ret = '';
if ( ! empty( $config['list'] ) ) {
$selected = $config['list'];
}
else {
$selected = null;
}
foreach ( cerber_get_country_list() as $code => $country ) {
if ( $selected && in_array( $code, $selected ) ) {
$sel = 'selected';
}
else {
$sel = '';
}
$ret .= '' . $country . ' ';
}
if ( ! empty( $config['type'] ) && $config['type'] == 'B' ) {
$w = '';
$b = 'checked="checked"';
}
else {
$w = 'checked="checked"';
$b = '';
}
if ( ! empty( $rule['desc'] ) ) {
$desc = $rule['desc'];
}
else {
$desc = '' . $rule['name'] . ' ';
}
$ret .= '
' . __( 'Click on a country name to add it to the list of selected countries', 'wp-cerber' ) . '
' . sprintf( _x( 'Selected countries are permitted to %s, other countries are not permitted to', 'to is a marker of infinitive, e.g. "to use it"', 'wp-cerber' ), $desc ) . '
' . sprintf( _x( 'Selected countries are not permitted to %s, other countries are permitted to', 'to is a marker of infinitive, e.g. "to use it"', 'wp-cerber' ), $desc ) . '
';
return $ret;
}
/**
* A list of available GEO rules
*
* @return array
*/
function cerber_geo_rule_set() {
$set = wp_roles()->role_names;
$rules = array(
'geo_login' => array(
'name' => __( 'Log into the website', 'wp-cerber' ),
'multi_set' => $set,
'multi_top' => __( 'All Users' )
),
'geo_register' => array( 'name' => __( 'Register on the website', 'wp-cerber' ) ),
'geo_submit' => array( 'name' => __( 'Submit forms', 'wp-cerber' ) ),
'geo_comment' => array( 'name' => __( 'Post comments', 'wp-cerber' ) ),
'geo_xmlrpc' => array( 'name' => __( 'Use XML-RPC', 'wp-cerber' ) ),
'geo_restapi' => array( 'name' => __( 'Use REST API', 'wp-cerber' ) ),
);
return $rules;
}
function crb_admin_save_geo_rules( $post_fields = array() ) {
global $cerber_country_names, $check, $admin_country;
if ( ! lab_lab() ) {
return;
}
$check = array_keys( $cerber_country_names );
// Prevent admin country from being blocked
if ( nexus_is_valid_request() ) {
$admin_country = null;
}
else {
$admin_country = lab_get_country( cerber_get_remote_ip(), false );
}
$geo = array();
foreach ( cerber_geo_rule_set() as $rule_id => $rule ) {
if ( $data = crb_admin_process_geo( $post_fields, $rule_id ) ) {
$geo[ $rule_id ] = $data;
}
if ( isset( $rule['multi_set'] ) ) {
foreach ( $rule['multi_set'] as $item_id => $item_title ) {
$id = $rule_id . '_' . $item_id;
if ( $data = crb_admin_process_geo( $post_fields, $id ) ) {
$geo[ $id ] = $data;
}
}
}
}
if ( update_site_option( CERBER_GEO_RULES, $geo ) ) {
cerber_admin_message( __( 'Security rules have been updated', 'wp-cerber' ) );
}
}
function crb_admin_process_geo( $post, $rule_id ) {
global $check, $admin_country;
if ( empty( $post[ 'crb-' . $rule_id . '-list' ] ) || empty( $post[ 'crb-' . $rule_id . '-type' ] ) ) {
return false;
}
$list = array_intersect( $post[ 'crb-' . $rule_id . '-list' ], $check );
if ( $post[ 'crb-' . $rule_id . '-type' ] == 'B' ) {
$type = 'B';
if ( $admin_country && ( ( $key = array_search( $admin_country, $list ) ) !== false ) ) {
unset( $list[ $key ] );
}
}
else {
$type = 'W';
if ( $admin_country && ( ( $key = array_search( $admin_country, $list ) ) === false ) ) {
array_push( $list, $admin_country );
}
}
return array( 'list' => $list, 'type' => $type );
}
/**
* Generates HTML for showing country in UI
*
* @param null $code
* @param null $ip
* @param bool $cache_only
*
* @return string
*/
function crb_country_html( $code = null, $ip = null, $cache_only = true ) {
if ( ! lab_lab() ) {
return '';
}
if ( ! $code ) {
if ( $ip ) {
$code = lab_get_country( $ip, $cache_only );
}
else {
return '';
}
}
if ( $code ) {
$ret = cerber_get_flag_html( $code, cerber_country_name( $code ) );
}
else {
$ip_id = cerber_get_id_ip( $ip );
$ret = crb_get_ajax_placeholder( 'country', $ip_id );
}
return $ret;
}
// Traffic =====================================================================================
function cerber_export_traffic( $params = array() ) {
crb_raise_limits( 512 );
$args = array(
'per_page' => 0,
'columns' => array( 'ip', 'stamp', 'uri', 'session_id', 'user_id', 'processing', 'request_method', 'http_code', 'wp_type', 'blog_id' )
);
if ( $params ) {
$args = array_merge( $params, $args );
}
list( $query, $found ) = cerber_traffic_query( $args );
// We split into several requests to avoid PHP and MySQL memory limitations
if ( defined( 'CERBER_EXPORT_CHUNK' ) && is_numeric( CERBER_EXPORT_CHUNK ) ) {
$per_chunk = CERBER_EXPORT_CHUNK;
}
else {
$per_chunk = 1000; // Rows per SQL request, we assume that this number is not too small and not too big
}
if ( ! $result = cerber_db_query( $query . ' LIMIT ' . $per_chunk ) ) {
wp_die( 'Nothing to export' );
}
$total = cerber_db_get_var( $found );
$info = array();
$heading = array(
__( 'IP address', 'wp-cerber' ),
__( 'Date', 'wp-cerber' ),
'Method',
'URI',
'HTTP Code',
'Request ID',
__( 'User ID', 'wp-cerber' ),
__( 'Page generation time', 'wp-cerber' ),
'Blog ID',
'Type',
'Unix Timestamp',
);
cerber_send_csv_header( 'wp-cerber-http-requests', $total, $heading, $info );
//$labels = cerber_get_labels( 'activity' );
//$status = cerber_get_labels( 'status' );
if ( crb_get_settings( 'plain_date' ) ) {
function _crb_csv_date( $timestamp ) {
static $gmt_offset;
if ( $gmt_offset === null ) {
$gmt_offset = get_option( 'gmt_offset' ) * 3600;
}
return date( 'Y-m-d H:i:s', $timestamp + $gmt_offset );
}
}
else {
function _crb_csv_date( $timestamp ) {
return cerber_date( $timestamp, false );
}
}
// The loop
$i = 0;
do {
while ( $row = mysqli_fetch_assoc( $result ) ) {
$values = array();
/*
$values[] = $row->ip;
$values[] = cerber_date( $row->stamp );
$values[] = $row->request_method;
$values[] = $row->uri;
$values[] = $row->http_code;
$values[] = $row->session_id;
$values[] = $row->user_id;
$values[] = $row->processing;
$values[] = $row->blog_id;
$values[] = $row->wp_type;
$values[] = $row->stamp;*/
$values[] = $row['ip'];
$values[] = _crb_csv_date( $row['stamp'] );
$values[] = $row['request_method'];
$values[] = $row['uri'];
$values[] = $row['http_code'];
$values[] = $row['session_id'];
$values[] = $row['user_id'];
$values[] = $row['processing'];
$values[] = $row['blog_id'];
$values[] = $row['wp_type'];
$values[] = $row['stamp'];
cerber_send_csv_line( $values );
}
mysqli_free_result( $result );
$i++;
$offset = $per_chunk * $i;
} while ( ( $result = cerber_db_query( $query . ' LIMIT ' . $offset . ', ' . $per_chunk ) )
&& $result->num_rows );
exit;
}
/*
* Display traffic in the WP Dashboard
*
*/
function cerber_show_traffic( $args = array(), $echo = true ) {
global $wpdb;
$labels = cerber_get_labels( 'activity' );
$status_labels = cerber_get_labels( 'status' ) + cerber_get_reason();
$base_url = cerber_admin_link( 'traffic' );
$logging_enabled = (bool) crb_get_settings( 'timode' );
$is_log_empty = ! ( (bool) cerber_db_get_var( 'SELECT stamp FROM ' . CERBER_TRAF_TABLE . ' LIMIT 1' ) );
$ret = '';
$total = 0;
list( $query, $found, $per_page, $filter_act, $filter_ip, $prc, $user_id ) = cerber_traffic_query( $args );
// No cache
//$rows = cerber_db_get_results( $query, MYSQL_FETCH_OBJECT_K );
//$total = cerber_db_get_var( $found );
list( $rows, $total ) = crb_q_cache_get( array(
array( $query, MYSQL_FETCH_OBJECT_K ),
array( $found )
), CERBER_TRAF_TABLE );
if ( is_object( $rows ) ) { // Due to json
$rows = get_object_vars( $rows );
}
$total = $total[0][0];
if ( $rows ) {
// No cache
// $act_events = cerber_db_get_results( 'SELECT * FROM ' . CERBER_LOG_TABLE . ' WHERE session_id IN ("' . implode( '", "', array_keys( $rows ) ) . '" ) ORDER BY stamp DESC', MYSQL_FETCH_OBJECT );
// $act_events = cerber_db_get_results( 'SELECT * FROM ' . CERBER_LOG_TABLE . ' WHERE session_id IN ("' . implode( '", "', array_keys( $rows ) ) . '" )', MYSQL_FETCH_OBJECT );
$act_events = crb_q_cache_get( array(
array(
'SELECT * FROM ' . CERBER_LOG_TABLE . ' WHERE session_id IN ("' . implode( '", "', array_keys( $rows ) ) . '" ) ORDER BY stamp DESC',
MYSQL_FETCH_OBJECT
)
), CERBER_LOG_TABLE );
$events = array();
if ( $act_events ) {
foreach ( $act_events as $ac ) {
$events[ $ac->session_id ][] = $ac;
}
}
$roles = wp_roles()->roles;
$users = array();
$acl = array();
$block = array();
$wp_objects = array();
foreach ( $rows as $row ) {
if ( $row->user_id && ! isset( $users[ $row->user_id ] ) ) {
if ( $u = get_userdata( $row->user_id ) ) {
$n = $u->display_name;
$r = '';
if ( ! is_multisite() && $u->roles ) {
$r = array();
foreach ( $u->roles as $role ) {
$r[] = $roles[ $role ]['name'];
}
$r = '' . implode( ', ', $r ) . ' ';
}
}
else {
$n = __( 'Unknown', 'wp-cerber' ) . ' (' . $row->user_id . ')';
$r = '';
}
$users[ $row->user_id ]['name'] = $n;
$users[ $row->user_id ]['roles'] = $r;
}
if ( ! isset( $acl[ $row->ip ] ) ) {
$acl[ $row->ip ] = cerber_acl_check( $row->ip );
}
if ( ! isset( $block[ $row->ip ] ) ) {
$block[ $row->ip ] = cerber_block_check( $row->ip );
}
// TODO: make it compatible with multisite WP
if ( $row->wp_type == 601 && $row->wp_id > 0 ) {
$title = cerber_db_get_var( 'SELECT post_title FROM ' . $wpdb->posts . ' WHERE ID = ' . absint( $row->wp_id ) );
if ( $title ) {
$wp_objects[ $row->wp_id ] = apply_filters( 'the_title', $title, $row->wp_id );
}
}
}
}
$info = '';
if ( $filter_ip ) {
$info .= cerber_ip_extra_view( $filter_ip, 'traffic' );
}
if ( $user_id ) {
$info .= cerber_user_extra_view( $user_id, 'traffic' );
}
if ( $rows ) {
$no_results = false;
$tbody = '';
$country = '';
$geo = lab_lab();
if ( ! defined( 'CERBER_FULL_URI' ) || ! CERBER_FULL_URI ) {
$short = true;
$site_url = cerber_get_site_url();
$len = strlen( rtrim( $site_url, '/' ) );
}
else {
$short = false;
$len = 0;
}
foreach ( $rows as $row ) {
// URI
$full_uri = urldecode( $row->uri );
if ( $short && 0 === strpos( $full_uri, $site_url ) ) {
$row_uri = substr( $full_uri, $len );
}
else {
$row_uri = $full_uri;
}
if ( strlen( $row_uri ) > 220 ) {
$truncated = true;
$row_uri = substr( $row_uri, 0, 220 );
}
else {
$truncated = false;
}
$row_uri = htmlentities( $row_uri, ENT_SUBSTITUTE );
$row_uri = str_replace( array( '-', '/', '%', '&', '=', '?', '(', ')', ),
array( '-', '/', '%', '&', '=', '?', '(', ')' ), $row_uri );
if ( $truncated ) {
$row_uri .= ' … ';
}
// User
$ip_id = cerber_get_id_ip( $row->ip );
if ( $row->user_id ) {
$name = '' . $users[ $row->user_id ]['name'] . ' ' . $users[ $row->user_id ]['roles'];
}
else {
$name = '';
}
if ( ! empty( $row->hostname ) ) {
$hostname = $row->hostname;
}
else {
$ip_info = cerber_get_ip_info( $row->ip, true );
$hostname = $ip_info['hostname_html'] ?? crb_get_ajax_placeholder( 'hostname', $ip_id );
}
if ( ! empty( $args['date'] ) && $args['date'] == 'ago' ) {
$date = cerber_ago_time( $row->stamp );
}
else {
$date = '' . cerber_date( $row->stamp ) . ' ';
}
if ( $geo ) {
$country = '' . crb_country_html( $row->country, $row->ip ) . '
';
}
$activity = '';
if ( ! empty( $events[ $row->session_id ] ) ) {
foreach ( $events[ $row->session_id ] as $a ) {
$activity .= '' . $labels[ $a->activity ] . ' ';
$ad = explode( '|', $a->details );
$status = (int) crb_array_get( $ad, 0, 0 );
if ( ! empty( $status ) && $status != 500 ) { // 500 Whitelisted
$activity .= ' ' . $status_labels[ $status ] . ' ';
}
}
}
if ( $row->processing ) {
$processing = $row->processing . ' ms';
if ( ( $threshold = crb_get_settings( 'tithreshold' ) ) && $row->processing > $threshold ) {
$processing = '' . $processing . ' ';
}
}
else {
$processing = '';
}
$wp_type = '';
if ( $t = cerber_get_wp_type( $row->wp_type ) ) {
$wp_type = '' . $t . ' ';
}
$wp_obj = '';
if ( isset( $wp_objects[ $row->wp_id ] ) ) {
$wp_obj = '
' . $wp_objects[ $row->wp_id ] . '
';
}
$more_details = array();
if ( $truncated ) {
$full_uri = htmlentities( $full_uri, ENT_SUBSTITUTE );
$full_uri = str_replace( array( '-', '/', '%', '&', '=', '?', '(', ')', ),
array( '-', '/', '%', '&', '=', '?', '(', ')' ), $full_uri );
$more_details[] = array(
'Full URL',
'' . $full_uri . ' '
);
}
$fields = crb_auto_decode( $row->request_fields );
$details = crb_auto_decode( $row->request_details );
if ( ! empty( $details[2] ) ) {
$more_details[] = array(
'Referrer',
'' . htmlentities( urldecode( $details[2] ), ENT_SUBSTITUTE ) . ' '
);
}
if ( ! empty( $details[1] ) ) {
$more_details[] = array(
'User Agent',
'' . htmlentities( $details[1], ENT_SUBSTITUTE ) . ' '
);
}
// POST fields
if ( ! empty( $fields[1] ) ) {
$more_details[] = array( '', cerber_table_view( 'Form fields', $fields[1] ) );
}
// GET fields
if ( ! empty( $fields[2] ) ) {
$more_details[] = array( '', cerber_table_view( 'GET fields', $fields[2] ) );
}
// Files
$files = array();
$f = '';
if ( ! empty( $fields[3] ) ) {
foreach ( $fields[3] as $field_name => $file ) {
if ( is_array( $file['error'] ) ) {
$f_err = array_shift( $file['error'] );
}
else {
$f_err = $file['error'];
}
if ( $f_err == UPLOAD_ERR_NO_FILE ) {
continue;
}
if ( is_array( $file['name'] ) ) {
$f_name = array_shift( $file['name'] );
}
else {
$f_name = $file['name'];
}
if ( is_array( $file['size'] ) ) {
$f_size = array_shift( $file['size'] );
}
else {
$f_size = $file['size'];
}
$files[ $field_name ] = htmlentities( $f_name, ENT_SUBSTITUTE ) . ', size: ' . absint( $f_size ) . ' bytes';
}
if ( ! empty( $files ) ) {
$f = 'F ';
$more_details[] = array( '', cerber_table_view( 'Files submitted', $files ) );
}
}
// Additional details
if ( ! empty( $details[5] ) ) {
$more_details[] = array( 'XML-RPC Data', htmlentities( $details[5], ENT_SUBSTITUTE ) );
}
if ( ! empty( $details[6] ) ) {
$more_details[] = array( '', cerber_table_view( 'Request Headers', $details[6] ) );
}
if ( ! empty( $details[10] ) ) {
$list = array();
foreach ( $details[10] as $item ) {
$e = explode( ':', $item, 2 );
/* $key = ( isset( $list[ $e[0] ] ) ) ? $e[0] . ' ' : $e[0];
$list[ $key ] = $e[1];*/
$list[] = array( $e[0] => $e[1] );
}
$more_details[] = array( '', cerber_table_view( 'Response Headers', $list ) );
}
if ( ! empty( $details[8] ) ) {
$more_details[] = array( '', cerber_table_view( 'Request Cookies', $details[8] ) );
}
if ( ! empty( $details[9] ) ) {
$start = strlen( 'Set-Cookie:' );
$list = array();
foreach ( $details[9] as $item ) {
if ( $e = explode( '=', substr( $item, $start ), 2 ) ) {
$list[ $e[0] ] = $e[1];
}
}
$more_details[] = array( '', cerber_table_view( 'Response Cookies', $list ) );
}
if ( ! empty( $details[7] ) ) {
$more_details[] = array( '', cerber_table_view( '$_SERVER', $details[7] ) );
}
$php_errors = '';
if ( $err_list = crb_auto_decode( $row->php_errors ) ) {
$errors = array();
foreach ( $err_list as $err ) {
$errors[] = array(
'type' => cerber_get_err_type( $err[0] ) . ' (' . $err[0] . ')',
'info' => $err[1],
'file' => $err[2],
'line' => $err[3]
);
}
$more_details[] = array( '', cerber_table_view( 'Software errors', $errors ) );
$php_errors = 'SE ' . count( $err_list ) . ' ';
}
$req_status = '';
if ( ! empty( $row->req_status ) ) {
$req_status = '' . $status_labels[ $row->req_status ] . ' ';
}
$request_details = '';
$more_link = '';
$toggle_class = '';
if ( $more_details ) {
foreach ( $more_details as $d ) {
if ( $d[0] ) {
$request_details .= '' . $d[0] . '
' . $d[1] . '
';
}
else {
$request_details .= $d[1];
}
}
$more_link = 'Details ';
$toggle_class = 'crb-toggle';
}
$request = '' . $row_uri . ' ' . '' . $row->request_method . ' ' . $f . $wp_type . ' HTTP ' . $row->http_code . ' ' . get_status_header_desc( $row->http_code ) . ' ' . $req_status . $php_errors . '' . $processing . ' ' . $more_link . ' ' . $activity . '
' . $wp_obj;
// Decorating this table can't be done via simple CSS
if ( ! empty( $even ) ) {
$class = 'crb-even';
$even = false;
}
else {
$even = true;
$class = 'crb-odd';
}
$tbody .= '
' . $date . '
' . $request . '
' . crb_admin_ip_cell( $row->ip, $base_url . '&filter_ip=' . $row->ip ) . '
' . $hostname . $country . '
' . cerber_detect_browser( $details[1] ) . '
' . $name . ' ';
$tbody .= '' . $request_details . '
';
}
$heading = array(
'crb_date_col' => __( 'Date', 'wp-cerber' ),
'crb_request_col' => __( 'Request', 'wp-cerber' ),
'crb_ip_col' => '
' . __( 'IP Address', 'wp-cerber' ),
'crb_hostinfo_col' => __( 'Host Info', 'wp-cerber' ),
'crb_ua_col' => __( 'User Agent', 'wp-cerber' ),
'crb_user_col' => __( 'Local User', 'wp-cerber' ),
);
$titles_head = '';
foreach ( $heading as $id => $title ) {
$titles_head .= '' . $title . ' ';
}
$titles_foot = '' . implode( ' ', $heading ) . ' ';
$table = '' . $titles_head . ' ' . $titles_foot . ' ' . $tbody . '
';
if ( empty( $args['no_navi'] ) ) {
$table .= cerber_page_navi( $total, $per_page );
}
//$legend = ''.sprintf(__('Showing last %d records from %d','wp-cerber'),count($rows),$total);
//if (empty($args['no_export'])) $export_link = ' '.__('Export','wp-cerber').' ';
}
else {
$no_results = true;
$is_search = ( array_intersect_key( CRB_TRF_PARAMS, crb_get_query_params() ) );
$hints = array();
if ( ! $is_search ) {
$hints[] = __( 'No requests have been logged yet.', 'wp-cerber' );
}
else {
$hints[] = __( 'No requests found using the given search criteria', 'wp-cerber' );
if ( ! $is_log_empty ) {
$hints[] = '' . __( 'View all logged requests', 'wp-cerber' ) . ' ';
}
}
if ( ! $logging_enabled ) {
$hints[] = __( 'Note: Logging is currently disabled', 'wp-cerber' );
if ( ! $is_search ) {
$hints[] = '' . __( 'Documentation', 'wp-cerber' ) . ' ';
}
}
$table = '
' . implode( '
', $hints ) . '
';
if ( $is_log_empty ) {
cerber_watchdog( true );
}
}
if ( empty( $args['no_navi'] ) ) {
$filter_links = '';
$search_button = '';
if ( ! $is_log_empty ) {
$nav_links = array();
$nav_links[] = array( array(), __( 'View all', 'wp-cerber' ) );
$nav_links[] = array( array( 'filter_set' => 1 ), __( 'Suspicious requests', 'wp-cerber' ) );
$nav_links[] = array( array( 'filter_http_code' => 399, 'filter_http_code_mode' => 'GT' ), __( 'Errors', 'wp-cerber' ) );
$nav_links[] = array( array( 'filter_user' => '*' ), __( 'Users', 'wp-cerber' ) );
$nav_links[] = array( array( 'filter_user' => 0 ), __( 'Non-authenticated', 'wp-cerber' ) );
$nav_links[] = array( array( 'filter_method' => 'POST', 'filter_wp_type' => 519, 'filter_wp_type_mode' => 'GT' ), __( 'Form submissions', 'wp-cerber' ) );
$nav_links[] = array( array( 'filter_http_code' => 404 ), __( 'Page Not Found', 'wp-cerber' ) );
$nav_links[] = array( array( 'filter_wp_type' => 520 ), 'REST API' );
$nav_links[] = array( array( 'filter_wp_type' => 515 ), 'XML-RPC' );
$nav_links[] = array( array( 'filter_user' => get_current_user_id() ), __( 'My requests', 'wp-cerber' ) );
$nav_links[] = array( array( 'filter_ip' => cerber_get_remote_ip() ), __( 'My IP', 'wp-cerber' ) );
if ( $threshold = crb_get_settings( 'tithreshold' ) ) {
$nav_links[] = array( array( 'filter_processing' => $threshold ), __( 'Longer than', 'wp-cerber' ) . ' ' . $threshold . ' ms' );
}
$filter_links = crb_make_nav_links( $nav_links, 'traffic' );
$search_button = ' ' . __( 'Advanced Search', 'wp-cerber' ) . ' ';
}
$export_button = '';
if ( ! $no_results ) {
$export_button .= ' ' . __( 'Export', 'wp-cerber' ) . ' ';
}
$right_links = $search_button . ' ' . $export_button;
$refresh = '';
if ( $logging_enabled && ! $no_results ) {
$refresh = ' ' . __( 'Refresh', 'wp-cerber' ) . ' ';
}
$top_bar = '' . $filter_links . '
' . $refresh . '
' . $right_links . '
';
$ret = '' . $top_bar . crb_admin_traffic_search_form() . $info . '
' . $ret;
}
$ret .= $table;
if ( ! $logging_enabled && ! $no_results ) {
$ret = 'Logging is currently disabled, you are viewing historical information.
' . $ret;
}
if ( $echo ) {
echo $ret;
}
else {
return $ret;
}
}
/**
* Detects known browsers/crawlers and platform in User Agent string
*
* @param $ua
*
* @return string Sanitized and escaped browser name and platform on success, 'Unknown' on failure
* @since 6.0
*/
function cerber_detect_browser( $ua ) {
$ua = trim( $ua );
if ( empty( $ua ) ) {
return __( 'Not specified', 'wp-cerber' );
}
if ( preg_match( '/\(compatible\;(.+)\)/i', $ua, $matches ) ) {
$bot_info = explode( ';', $matches[1] );
foreach ( $bot_info as $item ) {
if ( stripos( $item, 'bot' )
|| stripos( $item, 'crawler' )
|| stripos( $item, 'spider' )
|| stripos( $item, 'Yandex' )
|| stripos( $item, 'Yahoo! Slurp' )
) {
if ( strpos( $ua, 'Android' ) ) {
$item .= ' Mobile';
}
return htmlentities( $item, ENT_QUOTES | ENT_SUBSTITUTE );
}
}
if ( strpos( $ua, 'Google-Read-Aloud' ) ) {
return 'Google Read Aloud';
}
}
elseif ( strpos( $ua, 'google.com' ) || strpos( $ua, 'Google' ) ) {
// Various Google bots
$ret = '';
if ( false !== strpos( $ua, 'Googlebot' ) ) {
if ( strpos( $ua, 'Android' ) ) {
$ret = 'Googlebot Mobile';
}
elseif ( false !== strpos( $ua, 'Mozilla' ) ) {
$ret = 'Googlebot Desktop';
}
}
elseif ( preg_match( '/AdsBot-Google-Mobile|AdsBot-Google|APIs-Google|FeedFetcher-Google/', $ua, $matches ) ) {
$ret = $matches[0];
}
elseif ( 0 === strpos( $ua, 'Googlebot' ) ) {
if ( preg_match( '/Googlebot-\w+/', $ua, $matches ) ) {
$ret = $matches[0];
}
}
elseif ( 0 === strpos( $ua, 'Mediapartners-Google' ) ) {
return 'AdSense Crawler';
}
elseif ( 0 === strpos( $ua, 'AdsBot-Google-Mobile-Apps' ) ) {
return 'Mobile Apps Android';
}
elseif ( strpos( $ua, 'DuplexWeb-Google' ) ) {
return 'Duplex on the Web by Google';
}
elseif ( strpos( $ua, 'Google Favicon' ) ) {
return 'Google Favicon';
}
if ( $ret ) {
return htmlentities( $ret, ENT_QUOTES | ENT_SUBSTITUTE );
}
else {
return __( 'Unknown Google\'s bot', 'wp-cerber' );
}
}
/*elseif ( 0 === strpos( $ua, 'Googlebot' ) ) {
if ( preg_match( '/Googlebot-\w+/', $ua, $matches ) ) {
return $matches[0];
}
}*/
elseif ( 0 === strpos( $ua, 'WordPress/' ) ) {
list( $ret ) = explode( ';', $ua, 2 );
return htmlentities( $ret, ENT_QUOTES | ENT_SUBSTITUTE );
}
elseif ( 0 === strpos( $ua, 'PayPal IPN' ) ) {
return 'PayPal Payment Notification';
}
elseif (0 === strpos( $ua, 'Wget/' )){
return htmlentities( $ua, ENT_QUOTES | ENT_SUBSTITUTE );
}
elseif ( strpos( $ua, 'googleweblight' )){
return 'Web Light by Google';
}
$browsers = array(
'Firefox/' => 'Firefox',
'OPR/' => 'Opera',
'Opera/' => 'Opera',
'YaBrowser/' => 'Yandex Browser',
'Trident/' => 'Internet Explorer',
'IE/' => 'Internet Explorer',
'Edge/' => 'Microsoft Edge',
'Edg/' => 'Microsoft Edge',
'Chrome/' => 'Chrome',
'Safari/' => 'Safari',
'Lynx/' => 'Lynx',
);
$systems = array( 'Android' , 'Linux', 'Windows', 'iPhone', 'iPad', 'Macintosh', 'OpenBSD', 'Unix' );
$browser = '';
foreach ( $browsers as $browser_id => $browser_name ) {
if ( false !== strpos( $ua, $browser_id ) ) {
$browser = $browser_name;
break;
}
}
$system = '';
foreach ( $systems as $system_id ) {
if ( false !== strpos( $ua, $system_id ) ) {
$system = $system_id;
break;
}
}
if ( $browser == 'Lynx' && ! $system ) {
$system = 'Linux';
}
elseif ( $system == 'Macintosh' ) {
$system = 'Mac';
}
if ( $system == 'Android' ) {
if ( preg_match( '/(Android\s+\d+);/', $ua, $matches ) ) {
$system = $matches[1];
}
}
if ( $browser && $system ) {
$ret = $browser . ' on ' . $system;
}
elseif ( 0 === strpos( $ua, 'python-requests' ) ) {
$ret = 'Python Script';
}
elseif ( 0 === strpos( $ua, 'ApacheBench' ) ) {
$ret = $ua;
}
else {
$ret = __( 'Unknown', 'wp-cerber' );
}
return htmlentities( $ret, ENT_QUOTES | ENT_SUBSTITUTE );
}
/**
* Create a table view of an array to display it
*
* @param string $title
* @param array $fields
* @param string $sub_key
*
* @return string A sanitized and escaped table view
*/
function cerber_table_view( $title, $fields, $sub_key = null ) {
if ( empty( $fields ) ) {
return '';
}
if ( ! isset( $sub_key ) ) {
$class = 'crb-fields-table crb-top-table';
}
else {
$class = 'crb-fields-table crb-sub-table';
}
$view = '';
if ( $title ) {
$view .= '' . $title . ' ';
}
$i = 1;
foreach ( $fields as $key => $value ) {
if ( is_array( $value ) ) {
$view .= '' . htmlentities( $key, ENT_SUBSTITUTE ) . ' ' . cerber_table_view( '', $value, $key ) . ' ';
}
else {
$view .= '' . htmlentities( $key, ENT_SUBSTITUTE ) . ' ' . nl2br( htmlentities( $value, ENT_SUBSTITUTE ) ) . '
';
}
$i ++;
}
$view .= '
';
return $view;
}
/**
* Parse arguments and create SQL query for retrieving rows from the traffic table
*
* @param array $args Optional arguments to use them instead of using $_GET
*
* @return array
*
* @since 6.0
*/
function cerber_traffic_query( $args = array() ) {
global $wpdb;
$ret = array_fill( 0, 8, '' );
$where = array();
$join = '';
$join_act = false;
$q = crb_admin_parse_query( array_keys( CRB_TRF_PARAMS ), $args );
if ( preg_match( '/^\w+$/', $q->filter_sid ) ) {
$where[] = 'log.session_id = "' . $q->filter_sid . '"';
}
$falist = array();
if ( $q->filter_http_code ) { // Multiple codes can be requested this way: &filter_http_code[]=404&filter_http_code[]=405
if ( is_array( $q->filter_http_code ) ) {
$falist = array_filter( array_map( 'absint', $q->filter_http_code ) );
$where[] = 'log.http_code IN (' . implode( ',', $falist ) . ')';
}
else {
$filter = absint( $q->filter_http_code );
$op = '=';
if ( $q->filter_http_code_mode == 'GT' ) {
$op = '>';
}
$where[] = 'log.http_code ' . $op . $filter;
$falist = array( $filter ); // for further using in links
}
}
//$ret[3] = $falist;
if ( $q->filter_ip ) {
$range = cerber_any2range( $q->filter_ip );
if ( is_array( $range ) ) {
$where[] = '(log.ip_long >= ' . $range['begin'] . ' AND log.ip_long <= ' . $range['end'] . ')';
}
elseif ( cerber_is_ip_or_net( $q->filter_ip ) ) {
$where[] = 'log.ip = "' . $q->filter_ip . '"';
}
else {
$where[] = "log.ip = 'produce-no-result'";
}
$ret[4] = preg_replace( CRB_IP_NET_RANGE, '', $q->filter_ip );
}
if ( $q->filter_processing ) {
$p = absint( $q->filter_processing );
$where[] = 'log.processing > ' . $p;
$ret[5] = $p;
}
$filter_user = false;
if ( $q->filter_user !== false ) {
$filter_user = $q->filter_user;
}
elseif ( $q->filter_user_alt !== false ) {
$filter_user = $q->filter_user_alt;
}
if ( $filter_user !== false ) {
if ( $filter_user == '*' ) {
$um = ( ! empty( $q->filter_user_mode ) ) ? '=' : '!=';
$where[] = 'log.user_id ' . $um . ' 0';
$ret[6] = '*';
}
elseif ( preg_match_all( '/\d+/', $filter_user, $matches ) ) {
$users = implode( ',', $matches[0] );
if ( ! empty( $q->filter_user_mode ) ) {
$um = 'NOT IN';
}
else {
$um = 'IN';
$ret[6] = $users;
}
$where[] = 'log.user_id ' . $um . ' (' . $users . ')';
}
}
if ( $q->filter_wp_type ) {
$t = absint( $q->filter_wp_type );
$ret[7] = $t;
$op = '=';
if ( $q->filter_wp_type_mode == 'GT' ) {
$op = '>';
}
$where[] = 'log.wp_type ' . $op . $t;
}
if ( $q->search_traffic ) {
$search = stripslashes_deep( $q->search_traffic );
if ( $search['ip'] ) {
if ( $ip = filter_var( $search['ip'], FILTER_VALIDATE_IP ) ) {
$where[] = 'log.ip = "' . $ip . '"';
$ret[4] = $ip;
}
else {
$where[] = 'log.ip LIKE "%' . cerber_real_escape( $search['ip'] ) . '%"';
}
}
if ( $search['uri'] ) {
$where[] = 'log.uri LIKE "%' . cerber_real_escape( $search['uri'] ) . '%"';
}
if ( $search['fields'] ) {
$where[] = 'log.request_fields LIKE "%' . cerber_real_escape( $search['fields'] ) . '%"';
}
if ( $search['details'] ) {
$where[] = 'log.request_details LIKE "%' . cerber_real_escape( $search['details'] ) . '%"';
}
if ( $search['date_from'] ) {
if ( $stamp = strtotime( 'midnight ' . $search['date_from'] ) ) {
$gmt_offset = get_option( 'gmt_offset' ) * 3600;
$where[] = 'log.stamp >= ' . ( absint( $stamp ) - $gmt_offset );
}
}
if ( $search['date_to'] ) {
if ( $stamp = 24 * 3600 + strtotime( 'midnight ' . $search['date_to'] ) ) {
$gmt_offset = get_option( 'gmt_offset' ) * 3600;
$where[] = 'log.stamp <= ' . ( absint( $stamp ) - $gmt_offset );
}
}
if ( ! $q->filter_errors && $search['errors'] ) {
$where[] = 'log.php_errors LIKE "%' . cerber_real_escape( $search['errors'] ) . '%"';
}
}
if ( $q->filter_method ) {
$where[] = $wpdb->prepare( 'log.request_method = %s', $q->filter_method );
}
$activity = null;
if ( $q->filter_activity ) {
$activity = absint( $q->filter_activity );
}
if ( $q->filter_set ) {
$activity = implode( ',', crb_get_filter_set( $q->filter_set ) );
}
if ( $activity ) {
$ret[3] = $activity;
$where[] = 'act.activity IN (' . $activity . ')';
$join_act = true;
}
if ( $q->filter_errors ) {
$where[] = 'log.php_errors != ""';
}
// ---------------------------------------------------------------------------------
$where = ( ! empty( $where ) ) ? 'WHERE ' . implode( ' AND ', $where ) : '';
// Limits, if specified
$per_page = $args['per_page'] ?? crb_admin_get_per_page();
$per_page = absint( $per_page );
$ret[2] = $per_page;
$cols = 'log.*';
if ( ! empty( $args['columns'] ) ) {
$cols = '';
foreach ( $args['columns'] as $col_name ) {
$cols .= ',log.' . preg_replace( '/[^A-Z_\d]/i', '', $col_name );
}
$cols = trim( $cols, ',' );
}
if ( $join_act ) {
$join = ' JOIN ' . CERBER_LOG_TABLE . ' act ON (log.session_id = act.session_id)';
}
if ( $per_page ) {
$limit = cerber_get_sql_limit( $per_page );
//$ret[0] = 'SELECT SQL_CALC_FOUND_ROWS log.session_id,log.* FROM ' . CERBER_TRAF_TABLE . " log USE INDEX FOR ORDER BY (stamp) {$where} ORDER BY stamp DESC {$limit}";
$ret[0] = 'SELECT log.session_id,' . $cols . ' FROM ' . CERBER_TRAF_TABLE . " log {$join} {$where} ORDER BY log.stamp DESC {$limit}";
//$ret[0] = 'SELECT SQL_CALC_FOUND_ROWS log.*,act.activity FROM ' . CERBER_TRAF_TABLE . ' log USE INDEX FOR ORDER BY (stamp) LEFT JOIN '.CERBER_LOG_TABLE." act ON (log.session_id = act.session_id) {$where} ORDER BY log.stamp DESC {$limit}";
}
else {
$ret[0] = 'SELECT log.session_id,' . $cols . ' FROM ' . CERBER_TRAF_TABLE . " log {$join} {$where} ORDER BY stamp DESC"; // "ORDER BY" is mandatory!
}
$ret[1] = 'SELECT COUNT(log.stamp) FROM ' . CERBER_TRAF_TABLE . " log {$join} {$where}";
return $ret;
}
/**
* Traffic Search form HTML
*
* @return string
*
*/
function crb_admin_traffic_search_form() {
ob_start();
?>
'XML-RPC',
520 => 'REST API'
);
if ( isset( $types[ $wp_type ] ) ) {
return $types[ $wp_type ];
}
return '';
}
function cerber_get_err_type( $type ) {
$list = array(
'E_ERROR',
'E_WARNING',
'E_PARSE',
'E_NOTICE',
'E_CORE_ERROR',
'E_CORE_WARNING',
'E_COMPILE_ERROR',
'E_COMPILE_WARNING',
'E_USER_ERROR',
'E_USER_WARNING',
'E_USER_NOTICE',
'E_STRICT',
'E_RECOVERABLE_ERROR',
'E_DEPRECATED',
'E_USER_DEPRECATED',
'E_ALL',
);
return $list[ intval( log( $type, 2 ) ) ];
}
/**
* Check if admin AJAX is permitted.
*
*/
function cerber_check_ajax_permissions( $strict = true ) {
if ( nexus_is_valid_request() ) {
/*
$nonce = crb_array_get( nexus_request_data()->get_params, 'ajax_nonce' );
if ( ! $nonce ) {
$nonce = nexus_request_data()->get_post_data( 'ajax_nonce' );
}
if ( ! wp_verify_nonce( $nonce, 'crb-ajax-admin' ) ) {
return false;
}
*/
return true;
}
check_ajax_referer( 'crb-ajax-admin', 'ajax_nonce' );
if ( ! cerber_user_can_manage() ) {
if ( $strict ) {
wp_die( 'Oops! Access denied.' );
}
return 0;
}
return true;
}
function crb_admin_allowed_ajax( $action ) {
$list = array(
'cerber_ajax',
'cerber_scan_control',
'cerber_ref_upload',
'cerber_view_file',
'cerber_scan_bulk_files'
);
return in_array( $action, $list );
}
function crb_is_task_permitted( $die = false ) {
if ( is_super_admin()
|| nexus_is_valid_request() ) {
return true;
}
if ( $die ) {
wp_die( 'Oops! Access denied.' );
}
return false;
}
/**
* Identify and render Cerber's admin page
*
* @param string $page_id
* @param string $active_tab
*/
function cerber_render_admin_page( $page_id = '', $active_tab = '' ) {
if ( nexus_get_context() ) {
nexus_show_remote_page();
return;
}
$error = '';
if ( $page = cerber_get_admin_page_config( $page_id ) ) {
if ( ! empty( $page['pro_page'] ) && ! lab_lab() ) {
$error = 'This feature requires the PRO version of the plugin.';
}
else {
if ( ( $tab_filter = crb_array_get( $page, 'tab_filter' ) )
&& is_callable( $tab_filter ) ) {
$page['tabs'] = call_user_func( $tab_filter, $page['tabs'] );
}
cerber_show_admin_page( $page['title'], $page['tabs'], $active_tab, $page['callback'] );
}
}
else {
$error = 'Unknown admin page: ' . htmlspecialchars( $page_id );
}
if ( $error ) {
echo 'ERROR: ' . $error . ' on ' . get_bloginfo( 'name' ) . '
';
}
}
function cerber_get_admin_page_config( $page = '' ) {
if ( ! $page ) {
if ( ! $page = crb_admin_get_page() ) {
return false;
}
}
if ( $config = crb_addon_admin_page( $page ) ) {
return $config;
}
$admin_pages = array(
'cerber-security' => array(
'title' => 'WP Cerber Security',
'tabs' => array(
'dashboard' => array( 'bxs-dashboard', __( 'Dashboard', 'wp-cerber' ) ),
'activity' => array( 'bx-pulse', __( 'Activity', 'wp-cerber' ) ),
'sessions' => array( 'bx-group', __( 'Sessions', 'wp-cerber' ) ),
'lockouts' => array( 'bxs-shield', __( 'Lockouts', 'wp-cerber' ) ),
'main' => array( 'bx-slider', __( 'Main Settings', 'wp-cerber' ) ),
'acl' => array( 'bx-lock', __( 'Access Lists', 'wp-cerber' ) ),
'hardening' => array( 'bx-shield-alt', __( 'Hardening', 'wp-cerber' ) ),
//'users' => array( 'bx-group', __( 'Users', 'wp-cerber' ) ),
'notifications' => array( 'bx-bell', __( 'Notifications', 'wp-cerber' ) ),
),
'tab_filter' => function ( $tabs ) {
crb_del_expired_blocks();
$blocked = cerber_blocked_num();
$acl = cerber_db_get_var( 'SELECT COUNT(ip) FROM ' . CERBER_ACL_TABLE . ' WHERE acl_slice = 0' );
crb_sessions_del_expired();
$uss = crb_sessions_get_num();
if ( ! $uss ) {
cerber_bg_task_add( 'crb_sessions_sync_all' );
}
$tabs['sessions'][1] .= ' ' . $uss . ' ';
$tabs['lockouts'][1] .= ' ' . $blocked . ' ';
$tabs['acl'][1] .= ' ' . $acl . ' ';
return $tabs;
},
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'acl':
cerber_acl_form();
break;
case 'activity':
cerber_show_activity();
break;
case 'sessions':
crb_admin_show_sessions();
break;
case 'lockouts':
cerber_show_lockouts();
break;
case 'help':
cerber_show_help();
break;
case 'dashboard':
cerber_show_dashboard();
break;
default:
cerber_show_settings_form( $tab );
}
}
),
'cerber-recaptcha' => array(
'title' => __( 'Anti-spam and bot detection settings', 'wp-cerber' ),
'tabs' => array(
'antispam' => array( 'bx-chip', __( 'Anti-spam engine', 'wp-cerber' ) ),
'captcha' => array( 'bxl-google', 'reCAPTCHA' ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'captcha':
$group = 'recaptcha';
break;
default:
$group = 'antispam';
}
cerber_show_settings_form( $group );
}
),
'cerber-traffic' => array(
'title' => __( 'Traffic Inspector', 'wp-cerber' ),
'tabs' => array(
'traffic' => array( 'bx-show', __( 'Live Traffic', 'wp-cerber' ) ),
'ti_settings' => array( 'bx-slider', __( 'Settings', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'ti_settings':
cerber_show_settings_form( 'traffic' );
break;
default:
cerber_show_traffic();
}
}
),
'cerber-shield' => array(
'title' => __( 'Data Shield Policies', 'wp-cerber' ),
'tabs' => array(
'user_shield' => array( 'bx-group', __( 'Accounts & Roles', 'wp-cerber' ) ),
'opt_shield' => array( 'bx-slider', __( 'Site Settings', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
cerber_show_settings_form( $tab );
}
),
'cerber-users' => array(
'title' => __( 'User Policies', 'wp-cerber' ),
'tabs' => array(
'role_policies' => array( 'bx-group', __( 'Role-Based', 'wp-cerber' ) ),
'global_policies' => array( 'bx-user-detail', __( 'Global', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'role_policies':
crb_admin_show_role_policies();
break;
case 'global_policies':
cerber_show_settings_form( 'users' );
break;
default:
cerber_show_settings_form( $tab );
}
}
),
'cerber-rules' => array(
'pro_page' => 1,
'title' => __( 'Security Rules', 'wp-cerber' ),
'tabs' => array(
'geo' => array( 'bxs-world', __( 'Countries', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'geo':
crb_admin_show_geo_rules();
break;
default:
crb_admin_show_geo_rules();
}
}
),
'cerber-integrity' => array(
'title' => __( 'Site Integrity', 'wp-cerber' ),
'tabs' => array(
'scan_main' => array( 'bx-radar', __( 'Security Scanner', 'wp-cerber' ) ),
'scan_settings' => array( 'bxs-slider-alt', __( 'Settings', 'wp-cerber' ) ),
'scan_schedule' => array( 'bx-time', __( 'Scheduling', 'wp-cerber' ) ),
'scan_policy' => array( 'bx-bolt', __( 'Cleaning up', 'wp-cerber' ) ),
'scan_ignore' => array( 'bx-hide', __( 'Ignore List', 'wp-cerber' ) ),
'scan_quarantine' => array( 'bx-trash', __( 'Quarantine', 'wp-cerber' ) ),
'scan_insights' => array( 'bx-flask', __( 'Analytics', 'wp-cerber' ) ),
),
'tab_filter' => function ( $tabs ) {
$numi = 0;
if ( $list = cerber_get_set( 'ignore-list' ) ) {
$numi = count( $list );
}
$numq = cerber_get_set( 'quarantined_total', null, false );
if ( ! is_numeric( $numq ) ) {
cerber_bg_task_add( '_crb_qr_total_sync' );
$numq = '';
}
$tabs['scan_quarantine'][1] .= ' ' . $numq . ' ';
$tabs['scan_ignore'][1] .= ' ' . $numi . ' ';
return $tabs;
},
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'scan_settings':
cerber_show_settings_form( 'scanner' );
break;
case 'scan_schedule':
cerber_show_settings_form( 'schedule' );
break;
case 'scan_policy':
cerber_show_settings_form( 'policies' );
break;
case 'scan_quarantine':
cerber_show_quarantine();
break;
case 'scan_ignore':
cerber_show_ignore();
break;
case 'scan_insights':
cerber_scan_insights();
break;
case 'help':
cerber_show_help();
break;
default:
cerber_show_scanner();
}
}
),
'cerber-tools' => array(
'title' => __( 'Tools', 'wp-cerber' ),
'tabs' => array(
//'imex' => array( 'bx-layer', __( 'Export & Import', 'wp-cerber' ) ),
'imex' => array( 'bx-layer', __( 'Manage Settings', 'wp-cerber' ) ),
'diagnostic' => array( 'bx-wrench', __( 'Diagnostic', 'wp-cerber' ) ),
'diag-log' => array( 'bx-bug', __( 'Diagnostic Log', 'wp-cerber' ) ),
'change-log' => array( 'bx-collection', __( 'Changelog', 'wp-cerber' ) ),
'license' => array( 'bx-key', __( 'License', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'diagnostic':
cerber_show_diag();
break;
case 'license':
cerber_show_lic();
break;
case 'diag-log':
cerber_show_diag_log();
break;
case 'change-log':
cerber_show_change_log();
break;
case 'help':
cerber_show_help();
break;
default:
if ( ! nexus_is_valid_request() ) {
cerber_show_imex();
}
else {
echo 'This admin page is not available in this mode.';
}
}
}
),
);
return crb_array_get( $admin_pages, $page );
}
function crb_admin_parse_query( $fields, $alt = array() ) {
$arr = crb_get_query_params();
$ret = array();
foreach ( $fields as $field ) {
$val = $alt[ $field ] ?? crb_array_get( $arr, $field, false );
if ( is_array( $val ) ) {
$val = array_map( 'trim', $val );
}
elseif ( $val !== false ) {
$val = trim( $val );
}
$ret[ $field ] = $val;
}
return (object) $ret;
}
function crb_admin_get_page() {
if ( nexus_is_valid_request() ) {
$page = nexus_request_data()->page;
}
else {
$page = cerber_get_get( 'page', '[A-Z0-9\_\-]+' );
}
return $page;
}
function crb_admin_get_tab( $tabs = array() ) {
if ( nexus_is_valid_request() ) {
$tab = nexus_request_data()->tab;
}
else {
$tab = cerber_get_get( 'tab', '[\w\-]+' );
}
if ( empty( $tabs ) ) {
return $tab;
}
$tabs['help'] = 1; // always must be in the array
if ( ! $tab || ! isset( $tabs[ $tab ] ) ) {
reset( $tabs );
$tab = key( $tabs );
}
return $tab;
}
function cerber_show_tabs( $active, $tabs = array() ) {
echo '';
$args = array( 'page' => crb_admin_get_page() );
foreach ( $tabs as $tab => $data ) {
echo ' ' . $data[1] . ' ';
}
echo ' ' . __( 'Help', 'wp-cerber' ) . ' ';
$lab = lab_indicator();
$ro = '';
if ( nexus_is_valid_request() && ! nexus_is_granted( 'submit' ) ) {
$ro = 'Read-only mode ';
}
echo ' ' . $ro . ' ' . $lab . '
';
echo '';
}
// Access Lists (ACL) ---------------------------------------------------------
/**
* @param string $ip
* @param string $tag
* @param string $comment
* @param int $acl_slice
*
* @return bool|WP_Error
*/
function cerber_acl_add( $ip, $tag, $comment = '', $acl_slice = 0 ) {
global $wpdb;
$ip = trim( $ip );
$acl_slice = absint( $acl_slice );
$v6range = '';
$ver6 = 0;
if ( cerber_is_ipv4( $ip ) ) {
$begin = ip2long( $ip );
$end = ip2long( $ip );
}
elseif ( cerber_is_ipv6( $ip ) ) {
$ip = cerber_ipv6_short( $ip );
list( $begin, $end, $v6range ) = crb_ipv6_prepare( $ip, $ip );
$ver6 = 1;
}
elseif ( ( $range = cerber_any2range( $ip ) )
&& is_array( $range ) ) {
$ver6 = $range['IPV6'];
$begin = $range['begin'];
$end = $range['end'];
$v6range = $range['IPV6range'];
}
else {
return new WP_Error( 'acl_wrong_ip', __( 'Incorrect IP address or IP range', 'wp-cerber' ) . ' ' . $ip );
}
if ( cerber_db_get_var( 'SELECT ip FROM ' . CERBER_ACL_TABLE . ' WHERE acl_slice = ' . $acl_slice . ' AND ver6 = ' . $ver6 . ' AND ip_long_begin = ' . $begin . ' AND ip_long_end = ' . $end . ' AND v6range = "' . $v6range . '" LIMIT 1' ) ) {
return new WP_Error( 'acl_wrong_ip', __( 'The IP address you are trying to add is already in the list', 'wp-cerber' ) );
}
$result = $wpdb->insert( CERBER_ACL_TABLE, array(
'ip' => $ip,
'ip_long_begin' => $begin,
'ip_long_end' => $end,
'tag' => $tag,
'comments' => $comment,
'acl_slice' => $acl_slice,
'v6range' => $v6range,
'ver6' => $ver6,
), array( '%s', '%d', '%d', '%s', '%s', '%d', '%s', '%d' ) );
if ( ! $result ) {
return new WP_Error( 'acl_db_error', $wpdb->last_error );
}
crb_event_handler( 'ip_event', array(
'e_type' => 'acl_add',
'ip' => $ip,
'tag' => $tag,
'slice' => $acl_slice,
'comments' => $comment,
) );
return true;
}
function cerber_add_white( $ip, $comment = '' ) {
return cerber_acl_add( $ip, 'W', $comment );
}
function cerber_add_black( $ip, $comment = '' ) {
return cerber_acl_add( $ip, 'B', $comment );
}
function cerber_acl_remove( $ip, $acl_slice = 0 ) {
if ( ! is_numeric( $acl_slice ) ) {
return false;
}
$acl_slice = absint( $acl_slice );
$ip = preg_replace( CRB_IP_NET_RANGE, ' ', $ip );
$ret = cerber_db_query( 'DELETE FROM ' . CERBER_ACL_TABLE . ' WHERE acl_slice = ' . $acl_slice . ' AND ip = "' . $ip . '"' );
crb_event_handler( 'ip_event', array(
'e_type' => 'acl_remove',
'ip' => $ip,
'slice' => $acl_slice,
'result' => $ret
) );
return $ret;
}
/**
* Can a given IP be added to the blacklist
*
* @param $ip string A candidate to be added to the list
* @param $list string
*
* @return bool True if IP can be listed
*/
function cerber_can_be_listed( $ip, $list = 'B' ) {
if ( $list == 'B' ) {
$admin_ip = cerber_get_remote_ip();
if ( cerber_is_ip( $ip ) ) {
if ( $admin_ip == cerber_ipv6_short( $ip ) ) {
return false;
}
return true;
}
// $ip = range
if ( crb_acl_is_white( $admin_ip ) ) {
return true;
}
if ( ! $range = cerber_any2range( $ip ) ) {
return false;
}
if ( cerber_is_ip_in_range( $range, $admin_ip ) ) {
return false;
}
return true;
}
return true;
}
/**
* Bulk action for WP_List_Table
*
* @return bool|array|string
*/
function cerber_get_bulk_action() {
// GET
if ( cerber_is_http_get() ) {
if ( ( $ac = crb_get_query_params( 'action', '[\w\-]+' ) ) && $ac != '-1' ) {
return $ac;
}
if ( ( $ac = crb_get_query_params( 'action2', '[\w\-]+' ) ) && $ac != '-1' ) {
return $ac;
}
return false;
}
// POST
if ( ( $ac = crb_get_post_fields( 'action', false, '[\w\-]+' ) ) && $ac != '-1' ) {
return $ac;
}
if ( ( $ac = crb_get_post_fields( 'action2', false, '[\w\-]+' ) ) && $ac != '-1' ) {
return $ac;
}
return false;
}
function crb_admin_cool_features() {
return
'' .
__( 'These features are available in the professional version of WP Cerber.', 'wp-cerber' ) .
'
' . __( 'Know more about all advantages at', 'wp-cerber' ) .
'
https://wpcerber.com/pro/
';
}
/**
* @param string $url
* @param string $ancor
* @param string $msg
*
* @return string
*/
function crb_confirmation_link( $url, $ancor, $msg = '' ) {
if ( ! $msg ) {
$msg = __( 'Are you sure?', 'wp-cerber' );
}
return '' . $ancor . ' ';
}
/**
* @param array $args
* @param string $title
*
* @return string
*
* @since 8.9.6.1
*/
function crb_test_notify_link( $args = array(), $title = null ) {
return '[ ' . ( $title ?? __( 'Click to send test', 'wp-cerber' ) ) . ' ] ';
}
// Pop-up dialogs -------------------------------------------------------------
/**
* Creates a pop-up dialog window with a button that opens the dialog.
*
* @param array $fields Form fields - left side
* @param array $aside Form fields - right side, optional
* @param string $label Button label
* @param string $button_icon Button icon
* @param string $method Form method
* @param null $action Form action
*
* @return string The HTML code of the pop-up prepared to add to the web page
*
* @since 8.9.5.3
*/
function crb_create_popup_dialog( $fields, $aside = array(), $label = '', $button_icon = '', $method = 'get', $action = null ) {
static $counter = 0;
$counter ++; // In case of multiple dialogs on a page
if ( $button_icon ) {
$control = '';
}
else {
$control = '';
}
if ( $aside ) {
$class = 'crb-table-2cols';
$td_aside = ' ';
$last_row = ' ';
}
else {
$class = 'crb-table-1col';
$td_aside = '';
$last_row = '';
}
$dialog = $control;
$dialog .= '';
return $dialog;
}
/**
* Creates a basic HTML form for pop-up dialogs
*
* @param array $form_data List of form fields
*
* @return string HTML code
*
* @since 8.9.5.3
*/
function crb_create_dialog_form( $form_data ) {
$form = '';
if ( $title = $form_data['title'] ?? '' ) {
$form .= '' . $title . ' ';
}
$form .= $form_data['html'] ?? '';
$hidden = $form_data['hidden'] ?? array();
foreach ( $hidden as $field => $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $val ) {
$form .= ' ' . "\n";
}
}
else {
$form .= ' ' . "\n";
}
}
$form .= '';
$mess = $form_data['error_messages'] ?? array();
foreach ( $mess as $id => $msg ) {
$form .= '' . $msg . '
';
}
return '';
}
// Setting up menu editor -----------------------------------------------------
add_action( 'admin_head-nav-menus.php', function () {
add_meta_box( 'wp_cerber_nav_menu',
'WP Cerber',
'cerber_nav_menu_box',
'nav-menus',
'side',
'low' );
} );
function cerber_nav_menu_box() {
// Warning: do not change array indexes
$list = array(
'login-url' => __( 'Log In', 'wp-cerber' ),
'logout-url' => __( 'Log Out', 'wp-cerber' ),
'reg-url' => __( 'Register', 'wp-cerber' ),
);
if ( class_exists( 'WooCommerce' ) ) {
$list['wc-login-url'] = __( 'WooCommerce Log In', 'wp-cerber' );
$list['wc-logout-url'] = __( 'WooCommerce Log Out', 'wp-cerber' );
}
?>
o MIDATcRMad&PMX {(* 2
dcT 64dیEp1`61 6Dc* IENDB` assets/flags/tn.png 0000644 00000000357 14757753175 0010323 0 ustar 00 PNG
IHDR ( >] 0PLTE @N/>/`koy!%: tRNSV mIDAT(c`
TR.(ӴxA4ARQcTAMJJQEtE+9
+]R2DT,<4Jf ٮÝ݉GX=$ >8&o. IENDB` assets/flags/sk.png 0000644 00000000474 14757753175 0010317 0 ustar 00 PNG
IHDR ( 9=\ WPLTE$ͥ>G#:MN%T[u{JJ8ouaY`ZnkʼnHzϏM tRNS} IDAT8 @Q,
Z@qs F}q!
~\V7L-KM:T3!0yOEH!T}Fg~@k\ ~{lZme?p| S;DxA]"9@V IENDB` assets/flags/th.png 0000644 00000000160 14757753175 0010305 0 ustar 00 PNG
IHDR ( s~% PLTE1̇-*J2n{ IDATc` P8 4c$_ #%?"P IENDB` assets/flags/va.png 0000644 00000001172 14757753175 0010304 0 ustar 00 PNG
IHDR ( ( H_ #PLTE ؈rCbԺX배Λɼc}NuԬcߩ鰛qfƷ,쥥kGG݃ssꖗT<ҿᦦ㬄6ַֽ֟ߍ䝝ύllhޔA燇ӚQ1 e.NоHڀ\N?,E IDAT8r0`qR;4a23aK}饗iH`eAEz Өa 2ID(VJ6U36 <{jʮƢuɇS:
Vk0ɵb5 7U8-GXۏOW"N CY)j:46ł;${M'ir@і Ct#p9
0ҭ?sJ[n @G06)2$kEOa05; IENDB` assets/flags/sh.png 0000644 00000000632 14757753175 0010310 0 ustar 00 PNG
IHDR ( Ș PLTE!i+=cggn`t.Vk gzi7gUnny[/dE~4v[_e3asv{k6nN|q]̕~L[TnRuvYplQuq IDAT( *8uѽJԎĨ9hKJ*vdⴴ`
8T]$Jf
Rt ?B#BI"o$<_:u{0~כĕ5G!YJ*Im/v[,`Y^0ACjLBNy]ÌJ_ Y IENDB` assets/flags/at.png 0000644 00000000135 14757753175 0010300 0 ustar 00 PNG
IHDR ( 4_- PLTE)9Nm IDATc` I 07,N IENDB` assets/flags/om.png 0000644 00000000256 14757753175 0010313 0 ustar 00 PNG
IHDR (
hb *PLTE J\ [^im<@z~핗0 ?IDATc4 (;SPBLY1E1MQ @gAT TBt %d IENDB` assets/flags/tz.png 0000644 00000000577 14757753175 0010343 0 ustar 00 PNG
IHDR ( 9=\ xPLTE +#wc
)7: Q1Ť_O#jX l<+b.N&4SEͪ`!W:}z/ IDAT8ˍY0 auET74@w=:Fԕ0fy=(rQ#
g~w"`| cr ]ވs>CmTQ9WqʨrIUĉ#)p4rαpqwTܣkA
jN8(r[\8qm,;+ . IENDB` assets/flags/cy.png 0000644 00000000433 14757753175 0010310 0 ustar 00 PNG
IHDR ( 9=\ EPLTE˾}x կ͟_PڈÏpߚ?ycX{ IDATI@cT}&V
#0@xJn
ss~(xqOe(
\@
'FzqU~d\qhV
MoF6]hVL3ML).,M7- IENDB` assets/flags/zw.png 0000644 00000000501 14757753175 0010331 0 ustar 00 PNG
IHDR ( Ș uPLTE j @ d ڋ퓆H0J0dh`9q "f``^ th0oo[; h00pj`9 j `3 IDAT(ύG0H"`"]r FۯE@ĔZ]y5/6y>~jq6!b|&(%RXkf-~
B IENDB` assets/flags/am.png 0000644 00000000162 14757753175 0010271 0 ustar 00 PNG
IHDR (
hb PLTE 3
5(M IDATc`P`0bt !5Q IENDB` assets/flags/sl.png 0000644 00000000151 14757753175 0010310 0 ustar 00 PNG
IHDR ( s~% PLTE r:;ZI IDATcx
V3Њ
4c b[G IENDB` assets/flags/za.png 0000644 00000000600 14757753175 0010303 0 ustar 00 PNG
IHDR ( 9=\ {PLTE }K wI <1;1įܯ*/jH>#lN?vskHV ~C'o5ЖP;O;\\0#¾߰"' IDAT8ˍ c[bڪuDʹ;;!
G5 #9mRhQ(is$K2&ӢS_U"";*ElOfԁoDmw='Ѡ
!O+{#=Rk=Wnu=gpǼp_c7rb,h
|h IENDB` assets/flags/gh.png 0000644 00000000304 14757753175 0010270 0 ustar 00 PNG
IHDR ( >] 'PLTE k> k?&% lܶ
0(zbR tRNSV KIDAT(cp6 &CEPLX"Aiai*gvbho؈))(9c!Fi P* g)Q0; IENDB` assets/flags/cd.png 0000644 00000000474 14757753175 0010270 0 ustar 00 PNG
IHDR ( i BPLTE!p+ %J},Cl>źQ]oN_50 IDAT8ˍ EѨ(APՖt8kgbF(3?5- -.&
7j8Z`^Eo\qa
ܸ
\}C\jj
,W#FXQA@zRjU _? IENDB` assets/flags/um.png 0000644 00000000374 14757753175 0010322 0 ustar 00 PNG
IHDR ( 4- -PLTEYfؑ<;nِ"4TSHGw͵sr`_LD IDATc03.N637NEeA 0_9~X 0ؙ%?ad`|0P% h&H`NK(N2ۖ-;E9ؕ3 FѾ
: 0`صjW: gRW IENDB` assets/flags/bs.png 0000644 00000000323 14757753175 0010277 0 ustar 00 PNG
IHDR ( Ș 3PLTE@s 7A Yh JV k~ " w,(VG)_Jc!@2?1J [IDAT(ϭӻ DѨ +kr@ogK>48 B)*>F}`EU#w@i[!T?Cx.C~Tt5YI IENDB` assets/flags/sr.png 0000644 00000000335 14757753175 0010322 0 ustar 00 PNG
IHDR ( >] *PLTE6}>"A̳
-7~?ߙ e$?'{".)1#2x tRNS} aIDAT(c`
TJ\0;vAA,ÙAXm/cZvetLFW9---CUZbA֙6cZib5< "3Hc IENDB` assets/flags/hr.png 0000644 00000000530 14757753175 0010304 0 ustar 00 PNG
IHDR ( Ș PLTEhhTTAA ddnn~aV3`nOY%E߉<|PXX00a33T~\0B~44RWs#o|Tx^J"M IDAT(!EQL%^ڃ(Yʫ#4+`Y{Vs\_#S!t ̝Bi)u$p=p%6q>1CwP4[ v1/Re}e IENDB` assets/flags/gd.png 0000644 00000000570 14757753175 0010271 0 ustar 00 PNG
IHDR ( ZO lPLTE'"#SXJTI2! z^&ϲ"}[eAj3z;/C!ep7K8*Z(30$GH{~:'ۆ IDAT(ϕ š+vZ
/7NB@Clכ
}
+hpig]6+-Kg4L\A5A+`Q5Ԑ>FM{.%
f UaB
y! ;[R[aaw9?5A3{Kx IENDB` assets/flags/yt.png 0000644 00000001355 14757753175 0010335 0 ustar 00 PNG
IHDR ( 9=\ PLTEپ)ҤÇ,aLyD
ꖈﰥۙݡ{jՕ1||22ϷԈz]T=hhh}}}Ւ죖[[[o_½n[rrrFFLL{m&ຳ½$B tRNS} IDAT8˥r0P .DT ProɃIt&e083ޭTGU=\xp3n7n|.͞X^7ˠe*K4b2趒*ke
r{|R2V7v}~~}ur"E,bc;:! 9،h"i%1c26-I=IrY=cBȁ&
Ž|楦/h[guIwÇaZBS7*_2;lnj/iyv-7#Lh5 -wObtN` @MVk^=Tvίqv@NcV;Nz.ʃKdٮ_n8ү+zU-7O IENDB` assets/flags/bg.png 0000644 00000000143 14757753175 0010263 0 ustar 00 PNG
IHDR ( WS PLTE n&M IDATcXTe
U <6OP IENDB` assets/flags/fm.png 0000644 00000000237 14757753175 0010301 0 ustar 00 PNG
IHDR ( 4- PLTEu}h ?IDATc` 0)`"hEдlP"PK\Fw*I&XCnd IENDB` assets/flags/tr.png 0000644 00000000343 14757753175 0010322 0 ustar 00 PNG
IHDR ( >] -PLTE
)5LV'em8Bt{F&