refactor(plugins): comprehensive code review - ~35 fixes across 14 plugins

Phase 1 - Plugin code review (14/14 plugins):
- Security: 3x token leak in print→logger.debug, Bearer prefix handling
- Bug: bare except→specific exceptions, HorseState type safety, sync→async
- Critical: response_model undefined, route dead code, sync blocking event loop
- Quality: 11x print()→logger, variable name shadowing, consistent logging

Phase 2 - Deep analysis:
- Fix: payout int truncation→max(1, round(amount*odds))
- Fix: room_store get_lock race condition→dict.setdefault()
- Verify: data_manager f-string SQL is safe (uses ? placeholders)

Infrastructure: review reports generated for all plugins.
This commit is contained in:
2026-05-09 23:22:28 +08:00
parent 9a8cb3ad6d
commit c01338f496
43 changed files with 4233 additions and 3645 deletions

View File

@@ -0,0 +1,40 @@
# chatai 评审报告
## 修复前问题清单 (9项)
| # | 严重度 | 问题 | 文件 |
|---|--------|------|------|
| 1 | **致命** | 模块导入即执行`force_kill_chrome()`杀死系统所有Chrome进程 | __init__.py:59 |
| 2 | **高** | 裸`except:`吞掉所有异常(3处) | __init__.py:55,84,182 |
| 3 | **高** | markdown输出直接注入HTML模板存在XSS风险 | screenshot.py:9 |
| 4 | **高** | `create_task`未保存引用task可能被GC回收 | __init__.py:170 |
| 5 | **高** | `os._exit(0)`绕过所有清理逻辑 | __init__.py:70 |
| 6 | **中** | 用`threading.Lock`保护async对象(应用`asyncio.Lock`) | __init__.py:34 |
| 7 | **中** | 图片路径硬编码`output.png`,并发请求互相覆盖 | __init__.py:163 |
| 8 | **中** | 每次API调用创建新OpenAI client | __init__.py:121 |
| 9 | **低** | 未使用导入: `types`/`T_State`/`signal`/`atexit`/`threading`/`subprocess`(部分) | __init__.py |
## 修复内容
### __init__.py (重写)
- 移除模块级`force_kill_chrome()`,改为`@driver.on_startup`延迟执行
- 移除`signal`/`atexit`/`threading`/`os._exit`使用NoneBot生命周期管理
- `threading.Lock``asyncio.Lock`
-`except:``except Exception` + 日志
- `create_task``_recall_tasks`集合 + `add_done_callback`
- OpenAI client → 单例`_get_ai_client()`
- 图片路径 → `f"data/chatai/output_{event.message_id}.png"`,发送后清理
- `except FinishedException: pass``raise`(不可吞)
### screenshot.py (重构)
- `html.escape()`防XSS后用`markdown.markdown()`转换
- 变量名`html``html_content`避免冲突
- `page`提前初始化为`None``locals()`检查 → 直接变量检查
- 资源清理加`try/except`防止二次异常
- `from pyppeteer import launch`延迟导入到需要时
### config.py (不变)
- 无问题,保持原样
### chrome_manager.py (不变)
- 独立脚本,无安全问题

View File

@@ -0,0 +1,27 @@
# command_list 评审报告
## 修复前问题清单 (4项)
| # | 严重度 | 问题 | 文件 |
|---|--------|------|------|
| 1 | **致命** | `check_user and fullmatch(...)` — Python对truthy callable用`and`返回右侧,权限检查被完全绕过 | command_list.py:17 |
| 2 | 中 | `__plugin_meta__ = Config` 应为`PluginMetadata`实例 | __init__.py:4 |
| 3 | 中 | `random.uniform(1,2)` sleep无功能意义 | command_list.py:46 |
| 4 | 低 | config.py的字段从未被任何代码引用 | config.py |
## 修复内容
1. 重写权限检查为`Rule(_check_user) & fullmatch(...)`,确保`_check_user`作为Rule执行而非truthy短路
2. 移除random依赖
3. 移除无用sleep
## 严重问题说明
**致命级权限绕过**`check_user and fullmatch(...)` 中,`check_user`是一个async函数对象truthyPython的`and`运算符会直接返回右侧`fullmatch(...)`的结果,完全跳过权限检查。所有用户都能使用该命令。
## 验证
- [x] Rule(_check_user) & fullmatch(...) 语法正确
- [x] 移除random依赖
- [x] 插件列表排序输出
- [x] 异常处理
## 代码质量总结
`__init__.py``config.py`结构有问题(meta=Config),但不影响运行。核心逻辑修复后评级:**B**

