mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-11 20:04:05 +00:00
Merge 'stable' into 'minor-next'
Automatic merge performed by: https://github.com/pmmp/RestrictedActions/actions/runs/13666455727
This commit is contained in:
commit
09f0ce458c
@ -36,7 +36,7 @@ require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
*/
|
||||
$options = [
|
||||
"base_version" => VersionInfo::BASE_VERSION,
|
||||
"major_version" => fn() => explode(".", VersionInfo::BASE_VERSION)[0],
|
||||
"major_version" => fn() => explode(".", VersionInfo::BASE_VERSION, limit: 2)[0],
|
||||
"mcpe_version" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
|
||||
"is_dev" => VersionInfo::IS_DEVELOPMENT_BUILD,
|
||||
"changelog_file_name" => function() : string{
|
||||
|
@ -44,3 +44,9 @@ Released 26th February 2025.
|
||||
- Fixed confusing exception message when a block-breaking tool has an efficiency value of zero.
|
||||
- Fixed incorrect facing of doors since 1.21.60 (resulted in mismatched AABBs between client & server, rendering glitches etc.)
|
||||
- Resource pack UUIDs are now validated on load. Previously, invalid UUIDs would be accepted, and potentially cause a server crash on player join.
|
||||
|
||||
# 5.25.2
|
||||
Released 4th March 2025.
|
||||
|
||||
## Fixes
|
||||
- Added limits to various `explode()` calls.
|
||||
|
@ -13,6 +13,7 @@ rules:
|
||||
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
|
||||
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
|
||||
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
|
||||
- pocketmine\phpstan\rules\ExplodeLimitRule
|
||||
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
|
||||
# - pocketmine\phpstan\rules\ThreadedSupportedTypesRule
|
||||
|
||||
|
@ -264,7 +264,7 @@ JIT_WARNING
|
||||
$composerGitHash = InstalledVersions::getReference('pocketmine/pocketmine-mp');
|
||||
if($composerGitHash !== null){
|
||||
//we can't verify dependency versions if we were installed without using git
|
||||
$currentGitHash = explode("-", VersionInfo::GIT_HASH())[0];
|
||||
$currentGitHash = explode("-", VersionInfo::GIT_HASH(), 2)[0];
|
||||
if($currentGitHash !== $composerGitHash){
|
||||
critical_error("Composer dependencies and/or autoloader are out of sync.");
|
||||
critical_error("- Current revision is $currentGitHash");
|
||||
|
@ -31,7 +31,7 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "5.25.2";
|
||||
public const BASE_VERSION = "5.25.3";
|
||||
public const IS_DEVELOPMENT_BUILD = true;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
|
||||
|
@ -62,9 +62,10 @@ class Sign extends Spawnable{
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @deprecated
|
||||
*/
|
||||
public static function fixTextBlob(string $blob) : array{
|
||||
return array_slice(array_pad(explode("\n", $blob), 4, ""), 0, 4);
|
||||
return array_slice(array_pad(explode("\n", $blob, limit: 5), 4, ""), 0, 4);
|
||||
}
|
||||
|
||||
protected SignText $text;
|
||||
|
@ -79,7 +79,7 @@ class SignText{
|
||||
* @throws \InvalidArgumentException if the text is not valid UTF-8
|
||||
*/
|
||||
public static function fromBlob(string $blob, ?Color $baseColor = null, bool $glowing = false) : SignText{
|
||||
return new self(array_slice(array_pad(explode("\n", $blob), self::LINE_COUNT, ""), 0, self::LINE_COUNT), $baseColor, $glowing);
|
||||
return new self(array_slice(array_pad(explode("\n", $blob, limit: self::LINE_COUNT + 1), self::LINE_COUNT, ""), 0, self::LINE_COUNT), $baseColor, $glowing);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,6 +37,7 @@ use function array_values;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function str_replace;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
abstract class Command{
|
||||
|
||||
@ -113,7 +114,7 @@ abstract class Command{
|
||||
}
|
||||
|
||||
public function setPermission(?string $permission) : void{
|
||||
$this->setPermissions($permission === null ? [] : explode(";", $permission));
|
||||
$this->setPermissions($permission === null ? [] : explode(";", $permission, limit: PHP_INT_MAX));
|
||||
}
|
||||
|
||||
public function testPermission(CommandSender $target, ?string $permission = null) : bool{
|
||||
|
@ -39,6 +39,7 @@ use function ksort;
|
||||
use function min;
|
||||
use function sort;
|
||||
use function strtolower;
|
||||
use const PHP_INT_MAX;
|
||||
use const SORT_FLAG_CASE;
|
||||
use const SORT_NATURAL;
|
||||
|
||||
@ -108,7 +109,7 @@ class HelpCommand extends VanillaCommand{
|
||||
|
||||
$usage = $cmd->getUsage();
|
||||
$usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage;
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString)))
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString, limit: PHP_INT_MAX)))
|
||||
->prefix(TextFormat::GOLD));
|
||||
|
||||
$aliases = $cmd->getAliases();
|
||||
|
@ -219,7 +219,11 @@ class ParticleCommand extends VanillaCommand{
|
||||
break;
|
||||
case "blockdust":
|
||||
if($data !== null){
|
||||
$d = explode("_", $data);
|
||||
//to preserve the old unlimited explode behaviour, allow this to split into at most 5 parts
|
||||
//this allows the 4th argument to be processed normally if given without forcing it to also consume
|
||||
//any unexpected parts
|
||||
//we probably ought to error in this case, but this will do for now
|
||||
$d = explode("_", $data, limit: 5);
|
||||
if(count($d) >= 3){
|
||||
return new DustParticle(new Color(
|
||||
((int) $d[0]) & 0xff,
|
||||
|
@ -62,7 +62,7 @@ class ConsoleCommandSender implements CommandSender{
|
||||
$message = $this->getLanguage()->translate($message);
|
||||
}
|
||||
|
||||
foreach(explode("\n", trim($message)) as $line){
|
||||
foreach(explode("\n", trim($message), limit: PHP_INT_MAX) as $line){
|
||||
Terminal::writeLine(TextFormat::GREEN . "Command output | " . TextFormat::addBase(TextFormat::WHITE, $line));
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,8 @@ final class LegacyStringToItemParser{
|
||||
*/
|
||||
public function parse(string $input) : Item{
|
||||
$key = $this->reprocess($input);
|
||||
$b = explode(":", $key);
|
||||
//TODO: this should be limited to 2 parts, but 3 preserves old behaviour when given a string like 351:4:1
|
||||
$b = explode(":", $key, limit: 3);
|
||||
|
||||
if(!isset($b[1])){
|
||||
$meta = 0;
|
||||
|
@ -71,7 +71,7 @@ class Language{
|
||||
|
||||
foreach($files as $file){
|
||||
try{
|
||||
$code = explode(".", $file)[0];
|
||||
$code = explode(".", $file, limit: 2)[0];
|
||||
$strings = self::loadLang($path, $code);
|
||||
if(isset($strings[KnownTranslationKeys::LANGUAGE_NAME])){
|
||||
$result[$code] = $strings[KnownTranslationKeys::LANGUAGE_NAME];
|
||||
|
@ -72,9 +72,11 @@ final class JwtUtils{
|
||||
* @throws JwtException
|
||||
*/
|
||||
public static function split(string $jwt) : array{
|
||||
$v = explode(".", $jwt);
|
||||
//limit of 4 allows us to detect too many parts without having to split the string up into a potentially large
|
||||
//number of parts
|
||||
$v = explode(".", $jwt, limit: 4);
|
||||
if(count($v) !== 3){
|
||||
throw new JwtException("Expected exactly 3 JWT parts, got " . count($v));
|
||||
throw new JwtException("Expected exactly 3 JWT parts delimited by a period");
|
||||
}
|
||||
return [$v[0], $v[1], $v[2]]; //workaround phpstan bug
|
||||
}
|
||||
|
@ -148,7 +148,9 @@ class BanEntry{
|
||||
return null;
|
||||
}
|
||||
|
||||
$parts = explode("|", trim($str));
|
||||
//we expect at most 5 parts, but accept 6 in case of an extra unexpected delimiter
|
||||
//we don't want to include unexpected data into the ban reason
|
||||
$parts = explode("|", trim($str), limit: 6);
|
||||
$entry = new BanEntry(trim(array_shift($parts)));
|
||||
if(count($parts) > 0){
|
||||
$entry->setCreated(self::parseDate(array_shift($parts)));
|
||||
|
@ -54,6 +54,7 @@ use const CASE_LOWER;
|
||||
use const JSON_BIGINT_AS_STRING;
|
||||
use const JSON_PRETTY_PRINT;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const PHP_INT_MAX;
|
||||
use const YAML_UTF8_ENCODING;
|
||||
|
||||
/**
|
||||
@ -339,7 +340,7 @@ class Config{
|
||||
}
|
||||
|
||||
public function setNested(string $key, mixed $value) : void{
|
||||
$vars = explode(".", $key);
|
||||
$vars = explode(".", $key, limit: PHP_INT_MAX);
|
||||
$base = array_shift($vars);
|
||||
|
||||
if(!isset($this->config[$base])){
|
||||
@ -366,7 +367,7 @@ class Config{
|
||||
return $this->nestedCache[$key];
|
||||
}
|
||||
|
||||
$vars = explode(".", $key);
|
||||
$vars = explode(".", $key, limit: PHP_INT_MAX);
|
||||
$base = array_shift($vars);
|
||||
if(isset($this->config[$base])){
|
||||
$base = $this->config[$base];
|
||||
@ -390,7 +391,7 @@ class Config{
|
||||
$this->nestedCache = [];
|
||||
$this->changed = true;
|
||||
|
||||
$vars = explode(".", $key);
|
||||
$vars = explode(".", $key, limit: PHP_INT_MAX);
|
||||
|
||||
$currentNode = &$this->config;
|
||||
while(count($vars) > 0){
|
||||
@ -495,7 +496,7 @@ class Config{
|
||||
*/
|
||||
public static function parseList(string $content) : array{
|
||||
$result = [];
|
||||
foreach(explode("\n", trim(str_replace("\r\n", "\n", $content))) as $v){
|
||||
foreach(explode("\n", trim(str_replace("\r\n", "\n", $content)), limit: PHP_INT_MAX) as $v){
|
||||
$v = trim($v);
|
||||
if($v === ""){
|
||||
continue;
|
||||
|
@ -60,6 +60,7 @@ use const CURLOPT_RETURNTRANSFER;
|
||||
use const CURLOPT_SSL_VERIFYHOST;
|
||||
use const CURLOPT_SSL_VERIFYPEER;
|
||||
use const CURLOPT_TIMEOUT_MS;
|
||||
use const PHP_INT_MAX;
|
||||
use const SOCK_DGRAM;
|
||||
use const SOL_UDP;
|
||||
|
||||
@ -227,9 +228,10 @@ class Internet{
|
||||
$rawHeaders = substr($raw, 0, $headerSize);
|
||||
$body = substr($raw, $headerSize);
|
||||
$headers = [];
|
||||
foreach(explode("\r\n\r\n", $rawHeaders) as $rawHeaderGroup){
|
||||
//TODO: explore if we can set these limits lower
|
||||
foreach(explode("\r\n\r\n", $rawHeaders, limit: PHP_INT_MAX) as $rawHeaderGroup){
|
||||
$headerGroup = [];
|
||||
foreach(explode("\r\n", $rawHeaderGroup) as $line){
|
||||
foreach(explode("\r\n", $rawHeaderGroup, limit: PHP_INT_MAX) as $line){
|
||||
$nameValue = explode(":", $line, 2);
|
||||
if(isset($nameValue[1])){
|
||||
$headerGroup[trim(strtolower($nameValue[0]))] = trim($nameValue[1]);
|
||||
|
@ -369,7 +369,7 @@ final class Utils{
|
||||
debug_zval_dump($value);
|
||||
$contents = ob_get_contents();
|
||||
if($contents === false) throw new AssumptionFailedError("ob_get_contents() should never return false here");
|
||||
$ret = explode("\n", $contents);
|
||||
$ret = explode("\n", $contents, limit: 2);
|
||||
ob_end_clean();
|
||||
|
||||
if(preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){
|
||||
|
@ -26,10 +26,12 @@ namespace pocketmine\world\generator;
|
||||
use pocketmine\data\bedrock\BiomeIds;
|
||||
use pocketmine\item\LegacyStringToItemParser;
|
||||
use pocketmine\item\LegacyStringToItemParserException;
|
||||
use pocketmine\world\World;
|
||||
use function array_map;
|
||||
use function explode;
|
||||
use function preg_match;
|
||||
use function preg_match_all;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -70,7 +72,7 @@ final class FlatGeneratorOptions{
|
||||
*/
|
||||
public static function parseLayers(string $layers) : array{
|
||||
$result = [];
|
||||
$split = array_map('\trim', explode(',', $layers));
|
||||
$split = array_map('\trim', explode(',', $layers, limit: World::Y_MAX - World::Y_MIN));
|
||||
$y = 0;
|
||||
$itemParser = LegacyStringToItemParser::getInstance();
|
||||
foreach($split as $line){
|
||||
@ -96,7 +98,7 @@ final class FlatGeneratorOptions{
|
||||
* @throws InvalidGeneratorOptionsException
|
||||
*/
|
||||
public static function parsePreset(string $presetString) : self{
|
||||
$preset = explode(";", $presetString);
|
||||
$preset = explode(";", $presetString, limit: 4);
|
||||
$blocks = $preset[1] ?? "";
|
||||
$biomeId = (int) ($preset[2] ?? BiomeIds::PLAINS);
|
||||
$optionsString = $preset[3] ?? "";
|
||||
@ -109,9 +111,10 @@ final class FlatGeneratorOptions{
|
||||
$params = true;
|
||||
if($matches[3][$i] !== ""){
|
||||
$params = [];
|
||||
$p = explode(" ", $matches[3][$i]);
|
||||
$p = explode(" ", $matches[3][$i], limit: PHP_INT_MAX);
|
||||
foreach($p as $k){
|
||||
$k = explode("=", $k);
|
||||
//TODO: this should be limited to 2 parts, but 3 preserves old behaviour when given e.g. treecount=20=1
|
||||
$k = explode("=", $k, limit: 3);
|
||||
if(isset($k[1])){
|
||||
$params[$k[0]] = $k[1];
|
||||
}
|
||||
|
92
tests/phpstan/rules/ExplodeLimitRule.php
Normal file
92
tests/phpstan/rules/ExplodeLimitRule.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\phpstan\rules;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PHPStan\Analyser\ArgumentsNormalizer;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ParametersAcceptorSelector;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Rules\Rule;
|
||||
use PHPStan\Rules\RuleErrorBuilder;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* @phpstan-implements Rule<FuncCall>
|
||||
*/
|
||||
final class ExplodeLimitRule implements Rule{
|
||||
private ReflectionProvider $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
ReflectionProvider $reflectionProvider
|
||||
){
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getNodeType() : string{
|
||||
return FuncCall::class;
|
||||
}
|
||||
|
||||
public function processNode(Node $node, Scope $scope) : array{
|
||||
if(!$node->name instanceof Name){
|
||||
return [];
|
||||
}
|
||||
|
||||
if(!$this->reflectionProvider->hasFunction($node->name, $scope)){
|
||||
return [];
|
||||
}
|
||||
|
||||
$functionReflection = $this->reflectionProvider->getFunction($node->name, $scope);
|
||||
|
||||
if($functionReflection->getName() !== 'explode'){
|
||||
return [];
|
||||
}
|
||||
|
||||
$parametersAcceptor = ParametersAcceptorSelector::selectFromArgs(
|
||||
$scope,
|
||||
$node->getArgs(),
|
||||
$functionReflection->getVariants(),
|
||||
$functionReflection->getNamedArgumentsVariants(),
|
||||
);
|
||||
|
||||
$normalizedFuncCall = ArgumentsNormalizer::reorderFuncArguments($parametersAcceptor, $node);
|
||||
|
||||
if($normalizedFuncCall === null){
|
||||
return [];
|
||||
}
|
||||
|
||||
$count = count($normalizedFuncCall->getArgs());
|
||||
if($count !== 3){
|
||||
return [
|
||||
RuleErrorBuilder::message('The $limit parameter of explode() must be set to prevent malicious client data wasting resources.')
|
||||
->identifier("pocketmine.explode.limit")
|
||||
->build()
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
@ -624,7 +624,7 @@ function main(array $argv) : int{
|
||||
}
|
||||
|
||||
foreach($packets as $lineNum => $line){
|
||||
$parts = explode(':', $line);
|
||||
$parts = explode(':', $line, limit: 3);
|
||||
if(count($parts) !== 2){
|
||||
fwrite(STDERR, 'Wrong packet format at line ' . ($lineNum + 1) . ', expected read:base64 or write:base64');
|
||||
return 1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user