> An associative array of tab slugs and their labels. */ function wpvulnerability_get_admin_tabs() { $tabs = array( 'notifications' => array( 'label' => __( 'Notifications', 'wpvulnerability' ), ), 'analysis' => array( 'label' => __( 'Analysis', 'wpvulnerability' ), ), 'logs' => array( 'label' => __( 'Logs', 'wpvulnerability' ), ), 'security' => array( 'label' => __( 'Security', 'wpvulnerability' ), ), 'tools' => array( 'label' => __( 'Tools', 'wpvulnerability' ), ), ); // Add Debug tab only if WP_DEBUG is enabled. if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { $tabs['debug'] = array( 'label' => __( 'Debug', 'wpvulnerability' ), ); } $tabs['about'] = array( 'label' => __( 'About', 'wpvulnerability' ), ); return $tabs; } /** * Determines the active admin tab for the settings screen. * * @since 4.1.2 * * @param array> $tabs Registered admin tabs. * * @return string The active tab slug. */ function wpvulnerability_get_current_admin_tab( $tabs ) { $tab_keys = array_keys( $tabs ); $default = reset( $tab_keys ); $tab_filter = filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_SPECIAL_CHARS ); if ( $tab_filter ) { $tab_filter = sanitize_key( $tab_filter ); } if ( $tab_filter && isset( $tabs[ $tab_filter ] ) ) { return $tab_filter; } return $default ? $default : 'notifications'; } /** * Renders the requested admin tab content. * * @since 4.1.2 * * @param string $tab Tab slug to render. * * @return void */ function wpvulnerability_render_admin_tab( $tab ) { switch ( $tab ) { case 'analysis': wpvulnerability_render_admin_tab_analysis(); break; case 'logs': wpvulnerability_render_admin_tab_logs(); break; case 'security': wpvulnerability_render_admin_tab_security(); break; case 'tools': wpvulnerability_render_admin_tab_tools(); break; case 'debug': if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { wpvulnerability_render_admin_tab_debug(); } break; case 'about': wpvulnerability_render_admin_tab_about(); break; case 'notifications': default: wpvulnerability_render_admin_tab_notifications(); break; } } /** * Outputs the Notifications tab contents. * * @since 4.1.2 * * @return void */ function wpvulnerability_render_admin_tab_notifications() { $wpvulnerability_settings = get_option( 'wpvulnerability-config', array() ); if ( ! is_array( $wpvulnerability_settings ) ) { $wpvulnerability_settings = array(); } $defaults = array( 'cache' => 12, 'period' => 'weekly', 'day' => 'monday', 'hour' => 0, 'minute' => 0, 'emails' => '', 'slack_webhook' => '', 'teams_webhook' => '', 'discord_webhook' => '', 'telegram_bot_token' => '', 'telegram_chat_id' => '', 'notify' => array( 'email' => 'y', 'slack' => 'n', 'teams' => 'n', 'discord' => 'n', 'telegram' => 'n', ), ); $wpvulnerability_settings = wp_parse_args( $wpvulnerability_settings, $defaults ); // Normalize notify settings. if ( ! is_array( $wpvulnerability_settings['notify'] ) ) { $wpvulnerability_settings['notify'] = $defaults['notify']; } else { $wpvulnerability_settings['notify'] = wp_parse_args( $wpvulnerability_settings['notify'], $defaults['notify'] ); } $wpvulnerability_settings['notify'] = wpvulnerability_normalize_notify_settings( $wpvulnerability_settings['notify'] ); $email_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['email'] ); $slack_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['slack'] ); $teams_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['teams'] ); $discord_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['discord'] ); $telegram_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['telegram'] ); // Check if cache is forced. $forced_cache = null; $cache_options = array( 1, 6, 12, 24 ); if ( defined( 'WPVULNERABILITY_CACHE_HOURS' ) ) { $forced_cache = (int) WPVULNERABILITY_CACHE_HOURS; if ( ! in_array( $forced_cache, $cache_options, true ) ) { $cache_options[] = $forced_cache; sort( $cache_options, SORT_NUMERIC ); } } $current_cache = null !== $forced_cache ? $forced_cache : (int) $wpvulnerability_settings['cache']; $admin_email = get_bloginfo( 'admin_email' ); ?>

ā±ļø

Learn more →', 'wpvulnerability' ) ), esc_url( 'https://www.wpvulnerability.com/plugin/#cache-duration' ) ); ?>

šŸ“…
:
šŸ“¢
Learn how to create a Slack incoming webhook →', 'wpvulnerability' ) ), esc_url( 'https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/' ) ); ?>
Learn how to create a Teams incoming webhook →', 'wpvulnerability' ) ), esc_url( 'https://learn.microsoft.com/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook' ) ); ?>
Learn how to create a Discord webhook →', 'wpvulnerability' ) ), esc_url( 'https://support.discord.com/hc/articles/228383668' ) ); ?>
Learn how to create a Telegram bot and get Chat ID →', 'wpvulnerability' ) ), esc_url( 'https://core.telegram.org/bots' ) ); ?>
array( 'label' => __( 'WordPress Components', 'wpvulnerability' ), 'items' => array( 'core' => array( 'label' => __( 'WordPress Core', 'wpvulnerability' ), 'icon' => '🌐', ), 'plugins' => array( 'label' => __( 'Plugins', 'wpvulnerability' ), 'icon' => '🧩', ), 'themes' => array( 'label' => __( 'Themes', 'wpvulnerability' ), 'icon' => 'šŸŽØ', ), ), ), 'software' => array( 'label' => __( 'Software & Languages', 'wpvulnerability' ), 'items' => array( 'php' => array( 'label' => __( 'PHP', 'wpvulnerability' ), 'icon' => '🐘', ), ), ), 'webservers' => array( 'label' => __( 'Web Servers', 'wpvulnerability' ), 'items' => array( 'apache' => array( 'label' => __( 'Apache HTTPD', 'wpvulnerability' ), 'icon' => '🪶', ), 'nginx' => array( 'label' => __( 'nginx', 'wpvulnerability' ), 'icon' => '🟩', ), ), ), 'databases' => array( 'label' => __( 'Databases', 'wpvulnerability' ), 'items' => array( 'mariadb' => array( 'label' => __( 'MariaDB', 'wpvulnerability' ), 'icon' => '🐬', ), 'mysql' => array( 'label' => __( 'MySQL', 'wpvulnerability' ), 'icon' => '🐬', ), 'sqlite' => array( 'label' => __( 'SQLite', 'wpvulnerability' ), 'icon' => 'šŸ’¾', ), ), ), 'tools' => array( 'label' => __( 'Additional Tools', 'wpvulnerability' ), 'items' => array( 'imagemagick' => array( 'label' => __( 'ImageMagick', 'wpvulnerability' ), 'icon' => 'šŸ–¼ļø', ), 'curl' => array( 'label' => __( 'curl', 'wpvulnerability' ), 'icon' => '🌐', ), 'memcached' => array( 'label' => __( 'memcached', 'wpvulnerability' ), 'icon' => '⚔', ), 'redis' => array( 'label' => __( 'Redis', 'wpvulnerability' ), 'icon' => 'šŸ”“', ), ), ), ); ?>