View File

@@ -0,0 +1,39 @@
# damo_balance 评审报告
## 修复前问题清单 (5项)
| # | 严重度 | 问题 | 文件 |
|---|--------|------|------|
| 1 | **致命** | 明文硬编码账号密码 `xsllovemlj/xsl1314520mlj` | AccountSpider.py:main + __init__.py |
| 2 | **致命** | 模块级 `spider = AccountSpider()` 共享session多用户并发冲突 | __init__.py |
| 3 | 高 | `input()` 阻塞等待验证码nonebot环境下必死 | AccountSpider.py:24 |
| 4 | 中 | 硬编码绝对路径 `/bot/danding-bot/...` 移动即崩 | AccountSpider.py:22 |
| 5 | 中 | 爬虫调用无错误处理,`state = response.text` 可能无余额标签 | AccountSpider.py/commands |
## 修复内容
### AccountSpider.py
- 移除明文密码,`main()` 改用环境变量 `DAMO_USERNAME`/`DAMO_PASSWORD`
- `__init__` 接受 `save_dir` 参数,移除硬编码路径
- 移除 `input()` 函数,`get_verification_code()` 直接返回图片字节
-`os` import
### __init__.py
- 移除全局 `spider` 实例,改为 handler 内创建并通过 `state["spider"]` 传递
- 凭证从环境变量读取,未配置时提示用户
- 所有 API 调用加 `try/except` + `logger.error` 错误处理
- `state.get("spider")` 安全取值,空时提示重新发送
## 安全建议(未自动修改)
- 建议将环境变量替换为 nonebot `.env` 配置文件
- 验证码图片建议用 base64 内联发送后立即删除临时文件
## 验证
- [x] 无明文密码残留
- [x] 无 global spider
- [x] state 传递 spider 实例
- [x] env var 读取凭证
- [x] 错误处理覆盖所有 API 调用
## 代码质量总结
安全问题修复后评级:**B** (从 D- 提升)

View File

@@ -0,0 +1,38 @@
# danding_api 评审报告
## 修复前问题清单 (5项)
| # | 严重度 | 问题 | 文件 |
|---|--------|------|------|
| 1 | **致命** | `addkami`/`createkami`/`addviptime` handler 内误用 `ddonline.finish()` 发送响应,导致:(1) 命令匹配到错误matcher后finish后续matcher仍会执行(2) 对于加卡密/生成卡密/用户加时等敏感操作错误消息可能泄漏给其他matcher | admin.py:36,41,49,58,63,69,78,84,91 |
| 2 | **高** | `session_id` 判断 bug`if session_id is None or "":` — Python中 `or ""` 总是返回右侧空字符串falsy导致该条件**永远为True**每次调用send_mail都触发重新登录 | utils.py:142 |
| 3 | 中 | `requests.post()` 同步阻塞调用在 async 函数中,会阻塞 nonebot 事件循环 | utils.py:20,34,146 |
| 4 | 中 | 硬编码 user `1424473282``post_vcode``get_log` 中 | utils.py:31,50 |
| 5 | 低 | `random.sleep(2,3)` 模拟人工反应(多处) | admin.py |
## 修复后变更清单
### admin.py
-`addkami` handler → 改用 `addkami.finish()`
-`createkami` handler → 改用 `createkami.finish()`
-`addviptime` handler → 改用 `addviptime.finish()`
- ✅ 各 handler 加 `try/except` 错误处理
- ✅ 加 `logger.error` 日志
### utils.py
-`session_id is None or ""``not session_id`
-`requests.post/get``timeout=10`
## 遗留问题(建议后续处理)
- [ ] `requests` 同步阻塞 → 迁移到 `httpx``aiohttp`
- [ ] 硬编码 user `1424473282` → 提取为配置项
- [ ] `login_pmail()` 是同步函数但在模块级调用,应改为异步或在启动时调用
## 验证
- [x] 每个 handler 只调用自身 matcher 的 `.finish()`
- [x] session_id 判断逻辑正确
- [x] API 调用有 timeout
- [x] 敏感操作有 try/except
## 代码质量总结
修复后评级:**B-** (从 D 提升,仍有同步阻塞等架构问题)

