diff --git a/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php index 5bc7aea..9aba7a9 100644 --- a/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php @@ -11,6 +11,7 @@ use Drupal\Core\Routing\RedirectResponseToExternalUrl; use Drupal\Core\Routing\RequestContext; use Drupal\Core\Routing\UrlGeneratorInterface; +use InvalidArgumentException; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\KernelEvents; @@ -63,42 +64,48 @@ public function checkRedirectUrl(FilterResponseEvent $event) { // the following exception: // - Absolute URLs that point to this site (i.e. same base URL and // base path) are allowed. - if ($destination) { - if (!UrlHelper::isExternal($destination)) { - // The destination query parameter can be a relative URL in the sense - // of not including the scheme and host, but its path is expected to - // be absolute (start with a '/'). For such a case, prepend the - // scheme and host, because the 'Location' header must be absolute. - if (strpos($destination, '/') === 0) { - $destination = $request->getSchemeAndHttpHost() . $destination; + try { + if ($destination) { + if (!UrlHelper::isExternal($destination)) { + // The destination query parameter can be a relative URL in the sense + // of not including the scheme and host, but its path is expected to + // be absolute (start with a '/'). For such a case, prepend the + // scheme and host, because the 'Location' header must be absolute. + if (strpos($destination, '/') === 0) { + $destination = $request->getSchemeAndHttpHost() . $destination; + } + else { + // Legacy destination query parameters can be relative paths that + // have not yet been converted to URLs (outbound path processors + // and other URL handling still needs to be performed). + // @todo As generateFromPath() is deprecated, remove this in + // https://www.drupal.org/node/2418219. + $destination = UrlHelper::parse($destination); + $path = $destination['path']; + $options['query'] = $destination['query']; + $options['fragment'] = $destination['fragment']; + // The 'Location' HTTP header must always be absolute. + $options['absolute'] = TRUE; + $destination = $this->urlGenerator->generateFromPath($path, $options); + } + $response->setTargetUrl($destination); } - else { - // Legacy destination query parameters can be relative paths that - // have not yet been converted to URLs (outbound path processors - // and other URL handling still needs to be performed). - // @todo As generateFromPath() is deprecated, remove this in - // https://www.drupal.org/node/2418219. - $destination = UrlHelper::parse($destination); - $path = $destination['path']; - $options['query'] = $destination['query']; - $options['fragment'] = $destination['fragment']; - // The 'Location' HTTP header must always be absolute. - $options['absolute'] = TRUE; - $destination = $this->urlGenerator->generateFromPath($path, $options); + elseif (UrlHelper::externalIsLocal($destination, $this->requestContext->getCompleteBaseUrl())) { + $response->setTargetUrl($destination); } - $response->setTargetUrl($destination); } - elseif (UrlHelper::externalIsLocal($destination, $this->requestContext->getCompleteBaseUrl())) { - $response->setTargetUrl($destination); + + // In case someone setup a custom RedirectResponse object ensure that the + // URL is not external. In case this is wanted, check for a custom flag + // on RedirectResponse. + $url = $response->getTargetUrl(); + if (UrlHelper::isExternal($url) && !UrlHelper::externalIsLocal($url, $this->requestContext->getCompleteBaseUrl()) && !$response instanceof RedirectResponseToExternalUrl) { + throw new BadRequestHttpException('Redirects to external URLs are not allowed by default, use \Drupal\Core\Routing\RedirectResponseToExternalUrl for it'); } } - - // In case someone setup a custom RedirectResponse object ensure that the - // URL is not external. In case this is wanted, check for a custom flag - // on RedirectResponse. - $url = $response->getTargetUrl(); - if (UrlHelper::isExternal($url) && !UrlHelper::externalIsLocal($url, $this->requestContext->getCompleteBaseUrl()) && !$response instanceof RedirectResponseToExternalUrl) { - throw new BadRequestHttpException('Redirects to external URLs are not allowed by default, use \Drupal\Core\Routing\RedirectResponseToExternalUrl for it'); + // Potentially thrown by UrlHelper::externalIsLocal(). + catch (\InvalidArgumentException $e) { + throw new BadRequestHttpException($e->getMessage(), $e); } } } diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php index c4bef68..e1ec38f 100644 --- a/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php @@ -107,7 +107,7 @@ public static function providerTestDestinationRedirect() { public function testDestinationRedirectToExternalUrl($request, $expected) { $dispatcher = new EventDispatcher(); $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); - $response = new RedirectResponse(NULL); + $response = new RedirectResponse('http://other-example.com'); $url_generator = $this->getMockBuilder('Drupal\Core\Routing\UrlGenerator') ->disableOriginalConstructor() ->setMethods(array('generateFromPath')) @@ -193,7 +193,7 @@ public function providerTestDestinationRedirectToExternalUrl() { } /** - * @expectedException \InvalidArgumentException + * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException * * @dataProvider providerTestDestinationRedirectWithInvalidUrl */