$category_data ) : ?>
$component_data ) : ?>

Learn more about force-hiding checks →', 'wpvulnerability' ) ), esc_url( 'https://www.wpvulnerability.com/plugin/#force-hiding-checks' ) ); ?>

$total_pages ) { $current_page = $total_pages; } $logs = wpvulnerability_get_api_logs( $logs_per_page, $current_page ); $logs_page_url = add_query_arg( array( 'page' => 'wpvulnerability-options', 'tab' => 'logs', 'logs_per_page' => $logs_per_page, 'log_page' => $current_page, ), admin_url( 'options-general.php' ) ); $requested_log = isset( $_GET['log'] ) && is_string( $_GET['log'] ) ? absint( (int) wp_unslash( $_GET['log'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $view_log = null; $log_error = ''; $pagination = ''; if ( $total_pages > 1 ) { $pagination_base = remove_query_arg( array( 'log', 'log_page' ), $logs_page_url ); $pagination = paginate_links( array( 'base' => add_query_arg( 'log_page', '%#%', $pagination_base ), 'format' => '', 'current' => $current_page, 'total' => $total_pages, 'prev_text' => __( '« Previous', 'wpvulnerability' ), 'next_text' => __( 'Next »', 'wpvulnerability' ), 'type' => 'list', ) ); } if ( $requested_log > 0 ) { $nonce = isset( $_GET['_wpnonce'] ) && is_string( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( $nonce && wp_verify_nonce( $nonce, 'wpvulnerability_view_log_' . $requested_log ) ) { $view_log = wpvulnerability_get_api_log( $requested_log ); if ( ! $view_log ) { $log_error = __( 'Log entry not found or has been removed.', 'wpvulnerability' ); } } else { $log_error = __( 'Unable to load the requested log entry.', 'wpvulnerability' ); } } ?>

', esc_attr( (string) $forced ) ); printf( '

%s

', esc_html__( 'This value is enforced by the WPVULNERABILITY_LOG_RETENTION_DAYS constant.', 'wpvulnerability' ) ); } else { printf( '

%s

', esc_html__( 'Choose how long WPVulnerability should keep API response logs. Older logs are automatically deleted.', 'wpvulnerability' ) ); } ?>

post_content ) ); ?>

šŸ“‹

'margin: 0; padding: 4px 12px; height: auto;' ) ); ?>
'wpvulnerability-options', 'tab' => 'logs', 'log' => $log->ID, 'logs_per_page' => $logs_per_page, 'log_page' => $current_page, ), admin_url( 'options-general.php' ) ); $view_url = wp_nonce_url( $view_url, 'wpvulnerability_view_log_' . $log->ID ); ?>

'wpvulnerability-options', 'tab' => 'tools', ), admin_url( 'options-general.php' ) ); $wpvulnerability_cron_config = is_multisite() ? get_site_option( 'wpvulnerability-config', array() ) : get_option( 'wpvulnerability-config', array() ); if ( ! is_array( $wpvulnerability_cron_config ) ) { $wpvulnerability_cron_config = array(); } $is_main_site = ( ! is_multisite() || is_main_site() ); $cron_status = wpvulnerability_get_cron_status( $wpvulnerability_cron_config, $is_main_site ); $cron_schedules = function_exists( 'wp_get_schedules' ) ? wp_get_schedules() : array(); $_df = get_option( 'date_format' ); $_tf = get_option( 'time_format' ); $date_format = ( is_scalar( $_df ) ? (string) $_df : '' ) . ' ' . ( is_scalar( $_tf ) ? (string) $_tf : '' ); ?>

šŸ”„

šŸ“§

ā°

0 ? implode( ', ', $found_schedules ) : __( 'None', 'wpvulnerability' ); $next_run = ( $row_next_run_raw && is_numeric( $row_next_run_raw ) ) ? date_i18n( $date_format, (int) $row_next_run_raw ) : '—'; $row_messages = isset( $row['messages'] ) && is_array( $row['messages'] ) ? $row['messages'] : array(); $status_text = implode( ' ', array_map( static function ( $m ) { return is_scalar( $m ) ? (string) $m : ''; }, $row_messages ) ); // Determine status badge class. $status_class = 'success'; if ( strpos( $status_text, 'Missing' ) !== false || strpos( $status_text, 'Duplicate' ) !== false ) { $status_class = 'error'; } elseif ( strpos( $status_text, 'legacy' ) !== false ) { $status_class = 'warning'; } ?>

