undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
|
|
@ -0,0 +1,44 @@
|
|||
diff --git a/src/Composer/Repository/RepositoryUtils.php b/src/Composer/Repository/RepositoryUtils.php
|
||||
index e6960c6..9538b7b 100644
|
||||
--- a/src/Composer/Repository/RepositoryUtils.php
|
||||
+++ b/src/Composer/Repository/RepositoryUtils.php
|
||||
@@ -34,6 +34,26 @@ class RepositoryUtils
|
||||
* @return list<T>
|
||||
*/
|
||||
public static function filterRequiredPackages(array $packages, PackageInterface $requirer, bool $includeRequireDev = false, array $bucket = []): array
|
||||
+ {
|
||||
+ // CWE-407 fix: use SplObjectStorage (hash-backed set) for O(1) membership
|
||||
+ // instead of in_array() which is O(|bucket|). SplObjectStorage is passed as
|
||||
+ // an object reference so recursive calls share the same set instance.
|
||||
+ $bucketSet = new \SplObjectStorage();
|
||||
+ foreach ($bucket as $item) {
|
||||
+ $bucketSet->attach($item);
|
||||
+ }
|
||||
+
|
||||
+ return self::filterRequiredPackagesInternal($packages, $requirer, $includeRequireDev, $bucket, $bucketSet);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * @template T of PackageInterface
|
||||
+ * @param array<T> $packages
|
||||
+ * @param list<T> $bucket
|
||||
+ * @param \SplObjectStorage<T, null> $bucketSet
|
||||
+ * @return list<T>
|
||||
+ */
|
||||
+ private static function filterRequiredPackagesInternal(array $packages, PackageInterface $requirer, bool $includeRequireDev, array $bucket, \SplObjectStorage $bucketSet): array
|
||||
{
|
||||
$requires = $requirer->getRequires();
|
||||
if ($includeRequireDev) {
|
||||
@@ -43,9 +63,10 @@ public static function filterRequiredPackages(array $packages, PackageInterface
|
||||
foreach ($packages as $candidate) {
|
||||
foreach ($candidate->getNames() as $name) {
|
||||
if (isset($requires[$name])) {
|
||||
- if (!in_array($candidate, $bucket, true)) {
|
||||
+ if (!$bucketSet->contains($candidate)) {
|
||||
+ $bucketSet->attach($candidate);
|
||||
$bucket[] = $candidate;
|
||||
- $bucket = self::filterRequiredPackages($packages, $candidate, false, $bucket);
|
||||
+ $bucket = self::filterRequiredPackagesInternal($packages, $candidate, false, $bucket, $bucketSet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
67
defects/composer/patch/composer-0002-dependents-isset.patch
Normal file
67
defects/composer/patch/composer-0002-dependents-isset.patch
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
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];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue