From 21ca72ff6bfdf05865f04f77634fd2db198f6142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Jakes=CC=8C?= Date: Mon, 12 Oct 2026 17:24:58 +0200 Subject: [PATCH] Limit redirect cache flushing to SQLite installation Register the late redirect cache flush only after the database installation passes its capability and nonce checks. Ordinary redirects must preserve cache entries and transients. Add regression coverage for anonymous and authenticated redirects, rejected installation requests, and the required flush during an authorized installation. Report: https://hackerone.com/reports/4095826 --- .../activate.php | 22 ++++----- ...atabase_Integration_Authorization_Test.php | 49 +++++++++++++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/packages/plugin-sqlite-database-integration/activate.php b/packages/plugin-sqlite-database-integration/activate.php index 2274dd1c6..f18f70189 100644 --- a/packages/plugin-sqlite-database-integration/activate.php +++ b/packages/plugin-sqlite-database-integration/activate.php @@ -48,23 +48,23 @@ function sqlite_activation() { sqlite_plugin_copy_db_file(); + // Flush the cache at the last moment before switching databases. + add_filter( + 'x_redirect_by', + function ( $result ) { + wp_cache_flush(); + return $result; + }, + PHP_INT_MAX, + 1 + ); + // WordPress will automatically redirect to the install screen here. wp_redirect( admin_url() ); exit; } add_action( 'admin_init', 'sqlite_activation' ); -// Flush the cache at the last moment before the redirect. -add_filter( - 'x_redirect_by', - function ( $result ) { - wp_cache_flush(); - return $result; - }, - PHP_INT_MAX, - 1 -); - /** * Add the db.php file in wp-content. * diff --git a/tests/phpunit/WP_SQLite_Database_Integration_Authorization_Test.php b/tests/phpunit/WP_SQLite_Database_Integration_Authorization_Test.php index 8dc833568..cab1ac6f9 100644 --- a/tests/phpunit/WP_SQLite_Database_Integration_Authorization_Test.php +++ b/tests/phpunit/WP_SQLite_Database_Integration_Authorization_Test.php @@ -193,6 +193,26 @@ public function test_other_plugin_activation_does_not_redirect() { $this->assertNull( $this->get_activation_redirect( 'another-plugin/plugin.php' ) ); } + /** + * @dataProvider data_redirect_users + */ + public function test_unrelated_redirect_preserves_cache( $role ) { + $user_id = null === $role ? 0 : self::factory()->user->create( array( 'role' => $role ) ); + wp_set_current_user( $user_id ); + + sqlite_activation(); + + $this->assert_redirect_preserves_cache(); + } + + public static function data_redirect_users() { + return array( + 'anonymous' => array( null ), + 'subscriber' => array( 'subscriber' ), + 'administrator' => array( 'administrator' ), + ); + } + /** * @group ms-excluded */ @@ -205,6 +225,7 @@ public function test_confirm_install_is_ignored_on_another_page() { $_REQUEST = $_GET; $this->assertNull( sqlite_activation() ); + $this->assert_redirect_preserves_cache(); } /** @@ -827,6 +848,9 @@ private function assert_valid_nonce_reaches_install_redirect() { $redirect_reached = false; $filter = function ( $location ) use ( &$redirect_url ) { $redirect_url = $location; + wp_cache_set( 'sqlite_redirect_test', 'cached during redirect' ); + // Run the final redirect filter without sending headers or exiting the test. + $this->assertSame( 'WordPress', apply_filters( 'x_redirect_by', 'WordPress', 302, $location ) ); throw new RuntimeException( 'Install redirect reached.' ); }; @@ -844,6 +868,7 @@ private function assert_valid_nonce_reaches_install_redirect() { $this->assertTrue( $redirect_reached, 'The install request did not redirect.' ); $this->assertSame( admin_url(), $redirect_url ); + $this->assertFalse( wp_cache_get( 'sqlite_redirect_test' ), 'The database switch must flush the cache.' ); } private function assert_valid_nonce_install_is_denied() { @@ -870,6 +895,7 @@ private function assert_valid_nonce_install_is_denied() { $this->assertFalse( $nonce_checked, 'The capability check must run before nonce validation.' ); $this->assertSame( $dropin_before, file_get_contents( WP_CONTENT_DIR . '/db.php' ) ); + $this->assert_redirect_preserves_cache(); } private function assert_invalid_nonce_is_denied() { @@ -893,6 +919,29 @@ private function assert_invalid_nonce_is_denied() { } $this->assertTrue( $nonce_checked ); + $this->assert_redirect_preserves_cache(); + } + + private function assert_redirect_preserves_cache() { + $using_external_cache = (bool) wp_using_ext_object_cache(); + // Route transients through the object cache, as with a persistent cache drop-in. + wp_using_ext_object_cache( true ); + + try { + wp_cache_set( 'sqlite_redirect_test', 'cached value' ); + set_transient( 'sqlite_redirect_test', 'transient value', MINUTE_IN_SECONDS ); + set_site_transient( 'sqlite_redirect_test', 'site transient value', MINUTE_IN_SECONDS ); + + $this->assertSame( 'WordPress', apply_filters( 'x_redirect_by', 'WordPress', 302, wp_login_url() ) ); + $this->assertSame( 'cached value', wp_cache_get( 'sqlite_redirect_test' ) ); + $this->assertSame( 'transient value', get_transient( 'sqlite_redirect_test' ) ); + $this->assertSame( 'site transient value', get_site_transient( 'sqlite_redirect_test' ) ); + } finally { + wp_cache_delete( 'sqlite_redirect_test' ); + delete_transient( 'sqlite_redirect_test' ); + delete_site_transient( 'sqlite_redirect_test' ); + wp_using_ext_object_cache( $using_external_cache ); + } } private function set_install_request( $nonce ) {