āœ“

  • – –

āš ļø


__( 'Plugins', 'wpvulnerability' ), 'themes' => __( 'Themes', 'wpvulnerability' ), 'php' => __( 'PHP', 'wpvulnerability' ), 'apache' => __( 'Apache HTTPD', 'wpvulnerability' ), 'nginx' => __( 'nginx', 'wpvulnerability' ), 'mariadb' => __( 'MariaDB', 'wpvulnerability' ), 'mysql' => __( 'MySQL', 'wpvulnerability' ), 'imagemagick' => __( 'ImageMagick', 'wpvulnerability' ), 'curl' => __( 'curl', 'wpvulnerability' ), 'memcached' => __( 'memcached', 'wpvulnerability' ), 'redis' => __( 'Redis', 'wpvulnerability' ), 'sqlite' => __( 'SQLite', 'wpvulnerability' ), ); ?>
$label ) : ?>

' . esc_html( $formatted_datetime ) . '' ); restore_previous_locale(); ?>

'CVE', 'euvd' => 'EUVD', 'jvn' => 'JVN', 'patchstack' => 'Patchstack', 'wpscan' => 'WPScan', 'wordfence' => 'Wordfence', ); if ( ! empty( $sources_stat ) ) : ?>

$src_label ) : ?>

šŸ’Ž

šŸ‘„

' . esc_html( is_scalar( $wpvulnerability_message_manual_success ) ? (string) $wpvulnerability_message_manual_success : '' ) . '

'; delete_transient( 'wpvulnerability_message_manual_success' ); unset( $wpvulnerability_message_manual_success ); } $wpvulnerability_message_manual_error = get_transient( 'wpvulnerability_message_manual_error' ); if ( $wpvulnerability_message_manual_error ) { echo '

' . esc_html( is_scalar( $wpvulnerability_message_manual_error ) ? (string) $wpvulnerability_message_manual_error : '' ) . '

'; delete_transient( 'wpvulnerability_message_manual_error' ); unset( $wpvulnerability_message_manual_error ); } settings_errors( 'admin_wpvulnerability_settings' ); settings_errors( 'admin_wpvulnerability_analyze' ); $tabs = wpvulnerability_get_admin_tabs(); if ( empty( $tabs ) ) { return; } $current_tab = wpvulnerability_get_current_admin_tab( $tabs ); if ( ! isset( $tabs[ $current_tab ] ) ) { $tab_keys = array_keys( $tabs ); $current_tab = reset( $tab_keys ); } ?>

: '; foreach ( $options as $hours ) { printf( '', esc_attr( (string) $hours ), selected( $current, $hours, false ), esc_html( sprintf( /* translators: %d: number of hours */ _n( '%d hour', '%d hours', $hours, 'wpvulnerability' ), $hours ) ) ); } echo ''; if ( null !== $forced_cache ) { printf( '', esc_attr( (string) $current ) ); } printf( '

%2$s

', esc_url( 'https://www.wpvulnerability.com/plugin/#cache-duration' ), esc_html__( 'Read more if you want to force the cache time.', 'wpvulnerability' ) ); } /** * Print when to send the vulnerability scan emails. * * @since 2.0.0 * * @return void */ function wpvulnerability_admin_period_callback() { // Get the saved plugin settings. $wpvulnerability_settings = get_option( 'wpvulnerability-config', array() ); if ( ! is_array( $wpvulnerability_settings ) ) { $wpvulnerability_settings = array(); } $defaults = array( 'period' => 'weekly', 'day' => 'monday', 'hour' => 0, 'minute' => 0, ); $wpvulnerability_settings = wp_parse_args( $wpvulnerability_settings, $defaults ); ?>




'y', 'slack' => 'n', 'teams' => 'n', ); if ( ! isset( $wpvulnerability_settings['notify'] ) || ! is_array( $wpvulnerability_settings['notify'] ) ) { $wpvulnerability_settings['notify'] = $defaults; } else { $wpvulnerability_settings['notify'] = wp_parse_args( $wpvulnerability_settings['notify'], $defaults ); } $wpvulnerability_settings['notify'] = wpvulnerability_normalize_notify_settings( $wpvulnerability_settings['notify'] ); $email_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['email'] ); $slack_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['slack'] ); $teams_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['teams'] ); ?>














