Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions inc/rest.php
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,7 @@ public function optimizations( WP_REST_Request $request ) {
}
// save just the first 6 images, see https://github.com/Codeinwp/optimole-service/issues/1588#issuecomment-3357110865
$above_fold_images = array_slice( $above_fold_images, 0, 6 );
$above_fold_images = array_values( array_map( 'intval', array_filter( $above_fold_images, 'is_numeric' ) ) );
if ( count( $bg_selectors ) > 100 ) {
return $this->response( 'Background selectors limit exceeded', 'error' );
}
Expand Down
4 changes: 2 additions & 2 deletions inc/v2/PageProfiler/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public static function get_default_id( string $content ): string {
*
* @param string $id The profile ID.
* @param int $device_type The device type constant.
* @param array<string> $above_fold_images Array of above-fold images.
* @param array<int|string> $above_fold_images Array of above-fold image ids.
* @param array<string, array<string, array<int, string>>> $af_bg_selectors Array of above-fold background selectors.
* Array structure:
* [
Expand Down Expand Up @@ -482,7 +482,7 @@ public function get_current_profile_html_comment(): string {
// Add device-specific metrics
foreach ( $profile_data as $device_type => $device_data ) {
$device_name = $this->get_device_name( $device_type );
$comment_parts[] = 'measurement#device-' . $device_name . '#' . json_encode( $device_data );
$comment_parts[] = 'measurement#device-' . $device_name . '#' . json_encode( $device_data, JSON_HEX_TAG | JSON_HEX_AMP );
}

return '<!-- ' . implode( ' ', $comment_parts ) . ' -->';
Expand Down
31 changes: 31 additions & 0 deletions tests/test-lazyload-viewport.php
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,37 @@ public static function mock_page_ids() {
return 1;
}

/**
* The debug HTML comment must never be broken out of by stored profile data.
*
* Regression test for the unauthenticated stored XSS: a malicious above-fold
* image key must not emit a raw `-->` or `<` inside the profiler debug comment.
*/
public function test_profile_html_comment_cannot_break_out() {
$payload = '--><img src=x onerror=alert(document.domain)><!--marker';

$this->storeMockProfileData(
self::mock_page_ids(),
Profile::DEVICE_TYPE_DESKTOP,
[ $payload ]
);
$this->storeMockProfileData(
self::mock_page_ids(),
Profile::DEVICE_TYPE_MOBILE,
[ $payload ]
);

$comment = Optml_Manager::instance()->page_profiler->get_current_profile_html_comment();

// Comment stays a single well-formed HTML comment.
$this->assertStringStartsWith('<!--', $comment);
$this->assertStringEndsWith('-->', $comment);
// Body between the delimiters must contain no raw < or > to break out with.
$body = substr($comment, 4, -3);
$this->assertStringNotContainsString('<', $body);
$this->assertStringNotContainsString('>', $body);
}

/**
* Get sample HTML content for testing.
*
Expand Down
Loading