From bf02fde821d645d70d7f222b26af46af325ca7d7 Mon Sep 17 00:00:00 2001 From: Jesper Noordsij <45041769+jnoordsij@users.noreply.github.com> Date: Mon, 25 Mar 2024 22:07:10 +0100 Subject: [PATCH] [11.x] Enhance malformed request handling (#50735) * Convert SuspiciousOperationException to BadRequestHttpException This is suggested per https://github.com/symfony/symfony/blob/7.1/src/Symfony/Component/HttpFoundation/Exception/RequestExceptionInterface.php * Update existing test on SuspiciousOperationException handling * Add integration test for malformed requests * Return 400 code on all exceptions extending RequestExceptionInterface --- src/Illuminate/Foundation/Exceptions/Handler.php | 7 ++++--- .../FoundationExceptionsHandlerTest.php | 6 +++--- .../Foundation/ExceptionHandlerTest.php | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Illuminate/Foundation/Exceptions/Handler.php b/src/Illuminate/Foundation/Exceptions/Handler.php index d3111a462b5c..791f2cc69812 100644 --- a/src/Illuminate/Foundation/Exceptions/Handler.php +++ b/src/Illuminate/Foundation/Exceptions/Handler.php @@ -38,10 +38,11 @@ use Symfony\Component\Console\Application as ConsoleApplication; use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer; -use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; +use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface; use Symfony\Component\HttpFoundation\RedirectResponse as SymfonyRedirectResponse; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -143,7 +144,7 @@ class Handler implements ExceptionHandlerContract ModelNotFoundException::class, MultipleRecordsFoundException::class, RecordsNotFoundException::class, - SuspiciousOperationException::class, + RequestExceptionInterface::class, TokenMismatchException::class, ValidationException::class, ]; @@ -630,7 +631,7 @@ protected function prepareException(Throwable $e) ), $e instanceof AuthorizationException && ! $e->hasStatus() => new AccessDeniedHttpException($e->getMessage(), $e), $e instanceof TokenMismatchException => new HttpException(419, $e->getMessage(), $e), - $e instanceof SuspiciousOperationException => new NotFoundHttpException('Bad hostname provided.', $e), + $e instanceof RequestExceptionInterface => new BadRequestHttpException('Bad request.', $e), $e instanceof RecordsNotFoundException => new NotFoundHttpException('Not found.', $e), default => $e, }; diff --git a/tests/Foundation/FoundationExceptionsHandlerTest.php b/tests/Foundation/FoundationExceptionsHandlerTest.php index 51ecd52b16c0..70ac2a96f9c9 100644 --- a/tests/Foundation/FoundationExceptionsHandlerTest.php +++ b/tests/Foundation/FoundationExceptionsHandlerTest.php @@ -359,15 +359,15 @@ function ($argument) use (&$argumentActual) { $this->assertEquals($argumentExpected, $argumentActual); } - public function testSuspiciousOperationReturns404WithoutReporting() + public function testSuspiciousOperationReturns400WithoutReporting() { $this->config->shouldReceive('get')->with('app.debug', null)->once()->andReturn(true); $this->request->shouldReceive('expectsJson')->once()->andReturn(true); $response = $this->handler->render($this->request, new SuspiciousOperationException('Invalid method override "__CONSTRUCT"')); - $this->assertEquals(404, $response->getStatusCode()); - $this->assertStringContainsString('"message": "Bad hostname provided."', $response->getContent()); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertStringContainsString('"message": "Bad request."', $response->getContent()); $logger = m::mock(LoggerInterface::class); $this->container->instance(LoggerInterface::class, $logger); diff --git a/tests/Integration/Foundation/ExceptionHandlerTest.php b/tests/Integration/Foundation/ExceptionHandlerTest.php index d13a74f4122b..499751337c7a 100644 --- a/tests/Integration/Foundation/ExceptionHandlerTest.php +++ b/tests/Integration/Foundation/ExceptionHandlerTest.php @@ -124,6 +124,21 @@ public function testItHasFallbackErrorMessageForUnknownStatusCodes() ]); } + public function testItReturns400CodeOnMalformedRequests() + { + // HTTP request... + $this->post('test-route', ['_method' => '__construct']) + ->assertStatus(400) + ->assertSeeText('Bad Request'); // see https://github.com/symfony/symfony/blob/1d439995eb6d780531b97094ff5fa43e345fc42e/src/Symfony/Component/ErrorHandler/Resources/views/error.html.php#L12 + + // JSON request... + $this->postJson('test-route', ['_method' => '__construct']) + ->assertStatus(400) + ->assertExactJson([ + 'message' => 'Bad request.', + ]); + } + #[DataProvider('exitCodesProvider')] public function testItReturnsNonZeroExitCodesForUncaughtExceptions($providers, $successful) {