$input The input fields to sanitize. * * @return array The sanitized values. */ function wpvulnerability_admin_sanitize( $input ) { $input_emails = array(); $defaults = array( 'notify' => array( 'email' => 'n', 'slack' => 'n', 'teams' => 'n', ), 'slack_webhook' => '', 'teams_webhook' => '', 'day' => 'monday', 'hour' => 0, 'minute' => 0, 'cache' => 12, 'log_retention' => 0, 'emails' => null, 'period' => 'weekly', ); $current_values = is_multisite() ? get_site_option( 'wpvulnerability-config', array() ) : get_option( 'wpvulnerability-config', array() ); if ( ! is_array( $current_values ) ) { $current_values = array(); } $sanitized_values = $current_values; if ( isset( $input['emails'] ) ) { $input_email_text = explode( ',', is_scalar( $input['emails'] ) ? (string) $input['emails'] : '' ); foreach ( $input_email_text as $input_email ) { $input_email = sanitize_email( trim( (string) $input_email ) ); if ( is_email( $input_email ) ) { $input_emails[] = $input_email; } } if ( count( $input_emails ) ) { $sanitized_values['emails'] = implode( ',', $input_emails ); } else { $sanitized_values['emails'] = null; } } if ( isset( $input['period'] ) ) { // Check the value of the period field and sanitize it. switch ( $input['period'] ) { case 'never': $sanitized_values['period'] = 'never'; break; case 'daily': $sanitized_values['period'] = 'daily'; break; case 'weekly': $sanitized_values['period'] = 'weekly'; break; default: $sanitized_values['period'] = 'weekly'; break; } } if ( isset( $input['day'] ) ) { $day = strtolower( sanitize_text_field( is_scalar( $input['day'] ) ? (string) $input['day'] : '' ) ); $valid_days = array( 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' ); $sanitized_values['day'] = in_array( $day, $valid_days, true ) ? $day : 'monday'; } if ( isset( $input['hour'] ) ) { $hour = is_scalar( $input['hour'] ) ? (int) $input['hour'] : 0; $sanitized_values['hour'] = max( 0, min( 23, $hour ) ); } if ( isset( $input['minute'] ) ) { $minute = is_scalar( $input['minute'] ) ? (int) $input['minute'] : 0; $sanitized_values['minute'] = max( 0, min( 59, $minute ) ); } if ( isset( $input['cache'] ) ) { $cache = is_scalar( $input['cache'] ) ? (int) $input['cache'] : 0; $sanitized_values['cache'] = in_array( $cache, array( 1, 6, 12, 24 ), true ) ? $cache : 12; } if ( isset( $input['log_retention'] ) ) { $retention = is_scalar( $input['log_retention'] ) ? (int) $input['log_retention'] : 0; if ( in_array( $retention, wpvulnerability_get_log_retention_values(), true ) ) { $sanitized_values['log_retention'] = $retention; } } if ( isset( $input['notify'] ) && is_array( $input['notify'] ) ) { $notify_raw = (array) wp_unslash( $input['notify'] ); $notify_input = array(); foreach ( $notify_raw as $k => $v ) { $notify_input[ (string) $k ] = sanitize_text_field( is_scalar( $v ) ? (string) $v : '' ); } $sanitized_values['notify'] = wpvulnerability_normalize_notify_settings( $notify_input ); } if ( isset( $input['slack_webhook'] ) ) { $slack_url = trim( is_scalar( $input['slack_webhook'] ) ? (string) $input['slack_webhook'] : '' ); if ( '' !== $slack_url ) { $validated_slack = wpvulnerability_validate_webhook_url( $slack_url, array( 'hooks.slack.com' ) ); if ( '' === $validated_slack ) { add_settings_error( 'wpvulnerability-config', 'invalid-slack-webhook', __( 'Invalid Slack webhook URL. Must be a valid HTTPS URL from hooks.slack.com', 'wpvulnerability' ), 'error' ); $sanitized_values['slack_webhook'] = ''; } else { $sanitized_values['slack_webhook'] = $validated_slack; } } else { $sanitized_values['slack_webhook'] = ''; } } if ( isset( $input['teams_webhook'] ) ) { $teams_url = trim( is_scalar( $input['teams_webhook'] ) ? (string) $input['teams_webhook'] : '' ); if ( '' !== $teams_url ) { $validated_teams = wpvulnerability_validate_webhook_url( $teams_url, array( 'office.com', 'office365.com', 'api.hooks.microsoft.com' ) ); if ( '' === $validated_teams ) { add_settings_error( 'wpvulnerability-config', 'invalid-teams-webhook', __( 'Invalid Microsoft Teams webhook URL. Must be a valid HTTPS URL from office.com, office365.com, or api.hooks.microsoft.com', 'wpvulnerability' ), 'error' ); $sanitized_values['teams_webhook'] = ''; } else { $sanitized_values['teams_webhook'] = $validated_teams; } } else { $sanitized_values['teams_webhook'] = ''; } } if ( isset( $input['discord_webhook'] ) ) { $discord_url = trim( is_scalar( $input['discord_webhook'] ) ? (string) $input['discord_webhook'] : '' ); if ( '' !== $discord_url ) { $validated_discord = wpvulnerability_validate_webhook_url( $discord_url, array( 'discord.com', 'discordapp.com' ) ); if ( '' === $validated_discord ) { add_settings_error( 'wpvulnerability-config', 'invalid-discord-webhook', __( 'Invalid Discord webhook URL. Must be a valid HTTPS URL from discord.com or discordapp.com', 'wpvulnerability' ), 'error' ); $sanitized_values['discord_webhook'] = ''; } else { $sanitized_values['discord_webhook'] = $validated_discord; } } else { $sanitized_values['discord_webhook'] = ''; } } if ( isset( $input['telegram_bot_token'] ) ) { $telegram_bot_token = sanitize_text_field( trim( is_scalar( $input['telegram_bot_token'] ) ? (string) $input['telegram_bot_token'] : '' ) ); if ( '' !== $telegram_bot_token ) { if ( ! preg_match( '/^\d+:[A-Za-z0-9_-]+$/', $telegram_bot_token ) ) { add_settings_error( 'wpvulnerability-config', 'invalid-telegram-token', __( 'Invalid Telegram bot token format. Must be like: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', 'wpvulnerability' ), 'error' ); $sanitized_values['telegram_bot_token'] = ''; } else { $sanitized_values['telegram_bot_token'] = $telegram_bot_token; } } else { $sanitized_values['telegram_bot_token'] = ''; } } if ( isset( $input['telegram_chat_id'] ) ) { $sanitized_values['telegram_chat_id'] = sanitize_text_field( trim( is_scalar( $input['telegram_chat_id'] ) ? (string) $input['telegram_chat_id'] : '' ) ); } $sanitized_values = wp_parse_args( $sanitized_values, $defaults ); // Schedule notification based on sanitized values. wpvulnerability_schedule_notification_event( $sanitized_values ); return $sanitized_values; } /** * Sanitizes the input fields for vulnerability analysis. * * This function takes an array of input fields and sanitizes them by setting * the corresponding values in the output array to 1 if they are present in * the input. The possible fields are 'core', 'plugins', 'themes', 'php', * 'apache', and 'nginx'. * * @since 3.3.0 * * @param array $input The input fields to sanitize. * * @return array The sanitized values with keys 'core', 'plugins', 'themes', 'php', 'apache', and 'nginx'. */ function wpvulnerability_analyze_sanitize( $input ) { $sanitized_values = array( 'core' => isset( $input['core'] ) ? 1 : 0, 'plugins' => isset( $input['plugins'] ) ? 1 : 0, 'themes' => isset( $input['themes'] ) ? 1 : 0, 'php' => isset( $input['php'] ) ? 1 : 0, 'apache' => isset( $input['apache'] ) ? 1 : 0, 'nginx' => isset( $input['nginx'] ) ? 1 : 0, 'mariadb' => isset( $input['mariadb'] ) ? 1 : 0, 'mysql' => isset( $input['mysql'] ) ? 1 : 0, 'imagemagick' => isset( $input['imagemagick'] ) ? 1 : 0, 'curl' => isset( $input['curl'] ) ? 1 : 0, 'memcached' => isset( $input['memcached'] ) ? 1 : 0, 'redis' => isset( $input['redis'] ) ? 1 : 0, 'sqlite' => isset( $input['sqlite'] ) ? 1 : 0, ); foreach ( $sanitized_values as $component => $value ) { $constant = 'WPVULNERABILITY_HIDE_' . strtoupper( (string) $component ); if ( defined( $constant ) && constant( $constant ) ) { $sanitized_values[ $component ] = 1; } } return $sanitized_values; } /** * Content for the Dashboard Widget * * @since 2.2.0 * * @return void */ function wpvulnerability_admin_dashboard_content() { if ( ! wpvulnerability_capabilities() ) { return; } // Get vulnerability counts for all components. $core_count = wpvulnerability_get_component_count( 'core' ); $plugins_count = wpvulnerability_get_component_count( 'plugins' ); $themes_count = wpvulnerability_get_component_count( 'themes' ); $php_count = wpvulnerability_get_component_count( 'php' ); $apache_count = wpvulnerability_get_component_count( 'apache' ); $nginx_count = wpvulnerability_get_component_count( 'nginx' ); $mariadb_count = wpvulnerability_get_component_count( 'mariadb' ); $mysql_count = wpvulnerability_get_component_count( 'mysql' ); $imagemagick_count = wpvulnerability_get_component_count( 'imagemagick' ); $curl_count = wpvulnerability_get_component_count( 'curl' ); $memcached_count = wpvulnerability_get_component_count( 'memcached' ); $redis_count = wpvulnerability_get_component_count( 'redis' ); $sqlite_count = wpvulnerability_get_component_count( 'sqlite' ); // Calculate total vulnerabilities (only for enabled components). $total_vulnerabilities = 0; if ( wpvulnerability_analyze_filter( 'core' ) ) { $total_vulnerabilities += $core_count; } if ( wpvulnerability_analyze_filter( 'plugins' ) ) { $total_vulnerabilities += $plugins_count; } if ( wpvulnerability_analyze_filter( 'themes' ) ) { $total_vulnerabilities += $themes_count; } if ( wpvulnerability_analyze_filter( 'php' ) ) { $total_vulnerabilities += $php_count; } if ( wpvulnerability_analyze_filter( 'apache' ) ) { $total_vulnerabilities += $apache_count; } if ( wpvulnerability_analyze_filter( 'nginx' ) ) { $total_vulnerabilities += $nginx_count; } if ( wpvulnerability_analyze_filter( 'mariadb' ) ) { $total_vulnerabilities += $mariadb_count; } if ( wpvulnerability_analyze_filter( 'mysql' ) ) { $total_vulnerabilities += $mysql_count; } if ( wpvulnerability_analyze_filter( 'imagemagick' ) ) { $total_vulnerabilities += $imagemagick_count; } if ( wpvulnerability_analyze_filter( 'curl' ) ) { $total_vulnerabilities += $curl_count; } if ( wpvulnerability_analyze_filter( 'memcached' ) ) { $total_vulnerabilities += $memcached_count; } if ( wpvulnerability_analyze_filter( 'redis' ) ) { $total_vulnerabilities += $redis_count; } if ( wpvulnerability_analyze_filter( 'sqlite' ) ) { $total_vulnerabilities += $sqlite_count; } // Determine status badge. $status_class = 'wpvuln-status-secure'; $status_text = 'āœ“ ' . __( 'All Clear', 'wpvulnerability' ); $status_icon = 'āœ“'; if ( $total_vulnerabilities > 0 ) { if ( ( wpvulnerability_analyze_filter( 'core' ) && $core_count > 0 ) || ( wpvulnerability_analyze_filter( 'php' ) && $php_count > 0 ) || $total_vulnerabilities > 5 ) { $status_class = 'wpvuln-status-critical'; $status_icon = 'āœ•'; /* translators: %d: number of vulnerabilities */ $status_text = sprintf( _n( '%d Critical Issue Found', '%d Critical Issues Found', $total_vulnerabilities, 'wpvulnerability' ), $total_vulnerabilities ); } else { $status_class = 'wpvuln-status-warning'; $status_icon = '⚠'; /* translators: %d: number of vulnerabilities */ $status_text = sprintf( _n( '%d Issue Found', '%d Issues Found', $total_vulnerabilities, 'wpvulnerability' ), $total_vulnerabilities ); } } // Get last check time. $raw_core_cache = get_option( 'wpvulnerability-core-cache', '' ); $core_cache = json_decode( is_string( $raw_core_cache ) ? $raw_core_cache : '', true ); $last_check_text = __( 'Never checked', 'wpvulnerability' ); if ( $core_cache && is_numeric( $core_cache ) ) { $cache_hours = wpvulnerability_cache_hours(); $last_check_time = $core_cache - ( $cache_hours * 3600 ); $time_diff = time() - $last_check_time; if ( $time_diff < 3600 ) { $minutes_ago = (int) floor( $time_diff / 60 ); /* translators: %d: number of minutes */ $last_check_text = sprintf( _n( '%d minute ago', '%d minutes ago', $minutes_ago, 'wpvulnerability' ), $minutes_ago ); } else { $hours_ago = (int) floor( $time_diff / 3600 ); /* translators: %d: number of hours */ $last_check_text = sprintf( _n( '%d hour ago', '%d hours ago', $hours_ago, 'wpvulnerability' ), $hours_ago ); } } // Status badge. echo '
'; echo esc_html( $status_text ); echo '
'; // Meta information. echo '
'; /* translators: %s: time since last check */ echo esc_html( sprintf( __( 'Last checked: %s', 'wpvulnerability' ), $last_check_text ) ); echo ' | ↻ ' . esc_html( __( 'Refresh Now', 'wpvulnerability' ) ) . ''; echo '
'; // If no vulnerabilities, show empty state. if ( 0 === $total_vulnerabilities ) { echo '
'; echo '
āœ“
'; echo '
' . esc_html( __( 'No vulnerabilities detected', 'wpvulnerability' ) ) . '
'; echo '
' . esc_html( __( 'Your site is up to date and secure', 'wpvulnerability' ) ) . '
'; echo '
'; } else { // WordPress Components section. echo '
' . esc_html( __( 'WordPress Components', 'wpvulnerability' ) ) . '
'; // Core. if ( wpvulnerability_analyze_filter( 'core' ) ) { $badge_class = $core_count > 0 ? 'wpvuln-badge-critical' : 'wpvuln-badge-secure'; $badge_icon = $core_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo '' . esc_html( __( 'WordPress Core', 'wpvulnerability' ) ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $core_count ) . ''; echo '
'; } // Plugins. if ( wpvulnerability_analyze_filter( 'plugins' ) ) { $badge_class = $plugins_count > 0 ? 'wpvuln-badge-critical' : 'wpvuln-badge-secure'; $badge_icon = $plugins_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo '' . esc_html( __( 'Plugins', 'wpvulnerability' ) ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $plugins_count ) . ''; echo '
'; if ( $plugins_count > 0 ) { echo '
'; echo wpvulnerability_list_plugins(); // phpcs:ignore echo '
'; } } // Themes. if ( wpvulnerability_analyze_filter( 'themes' ) ) { $badge_class = $themes_count > 0 ? 'wpvuln-badge-critical' : 'wpvuln-badge-secure'; $badge_icon = $themes_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo '' . esc_html( __( 'Themes', 'wpvulnerability' ) ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $themes_count ) . ''; echo '
'; if ( $themes_count > 0 ) { echo '
'; echo wpvulnerability_list_themes(); // phpcs:ignore echo '
'; } } // Server Software section. $php_version = wpvulnerability_detect_php(); $webserver = wpvulnerability_detect_webserver(); $sqlserver = wpvulnerability_detect_sqlserver(); $show_server_section = false; // Check if any server software is detected. if ( ( wpvulnerability_analyze_filter( 'php' ) && $php_version ) || ( isset( $webserver['id'] ) && isset( $webserver['version'] ) ) || ( isset( $sqlserver['id'] ) && isset( $sqlserver['version'] ) ) || wpvulnerability_get_software_version( 'imagemagick' ) || wpvulnerability_get_software_version( 'curl' ) || wpvulnerability_get_software_version( 'memcached' ) || wpvulnerability_get_software_version( 'redis' ) || wpvulnerability_get_software_version( 'sqlite' ) ) { $show_server_section = true; } if ( $show_server_section ) { echo '
' . esc_html( __( 'Server Software', 'wpvulnerability' ) ) . '
'; echo '
'; // PHP. if ( wpvulnerability_analyze_filter( 'php' ) && $php_version ) { $badge_class = $php_count > 0 ? 'wpvuln-badge-critical' : 'wpvuln-badge-secure'; $badge_icon = $php_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'PHP ' . esc_html( $php_version ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $php_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'php' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } // Web server (Apache/nginx). if ( isset( $webserver['id'] ) && isset( $webserver['version'] ) ) { if ( 'apache' === $webserver['id'] && wpvulnerability_analyze_filter( 'apache' ) ) { $badge_class = $apache_count > 0 ? 'wpvuln-badge-warning' : 'wpvuln-badge-secure'; $badge_icon = $apache_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'Apache ' . esc_html( $webserver['version'] ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $apache_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'apache' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } elseif ( 'nginx' === $webserver['id'] && wpvulnerability_analyze_filter( 'nginx' ) ) { $badge_class = $nginx_count > 0 ? 'wpvuln-badge-warning' : 'wpvuln-badge-secure'; $badge_icon = $nginx_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'nginx ' . esc_html( $webserver['version'] ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $nginx_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'nginx' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } } // Database (MariaDB/MySQL). if ( isset( $sqlserver['id'] ) && isset( $sqlserver['version'] ) ) { if ( 'mariadb' === $sqlserver['id'] && wpvulnerability_analyze_filter( 'mariadb' ) ) { $badge_class = $mariadb_count > 0 ? 'wpvuln-badge-warning' : 'wpvuln-badge-secure'; $badge_icon = $mariadb_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'MariaDB ' . esc_html( $sqlserver['version'] ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $mariadb_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'mariadb' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } elseif ( 'mysql' === $sqlserver['id'] && wpvulnerability_analyze_filter( 'mysql' ) ) { $badge_class = $mysql_count > 0 ? 'wpvuln-badge-warning' : 'wpvuln-badge-secure'; $badge_icon = $mysql_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'MySQL ' . esc_html( $sqlserver['version'] ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $mysql_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'mysql' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } } // ImageMagick. $imagemagick_version = wpvulnerability_get_software_version( 'imagemagick' ); if ( $imagemagick_version && wpvulnerability_analyze_filter( 'imagemagick' ) ) { $badge_class = $imagemagick_count > 0 ? 'wpvuln-badge-warning' : 'wpvuln-badge-secure'; $badge_icon = $imagemagick_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'ImageMagick ' . esc_html( $imagemagick_version ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $imagemagick_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'imagemagick' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } // curl. $curl_version = wpvulnerability_get_software_version( 'curl' ); if ( $curl_version && wpvulnerability_analyze_filter( 'curl' ) ) { $badge_class = $curl_count > 0 ? 'wpvuln-badge-warning' : 'wpvuln-badge-secure'; $badge_icon = $curl_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'curl ' . esc_html( $curl_version ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $curl_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'curl' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } // memcached. $memcached_version = wpvulnerability_get_software_version( 'memcached' ); if ( $memcached_version && wpvulnerability_analyze_filter( 'memcached' ) ) { $badge_class = $memcached_count > 0 ? 'wpvuln-badge-warning' : 'wpvuln-badge-secure'; $badge_icon = $memcached_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'memcached ' . esc_html( $memcached_version ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $memcached_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'memcached' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } // Redis. $redis_version = wpvulnerability_get_software_version( 'redis' ); if ( $redis_version && wpvulnerability_analyze_filter( 'redis' ) ) { $badge_class = $redis_count > 0 ? 'wpvuln-badge-warning' : 'wpvuln-badge-secure'; $badge_icon = $redis_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'Redis ' . esc_html( $redis_version ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $redis_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'redis' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } // SQLite. $sqlite_version = wpvulnerability_get_software_version( 'sqlite' ); if ( $sqlite_version && wpvulnerability_analyze_filter( 'sqlite' ) ) { $badge_class = $sqlite_count > 0 ? 'wpvuln-badge-warning' : 'wpvuln-badge-secure'; $badge_icon = $sqlite_count > 0 ? 'āœ•' : 'āœ“'; echo '
'; echo ''; echo 'SQLite ' . esc_html( $sqlite_version ) . ''; echo '' . esc_html( $badge_icon ) . ' ' . esc_html( (string) $sqlite_count ) . ''; echo wp_kses( wpvulnerability_eol_badge_html( 'sqlite' ), array( 'span' => array( 'class' => array(), 'title' => array(), ), ) ); echo '
'; } echo '
'; } } // Footer links. echo ''; } /** * Created a widget in the WordPress dashboard with vulnerability info. * * @since 2.2.0 * * @return void */ function wpvulnerability_admin_dashboard() { if ( wpvulnerability_capabilities() ) { wp_add_dashboard_widget( 'wpvulnerability', __( 'WPVulnerability Status', 'wpvulnerability' ), 'wpvulnerability_admin_dashboard_content', null, null, 'side', 'high' ); } } add_action( 'wp_dashboard_setup', 'wpvulnerability_admin_dashboard' ); /** * Initializes the WP-Admin settings page for the WP Vulnerability plugin * * @since 2.0.0 * * @return void */ function wpvulnerability_admin_init() { // Register the plugin settings to be saved in the database. register_setting( 'admin_wpvulnerability_settings', 'wpvulnerability-config', array( 'sanitize_callback' => 'wpvulnerability_admin_sanitize', 'default' => array(), ) ); // Add a section to the settings page. add_settings_section( 'admin_wpvulnerability_settings', __( 'Receive vulnerability notifications', 'wpvulnerability' ), 'wpvulnerability_admin_section_notifications', 'wpvulnerability-config' ); // Add a field for the cache expiration time. add_settings_field( 'wpvulnerability_cache', __( 'Cache expiration time', 'wpvulnerability' ), 'wpvulnerability_admin_cache_callback', 'wpvulnerability-config', 'admin_wpvulnerability_settings' ); // Add a field for the notification period. add_settings_field( 'wpvulnerability_period', __( 'How often you want to receive notifications', 'wpvulnerability' ), 'wpvulnerability_admin_period_callback', 'wpvulnerability-config', 'admin_wpvulnerability_settings' ); // Add a field for notification methods. add_settings_field( 'wpvulnerability_notify', __( 'Where do you want to receive notifications?', 'wpvulnerability' ), 'wpvulnerability_admin_notify_callback', 'wpvulnerability-config', 'admin_wpvulnerability_settings' ); // Add a field for the email addresses. add_settings_field( 'wpvulnerability_emails', __( 'Email addresses to notify (separated by commas)', 'wpvulnerability' ), 'wpvulnerability_admin_emails_callback', 'wpvulnerability-config', 'admin_wpvulnerability_settings' ); // Add a field for the Slack webhook. add_settings_field( 'wpvulnerability_slack_webhook', __( 'Slack webhook URL', 'wpvulnerability' ), 'wpvulnerability_admin_slack_callback', 'wpvulnerability-config', 'admin_wpvulnerability_settings' ); // Add a field for the Teams webhook. add_settings_field( 'wpvulnerability_teams_webhook', __( 'Teams webhook URL', 'wpvulnerability' ), 'wpvulnerability_admin_teams_callback', 'wpvulnerability-config', 'admin_wpvulnerability_settings' ); // Register the settings for analysis. register_setting( 'admin_wpvulnerability_analyze', 'wpvulnerability-analyze', array( 'sanitize_callback' => 'wpvulnerability_analyze_sanitize', 'default' => array(), ) ); // Add a section for analyzing settings. add_settings_section( 'admin_wpvulnerability_analyze', __( 'Vulnerabilities to hide', 'wpvulnerability' ), 'wpvulnerability_admin_section_analyze', 'wpvulnerability-analyze' ); // Add a field for analysis options. add_settings_field( 'wpvulnerability_analyze', __( 'What do you want to hide?', 'wpvulnerability' ), 'wpvulnerability_admin_analyze_callback', 'wpvulnerability-analyze', 'admin_wpvulnerability_analyze' ); } add_action( 'admin_init', 'wpvulnerability_admin_init' ); /** * Outputs the Security tab contents. * * Displays shell_exec security status, detection methods, and audit logs. * * @since 4.3.0 * * @return void */ function wpvulnerability_render_admin_tab_security() { ?>
__( 'Standard (Hybrid Detection)', 'wpvulnerability' ), 'strict' => __( 'Strict (Extensions Only)', 'wpvulnerability' ), 'disabled' => __( 'Disabled (No Detection)', 'wpvulnerability' ), ); $mode_label = isset( $mode_labels[ $security_mode ] ) ? $mode_labels[ $security_mode ] : $security_mode; ?>

0 ) : ?>
post_date ) ), time() ) ) ); ?>


