1
0
Fork 0

Merge pull request #898 from Seldaek/providefix

Fix hijacking possibility via provide bug
pull/928/head
Nils Adermann 2012-07-17 12:27:18 -07:00
commit 84dd1fc1bf
6 changed files with 87 additions and 21 deletions

View File

@ -140,15 +140,42 @@ class Pool
return $candidates; return $candidates;
} }
$result = array(); $matches = $provideMatches = array();
$nameMatch = false;
foreach ($candidates as $candidate) { foreach ($candidates as $candidate) {
if ($candidate->matches($name, $constraint)) { switch ($candidate->matches($name, $constraint)) {
$result[] = $candidate; case BasePackage::MATCH_NONE:
break;
case BasePackage::MATCH_NAME:
$nameMatch = true;
break;
case BasePackage::MATCH:
$nameMatch = true;
$matches[] = $candidate;
break;
case BasePackage::MATCH_PROVIDE:
$provideMatches[] = $candidate;
break;
case BasePackage::MATCH_REPLACE:
$matches[] = $candidate;
break;
default:
throw new \UnexpectedValueException('Unexpected match type');
} }
} }
return $result; // if a package with the required name exists, we ignore providers
if ($nameMatch) {
return $matches;
}
return array_merge($matches, $provideMatches);
} }
public function literalToPackage($literal) public function literalToPackage($literal)

View File

@ -38,6 +38,12 @@ abstract class BasePackage implements PackageInterface
const STABILITY_ALPHA = 15; const STABILITY_ALPHA = 15;
const STABILITY_DEV = 20; const STABILITY_DEV = 20;
const MATCH_NAME = -1;
const MATCH_NONE = 0;
const MATCH = 1;
const MATCH_PROVIDE = 2;
const MATCH_REPLACE = 3;
public static $stabilities = array( public static $stabilities = array(
'stable' => self::STABILITY_STABLE, 'stable' => self::STABILITY_STABLE,
'RC' => self::STABILITY_RC, 'RC' => self::STABILITY_RC,
@ -122,27 +128,27 @@ abstract class BasePackage implements PackageInterface
* *
* @param string $name Name of the package to be matched * @param string $name Name of the package to be matched
* @param LinkConstraintInterface $constraint The constraint to verify * @param LinkConstraintInterface $constraint The constraint to verify
* @return bool Whether this package matches the name and constraint * @return int One of the MATCH* constants of this class or 0 if there is no match
*/ */
public function matches($name, LinkConstraintInterface $constraint) public function matches($name, LinkConstraintInterface $constraint)
{ {
if ($this->name === $name) { if ($this->name === $name) {
return $constraint->matches(new VersionConstraint('==', $this->getVersion())); return $constraint->matches(new VersionConstraint('==', $this->getVersion())) ? self::MATCH : self::MATCH_NAME;
} }
foreach ($this->getProvides() as $link) { foreach ($this->getProvides() as $link) {
if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) { if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) {
return true; return self::MATCH_PROVIDE;
} }
} }
foreach ($this->getReplaces() as $link) { foreach ($this->getReplaces() as $link) {
if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) { if ($link->getTarget() === $name && $constraint->matches($link->getConstraint())) {
return true; return self::MATCH_REPLACE;
} }
} }
return false; return self::MATCH_NONE;
} }
public function getRepository() public function getRepository()

View File

@ -430,7 +430,6 @@ class SolverTest extends TestCase
{ {
$this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
$this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0')); $this->repo->addPackage($packageQ = $this->getPackage('Q', '1.0'));
$this->repo->addPackage($packageB = $this->getPackage('B', '0.8'));
$packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires'))); $packageA->setRequires(array(new Link('A', 'B', $this->getVersionConstraint('>=', '1.0'), 'requires')));
$packageQ->setProvides(array(new Link('Q', 'B', $this->getVersionConstraint('=', '1.0'), 'provides'))); $packageQ->setProvides(array(new Link('Q', 'B', $this->getVersionConstraint('=', '1.0'), 'provides')));

View File

@ -0,0 +1,34 @@
--TEST--
Provide only applies when no existing package has the given name
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "higher-prio-hijacker", "version": "1.1.0", "provide": { "package": "1.0.0" } },
{ "name": "provider2", "version": "1.1.0", "provide": { "package2": "1.0.0" } }
]
},
{
"type": "package",
"package": [
{ "name": "package", "version": "0.9.0" },
{ "name": "package", "version": "1.0.0" },
{ "name": "hijacker", "version": "1.1.0", "provide": { "package": "1.0.0" } },
{ "name": "provider3", "version": "1.1.0", "provide": { "package3": "1.0.0" } }
]
}
],
"require": {
"package": "1.*",
"package2": "1.*",
"provider3": "1.1.0"
}
}
--RUN--
install
--EXPECT--
Installing package (1.0.0)
Installing provider2 (1.1.0)
Installing provider3 (1.1.0)

View File

@ -6,7 +6,7 @@ Replace takes precedence only in higher priority repositories
{ {
"type": "package", "type": "package",
"package": [ "package": [
{ "name": "forked", "version": "1.1.0", "provide": { "package2": "1.1.0" } } { "name": "forked", "version": "1.1.0", "replace": { "package2": "1.1.0" } }
] ]
}, },
{ {
@ -14,7 +14,7 @@ Replace takes precedence only in higher priority repositories
"package": [ "package": [
{ "name": "package", "version": "1.0.0" }, { "name": "package", "version": "1.0.0" },
{ "name": "package2", "version": "1.0.0" }, { "name": "package2", "version": "1.0.0" },
{ "name": "hijacker", "version": "1.1.0", "provide": { "package": "1.1.0" } } { "name": "hijacker", "version": "1.1.0", "replace": { "package": "1.1.0" } }
] ]
} }
], ],