Commit 92fcdbfd authored by catch's avatar catch
Browse files

Issue #3035589 by slip, Berdir, wells, Neslee Canil Pinto, paulocs,...

Issue #3035589 by slip, Berdir, wells, Neslee Canil Pinto, paulocs, indigoxela, alexpott, Wim Leers: Website exception via GET with QUERY_STRING "?_format=..."
parent 108a6efd
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -3,6 +3,9 @@
namespace Drupal\Core\EventSubscriber;

use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Utility\Error;
@@ -138,10 +141,39 @@ public function onException(ExceptionEvent $event) {
    $event->setResponse($response);
  }

  /**
   * Handles all 4xx errors that aren't caught in other exception subscribers.
   *
   * For example, we catch 406s and 403s generated when handling unsupported
   * formats.
   *
   * @param \Symfony\Component\HttpKernel\Event\ExceptionEvent $event
   *   The event to process.
   */
  public function on4xx(ExceptionEvent $event) {
    $exception = $event->getThrowable();
    if ($exception && $exception instanceof HttpExceptionInterface && str_starts_with($exception->getStatusCode(), '4')) {
      $message = PlainTextOutput::renderFromHtml($exception->getMessage());
      // If the exception is cacheable, generate a cacheable response.
      if ($exception instanceof CacheableDependencyInterface) {
        $response = new CacheableResponse($message, $exception->getStatusCode(), ['Content-Type' => 'text/plain']);
        $response->addCacheableDependency($exception);
      }
      else {
        $response = new Response($message, $exception->getStatusCode(), ['Content-Type' => 'text/plain']);
      }

      $response->headers->add($exception->getHeaders());
      $event->setResponse($response);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    // Listen on 4xx exceptions late, but before the final exception handler.
    $events[KernelEvents::EXCEPTION][] = ['on4xx', -250];
    // Run as the final (very late) KernelEvents::EXCEPTION subscriber.
    $events[KernelEvents::EXCEPTION][] = ['onException', -256];
    return $events;
+15 −1
Original line number Diff line number Diff line
@@ -42,6 +42,19 @@ public function test405() {
    $this->assertEquals(Response::HTTP_METHOD_NOT_ALLOWED, $response->getStatusCode());
  }

  /**
   * Tests a route with a non-supported _format parameter.
   */
  public function test406() {
    $request = Request::create('/router_test/test2?_format=non_existent_format');

    /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $kernel */
    $kernel = \Drupal::getContainer()->get('http_kernel');
    $response = $kernel->handle($request);

    $this->assertEquals(Response::HTTP_NOT_ACCEPTABLE, $response->getStatusCode());
  }

  /**
   * Tests the exception handling for json and 403 status code.
   */
@@ -200,7 +213,8 @@ public function testExceptionEscaping() {
    // final exception subscriber, it is printed as partial HTML, and hence
    // escaped.
    $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('Content-type'));
    $this->assertStringStartsWith('The website encountered an unexpected error. Please try again later.<br><br><em class="placeholder">Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException</em>: Not acceptable format: json&lt;script&gt;alert(123);&lt;/script&gt; in <em class="placeholder">', $response->getContent());
    // cspell:ignore jsonalert
    $this->assertStringStartsWith('Not acceptable format: jsonalert(123);', $response->getContent());
  }

}