From c4b4575c74f1e007709934e1ccde9c521eeb7f91 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 12 Apr 2019 19:51:43 +0100 Subject: [PATCH] Refuse to load plugins with ambiguous minAPI versions closes #2381 --- resources/locale | 2 +- src/pocketmine/plugin/ApiVersion.php | 38 +++++++++++++++++++++++++ src/pocketmine/plugin/PluginManager.php | 8 ++++++ tests/phpunit/plugin/ApiVersionTest.php | 23 +++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/resources/locale b/resources/locale index 15a2b7d4f..64844b7b6 160000 --- a/resources/locale +++ b/resources/locale @@ -1 +1 @@ -Subproject commit 15a2b7d4f05959376e3ef6d71e793b7bbdb2b60e +Subproject commit 64844b7b6b94a866367831f998281801a86f07a1 diff --git a/src/pocketmine/plugin/ApiVersion.php b/src/pocketmine/plugin/ApiVersion.php index 2dc83dc8b..e935fa0fb 100644 --- a/src/pocketmine/plugin/ApiVersion.php +++ b/src/pocketmine/plugin/ApiVersion.php @@ -24,6 +24,10 @@ declare(strict_types=1); namespace pocketmine\plugin; use pocketmine\utils\VersionString; +use function array_map; +use function array_push; +use function count; +use function usort; final class ApiVersion{ @@ -62,4 +66,38 @@ final class ApiVersion{ return false; } + + /** + * @param string[] $versions + * + * @return string[] + */ + public static function checkAmbiguousVersions(array $versions) : array{ + /** @var VersionString[][] $indexedVersions */ + $indexedVersions = []; + + foreach($versions as $str){ + $v = new VersionString($str); + if($v->getSuffix() !== ""){ //suffix is always unambiguous + continue; + } + if(!isset($indexedVersions[$v->getMajor()])){ + $indexedVersions[$v->getMajor()] = [$v]; + }else{ + $indexedVersions[$v->getMajor()][] = $v; + } + } + + /** @var VersionString[] $result */ + $result = []; + foreach($indexedVersions as $major => $list){ + if(count($list) > 1){ + array_push($result, ...$list); + } + } + + usort($result, static function(VersionString $string1, VersionString $string2){ return $string1->compare($string2); }); + + return array_map(static function(VersionString $string){ return $string->getBaseVersion(); }, $result); + } } diff --git a/src/pocketmine/plugin/PluginManager.php b/src/pocketmine/plugin/PluginManager.php index 4d48bbe26..36d63f477 100644 --- a/src/pocketmine/plugin/PluginManager.php +++ b/src/pocketmine/plugin/PluginManager.php @@ -258,6 +258,14 @@ class PluginManager{ ])); continue; } + $ambiguousVersions = ApiVersion::checkAmbiguousVersions($description->getCompatibleApis()); + if(!empty($ambiguousVersions)){ + $this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [ + $name, + $this->server->getLanguage()->translateString("pocketmine.plugin.ambiguousMinAPI", [implode(", ", $ambiguousVersions)]) + ])); + continue; + } if(count($pluginMcpeProtocols = $description->getCompatibleMcpeProtocols()) > 0){ $serverMcpeProtocols = [ProtocolInfo::CURRENT_PROTOCOL]; diff --git a/tests/phpunit/plugin/ApiVersionTest.php b/tests/phpunit/plugin/ApiVersionTest.php index 7c347a339..236b5b2c9 100644 --- a/tests/phpunit/plugin/ApiVersionTest.php +++ b/tests/phpunit/plugin/ApiVersionTest.php @@ -52,4 +52,27 @@ class ApiVersionTest extends TestCase{ public function testCompatibleApi(string $myVersion, string $wantVersion, bool $expected) : void{ self::assertSame($expected, ApiVersion::isCompatible($myVersion, [$wantVersion]), "my version: $myVersion, their version: $wantVersion, expect " . ($expected ? "yes" : "no")); } + + public function ambiguousVersionsProvider() : \Generator{ + yield [["3.0.0"], []]; + yield [["3.0.0", "3.0.1"], ["3.0.0", "3.0.1"]]; + yield [["3.0.0", "3.1.0", "4.0.0"], ["3.0.0", "3.1.0"]]; + yield [["3.0.0", "4.0.0"], []]; + yield [["3.0.0-ALPHA1", "3.0.0-ALPHA2"], []]; + } + + /** + * @dataProvider ambiguousVersionsProvider + * + * @param string[] $input + * @param string[] $expectedOutput + */ + public function testFindAmbiguousVersions(array $input, array $expectedOutput) : void{ + $ambiguous = ApiVersion::checkAmbiguousVersions($input); + + sort($expectedOutput); + sort($ambiguous); + + self::assertSame($expectedOutput, $ambiguous); + } }