Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent URL in Link header from including invalid characters #1802

Merged
21 changes: 18 additions & 3 deletions plugins/optimization-detective/class-od-link-collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,24 @@ public function get_response_header(): ?string {
$link_headers = array();

foreach ( $this->get_prepared_links() as $link ) {
// The about:blank is present since a Link without a reference-uri is invalid so any imagesrcset would otherwise not get downloaded.
$link['href'] = isset( $link['href'] ) ? esc_url_raw( $link['href'] ) : 'about:blank';
$link_header = '<' . $link['href'] . '>';
if ( isset( $link['href'] ) ) {
$decoded_url = urldecode( $link['href'] );
AhmarZaidi marked this conversation as resolved.
Show resolved Hide resolved

// Encode characters not allowed in a URL per RFC 3986 (anything that is not among the reserved and unreserved characters).
$encoded_url = preg_replace_callback(
'/[^A-Za-z0-9\-._~:\/?#\[\]@!$&\'()*+,;=]/',
static function ( $matches ) {
return rawurlencode( $matches[0] );
},
$decoded_url
);
$link['href'] = esc_url_raw( $encoded_url ?? '' );
} else {
// The about:blank is present since a Link without a reference-uri is invalid so any imagesrcset would otherwise not get downloaded.
$link['href'] = 'about:blank';
}

$link_header = '<' . $link['href'] . '>';
unset( $link['href'] );
foreach ( $link as $name => $value ) {
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ public function data_provider_to_test_add_link(): array {
'expected_count' => 1,
'error' => '',
),
'preload_imagesrcset_without_href' => array(
'links_args' => array(
array(
array(
'rel' => 'preload',
'imagesrcset' => 'https://example.com/foo-400.jpg 400w, https://example.com/foo-800.jpg 800w',
'imagesizes' => '(max-width: 600px) 480px, 800px',
'as' => 'image',
'media' => 'screen',
),
),
),
'expected_html' => '
<link data-od-added-tag rel="preload" imagesrcset="https://example.com/foo-400.jpg 400w, https://example.com/foo-800.jpg 800w" imagesizes="(max-width: 600px) 480px, 800px" as="image" media="screen">
',
'expected_header' => 'Link: <about:blank>; rel="preload"; imagesrcset="https://example.com/foo-400.jpg 400w, https://example.com/foo-800.jpg 800w"; imagesizes="(max-width: 600px) 480px, 800px"; as="image"; media="screen"',
'expected_count' => 1,
'error' => '',
),
'preload_with_min0_max_viewport_widths' => array(
'links_args' => array(
array(
Expand Down Expand Up @@ -369,6 +388,74 @@ public function data_provider_to_test_add_link(): array {
'expected_count' => 0,
'error' => 'Maximum width must be greater than zero and greater than the minimum width.',
),
'international_domain_name' => array(
'links_args' => array(
array(
array(
'rel' => 'preload',
'href' => 'https://例.example.com/תמונה.jpg',
'as' => 'image',
),
),
),
'expected_html' => '
<link data-od-added-tag rel="preload" href="https://例.example.com/תמונה.jpg" as="image">
',
'expected_header' => 'Link: <https://%E4%BE%8B.example.com/%D7%AA%D7%9E%D7%95%D7%A0%D7%94.jpg>; rel="preload"; as="image"',
'expected_count' => 1,
'error' => '',
),
'non_ascii_path' => array(
'links_args' => array(
array(
array(
'rel' => 'preload',
'href' => 'https://example.com/חנות/תמונה.jpg',
'as' => 'image',
),
),
),
'expected_html' => '
<link data-od-added-tag rel="preload" href="https://example.com/חנות/תמונה.jpg" as="image">
',
'expected_header' => 'Link: <https://example.com/%D7%97%D7%A0%D7%95%D7%AA/%D7%AA%D7%9E%D7%95%D7%A0%D7%94.jpg>; rel="preload"; as="image"',
'expected_count' => 1,
'error' => '',
),
'percent-in-path' => array(
'links_args' => array(
array(
array(
'rel' => 'preload',
'href' => 'https://example.com/100%25-one-hundred-percent.png?a[1]=2',
'as' => 'image',
),
),
),
'expected_html' => '
<link data-od-added-tag rel="preload" href="https://example.com/100%25-one-hundred-percent.png?a[1]=2" as="image">
',
'expected_header' => 'Link: <https://example.com/100%25-one-hundred-percent.png?a%5B1%5D=2>; rel="preload"; as="image"',
'expected_count' => 1,
'error' => '',
),
'multisite_subdirectory_non_ascii' => array(
'links_args' => array(
array(
array(
'rel' => 'preload',
'href' => 'https://example.com/חנות/wp-content/uploads/2025/01/example.jpg?ver=1+2',
'as' => 'image',
),
),
),
'expected_html' => '
<link data-od-added-tag rel="preload" href="https://example.com/חנות/wp-content/uploads/2025/01/example.jpg?ver=1+2" as="image">
',
'expected_header' => 'Link: <https://example.com/%D7%97%D7%A0%D7%95%D7%AA/wp-content/uploads/2025/01/example.jpg?ver=1%202>; rel="preload"; as="image"',
'expected_count' => 1,
'error' => '',
),
);
}

Expand Down
Loading