From d98adc7c2ceec9e4de70e17c514cc39eb61e6e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Riemenschneider?= Date: Fri, 2 Aug 2024 12:48:06 +0200 Subject: [PATCH] [#551] Dependency patch resolution: Only allow patches from trusted dependencies --- docs/usage/configuration.md | 21 ++++++++++++++++++ src/Plugin/Patches.php | 4 ++++ src/Resolver/Dependencies.php | 42 +++++++++++++++++++++++++++-------- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/docs/usage/configuration.md b/docs/usage/configuration.md index 087c07d..cc609a9 100644 --- a/docs/usage/configuration.md +++ b/docs/usage/configuration.md @@ -49,6 +49,27 @@ Technically, this value can be a path to a file that is nested in a deeper direc --- +### `allow-dependency-patches` + +```json +{ + [...], + "extra": { + "composer-patches": { + "allow-dependency-patches": [ + "some/package", + ] + } + } +} +``` + +**Default value**: null + +`allow-dependency-patches` allows you to explicitly apply patches defined by only the listed dependencies. For instance, if your project requires `some/packageA` and `some/packageB`, and both packages define patches, listing `some/packageA` in `allow-dependency-patches` would cause that only `some/packageA` will be evaluated while `some/packageB` will be ignored. If set `allow-dependency-patches: []`, no package will be evaluated. This does _not_ affect the _target_ of those patches. For instance, listing `drupal/core` here would not cause patches _to_ `drupal/core` to be ignored. + +--- + ### `ignore-dependency-patches` ```json diff --git a/src/Plugin/Patches.php b/src/Plugin/Patches.php index 5b74ca1..55b04f2 100644 --- a/src/Plugin/Patches.php +++ b/src/Plugin/Patches.php @@ -146,6 +146,10 @@ public function activate(Composer $composer, IOInterface $io): void 'type' => 'string', 'default' => 'patches.json', ], + "allow-dependency-patches" => [ + 'type' => 'list', + 'default' => null, + ], "ignore-dependency-patches" => [ 'type' => 'list', 'default' => [], diff --git a/src/Resolver/Dependencies.php b/src/Resolver/Dependencies.php index e7ee835..0e1e512 100644 --- a/src/Resolver/Dependencies.php +++ b/src/Resolver/Dependencies.php @@ -24,21 +24,42 @@ public function resolve(PatchCollection $collection): void return; } - $this->io->write(' - Resolving patches from dependencies.'); - + // Using both config keys, if $allowed_dependencies is set, it takes precedence + $allowed_dependencies = $this->plugin->getConfig('allow-dependency-patches'); $ignored_dependencies = $this->plugin->getConfig('ignore-dependency-patches'); + // First check, if we do allow dependency patches at all. + if ($allowed_dependencies === []) { + $this->io->write(' - No patches from dependencies are allowed.'); + return; + } + + $this->io->write(' - Resolving patches from dependencies.'); + $lockdata = $locker->getLockData(); foreach ($lockdata['packages'] as $p) { - // If we're supposed to skip gathering patches from a dependency, do that. - if (in_array($p['name'], $ignored_dependencies)) { - continue; + $allowed = in_array($p['name'], $allowed_dependencies ?? []); + $ignored = in_array($p['name'], $ignored_dependencies); + + // Allowed dependencies is not set in composer.json, and we're not + // supposed to skip gathering patches from this dependency + if (is_null($allowed_dependencies) && !$ignored) { + $this->lookForPatches($p, $collection); } - // Find patches in the composer.json for dependencies. - if (!isset($p['extra']) || !isset($p['extra']['patches'])) { - continue; + // Allowed dependencies are set, act only on allowed, if they're not + // ignored also. + if ($allowed && !$ignored) { + $this->lookForPatches($p, $collection); } + } + } + + private function lookForPatches(array $p, PatchCollection $collection): void + { + + // Find patches in the composer.json for dependencies. + if (isset($p['extra']['patches'])) { foreach ($this->findPatchesInJson($p['extra']['patches']) as $package => $patches) { foreach ($patches as $patch) { $patch->extra['provenance'] = "dependency:" . $package; @@ -47,8 +68,11 @@ public function resolve(PatchCollection $collection): void $collection->addPatch($patch); } } + } - // TODO: Also find patches in a configured patches.json for the dependency. + // Find patches in a patch-file for dependencies. + if (isset($p['extra']['composer-patches']['patches-file'])) { + // @todo Handle patch files. } } }