🐛 修复插件商店检查插件更新问题 (#1597)

* 🐛 修复插件商店检查插件更新问题

* 🐛 恶意命令检测问题
This commit is contained in:
AkashiCoin 2024-09-02 12:00:27 +08:00 committed by GitHub
parent a56d1f2bd8
commit 9eca6a97ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 311 additions and 27 deletions

View File

@ -13,6 +13,7 @@ from nonebot.adapters.onebot.v11.event import GroupMessageEvent
from tests.utils import _v11_group_message_event from tests.utils import _v11_group_message_event
from tests.config import BotId, UserId, GroupId, MessageId from tests.config import BotId, UserId, GroupId, MessageId
from tests.utils import get_content_bytes as _get_content_bytes
from tests.utils import get_response_json as _get_response_json from tests.utils import get_response_json as _get_response_json
@ -20,6 +21,10 @@ def get_response_json(file: str) -> dict:
return _get_response_json(Path() / "plugin_store", file=file) return _get_response_json(Path() / "plugin_store", file=file)
def get_content_bytes(file: str) -> bytes:
return _get_content_bytes(Path() / "plugin_store", file)
def init_mocked_api(mocked_api: MockRouter) -> None: def init_mocked_api(mocked_api: MockRouter) -> None:
mocked_api.get( mocked_api.get(
"https://data.jsdelivr.com/v1/packages/gh/xuanerwa/zhenxun_github_sub@main", "https://data.jsdelivr.com/v1/packages/gh/xuanerwa/zhenxun_github_sub@main",
@ -40,15 +45,15 @@ def init_mocked_api(mocked_api: MockRouter) -> None:
mocked_api.get( mocked_api.get(
"https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins/search_image/__init__.py", "https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins/search_image/__init__.py",
name="search_image_plugin_file_init", name="search_image_plugin_file_init",
).respond(content=b"") ).respond(content=get_content_bytes("search_image.py"))
mocked_api.get( mocked_api.get(
"https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins/alapi/jitang.py", "https://raw.githubusercontent.com/zhenxun-org/zhenxun_bot_plugins/main/plugins/alapi/jitang.py",
name="jitang_plugin_file", name="jitang_plugin_file",
).respond(content=b"") ).respond(content=get_content_bytes("jitang.py"))
mocked_api.get( mocked_api.get(
"https://raw.githubusercontent.com/xuanerwa/zhenxun_github_sub/main/github_sub/__init__.py", "https://raw.githubusercontent.com/xuanerwa/zhenxun_github_sub/main/github_sub/__init__.py",
name="github_sub_plugin_file_init", name="github_sub_plugin_file_init",
).respond(content=b"") ).respond(content=get_content_bytes("github_sub.py"))
async def test_add_plugin_basic( async def test_add_plugin_basic(
@ -207,7 +212,7 @@ async def test_add_plugin_extra(
assert (mock_base_path / "plugins" / "github_sub" / "__init__.py").is_file() assert (mock_base_path / "plugins" / "github_sub" / "__init__.py").is_file()
async def test_update_plugin_basic( async def test_update_plugin_basic_need_update(
app: App, app: App,
mocker: MockerFixture, mocker: MockerFixture,
mocked_api: MockRouter, mocked_api: MockRouter,
@ -215,7 +220,7 @@ async def test_update_plugin_basic(
tmp_path: Path, tmp_path: Path,
) -> None: ) -> None:
""" """
测试更新基础插件 测试更新基础插件插件需要更新
""" """
from zhenxun.builtin_plugins.plugin_store import _matcher from zhenxun.builtin_plugins.plugin_store import _matcher
@ -224,6 +229,10 @@ async def test_update_plugin_basic(
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH", "zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
new=tmp_path / "zhenxun", new=tmp_path / "zhenxun",
) )
mock_base_path = mocker.patch(
"zhenxun.builtin_plugins.plugin_store.data_source.ShopManage.get_loaded_plugins",
return_value=[("search_image", "0.0")],
)
plugin_id = 1 plugin_id = 1
@ -259,6 +268,59 @@ async def test_update_plugin_basic(
assert (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file() assert (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file()
async def test_update_plugin_basic_is_new(
app: App,
mocker: MockerFixture,
mocked_api: MockRouter,
create_bot: Callable,
tmp_path: Path,
) -> None:
"""
测试更新基础插件插件是最新版
"""
from zhenxun.builtin_plugins.plugin_store import _matcher
init_mocked_api(mocked_api=mocked_api)
mocker.patch(
"zhenxun.builtin_plugins.plugin_store.data_source.BASE_PATH",
new=tmp_path / "zhenxun",
)
mocker.patch(
"zhenxun.builtin_plugins.plugin_store.data_source.ShopManage.get_loaded_plugins",
return_value=[("search_image", "0.1")],
)
plugin_id = 1
async with app.test_matcher(_matcher) as ctx:
bot = create_bot(ctx)
bot: Bot = cast(Bot, bot)
raw_message = f"更新插件 {plugin_id}"
event: GroupMessageEvent = _v11_group_message_event(
message=raw_message,
self_id=BotId.QQ_BOT,
user_id=UserId.SUPERUSER,
group_id=GroupId.GROUP_ID_LEVEL_5,
message_id=MessageId.MESSAGE_ID,
to_me=True,
)
ctx.receive_event(bot=bot, event=event)
ctx.should_call_send(
event=event,
message=Message(message=f"正在更新插件 Id: {plugin_id}"),
result=None,
bot=bot,
)
ctx.should_call_send(
event=event,
message=Message(message="插件 识图 已是最新版本"),
result=None,
bot=bot,
)
assert mocked_api["basic_plugins"].called
assert mocked_api["extra_plugins"].called
async def test_remove_plugin( async def test_remove_plugin(
app: App, app: App,
mocker: MockerFixture, mocker: MockerFixture,
@ -280,8 +342,8 @@ async def test_remove_plugin(
plugin_path = mock_base_path / "plugins" / "search_image" plugin_path = mock_base_path / "plugins" / "search_image"
plugin_path.mkdir(parents=True, exist_ok=True) plugin_path.mkdir(parents=True, exist_ok=True)
with open(plugin_path / "__init__.py", "w") as f: with open(plugin_path / "__init__.py", "wb") as f:
f.write("") f.write(get_content_bytes("search_image.py"))
plugin_id = 1 plugin_id = 1
@ -307,3 +369,159 @@ async def test_remove_plugin(
assert mocked_api["basic_plugins"].called assert mocked_api["basic_plugins"].called
assert mocked_api["extra_plugins"].called assert mocked_api["extra_plugins"].called
assert not (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file() assert not (mock_base_path / "plugins" / "search_image" / "__init__.py").is_file()
async def test_plugin_not_exist_add(
app: App,
mocker: MockerFixture,
mocked_api: MockRouter,
create_bot: Callable,
tmp_path: Path,
) -> None:
"""
测试插件不存在添加插件
"""
from zhenxun.builtin_plugins.plugin_store import _matcher
init_mocked_api(mocked_api=mocked_api)
plugin_id = -1
async with app.test_matcher(_matcher) as ctx:
bot = create_bot(ctx)
bot: Bot = cast(Bot, bot)
raw_message = f"添加插件 {plugin_id}"
event: GroupMessageEvent = _v11_group_message_event(
message=raw_message,
self_id=BotId.QQ_BOT,
user_id=UserId.SUPERUSER,
group_id=GroupId.GROUP_ID_LEVEL_5,
message_id=MessageId.MESSAGE_ID,
to_me=True,
)
ctx.receive_event(bot=bot, event=event)
ctx.should_call_send(
event=event,
message=Message(message=f"正在添加插件 Id: {plugin_id}"),
result=None,
bot=bot,
)
ctx.should_call_send(
event=event,
message=Message(message="插件ID不存在..."),
result=None,
bot=bot,
)
async def test_plugin_not_exist_update(
app: App,
mocker: MockerFixture,
mocked_api: MockRouter,
create_bot: Callable,
tmp_path: Path,
) -> None:
"""
测试插件不存在更新插件
"""
from zhenxun.builtin_plugins.plugin_store import _matcher
init_mocked_api(mocked_api=mocked_api)
plugin_id = -1
async with app.test_matcher(_matcher) as ctx:
bot = create_bot(ctx)
bot: Bot = cast(Bot, bot)
raw_message = f"更新插件 {plugin_id}"
event: GroupMessageEvent = _v11_group_message_event(
message=raw_message,
self_id=BotId.QQ_BOT,
user_id=UserId.SUPERUSER,
group_id=GroupId.GROUP_ID_LEVEL_5,
message_id=MessageId.MESSAGE_ID_2,
to_me=True,
)
ctx.receive_event(bot=bot, event=event)
ctx.should_call_send(
event=event,
message=Message(message=f"正在更新插件 Id: {plugin_id}"),
result=None,
bot=bot,
)
ctx.should_call_send(
event=event,
message=Message(message="插件ID不存在..."),
result=None,
bot=bot,
)
async def test_plugin_not_exist_remove(
app: App,
mocker: MockerFixture,
mocked_api: MockRouter,
create_bot: Callable,
tmp_path: Path,
) -> None:
"""
测试插件不存在移除插件
"""
from zhenxun.builtin_plugins.plugin_store import _matcher
init_mocked_api(mocked_api=mocked_api)
plugin_id = -1
async with app.test_matcher(_matcher) as ctx:
bot = create_bot(ctx)
bot: Bot = cast(Bot, bot)
raw_message = f"移除插件 {plugin_id}"
event: GroupMessageEvent = _v11_group_message_event(
message=raw_message,
self_id=BotId.QQ_BOT,
user_id=UserId.SUPERUSER,
group_id=GroupId.GROUP_ID_LEVEL_5,
message_id=MessageId.MESSAGE_ID_2,
to_me=True,
)
ctx.receive_event(bot=bot, event=event)
ctx.should_call_send(
event=event,
message=Message(message="插件ID不存在..."),
result=None,
bot=bot,
)
async def test_plugin_not_exist_search(
app: App,
mocker: MockerFixture,
mocked_api: MockRouter,
create_bot: Callable,
tmp_path: Path,
) -> None:
"""
测试插件不存在搜索插件
"""
from zhenxun.builtin_plugins.plugin_store import _matcher
init_mocked_api(mocked_api=mocked_api)
plugin_id = -1
async with app.test_matcher(_matcher) as ctx:
bot = create_bot(ctx)
bot: Bot = cast(Bot, bot)
raw_message = f"搜索插件 {plugin_id}"
event: GroupMessageEvent = _v11_group_message_event(
message=raw_message,
self_id=BotId.QQ_BOT,
user_id=UserId.SUPERUSER,
group_id=GroupId.GROUP_ID_LEVEL_5,
message_id=MessageId.MESSAGE_ID_3,
to_me=True,
)
ctx.receive_event(bot=bot, event=event)
ctx.should_call_send(
event=event,
message=Message(message="未找到相关插件..."),
result=None,
bot=bot,
)

View File

@ -18,3 +18,5 @@ class GroupId:
class MessageId: class MessageId:
MESSAGE_ID = 30001 MESSAGE_ID = 30001
MESSAGE_ID_2 = 30002 MESSAGE_ID_2 = 30002
MESSAGE_ID_3 = 30003
MESSAGE_ID_4 = 30004

View File

@ -0,0 +1,24 @@
from nonebot.plugin import PluginMetadata
from zhenxun.configs.utils import PluginExtraData
__plugin_meta__ = PluginMetadata(
name="github订阅",
description="订阅github用户或仓库",
usage="""
usage
github新CommentPRIssue等提醒
指令
添加github ['用户'/'仓库'] [用户名/{owner/repo}]
删除github [用户名/{owner/repo}]
查看github
示例添加github订阅 用户 HibiKier
示例添加gb订阅 仓库 HibiKier/zhenxun_bot
示例添加github 用户 HibiKier
示例删除gb订阅 HibiKier
""".strip(),
extra=PluginExtraData(
author="xuanerwa",
version="0.7",
).dict(),
)

View File

@ -0,0 +1,17 @@
from nonebot.plugin import PluginMetadata
from zhenxun.configs.utils import PluginExtraData
__plugin_meta__ = PluginMetadata(
name="鸡汤",
description="喏,亲手为你煮的鸡汤",
usage="""
不喝点什么感觉有点不舒服
指令
鸡汤
""".strip(),
extra=PluginExtraData(
author="HibiKier",
version="0.1",
).dict(),
)

View File

@ -0,0 +1,18 @@
from nonebot.plugin import PluginMetadata
from zhenxun.configs.utils import PluginExtraData
__plugin_meta__ = PluginMetadata(
name="识图",
description="以图搜图,看破本源",
usage="""
识别图片 [二次元图片]
指令
识图 [图片]
""".strip(),
extra=PluginExtraData(
author="HibiKier",
version="0.1",
menu_type="一些工具",
).dict(),
)

View File

@ -2,19 +2,19 @@ import time
from collections import defaultdict from collections import defaultdict
from nonebot.adapters import Event from nonebot.adapters import Event
from nonebot.adapters.onebot.v11 import Bot
from nonebot.exception import IgnoredException
from nonebot.matcher import Matcher
from nonebot.message import run_preprocessor
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.matcher import Matcher
from nonebot_plugin_alconna import At from nonebot_plugin_alconna import At
from nonebot.adapters.onebot.v11 import Bot
from nonebot.message import run_preprocessor
from nonebot.exception import IgnoredException
from nonebot_plugin_session import EventSession from nonebot_plugin_session import EventSession
from zhenxun.configs.config import Config
from zhenxun.models.ban_console import BanConsole
from zhenxun.services.log import logger from zhenxun.services.log import logger
from zhenxun.configs.config import Config
from zhenxun.utils.enum import PluginType from zhenxun.utils.enum import PluginType
from zhenxun.utils.message import MessageUtils from zhenxun.utils.message import MessageUtils
from zhenxun.models.ban_console import BanConsole
malicious_check_time = Config.get_config("hook", "MALICIOUS_CHECK_TIME") malicious_check_time = Config.get_config("hook", "MALICIOUS_CHECK_TIME")
malicious_ban_count = Config.get_config("hook", "MALICIOUS_BAN_COUNT") malicious_ban_count = Config.get_config("hook", "MALICIOUS_BAN_COUNT")
@ -36,12 +36,12 @@ class BanCheckLimiter:
self.default_check_time = default_check_time self.default_check_time = default_check_time
self.default_count = default_count self.default_count = default_count
def add(self, key: str | int | float): def add(self, key: str | float):
if self.mint[key] == 1: if self.mint[key] == 1:
self.mtime[key] = time.time() self.mtime[key] = time.time()
self.mint[key] += 1 self.mint[key] += 1
def check(self, key: str | int | float) -> bool: def check(self, key: str | float) -> bool:
if time.time() - self.mtime[key] > self.default_check_time: if time.time() - self.mtime[key] > self.default_check_time:
self.mtime[key] = time.time() self.mtime[key] = time.time()
self.mint[key] = 0 self.mint[key] = 0
@ -76,6 +76,7 @@ async def _(
PluginType.HIDDEN, PluginType.HIDDEN,
PluginType.DEPENDANT, PluginType.DEPENDANT,
PluginType.ADMIN, PluginType.ADMIN,
PluginType.SUPERUSER,
]: ]:
return return
else: else:
@ -101,7 +102,7 @@ async def _(
await MessageUtils.build_message( await MessageUtils.build_message(
[ [
At(flag="user", target=user_id), At(flag="user", target=user_id),
f"检测到恶意触发命令,您将被封禁 30 分钟", "检测到恶意触发命令,您将被封禁 30 分钟",
] ]
).send() ).send()
logger.debug( logger.debug(

View File

@ -15,6 +15,7 @@ CONFIG_INDEX_CDN_URL = "https://cdn.jsdelivr.net/gh/zhenxun-org/zhenxun_bot_plug
"""插件索引库信息文件cdn""" """插件索引库信息文件cdn"""
DEFAULT_GITHUB_URL = "https://github.com/zhenxun-org/zhenxun_bot_plugins/tree/main" DEFAULT_GITHUB_URL = "https://github.com/zhenxun-org/zhenxun_bot_plugins/tree/main"
"""默认github仓库地址"""
GITHUB_REPO_URL_PATTERN = re.compile( GITHUB_REPO_URL_PATTERN = re.compile(
r"^https://github.com/(?P<owner>[^/]+)/(?P<repo>[^/]+)(/tree/(?P<branch>[^/]+))?$" r"^https://github.com/(?P<owner>[^/]+)/(?P<repo>[^/]+)(/tree/(?P<branch>[^/]+))?$"

View File

@ -180,7 +180,7 @@ class ShopManage:
str: 版本号 str: 版本号
""" """
module = plugin_info.module module = plugin_info.module
if cls.check_version_is_new(plugin_info, suc_plugin): if not cls.check_version_is_new(plugin_info, suc_plugin):
return f"{suc_plugin[module]} (有更新->{plugin_info.version})" return f"{suc_plugin[module]} (有更新->{plugin_info.version})"
return plugin_info.version return plugin_info.version
@ -198,7 +198,16 @@ class ShopManage:
bool: 是否有更新 bool: 是否有更新
""" """
module = plugin_info.module module = plugin_info.module
return module in suc_plugin and plugin_info.version != suc_plugin[module] return suc_plugin.get(module) and plugin_info.version == suc_plugin[module]
@classmethod
async def get_loaded_plugins(cls, *args) -> list[tuple[str, str]]:
"""获取已加载的插件
返回:
list[str]: 已加载的插件
"""
return await PluginInfo.filter(load_status=True).values_list(*args)
@classmethod @classmethod
async def get_plugins_info(cls) -> BuildImage | str: async def get_plugins_info(cls) -> BuildImage | str:
@ -209,9 +218,7 @@ class ShopManage:
""" """
data: dict[str, StorePluginInfo] = await cls.__get_data() data: dict[str, StorePluginInfo] = await cls.__get_data()
column_name = ["-", "ID", "名称", "简介", "作者", "版本", "类型"] column_name = ["-", "ID", "名称", "简介", "作者", "版本", "类型"]
plugin_list = await PluginInfo.filter(load_status=True).values_list( plugin_list = await cls.get_loaded_plugins("module", "version")
"module", "version"
)
suc_plugin = {p[0]: (p[1] or "0.1") for p in plugin_list} suc_plugin = {p[0]: (p[1] or "0.1") for p in plugin_list}
data_list = [ data_list = [
[ [
@ -380,9 +387,7 @@ class ShopManage:
BuildImage | str: 返回消息 BuildImage | str: 返回消息
""" """
data = await cls.__get_data() data = await cls.__get_data()
plugin_list = await PluginInfo.filter(load_status=True).values_list( plugin_list = await cls.get_loaded_plugins("module", "version")
"module", "version"
)
suc_plugin = {p[0]: p[1] for p in plugin_list if p[1]} suc_plugin = {p[0]: p[1] for p in plugin_list if p[1]}
filtered_data = [ filtered_data = [
(id, plugin_info) (id, plugin_info)
@ -430,9 +435,7 @@ class ShopManage:
plugin_key = list(data.keys())[plugin_id] plugin_key = list(data.keys())[plugin_id]
logger.info(f"尝试更新插件 {plugin_key}", "插件管理") logger.info(f"尝试更新插件 {plugin_key}", "插件管理")
plugin_info = data[plugin_key] plugin_info = data[plugin_key]
plugin_list = await PluginInfo.filter(load_status=True).values_list( plugin_list = await cls.get_loaded_plugins("module", "version")
"module", "version"
)
suc_plugin = {p[0]: p[1] for p in plugin_list if p[1]} suc_plugin = {p[0]: p[1] for p in plugin_list if p[1]}
logger.debug(f"当前插件列表: {suc_plugin}", "插件管理") logger.debug(f"当前插件列表: {suc_plugin}", "插件管理")
if cls.check_version_is_new(plugin_info, suc_plugin): if cls.check_version_is_new(plugin_info, suc_plugin):