Skip to content

Commit 1f4416c

Browse files
committed
REST API: Prevent inaccessible attachments from being embedded in posts.
When an attachment is used by multiple posts, it could be included in `_embed` for a published post even if its `post_parent` is a draft. This commit avoids embedding attachments that are not viewable in this context. Props bor0. Fixes #64183. git-svn-id: https://develop.svn.wordpress.org/trunk@61996 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 0d03283 commit 1f4416c

2 files changed

Lines changed: 106 additions & 1 deletion

File tree

src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2294,7 +2294,7 @@ protected function prepare_links( $post ) {
22942294

22952295
// If we have a featured media, add that.
22962296
$featured_media = get_post_thumbnail_id( $post->ID );
2297-
if ( $featured_media ) {
2297+
if ( $featured_media && ( 'publish' === get_post_status( $featured_media ) || current_user_can( 'read_post', $featured_media ) ) ) {
22982298
$image_url = rest_url( rest_get_route_for_post( $featured_media ) );
22992299

23002300
$links['https://api.w.org/featuredmedia'] = array(

tests/phpunit/tests/rest-api/rest-posts-controller.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3330,6 +3330,111 @@ public function test_create_update_post_with_featured_media() {
33303330
$this->assertSame( 0, (int) get_post_thumbnail_id( $new_post->ID ) );
33313331
}
33323332

3333+
/**
3334+
* Data provider for featured media link permission tests.
3335+
*
3336+
* @return array
3337+
*/
3338+
public function data_featured_media_link_permissions() {
3339+
return array(
3340+
'unauthenticated user with draft parent attachment' => array(
3341+
'attachment_parent_status' => 'draft',
3342+
'attachment_status' => 'inherit',
3343+
'user_id' => 0,
3344+
'expect_link' => false,
3345+
),
3346+
'authenticated editor with draft parent attachment' => array(
3347+
'attachment_parent_status' => 'draft',
3348+
'attachment_status' => 'inherit',
3349+
'user_id' => 'editor',
3350+
'expect_link' => true,
3351+
),
3352+
'unauthenticated user with published attachment' => array(
3353+
'attachment_parent_status' => null,
3354+
'attachment_status' => 'publish',
3355+
'user_id' => 0,
3356+
'expect_link' => true,
3357+
),
3358+
);
3359+
}
3360+
3361+
/**
3362+
* Tests that featured media links respect attachment permissions.
3363+
*
3364+
* @ticket 64183
3365+
* @dataProvider data_featured_media_link_permissions
3366+
*
3367+
* @param string|null $attachment_parent_status Status of the attachment's parent post, or null for no parent.
3368+
* @param string $attachment_status Status to set on the attachment.
3369+
* @param int|string $user_id User ID (0 for unauthenticated) or 'editor' for editor role.
3370+
* @param bool $expect_link Whether the featured media link should be included.
3371+
*/
3372+
public function test_get_item_featured_media_link_permissions( $attachment_parent_status, $attachment_status, $user_id, $expect_link ) {
3373+
$file = DIR_TESTDATA . '/images/canola.jpg';
3374+
3375+
// Create attachment parent if needed.
3376+
$parent_post_id = 0;
3377+
if ( null !== $attachment_parent_status ) {
3378+
$parent_post_id = self::factory()->post->create(
3379+
array(
3380+
'post_title' => 'Parent Post',
3381+
'post_status' => $attachment_parent_status,
3382+
)
3383+
);
3384+
}
3385+
3386+
// Create attachment.
3387+
$attachment_id = self::factory()->attachment->create_object(
3388+
$file,
3389+
$parent_post_id,
3390+
array(
3391+
'post_mime_type' => 'image/jpeg',
3392+
)
3393+
);
3394+
3395+
// Set attachment status if different from default.
3396+
if ( 'publish' === $attachment_status ) {
3397+
wp_update_post(
3398+
array(
3399+
'ID' => $attachment_id,
3400+
'post_status' => 'publish',
3401+
)
3402+
);
3403+
}
3404+
3405+
// Create published post with featured media.
3406+
$published_post_id = self::factory()->post->create(
3407+
array(
3408+
'post_title' => 'Published Post',
3409+
'post_status' => 'publish',
3410+
)
3411+
);
3412+
set_post_thumbnail( $published_post_id, $attachment_id );
3413+
3414+
// Set current user.
3415+
if ( 'editor' === $user_id ) {
3416+
wp_set_current_user( self::$editor_id );
3417+
} else {
3418+
wp_set_current_user( $user_id );
3419+
}
3420+
3421+
// Make request.
3422+
$request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $published_post_id ) );
3423+
$response = rest_get_server()->dispatch( $request );
3424+
$links = $response->get_links();
3425+
3426+
// Assert link presence based on expectation.
3427+
if ( $expect_link ) {
3428+
$this->assertArrayHasKey( 'https://api.w.org/featuredmedia', $links );
3429+
$this->assertSame(
3430+
rest_url( '/wp/v2/media/' . $attachment_id ),
3431+
$links['https://api.w.org/featuredmedia'][0]['href']
3432+
);
3433+
} else {
3434+
$this->assertArrayNotHasKey( 'https://api.w.org/featuredmedia', $links );
3435+
}
3436+
}
3437+
33333438
public function test_create_post_invalid_author() {
33343439
wp_set_current_user( self::$editor_id );
33353440

0 commit comments

Comments
 (0)