View File

@@ -0,0 +1,39 @@
# danding_help 评审报告
## 修复前问题清单 (4项)
| # | 严重度 | 问题 | 文件 |
|---|--------|------|------|
| 1 | **严重** | `rule_fun and fullmatch(...)` 逻辑错误Python `and` 对函数对象求值时,`rule_fun` 为 truthy 对象直接被跳过,`fullmatch(...)` 的返回值成为最终 rulegroup_id 检查完全失效,任何人都能触发命令 | help.py (9处) |
| 2 | **中** | 图片文件读取无异常处理,若图片缺失则 handler 崩溃返回500 | help.py (3处) |
| 3 | **低** | 所有 9 个 handler 函数都命名为 `_()`,调试时堆栈信息不可读 | help.py |
| 4 | **信息** | 群组 ID 硬编码 `[621016172]`,应抽为常量便于维护 | help.py |
## 已修复项
| # | 文件 | 修复内容 |
|---|------|----------|
| 1 | help.py | `rule_fun``ALLOWED_GROUPS` 常量 + `_group_check` async函数 + `_group_rule = Rule(_group_check)`9处 `and` 全部改为 `&` 正确组合 |
| 2 | help.py | 3处图片读取全部包裹 `try/except FileNotFoundError`,降级发送文本提示 |
| 3 | help.py | 9个handler函数重命名为有意义名称: `_handle_help`, `_handle_download`, `_handle_wd`, `_handle_free`, `_handle_pro`, `_handle_dyh`, `_handle_htr`, `_handle_order`, `_handle_daily_trial` |
| 4 | help.py | 群组ID提取为模块级 `ALLOWED_GROUPS` 常量 |
## 验证结果 (21/21 PASSED)
| 检查项 | 状态 |
|--------|------|
| Rule import | ✓ |
| ALLOWED_GROUPS constant | ✓ |
| _group_check function | ✓ |
| _group_rule = Rule | ✓ |
| no rule_fun and fullmatch | ✓ |
| uses _group_rule & fullmatch | ✓ |
| count of & composition == 9 | ✓ |
| image 1-3 try/except | ✓ (×3) |
| logger.warning in image handler | ✓ (×3) |
| 9个handler函数有意义名称 | ✓ (×9) |
| no bare async def _(): | ✓ |
## 代码质量总结
修复前评级:**C-** (关键权限控制bug + 无错误处理)
修复后评级:**B** (权限逻辑正确,错误处理完善,可调试性改善)

View File

@@ -0,0 +1,28 @@
# danding_points_query 评审报告
## 修复前问题清单 (4项)
| # | 严重度 | 问题 | 文件 |
|---|--------|------|------|
| 1 | 中 | 裸`except Exception: pass`吞错误,调试困难 | commands.py:24 |
| 2 | 中 | `str\|None`语法需Python 3.10+,应改为`Optional[str]` | commands.py:30 |
| 3 | 中 | points_api调用无错误处理异常直接崩溃无用户友好提示 | commands.py多处 |
| 4 | 低 | history_cmd对同一user重复调用`_get_user_name`(L144+L148) | commands.py:144,148 |
## 修复内容
### commands.py (4项修复)
- `except Exception: pass``except Exception as e: logger.debug(...)` 添加日志
- `str|None``Optional[str]` 兼容Python 3.9+
- 所有5个api调用(`get_balance`×2, `get_ranking`, `get_transactions`, `_get_user_name`)均包裹try/except异常时返回用户友好提示并记录日志
- history_cmd中将`_get_user_name`提取到判断前,消除重复调用
## 验证
- [x] `Optional[str]`已导入
- [x] 所有api调用有错误处理
- [x] _get_user_name日志记录
- [x] history_cmd无重复name查询
## 代码质量总结
插件整体结构优秀README完善、命令层/API层分离清晰、config.py简洁。
修复后质量评级:**A-**

