Skip to content

Commit c9e6b98

Browse files
committed
XML-RPC: Improve error messages for unprivileged users.
Add specific permission checks to avoid ambiguous failure messages. Props zieladam, peterwilsoncc, xknown, whyisjake. git-svn-id: https://develop.svn.wordpress.org/trunk@49380 602fd350-edb4-49c9-b593-d223f7449a82
1 parent ed2b1a4 commit c9e6b98

File tree

2 files changed

+186
-14
lines changed

2 files changed

+186
-14
lines changed

Diff for: src/wp-includes/class-wp-xmlrpc-server.php

+15
Original file line numberDiff line numberDiff line change
@@ -3876,6 +3876,21 @@ public function wp_newComment( $args ) {
38763876
return new IXR_Error( 403, __( 'Sorry, comments are closed for this item.' ) );
38773877
}
38783878

3879+
if (
3880+
'publish' === get_post_status( $post_id ) &&
3881+
! current_user_can( 'edit_post', $post_id ) &&
3882+
post_password_required( $post_id )
3883+
) {
3884+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) );
3885+
}
3886+
3887+
if (
3888+
'private' === get_post_status( $post_id ) &&
3889+
! current_user_can( 'read_post', $post_id )
3890+
) {
3891+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) );
3892+
}
3893+
38793894
$comment = array(
38803895
'comment_post_ID' => $post_id,
38813896
'comment_content' => trim( $content_struct['content'] ),

Diff for: tests/phpunit/tests/xmlrpc/wp/newComment.php

+171-14
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,45 @@
66
class Tests_XMLRPC_wp_newComment extends WP_XMLRPC_UnitTestCase {
77

88
/**
9-
* Post object for shared fixture.
9+
* Array of posts.
1010
*
11-
* @var WP_Post
11+
* @var WP_Post[]
1212
*/
13-
public static $post;
13+
public static $posts;
14+
15+
/**
16+
* User IDs.
17+
*
18+
* Array of user IDs keyed by role.
19+
*
20+
* @var int[]
21+
*/
22+
public static $user_ids;
1423

1524
public static function wpSetUpBeforeClass( $factory ) {
16-
self::make_user_by_role( 'administrator' );
17-
self::$post = $factory->post->create_and_get();
25+
self::$user_ids = array(
26+
'administrator' => self::make_user_by_role( 'administrator' ),
27+
'contributor' => self::make_user_by_role( 'contributor' ),
28+
);
29+
self::$posts['publish'] = $factory->post->create_and_get();
30+
self::$posts['password'] = $factory->post->create_and_get(
31+
array(
32+
'post_password' => 'xmlrpc',
33+
'post_author' => self::$user_ids['administrator'],
34+
)
35+
);
36+
self::$posts['private'] = $factory->post->create_and_get(
37+
array(
38+
'post_status' => 'private',
39+
'post_author' => self::$user_ids['administrator'],
40+
)
41+
);
42+
self::$posts['private_contributor'] = $factory->post->create_and_get(
43+
array(
44+
'post_status' => 'private',
45+
'post_author' => self::$user_ids['contributor'],
46+
)
47+
);
1848
}
1949

2050
function test_valid_comment() {
@@ -23,7 +53,7 @@ function test_valid_comment() {
2353
1,
2454
'administrator',
2555
'administrator',
26-
self::$post->ID,
56+
self::$posts['publish']->ID,
2757
array(
2858
'content' => rand_str( 100 ),
2959
),
@@ -39,7 +69,7 @@ function test_empty_comment() {
3969
1,
4070
'administrator',
4171
'administrator',
42-
self::$post->ID,
72+
self::$posts['publish']->ID,
4373
array(
4474
'content' => '',
4575
),
@@ -59,7 +89,7 @@ public function test_empty_content_multiple_spaces() {
5989
1,
6090
'administrator',
6191
'administrator',
62-
self::$post->ID,
92+
self::$posts['publish']->ID,
6393
array(
6494
'content' => ' ',
6595
),
@@ -79,7 +109,7 @@ public function test_valid_comment_0_content() {
79109
1,
80110
'administrator',
81111
'administrator',
82-
self::$post->ID,
112+
self::$posts['publish']->ID,
83113
array(
84114
'content' => '0',
85115
),
@@ -99,7 +129,7 @@ public function test_valid_comment_allow_empty_content() {
99129
1,
100130
'administrator',
101131
'administrator',
102-
self::$post->ID,
132+
self::$posts['publish']->ID,
103133
array(
104134
'content' => ' ',
105135
),
@@ -139,7 +169,7 @@ function test_new_comment_duplicated() {
139169
1,
140170
'administrator',
141171
'administrator',
142-
self::$post->ID,
172+
self::$posts['publish']->ID,
143173
array(
144174
'content' => rand_str( 100 ),
145175
),
@@ -168,7 +198,7 @@ function test_allowed_anon_comments() {
168198
1,
169199
'',
170200
'',
171-
self::$post->ID,
201+
self::$posts['publish']->ID,
172202
array(
173203
'author' => 'WordPress',
174204
'author_email' => 'noreply@wordpress.org',
@@ -193,7 +223,7 @@ function test_anon_comments_require_email() {
193223
1,
194224
'',
195225
'',
196-
self::$post->ID,
226+
self::$posts['publish']->ID,
197227
array(
198228
'author' => 'WordPress',
199229
'author_email' => 'noreply at wordpress.org',
@@ -218,7 +248,7 @@ function test_username_avoids_anon_flow() {
218248
1,
219249
'administrator',
220250
'administrator',
221-
self::$post->ID,
251+
self::$posts['publish']->ID,
222252
array(
223253
'author' => 'WordPress',
224254
'author_email' => 'noreply at wordpress.org',
@@ -232,4 +262,131 @@ function test_username_avoids_anon_flow() {
232262

233263
$this->assertSame( $user_id, (int) $comment->user_id );
234264
}
265+
266+
/**
267+
* Ensure users can only comment on posts they're permitted to access.
268+
*
269+
* @dataProvider data_comments_observe_post_permissions
270+
*
271+
* @param string $post_key Post identifier from the self::$posts array.
272+
* @param string $username Username leaving comment.
273+
* @param bool $expected Expected result. True: successfull comment. False: Refused comment.
274+
* @param string $anon_callback Optional. Allow anonymous comment callback. Default __return_false.
275+
*/
276+
function test_comments_observe_post_permissions( $post_key, $username, $expected, $anon_callback = '__return_false' ) {
277+
add_filter( 'xmlrpc_allow_anonymous_comments', $anon_callback );
278+
279+
$comment_args = array(
280+
1,
281+
$username,
282+
$username,
283+
self::$posts[ $post_key ]->ID,
284+
array(
285+
'author' => 'WordPress',
286+
'author_email' => 'noreply@wordpress.org',
287+
'content' => 'Test Comment',
288+
),
289+
);
290+
291+
$result = $this->myxmlrpcserver->wp_newComment( $comment_args );
292+
if ( $expected ) {
293+
$this->assertInternalType( 'int', $result );
294+
return;
295+
}
296+
297+
$this->assertIXRError( $result );
298+
$this->assertSame( 403, $result->code );
299+
}
300+
301+
/**
302+
* Data provider for test_comments_observe_post_permissions.
303+
*
304+
* @return array[] {
305+
* @type string Post identifier from the self::$posts array.
306+
* @type string Username leaving comment.
307+
* @type bool Expected result. True: successfull comment. False: Refused comment.
308+
* @type string Optional. Allow anonymous comment callback. Default __return_false.
309+
* }
310+
*/
311+
function data_comments_observe_post_permissions() {
312+
return array(
313+
// 0: Post author, password protected public post.
314+
array(
315+
'password',
316+
'administrator',
317+
true,
318+
),
319+
// 1: Low privileged non-author, password protected public post.
320+
array(
321+
'password',
322+
'contributor',
323+
false,
324+
),
325+
// 2: Anonymous user, password protected public post.
326+
array(
327+
'password',
328+
'', // Anonymous user.
329+
false,
330+
),
331+
// 3: Anonymous user, anon comments allowed, password protected public post.
332+
array(
333+
'password',
334+
'', // Anonymous user.
335+
false,
336+
'__return_true',
337+
),
338+
339+
// 4: Post author, private post.
340+
array(
341+
'private',
342+
'administrator',
343+
true,
344+
),
345+
// 5: Low privileged non-author, private post.
346+
array(
347+
'private',
348+
'contributor',
349+
false,
350+
),
351+
// 6: Anonymous user, private post.
352+
array(
353+
'private',
354+
'', // Anonymous user.
355+
false,
356+
),
357+
// 7: Anonymous user, anon comments allowed, private post.
358+
array(
359+
'private',
360+
'', // Anonymous user.
361+
false,
362+
'__return_true',
363+
),
364+
365+
// 8: High privileged non-author, private post.
366+
array(
367+
'private_contributor',
368+
'administrator',
369+
true,
370+
),
371+
// 9: Low privileged author, private post.
372+
array(
373+
'private_contributor',
374+
'contributor',
375+
true,
376+
),
377+
// 10: Anonymous user, private post.
378+
array(
379+
'private_contributor',
380+
'', // Anonymous user.
381+
false,
382+
),
383+
// 11: Anonymous user, anon comments allowed, private post.
384+
array(
385+
'private_contributor',
386+
'', // Anonymous user.
387+
false,
388+
'__return_true',
389+
),
390+
);
391+
}
235392
}

0 commit comments

Comments
 (0)