diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 304edc35005..56ac01ceed5 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -1565,6 +1565,22 @@ private function process_link_element( DOMElement $element ) { * @return string|WP_Error Stylesheet string on success, or WP_Error on failure. */ private function get_stylesheet_from_url( $stylesheet_url ) { + // For absolute paths, provide the origin (host and port). + if ( '/' === substr( $stylesheet_url, 0, 1 ) && '//' !== substr( $stylesheet_url, 0, 2 ) ) { + $parsed_home_url = wp_parse_url( home_url() ); + if ( ! isset( $parsed_home_url['host'] ) ) { + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $parsed_home_url['host'] = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : 'localhost'; + } + + $stylesheet_origin = '//' . $parsed_home_url['host']; + if ( isset( $parsed_home_url['port'] ) ) { + $stylesheet_origin .= ':' . $parsed_home_url['port']; + } + + $stylesheet_url = $stylesheet_origin . $stylesheet_url; + } + $stylesheet = false; $css_file_path = $this->get_validated_url_file_path( $stylesheet_url, [ 'css', 'less', 'scss', 'sass' ] ); if ( ! is_wp_error( $css_file_path ) ) { diff --git a/tests/php/test-amp-style-sanitizer.php b/tests/php/test-amp-style-sanitizer.php index d8f2bc2765e..fade834bfd0 100644 --- a/tests/php/test-amp-style-sanitizer.php +++ b/tests/php/test-amp-style-sanitizer.php @@ -945,6 +945,52 @@ static function( $preempt, $request, $url ) { } } + /** + * Test get_stylesheet_from_url with a bad URL. + * + * @covers AMP_Style_Sanitizer::get_stylesheet_from_url() + */ + public function test_get_stylesheet_from_url_bad_url() { + $dom = Document::fromHtml( '', Options::DEFAULTS ); + + $sanitizer = new AMP_Style_Sanitizer( $dom, [] ); + + $css_url = 'https://example.com/style.css'; + + add_filter( + 'pre_http_request', + static function( $preempt, $request, $url ) use ( $css_url ) { + if ( $css_url === $url ) { + return new WP_Error( 'http_request_failed', 'Failed to fetch URL.' ); + } + + return $preempt; + }, + 10, + 3 + ); + + $stylesheet = $this->call_private_method( $sanitizer, 'get_stylesheet_from_url', [ $css_url ] ); + + $this->assertTrue( is_wp_error( $stylesheet ) ); + $this->assertInstanceOf( WP_Error::class, $stylesheet ); + $this->assertStringStartsWith( 'Failed to fetch:', $stylesheet->get_error_message() ); + + $css_url = amp_get_asset_url( 'css/amp-default.css' ); + $stylesheet = $this->call_private_method( $sanitizer, 'get_stylesheet_from_url', [ $css_url ] ); + + $this->assertIsString( $stylesheet ); + $this->assertNotEmpty( $stylesheet ); + $this->assertFalse( is_wp_error( $stylesheet ) ); + + $css_url = '/wp-includes/css/admin-bar.css'; + $stylesheet = $this->call_private_method( $sanitizer, 'get_stylesheet_from_url', [ $css_url ] ); + + $this->assertIsString( $stylesheet ); + $this->assertNotEmpty( $stylesheet ); + $this->assertFalse( is_wp_error( $stylesheet ) ); + } + /** * Add test coverage for the property_allowlist condition in process_css_declaration_block which is not currently reachable given the spec. *