From e24b06fb32720cb5d09da249f4075987075eb2b1 Mon Sep 17 00:00:00 2001 From: Khokan Sardar Date: Mon, 20 Jul 2026 22:27:59 +0530 Subject: [PATCH] Media: Exclude IMG from crossorigin injection in media templates. When the client-side media processing feature was re-introduced, `wp_print_media_templates()` began adding `crossorigin="anonymous"` to `AUDIO`, `IMG`, and `VIDEO` tags inside the media manager Backbone templates. This reintroduced, for the media library picker, the exact issue fixed for `wp_add_crossorigin_attributes()` in [62048]. Under `Document-Isolation-Policy: isolate-and-credentialless` the browser already loads cross-origin images in credentialless mode without CORS headers. Forcing `crossorigin="anonymous"` on `IMG` triggers a CORS request that breaks previews of images served without `Access-Control-Allow-Origin` headers, such as media offloaded to a CDN. Remove `IMG` from the injected tags so it matches `wp_add_crossorigin_attributes()`, and add a regression test. Follow-up to #64886. Fixes #65673. --- src/wp-includes/media-template.php | 8 +++++- .../tests/media/wpCrossOriginIsolation.php | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/media-template.php b/src/wp-includes/media-template.php index 20f485b5416e5..460cf3b3020e5 100644 --- a/src/wp-includes/media-template.php +++ b/src/wp-includes/media-template.php @@ -1619,11 +1619,17 @@ function wp_print_media_templates() { * The crossorigin attribute is added unconditionally to all relevant * media tags to ensure cross-origin isolation works regardless of * the final URL value at render time. + * + * IMG is intentionally excluded, matching wp_add_crossorigin_attributes(). + * Under Document-Isolation-Policy: isolate-and-credentialless the browser + * loads cross-origin images in credentialless mode without CORS headers, + * so adding crossorigin="anonymous" would force a CORS request and break + * previews of images served without Access-Control-Allow-Origin headers. */ $template_processor = new WP_HTML_Tag_Processor( $script_processor->get_modifiable_text() ); while ( $template_processor->next_tag() ) { if ( - in_array( $template_processor->get_tag(), array( 'AUDIO', 'IMG', 'VIDEO' ), true ) + in_array( $template_processor->get_tag(), array( 'AUDIO', 'VIDEO' ), true ) && ! is_string( $template_processor->get_attribute( 'crossorigin' ) ) ) { $template_processor->set_attribute( 'crossorigin', 'anonymous' ); diff --git a/tests/phpunit/tests/media/wpCrossOriginIsolation.php b/tests/phpunit/tests/media/wpCrossOriginIsolation.php index 1275ed21c3bc8..b1c40e8596780 100644 --- a/tests/phpunit/tests/media/wpCrossOriginIsolation.php +++ b/tests/phpunit/tests/media/wpCrossOriginIsolation.php @@ -538,4 +538,30 @@ public function test_output_buffer_handles_mixed_tags() { // Script and audio should have crossorigin. $this->assertSame( 2, substr_count( $output, 'crossorigin="anonymous"' ), 'Script and audio should both get crossorigin, but not img.' ); } + + /** + * IMG tags in the media manager templates must not receive + * crossorigin="anonymous", matching wp_add_crossorigin_attributes(). + * + * Adding the attribute forces a CORS request that breaks previews of + * images served without Access-Control-Allow-Origin headers, such as + * media offloaded to a CDN. + * + * @ticket 65673 + * + * @covers ::wp_print_media_templates + */ + public function test_print_media_templates_does_not_add_crossorigin_to_img() { + require_once ABSPATH . WPINC . '/media-template.php'; + + add_filter( 'wp_client_side_media_processing_enabled', '__return_true' ); + + ob_start(); + wp_print_media_templates(); + $output = ob_get_clean(); + + $this->assertMatchesRegularExpression( '/assertDoesNotMatchRegularExpression( '/]*\bcrossorigin\b/i', $output, 'IMG tags in the media templates must not receive a crossorigin attribute.' ); + $this->assertMatchesRegularExpression( '/<(?:audio|video)\b[^>]*crossorigin="anonymous"/i', $output, 'AUDIO and VIDEO tags in the media templates should still receive crossorigin="anonymous".' ); + } }