diff --git a/composer.json b/composer.json index 8d91fce..9eeb9de 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "magento/composer-dependency-version-audit-plugin", "type": "composer-plugin", "description": "Validating packages through a composer plugin", - "version": "0.1.4", + "version": "0.1.5", "license": [ "OSL-3.0" ], diff --git a/src/Utils/Version.php b/src/Utils/Version.php index 0d053e9..5c31631 100644 --- a/src/Utils/Version.php +++ b/src/Utils/Version.php @@ -20,6 +20,12 @@ */ class Version { + /** + * Preferred stability level + * + * @var string + */ + public const STABILITY_DEV = 'dev'; /** * Get Highest version package @@ -63,7 +69,7 @@ public function findBestCandidateComposer1(Composer $composer, string $packageNa } $pool = new Pool($minStability, $stabilityFlags); $pool->addRepository($repository); - return (new VersionSelector($pool))->findBestCandidate($packageName); + return (new VersionSelector($pool))->findBestCandidate($packageName, null, null, self::STABILITY_DEV); } /** @@ -85,6 +91,7 @@ public function findBestCandidateComposer2(Composer $composer, string $packageNa $repositorySet = new RepositorySet($minStability, $stabilityFlags); $repositorySet->addRepository($repository); - return (new VersionSelector($repositorySet))->findBestCandidate($packageName); + + return (new VersionSelector($repositorySet))->findBestCandidate($packageName, null, self::STABILITY_DEV); } } diff --git a/tests/Unit/Magento/ComposerDependencyVersionAuditPlugin/PluginTest.php b/tests/Unit/Magento/ComposerDependencyVersionAuditPlugin/PluginTest.php index 5e82f4b..8bcef16 100644 --- a/tests/Unit/Magento/ComposerDependencyVersionAuditPlugin/PluginTest.php +++ b/tests/Unit/Magento/ComposerDependencyVersionAuditPlugin/PluginTest.php @@ -432,4 +432,68 @@ public function testInvalidPackageUpdateWithException(): void $this->plugin->packageUpdate($this->eventMock); } + /** + * Test update unstable package from public repo that should throw an exception + */ + public function testUpdateUnstablePackageWithException(): void + { + $privateRepoUrl = 'https://example.org'; + $publicRepoVersion ='1.9.0-beta1'; + $privateRepoVersion = '1.8.0'; + + $this->repositoryMock1->expects($this->any()) + ->method('getRepoConfig') + ->willReturn(['url' => 'https://repo.packagist.org']); + + $this->repositoryMock2->expects($this->any()) + ->method('getRepoConfig') + ->willReturn(['url' => $privateRepoUrl]); + + $this->packageMock->expects($this->any()) + ->method('getFullPrettyVersion') + ->willReturnOnConsecutiveCalls($publicRepoVersion, $privateRepoVersion); + + $constraintMock = $this->getMockBuilder(Constraint::class) + ->onlyMethods(['getPrettyString']) + ->disableOriginalConstructor() + ->getMock(); + + $constraintMock->expects($this->any()) + ->method('getPrettyString') + ->willReturn("^1.9.0-beta1"); + + $this->versionSelectorMock->expects($this->any()) + ->method('findBestCandidate') + ->willReturn($this->packageMock); + + if ((int)explode('.', Composer::VERSION)[0] === 1) { + $this->requestMock->expects($this->any()) + ->method('getJobs') + ->willReturn([ + ['packageName' => self::PACKAGE_NAME, 'cmd' => 'install', 'fixed' => false, 'constraint' => $constraintMock] + ]); + } else { + + $this->requestMock->expects($this->any()) + ->method('getRequires') + ->willReturn([ + self::PACKAGE_NAME => $constraintMock + ]); + + $this->prePoolCreateMock->expects($this->any()) + ->method('getPackages') + ->willReturn([]); + + $this->plugin->prePoolCreate($this->prePoolCreateMock); + } + + $packageName = self::PACKAGE_NAME; + $exceptionMessage = "Higher matching version {$publicRepoVersion} of {$packageName} was found in public repository packagist.org + than {$privateRepoVersion} in private {$privateRepoUrl}. Public package might've been taken over by a malicious entity, + please investigate and update package requirement to match the version from the private repository"; + $this->expectException(\Exception::class); + $this->expectExceptionMessage(sprintf($exceptionMessage, self::PACKAGE_NAME)); + + $this->plugin->packageUpdate($this->eventMock); + } }