security configuration documentation for details.', 'wpvulnerability' ) ), esc_url( 'https://www.wpvulnerability.com/plugin/#security-configuration' ) ); ?>

__( 'ImageMagick', 'wpvulnerability' ), 'redis' => __( 'Redis', 'wpvulnerability' ), 'memcached' => __( 'Memcached', 'wpvulnerability' ), 'sqlite' => __( 'SQLite', 'wpvulnerability' ), ); $detections = array(); foreach ( array_keys( $components ) as $component ) { $detection = null; // Do not run detection for components hidden via the analysis settings or // a WPVULNERABILITY_HIDE_* constant: they must not trigger shell_exec. if ( ! wpvulnerability_analyze_filter( $component ) ) { continue; } switch ( $component ) { case 'imagemagick': $detection = wpvulnerability_detect_imagemagick(); break; case 'redis': $detection = wpvulnerability_detect_redis(); break; case 'memcached': $detection = wpvulnerability_detect_memcached(); break; case 'sqlite': $detection = wpvulnerability_detect_sqlite(); break; } $detections[ $component ] = $detection; } $method_labels = array( 'imagick_extension' => __( 'Imagick Extension', 'wpvulnerability' ), 'redis_extension' => __( 'Redis Extension', 'wpvulnerability' ), 'memcached_extension' => __( 'Memcached Extension', 'wpvulnerability' ), 'memcache_extension' => __( 'Memcache Extension', 'wpvulnerability' ), 'sqlite3_extension' => __( 'SQLite3 Extension', 'wpvulnerability' ), 'pdo_sqlite' => __( 'PDO SQLite', 'wpvulnerability' ), 'shell_exec' => __( 'Shell Command', 'wpvulnerability' ), 'binary_exists' => __( 'Binary Check', 'wpvulnerability' ), 'none' => __( 'Not Detected', 'wpvulnerability' ), ); ?>

$detection ) : ?>
' . esc_html__( 'Not detected', 'wpvulnerability' ) . ''; } ?> 0 ) { $reliability_class = 'low'; if ( $reliability >= 80 ) { $reliability_class = ''; } elseif ( $reliability >= 50 ) { $reliability_class = 'medium'; } ?> % —

