From 3116614f70691958351b1bcfa1f5db0b85a361cd Mon Sep 17 00:00:00 2001
From: Dave Long <dave@longwaveconsulting.com>
Date: Tue, 10 Dec 2024 21:30:03 +0000
Subject: [PATCH] Issue #3490710 by mfb, catch, spokje: Catch potential
 exception when calling Request::create() in PathBasedBreadcrumbBuilder

(cherry picked from commit c6143563c318afd4864e60b31a6642afa5bf177a)
---
 .../system/src/PathBasedBreadcrumbBuilder.php |  8 ++++++-
 .../PathBasedBreadcrumbBuilderTest.php        | 22 +++++++++++++++++++
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/core/modules/system/src/PathBasedBreadcrumbBuilder.php b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
index 26c3066581df..e6b657196f23 100644
--- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php
+++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
@@ -20,6 +20,7 @@
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\Url;
+use Symfony\Component\HttpFoundation\Exception\BadRequestException;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -211,7 +212,12 @@ protected function getRequestForPath($path, array $exclude) {
     if (!empty($exclude[$path])) {
       return NULL;
     }
-    $request = Request::create($path);
+    try {
+      $request = Request::create($path);
+    }
+    catch (BadRequestException) {
+      return NULL;
+    }
     // Performance optimization: set a short accept header to reduce overhead in
     // AcceptHeaderMatcher when matching the request.
     $request->headers->set('Accept', 'text/html');
diff --git a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
index fed64ed349d1..d33dd55fa280 100644
--- a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
+++ b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
@@ -336,6 +336,28 @@ public function testBuildWithNonProcessedPath(): void {
     $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
   }
 
+  /**
+   * Tests the build method with an invalid path.
+   *
+   * @covers ::build
+   * @covers ::getRequestForPath
+   */
+  public function testBuildWithInvalidPath(): void {
+    // The parse_url() function returns FALSE for '/:123/foo' so the
+    // Request::create() method therefore considers it to be an invalid URI.
+    $this->context->expects($this->once())
+      ->method('getPathInfo')
+      ->willReturn('/:123/foo/bar');
+
+    $breadcrumb = $this->builder->build($this->createMock('Drupal\Core\Routing\RouteMatchInterface'));
+
+    // No path matched, though at least the frontpage is displayed.
+    $this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
+    $this->assertEqualsCanonicalizing(['url.path.is_front', 'url.path.parent'], $breadcrumb->getCacheContexts());
+    $this->assertEqualsCanonicalizing([], $breadcrumb->getCacheTags());
+    $this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
+  }
+
   /**
    * Tests the applied method.
    *
-- 
GitLab