diff --git a/inc/rest.php b/inc/rest.php index 4a5ff2ab..6e70298c 100644 --- a/inc/rest.php +++ b/inc/rest.php @@ -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' ); } diff --git a/inc/v2/PageProfiler/Profile.php b/inc/v2/PageProfiler/Profile.php index bd7d7fa0..31fa3634 100644 --- a/inc/v2/PageProfiler/Profile.php +++ b/inc/v2/PageProfiler/Profile.php @@ -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 $above_fold_images Array of above-fold images. + * @param array $above_fold_images Array of above-fold image ids. * @param array>> $af_bg_selectors Array of above-fold background selectors. * Array structure: * [ @@ -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 ''; diff --git a/tests/test-lazyload-viewport.php b/tests/test-lazyload-viewport.php index 93930c70..86028ea6 100644 --- a/tests/test-lazyload-viewport.php +++ b/tests/test-lazyload-viewport.php @@ -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 = '-->', $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. *