create_database_tables(); $this->set_free_installation_timestamp(); $this->on_activation(); } /** * Deactivation. */ public function deactivation_hook() { wp_clear_scheduled_hook( 'firewall_clean_up_logs' ); wp_clear_scheduled_hook( 'audit_sync_events' ); wp_clear_scheduled_hook( 'audit_clean_up_logs' ); wp_clear_scheduled_hook( 'wdf_maybe_send_report' ); wp_clear_scheduled_hook( 'wp_defender_clear_logs' ); wp_clear_scheduled_hook( 'wpdef_sec_key_gen' ); wp_clear_scheduled_hook( 'wpdef_clear_scan_logs' ); wp_clear_scheduled_hook( 'wpdef_log_rotational_delete' ); wp_clear_scheduled_hook( 'wpdef_update_geoip' ); // Remove old legacy cron jobs if they exist. wp_clear_scheduled_hook( 'lockoutReportCron' ); wp_clear_scheduled_hook( 'auditReportCron' ); wp_clear_scheduled_hook( 'cleanUpOldLog' ); wp_clear_scheduled_hook( 'scanReportCron' ); wp_clear_scheduled_hook( 'tweaksSendNotification' ); } /** * Creates Defender's tables. * @since 2.7.1 No use dbDelta because PHP v8.1 triggers an error when calling query "DESCRIBE {$table};" if the table doesn't exist. */ protected function create_database_tables() { require_once ABSPATH . 'wp-admin/includes/upgrade.php'; global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // Hide errors. $wpdb->hide_errors(); // Email log table. if ( ! $this->table_exists( 'defender_email_log' ) ) { $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_email_log ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `timestamp` int NOT NULL, `source` varchar(255) NOT NULL, `to` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `source` (`source`) ) $charset_collate;"; $wpdb->query( $sql ); } // Audit log table. Though our data mainly store on API side, we will need a table for caching. if ( ! $this->table_exists( 'defender_audit_log' ) ) { $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_audit_log ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `timestamp` int NOT NULL, `event_type` varchar(255) NOT NULL, `action_type` varchar(255) NOT NULL, `site_url` varchar(255) NOT NULL, `user_id` int NOT NULL, `context` varchar(255) NOT NULL, `ip` varchar(255) NOT NULL, `msg` varchar(255) NOT NULL, `blog_id` int NOT NULL, `synced` int NOT NULL, `ttl` int NOT NULL, PRIMARY KEY (`id`), KEY `event_type` (`event_type`), KEY `action_type` (`action_type`), KEY `user_id` (`user_id`), KEY `context` (`context`), KEY `ip` (`ip`) ) $charset_collate;"; $wpdb->query( $sql ); } // Scan item table. if ( ! $this->table_exists( 'defender_scan_item' ) ) { $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_scan_item ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `parent_id` int NOT NULL, `type` varchar(255) NOT NULL, `status` varchar(255) NOT NULL, `raw_data` text NOT NULL, PRIMARY KEY (`id`), KEY `type` (`type`), KEY `status` (`status`) ) $charset_collate;"; $wpdb->query( $sql ); } // Scan table. if ( ! $this->table_exists( 'defender_scan' ) ) { $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_scan ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `percent` float NOT NULL, `total_tasks` tinyint(4) NOT NULL, `task_checkpoint` varchar(255) NOT NULL, `status` varchar(255) NOT NULL, `date_start` datetime NOT NULL, `date_end` datetime NOT NULL, `is_automation` bool NOT NULL, PRIMARY KEY (`id`) ) $charset_collate;"; $wpdb->query( $sql ); } // Lockout log table. if ( ! $this->table_exists( 'defender_lockout_log' ) ) { $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_lockout_log ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `log` text, `ip` varchar(255) DEFAULT NULL, `date` int(11) DEFAULT NULL, `type` varchar(16) DEFAULT NULL, `user_agent` varchar(255) DEFAULT NULL, `blog_id` int(11) DEFAULT NULL, `tried` varchar(255), `country_iso_code` char(2) DEFAULT NULL, PRIMARY KEY (`id`), KEY `ip` (`ip`), KEY `type` (`type`), KEY `tried` (`tried`), KEY `country_iso_code` (`country_iso_code`) ) $charset_collate;"; $wpdb->query( $sql ); } // Lockout table. if ( ! $this->table_exists( 'defender_lockout' ) ) { $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_lockout ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `ip` varchar(255) DEFAULT NULL, `status` varchar(16) DEFAULT NULL, `lockout_message` text, `release_time` int(11) DEFAULT NULL, `lock_time` int(11) DEFAULT NULL, `lock_time_404` int(11) DEFAULT NULL, `attempt` int(11) DEFAULT NULL, `attempt_404` int(11) DEFAULT NULL, `meta` text, PRIMARY KEY (`id`), KEY `ip` (`ip`), KEY `status` (`status`), KEY `attempt` (`attempt`), KEY `attempt_404` (`attempt_404`) ) $charset_collate;"; $wpdb->query( $sql ); } } /** * Add option with plugin install date. * * @since 2.4 */ protected function set_free_installation_timestamp() { // It's for both cases because don’t have a Pro checking during plugin activation. if ( empty( get_site_option( 'defender_free_install_date' ) ) ) { update_site_option( 'defender_free_install_date', time() ); } } /** * Load all modules. */ public function init_modules() { // Init main ORM. Array_Cache::set( 'orm', new Mapper() ); /** * Display Onboarding if: * it's a fresh install and there were no requests from the Hub before, * after Reset Settings. */ $hub_class = wd_di()->get( HUB::class ); $hub_class->set_onboarding_status( $this->maybe_show_onboarding() ); $hub_class->listen_to_requests(); if ( $hub_class->get_onboarding_status() && 'cli' !== php_sapi_name() ) { // If it's cli we should start this normally. Array_Cache::set( 'onboard', wd_di()->get( Onboard::class ) ); } else { // Initialize the main controllers of every module. wd_di()->get( Dashboard::class ); } wd_di()->get( Security_Tweaks::class ); wd_di()->get( Scan::class ); wd_di()->get( Audit_Logging::class ); wd_di()->get( Firewall::class ); wd_di()->get( WAF::class ); wd_di()->get( Two_Factor::class ); wd_di()->get( Advanced_Tools::class ); wd_di()->get( Mask_Login::class ); wd_di()->get( Security_Headers::class ); wd_di()->get( Recaptcha::class ); wd_di()->get( Notification::class ); wd_di()->get( Main_Setting::class ); wd_di()->get( Tutorial::class ); wd_di()->get( Blocklist_Monitor::class ); wd_di()->get( Password_Protection::class ); wd_di()->get( Password_Reset::class ); wd_di()->get( Webauthn::class ); $this->init_free_dashboard(); } /** * @return bool */ private function maybe_show_onboarding() { // First we need to check if the site is newly create. global $wpdb; if ( ! is_multisite() ) { $res = $wpdb->get_var( "SELECT option_value FROM $wpdb->options WHERE option_name = 'wp_defender_shown_activator'" ); } else { $sql = $wpdb->prepare( "SELECT meta_value FROM $wpdb->sitemeta WHERE meta_key = 'wp_defender_shown_activator' AND site_id = %d", get_current_network_id() ); $res = $wpdb->get_var( $sql ); } // Get '1' for direct SQL request if Onboarding was already. if ( empty( $res ) ) { return true; } return false; } public function init_free_dashboard() { require_once defender_path( 'extra/free-dashboard/module.php' ); add_filter( 'wdev-email-message-' . DEFENDER_PLUGIN_BASENAME, array( &$this, 'defender_ads_message' ) ); $screen_prefix = 'defender'; $screen_suffix = is_multisite() ? '-network' : ''; $free_install_date = get_site_option( 'defender_free_install_date', false ); do_action( 'wpmudev_register_notices', 'defender', array( 'basename' => DEFENDER_PLUGIN_BASENAME, 'title' => 'Defender', 'wp_slug' => 'defender-security', 'cta_email' => __( 'Get Secure!', 'wpdef' ), 'mc_list_id' => '0cecf2890e', 'installed_on' => $free_install_date ? $free_install_date : time(), 'screens' => array( 'toplevel_page_wp-defender' . $screen_suffix, $screen_prefix . '_page_wdf-hardener' . $screen_suffix, $screen_prefix . '_page_wdf-scan' . $screen_suffix, $screen_prefix . '_page_wdf-logging' . $screen_suffix, $screen_prefix . '_page_wdf-ip-lockout' . $screen_suffix, $screen_prefix . '_page_wdf-waf' . $screen_suffix, $screen_prefix . '_page_wdf-2fa' . $screen_suffix, $screen_prefix . '_page_wdf-advanced-tools' . $screen_suffix, $screen_prefix . '_page_wdf-notification' . $screen_suffix, $screen_prefix . '_page_wdf-setting' . $screen_suffix, $screen_prefix . '_page_wdf-tutorial' . $screen_suffix, ), ) ); } /** * @param $message * * @return string|void */ public function defender_ads_message( $message ) { return __( "You're awesome for installing Defender! Are you interested in how to make the most of this plugin? We've collected all the best security resources we know in a single email - just for users of Defender!", 'wpdef' ); } public function add_sui_to_body( $classes ) { if ( ! defender_current_page() ) { return $classes; } $classes .= sprintf( ' sui-%s ', DEFENDER_SUI ); return $classes; } /** * Register all core assets. */ public function register_assets() { $base_url = plugin_dir_url( __DIR__ ); wp_enqueue_style( 'defender-menu', $base_url . 'assets/css/defender-icon.css' ); $css_files = array( 'defender' => $base_url . 'assets/css/styles.css', ); foreach ( $css_files as $slug => $file ) { wp_register_style( $slug, $file, array(), DEFENDER_VERSION ); } $is_min = defined( 'SCRIPT_DEBUG' ) && constant( 'SCRIPT_DEBUG' ) === true ? '' : '.min'; $dependencies = [ 'def-vue', 'defender', 'wp-i18n' ]; $js_files = array( 'wpmudev-sui' => [ $base_url . 'assets/js/shared-ui.js', ], 'defender' => [ $base_url . 'assets/js/scripts.js', ], 'def-vue' => [ $base_url . 'assets/js/vendor/vue.runtime' . $is_min . '.js' ], 'def-dashboard' => [ $base_url . 'assets/app/dashboard.js', $dependencies, ], 'def-securitytweaks' => [ $base_url . 'assets/app/security-tweak.js', array_merge( $dependencies, [ 'clipboard', 'wpmudev-sui' ] ), ], 'def-scan' => [ $base_url . 'assets/app/scan.js', $dependencies, ], 'def-audit' => [ $base_url . 'assets/app/audit.js', $dependencies, ], 'def-iplockout' => [ $base_url . 'assets/app/ip-lockout.js', $dependencies, ], 'def-advancedtools' => [ $base_url . 'assets/app/advanced-tools.js', $dependencies, ], 'def-settings' => [ $base_url . 'assets/app/settings.js', $dependencies, ], 'def-2fa' => [ $base_url . 'assets/app/two-fa.js', $dependencies ], 'def-notification' => [ $base_url . 'assets/app/notification.js', $dependencies ], 'def-waf' => [ $base_url . 'assets/app/waf.js', $dependencies ], 'def-onboard' => [ $base_url . 'assets/app/onboard.js', $dependencies ], 'def-tutorial' => [ $base_url . 'assets/app/tutorial.js', $dependencies ] ); global $wp_defender_central; foreach ( $js_files as $slug => $file ) { if ( isset( $file[1] ) ) { wp_register_script( $slug, $file[0], $file[1], DEFENDER_VERSION, true ); } else { wp_register_script( $slug, $file[0], array( 'jquery' ), DEFENDER_VERSION, true ); } } $wpmu_dev = new WPMUDEV(); wp_localize_script( 'def-vue', 'defender', [ 'whitelabel' => defender_white_label_status(), 'misc' => [ 'high_contrast' => defender_high_contrast(), ], 'site_url' => network_site_url(), 'admin_url' => network_admin_url(), 'defender_url' => $base_url, 'is_free' => $wpmu_dev->is_pro() ? 0 : 1, 'is_membership' => true, 'is_whitelabel' => $wpmu_dev->is_whitelabel_enabled() ? 'enabled' : 'disabled', 'opcache_save_comments' => $wp_defender_central->is_opcache_save_comments_disabled() ? 'disabled' : 'enabled', 'wpmudev_url' => 'https://wpmudev.com/docs/wpmu-dev-plugins/defender/', ] ); wp_localize_script( 'defender', 'defenderGetText', $this->defender_gettext_translations() ); do_action( 'defender_enqueue_assets' ); } /** * Check to exist table. * * @param string $table_name * * @return bool */ private function table_exists( $table_name ) { global $wpdb; // Full table name. $table_name = $wpdb->base_prefix . $table_name; return $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) === $table_name; } /** * Check and create tables if its aren't existed. */ public function check_if_table_exists() { $db_version = get_site_option( 'wd_db_version' ); if ( isset( $db_version ) && version_compare( $db_version, '2.4', '>=') ) { return; } $this->create_database_tables(); } /** * Find all the strings from .mo file. * `wpdef` is our text domain. */ private function defender_gettext_translations() { global $l10n; if ( ! isset( $l10n['wpdef'] ) ) { return array(); } $items = array(); foreach ( $l10n['wpdef']->entries as $key => $value ) { $items[ $key ] = count( $value->translations ) ? $value->translations[0] : $key; } return $items; } /** * Trigger mandatory actions on activation. */ private function on_activation() { add_action( 'admin_init', function() { $security_tweaks = wd_di()->get( Security_Tweaks::class ); $security_tweaks->get_security_key()->cron_schedule(); } ); } /** * @param array $schedules * * @return array */ public function cron_schedules( $schedules ) { return defender_cron_schedules( $schedules ); } public function includes() { add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) ); // Initialize modules. add_action( 'init', array( $this, 'init_modules' ), 8 ); // Register routes. add_action( 'init', function () { require_once WP_DEFENDER_DIR . 'src/routes.php'; }, 9 ); // Include admin class. Don't use is_admin(). add_action( 'admin_init', [ ( new \WP_Defender\Admin() ), 'init' ] ); // Add WP-CLI commands. if ( defined( 'WP_CLI' ) && WP_CLI ) { \WP_CLI::add_command( 'defender', Cli::class ); } // Rotational logger initialization. add_action( 'init', array( ( new \WP_Defender\Component\Logger\Rotation_Logger() ), 'init' ), 99 ); } }