From 71f849dc91bbd2340a23a3dc10e8c22fde7ee4ea Mon Sep 17 00:00:00 2001
From: Yash Rode <57207-yash.rode@users.noreply.drupalcode.org>
Date: Thu, 4 May 2023 12:36:11 +0000
Subject: [PATCH] Issue #3357578 by yash.rode, Wim Leers, phenaproxima:
 str_starts_with($path, '/') does not correctly detect absolute paths on
 Windows

---
 package_manager/src/PathExcluder/GitExcluder.php         | 6 +++++-
 package_manager/src/PathExcluder/NodeModulesExcluder.php | 6 +++++-
 package_manager/src/PathExcluder/PathExclusionsTrait.php | 9 ++++++++-
 .../src/PathExcluder/SiteConfigurationExcluder.php       | 6 +++++-
 package_manager/src/PathExcluder/SiteFilesExcluder.php   | 5 +++++
 .../src/PathExcluder/SqliteDatabaseExcluder.php          | 6 +++++-
 package_manager/src/PathExcluder/TestSiteExcluder.php    | 6 +++++-
 package_manager/src/PathExcluder/UnknownPathExcluder.php | 6 +++++-
 .../src/PathExcluder/VendorHardeningExcluder.php         | 6 +++++-
 9 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/package_manager/src/PathExcluder/GitExcluder.php b/package_manager/src/PathExcluder/GitExcluder.php
