Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions includes/corrections/class-corrections.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ public static function rest_save_corrections( WP_REST_Request $request ) {
return rest_ensure_response( new WP_Error( 'invalid_post_id', 'Invalid post ID.', [ 'status' => 400 ] ) );
}

if ( ! current_user_can( 'edit_post', $post_id ) ) {
return rest_ensure_response( new WP_Error( 'unauthorized', 'You do not have permission to edit this post.', [ 'status' => 403 ] ) );
}

$existing_corrections = self::get_corrections( $post_id );
$existing_ids = wp_list_pluck( $existing_corrections, 'ID' );

Expand All @@ -243,6 +247,10 @@ public static function rest_save_corrections( WP_REST_Request $request ) {

// ID will be null if it's a new correction.
if ( ! empty( $correction_id ) ) {
// Verify the correction belongs to this post.
if ( ! in_array( $correction_id, $existing_ids, true ) ) {
return rest_ensure_response( new WP_Error( 'invalid_correction', 'The correction does not belong to this post.', [ 'status' => 400 ] ) );
}
// Update existing correction.
self::update_correction( $post_id, $correction_id, $correction );
$processed_ids[] = $correction_id;
Expand Down
88 changes: 88 additions & 0 deletions tests/unit-tests/corrections.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ class Test_Corrections extends WP_UnitTestCase {
*/
protected static $post_id;

/**
* Holds an editor user ID.
*
* @var int
*/
protected static $editor_id;

/**
* Set up test fixtures.
*/
Expand All @@ -29,6 +36,9 @@ public function set_up() {

Corrections::init();

self::$editor_id = $this->factory()->user->create( [ 'role' => 'editor' ] );
wp_set_current_user( self::$editor_id );

self::$post_id = $this->factory()->post->create( [ 'post_type' => 'post' ] );
}

Expand Down Expand Up @@ -696,4 +706,82 @@ function( $types ) {
$filtered_types = Corrections::get_supported_post_types();
$this->assertContains( 'test_post_type', $filtered_types );
}

/**
* Test that an author cannot save corrections for another user's post.
*
* @covers \Newspack\Corrections::rest_save_corrections
*/
public function test_cannot_save_corrections_for_other_users_post() {
$author_id = $this->factory()->user->create( [ 'role' => 'author' ] );
wp_set_current_user( $author_id );

$corrections = [
[
'id' => null,
'content' => 'IDOR injected correction',
'type' => 'correction',
'date' => current_time( 'mysql' ),
'priority' => 'high',
],
];

$request = new WP_REST_Request( WP_REST_Server::CREATABLE, '/' . NEWSPACK_API_NAMESPACE . '/corrections/' );
$request->set_param( 'post_id', self::$post_id );
$request->set_param( 'corrections', $corrections );

$response = Corrections::rest_save_corrections( $request );

$this->assertInstanceOf( 'WP_Error', $response, 'Authors should not be able to save corrections for posts they do not own.' );
$this->assertEquals( 'unauthorized', $response->get_error_code() );
$this->assertEquals( 403, $response->get_error_data()['status'] );
}

/**
* Test that a correction ID belonging to a different post cannot be updated via another post.
*
* @covers \Newspack\Corrections::rest_save_corrections
*/
public function test_cannot_update_correction_belonging_to_different_post() {
wp_set_current_user( self::$editor_id );

// Create a correction on the default post.
$correction_id = Corrections::add_correction(
self::$post_id,
[
'content' => 'Original correction',
'type' => 'correction',
'date' => current_time( 'mysql' ),
'priority' => 'low',
]
);

// Create a second post that the editor also owns.
$other_post_id = $this->factory()->post->create( [ 'post_author' => self::$editor_id ] );

// Attempt to update the first post's correction via the second post's endpoint.
$corrections = [
[
'id' => $correction_id,
'content' => 'Hijacked correction',
'type' => 'correction',
'date' => current_time( 'mysql' ),
'priority' => 'high',
],
];

$request = new WP_REST_Request( WP_REST_Server::CREATABLE, '/' . NEWSPACK_API_NAMESPACE . '/corrections/' );
$request->set_param( 'post_id', $other_post_id );
$request->set_param( 'corrections', $corrections );

$response = Corrections::rest_save_corrections( $request );

$this->assertInstanceOf( 'WP_Error', $response, 'Should not allow updating a correction that belongs to a different post.' );
$this->assertEquals( 'invalid_correction', $response->get_error_code() );
$this->assertEquals( 400, $response->get_error_data()['status'] );

// Verify the original correction was not modified.
$original = get_post( $correction_id );
$this->assertEquals( 'Original correction', $original->post_content );
}
}
Loading