ies'], true ) ) { $deps[] = 'wp-components'; } foreach ( array_reverse( $config['vendors'] ) as $vendor ) { if ( ! isset( $manifest[ $vendor ] ) || $name === $vendor ) { continue; } if ( $is_js && $this->has_js( $manifest[ $vendor ]['files'] ) ) { $deps[] = $this->name_to_handle( $vendor ); } elseif ( $is_css && $this->has_css( $manifest[ $vendor ]['files'] ) ) { $deps[] = $this->name_to_handle( $vendor ); } } if ( $is_css ) { wp_register_style( $handle, plugins_url( $path, ITSEC_Core::get_plugin_file() ), $deps, $version ); } else { wp_register_script( $handle, plugins_url( $path, ITSEC_Core::get_plugin_file() ), $deps, $version ); } if ( in_array( 'wp-i18n', $deps, true ) ) { wp_set_script_translations( $handle, 'better-wp-security', '' ); } if ( $is_js && ! empty( $config['runtime'] ) ) { $public_path = esc_js( trailingslashit( plugins_url( 'dist', ITSEC_Core::get_plugin_file() ) ) ); wp_add_inline_script( $handle, "window.itsecWebpackPublicPath = window.itsecWebpackPublicPath || '{$public_path}';", 'before' ); } } if ( ! $has_css && in_array( 'wp-components', $config['dependencies'], true ) ) { wp_register_style( $this->name_to_handle( $name ), '', [ 'wp-components' ] ); } } foreach ( $handles_with_package_dependencies as $handle => $dependencies ) { if ( ! $asset = wp_styles()->registered[ $handle ] ?? null ) { continue; } foreach ( $dependencies as $dependency ) { if ( ! wp_style_is( $dependency, 'registered' ) ) { continue; } $asset->deps[] = $dependency; } } wp_add_inline_script( 'itsec-packages-data', sprintf( "wp.data.dispatch( 'ithemes-security/core' ).__unstableLoadInitialFeatureFlags( %s );", wp_json_encode( ITSEC_Lib_Feature_Flags::get_enabled() ) ) ); } public function add_live_reload() { if ( ! ITSEC_Core::is_development() ) { return; } foreach ( $this->handles as $handle ) { if ( wp_script_is( $handle ) ) { $url = 'http://localhost:35729/livereload.js'; if ( is_ssl() ) { $url = set_url_scheme( $url, 'https' ); } echo ''; return; } } } private function has_js( $files ) { foreach ( $files as $file ) { if ( ITSEC_Lib::str_ends_with( $file, '.js' ) ) { return true; } } return false; } private function has_css( $files ) { foreach ( $files as $file ) { if ( ITSEC_Lib::str_ends_with( $file, '.css' ) ) { return true; } } return false; } private function name_to_handle( $name ) { $name = str_replace( '/dist/', '/entry/', $name ); return 'itsec-' . str_replace( '/', '-', $name ); } /** * Registers the Profile Settings block. */ public function register_block() { register_block_type_from_metadata( __DIR__ . '/entries/profile-block/block.json', [ 'render_callback' => [ $this, 'render_block' ], ] ); add_shortcode( 'solid_security_user_profile_settings', function () { wp_enqueue_script( 'itsec-core-profile-front' ); wp_enqueue_style( 'itsec-core-profile-front' ); return render_block( [ 'blockName' => 'ithemes-security/user-profile-settings', 'attributes' => [], 'children' => [], ] ); } ); } /** * Renders the Profile Settings block. * * @return string */ public function render_block(): string { if ( ! is_user_logged_in() ) { return ''; } $user = wp_get_current_user(); $can_manage = ITSEC_Core::current_user_can_manage(); return sprintf( '
', $user->ID, $can_manage ); } /** * Loads additional JS for the Profile Block JIT. * * @return void */ public function load_profile_js_jit() { if ( ! wp_script_is( 'itsec-core-profile-front' ) ) { return; } $request = new WP_REST_Request( 'GET', '/wp/v2/users/me' ); $request->set_query_params( [ 'context' => 'edit' ] ); $response = rest_do_request( $request ); if ( ! $response->is_error() ) { wp_add_inline_script( 'itsec-core-profile-front', sprintf( "wp.data.dispatch('%s').receiveCurrentUserId( %d );", 'ithemes-security/core', get_current_user_id() ) ); wp_add_inline_script( 'itsec-core-profile-front', sprintf( "wp.data.dispatch('%s').receiveUser( %s );", 'ithemes-security/core', wp_json_encode( rest_get_server()->response_to_data( $response, false ) ) ) ); } foreach ( ITSEC_Modules::get_active_modules_to_run() as $module ) { $handle = "itsec-{$module}-profile"; if ( wp_script_is( $handle, 'registered' ) ) { wp_enqueue_script( $handle ); } if ( wp_style_is( $handle, 'registered' ) ) { wp_enqueue_style( $handle ); } } /** * Fires when scripts are enqueued for the User Profile JS code. * * @param WP_User $user */ do_action( 'itsec_enqueue_profile', wp_get_current_user() ); } public function register_tools( Tools_Registry $registry ) { $registry->register( new class( 'set-encryption-key', ITSEC_Modules::get_config( 'core' ) ) extends Config_Tool { public function run( array $form = [] ): Result { if ( ITSEC_Lib_Encryption::is_available() && ! $form['confirm'] ) { return Result::error( new WP_Error( 'itsec.tool.set-encryption-key.unconfirmed', __( 'You must check “Confirm Reset Key” to continue.', 'better-wp-security' ) ) ); } try { $key = ITSEC_Lib_Encryption::generate_secret(); } catch ( RuntimeException $e ) { return Result::error( new WP_Error( 'itsec.tool.set-encryption-key.cannot-generate-key', __( 'Could not generate a random encryption key.', 'better-wp-security' ) ) ); } $saved = ITSEC_Lib_Encryption::save_secret_key( $key ); if ( ! $saved->is_success() ) { return $saved; } if ( ITSEC_Lib_Encryption::is_available() ) { $rotated = ITSEC_Lib_Encryption::rotate_with_new_key( $key ); return Result::combine( $saved, $rotated ); } return $saved; } public function get_help(): string { $help = parent::get_help(); if ( ! ITSEC_Lib_Encryption::is_available() ) { return $help; } $help .= ' ' . __( 'Your site already has a valid encryption key. Use this tool to automatically re-encrypt all secrets with a new encryption key.', 'better-wp-security' ); $help .= ' ' . __( 'This may take a while. If available, try running this tool with WP CLI for better performance.', 'better-wp-security' ); return $help; } public function get_form(): array { if ( ITSEC_Lib_Encryption::is_available() ) { return parent::get_form(); } return []; } public function is_available(): bool { return ITSEC_Files::can_write_to_files(); } } ); $registry->register( new class( 'rotate-encryption-key', ITSEC_Modules::get_config( 'core' ) ) extends Config_Tool { public function run( array $form = [] ): Result { $old_key = $form['previous']; return ITSEC_Lib_Encryption::rotate_with_old_key( $old_key ); } public function is_available(): bool { return ITSEC_Lib_Encryption::has_encryption_key_changed() && ITSEC_Lib_Encryption::is_available(); } } ); $registry->register( new class( 'create-mu-plugin', ITSEC_Modules::get_config( 'core' ) ) extends Config_Tool { public function run( array $form = [] ): Result { $template = ITSEC_Lib_File::read( __DIR__ . '/security-loader.txt' ); if ( is_wp_error( $template ) ) { return Result::error( $template ); } $basename = plugin_basename( ITSEC_Core::get_plugin_file() ); if ( ! file_exists( WP_CONTENT_DIR . '/plugins/' . $basename ) ) { return Result::error( new WP_Error( 'itsec.tool.create-mu-plugin.unknown-installation', __( 'Could not determine the correct path to load Solid Security.', 'better-wp-security' ) ) ); } $contents = sprintf( $template, $basename ); $path = WP_CONTENT_DIR . '/mu-plugins/000-solid-security-loader.php'; $written = ITSEC_Lib_File::write( $path, $contents ); if ( is_wp_error( $written ) ) { return Result::error( $written ); } return Result::success()->add_success_message( __( 'Created MU-Plugin loader.', 'better-wp-security' ) ); } public function is_available(): bool { return ! defined( 'ITSEC_LOAD_EARLY' ) || ! ITSEC_LOAD_EARLY; } } ); $registry->register( new class( 'remove-mu-plugin', ITSEC_Modules::get_config( 'core' ) ) extends Config_Tool { public function run( array $form = [] ): Result { $path = WP_CONTENT_DIR . '/mu-plugins/000-solid-security-loader.php'; $removed = ITSEC_Lib_File::remove( $path ); if ( is_wp_error( $removed ) ) { return Result::error( $removed ); } return Result::success()->add_success_message( __( 'Removed MU-Plugin loader.', 'better-wp-security' ) ); } public function is_available(): bool { return file_exists( WP_CONTENT_DIR . '/mu-plugins/000-solid-security-loader.php' ); } } ); } public function rotate_encrypted_user_keys( User_Key_Rotator $rotator, Result $result ) { global $wpdb; $user_meta_keys = array_reduce( ITSEC_Modules::get_config_list( ':all' ), function ( $keys, Module_Config $config ) { array_push( $keys, ...$config->get_encrypted_user_meta_keys() ); return $keys; }, [] ); if ( ! $user_meta_keys ) { return; } $in_sql = implode( ', ', array_fill( 0, count( $user_meta_keys ), '%s' ) ); $query = "SELECT * FROM {$wpdb->usermeta} WHERE meta_key IN (" . $in_sql . ")"; $rows = $wpdb->get_results( $wpdb->prepare( $query, $user_meta_keys ) ); if ( $wpdb->last_error ) { $result->add_warning_message( sprintf( __( 'Could not fetch user metadata to update: %s', 'better-wp-security' ), $wpdb->last_error ) ); return; } $users_to_clear = []; foreach ( $rows as $row ) { $meta_id = (int) $row->umeta_id; $user_id = (int) $row->user_id; $meta_key = (string) $row->meta_key; $meta_value = (string) $row->meta_value; if ( ! ITSEC_Lib_Encryption::is_encrypted( $meta_value ) ) { continue; } try { $rotated = $rotator( $meta_value, $user_id ); do_action( 'update_user_meta', $meta_id, $user_id, $meta_key, $rotated ); $updated = $wpdb->update( $wpdb->usermeta, [ 'meta_value' => $rotated ], [ 'umeta_id' => $meta_id ], '%s', '%d' ); if ( $updated ) { do_action( 'updated_user_meta', $meta_id, $user_id, $meta_key, $rotated ); $users_to_clear[ $user_id ] = true; } else { $result->add_warning_message( sprintf( __( 'Could not rotate \'%1$s\' for \'%2$d\': %3$s', 'better-wp-security' ), $meta_key, $user_id, $wpdb->last_error ?: __( 'User meta not updated.', 'better-wp-security' ) ) ); } } catch ( RuntimeException $e ) { $result->add_warning_message( sprintf( __( 'Could not rotate \'%1$s\' for \'%2$d\': %3$s', 'better-wp-security' ), $meta_key, $user_id, $e->getMessage() ) ); } } foreach ( array_flip( $users_to_clear ) as $user_id ) { wp_cache_delete( $user_id, 'user_meta' ); } } public function enable_encryption() { if ( ITSEC_Lib_Encryption::is_available() || ! ITSEC_Files::can_write_to_files() ) { return; } if ( ! ITSEC_Lib_Feature_Flags::is_enabled( 'enable_encryption' ) ) { return; } if ( get_site_option( 'itsec-enable-encryption-failed' ) ) { return; } try { ITSEC_Log::add_debug( 'core', 'try-enable-encryption' ); $secret = ITSEC_Lib_Encryption::generate_secret(); $result = ITSEC_Lib_Encryption::save_secret_key( $secret ); if ( $result->is_success() ) { ITSEC_Log::add_debug( 'core', 'enabled-encryption' ); } else { update_site_option( 'itsec-enable-encryption-failed', ITSEC_Core::get_current_time_gmt() ); ITSEC_Log::add_warning( 'core', 'enable-encryption-failed', $result->get_error() ); } } catch ( \Exception $e ) { update_site_option( 'itsec-enable-encryption-failed', ITSEC_Core::get_current_time_gmt() ); ITSEC_Log::add_warning( 'core', 'enable-encryption-failed', new WP_Error( 'itsec.encryption.cannot-generate-secret', $e->getMessage() ) ); } } }