index 42f473e74c..f8e2e77c38 100644
--- a/package_manager/src/PathExcluder/GitExcluder.php
+++ b/package_manager/src/PathExcluder/GitExcluder.php
@@ -7,6 +7,7 @@ namespace Drupal\package_manager\PathExcluder;
 use Drupal\package_manager\ComposerInspector;
 use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
 use Drupal\package_manager\PathLocator;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -28,9 +29,12 @@ final class GitExcluder implements EventSubscriberInterface {
    *   The path locator service.
    * @param \Drupal\package_manager\ComposerInspector $composerInspector
    *   The Composer inspector service.
+   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
+   *   The path factory service.
    */
-  public function __construct(PathLocator $path_locator, private readonly ComposerInspector $composerInspector) {
+  public function __construct(PathLocator $path_locator, private readonly ComposerInspector $composerInspector, PathFactoryInterface $path_factory) {
     $this->pathLocator = $path_locator;
+    $this->pathFactory = $path_factory;
   }
 
   /**
diff --git a/package_manager/src/PathExcluder/NodeModulesExcluder.php b/package_manager/src/PathExcluder/NodeModulesExcluder.php
index 21c1278024..9f07bf205f 100644
--- a/package_manager/src/PathExcluder/NodeModulesExcluder.php
+++ b/package_manager/src/PathExcluder/NodeModulesExcluder.php
@@ -6,6 +6,7 @@ namespace Drupal\package_manager\PathExcluder;
 
 use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
 use Drupal\package_manager\PathLocator;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -25,9 +26,12 @@ class NodeModulesExcluder implements EventSubscriberInterface {
    *
    * @param \Drupal\package_manager\PathLocator $path_locator
    *   The path locator service.
+   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
+   *   The path factory service.
    */
-  public function __construct(PathLocator $path_locator) {
+  public function __construct(PathLocator $path_locator, PathFactoryInterface $path_factory) {
     $this->pathLocator = $path_locator;
+    $this->pathFactory = $path_factory;
   }
 
   /**
diff --git a/package_manager/src/PathExcluder/PathExclusionsTrait.php b/package_manager/src/PathExcluder/PathExclusionsTrait.php
index 9f417a78f3..5a3bb9183e 100644
--- a/package_manager/src/PathExcluder/PathExclusionsTrait.php
+++ b/package_manager/src/PathExcluder/PathExclusionsTrait.php
@@ -18,6 +18,13 @@ trait PathExclusionsTrait {
    */
   protected $pathLocator;
 
+  /**
+   * The path factory service.
+   *
+   * @var \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface
+   */
+  protected $pathFactory;
+
   /**
    * Flags paths to be excluded, relative to the web root.
    *
@@ -56,7 +63,7 @@ trait PathExclusionsTrait {
     $project_root = $this->pathLocator->getProjectRoot();
 
     foreach ($paths as $path) {
-      if (str_starts_with($path, '/')) {
+      if ($this->pathFactory->create($path)->isAbsolute()) {
         if (!str_starts_with($path, $project_root)) {
           throw new \LogicException("$path is not inside the project root: $project_root.");
         }
diff --git a/package_manager/src/PathExcluder/SiteConfigurationExcluder.php b/package_manager/src/PathExcluder/SiteConfigurationExcluder.php
index b692110cfa..92b0b43004 100644
--- a/package_manager/src/PathExcluder/SiteConfigurationExcluder.php
+++ b/package_manager/src/PathExcluder/SiteConfigurationExcluder.php
@@ -6,6 +6,7 @@ namespace Drupal\package_manager\PathExcluder;
 
 use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
 use Drupal\package_manager\PathLocator;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -27,9 +28,12 @@ class SiteConfigurationExcluder implements EventSubscriberInterface {
    *   The current site path, relative to the Drupal root.
    * @param \Drupal\package_manager\PathLocator $path_locator
    *   The path locator service.
+   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
+   *   The path factory service.
    */
-  public function __construct(protected string $sitePath, PathLocator $path_locator) {
+  public function __construct(protected string $sitePath, PathLocator $path_locator, PathFactoryInterface $path_factory) {
     $this->pathLocator = $path_locator;
+    $this->pathFactory = $path_factory;
   }
 
   /**
diff --git a/package_manager/src/PathExcluder/SiteFilesExcluder.php b/package_manager/src/PathExcluder/SiteFilesExcluder.php
index 07bbdc757d..ce23ee3756 100644
--- a/package_manager/src/PathExcluder/SiteFilesExcluder.php
+++ b/package_manager/src/PathExcluder/SiteFilesExcluder.php
@@ -8,6 +8,7 @@ use Drupal\Core\StreamWrapper\LocalStream;
 use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
 use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
 use Drupal\package_manager\PathLocator;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\Filesystem\Filesystem;
 
@@ -28,6 +29,8 @@ final class SiteFilesExcluder implements EventSubscriberInterface {
    *
    * @param \Drupal\package_manager\PathLocator $path_locator
    *   The path locator service.
+   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
+   *   The path factory service.
    * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $streamWrapperManager
    *   The stream wrapper manager service.
    * @param \Symfony\Component\Filesystem\Filesystem $fileSystem
@@ -35,10 +38,12 @@ final class SiteFilesExcluder implements EventSubscriberInterface {
    */
   public function __construct(
     PathLocator $path_locator,
+    PathFactoryInterface $path_factory,
     private readonly StreamWrapperManagerInterface $streamWrapperManager,
     private readonly Filesystem $fileSystem
   ) {
     $this->pathLocator = $path_locator;
+    $this->pathFactory = $path_factory;
   }
 
   /**
diff --git a/package_manager/src/PathExcluder/SqliteDatabaseExcluder.php b/package_manager/src/PathExcluder/SqliteDatabaseExcluder.php
index 260b28ae1b..42bb065516 100644
--- a/package_manager/src/PathExcluder/SqliteDatabaseExcluder.php
+++ b/package_manager/src/PathExcluder/SqliteDatabaseExcluder.php
@@ -7,6 +7,7 @@ namespace Drupal\package_manager\PathExcluder;
 use Drupal\Core\Database\Connection;
 use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
 use Drupal\package_manager\PathLocator;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -28,9 +29,12 @@ class SqliteDatabaseExcluder implements EventSubscriberInterface {
    *   The path locator service.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection.
+   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
+   *   The path factory service.
    */
-  public function __construct(PathLocator $path_locator, protected Connection $database) {
+  public function __construct(PathLocator $path_locator, protected Connection $database, PathFactoryInterface $path_factory) {
     $this->pathLocator = $path_locator;
+    $this->pathFactory = $path_factory;
   }
 
   /**
diff --git a/package_manager/src/PathExcluder/TestSiteExcluder.php b/package_manager/src/PathExcluder/TestSiteExcluder.php
index 7548d82ef6..f3a5080f36 100644
--- a/package_manager/src/PathExcluder/TestSiteExcluder.php
+++ b/package_manager/src/PathExcluder/TestSiteExcluder.php
@@ -6,6 +6,7 @@ namespace Drupal\package_manager\PathExcluder;
 
 use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
 use Drupal\package_manager\PathLocator;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -25,9 +26,12 @@ final class TestSiteExcluder implements EventSubscriberInterface {
    *
    * @param \Drupal\package_manager\PathLocator $path_locator
    *   The path locator service.
+   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
+   *   The path factory service.
    */
-  public function __construct(PathLocator $path_locator) {
+  public function __construct(PathLocator $path_locator, PathFactoryInterface $path_factory) {
     $this->pathLocator = $path_locator;
+    $this->pathFactory = $path_factory;
   }
 
   /**
diff --git a/package_manager/src/PathExcluder/UnknownPathExcluder.php b/package_manager/src/PathExcluder/UnknownPathExcluder.php
index d9c3071c3b..9150bc3c0a 100644
--- a/package_manager/src/PathExcluder/UnknownPathExcluder.php
+++ b/package_manager/src/PathExcluder/UnknownPathExcluder.php
@@ -8,6 +8,7 @@ use Drupal\Component\Serialization\Json;
 use Drupal\package_manager\ComposerInspector;
 use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
 use Drupal\package_manager\PathLocator;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -39,9 +40,12 @@ final class UnknownPathExcluder implements EventSubscriberInterface {
    *   The Composer inspector service.
    * @param \Drupal\package_manager\PathLocator $path_locator
    *   The path locator service.
+   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
+   *   The path factory service.
    */
-  public function __construct(private readonly ComposerInspector $composerInspector, PathLocator $path_locator) {
+  public function __construct(private readonly ComposerInspector $composerInspector, PathLocator $path_locator, PathFactoryInterface $path_factory) {
     $this->pathLocator = $path_locator;
+    $this->pathFactory = $path_factory;
   }
 
   /**
diff --git a/package_manager/src/PathExcluder/VendorHardeningExcluder.php b/package_manager/src/PathExcluder/VendorHardeningExcluder.php
index a7bf69d7c9..509516390a 100644
--- a/package_manager/src/PathExcluder/VendorHardeningExcluder.php
+++ b/package_manager/src/PathExcluder/VendorHardeningExcluder.php
@@ -6,6 +6,7 @@ namespace Drupal\package_manager\PathExcluder;
 
 use Drupal\package_manager\Event\CollectPathsToExcludeEvent;
 use Drupal\package_manager\PathLocator;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -25,9 +26,12 @@ final class VendorHardeningExcluder implements EventSubscriberInterface {
    *
    * @param \Drupal\package_manager\PathLocator $path_locator
    *   The path locator service.
+   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
+   *   The path factory service.
    */
-  public function __construct(PathLocator $path_locator) {
+  public function __construct(PathLocator $path_locator, PathFactoryInterface $path_factory) {
     $this->pathLocator = $path_locator;
+    $this->pathFactory = $path_factory;
   }
 
   /**
-- 
GitLab