From e74994b2f47bf9a53633142e3e287abe7b84537e Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Tue, 8 Nov 2016 06:35:51 +0000 Subject: [PATCH] REST API: Respect unfiltered_html for HTML comment fields. Same as [39155], but for comments, natch. Props jnylen0. Fixes #38704, see #38609. git-svn-id: https://develop.svn.wordpress.org/trunk@39157 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-comments-controller.php | 8 +- .../rest-api/rest-comments-controller.php | 148 +++++++++++++++++- 2 files changed, 151 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php index e6f79203fb..3eb8737578 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php @@ -520,7 +520,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { */ $prepared_comment = apply_filters( 'rest_pre_insert_comment', $prepared_comment, $request ); - $comment_id = wp_insert_comment( $prepared_comment ); + $comment_id = wp_insert_comment( wp_filter_comment( wp_slash( (array) $prepared_comment ) ) ); if ( ! $comment_id ) { return new WP_Error( 'rest_comment_failed_create', __( 'Creating comment failed.' ), array( 'status' => 500 ) ); @@ -644,7 +644,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { return new WP_Error( $error_code, __( 'Comment field exceeds maximum length allowed.' ), array( 'status' => 400 ) ); } - $updated = wp_update_comment( $prepared_args ); + $updated = wp_update_comment( wp_slash( (array) $prepared_args ) ); if ( 0 === $updated ) { return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment failed.' ), array( 'status' => 500 ) ); @@ -995,9 +995,9 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { * the 'content.raw' properties of the Request object. */ if ( isset( $request['content'] ) && is_string( $request['content'] ) ) { - $prepared_comment['comment_content'] = wp_filter_kses( $request['content'] ); + $prepared_comment['comment_content'] = $request['content']; } elseif ( isset( $request['content']['raw'] ) && is_string( $request['content']['raw'] ) ) { - $prepared_comment['comment_content'] = wp_filter_kses( $request['content']['raw'] ); + $prepared_comment['comment_content'] = $request['content']['raw']; } if ( isset( $request['post'] ) ) { diff --git a/tests/phpunit/tests/rest-api/rest-comments-controller.php b/tests/phpunit/tests/rest-api/rest-comments-controller.php index a8526c893a..f409c431b7 100644 --- a/tests/phpunit/tests/rest-api/rest-comments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-comments-controller.php @@ -10,8 +10,9 @@ * @group restapi */ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase { - + protected static $superadmin_id; protected static $admin_id; + protected static $editor_id; protected static $subscriber_id; protected static $author_id; @@ -25,9 +26,16 @@ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase protected $endpoint; public static function wpSetUpBeforeClass( $factory ) { + self::$superadmin_id = $factory->user->create( array( + 'role' => 'administrator', + 'user_login' => 'superadmin', + ) ); self::$admin_id = $factory->user->create( array( 'role' => 'administrator', ) ); + self::$editor_id = $factory->user->create( array( + 'role' => 'editor', + ) ); self::$subscriber_id = $factory->user->create( array( 'role' => 'subscriber', ) ); @@ -79,6 +87,9 @@ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase public function setUp() { parent::setUp(); $this->endpoint = new WP_REST_Comments_Controller; + if ( is_multisite() ) { + update_site_option( 'site_admins', array( 'superadmin' ) ); + } } public function tearDown() { @@ -1880,6 +1891,141 @@ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase $this->assertErrorResponse( 'comment_content_column_length', $response, 400 ); } + public function verify_comment_roundtrip( $input = array(), $expected_output = array() ) { + // Create the comment + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->set_param( 'author_email', 'cbg@androidsdungeon.com' ); + $request->set_param( 'post', self::$post_id ); + foreach ( $input as $name => $value ) { + $request->set_param( $name, $value ); + } + $response = $this->server->dispatch( $request ); + $this->assertEquals( 201, $response->get_status() ); + $actual_output = $response->get_data(); + + // Compare expected API output to actual API output + $this->assertInternalType( 'array', $actual_output['content'] ); + $this->assertArrayHasKey( 'raw', $actual_output['content'] ); + $this->assertEquals( $expected_output['content']['raw'] , $actual_output['content']['raw'] ); + $this->assertEquals( $expected_output['content']['rendered'], trim( $actual_output['content']['rendered'] ) ); + $this->assertEquals( $expected_output['author_name'] , $actual_output['author_name'] ); + $this->assertEquals( $expected_output['author_user_agent'] , $actual_output['author_user_agent'] ); + + // Compare expected API output to WP internal values + $comment = get_comment( $actual_output['id'] ); + $this->assertEquals( $expected_output['content']['raw'] , $comment->comment_content ); + $this->assertEquals( $expected_output['author_name'] , $comment->comment_author ); + $this->assertEquals( $expected_output['author_user_agent'], $comment->comment_agent ); + + // Update the comment + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $actual_output['id'] ) ); + foreach ( $input as $name => $value ) { + $request->set_param( $name, $value ); + } + // FIXME at least one value must change, or update fails + // See https://core.trac.wordpress.org/ticket/38700 + $request->set_param( 'author_ip', '127.0.0.2' ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $actual_output = $response->get_data(); + + // Compare expected API output to actual API output + $this->assertEquals( $expected_output['content']['raw'] , $actual_output['content']['raw'] ); + $this->assertEquals( $expected_output['content']['rendered'], trim( $actual_output['content']['rendered'] ) ); + $this->assertEquals( $expected_output['author_name'] , $actual_output['author_name'] ); + $this->assertEquals( $expected_output['author_user_agent'] , $actual_output['author_user_agent'] ); + + // Compare expected API output to WP internal values + $comment = get_comment( $actual_output['id'] ); + $this->assertEquals( $expected_output['content']['raw'] , $comment->comment_content ); + $this->assertEquals( $expected_output['author_name'] , $comment->comment_author ); + $this->assertEquals( $expected_output['author_user_agent'], $comment->comment_agent ); + } + + public function test_comment_roundtrip_as_editor() { + wp_set_current_user( self::$editor_id ); + $this->assertEquals( ! is_multisite(), current_user_can( 'unfiltered_html' ) ); + $this->verify_comment_roundtrip( array( + 'content' => '\o/ ¯\_(ツ)_/¯', + 'author_name' => '\o/ ¯\_(ツ)_/¯', + 'author_user_agent' => '\o/ ¯\_(ツ)_/¯', + ), array( + 'content' => array( + 'raw' => '\o/ ¯\_(ツ)_/¯', + 'rendered' => '

