diff --git a/composer.json b/composer.json index 5e727a2a..98bf344b 100644 --- a/composer.json +++ b/composer.json @@ -59,6 +59,6 @@ "php": ">=7.4", "codeinwp/themeisle-sdk": "^3.3", "codeinwp/optimole-sdk": "^1.2", - "enshrined/svg-sanitize": "^0.22.0" + "enshrined/svg-sanitize": "^1.0.0" } } diff --git a/composer.lock b/composer.lock index 2cc90509..0c300585 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "982a4078faab475dd9f9f90a3f065675", + "content-hash": "9236121e91b49149c045b0f11d2dbc13", "packages": [ { "name": "codeinwp/optimole-sdk", @@ -105,16 +105,16 @@ }, { "name": "enshrined/svg-sanitize", - "version": "0.22.0", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/darylldoyle/svg-sanitizer.git", - "reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500" + "reference": "f3300fcd1bbf67d205b52217c75d0f7d6a8c47ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/0afa95ea74be155a7bcd6c6fb60c276c39984500", - "reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/f3300fcd1bbf67d205b52217c75d0f7d6a8c47ff", + "reference": "f3300fcd1bbf67d205b52217c75d0f7d6a8c47ff", "shasum": "" }, "require": { @@ -144,9 +144,9 @@ "description": "An SVG sanitizer for PHP", "support": { "issues": "https://github.com/darylldoyle/svg-sanitizer/issues", - "source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.22.0" + "source": "https://github.com/darylldoyle/svg-sanitizer/tree/1.0.0" }, - "time": "2025-08-12T10:13:48+00:00" + "time": "2026-09-01T09:35:47+00:00" }, { "name": "symfony/polyfill-php80", diff --git a/inc/manager.php b/inc/manager.php index cb3cea3f..1fa34eae 100644 --- a/inc/manager.php +++ b/inc/manager.php @@ -393,7 +393,7 @@ public static function is_ajax_request() { if ( ! wp_doing_ajax() ) { return false; } - if ( isset( $_REQUEST['action'] ) && strpos( $_REQUEST['action'], 'wpmdb' ) !== false ) { + if ( isset( $_REQUEST['action'] ) && is_string( $_REQUEST['action'] ) && strpos( $_REQUEST['action'], 'wpmdb' ) !== false ) { return false; } diff --git a/tests/test-ajax-request-detection.php b/tests/test-ajax-request-detection.php new file mode 100644 index 00000000..914af0af --- /dev/null +++ b/tests/test-ajax-request-detection.php @@ -0,0 +1,104 @@ +had_action = isset( $_REQUEST['action'] ); + + if ( $this->had_action ) { + $this->original_action = $_REQUEST['action']; + } + + // DOING_AJAX cannot be defined per test, the filter is the supported way in. + add_filter( 'wp_doing_ajax', '__return_true' ); + + wp_set_current_user( 0 ); + } + + public function tearDown(): void { + remove_filter( 'wp_doing_ajax', '__return_true' ); + + if ( $this->had_action ) { + $_REQUEST['action'] = $this->original_action; + } else { + unset( $_REQUEST['action'] ); + } + + $this->had_action = false; + $this->original_action = null; + + parent::tearDown(); + } + + /** + * An array-valued action, i.e. `action[]=wpmdb`, must not fatal. + */ + public function test_array_action_does_not_fatal() { + $_REQUEST['action'] = [ 'wpmdb' ]; + + $this->assertTrue( Optml_Manager::is_ajax_request() ); + } + + /** + * A nested array action is handled the same way. + */ + public function test_nested_array_action_does_not_fatal() { + $_REQUEST['action'] = [ 'a' => [ 'wpmdb' ] ]; + + $this->assertTrue( Optml_Manager::is_ajax_request() ); + } + + /** + * WP Migrate DB requests stay excluded, the guarantee from 02df0774. + */ + public function test_wpmdb_action_still_excluded() { + $_REQUEST['action'] = 'wpmdb_verify_connection_to_remote_site'; + + $this->assertFalse( Optml_Manager::is_ajax_request() ); + } + + /** + * An unrelated AJAX action is still treated as a replaceable request. + */ + public function test_unrelated_action_is_ajax_request() { + $_REQUEST['action'] = 'woocommerce_get_refreshed_fragments'; + + $this->assertTrue( Optml_Manager::is_ajax_request() ); + } + + /** + * A request with no action at all is still treated as a replaceable request. + */ + public function test_missing_action_is_ajax_request() { + unset( $_REQUEST['action'] ); + + $this->assertTrue( Optml_Manager::is_ajax_request() ); + } +}