settings = ITSEC_Modules::get_settings( 'version-management' );
if ( ! ITSEC_Lib::is_wp_version_at_least( '5.6' ) && $this->settings['wordpress_automatic_updates'] ) {
add_filter( 'auto_update_core', '__return_true', 20 );
add_filter( 'allow_dev_auto_core_updates', '__return_true', 20 );
add_filter( 'allow_minor_auto_core_updates', '__return_true', 20 );
add_filter( 'allow_major_auto_core_updates', '__return_true', 20 );
}
add_filter( 'auto_update_plugin', array( $this, 'auto_update_plugin' ), 20, 2 );
add_filter( 'auto_update_theme', array( $this, 'auto_update_theme' ), 20, 2 );
if ( 'none' !== $this->settings['plugin_automatic_updates'] ) {
add_filter( 'plugins_auto_update_enabled', '__return_false' );
}
if ( 'none' !== $this->settings['theme_automatic_updates'] ) {
add_filter( 'themes_auto_update_enabled', '__return_false' );
}
add_action( 'set_site_transient_update_plugins', array( $this, 'watch_plugin_updates' ), 100 );
add_action( 'set_site_transient_update_themes', array( $this, 'watch_theme_updates' ), 100 );
if ( $this->settings['scan_for_old_wordpress_sites'] ) {
add_action( 'itsec_scheduled_old-site-scan', array( $this, 'scan_for_old_sites' ) );
}
add_action( 'itsec_scheduled_outdated-software', array( $this, 'check_for_outdated_software' ) );
add_action( 'upgrader_process_complete', array( $this, 'check_for_outdated_software' ), 100 );
add_action( 'upgrader_process_complete', array( $this, 'log_updates' ), - 100, 2 );
add_action( '_core_updated_successfully', array( $this, 'log_core_update' ) );
add_action( 'automatic_updates_complete', array( $this, 'log_auto_update' ) );
if ( version_compare( $GLOBALS['wp_version'], '5.5', '>=' ) ) {
add_filter( 'auto_plugin_update_send_email', array( $this, 'maybe_enable_automatic_updates_debug_email' ), 100 );
add_filter( 'auto_theme_update_send_email', array( $this, 'maybe_enable_automatic_updates_debug_email' ), 100 );
add_filter( 'auto_plugin_theme_update_email', array( $this, 'filter_automatic_updates_debug_email' ), 100 );
} else {
add_filter( 'automatic_updates_send_debug_email', array( $this, 'maybe_enable_automatic_updates_debug_email' ), 100 );
add_filter( 'automatic_updates_debug_email', array( $this, 'filter_automatic_updates_debug_email' ), 100 );
}
add_action( 'itsec_scheduler_register_events', array( __CLASS__, 'register_events' ) );
add_filter( 'itsec_notifications', array( $this, 'register_notifications' ) );
add_filter( 'itsec_old-site-scan_notification_strings', array( $this, 'old_site_scan_strings' ) );
add_filter( 'itsec_automatic-updates-debug_notification_strings', array( $this, 'automatic_updates_strings' ) );
if ( 'plugins.php' === $pagenow ) {
$packages = ITSEC_Modules::get_setting( 'version-management', 'packages' );
foreach ( $packages as $package => $config ) {
list( $kind, $file ) = explode( ':', $package );
if ( 'plugin' !== $kind || 'delay' !== $config['type'] ) {
continue;
}
add_action( "in_plugin_update_message-{$file}", array( $this, 'plugin_update_row' ), 10, 2 );
}
}
}
public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new self;
}
return self::$instance;
}
public static function activate() {
$self = self::get_instance();
$self->check_for_outdated_software();
}
public static function deactivate() {
}
/**
* Register the events.
*
* @param ITSEC_Scheduler|null $scheduler
*/
public static function register_events( $scheduler = null ) {
$scheduler = $scheduler ?: ITSEC_Core::get_scheduler();
$scheduler->register_events_for_config( ITSEC_Modules::get_config( 'version-management' ) );
}
/**
* When the site is out of date, prevent the pingback URL from being displayed.
*
* @param string $output
* @param string $show
*
* @return string
*/
public function remove_pingback_url( $output, $show ) {
if ( $show === 'pingback_url' ) {
return '';
}
return $output;
}
/**
* Prevent a user from attempting multiple authentications in one XML RPC request.
*
* @param WP_User|WP_Error|null $filter_val
* @param string $username
* @param string $password
*
* @return mixed
* @see ITSEC_WordPress_Tweaks::block_multiauth_attempts()
*
*/
public function block_multiauth_attempts( $filter_val, $username, $password ) {
if ( empty( $this->first_xmlrpc_credentials ) ) {
$this->first_xmlrpc_credentials = array(
$username,
$password
);
return $filter_val;
}
if ( $username === $this->first_xmlrpc_credentials[0] && $password === $this->first_xmlrpc_credentials[1] ) {
return $filter_val;
}
status_header( 405 );
header( 'Content-Type: text/plain' );
die( __( 'XML-RPC services are disabled on this site.' ) );
}
/**
* Whenever the update plugins transient changes, store the request time for the newly available versions if we haven't
* seen then before.
*
* @param object $transient
*/
public function watch_plugin_updates( $transient ) {
if ( ! is_object( $transient ) || ! isset( $transient->response ) || ! is_array( $transient->response ) || empty( $transient->response ) ) {
return;
}
$first_seen = ITSEC_Modules::get_setting( 'version-management', 'first_seen' );
foreach ( $transient->response as $file => $package ) {
if ( 0 !== validate_file( $file ) ) {
continue;
}
if ( ! ITSEC_Lib::str_ends_with( $file, '.php' ) ) {
continue;
}
if ( ! isset( $first_seen['plugin'][ $file ][ $package->new_version ] ) ) {
$first_seen['plugin'][ $file ] = array( $package->new_version => $transient->last_checked );
}
}
ITSEC_Modules::set_setting( 'version-management', 'first_seen', $first_seen );
}
/**
* Whenever the update themes transient changes, store the request time for the newly available versions if we haven't
* seen then before.
*
* @param object $transient
*/
public function watch_theme_updates( $transient ) {
if ( ! is_object( $transient ) || ! isset( $transient->response ) || ! is_array( $transient->response ) || empty( $transient->response ) ) {
return;
}
$first_seen = ITSEC_Modules::get_setting( 'version-management', 'first_seen' );
foreach ( $transient->response as $file => $package ) {
if ( ! isset( $first_seen['theme'][ $file ][ $package['new_version'] ] ) ) {
$first_seen['theme'][ $file ] = array( $package['new_version'] => $transient->last_checked );
}
}
ITSEC_Modules::set_setting( 'version-management', 'first_seen', $first_seen );
}
/**
* Check if we should auto-update this plugin.
*
* @param bool $update
* @param object $item
*
* @return bool
*/
public function auto_update_plugin( $update, $item ) {
if ( $update ) {
return true;
}
if ( empty( $item->plugin ) ) {
return $update;
}
require_once( dirname( __FILE__ ) . '/utility.php' );
$should = ITSEC_VM_Utility::should_auto_update_plugin( $item->plugin, isset( $item->new_version ) ? $item->new_version : '' );
if ( null === $should ) {
return $update;
}
return $should;
}
/**
* Check if we should auto-update this theme.
*
* @param bool $update
* @param object $item
*
* @return bool
*/
public function auto_update_theme( $update, $item ) {
if ( $update ) {
return true;
}
if ( empty( $item->theme ) ) {
return $update;
}
require_once( dirname( __FILE__ ) . '/utility.php' );
$should = ITSEC_VM_Utility::should_auto_update_theme( $item->theme, isset( $item->new_version ) ? $item->new_version : '' );
if ( null === $should ) {
return $update;
}
return $should;
}
/**
* Display a notice when the plugin will be auto-updated by ITSEC on the Admin Plugins screen.
*
* @param array $plugin_data
* @param object $response
*/
public function plugin_update_row( $plugin_data, $response ) {
if ( ! isset( $response->plugin ) ) {
return;
}
if ( 'custom' !== ITSEC_Modules::get_setting( 'version-management', 'plugin_automatic_updates' ) ) {
return;
}
$first_seen = ITSEC_Modules::get_setting( 'version-management', 'first_seen' );
$packages = ITSEC_Modules::get_setting( 'version-management', 'packages' );
if ( isset( $first_seen['plugin'][ $response->plugin ][ $response->new_version ] ) ) {
$time = $first_seen['plugin'][ $response->plugin ][ $response->new_version ];
$days = $packages["plugin:{$response->plugin}"]['delay'];
$url = esc_url( network_admin_url( 'admin.php?page=itsec&module=version-management' ) );
if ( $time + $days * DAY_IN_SECONDS < ITSEC_Core::get_current_time_gmt() ) {
printf( ' ' . esc_html__( 'This plugin will automatically update %1$sshortly%2$s.', 'it-l10n-ithemes-security-pro' ), "", '' );
} else {
$diff = human_time_diff( $time + $days * DAY_IN_SECONDS );
$link = "{$diff}";
printf( ' ' . esc_html__( 'This plugin will automatically update in %s.', 'it-l10n-ithemes-security-pro' ), $link );
}
}
}
/**
* Run the scanner to detect if outdated software is running.
*
* The scanner will not be run if the software is already marked as outdated.
*/
public function check_for_outdated_software() {
require_once( dirname( __FILE__ ) . '/outdated-software-scanner.php' );
ITSEC_VM_Outdated_Software_Scanner::run_scan();
$this->update_outdated_software_flag();
}
/**
* Mark the site as running outdated software in this module's settings.
*/
public function update_outdated_software_flag() {
require_once( dirname( __FILE__ ) . '/strengthen-site.php' );
$is_software_outdated = ITSEC_Version_Management_Strengthen_Site::is_software_outdated();
if ( $is_software_outdated !== $this->settings['is_software_outdated'] ) {
$this->settings['is_software_outdated'] = $is_software_outdated;
ITSEC_Modules::set_setting( 'version-management', 'is_software_outdated', $is_software_outdated );
}
}
/**
* Scan for outdated sites in the same web root.
*
* This will not be run if old WordPress sites have already been detected.
*/
public function scan_for_old_sites() {
require_once( dirname( __FILE__ ) . '/old-site-scanner.php' );
ITSEC_VM_Old_Site_Scanner::run_scan();
}
/**
* Log updates.
*
* @param WP_Upgrader|Plugin_Upgrader|Theme_Upgrader $upgrader
* @param array $context
*/
public function log_updates( $upgrader, $context ) {
if ( empty( $context['type'] ) ) {
return;
}
$auto = doing_action( 'wp_maybe_auto_update' ) ? 'auto' : 'manual';
switch ( $context['type'] ) {
case 'plugin':
$update = get_site_transient( 'update_plugins' );
if ( ! empty( $context['bulk'] ) ) {
$files = $context['plugins'];
$install = false;
} elseif ( 'install' === $context['action'] ) {
$plugin = $upgrader->plugin_info();
$files = $plugin ? array( $plugin ) : array();
$install = true;
} else {
$files = array( $context['plugin'] );
$install = false;
}
foreach ( $files as $file ) {
if ( isset( $update->response[ $file ]->new_version ) ) {
$version = $update->response[ $file ]->new_version;
} else {
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
}
if ( function_exists( 'get_plugin_data' ) && file_exists( WP_PLUGIN_DIR . '/' . $file ) ) {
$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $file );
$version = $plugin_data['Version'];
} else {
$version = '-';
}
}
$code = $install ? 'install' : 'update';
ITSEC_Log::add_notice( 'version_management', "{$code}::plugin,{$file},{$version},{$auto}", compact( 'context' ) );
if ( ! $install ) {
ITSEC_Dashboard_Util::record_event( 'vm-update-plugin' );
}
}
break;
case 'theme':
$update = get_site_transient( 'update_themes' );
if ( ! empty( $context['bulk'] ) ) {
$files = $context['themes'];
$install = false;
} elseif ( 'install' === $context['action'] ) {
$theme = $upgrader->theme_info();
$files = $theme ? array( $theme->get_stylesheet() ) : array();
$install = true;
} else {
$files = array( $context['theme'] );
$install = false;
}
foreach ( $files as $file ) {
if ( isset( $update->response[ $file ]['new_version'] ) ) {
$version = $update->response[ $file ]['new_version'];
} elseif ( ( $theme = wp_get_theme( $file ) ) && $theme->exists() ) {
$theme->cache_delete();
$version = $theme->get( 'Version' );
} else {
$version = '-';
}
$code = $install ? 'install' : 'update';
ITSEC_Log::add_notice( 'version_management', "{$code}::theme,{$file},{$version},{$auto}", compact( 'context' ) );
if ( ! $install ) {
ITSEC_Dashboard_Util::record_event( 'vm-update-theme' );
}
}
break;
}
}
/**
* Log WordPress core updating.
*
* @param string $new_version
*/
public function log_core_update( $new_version ) {
$auto = doing_action( 'wp_maybe_auto_update' ) ? 'auto' : 'manual';
$old_version = $GLOBALS['wp_version'];
ITSEC_Log::add_notice( 'version_management', "update-core::{$new_version},{$old_version},{$auto}" );
ITSEC_Dashboard_Util::record_event( 'vm-update-core' );
}
/**
* Log any errors encountered when auto-updating.
*
* @param array $results
*/
public function log_auto_update( $results ) {
$has_core = $has_error = false;
foreach ( array( 'core', 'plugin', 'theme', 'translation' ) as $type ) {
if ( empty( $results[ $type ] ) ) {
continue;
}
foreach ( $results[ $type ] as $update ) {
if ( ! $update->result || is_wp_error( $update->result ) ) {
$has_error = true;
$has_core = $has_core || 'core' === $type;
}
}
}
if ( $has_core ) {
$method = 'add_error';
} elseif ( $has_error ) {
$method = 'add_warning';
} else {
$method = 'add_debug';
}
ITSEC_Log::$method( 'version_management', 'auto-update', $results );
}
/**
* Enable the automatic update email if it is enabled in the Notification Center.
*
* @param bool $enabled
*
* @return bool
*/
public function maybe_enable_automatic_updates_debug_email( $enabled ) {
// The debug email is turned off by default. We don't want to disable it if it has been enabled by another system.
if ( $enabled && 'automatic_updates_send_debug_email' === current_filter() ) {
return $enabled;
}
return ITSEC_Core::get_notification_center()->is_notification_enabled( 'automatic-updates-debug' );
}
/**
* Set automatic update email addresses.
*
* @param array $email
*
* @return array
*/
public function filter_automatic_updates_debug_email( $email ) {
if ( ITSEC_Core::get_notification_center()->is_notification_enabled( 'automatic-updates-debug' ) ) {
$email['to'] = ITSEC_Core::get_notification_center()->get_recipients( 'automatic-updates-debug' );
}
return $email;
}
public function register_notifications( $notifications ) {
// Ask for the settings again in case of saving and adding new notifications so the cache clear happens.
$settings = ITSEC_Modules::get_settings( 'version-management' );
if ( $settings['wordpress_automatic_updates'] || $settings['plugin_automatic_updates'] || $settings['theme_automatic_updates'] ) {
$notifications['automatic-updates-debug'] = array(
'recipient' => ITSEC_Notification_Center::R_USER_LIST,
'optional' => true,
'module' => 'version-management',
);
}
if ( $settings['scan_for_old_wordpress_sites'] ) {
$notifications['old-site-scan'] = array(
'slug' => 'old-site-scan',
'recipient' => ITSEC_Notification_Center::R_USER_LIST,
'schedule' => ITSEC_Notification_Center::S_CONFIGURABLE,
'subject_editable' => true,
'module' => 'version-management',
'template' => array(
array(
'header',
esc_html__( 'Outdated Site Scan', 'it-l10n-ithemes-security-pro' ),
/* translators: %s is a date range ( 1/1/16 - 2/1/16 ) */
sprintf( esc_html__( 'Outdated sites detected on %s', 'it-l10n-ithemes-security-pro' ), '{{ $_period }}' )
),
array(
'table',
array(
esc_html__( 'File Path', 'it-l10n-ithemes-security-pro' ),
esc_html__( 'WordPress Version', 'it-l10n-ithemes-security-pro' )
),
array(
':data.path',
':data.version',
),
),
array(
'footer'
),
),
);
}
return $notifications;
}
public function automatic_updates_strings() {
return array(
'label' => __( 'Automatic Updates Info', 'it-l10n-ithemes-security-pro' ),
'description' => sprintf(
__( 'The %sVersion Management%s module will send an email with details about any automatic updates that have been performed.', 'it-l10n-ithemes-security-pro' ),
ITSEC_Core::get_link_for_settings_route( ITSEC_Core::get_settings_module_route( 'version-management' ) ),
''
)
);
}
public function old_site_scan_strings() {
return array(
'label' => __( 'Old Site Scan', 'it-l10n-ithemes-security-pro' ),
'description' => sprintf(
__( 'The %1$sVersion Management%2$s module will send an email if it detects outdated WordPress sites on your hosting account. A single outdated WordPress site with a vulnerability could allow attackers to compromise all the other sites on the same hosting account.', 'it-l10n-ithemes-security-pro' ),
ITSEC_Core::get_link_for_settings_route( ITSEC_Core::get_settings_module_route( 'version-management' ) ),
''
),
'subject' => __( 'Old sites found on hosting account', 'it-l10n-ithemes-security-pro' )
);
}
}
ITSEC_Version_Management::get_instance();