Page profiler endpoint fatals on PHP 8 when the request has no `b` (background selectors) field
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 88/100
Hướng nghiên cứu
Bắt đầu trong inc/rest.php quanh các dòng 958-994, nơi tham số b được đọc và truyền vào count(), và kiểm tra đăng ký route quanh các dòng 142-160. Tái hiện bằng REST request được cung cấp hoặc kiểm tra PHP thuần, sau đó làm cho b bị thiếu, null hoặc kiểu chuỗi hoạt động như không có background selector mà không gây fatal. Cũng bao quát các phép đếm lồng nhau tại :987 và :993. Hoàn tất nghĩa là các trường hợp đó trả về bình thường hoặc trả về phản hồi lỗi chuẩn, không phải HTTP 500, và một request hợp lệ vẫn lưu dữ liệu tối ưu hóa.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Description
POST /wp-json/optml/v1/optimizations (the public page profiler endpoint) reads b with get_param( 'b' ) and calls count() on it without checking its type. b is not declared in the route args, so when it is missing (or null, or a string) PHP 8 throws TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given at inc/rest.php:974. WordPress then answers HTTP 500 "There has been a critical error on this website." and a PHP fatal is written to the error log.
This happens after the HMAC and Origin checks, so it needs a valid page token. That token is printed in the page source for every logged-out visitor on a page that is not profiled yet, so any HTTP client (not only the bundled script) can trigger the fatal and the log line. The bundled script always sends b, so normal visits are not affected.
The nested values have the same problem: count( $above_fold_bg_selectors ) (:987) and count( $bg_urls ) (:993) fatal on null. The bundled script sets each entry to null first and only replaces it when it finds URLs (assets/js/modules/main.js:231-237), so a null there is likely rare but possible.
Expected: a missing or malformed b is treated as "no background selectors" (or rejected with the normal code: error response), never a fatal.
Step-by-step reproduction instructions
- On a test site running PHP 8.x with Optimole 4.2.17 active (the route is registered even when the plugin is not connected), run:
wp eval ' $u = "repro-profile-id"; $t = time(); $pu = home_url( "/" ); $r = new WP_REST_Request( "POST", "/optml/v1/optimizations" ); $r->set_header( "content-type", "application/json" ); $r->set_header( "origin", "https://" . wp_parse_url( home_url(), PHP_URL_HOST ) ); $r->set_body( wp_json_encode( [ "d" => 1, "a" => [ 1 ], "u" => $u, "t" => $t, "pu" => $pu, "h" => wp_hash( $u . $t . $pu, "nonce" ) ] ) ); var_dump( rest_do_request( $r )->get_data() ); ' - Result (from reading the code and the PHP check in step 5; I did not run it on a live site):
PHP Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given in .../inc/rest.php:974. - Add
"b" => new stdClass()to the body and run it again: it should get past :974 with no fatal and return "Optimization data stored successfully" (it stores a small profile transient forrepro-profile-id). - Over HTTP as a logged-out client: open a not-yet-profiled page, copy
pageProfileId,_t,hmacandpageProfileUrlfrom theoptimoleDataOptimizerscript in the page source, and POST them asu,t,h,pu(plusd,a) to/wp-json/optml/v1/optimizationswithContent-Type: application/jsonandOrigin: https://<site host>, leaving outb. The response is HTTP 500 with"code":"internal_server_error"and the fatal above is logged. - Pure PHP check of the same lines (PHP 8.4.19), body decoded with
json_decode( $body, true )asWP_REST_Requestdoes:b absent => TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given b is {} => Optimization data stored successfully b null => TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given b string => TypeError: count(): Argument #1 ($value) must be of type Countable|array, string given b inner url null => TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given (the :993 count)
Screenshots, screen recording, code snippet or Help Scout ticket
b is read after the signature and origin checks and counted without a type check:
https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/rest.php#L958-L976
if ( wp_hash( $url . $time . $current_url, 'nonce' ) !== $hmac ) { ... }
$bg_selectors = $request->get_param( 'b' );
...
if ( count( $bg_selectors ) > 100 ) {
Nested counts: https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/rest.php#L983-L994
The route only declares d, a and u, with permission_callback => '__return_true':
https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/rest.php#L142-L160
The token is printed for logged-out visitors on unprofiled pages:
https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/manager.php#L515-L523 and https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/admin.php#L1070-L1083
The bundled script's null placeholder for nested entries:
https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/assets/js/modules/main.js#L226-L237
Core does not catch it: call_user_func( $handler['callback'], $request ) in WP_REST_Server::respond_to_request() (wp-includes/rest-api/class-wp-rest-server.php:1293 in WordPress 7.1.3, :1181 in 6.2). The fatal error handler then answers with status 500 (wp-includes/class-wp-fatal-error-handler.php:212 in 7.1.3). REST is not a protected endpoint, so no recovery mode email is sent (wp-includes/class-wp-recovery-mode.php:176-178).
A likely fix is to declare b in the route args (type => object, default => []) or to guard with is_array() before each count() and foreach, at :974, :987 and :993.
Environment info
- Optimole 4.2.17 (
inc/rest.phpis the same onmasteranddevelopmentat 63f3f7c) - Any WordPress version the plugin supports (5.5 and later; core lines cited from 7.1.3 and 6.2)
- PHP 8.0 and later. On PHP 7.4
count( null )only logs a warning and the request goes on.
Is the issue you are reporting a regression
No. The same code has been there since the endpoint was added in 4.0.0.
Found while writing the WordPress compatibility checks for Optimole.
- Ngôn ngữ chính
- PHP
- Star
- 74
- Fork
- 14
- Merge trung bình
- 1 ngày 21 giờ
- Pull request đã merge (30 ngày)
- 30
Chuẩn bị môi trường
- Có Dockerfile hoặc tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của Codeinwp/optimole-wp
-
Background image with a gradient is not lazy-loaded on WordPress 7.1 (Quote, Pullquote, Verse, Post Content, Accordion)Có thể đã có người làm @girishpanchal30 đã nhận 1 ngày trước. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Codeinwp/optimole-wp#1203 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Page profiler never treats the detected LCP image as LCP, so it gets no fetchpriority="high" (stored ID is a string, compared with === to an int)Có thể đã có người làm @girishpanchal30 đã nhận 1 ngày trước. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
Codeinwp/optimole-wp#1202 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Account sign-up fatals when the Optimole API returns an empty or non-JSON responseCó thể đã có người làm @girishpanchal30 đã nhận 1 ngày trước. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
Codeinwp/optimole-wp#1200 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Renaming an image leaves the old URL in comments and term descriptions, with "Unknown column 'ID'" database errorsCó thể đã có người làm @girishpanchal30 đã nhận 1 ngày trước. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
Codeinwp/optimole-wp#1199 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug-report customer report
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 56/100
Codeinwp/optimole-wp#1211 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của Codeinwp/optimole-wp
Issue tương tự
-
GD decoder discards the finfo media type in mediaTypeByBinary() since 4.4.0Có thể đã có người làm @lazerg đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 77/100
Intervention/image#1556 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug No Code Attached Yet
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 79/100
joomla/joomla-cms#48730 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[Sync EN] Add more details about the array returned from openssl_x509_parse (php#2666) (#6013)Đang mởsync-en
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[Sync EN] Add more details about the array returned from openssl_x509_parse (php#2666) (#6013)Đang mởsync-en
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Перевод устарел
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 65/100