šŸ”’

post_content, true ); if ( ! is_array( $log_data ) ) { $log_data = array(); } if ( empty( $log_data ) ) { continue; } ?>
post_date ) ), time() ) ) ); ?>
50 ) : ?> ...
—

__( 'Invalid nonce.', 'wpvulnerability' ) ) ); } // Check permissions. if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'wpvulnerability' ) ) ); } // Get component. $component = isset( $_POST['component'] ) ? sanitize_key( is_string( $_POST['component'] ) ? $_POST['component'] : '' ) : ''; if ( empty( $component ) ) { wp_send_json_error( array( 'message' => __( 'No component specified.', 'wpvulnerability' ) ) ); } // Load debug functions. if ( ! function_exists( 'wpvulnerability_debug_test_api_component' ) ) { require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-debug.php'; } // Test API. $wpvulnerability_result = wpvulnerability_debug_test_api_component( $component ); wp_send_json_success( $wpvulnerability_result ); } add_action( 'wp_ajax_wpvulnerability_test_api', 'wpvulnerability_ajax_test_api' ); /** * Outputs the Debug tab contents. * * @since 4.3.0 * * @return void */ function wpvulnerability_render_admin_tab_debug() { // Security check: only show in debug mode. if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) { return; } // Load debug functions if not already loaded. if ( ! function_exists( 'wpvulnerability_debug_get_system_info' ) ) { require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-debug.php'; } ?>

$loaded ) : ?> ā—