Verified Commit c6cc68a2 authored by Lee Rowlands's avatar Lee Rowlands
Browse files

fix: #3593390 Media Preview Endpoint Leaks Labels Of Non-Viewable Media By UUID

By: larowlan
By: cantina_security
By: damienmckenna
By: mohit_aghera
By: catch
By: smustgrave
parent ab8521c6
Loading
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\filter\FilterFormatInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -25,6 +26,8 @@
 */
class MediaFilterController implements ContainerInjectionInterface {

  use StringTranslationTrait;

  /**
   * The renderer service.
   *
@@ -114,7 +117,8 @@ public function preview(Request $request, FilterFormatInterface $filter_format)
    // in an ARIA label.
    $headers = [];
    if ($media = $this->entityRepository->loadEntityByUuid('media', $uuid)) {
      $headers['Drupal-Media-Label'] = $this->entityRepository->getTranslationFromContext($media)->label();
      $media = $this->entityRepository->getTranslationFromContext($media);
      $headers['Drupal-Media-Label'] = $media->access('view label') ? $media->label() : $this->t('Media @id', ['@id' => $media->id()]);
    }

    // Note that we intentionally do not use:
+82 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Tests\media\Kernel;

use Drupal\Core\Session\AnonymousUserSession;
use Drupal\Core\Url;
use Drupal\filter\Entity\FilterFormat;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
use Symfony\Component\HttpFoundation\Response;

/**
 * Tests that media preview route follows the media's access control.
 *
 * @coversDefaultClass \Drupal\media\Controller\MediaFilterController
 * @group media
 */
class MediaFilterControllerTest extends MediaEmbedFilterTestBase {

  /**
   * The text format used in the preview request.
   */
  protected FilterFormat $filterFormat;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->installEntitySchema('user');
    $this->installConfig(['user']);

    $this->filterFormat = FilterFormat::create([
      'format' => 'media_preview_test',
      'name' => 'Media preview test',
      'roles' => [RoleInterface::ANONYMOUS_ID],
      'filters' => [
        'media_embed' => ['status' => TRUE],
      ],
    ]);
    $this->filterFormat->save();
    Role::load(RoleInterface::ANONYMOUS_ID)->grantPermission('use text format media_preview_test')->save();
    $this->container->get('current_user')->setAccount(new AnonymousUserSession());
  }

  /**
   * Tests that media access is validated on media filter preview request.
   */
  public function testPreview(): void {
    $text = $this->createEmbedCode([
      'data-entity-type' => 'media',
      'data-entity-uuid' => static::EMBEDDED_ENTITY_UUID,
    ]);

    $url = Url::fromRoute('media.filter.preview', [
      'filter_format' => $this->filterFormat->id(),
    ], [
      'query' => [
        'text' => $text,
        'uuid' => static::EMBEDDED_ENTITY_UUID,
      ],
    ]);
    $output = $this->drupalGet(
      $url,
      [],
      ['X-Drupal-MediaPreview-CSRF-Token' => 'placeholder-token'],
    );

    $session = $this->getSession();
    $this->assertSame(Response::HTTP_OK, $session->getStatusCode());
    $this->assertStringNotContainsString('<drupal-media', $output);
    $media = \Drupal::service('entity.repository')->loadEntityByUuid('media', static::EMBEDDED_ENTITY_UUID);
    $this->assertFalse($media->access('view label', \Drupal::currentUser()));
    self::assertSame(
      \sprintf('Media %s', $media->id()),
      $session->getResponseHeader('Drupal-Media-Label'),
    );
  }

}