\o/ ¯\_(ツ)_/¯

', + ), + 'author_name' => '\o/ ¯\_(ツ)_/¯', + 'author_user_agent' => '\o/ ¯\_(ツ)_/¯', + ) ); + } + + public function test_comment_roundtrip_as_editor_unfiltered_html() { + wp_set_current_user( self::$editor_id ); + if ( is_multisite() ) { + $this->assertFalse( current_user_can( 'unfiltered_html' ) ); + $this->verify_comment_roundtrip( array( + 'content' => '
div
strong ', + 'author_name' => '
div
strong ', + 'author_user_agent' => '
div
strong ', + ), array( + 'content' => array( + 'raw' => 'div strong oh noes', + 'rendered' => '

div strong oh noes

', + ), + 'author_name' => 'div strong', + 'author_user_agent' => 'div strong', + ) ); + } else { + $this->assertTrue( current_user_can( 'unfiltered_html' ) ); + $this->verify_comment_roundtrip( array( + 'content' => '
div
strong ', + 'author_name' => '
div
strong ', + 'author_user_agent' => '
div
strong ', + ), array( + 'content' => array( + 'raw' => '
div
strong ', + 'rendered' => "
div
\n

strong

", + ), + 'author_name' => 'div strong', + 'author_user_agent' => 'div strong', + ) ); + } + } + + public function test_comment_roundtrip_as_superadmin() { + wp_set_current_user( self::$superadmin_id ); + $this->assertTrue( current_user_can( 'unfiltered_html' ) ); + $this->verify_comment_roundtrip( array( + 'content' => '\\\&\\\ & &invalid; < < &lt;', + 'author_name' => '\\\&\\\ & &invalid; < < &lt;', + 'author_user_agent' => '\\\&\\\ & &invalid; < < &lt;', + ), array( + 'content' => array( + 'raw' => '\\\&\\\ & &invalid; < < &lt;', + 'rendered' => '

\\\&\\\ & &invalid; < < &lt;' . "\n

", + ), + 'author_name' => '\\\&\\\ & &invalid; < < &lt;', + 'author_user_agent' => '\\\&\\\ & &invalid; < < &lt;', + ) ); + } + + public function test_comment_roundtrip_as_superadmin_unfiltered_html() { + wp_set_current_user( self::$superadmin_id ); + $this->assertTrue( current_user_can( 'unfiltered_html' ) ); + $this->verify_comment_roundtrip( array( + 'content' => '
div
strong ', + 'author_name' => '
div
strong ', + 'author_user_agent' => '
div
strong ', + ), array( + 'content' => array( + 'raw' => '
div
strong ', + 'rendered' => "
div
\n

strong

", + ), + 'author_name' => 'div strong', + 'author_user_agent' => 'div strong', + ) ); + } + public function test_delete_item() { wp_set_current_user( self::$admin_id );