CVE-2023-1979 (PHP, published 2023-05-08T17:15Z)
Description: value: The Web Stories for WordPress plugin supports the WordPress built-in functionality of protecting content with a password. The content is then only accessible to website visitors after entering the password. In WordPress, users with the "Author" role can create stories, but don't have the ability to edit password protected stories. The vulnerability allowed users with said role to bypass this permission check when trying to duplicate the protected story in the plugin's own dashboard, giving them access to the seemingly protected content. We recommend upgrading to version 1.32 or beyond commit  ad49781c2a35c5c92ef704d4b621ab4e5cb77d68 https://github.com/GoogleForCreators/web-stories-wp/commit/ad49781c2a35c5c92ef704d4b621ab4e5cb77d68 

Severity: nan (CVSS v3 base score: 6.5)
Weakness: CWE-863 \u2014 Incorrect Authorization
The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
Repository: https://github.com/GoogleForCreators/web-stories-wp
Files changed:
includes/REST_API/Stories_Base_Controller.php
includes/REST_API/Stories_Controller.php
tests/phpunit/integration/tests/REST_API/Stories_Controller.php
Commit message: Merge pull request from GHSA-xcx6-4gm7-wrfm

* Update permission check when duplicating stories

Check for update instead of read permission

* Disallow view context for `style_presets` field

* Do not return `story_data` for password protected stories

Vulnerable code:
			$data['story_data'] = rest_sanitize_value_from_schema( $post_story_data, $schema['properties']['story_data'] );
		if ( ! $this->check_read_permission( $original_post ) ) {
			'context'     => [ 'view', 'edit' ],
	public function test_create_item_duplicate_id_permission(): void {

Fixed code:
			$data['story_data'] = post_password_required( $post ) ? (object) [] : rest_sanitize_value_from_schema( $post_story_data, $schema['properties']['story_data'] );
		if ( ! $this->check_update_permission( $original_post ) ) {
			'context'     => [ 'edit' ],
	/**
	 * @covers ::get_item
	 * @covers ::prepare_item_for_response
	 * @covers \Google\Web_Stories\REST_API\Stories_Base_Controller::prepare_item_for_response
	 */
	public function test_get_item_no_story_data_for_password_protected_post(): void {
		$this->controller->register_routes();

		$story = self::factory()->post->create(
			[
				'post_type'     => Story_Post_Type::POST_TYPE_SLUG,
				'post_status'   => 'publish',
				'post_password' => 'Top Secret',
				'post_author'   => self::$user_id,
			]
		);

		wp_set_current_user( self::$author_id );

		$request  = new WP_REST_Request( \WP_REST_Server::READABLE, '/web-stories/v1/web-story/' . $story );
		$response = rest_get_server()->dispatch( $request );

		$data = $response->get_data();

		$this->assertIsArray( $data );
		$this->assertArrayHasKey( 'story_data', $data );
		$this->assertIsObject( $data['story_data'] );
		$this->assertEmpty( (array) $data['story_data'] );
	}

	public function test_create_item_duplicate_id_no_permission_for_private_post(): void {

	/**
	 * @covers ::create_item
	 */
	public function test_create_item_duplicate_id_no_permission_for_password_protected_post(): void {
		$this->controller->register_routes();

		$unsanitized_content    = file_get_contents( WEB_STORIES_TEST_DATA_DIR . '/story_post_content.html' );
		$unsanitized_story_data = wp_json_encode( [ 'pages' => [] ] );
		$original_id            = self::factory()->post->create(
			[
				'post_type'             => Story_Post_Type::POST_TYPE_SLUG,
				'post_content'          => $unsanitized_content,
				'post_title'            => 'Example title',
				'post_excerpt'          => 'Example excerpt',
				'post_author'           => self::$user_id,
				'post_status'           => 'publish',
				'post_password'         => 'Top Secret',
				'post_content_filtered' => $unsanitized_story_data,
			]
		);

		$attachment_id = self::factory()->attachment->create_upload_object( WEB_STORIES_TEST_DATA_DIR . '/attachment.jpg' );

		$this->assertNotWPError( $attachment_id );

		set_post_thumbnail( $original_id, $attachment_id );

		wp_set_current_user( self::$author_id );
		$this->kses_int();

		$request = new WP_REST_Request( \WP_REST_Server::CREATABLE, '/web-stories/v1/web-story' );
		$request->set_body_params(
			[
				'original_id' => $original_id,
			]
		);

		$response = rest_get_server()->dispatch( $request );
		$this->assertErrorResponse( 'rest_cannot_create', $response, 403 );
	}