From f456bba0facf1703cfa765d39c0cb5888810a8ce Mon Sep 17 00:00:00 2001
From: omkar podey <58183-omkar.podey@users.noreply.drupalcode.org>
Date: Wed, 11 Jan 2023 14:52:43 +0000
Subject: [PATCH] Issue #3330139 by omkar.podey, Wim Leers: Harden
 PathExclusionsTrait::excludeInProjectRoot() for paths outside of project root

---
 .../tests/src/Kernel/ExtensionUpdaterTest.php       |  3 ---
 .../src/PathExcluder/PathExclusionsTrait.php        |  6 ++++++
 .../src/PathExcluder/SqliteDatabaseExcluder.php     |  4 ++++
 .../src/Kernel/PackageManagerKernelTestBase.php     | 13 +++++++++++++
 4 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/automatic_updates_extensions/tests/src/Kernel/ExtensionUpdaterTest.php b/automatic_updates_extensions/tests/src/Kernel/ExtensionUpdaterTest.php
index 78f55b2742..742dc170fd 100644
--- a/automatic_updates_extensions/tests/src/Kernel/ExtensionUpdaterTest.php
+++ b/automatic_updates_extensions/tests/src/Kernel/ExtensionUpdaterTest.php
@@ -45,8 +45,6 @@ class ExtensionUpdaterTest extends AutomaticUpdatesExtensionsKernelTestBase {
     // Create a user who will own the stage even after the container is rebuilt.
     $user = $this->createUser([], NULL, TRUE, ['uid' => 2]);
     $this->setCurrentUser($user);
-
-    $this->createVirtualProject(__DIR__ . '/../../fixtures/fake-site');
   }
 
   /**
@@ -162,7 +160,6 @@ class ExtensionUpdaterTest extends AutomaticUpdatesExtensionsKernelTestBase {
    * @dataProvider providerUpdateException
    */
   public function testUpdateException(string $event_class): void {
-    $this->createVirtualProject(__DIR__ . '/../../fixtures/fake-site');
     $extension_updater = $this->container->get('automatic_updates_extensions.updater');
     $results = [
       ValidationResult::createError(['An error of some sorts.']),
diff --git a/package_manager/src/PathExcluder/PathExclusionsTrait.php b/package_manager/src/PathExcluder/PathExclusionsTrait.php
index f81b1541db..bd9fb8928b 100644
--- a/package_manager/src/PathExcluder/PathExclusionsTrait.php
+++ b/package_manager/src/PathExcluder/PathExclusionsTrait.php
@@ -65,6 +65,12 @@ trait PathExclusionsTrait {
     $project_root = $this->pathLocator->getProjectRoot();
 
     foreach ($paths as $path) {
+      if (str_starts_with($path, '/') || str_starts_with($path, 'vfs:')) {
+        if (!str_starts_with($path, $project_root)) {
+          throw new \LogicException("$path is not inside the project root: $project_root.");
+        }
+      }
+
       // Make absolute paths relative to the project root.
       $path = str_replace($project_root, '', $path);
       $path = ltrim($path, '/');
diff --git a/package_manager/src/PathExcluder/SqliteDatabaseExcluder.php b/package_manager/src/PathExcluder/SqliteDatabaseExcluder.php
index 43de7961d8..275b6a8efa 100644
--- a/package_manager/src/PathExcluder/SqliteDatabaseExcluder.php
+++ b/package_manager/src/PathExcluder/SqliteDatabaseExcluder.php
@@ -61,6 +61,10 @@ class SqliteDatabaseExcluder implements EventSubscriberInterface {
     // and we should ignore it. Always treat it as relative to the project root.
     if ($this->database->driver() === 'sqlite') {
       $options = $this->database->getConnectionOptions();
+      // Nothing to exclude if the database lives outside the project root.
+      if (str_starts_with($options['database'], '/') && !str_starts_with($options['database'], $this->pathLocator->getProjectRoot())) {
+        return;
+      }
       $this->excludeInProjectRoot($event, [
         $options['database'],
         $options['database'] . '-shm',
diff --git a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
index 1af46e987a..fdb7db4f7f 100644
--- a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
+++ b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
@@ -5,6 +5,7 @@ declare(strict_types = 1);
 namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Site\Settings;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\package_manager\Event\PreApplyEvent;
 use Drupal\package_manager\Event\StageEvent;
@@ -236,8 +237,20 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
     $active_dir = vfsStream::newDirectory('active');
     $this->vfsRoot->addChild($active_dir);
     $active_dir = $active_dir->url();
+    // Move vfs://root/sites to vfs://root/active/sites.
+    $sites_in_vfs = vfsStream::url('root/sites');
+    rename($sites_in_vfs, $sites_in_vfs . '/active');
     static::copyFixtureFilesTo($source_dir, $active_dir);
 
+    // Override siteDirectory to point to root/active/... instead of root/... .
+    $test_site_path = str_replace('vfs://root/', '', $this->siteDirectory);
+    $this->siteDirectory = vfsStream::url('root/active/' . $test_site_path);
+    // Override KernelTestBase::setUpFilesystem's Settings object.
+    $settings = Settings::getInstance() ? Settings::getAll() : [];
+    $settings['file_public_path'] = $this->siteDirectory . '/files';
+    $settings['config_sync_directory'] = $this->siteDirectory . '/files/config/sync';
+    new Settings($settings);
+
     // Create a stage root directory alongside the active directory.
     $stage_dir = vfsStream::newDirectory('stage');
     $this->vfsRoot->addChild($stage_dir);
-- 
GitLab