From a92c441b1a9c763592252faecd39825fd04fea3a Mon Sep 17 00:00:00 2001 From: Steven Qiu Date: Sun, 29 Jun 2025 06:41:35 +0800 Subject: [PATCH] feat: limit max texture width to avoid png bomb --- app/Http/Controllers/OptionsController.php | 4 ++++ app/Http/Controllers/SkinlibController.php | 13 +++++++++++-- resources/lang/en/options.yml | 9 ++++++--- resources/lang/en/skinlib.yml | 1 + resources/lang/zh_CN/options.yml | 7 +++++-- resources/lang/zh_CN/skinlib.yml | 1 + .../ControllersTest/SkinlibControllerTest.php | 16 ++++++++++++++++ 7 files changed, 44 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/OptionsController.php b/app/Http/Controllers/OptionsController.php index a308e0c0..14eb5e84 100644 --- a/app/Http/Controllers/OptionsController.php +++ b/app/Http/Controllers/OptionsController.php @@ -163,6 +163,10 @@ class OptionsController extends Controller ->text('max_upload_file_size')->addon('KB') ->hint(trans('options.general.max_upload_file_size.hint', ['size' => ini_get('upload_max_filesize')])); + $form->group('max_texture_width') + ->text('max_texture_width')->addon('px') + ->hint(trans('options.general.max_texture_width.hint')); + $form->select('player_name_rule') ->option('official', trans('options.general.player_name_rule.official')) ->option('cjk', trans('options.general.player_name_rule.cjk')) diff --git a/app/Http/Controllers/SkinlibController.php b/app/Http/Controllers/SkinlibController.php index a629709f..27ad3303 100644 --- a/app/Http/Controllers/SkinlibController.php +++ b/app/Http/Controllers/SkinlibController.php @@ -217,9 +217,17 @@ class SkinlibController extends Controller return json($can->getReason(), 1); } - $image = imagecreatefrompng($file); $type = $data['type']; - $size = [imagesx($image), imagesy($image)]; + $size = getimagesize($file); + + $maxWidth = option('max_texture_width', 8192); + if ($size[0] > $maxWidth) { + $message = trans('skinlib.upload.too-wide', [ + 'width' => $size[0], + 'maxWidth' => $maxWidth + ]); + return json($message, 1); + } if ($size[0] % 64 != 0 || $size[1] % 32 != 0) { $message = trans('skinlib.upload.invalid-size', [ @@ -254,6 +262,7 @@ class SkinlibController extends Controller } } + $image = imagecreatefrompng($file); $imageSanitized = imagecreatetruecolor($size[0], $size[1]); imagealphablending($imageSanitized, false); imagesavealpha($imageSanitized, true); diff --git a/resources/lang/en/options.yml b/resources/lang/en/options.yml index 76da38c6..91a5bc53 100644 --- a/resources/lang/en/options.yml +++ b/resources/lang/en/options.yml @@ -110,7 +110,10 @@ general: regs_per_ip: Max accounts of one IP max_upload_file_size: title: Max Upload Size - hint: "Limit specified in php.ini: :size" + hint: 'Limit specified in php.ini: :size' + max_texture_width: + title: Max Texture Width + hint: Maximum width of uploaded textures, must be an integer multiple of 64 player_name_rule: title: Player Name Rule official: Letters, numbers and underscores (Mojang's official rule) @@ -129,8 +132,8 @@ general: label: Delete invalid textures automatically. hint: Delete textures records whose file no longer exists from skinlib. allow_downloading_texture: - title: Downloading Textures - label: Allow users to directly download the source file of a skinlib item. + title: Downloading Textures + label: Allow users to directly download the source file of a skinlib item. status_code_for_private: title: HTTP Code for Rejecting Accessing Private Textures texture_name_regexp: diff --git a/resources/lang/en/skinlib.yml b/resources/lang/en/skinlib.yml index 1ca467a0..1a514925 100644 --- a/resources/lang/en/skinlib.yml +++ b/resources/lang/en/skinlib.yml @@ -13,6 +13,7 @@ upload: private-score-notice: It will spend you more scores for setting it as private. You will be charged :score scores for per KB storage. invalid-size: Invalid :type file (width :width, height :height) invalid-hd-skin: Invalid HD skin (width and height should be divisible by 32) + too-wide: The texture is too wide (:widthpx), maximum width allowed is :maxWidthpx lack-score: You don't have enough score to upload this texture. repeated: The texture is already uploaded by someone else. You can add it to your closet directly. success: Texture :name was uploaded successfully. diff --git a/resources/lang/zh_CN/options.yml b/resources/lang/zh_CN/options.yml index d1d6a8d0..ea6fe8c0 100644 --- a/resources/lang/zh_CN/options.yml +++ b/resources/lang/zh_CN/options.yml @@ -20,7 +20,7 @@ homepage: label: 开启后背景不会随页面滚动而滚动 copyright_prefer: title: 程序版权信息 - description: "每种支持的语言都可以对应不同的程序版权信息,如果想要编辑某种特定语言下的版权信息,请在右上角切换至该语言后再提交修改。对于任何恶意修改页面右下角的版权信息(包括不限于删除、修改作者信息、修改链接指向)的用户,作者保留对其追究责任的权利。" + description: '每种支持的语言都可以对应不同的程序版权信息,如果想要编辑某种特定语言下的版权信息,请在右上角切换至该语言后再提交修改。对于任何恶意修改页面右下角的版权信息(包括不限于删除、修改作者信息、修改链接指向)的用户,作者保留对其追究责任的权利。' copyright_text: title: 自定义版权文字 description: 自定义版权文字内可使用占位符,{site_name} 将会被自动替换为站点名称,{site_url} 会被替换为站点地址。每种支持的语言都可以对应不同的自定义版权文字,如果想要编辑某种特定语言下的版权文字,请在右上角切换至该语言后再提交修改。 @@ -97,7 +97,10 @@ general: regs_per_ip: 每个 IP 限制注册数 max_upload_file_size: title: 最大允许上传大小 - hint: "PHP 限制::size,定义在 php.ini 中。" + hint: 'PHP 限制::size,定义在 php.ini 中。' + max_texture_width: + title: 最大允许材质宽度 + hint: 允许上传的材质的最大的宽度,必须是 64 的整数倍 player_name_rule: title: 角色名规则 official: 大小写字母数字下划线(Mojang 官方的用户名规则) diff --git a/resources/lang/zh_CN/skinlib.yml b/resources/lang/zh_CN/skinlib.yml index d1b7051b..18b186b2 100644 --- a/resources/lang/zh_CN/skinlib.yml +++ b/resources/lang/zh_CN/skinlib.yml @@ -12,6 +12,7 @@ upload: private-score-notice: 私密材质将会消耗更多的积分:每 KB 存储空间 :score 积分 invalid-size: 不是有效的 :type 文件(宽 :width,高 :height) invalid-hd-skin: 不是有效的高清皮肤(宽和高不是 32 的整数倍) + too-wide: 材质过宽(:widthpx),本站允许的最大宽度为 :maxWidthpx lack-score: 积分不足 repeated: 已经有人上传过这个材质了,直接添加到衣柜使用吧~ success: 材质 :name 上传成功 diff --git a/tests/HttpTest/ControllersTest/SkinlibControllerTest.php b/tests/HttpTest/ControllersTest/SkinlibControllerTest.php index 67be19c3..081e8883 100644 --- a/tests/HttpTest/ControllersTest/SkinlibControllerTest.php +++ b/tests/HttpTest/ControllersTest/SkinlibControllerTest.php @@ -303,6 +303,22 @@ class SkinlibControllerTest extends TestCase 'type' => 'steve', ])->assertJsonValidationErrors('public'); + // too wide texture + option(['max_texture_width' => 128]); + $this->postJson(route('texture.upload'), [ + 'name' => 'texture', + 'file' => UploadedFile::fake()->image('wide.png', 256, 256), + 'type' => 'steve', + 'public' => true, + ])->assertJson([ + 'code' => 1, + 'message' => trans('skinlib.upload.too-wide', [ + 'width' => 256, + 'maxWidth' => 128, + ]), + ]); + option(['max_texture_width' => 8192]); + // invalid skin size $this->postJson(route('texture.upload'), [ 'name' => 'texture',