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. */ $first_run = Vigilante_Settings::apply_install_tweaks( $first_run ); 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(); } } // 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(); // Capture the self-integrity anchor (A3: manifest fingerprint in DB). self::anchor_self_integrity( $settings ); // 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(); } /** * Anchor the self-integrity check on activation * * The first activation captures the fingerprint of the shipped manifest, * unless WordPress.org distributes something else for that version, so * the self-check has a baseline from the very first run. A * reactivation keeps the anchor it already has and checks against it: * capturing again adopted whatever manifest the folder held at that * moment, a regenerated one included, the same reason the critical files * baseline is not thrown away on reactivation. * * @since 3.0.0 * * @param Vigilante_Settings $settings Settings instance. * @return void */ public static function anchor_self_integrity( $settings ) { if ( ! class_exists( 'Vigilante_Self_Integrity' ) ) { require_once VIGILANTE_INCLUDES_DIR . 'class-self-integrity.php'; } // run_check() captures on the first run itself, but only when // WordPress.org does not contradict the manifest; with an anchor already // there it checks against it. $self_integrity = new Vigilante_Self_Integrity( $settings ); $self_integrity->run_check( 'activation' ); } /** * 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