From 7cd5ecd667177828eeb801c74f4592fe744dd2cf Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Mon, 24 May 2021 02:17:36 +0000 Subject: [PATCH] XML-RPC: Set HTTP status code in accordance with the spec. When the XML-RPC endpoint is enabled, always return a HTTP `200 OK` status code in accordance with the XML-RPC specification. Continue to return an HTTP `405 Method Not Allowed` status code when the endpoint is disabled. Props ariskataoka, johnbillion. Fixes #52958. git-svn-id: https://develop.svn.wordpress.org/trunk@50954 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/IXR/class-IXR-server.php | 4 - src/wp-includes/class-wp-xmlrpc-server.php | 113 ++++++++++++++------- tests/phpunit/tests/xmlrpc/basic.php | 22 ++-- 3 files changed, 89 insertions(+), 50 deletions(-) diff --git a/src/wp-includes/IXR/class-IXR-server.php b/src/wp-includes/IXR/class-IXR-server.php index 2ead9b783c..2ca657b2d4 100644 --- a/src/wp-includes/IXR/class-IXR-server.php +++ b/src/wp-includes/IXR/class-IXR-server.php @@ -130,10 +130,6 @@ EOD; $error = new IXR_Error($error, $message); } - if ( function_exists( 'status_header' ) ) { - status_header( $error->code ); - } - $this->output($error->getXml()); } diff --git a/src/wp-includes/class-wp-xmlrpc-server.php b/src/wp-includes/class-wp-xmlrpc-server.php index 45908d695a..845ff2f0bf 100644 --- a/src/wp-includes/class-wp-xmlrpc-server.php +++ b/src/wp-includes/class-wp-xmlrpc-server.php @@ -14,7 +14,7 @@ * options, etc. * * As of WordPress 3.5.0, XML-RPC is enabled by default. It can be disabled - * via the {@see 'xmlrpc_enabled'} filter found in wp_xmlrpc_server::login(). + * via the {@see 'xmlrpc_enabled'} filter found in wp_xmlrpc_server::set_is_enabled(). * * @since 1.5.0 * @@ -49,6 +49,13 @@ class wp_xmlrpc_server extends IXR_Server { */ protected $auth_failed = false; + /** + * Flags that XML-RPC is enabled + * + * @var bool + */ + private $is_enabled; + /** * Registers all of the XMLRPC methods that XMLRPC server understands. * @@ -164,6 +171,51 @@ class wp_xmlrpc_server extends IXR_Server { * @param string[] $methods An array of XML-RPC methods, keyed by their methodName. */ $this->methods = apply_filters( 'xmlrpc_methods', $this->methods ); + + $this->set_is_enabled(); + } + + /** + * Set wp_xmlrpc_server::$is_enabled property. + * + * Determine whether the xmlrpc server is enabled on this WordPress install + * and set the is_enabled property accordingly. + * + * @since 5.7.3 + */ + private function set_is_enabled() { + /* + * Respect old get_option() filters left for back-compat when the 'enable_xmlrpc' + * option was deprecated in 3.5.0. Use the 'xmlrpc_enabled' hook instead. + */ + $is_enabled = apply_filters( 'pre_option_enable_xmlrpc', false ); + if ( false === $is_enabled ) { + $is_enabled = apply_filters( 'option_enable_xmlrpc', true ); + } + + /** + * Filters whether XML-RPC methods requiring authentication are enabled. + * + * Contrary to the way it's named, this filter does not control whether XML-RPC is *fully* + * enabled, rather, it only controls whether XML-RPC methods requiring authentication - such + * as for publishing purposes - are enabled. + * + * Further, the filter does not control whether pingbacks or other custom endpoints that don't + * require authentication are enabled. This behavior is expected, and due to how parity was matched + * with the `enable_xmlrpc` UI option the filter replaced when it was introduced in 3.5. + * + * To disable XML-RPC methods that require authentication, use: + * + * add_filter( 'xmlrpc_enabled', '__return_false' ); + * + * For more granular control over all XML-RPC methods and requests, see the {@see 'xmlrpc_methods'} + * and {@see 'xmlrpc_element_limit'} hooks. + * + * @since 3.5.0 + * + * @param bool $is_enabled Whether XML-RPC is enabled. Default true. + */ + $this->is_enabled = apply_filters( 'xmlrpc_enabled', $is_enabled ); } /** @@ -231,40 +283,7 @@ class wp_xmlrpc_server extends IXR_Server { * @return WP_User|false WP_User object if authentication passed, false otherwise */ public function login( $username, $password ) { - /* - * Respect old get_option() filters left for back-compat when the 'enable_xmlrpc' - * option was deprecated in 3.5.0. Use the 'xmlrpc_enabled' hook instead. - */ - $enabled = apply_filters( 'pre_option_enable_xmlrpc', false ); - if ( false === $enabled ) { - $enabled = apply_filters( 'option_enable_xmlrpc', true ); - } - - /** - * Filters whether XML-RPC methods requiring authentication are enabled. - * - * Contrary to the way it's named, this filter does not control whether XML-RPC is *fully* - * enabled, rather, it only controls whether XML-RPC methods requiring authentication - such - * as for publishing purposes - are enabled. - * - * Further, the filter does not control whether pingbacks or other custom endpoints that don't - * require authentication are enabled. This behavior is expected, and due to how parity was matched - * with the `enable_xmlrpc` UI option the filter replaced when it was introduced in 3.5. - * - * To disable XML-RPC methods that require authentication, use: - * - * add_filter( 'xmlrpc_enabled', '__return_false' ); - * - * For more granular control over all XML-RPC methods and requests, see the {@see 'xmlrpc_methods'} - * and {@see 'xmlrpc_element_limit'} hooks. - * - * @since 3.5.0 - * - * @param bool $enabled Whether XML-RPC is enabled. Default true. - */ - $enabled = apply_filters( 'xmlrpc_enabled', $enabled ); - - if ( ! $enabled ) { + if ( ! $this->is_enabled ) { $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) ); return false; } @@ -335,6 +354,30 @@ class wp_xmlrpc_server extends IXR_Server { } } + /** + * Send error response to client. + * + * Send an XML error response to the client. If the endpoint is enabled + * an HTTP 200 response is always sent per the XML-RPC specification. + * + * @since 5.7.3 + * + * @param IXR_Error|string $error Error code or an error object. + * @param false $message Error message. Optional. + */ + public function error( $error, $message = false ) { + // Accepts either an error object or an error code and message + if ( $message && ! is_object( $error ) ) { + $error = new IXR_Error( $error, $message ); + } + + if ( ! $this->is_enabled ) { + status_header( $error->code ); + } + + $this->output( $error->getXml() ); + } + /** * Retrieve custom fields for post. * diff --git a/tests/phpunit/tests/xmlrpc/basic.php b/tests/phpunit/tests/xmlrpc/basic.php index e167d395be..a3faff0c41 100644 --- a/tests/phpunit/tests/xmlrpc/basic.php +++ b/tests/phpunit/tests/xmlrpc/basic.php @@ -16,24 +16,15 @@ class Tests_XMLRPC_Basic extends WP_XMLRPC_UnitTestCase { $this->assertSame( 403, $result->code ); } - function test_disabled() { - add_filter( 'xmlrpc_enabled', '__return_false' ); - - $result = $this->myxmlrpcserver->wp_getOptions( array( 1, 'username', 'password' ) ); - - $this->assertIXRError( $result ); - $this->assertSame( 405, $result->code ); - } - function test_login_pass_ok() { - $user_id = $this->make_user_by_role( 'subscriber' ); + $this->make_user_by_role( 'subscriber' ); $this->assertTrue( $this->myxmlrpcserver->login_pass_ok( 'subscriber', 'subscriber' ) ); $this->assertInstanceOf( 'WP_User', $this->myxmlrpcserver->login( 'subscriber', 'subscriber' ) ); } function test_login_pass_bad() { - $user_id = $this->make_user_by_role( 'subscriber' ); + $this->make_user_by_role( 'subscriber' ); $this->assertFalse( $this->myxmlrpcserver->login_pass_ok( 'username', 'password' ) ); $this->assertFalse( $this->myxmlrpcserver->login( 'username', 'password' ) ); @@ -117,4 +108,13 @@ class Tests_XMLRPC_Basic extends WP_XMLRPC_UnitTestCase { $this->assertXmlStringEqualsXmlString( $return, $value->getXML() ); } + + function test_disabled() { + add_filter( 'xmlrpc_enabled', '__return_false' ); + $testcase_xmlrpc_server = new wp_xmlrpc_server(); + $result = $testcase_xmlrpc_server->wp_getOptions( array( 1, 'username', 'password' ) ); + + $this->assertIXRError( $result ); + $this->assertSame( 405, $result->code ); + } }