From 86030f495e36ba8d17c97dce55920a458047f56a Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Wed, 12 Aug 2026 20:05:24 -0500 Subject: [PATCH 1/5] Core APIs: Sanitize request input and escape output in browse-happy, serve-happy, credits, importers, and patterns. JSONP callbacks and versions are validated against their expected formats, the server protocol is validated before being echoed into status headers, user agents are stripped of control characters before being parsed or stored, and remaining output sites carry narrow, justified ignores (JSON/serialized payloads that escaping would corrupt). Also fixes an undefined-offset warning in browse-happy when core sends a user agent without a site URL. Nonce and unslash sniffs are disabled per file with justification: these are standalone, unauthenticated endpoints where WordPress (and thus slashing and nonces) does not exist. Co-Authored-By: Claude Fable 5 (cherry picked from commit e7130d8ebc688e81a7e2a5ce0c59a0755f7274fc) --- .../core/browse-happy/1.0/index.php | 46 +++++++++++++++--- .../core/browse-happy/1.0/test.php | 24 ++++++++-- .../public_html/core/credits/index.php | 48 +++++++++++++++++-- .../public_html/core/credits/wp-credits.php | 1 + .../public_html/core/importers/1.0/index.php | 16 ++++++- .../core/serve-happy/1.0/include.php | 7 +++ .../core/serve-happy/1.0/index.php | 40 ++++++++++++++-- .../public_html/patterns/1.0/index.php | 8 +++- 8 files changed, 168 insertions(+), 22 deletions(-) diff --git a/api.wordpress.org/public_html/core/browse-happy/1.0/index.php b/api.wordpress.org/public_html/core/browse-happy/1.0/index.php index b307bc2e58..ad0134f2ba 100644 --- a/api.wordpress.org/public_html/core/browse-happy/1.0/index.php +++ b/api.wordpress.org/public_html/core/browse-happy/1.0/index.php @@ -1,38 +1,65 @@ array( + 'regexp' => '/^[a-zA-Z0-9_]+$/', + 'default' => '', + ), + 'flags' => FILTER_REQUIRE_SCALAR, +); + $jsonp = ''; if ( ! empty( $_GET['jsonp'] ) ) { - $jsonp = preg_replace( '/[^a-zA-Z0-9_]/', '', $_GET['jsonp'] ); + $jsonp = filter_var( $_GET['jsonp'], FILTER_VALIDATE_REGEXP, $jsonp_filter_args ); header( 'Content-Type: application/javascript' ); } else if ( ! empty( $_GET['callback'] ) ) { - $jsonp = preg_replace( '/[^a-zA-Z0-9_]/', '', $_GET['callback'] ); + $jsonp = filter_var( $_GET['callback'], FILTER_VALIDATE_REGEXP, $jsonp_filter_args ); header( 'Content-Type: application/javascript' ); } -if ( empty( $_REQUEST['useragent'] ) ) { +if ( empty( $_REQUEST['useragent'] ) || ! is_string( $_REQUEST['useragent'] ) ) { return; } -$user_agent = $_REQUEST['useragent']; +$user_agent = filter_var( $_REQUEST['useragent'], FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW ); $data = browsehappy_parse_user_agent( $user_agent ); // Collect a sample: One out of every 25. -if ( 0 === strpos( $_SERVER['HTTP_USER_AGENT'], 'WordPress/' ) && 1 === rand( 1, 25 ) ) { +/* + * Only used for a prefix comparison, never output or stored. + * phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + */ +if ( 0 === strpos( $_SERVER['HTTP_USER_AGENT'] ?? '', 'WordPress/' ) && 1 === rand( 1, 25 ) ) { require( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) . '/includes/hyperdb/bb-10-hyper-db.php' ); bh_record_data( $user_agent, $data ); } if ( $jsonp ) { header( 'Access-Control-Allow-Origin: *' ); - echo $jsonp.'('.json_encode($data).')'; + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- validated callback, JSON-encoded payload. + echo $jsonp . '(' . json_encode( $data ) . ')'; } elseif ( defined( 'JSON_RESPONSE' ) ) { header( 'Access-Control-Allow-Origin: *' ); header( 'Content-Type: application/json' ); echo json_encode( $data ); } else { header( 'Content-Type: text/plain' ); + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- serialized payload served as text/plain. echo serialize( $data ); } @@ -46,7 +73,12 @@ function bh_record_data( $ua, $data ) { global $wpdb; - list( $wp_ver, $url ) = explode( ';', $_SERVER['HTTP_USER_AGENT'], 2 ); + // The requesting client's own user agent, which is recorded alongside the reported one. + $client_ua = filter_var( $_SERVER['HTTP_USER_AGENT'] ?? '', FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW ); + + /* Core sends `WordPress/{version}; {site url}`, but the URL may be absent. */ + list( $wp_ver, $url ) = array_pad( explode( ';', $client_ua, 2 ), 2, '' ); + $wp_ver = substr( $wp_ver, 10, 64 ); $url = rtrim( strtolower( trim( $url ) ), '/' ); $pk = md5( $url . '|' . $ua ); diff --git a/api.wordpress.org/public_html/core/browse-happy/1.0/test.php b/api.wordpress.org/public_html/core/browse-happy/1.0/test.php index 18aeb00ce3..3bff3594de 100644 --- a/api.wordpress.org/public_html/core/browse-happy/1.0/test.php +++ b/api.wordpress.org/public_html/core/browse-happy/1.0/test.php @@ -1,10 +1,26 @@
"; +/* + * This is a standalone diagnostic page: WordPress is not loaded, so request data is + * never slashed and the `esc_*()` escaping helpers are unavailable. + * + * phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash + */ include dirname( __FILE__ ) . '/parse.php'; +$user_agent = filter_var( $_SERVER['HTTP_USER_AGENT'] ?? '', FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW ); -$output = browsehappy_parse_user_agent( $_SERVER['HTTP_USER_AGENT'] ); +// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- htmlspecialchars() escapes; no esc_html() here. +echo htmlspecialchars( $user_agent, ENT_QUOTES ) . '

'; -foreach ( $output as $k => $v ) - echo htmlspecialchars( $k . ' = ' . ( is_bool( $v ) ? (int) $v : $v ), ENT_QUOTES ) . "
"; +$output = browsehappy_parse_user_agent( $user_agent ); + +foreach ( $output as $k => $v ) { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- htmlspecialchars() escapes; no esc_html() here. + echo htmlspecialchars( $k . ' = ' . ( is_bool( $v ) ? (int) $v : $v ), ENT_QUOTES ) . '
'; +} diff --git a/api.wordpress.org/public_html/core/credits/index.php b/api.wordpress.org/public_html/core/credits/index.php index d554a0e7e5..80b0ca643a 100644 --- a/api.wordpress.org/public_html/core/credits/index.php +++ b/api.wordpress.org/public_html/core/credits/index.php @@ -1,4 +1,16 @@ array( + 'regexp' => '/^[0-9][.0-9]*/', + 'default' => '', + ), + ) + ) + ); } elseif ( 'cli' == php_sapi_name() && isset( $argv[1] ) ) { $version = preg_replace( '/^([.0-9]+).*/', '$1', $argv[1] ); } else { $version = WP_CORE_LATEST_RELEASE; } +// A WP locale, e.g. `de_DE_formal` or `es_419`. +$requested_locale = isset( $_GET['locale'] ) ? filter_var( + $_GET['locale'], + FILTER_VALIDATE_REGEXP, + array( + 'options' => array( + 'regexp' => '/^[A-Za-z0-9_-]+$/', + 'default' => '', + ), + ) +) : ''; + if ( ! is_string( $version ) || version_compare( $version, '3.2', '<' ) || - ( isset( $_GET['locale'] ) && ! is_string( $_GET['locale'] ) ) + ( isset( $_GET['locale'] ) && ! is_string( $requested_locale ) ) ) { header( 'HTTP/1.0 400 Bad Request', true, 400 ); die( 'Bad request.' ); @@ -50,10 +87,13 @@ function like_escape( $text ) { $locale = false; // Convert a locale from a WP locale to a GP locale. -if ( ( isset( $_GET['locale'] ) && 'en_US' != $_GET['locale'] ) || ( 'cli' == php_sapi_name() && isset( $argv[2] ) ) ) { +if ( + ( isset( $_GET['locale'] ) && 'en_US' != $requested_locale ) || + ( 'cli' == php_sapi_name() && isset( $argv[2] ) ) +) { require GLOTPRESS_LOCALES_PATH; - $gp_locale = GP_Locales::by_field( 'wp_locale', isset( $argv[2] ) ? $argv[2] : $_GET['locale'] ); + $gp_locale = GP_Locales::by_field( 'wp_locale', isset( $argv[2] ) ? $argv[2] : $requested_locale ); if ( $gp_locale ) { $locale = $gp_locale; } diff --git a/api.wordpress.org/public_html/core/credits/wp-credits.php b/api.wordpress.org/public_html/core/credits/wp-credits.php index 3638294178..3ba85e0af0 100644 --- a/api.wordpress.org/public_html/core/credits/wp-credits.php +++ b/api.wordpress.org/public_html/core/credits/wp-credits.php @@ -447,6 +447,7 @@ final public function execute() { } elseif ( defined( 'JSON_RESPONSE' ) && JSON_RESPONSE ) { echo json_encode( $results ); } else { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- serialized payload served as text/plain. echo serialize( $results ); } } diff --git a/api.wordpress.org/public_html/core/importers/1.0/index.php b/api.wordpress.org/public_html/core/importers/1.0/index.php index 0258dfa6d9..c544ca86c8 100644 --- a/api.wordpress.org/public_html/core/importers/1.0/index.php +++ b/api.wordpress.org/public_html/core/importers/1.0/index.php @@ -1,4 +1,16 @@ =' ) ) { @@ -21,6 +34,7 @@ } $response = array( 'importers' => $popular_importers, 'translated' => false ); +// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON or serialized payload, not HTML. echo defined( 'JSON_RESPONSE' ) ? json_encode( $response ) : serialize( $response ); function __( $string ) { return $string; } diff --git a/api.wordpress.org/public_html/core/serve-happy/1.0/include.php b/api.wordpress.org/public_html/core/serve-happy/1.0/include.php index 5bcde61f33..966a4f0706 100644 --- a/api.wordpress.org/public_html/core/serve-happy/1.0/include.php +++ b/api.wordpress.org/public_html/core/serve-happy/1.0/include.php @@ -1,6 +1,13 @@ array( + 'regexp' => '#^HTTP/[0-9]+(\.[0-9]+)?$#', + 'default' => 'HTTP/1.1', + ), + ) + ); $http_code_texts = [ 400 => 'Bad Request', ]; @@ -39,15 +56,28 @@ function output_response( $data ) { header( 'Access-Control-Allow-Origin: *' ); - if ( !empty( $_GET['callback'] ) ) { + // A JSONP callback is a JavaScript identifier, optionally namespaced; anything else is discarded. + $callback = filter_var( + $_GET['callback'] ?? '', + FILTER_VALIDATE_REGEXP, + array( + 'options' => array( + 'regexp' => '/^[a-zA-Z0-9_.]+$/', + 'default' => '', + ), + 'flags' => FILTER_REQUIRE_SCALAR, + ) + ); + + if ( $callback ) { call_headers( 'application/javascript' ); - echo '/**/' . - preg_replace('/[^a-zA-Z0-9_.]/', '', $_GET['callback'] ) . - '(' . $json_data . ')'; + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- validated callback, JSON-encoded payload. + echo '/**/' . $callback . '(' . $json_data . ')'; } else { call_headers( 'application/json' ); + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON-encoded payload served as application/json. echo $json_data; } } \ No newline at end of file diff --git a/api.wordpress.org/public_html/patterns/1.0/index.php b/api.wordpress.org/public_html/patterns/1.0/index.php index 1c838833e7..17a3372708 100644 --- a/api.wordpress.org/public_html/patterns/1.0/index.php +++ b/api.wordpress.org/public_html/patterns/1.0/index.php @@ -2,13 +2,19 @@ namespace WordPressdotorg\API\Patterns; +/* + * The request is read here, before `main()` loads WordPress, so it has not been slashed. + * + * phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash + */ + /* * Supply Block Pattern Directory data to the block editor. * * This is cached by nginx, so we don't have to worry about the performance costs of loading WP, and don't need to * do any any object caching. */ -main( $_SERVER['QUERY_STRING'] ); +main( filter_var( $_SERVER['QUERY_STRING'] ?? '', FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW ) ); /** * Last minute rewrite of headers, to correct URLs set by the internal API endpoint. From ebc16da926e99016ab0fb6e62bf494e38081bbbd Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Wed, 12 Aug 2026 20:37:14 -0500 Subject: [PATCH 2/5] Core APIs: Validate JSONP callbacks as real JavaScript identifiers. Callbacks starting with a digit (or containing empty namespace segments in serve-happy) produced syntactically invalid JavaScript; the regexes now enforce identifier rules and invalid callbacks fall back to the non-JSONP response. Also restores the browse-happy user-agent ignore to single-line form, since an annotation inside a multi-line block comment does not apply to the following code. Co-Authored-By: Claude Fable 5 --- .../public_html/core/browse-happy/1.0/index.php | 7 ++----- .../public_html/core/serve-happy/1.0/index.php | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/api.wordpress.org/public_html/core/browse-happy/1.0/index.php b/api.wordpress.org/public_html/core/browse-happy/1.0/index.php index ad0134f2ba..ac1ac99f4f 100644 --- a/api.wordpress.org/public_html/core/browse-happy/1.0/index.php +++ b/api.wordpress.org/public_html/core/browse-happy/1.0/index.php @@ -17,7 +17,7 @@ // A JSONP callback is a plain JavaScript identifier; anything else is discarded. $jsonp_filter_args = array( 'options' => array( - 'regexp' => '/^[a-zA-Z0-9_]+$/', + 'regexp' => '/^[a-zA-Z_][a-zA-Z0-9_]*$/', 'default' => '', ), 'flags' => FILTER_REQUIRE_SCALAR, @@ -40,10 +40,7 @@ $data = browsehappy_parse_user_agent( $user_agent ); // Collect a sample: One out of every 25. -/* - * Only used for a prefix comparison, never output or stored. - * phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - */ +// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Only used for a prefix comparison, never output or stored. if ( 0 === strpos( $_SERVER['HTTP_USER_AGENT'] ?? '', 'WordPress/' ) && 1 === rand( 1, 25 ) ) { require( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) . '/includes/hyperdb/bb-10-hyper-db.php' ); bh_record_data( $user_agent, $data ); diff --git a/api.wordpress.org/public_html/core/serve-happy/1.0/index.php b/api.wordpress.org/public_html/core/serve-happy/1.0/index.php index f23f4e3681..444f2d2ab4 100644 --- a/api.wordpress.org/public_html/core/serve-happy/1.0/index.php +++ b/api.wordpress.org/public_html/core/serve-happy/1.0/index.php @@ -62,7 +62,7 @@ function output_response( $data ) { FILTER_VALIDATE_REGEXP, array( 'options' => array( - 'regexp' => '/^[a-zA-Z0-9_.]+$/', + 'regexp' => '/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/', 'default' => '', ), 'flags' => FILTER_REQUIRE_SCALAR, From 3c583941c95ce2dd8a5af6b7ae257d01de1739bd Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Wed, 12 Aug 2026 20:51:32 -0500 Subject: [PATCH 3/5] Core APIs: Fold the phpcs justification comments into the file docblocks. Co-Authored-By: Claude Fable 5 --- .../public_html/core/browse-happy/1.0/index.php | 6 ++---- .../public_html/core/browse-happy/1.0/test.php | 6 ++---- api.wordpress.org/public_html/core/credits/index.php | 6 ++---- api.wordpress.org/public_html/core/importers/1.0/index.php | 6 ++---- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/api.wordpress.org/public_html/core/browse-happy/1.0/index.php b/api.wordpress.org/public_html/core/browse-happy/1.0/index.php index ac1ac99f4f..f7651bc488 100644 --- a/api.wordpress.org/public_html/core/browse-happy/1.0/index.php +++ b/api.wordpress.org/public_html/core/browse-happy/1.0/index.php @@ -2,14 +2,12 @@ /** * WordPress.org Browse Happy API endpoint. * - * @package BrowseHappy - */ - -/* * This is a standalone, unauthenticated, stateless API endpoint: WordPress is not loaded, * so request data is never slashed, and there is no session or nonce infrastructure. * * phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.MissingUnslash + * + * @package BrowseHappy */ require dirname( __FILE__ ) . '/parse.php'; diff --git a/api.wordpress.org/public_html/core/browse-happy/1.0/test.php b/api.wordpress.org/public_html/core/browse-happy/1.0/test.php index 3bff3594de..c775c8a3e5 100644 --- a/api.wordpress.org/public_html/core/browse-happy/1.0/test.php +++ b/api.wordpress.org/public_html/core/browse-happy/1.0/test.php @@ -2,14 +2,12 @@ /** * Browse Happy user agent parser test page. * - * @package BrowseHappy - */ - -/* * This is a standalone diagnostic page: WordPress is not loaded, so request data is * never slashed and the `esc_*()` escaping helpers are unavailable. * * phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash + * + * @package BrowseHappy */ include dirname( __FILE__ ) . '/parse.php'; diff --git a/api.wordpress.org/public_html/core/credits/index.php b/api.wordpress.org/public_html/core/credits/index.php index 80b0ca643a..50c683e2c5 100644 --- a/api.wordpress.org/public_html/core/credits/index.php +++ b/api.wordpress.org/public_html/core/credits/index.php @@ -2,14 +2,12 @@ /** * WordPress.org Credits API endpoint. * - * @package WordPressdotorg\API\Credits - */ - -/* * This is a standalone, unauthenticated, stateless API endpoint: WordPress is not loaded, * so request data is never slashed, and there is no session or nonce infrastructure. * * phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.MissingUnslash + * + * @package WordPressdotorg\API\Credits */ $api_root = dirname( dirname( __DIR__ ) ); diff --git a/api.wordpress.org/public_html/core/importers/1.0/index.php b/api.wordpress.org/public_html/core/importers/1.0/index.php index c544ca86c8..8bd2df4855 100644 --- a/api.wordpress.org/public_html/core/importers/1.0/index.php +++ b/api.wordpress.org/public_html/core/importers/1.0/index.php @@ -2,14 +2,12 @@ /** * WordPress.org Importers API endpoint. * - * @package WordPressdotorg\API\Importers - */ - -/* * This is a standalone, unauthenticated, stateless API endpoint: WordPress is not loaded, * so request data is never slashed, and there is no session or nonce infrastructure. * * phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.MissingUnslash + * + * @package WordPressdotorg\API\Importers */ // Allow playground access. From ddf941cae609aa87832178d4de8d02e78835a8c7 Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Wed, 12 Aug 2026 21:01:50 -0500 Subject: [PATCH 4/5] Core APIs: Add file docblocks carrying the phpcs justifications. Co-Authored-By: Claude Fable 5 --- .../public_html/core/serve-happy/1.0/include.php | 10 +++++++--- .../public_html/core/serve-happy/1.0/index.php | 10 +++++++--- api.wordpress.org/public_html/patterns/1.0/index.php | 12 +++++++----- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/api.wordpress.org/public_html/core/serve-happy/1.0/include.php b/api.wordpress.org/public_html/core/serve-happy/1.0/include.php index 966a4f0706..481bd6722f 100644 --- a/api.wordpress.org/public_html/core/serve-happy/1.0/include.php +++ b/api.wordpress.org/public_html/core/serve-happy/1.0/include.php @@ -1,13 +1,17 @@ Date: Wed, 12 Aug 2026 21:31:58 -0500 Subject: [PATCH 5/5] Core APIs: Anchor validation regexes and fix the bail() reason phrase. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCRE's $ matches before a trailing newline, so "HTTP/1.1\n" passed the protocol validation only to be rejected by header(), and a trailing newline slipped through the JSONP callback and credits version patterns — all now anchored with \z (credits keeps its deliberate trailing-junk trim, made newline-safe with /s). bail()'s reason-phrase fallback was dead code: the parameter defaulted to false, which null coalescing never replaces, so every error response sent an empty reason phrase — it now defaults to null and gets a proper docblock. Co-Authored-By: Claude Fable 5 --- .../public_html/core/browse-happy/1.0/index.php | 2 +- .../public_html/core/credits/index.php | 4 ++-- .../public_html/core/serve-happy/1.0/index.php | 15 +++++++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/api.wordpress.org/public_html/core/browse-happy/1.0/index.php b/api.wordpress.org/public_html/core/browse-happy/1.0/index.php index f7651bc488..0f9c624f7f 100644 --- a/api.wordpress.org/public_html/core/browse-happy/1.0/index.php +++ b/api.wordpress.org/public_html/core/browse-happy/1.0/index.php @@ -15,7 +15,7 @@ // A JSONP callback is a plain JavaScript identifier; anything else is discarded. $jsonp_filter_args = array( 'options' => array( - 'regexp' => '/^[a-zA-Z_][a-zA-Z0-9_]*$/', + 'regexp' => '/^[a-zA-Z_][a-zA-Z0-9_]*\z/', 'default' => '', ), 'flags' => FILTER_REQUIRE_SCALAR, diff --git a/api.wordpress.org/public_html/core/credits/index.php b/api.wordpress.org/public_html/core/credits/index.php index 50c683e2c5..30cbb05340 100644 --- a/api.wordpress.org/public_html/core/credits/index.php +++ b/api.wordpress.org/public_html/core/credits/index.php @@ -43,7 +43,7 @@ function like_escape( $text ) { if ( ! empty( $_GET['version'] ) ) { $version = preg_replace( - '/^([.0-9]+).*/', + '/^([.0-9]+).*/s', '$1', filter_var( $_GET['version'], @@ -68,7 +68,7 @@ function like_escape( $text ) { FILTER_VALIDATE_REGEXP, array( 'options' => array( - 'regexp' => '/^[A-Za-z0-9_-]+$/', + 'regexp' => '/^[A-Za-z0-9_-]+\z/', 'default' => '', ), ) diff --git a/api.wordpress.org/public_html/core/serve-happy/1.0/index.php b/api.wordpress.org/public_html/core/serve-happy/1.0/index.php index 43eac4c25c..8e13d3a54a 100644 --- a/api.wordpress.org/public_html/core/serve-happy/1.0/index.php +++ b/api.wordpress.org/public_html/core/serve-happy/1.0/index.php @@ -26,15 +26,22 @@ ) ); -// Output functions -function bail( $error_code, $error_text, $http_code = 400, $http_code_text = false ) { +/** + * Sends an error response and halts. + * + * @param string $error_code Machine-readable error code. + * @param string $error_text Human-readable error description. + * @param int $http_code Optional. HTTP status code. Default 400. + * @param string|null $http_code_text Optional. HTTP status reason phrase. Default derived from the status code. + */ +function bail( $error_code, $error_text, $http_code = 400, $http_code_text = null ) { // Only a well-formed protocol version is echoed back into the status header. $server_protocol = filter_var( $_SERVER['SERVER_PROTOCOL'] ?? '', FILTER_VALIDATE_REGEXP, array( 'options' => array( - 'regexp' => '#^HTTP/[0-9]+(\.[0-9]+)?$#', + 'regexp' => '#^HTTP/[0-9]+(\.[0-9]+)?\z#', 'default' => 'HTTP/1.1', ), ) @@ -66,7 +73,7 @@ function output_response( $data ) { FILTER_VALIDATE_REGEXP, array( 'options' => array( - 'regexp' => '/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/', + 'regexp' => '/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*\z/', 'default' => '', ), 'flags' => FILTER_REQUIRE_SCALAR,