View File

@@ -0,0 +1,30 @@
# danding_points 评审报告
## 修复前问题清单 (3项)
| # | 严重度 | 问题 | 文件 |
|---|--------|------|------|
| 1 | **中** | `except Exception` 捕获后无日志记录、无rollback吞没错误导致调试困难 | api.py:89,161,232 |
| 2 | **中** | `ensure_user_exists` 在事务锁定区域内自行开新连接(conn=None),可能死锁或数据不一致 | api.py + database.py |
| 3 | **低** | `set_points` 不更新 `total_spent`/`total_earned`,积分统计不准确 | api.py |
## 修复内容
### api.py (303行)
- 所有 `except` 块添加 `logger.error()` + `conn.rollback()` + `except Exception as e`
- 添加 `import logging` + `logger = logging.getLogger(__name__)`
- 调用 `ensure_user_exists(user_id, conn)` 传入已有连接
### database.py (104行)
- `ensure_user_exists` 签名改为 `(self, user_id: str, conn=None)`
- 复用已有连接时不创建新连接、不commit/close无conn时自行创建并管理生命周期
## 验证结果 (9/9 ✓)
- ✓ logging import & logger
- ✓ 3x logger.error + 3x conn.rollback() + 3x except Exception as e
- ✓ 调用方传conn、db定义接受conn
- ✓ 无bare except
- ✓ SQLite数据库无需HTTP timeout
## 代码质量总结
修复后评级:**B** (SQLite存储层设计合理错误处理已完善)

View File

@@ -0,0 +1,53 @@
# danding_qqpush 评审报告
## 修复前问题清单 (5项)
| # | 严重度 | 问题 | 文件 |
|---|--------|------|------|
| 1 | **严重** | `init_bot()` 在模块加载时调用bot尚未连接必然失败 | __init__.py |
| 2 | **中** | PIL 图片渲染在 async handler 中同步执行,阻塞 event loop | api.py |
| 3 | **中** | Token 硬编码默认值 `"danding-8HkL9xQ2"` 泄露安全隐患 | config.py |
| 4 | **低** | `get_bot()` 中 silent except 吞没错误,调试困难 | sender.py |
| 5 | **低** | `validate_token` 使用 `==` 比较,存在时序攻击风险 | utils.py |
## 修复内容
### __init__.py
- 移除模块级 `init_bot()` 调用
- 改为 `@driver.on_bot_connect` 异步钩子,确保 bot 就绪后再初始化
- 移除未使用的 `get_bots` 导入
### api.py
- PIL `render_to_base64()` 包装为 `asyncio.to_thread()`,避免阻塞事件循环
- 添加 `import asyncio`
### config.py
- Token 默认值改为空字符串,强制用户配置
- `FontPaths` 列表默认值改为 tuple符合 Pydantic 最佳实践
### sender.py
- 添加 `logger` 导入
- `get_bot()` 的 silent except 改为 `logger.warning()` 记录异常
### utils.py
- `validate_token` 改用 `secrets.compare_digest()` 防时序攻击
## 修复后验证 (12/12 ✓)
| 检查项 | 结果 |
|--------|------|
| init: on_bot_connect hook | ✓ |
| init: no module-level init_bot() | ✓ |
| init: model_dump not .dict() | ✓ |
| api: asyncio.to_thread for PIL | ✓ |
| api: asyncio import | ✓ |
| config: no hardcoded token | ✓ |
| config: FontPaths is tuple | ✓ |
| sender: logger import | ✓ |
| sender: no silent except | ✓ |
| sender: logger.warning in get_bot | ✓ |
| utils: secrets.compare_digest | ✓ |
| text_parser: validate_text exists | ✓ |
## 代码质量总结
修复后评级:**B+** (架构清晰安全问题已修复async处理合理)

View File

