java-topology/defects/composer/patch/composer-0002-dependents-isset.patch

68 lines
4.7 KiB
Diff

# UNDF: UNDF-2026-000000038
diff --git a/src/Composer/Repository/InstalledRepository.php b/src/Composer/Repository/InstalledRepository.php
index 3520fde..3cbe917 100644
--- a/src/Composer/Repository/InstalledRepository.php
+++ b/src/Composer/Repository/InstalledRepository.php
@@ -86,7 +86,7 @@ public function findPackagesWithReplacersAndProviders(string $name, $constraint
* @return array[] An associative array of arrays as described above.
* @phpstan-return array<array{0: PackageInterface, 1: Link, 2: array<mixed>|false}>
*/
- public function getDependents($needle, ?ConstraintInterface $constraint = null, bool $invert = false, bool $recurse = true, ?array $packagesFound = null): array
+ public function getDependents($needle, ?ConstraintInterface $constraint = null, bool $invert = false, bool $recurse = true, ?array $packagesFound = null, ?array $packagesFoundSet = null): array
{
$needles = array_map('strtolower', (array) $needle);
$results = [];
@@ -96,6 +96,13 @@ public function getDependents($needle, ?ConstraintInterface $constraint = null,
$packagesFound = $needles;
}
+ // CWE-407 fix: build a parallel hash set for O(1) in_array() replacement.
+ // $packagesFoundSet mirrors $packagesFound as an associative array keyed by
+ // package name string so isset() is O(1) instead of in_array() O(n).
+ if (null === $packagesFoundSet) {
+ $packagesFoundSet = array_fill_keys($packagesFound, true);
+ }
+
// locate root package for use below
$rootPackage = null;
foreach ($this->getPackages() as $package) {
@@ -112,6 +119,7 @@ public function getDependents($needle, ?ConstraintInterface $constraint = null,
// each loop needs its own "tree" as we want to show the complete dependent set of every needle
// without warning all the time about finding circular deps
$packagesInTree = $packagesFound;
+ $packagesInTreeSet = $packagesFoundSet;
// Replacements are considered valid reasons for a package to be installed during forward resolution
if (!$invert) {
@@ -125,12 +133,13 @@ public function getDependents($needle, ?ConstraintInterface $constraint = null,
if ($link->getSource() === $needle) {
if ($constraint === null || ($link->getConstraint()->matches($constraint) === true)) {
// already displayed this node's dependencies, cutting short
- if (in_array($link->getTarget(), $packagesInTree)) {
+ if (isset($packagesInTreeSet[$link->getTarget()])) {
$results[] = [$package, $link, false];
continue;
}
+ $packagesInTreeSet[$link->getTarget()] = true;
$packagesInTree[] = $link->getTarget();
- $dependents = $recurse ? $this->getDependents($link->getTarget(), null, false, true, $packagesInTree) : [];
+ $dependents = $recurse ? $this->getDependents($link->getTarget(), null, false, true, $packagesInTree, $packagesInTreeSet) : [];
$results[] = [$package, $link, $dependents];
$needles[] = $link->getTarget();
}
@@ -151,12 +160,13 @@ public function getDependents($needle, ?ConstraintInterface $constraint = null,
if ($link->getTarget() === $needle) {
if ($constraint === null || ($link->getConstraint()->matches($constraint) === !$invert)) {
// already displayed this node's dependencies, cutting short
- if (in_array($link->getSource(), $packagesInTree)) {
+ if (isset($packagesInTreeSet[$link->getSource()])) {
$results[] = [$package, $link, false];
continue;
}
+ $packagesInTreeSet[$link->getSource()] = true;
$packagesInTree[] = $link->getSource();
- $dependents = $recurse ? $this->getDependents($link->getSource(), null, false, true, $packagesInTree) : [];
+ $dependents = $recurse ? $this->getDependents($link->getSource(), null, false, true, $packagesInTree, $packagesInTreeSet) : [];
$results[] = [$package, $link, $dependents];
}
}