878 lines
30 KiB
PHP
878 lines
30 KiB
PHP
<?php
|
|
/**
|
|
* Running functions
|
|
*
|
|
* @package WPVulnerability
|
|
*
|
|
* @version 2.0.0
|
|
*/
|
|
|
|
defined( 'ABSPATH' ) || die( 'No script kiddies please!' );
|
|
|
|
/**
|
|
* Adds a settings link to the plugin row in the plugins list.
|
|
*
|
|
* This function conditionally adds either a network admin settings link for multisite
|
|
* or a standard settings link for single site installations.
|
|
*
|
|
* @since 3.5.0
|
|
*
|
|
* @param array<int|string, mixed> $links The links that appear in the plugin row.
|
|
*
|
|
* @return array<int|string, mixed> The modified array of links.
|
|
*/
|
|
function wpvulnerability_add_settings_link( $links ) {
|
|
// Check if the user has the required capabilities to view the settings link.
|
|
if ( wpvulnerability_capabilities() ) {
|
|
// Determine the correct settings link based on the environment.
|
|
if ( is_multisite() && is_network_admin() ) {
|
|
// Network admin settings link for multisite.
|
|
$links[] = '<a href="' . esc_url( network_admin_url( 'settings.php?page=wpvulnerability-options' ) ) . '">' . esc_html__( 'Network Settings', 'wpvulnerability' ) . '</a>';
|
|
} elseif ( ! is_multisite() && is_admin() ) {
|
|
// Standard settings link for single site.
|
|
$links[] = '<a href="' . esc_url( get_admin_url( null, 'options-general.php?page=wpvulnerability-options' ) ) . '">' . esc_html__( 'Settings', 'wpvulnerability' ) . '</a>';
|
|
}
|
|
}
|
|
return $links;
|
|
}
|
|
|
|
// Hook the function to the appropriate filters.
|
|
if ( is_multisite() ) {
|
|
add_filter( 'network_admin_plugin_action_links_' . WPVULNERABILITY_PLUGIN_BASE, 'wpvulnerability_add_settings_link' );
|
|
} else {
|
|
add_filter( 'plugin_action_links_' . WPVULNERABILITY_PLUGIN_BASE, 'wpvulnerability_add_settings_link' );
|
|
}
|
|
|
|
/**
|
|
* Updates the plugin's vulnerability data.
|
|
*
|
|
* This function updates the vulnerability data for WordPress core, plugins, themes, PHP, Apache, nginx, MariaDB, and MySQL.
|
|
* It ensures that the required functions are available by including the necessary files.
|
|
* After updating the vulnerabilities, it flushes the WordPress cache.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_update_database_data() {
|
|
|
|
// Ensure necessary files are included for core, plugins, and themes.
|
|
require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-core.php';
|
|
require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-plugins.php';
|
|
require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-themes.php';
|
|
require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-software.php';
|
|
|
|
wpvulnerability_delete_transients();
|
|
|
|
// Update core, plugins, and themes vulnerabilities.
|
|
wpvulnerability_core_get_vulnerabilities_clean();
|
|
wpvulnerability_plugin_get_vulnerabilities_clean();
|
|
wpvulnerability_theme_get_vulnerabilities_clean();
|
|
|
|
// Array of software types to update.
|
|
$software_types = array( 'php', 'apache', 'nginx', 'mariadb', 'mysql', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' );
|
|
|
|
// Update vulnerabilities for each software type.
|
|
foreach ( $software_types as $software ) {
|
|
wpvulnerability_get_vulnerabilities_clean( $software );
|
|
}
|
|
|
|
wpvulnerability_statistics_get();
|
|
}
|
|
|
|
/**
|
|
* Updates the plugin's vulnerability data if the cache has expired.
|
|
*
|
|
* This function checks if the cached vulnerability data for various components (core, plugins, themes, PHP, Apache, nginx, MariaDB, MySQL) has expired and updates it accordingly.
|
|
* It ensures that the required functions are available by including the necessary files.
|
|
* The function handles both multisite and single site installations.
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_expired_database_data() {
|
|
|
|
// Ensure necessary files are included for core, plugins, and themes.
|
|
require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-core.php';
|
|
require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-plugins.php';
|
|
require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-themes.php';
|
|
require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-software.php';
|
|
|
|
// Current time for cache expiration comparison.
|
|
$cache_time = time();
|
|
|
|
// Check and update core, plugins, and themes vulnerabilities if cache has expired.
|
|
$components = array(
|
|
'core' => 'wpvulnerability-core-cache',
|
|
'plugin' => 'wpvulnerability-plugins-cache',
|
|
'theme' => 'wpvulnerability-themes-cache',
|
|
);
|
|
|
|
foreach ( $components as $component => $cache_key ) {
|
|
$cache_value = is_multisite() ? get_site_option( $cache_key ) : get_option( $cache_key );
|
|
|
|
if ( json_decode( is_string( $cache_value ) ? $cache_value : '' ) < $cache_time ) {
|
|
call_user_func( "wpvulnerability_{$component}_get_vulnerabilities_clean" );
|
|
}
|
|
}
|
|
|
|
// Array of software types to update.
|
|
$software_types = array( 'php', 'apache', 'nginx', 'mariadb', 'mysql', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' );
|
|
|
|
// Ensure necessary files are included and update vulnerabilities for each software type.
|
|
foreach ( $software_types as $software ) {
|
|
if ( is_multisite() ) {
|
|
$site_opt = get_site_option( 'wpvulnerability-' . $software . '-cache' );
|
|
if ( json_decode( is_string( $site_opt ) ? $site_opt : '' ) < $cache_time ) {
|
|
wpvulnerability_get_vulnerabilities_clean( $software );
|
|
}
|
|
} else {
|
|
$opt = get_option( 'wpvulnerability-' . $software . '-cache' );
|
|
if ( json_decode( is_string( $opt ) ? $opt : '' ) < $cache_time ) {
|
|
wpvulnerability_get_vulnerabilities_clean( $software );
|
|
}
|
|
}
|
|
}
|
|
|
|
$statistics_cache_raw = is_multisite() ? get_site_option( 'wpvulnerability-statistics-cache' ) : get_option( 'wpvulnerability-statistics-cache' );
|
|
|
|
if ( is_numeric( $statistics_cache_raw ) ) {
|
|
$statistics_cache = (int) $statistics_cache_raw;
|
|
} elseif ( is_string( $statistics_cache_raw ) ) {
|
|
$decoded_cache = json_decode( $statistics_cache_raw );
|
|
$statistics_cache = is_numeric( $decoded_cache ) ? (int) $decoded_cache : 0;
|
|
} else {
|
|
$statistics_cache = 0;
|
|
}
|
|
|
|
if ( $statistics_cache < $cache_time ) {
|
|
wpvulnerability_statistics_get();
|
|
}
|
|
|
|
unset( $cache_time, $statistics_cache, $statistics_cache_raw );
|
|
}
|
|
|
|
/**
|
|
* Initializes persistent plugin data, ensures required options exist, and tracks
|
|
* initialization metadata so upgrades provision new defaults when needed.
|
|
*
|
|
* @since 4.1.2
|
|
*
|
|
* @param bool $is_real_activation Optional. Whether the routine runs during the activation hook. Default false.
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_initialize_plugin_data( $is_real_activation = false ) {
|
|
$is_multisite = is_multisite();
|
|
$config_key = $is_multisite ? 'get_site_option' : 'get_option';
|
|
$add_option = $is_multisite ? 'add_site_option' : 'add_option';
|
|
$update_option = $is_multisite ? 'update_site_option' : 'update_option';
|
|
|
|
$initialized_raw = $config_key( 'wpvulnerability_initialized' );
|
|
$initialized = array(
|
|
'timestamp' => 0,
|
|
'version' => '',
|
|
);
|
|
|
|
if ( is_array( $initialized_raw ) ) {
|
|
if ( isset( $initialized_raw['timestamp'] ) && is_numeric( $initialized_raw['timestamp'] ) ) {
|
|
$initialized['timestamp'] = (int) $initialized_raw['timestamp'];
|
|
}
|
|
|
|
if ( isset( $initialized_raw['version'] ) && is_string( $initialized_raw['version'] ) ) {
|
|
$initialized['version'] = $initialized_raw['version'];
|
|
}
|
|
} elseif ( is_numeric( $initialized_raw ) ) {
|
|
$initialized['timestamp'] = (int) $initialized_raw;
|
|
}
|
|
|
|
$needs_upgrade = version_compare( (string) $initialized['version'], WPVULNERABILITY_PLUGIN_VERSION, '<' );
|
|
|
|
if ( ! $is_real_activation && ! empty( $initialized_raw ) && ! $needs_upgrade ) {
|
|
return;
|
|
}
|
|
|
|
if ( $is_real_activation ) {
|
|
wpvulnerability_delete_transients();
|
|
}
|
|
|
|
// Add wpvulnerability-config option if it does not exist.
|
|
if ( ! $config_key( 'wpvulnerability-config' ) ) {
|
|
$default_config = array(
|
|
'emails' => get_bloginfo( 'admin_email' ),
|
|
'period' => 'weekly',
|
|
'day' => 'monday',
|
|
'hour' => 0,
|
|
'minute' => 0,
|
|
'cache' => 12,
|
|
'log_retention' => 0,
|
|
'delete_on_uninstall' => 0,
|
|
'notify' => array(
|
|
'email' => 'y',
|
|
'slack' => 'n',
|
|
'teams' => 'n',
|
|
),
|
|
'slack_webhook' => '',
|
|
'teams_webhook' => '',
|
|
);
|
|
$add_option( 'wpvulnerability-config', $default_config );
|
|
}
|
|
|
|
// Add other options if they do not exist.
|
|
$options = array(
|
|
'wpvulnerability-plugins' => '',
|
|
'wpvulnerability-plugins-cache' => 0,
|
|
'wpvulnerability-plugins-vulnerable' => 0,
|
|
'wpvulnerability-plugins-data' => '',
|
|
'wpvulnerability-plugins-data-cache' => 0,
|
|
'wpvulnerability-themes' => '',
|
|
'wpvulnerability-themes-cache' => 0,
|
|
'wpvulnerability-themes-vulnerable' => 0,
|
|
'wpvulnerability-core' => '',
|
|
'wpvulnerability-core-cache' => 0,
|
|
'wpvulnerability-core-vulnerable' => 0,
|
|
'wpvulnerability-php' => '',
|
|
'wpvulnerability-php-cache' => 0,
|
|
'wpvulnerability-php-vulnerable' => 0,
|
|
'wpvulnerability-apache' => '',
|
|
'wpvulnerability-apache-cache' => 0,
|
|
'wpvulnerability-apache-vulnerable' => 0,
|
|
'wpvulnerability-nginx' => '',
|
|
'wpvulnerability-nginx-cache' => 0,
|
|
'wpvulnerability-nginx-vulnerable' => 0,
|
|
'wpvulnerability-mariadb' => '',
|
|
'wpvulnerability-mariadb-cache' => 0,
|
|
'wpvulnerability-mariadb-vulnerable' => 0,
|
|
'wpvulnerability-mysql' => '',
|
|
'wpvulnerability-mysql-cache' => 0,
|
|
'wpvulnerability-mysql-vulnerable' => 0,
|
|
'wpvulnerability-imagemagick' => '',
|
|
'wpvulnerability-imagemagick-cache' => 0,
|
|
'wpvulnerability-imagemagick-vulnerable' => 0,
|
|
'wpvulnerability-curl' => '',
|
|
'wpvulnerability-curl-cache' => 0,
|
|
'wpvulnerability-curl-vulnerable' => 0,
|
|
'wpvulnerability-memcached' => '',
|
|
'wpvulnerability-memcached-cache' => 0,
|
|
'wpvulnerability-memcached-vulnerable' => 0,
|
|
'wpvulnerability-redis' => '',
|
|
'wpvulnerability-redis-cache' => 0,
|
|
'wpvulnerability-redis-vulnerable' => 0,
|
|
'wpvulnerability-sqlite' => '',
|
|
'wpvulnerability-sqlite-cache' => 0,
|
|
'wpvulnerability-sqlite-vulnerable' => 0,
|
|
'wpvulnerability-statistics' => '',
|
|
'wpvulnerability-statistics-cache' => 0,
|
|
'wpvulnerability_initialized' => 0,
|
|
);
|
|
|
|
// Large data blobs should not autoload on every WP request (single-site only;
|
|
// add_site_option does not support autoload control).
|
|
$no_autoload_keys = array(
|
|
'wpvulnerability-plugins',
|
|
'wpvulnerability-plugins-cache',
|
|
'wpvulnerability-plugins-vulnerable',
|
|
'wpvulnerability-plugins-data',
|
|
'wpvulnerability-plugins-data-cache',
|
|
'wpvulnerability-themes',
|
|
'wpvulnerability-core',
|
|
'wpvulnerability-php',
|
|
'wpvulnerability-apache',
|
|
'wpvulnerability-nginx',
|
|
'wpvulnerability-mariadb',
|
|
'wpvulnerability-mysql',
|
|
'wpvulnerability-imagemagick',
|
|
'wpvulnerability-curl',
|
|
'wpvulnerability-memcached',
|
|
'wpvulnerability-redis',
|
|
'wpvulnerability-sqlite',
|
|
'wpvulnerability-statistics',
|
|
);
|
|
|
|
foreach ( $options as $key => $value ) {
|
|
if ( ! $config_key( $key ) ) {
|
|
if ( ! $is_multisite && in_array( $key, $no_autoload_keys, true ) ) {
|
|
add_option( $key, $value, '', false );
|
|
} else {
|
|
$add_option( $key, $value );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add wpvulnerability-analyze option if it does not exist.
|
|
if ( ! $config_key( 'wpvulnerability-analyze' ) ) {
|
|
$default_analyze = array(
|
|
'core' => 0,
|
|
'plugins' => 0,
|
|
'themes' => 0,
|
|
'php' => 0,
|
|
'apache' => 0,
|
|
'nginx' => 0,
|
|
'mariadb' => 0,
|
|
'mysql' => 0,
|
|
'imagemagick' => 0,
|
|
'curl' => 0,
|
|
'memcached' => 0,
|
|
'redis' => 0,
|
|
'sqlite' => 0,
|
|
);
|
|
|
|
foreach ( array_keys( $default_analyze ) as $component ) {
|
|
$constant = 'WPVULNERABILITY_HIDE_' . strtoupper( (string) $component );
|
|
if ( defined( $constant ) && constant( $constant ) ) {
|
|
$default_analyze[ $component ] = 1;
|
|
}
|
|
}
|
|
$current_option_raw = $config_key( 'wpvulnerability-analyze' );
|
|
$current_option = is_array( $current_option_raw ) ? $current_option_raw : false;
|
|
|
|
if ( false === $current_option ) {
|
|
$add_option( 'wpvulnerability-analyze', $default_analyze );
|
|
} else {
|
|
$updated_option = array_merge( $default_analyze, $current_option );
|
|
$update_option( 'wpvulnerability-analyze', $updated_option );
|
|
}
|
|
}
|
|
|
|
$update_option(
|
|
'wpvulnerability_initialized',
|
|
array(
|
|
'timestamp' => (int) time(),
|
|
'version' => WPVULNERABILITY_PLUGIN_VERSION,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Callback function for when the plugin is activated.
|
|
* Adds plugin data options if they are not already created.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_activation() {
|
|
wpvulnerability_initialize_plugin_data( true );
|
|
|
|
// init has already fired during the activation request, so the deferred
|
|
// scheduling in wpvulnerability-schedule.php cannot run here; schedule directly.
|
|
wpvulnerability_schedule_core_events();
|
|
}
|
|
|
|
/**
|
|
* Callback function to run when the plugin is deactivated.
|
|
* Deletes options and removes scheduled wp-cron jobs.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_deactivation() {
|
|
$options = array(
|
|
'wpvulnerability_settings',
|
|
'wpvulnerability-data',
|
|
'wpvulnerability-themes',
|
|
'wpvulnerability-themes-cache',
|
|
'wpvulnerability-themes-vulnerable',
|
|
'wpvulnerability-plugins',
|
|
'wpvulnerability-plugins-cache',
|
|
'wpvulnerability-plugins-vulnerable',
|
|
'wpvulnerability-core',
|
|
'wpvulnerability-core-cache',
|
|
'wpvulnerability-core-vulnerable',
|
|
'wpvulnerability-php',
|
|
'wpvulnerability-php-cache',
|
|
'wpvulnerability-php-vulnerable',
|
|
'wpvulnerability-apache',
|
|
'wpvulnerability-apache-cache',
|
|
'wpvulnerability-apache-vulnerable',
|
|
'wpvulnerability-nginx',
|
|
'wpvulnerability-nginx-cache',
|
|
'wpvulnerability-nginx-vulnerable',
|
|
'wpvulnerability-mariadb',
|
|
'wpvulnerability-mariadb-cache',
|
|
'wpvulnerability-mariadb-vulnerable',
|
|
'wpvulnerability-mysql',
|
|
'wpvulnerability-mysql-cache',
|
|
'wpvulnerability-mysql-vulnerable',
|
|
'wpvulnerability-imagemagick',
|
|
'wpvulnerability-imagemagick-cache',
|
|
'wpvulnerability-imagemagick-vulnerable',
|
|
'wpvulnerability-curl',
|
|
'wpvulnerability-curl-cache',
|
|
'wpvulnerability-curl-vulnerable',
|
|
'wpvulnerability-memcached',
|
|
'wpvulnerability-memcached-cache',
|
|
'wpvulnerability-memcached-vulnerable',
|
|
'wpvulnerability-redis',
|
|
'wpvulnerability-redis-cache',
|
|
'wpvulnerability-redis-vulnerable',
|
|
'wpvulnerability-sqlite',
|
|
'wpvulnerability-sqlite-cache',
|
|
'wpvulnerability-sqlite-vulnerable',
|
|
'wpvulnerability-statistics',
|
|
'wpvulnerability-statistics-cache',
|
|
'wpvulnerability_initialized',
|
|
);
|
|
|
|
// Delete options based on the installation type.
|
|
$delete_option_func = is_multisite() ? 'delete_site_option' : 'delete_option';
|
|
foreach ( $options as $option ) {
|
|
$delete_option_func( $option );
|
|
}
|
|
|
|
wpvulnerability_delete_transients();
|
|
|
|
// Unschedule and remove scheduled wp-cron jobs.
|
|
$cron_jobs = array(
|
|
'wpvulnerability_notification',
|
|
'wpvulnerability_update_database',
|
|
'wpvulnerability_pull_db_data_event',
|
|
'wpvulnerability_cleanup_logs',
|
|
);
|
|
foreach ( $cron_jobs as $job ) {
|
|
$next_ts = wp_next_scheduled( $job );
|
|
if ( false !== $next_ts ) {
|
|
wp_unschedule_event( $next_ts, $job );
|
|
}
|
|
wp_clear_scheduled_hook( $job );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes all transients that start with 'wpvulnerability_'.
|
|
*
|
|
* @since 3.5.0
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_delete_transients() {
|
|
global $wpdb;
|
|
|
|
// Determine if the site is multisite.
|
|
$is_multisite = is_multisite();
|
|
|
|
// Define the prefix according to whether it is multisite or not.
|
|
$transient_prefix = $is_multisite ? '_site_transient_wpvulnerability_' : '_transient_wpvulnerability_';
|
|
|
|
// Prepare the LIKE pattern securely.
|
|
$like_pattern = $wpdb->esc_like( $transient_prefix ) . '%';
|
|
|
|
// Try to get transients from cache first.
|
|
$cache_key = 'wpvulnerability_transients_list';
|
|
$transients = wp_cache_get( $cache_key );
|
|
|
|
if ( false === $transients ) {
|
|
// If cache is empty, query the database for matching transients.
|
|
if ( $is_multisite ) {
|
|
$transients = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
|
|
$wpdb->prepare(
|
|
"SELECT meta_key FROM {$wpdb->sitemeta} WHERE meta_key LIKE %s AND site_id = %d",
|
|
$like_pattern,
|
|
get_current_network_id()
|
|
)
|
|
); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
|
|
} else {
|
|
$transients = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
|
|
$wpdb->prepare(
|
|
"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
|
|
$like_pattern
|
|
)
|
|
); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching
|
|
}
|
|
|
|
// Store the result in cache for future use.
|
|
wp_cache_set( $cache_key, $transients, '', HOUR_IN_SECONDS );
|
|
}
|
|
|
|
// If no transients are found, exit early.
|
|
if ( empty( $transients ) ) {
|
|
return;
|
|
}
|
|
|
|
foreach ( $transients as $transient ) {
|
|
if ( $is_multisite ) {
|
|
// For multisite, delete using delete_site_transient.
|
|
$transient_name = str_replace( '_site_transient_', '', $transient );
|
|
delete_site_transient( $transient_name );
|
|
} else {
|
|
// For single sites, delete using delete_transient.
|
|
$transient_name = str_replace( '_transient_', '', $transient );
|
|
delete_transient( $transient_name );
|
|
}
|
|
}
|
|
|
|
// Optionally clear the cache after deletion.
|
|
wp_cache_delete( $cache_key );
|
|
}
|
|
|
|
/**
|
|
* Reset the plugin data to defaults and repopulate from the API.
|
|
*
|
|
* This routine mimics a full uninstall by deleting options, logs, and cron jobs,
|
|
* then provisions default settings, reschedules events, and reloads data.
|
|
*
|
|
* @since 4.3.0
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_reset_plugin_data() {
|
|
wpvulnerability_uninstall();
|
|
|
|
wpvulnerability_initialize_plugin_data( true );
|
|
|
|
if ( function_exists( 'wpvulnerability_schedule_core_events' ) ) {
|
|
wpvulnerability_schedule_core_events();
|
|
}
|
|
|
|
$config_raw = is_multisite() ? get_site_option( 'wpvulnerability-config', array() ) : get_option( 'wpvulnerability-config', array() );
|
|
$config = is_array( $config_raw ) ? $config_raw : array();
|
|
|
|
if ( function_exists( 'wpvulnerability_schedule_notification_event' ) ) {
|
|
wpvulnerability_schedule_notification_event( $config );
|
|
}
|
|
|
|
wpvulnerability_update_database_data();
|
|
}
|
|
|
|
/**
|
|
* Callback function to run when the plugin is uninstalled.
|
|
* Deletes options and removes scheduled wp-cron jobs.
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_uninstall() {
|
|
// Delete deprecated options.
|
|
delete_option( 'wpvulnerability_settings' );
|
|
delete_option( 'wpvulnerability-data' );
|
|
if ( function_exists( 'delete_site_option' ) ) {
|
|
delete_site_option( 'wpvulnerability_settings' );
|
|
delete_site_option( 'wpvulnerability-data' );
|
|
}
|
|
|
|
// Options to delete for both single site and multisite.
|
|
$options = array(
|
|
'wpvulnerability-themes',
|
|
'wpvulnerability-themes-cache',
|
|
'wpvulnerability-themes-vulnerable',
|
|
'wpvulnerability-themes-signature',
|
|
'wpvulnerability-plugins',
|
|
'wpvulnerability-plugins-cache',
|
|
'wpvulnerability-plugins-vulnerable',
|
|
'wpvulnerability-plugins-signature',
|
|
'wpvulnerability-plugins-data',
|
|
'wpvulnerability-plugins-cache-data',
|
|
'wpvulnerability-plugins-data-cache',
|
|
'wpvulnerability-core',
|
|
'wpvulnerability-core-cache',
|
|
'wpvulnerability-core-vulnerable',
|
|
'wpvulnerability-core-version',
|
|
'wpvulnerability-php',
|
|
'wpvulnerability-php-cache',
|
|
'wpvulnerability-php-vulnerable',
|
|
'wpvulnerability-apache',
|
|
'wpvulnerability-apache-cache',
|
|
'wpvulnerability-apache-vulnerable',
|
|
'wpvulnerability-nginx',
|
|
'wpvulnerability-nginx-cache',
|
|
'wpvulnerability-nginx-vulnerable',
|
|
'wpvulnerability-mariadb',
|
|
'wpvulnerability-mariadb-cache',
|
|
'wpvulnerability-mariadb-vulnerable',
|
|
'wpvulnerability-mysql',
|
|
'wpvulnerability-mysql-cache',
|
|
'wpvulnerability-mysql-vulnerable',
|
|
'wpvulnerability-imagemagick',
|
|
'wpvulnerability-imagemagick-cache',
|
|
'wpvulnerability-imagemagick-vulnerable',
|
|
'wpvulnerability-curl',
|
|
'wpvulnerability-curl-cache',
|
|
'wpvulnerability-curl-vulnerable',
|
|
'wpvulnerability-memcached',
|
|
'wpvulnerability-memcached-cache',
|
|
'wpvulnerability-memcached-vulnerable',
|
|
'wpvulnerability-redis',
|
|
'wpvulnerability-redis-cache',
|
|
'wpvulnerability-redis-vulnerable',
|
|
'wpvulnerability-sqlite',
|
|
'wpvulnerability-sqlite-cache',
|
|
'wpvulnerability-sqlite-vulnerable',
|
|
'wpvulnerability-statistics',
|
|
'wpvulnerability-statistics-cache',
|
|
'wpvulnerability_initialized',
|
|
'wpvulnerability-analyze',
|
|
);
|
|
|
|
foreach ( $options as $option ) {
|
|
delete_option( $option );
|
|
if ( function_exists( 'delete_site_option' ) ) {
|
|
delete_site_option( $option );
|
|
}
|
|
}
|
|
|
|
// Delete all stored log entries - both API request logs and shell audit
|
|
// logs - in batches to avoid timeouts. The post types may not be
|
|
// registered yet when this routine runs early (the admin reset executes
|
|
// during init, and uninstall.php does not process init): queries for
|
|
// unregistered post types silently return nothing, so registration is
|
|
// forced here first.
|
|
foreach (
|
|
array(
|
|
'wpvulnerability_log' => 'wpvulnerability_register_log_post_type',
|
|
'wpv_shell_log' => 'wpvulnerability_register_shell_log_post_type',
|
|
)
|
|
as $log_post_type => $log_register_callback
|
|
) {
|
|
if ( ! post_type_exists( $log_post_type ) && function_exists( $log_register_callback ) ) {
|
|
call_user_func( $log_register_callback );
|
|
}
|
|
|
|
$log_query_args = array(
|
|
'post_type' => $log_post_type,
|
|
'fields' => 'ids',
|
|
'post_status' => 'any',
|
|
'posts_per_page' => 100,
|
|
'orderby' => 'ID',
|
|
'order' => 'ASC',
|
|
'no_found_rows' => true,
|
|
'update_post_meta_cache' => false,
|
|
'update_post_term_cache' => false,
|
|
'suppress_filters' => false,
|
|
);
|
|
|
|
while ( true ) {
|
|
$log_ids = get_posts( $log_query_args );
|
|
|
|
if ( empty( $log_ids ) ) {
|
|
break;
|
|
}
|
|
|
|
foreach ( $log_ids as $log_id ) {
|
|
wp_delete_post( (int) $log_id, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete config data.
|
|
delete_option( 'wpvulnerability-config' );
|
|
if ( function_exists( 'delete_site_option' ) ) {
|
|
delete_site_option( 'wpvulnerability-config' );
|
|
}
|
|
|
|
wpvulnerability_delete_transients();
|
|
|
|
// Unschedule and remove scheduled wp-cron jobs.
|
|
$cron_jobs = array(
|
|
'wpvulnerability_notification',
|
|
'wpvulnerability_update_database',
|
|
'wpvulnerability_pull_db_data_event',
|
|
'wpvulnerability_cleanup_logs',
|
|
);
|
|
foreach ( $cron_jobs as $job ) {
|
|
$next_ts = wp_next_scheduled( $job );
|
|
if ( false !== $next_ts ) {
|
|
wp_unschedule_event( $next_ts, $job );
|
|
}
|
|
wp_clear_scheduled_hook( $job );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filters and returns the WPVulnerability analysis setting for a given type.
|
|
*
|
|
* This function retrieves the WPVulnerability analysis settings, either from
|
|
* the single site or the multisite network, depending on the WordPress setup.
|
|
* It returns false if the specified type ('core', 'plugins', 'themes',
|
|
* 'php', 'apache', 'nginx', 'mariadb', 'mysql') is set. If the type is not set or is invalid, it returns true.
|
|
*
|
|
* @since 3.3.0
|
|
*
|
|
* @param string $type The type of analysis setting to retrieve ('core', 'plugins', 'themes', 'php', 'apache', 'nginx', 'mariadb', 'mysql').
|
|
*
|
|
* @return bool False if the specified type is set, true if not set or invalid.
|
|
*/
|
|
function wpvulnerability_analyze_filter( $type ) {
|
|
// Retrieve the analysis settings based on the WordPress setup.
|
|
$raw_analyze = is_multisite() ? get_site_option( 'wpvulnerability-analyze', array() ) : get_option( 'wpvulnerability-analyze', array() );
|
|
$wpvulnerability_analyze = is_array( $raw_analyze ) ? $raw_analyze : array();
|
|
|
|
// Define the valid types for analysis.
|
|
$valid_types = array( 'core', 'plugins', 'themes', 'php', 'apache', 'nginx', 'mariadb', 'mysql', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' );
|
|
|
|
if ( in_array( $type, $valid_types, true ) ) {
|
|
$constant = 'WPVULNERABILITY_HIDE_' . strtoupper( (string) $type );
|
|
if ( defined( $constant ) && constant( $constant ) ) {
|
|
return false;
|
|
}
|
|
|
|
$type_val = $wpvulnerability_analyze[ $type ] ?? 0;
|
|
return ! ( is_numeric( $type_val ) && (int) $type_val );
|
|
}
|
|
|
|
return true; // Return true for invalid types.
|
|
}
|
|
|
|
/**
|
|
* Clean the cache after an update.
|
|
*
|
|
* This function is triggered after a plugin or theme update to clean the cache
|
|
* and refresh the vulnerability data.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @return void
|
|
*/
|
|
add_action( 'upgrader_process_complete', 'wpvulnerability_update_database_data', 10, 0 );
|
|
|
|
/**
|
|
* Adds a notification count to the Plugins menu item in the WordPress admin if there are vulnerable plugins.
|
|
*
|
|
* This function retrieves the number of vulnerable plugins from the cache, either from a single site
|
|
* or a multisite setup, and displays the count in the WordPress admin menu next to the Plugins menu item.
|
|
* The count is shown with a gold background (#FFD700) and black text.
|
|
*
|
|
* @since 3.3.5
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_counter_plugins() {
|
|
|
|
if ( ! wpvulnerability_analyze_filter( 'plugins' ) ) {
|
|
return; // Skip if plugin analysis is disabled.
|
|
}
|
|
|
|
// Retrieve the number of vulnerable plugins from cache.
|
|
$wpvulnerability_plugins_count = is_multisite() && is_network_admin()
|
|
? get_site_option( 'wpvulnerability-plugins-vulnerable' )
|
|
: get_option( 'wpvulnerability-plugins-vulnerable' );
|
|
|
|
// Decode the count from JSON, default to 0 if not set.
|
|
$wpvulnerability_plugins_decoded = is_string( $wpvulnerability_plugins_count ) ? json_decode( $wpvulnerability_plugins_count ) : 0;
|
|
$wpvulnerability_plugins_total = is_numeric( $wpvulnerability_plugins_decoded ) ? (int) $wpvulnerability_plugins_decoded : 0;
|
|
|
|
if ( $wpvulnerability_plugins_total > 0 ) {
|
|
global $menu;
|
|
foreach ( $menu as $key => $value ) {
|
|
if ( 'plugins.php' === $menu[ $key ][2] ) {
|
|
$menu[ $key ][0] .= ' <span class="update-plugins" style="background-color: #FFD700; color: #000000;"><span class="update-count" title="' . esc_html__( 'Vulnerabilities', 'wpvulnerability' ) . '">' . esc_html( (string) $wpvulnerability_plugins_total ) . '</span></span>'; // phpcs:ignore
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hook into the appropriate admin menu action based on the site type.
|
|
if ( is_multisite() && is_network_admin() ) {
|
|
add_action( 'network_admin_menu', 'wpvulnerability_counter_plugins' );
|
|
} elseif ( ! is_multisite() ) {
|
|
add_action( 'admin_menu', 'wpvulnerability_counter_plugins' );
|
|
}
|
|
|
|
/**
|
|
* Adds a notification count to the Themes menu item in the WordPress admin if there are vulnerable themes.
|
|
*
|
|
* This function retrieves the number of vulnerable themes from the cache, either from a single site
|
|
* or a multisite setup, and displays the count in the WordPress admin menu next to the Themes menu item.
|
|
* The count is displayed with a gold background (#FFD700) and black text.
|
|
*
|
|
* @since 3.3.5
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_counter_themes() {
|
|
|
|
if ( ! wpvulnerability_analyze_filter( 'themes' ) ) {
|
|
return; // Skip if theme analysis is disabled.
|
|
}
|
|
|
|
// Retrieve the number of theme vulnerabilities from cache.
|
|
$wpvulnerability_themes_count = ( is_multisite() && is_network_admin() )
|
|
? get_site_option( 'wpvulnerability-themes-vulnerable' )
|
|
: get_option( 'wpvulnerability-themes-vulnerable' );
|
|
|
|
// Decode the count from JSON, default to 0 if not set.
|
|
$wpvulnerability_themes_decoded = is_string( $wpvulnerability_themes_count ) ? json_decode( $wpvulnerability_themes_count ) : 0;
|
|
$wpvulnerability_themes_total = is_numeric( $wpvulnerability_themes_decoded ) ? (int) $wpvulnerability_themes_decoded : 0;
|
|
|
|
if ( $wpvulnerability_themes_total > 0 ) {
|
|
|
|
// Check if we are in a multisite setup or not.
|
|
if ( ! is_multisite() ) {
|
|
global $submenu;
|
|
|
|
// Check if the submenu for themes exists.
|
|
if ( isset( $submenu['themes.php'] ) ) {
|
|
foreach ( $submenu['themes.php'] as $key => $value ) {
|
|
if ( 'themes.php' === $submenu['themes.php'][ $key ][2] ) {
|
|
$submenu['themes.php'][ $key ][0] .= ' <span class="update-plugins" style="background-color: #FFD700; color: #000000;"><span class="update-count" title="' . esc_html__( 'Vulnerabilities', 'wpvulnerability' ) . '">' . esc_html( (string) $wpvulnerability_themes_total ) . '</span></span>'; // phpcs:ignore
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} elseif ( is_network_admin() ) {
|
|
global $menu;
|
|
|
|
foreach ( $menu as $key => $value ) {
|
|
if ( 'themes.php' === $menu[ $key ][2] ) {
|
|
$menu[ $key ][0] .= ' <span class="update-plugins" style="background-color: #FFD700; color: #000000;"><span class="update-count" title="' . esc_html__( 'Vulnerabilities', 'wpvulnerability' ) . '">' . esc_html( (string) $wpvulnerability_themes_total ) . '</span></span>'; // phpcs:ignore
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hook into the appropriate admin menu action based on the site type.
|
|
if ( is_multisite() && is_network_admin() ) {
|
|
add_action( 'network_admin_menu', 'wpvulnerability_counter_themes' );
|
|
} elseif ( ! is_multisite() ) {
|
|
add_action( 'admin_menu', 'wpvulnerability_counter_themes' );
|
|
}
|
|
|
|
/**
|
|
* Adds a notification count to the Updates submenu item under Dashboard in the WordPress admin if there are core updates.
|
|
*
|
|
* This function checks for core updates and then displays the count in the WordPress admin submenu
|
|
* next to the Updates menu item with a gold background (#FFD700) and black text.
|
|
*
|
|
* @since 3.3.5
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_counter_core() {
|
|
|
|
if ( ! wpvulnerability_analyze_filter( 'core' ) ) {
|
|
return; // Skip if core analysis is disabled.
|
|
}
|
|
|
|
// Retrieve the number of core vulnerabilities from cache.
|
|
$wpvulnerability_core_count = is_multisite() && is_network_admin()
|
|
? get_site_option( 'wpvulnerability-core-vulnerable' )
|
|
: get_option( 'wpvulnerability-core-vulnerable' );
|
|
|
|
// Decode the count from JSON, default to 0 if not set.
|
|
$wpvulnerability_core_decoded = is_string( $wpvulnerability_core_count ) ? json_decode( $wpvulnerability_core_count ) : 0;
|
|
$wpvulnerability_core_total = is_numeric( $wpvulnerability_core_decoded ) ? (int) $wpvulnerability_core_decoded : 0;
|
|
|
|
if ( $wpvulnerability_core_total > 0 ) {
|
|
global $submenu;
|
|
if ( isset( $submenu['index.php'] ) ) {
|
|
foreach ( $submenu['index.php'] as $key => $value ) {
|
|
if ( 'update-core.php' === $submenu['index.php'][ $key ][2] ) {
|
|
$submenu['index.php'][ $key ][0] .= ' <span class="update-plugins" style="background-color: #FFD700; color: #000000;"><span class="update-count" title="' . esc_html__( 'Vulnerabilities', 'wpvulnerability' ) . '">' . esc_html( (string) $wpvulnerability_core_total ) . '</span></span>'; // phpcs:ignore
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hook into the appropriate admin menu action based on the site type.
|
|
if ( is_multisite() && is_network_admin() ) {
|
|
add_action( 'network_admin_menu', 'wpvulnerability_counter_core' );
|
|
} elseif ( ! is_multisite() ) {
|
|
add_action( 'admin_menu', 'wpvulnerability_counter_core' );
|
|
}
|