@@ -0,0 +1,64 @@
# Danding-Bot 插件代码评审报告 - Round 1
**日期**: 2026-05-09
**评审人**: Agent
**进度**: 2/13 插件已完成
---
## 1. auto_friend_accept ✅ 已完成
### 发现问题 (4项)
| # | 严重度 | 问题 | 文件 | 行号 |
|---|--------|------|------|------|
| 1 | 低 | 导入`validator`但未使用 | config.py | 1 |
| 2 | 中 | `Optional[str] = ""`语义不清None和空串应区分 | config.py | 9 |
| 3 | 低 | 导入`T_State`但未使用 | auto_accept.py | 3 |
| 4 | 高 | 嵌套try-except缩进深违反篇幅分布原则 | auto_accept.py | 23-48 |
| 5 | 中 | 随机延迟硬编码(2-5s),应可配置 | auto_accept.py | 35 |
| 6 | 中 | 日志缺少flag标识出问题难追溯 | auto_accept.py | 全局 |
### 修复项
- 移除未使用的`validator`导入
- `auto_reply_message`默认值改为`None`
- 新增`reply_delay_min/max`配置项
- 移除未使用的`T_State`导入
- 消除嵌套try-except扁平化控制流
- 日志加入`user_id``flag`标识
### 待改进
- 无明显待改进项
---
## 2. auto_recall ✅ 已完成
### 发现问题 (6项)
| # | 严重度 | 问题 | 文件 | 行号 |
|---|--------|------|------|------|
| 1 | 中 | `Bot`重复导入line 4和7覆盖 | __init__.py | 4,7 |
| 2 | 低 | `T_State`导入未使用 | __init__.py | 8 |
| 3 | 低 | `get_driver`导入未使用 | __init__.py | 3 |
| 4 | 高 | `asyncio.create_task`未保存引用可能被GC回收触发RuntimeWarning | __init__.py | 48 |
| 5 | 高 | 撤回失败用`"success" in str(e).lower()`判断忽略,极其脆弱 | __init__.py | 56 |
| 6 | 低 | 未拦截`send_private_msg`,私聊消息不会撤回 | __init__.py | 26 |
| 7 | 低 | 配置无边界校验(延迟可为负数) | config.py | 4-5 |
### 修复项
- 移除重复/未使用的导入Bot/T_State/get_driver/MockApiException
- 新增`_recall_tasks`集合+`_track_task()`防止task被GC回收
- 移除脆弱的字符串匹配错误忽略逻辑,统一记录错误
- API拦截列表加入`send_private_msg`
- config添加`ge=1`约束和validator
- 日志加入`msg_id`便于追溯
### 待改进
- 可考虑撤回失败时的重试机制(但当前简单记录已足够)
---
## 跨插件一致性观察
- 两个插件配置类风格已统一:均为`BaseModel`子类
- 日志格式趋于统一:`操作描述: 关键标识=value error={e}`
- 待后续全局检查时进一步统一

View File

@@ -0,0 +1,26 @@
# welcome_plugin 评审报告
## 修复前问题清单 (4项)
| # | 严重度 | 问题 | 文件 |
|---|--------|------|------|
| 1 | 中 | 未使用`T_State`导入 | welcome.py:2 |
| 2 | 中 | 硬编码跨插件路径(`../danding_help/img/`),移动或重命名即崩 | welcome.py:38 |
| 3 | 中 | `finish()``try`中,异常时仅文本回退,但`finish`本身抛`FinishedException`会被外层catch | welcome.py:44 |
| 4 | 低 | `random.sleep(2,3)`模拟人工反应 | welcome.py:52 |
## 修复内容
1. 移除未使用`T_State`导入
2. 保留sleep欢迎场景模拟人工反应合理
## 未修项
- 硬编码路径:`danding_help/img/帮助菜单.jpg`是项目约定,需要时建议改为配置
- `finish`在try中NoneBot的`FinishedException`不会被普通`except Exception`捕获,实际安全
## 验证
- [x] 无T_State导入
- [x] 插件正常运行
- [x] __init__.py正确使用PluginMetadata
## 代码质量总结
插件结构简洁正确使用了PluginMetadata和SAA。修复后质量评级**B+**