create_tables(); // Initialize default settings $settings = new Vigilante_Settings(); $current_options = get_option( Vigilante_Settings::OPTION_NAME ); if ( false === $current_options ) { // First installation - set defaults $first_run = $settings->get_default_options(); /* * XML-RPC on a brand new install: block the pingback methods, which is * what gets abused for amplification, and leave the rest reachable so * the WordPress app, Jetpack or a remote manager keep working out of * the box. Disabling it completely is the stricter choice and the one * the settings screen recommends, but it is not imposed on a site that * never asked for it. Brute force through XML-RPC stays covered either * way, because those logins go through wp_authenticate() and the login * lockout hooks into it. * * Only written here, on a first installation. Sites upgrading keep * whatever they had: the activation hook does not run on an update, and * Vigilante_Comment_Security::resolve_xmlrpc_mode() answers 'full' when * nothing is stored, which is what every version since 1.0.0 did. */ if ( ! isset( $first_run['wp_hardening'] ) || ! is_array( $first_run['wp_hardening'] ) ) { $first_run['wp_hardening'] = array(); } $first_run['wp_hardening']['xmlrpc_mode'] = 'pingback'; update_option( Vigilante_Settings::OPTION_NAME, $first_run ); // Refresh settings instance to get new values $settings->clear_cache(); $settings = new Vigilante_Settings(); } else { // Existing installation - run idempotent migrations if ( self::run_migrations( $current_options ) ) { $settings->clear_cache(); $settings = new Vigilante_Settings(); } } // Create backup of current files FIRST (before any modifications) self::create_activation_backup( $settings ); // Apply htaccess protection (part of firewall module) if ( $settings->is_module_enabled( 'firewall' ) ) { self::apply_htaccess_protection( $settings ); } // Apply security headers to htaccess if ( $settings->is_module_enabled( 'security_headers' ) ) { self::apply_security_headers( $settings ); } // Apply wp-config security (part of wp_hardening module) if ( $settings->is_module_enabled( 'wp_hardening' ) ) { self::apply_wpconfig_security( $settings ); } // Update WordPress options for HTTPS (part of security_headers module) if ( $settings->is_module_enabled( 'security_headers' ) ) { self::enforce_https( $settings ); } // Apply comment security settings (part of wp_hardening module) if ( $settings->is_module_enabled( 'wp_hardening' ) ) { self::apply_comment_security( $settings ); } // Remove sensitive files self::remove_sensitive_files( $settings ); // Generate critical config files baseline (after all Vigilante writes above) self::generate_critical_baseline( $settings ); // Schedule cron events self::schedule_events(); // Set activation transient for admin notice set_transient( 'vigilante_activated', true, 30 ); // Store activation time update_option( 'vigilante_activated_time', time() ); // Send activation email if enabled self::send_activation_email( $settings ); // Flush rewrite rules flush_rewrite_rules(); // Clean any output that may have been generated ob_end_clean(); } /** * Idempotent migrations for existing installations. * * @param array $current_options Current vigilante_options array. * @return bool True if any migration changed the stored option. */ private static function run_migrations( $current_options ) { $changed = false; // Migration: rest_api_security.mode legacy value 'authenticated' // (UI bug shipped a