Skip to content

用ai补充了一下语言包 - #755

Open
lietxia wants to merge 11 commits into
jiangtian616:masterfrom
lietxia:master
Open

lietxia wants to merge 11 commits into
jiangtian616:masterfrom
lietxia:master

Conversation

@lietxia

@lietxia lietxia commented Apr 2, 2026

Copy link
Copy Markdown

另外用ai检测了一下安全+性能。看不懂。

审计日期:2026-04-02
项目版本:8.0.12+309
审计范围:lib/src/ 全目录(407 个 Dart 文件)


总体评估

维度 状态 说明
整体架构 ✅ 良好 GetX + Drift 架构合理,关注点分离清晰
安全 🟡 中等风险 存在敏感日志泄漏、弱密码哈希等问题
性能 🟡 中等风险 局部 Widget 重建泄漏,build 中有临时对象
供应链 🟡 需关注 多个 git 依赖指向非官方 fork(均为作者自己的 fork,可接受但需锁版本)

一、安全问题

🔴 S1 — 日志明文打印敏感凭据

文件: lib/src/setting/user_setting.dart:64

log.debug('saveUserInfo: $userName, $ipbMemberId, $ipbPassHash, $avatarImgUrl, $nickName');

ipbMemberIdipbPassHash 是 E-Hentai 会话 Cookie,等同于登录凭据。调试日志在 release 模式下默认仍可被写入设备存储并上报。

修复: 不要在日志中打印认证相关字段,或用占位符替代:

log.debug('saveUserInfo: $userName, memberId=***, passHash=***, $avatarImgUrl, $nickName');

🔴 S2 — 密码仅用 MD5 哈希(弱哈希)

文件: lib/src/setting/security_setting.dart:111

String md5 = keyToMd5(rawPassword);
log.debug('saveEncryptedPassword:$md5');  // 还写入日志
this.encryptedPassword.value = md5;

MD5 为已损坏的哈希算法,无加盐,可被彩虹表秒破。且哈希值还被写入 debug 日志。

修复: 使用带盐的 PBKDF2 / bcrypt / Argon2(可用 pointycastle 包),并移除日志打印。


🟡 S3 — 正则表达式注入(用户输入直接构建 RegExp)

文件 1: lib/src/pages/download_search/download_search_logic.dart:155

regExp = RegExp(value);  // value 来自用户输入的搜索词

文件 2: lib/src/service/local_block_rule_service.dart:505

return attribute.any((a) => RegExp(rule.expression).hasMatch(a));

虽然代码对构建 RegExp 有 try/catch 保护(防止崩溃),但恶意/复杂正则仍可造成 ReDoS(正则表达式拒绝服务),导致主线程长时间阻塞。

修复: 在 Isolate 中执行正则匹配,或设置超时:

// 方案:将匹配操作移入 compute()
final results = await compute(_matchInIsolate, (pattern: value, list: gallerys));

🟡 S4 — 敏感数据明文存储(get_storage 无加密)

文件: lib/src/service/storage_service.dart:20

_storage = GetStorage(storageFileName, pathService.getVisibleDir().path);

GetStorage 以 JSON 明文存储在 可见目录(非沙盒私有目录)。ipbPassHashapiKey(Archive Bot)、encryptedPassword(MD5)等均经此机制持久化。

在 root 设备、adb 备份或 macOS 系统上,任何应用均可读取该文件。

修复:

  • 将存储路径改为私有目录(getApplicationSupportDirectory()
  • 对认证相关字段使用 flutter_secure_storage 加密存储

🟡 S5 — git 依赖锁定风险(供应链)

文件: pubspec.yaml

多个依赖使用 git URL + master 分支(无版本锁定):

flutter_slidable:
  git:
    url: https://github.com/letsar/flutter_slidable.git
    ref: master   # ⚠️ 未锁定 commit hash

其他使用默认默认分支(无 ref)的:like_buttonzoom_viewreceive_sharing_intent 等。

修复: 所有 git 依赖加上 ref: <commit-hash> 锁定,防止上游仓库被恶意推送影响构建。


🟢 S6 — Archive Bot API Key 日志

文件: lib/src/setting/archive_bot_setting.dart

API Key 通过 storageService 持久化(同 S4 问题),但未发现在日志中直接打印,风险较低。主要风险是 get_storage 明文存储(见 S4)。


二、性能问题

🔴 P1 — build() 中每帧创建 ScrollController(内存泄漏)

文件: lib/src/pages/read/layout/horizontal_page/horizontal_page_layout.dart:42

item = Center(
  child: SingleChildScrollView(
    controller: ScrollController(),  // ⚠️ 每次 build 都创建新实例,且永远不 dispose
    child: item,
  ),
);

HorizontalPageLayout 继承 BaseLayout(StatelessWidget 语义),每次 Obx 触发重建都会创建新的 ScrollController从不 dispose,导致内存持续增长。

修复: 将此 Widget 转为 StatefulWidget,在 initState 中创建,dispose 中销毁:

class _HorizontalPageItemWidget extends StatefulWidget { ... }
class _State extends State<_HorizontalPageItemWidget> {
  late final ScrollController _scrollController = ScrollController();
  @override void dispose() { _scrollController.dispose(); super.dispose(); }
}

🔴 P2 — desktop_layout leftTabBarScrollController 未 dispose

文件: lib/src/pages/layout/desktop/desktop_layout_page_state.dart:35
文件: lib/src/pages/layout/desktop/desktop_layout_page_logic.dart(onClose 中无 dispose)

// State 中创建:
final ScrollController leftTabBarScrollController = ScrollController();

// Logic onClose() 中:
resizableController.dispose();  // ✅ 有
// leftTabBarScrollController.dispose();  ❌ 缺失

桌面布局页面生命周期贯穿整个 App 运行时,虽然影响有限,但不符合资源管理规范。

修复:DesktopLayoutPageLogic.onClose() 添加:

state.leftTabBarScrollController.dispose();

🟡 P3 — for 循环内 await 串行执行(批量数据库操作)

文件: lib/src/service/archive_download_service.dart:339-346

for (ArchiveDownloadedData a in archiveDownloadedDatas) {
  await _updateArchiveInDatabase(a.gid);   // 串行
}
for (ArchiveDownloadedData a in archiveDownloadedDatas) {
  await _updateArchiveInfoInDisk(a.gid);   // 串行
}

文件: lib/src/service/gallery_download_service.dart:1380-1391(迁移图片时多个串行 await)

数据库更新已部分放入 transaction 中(是正确的),但 _updateArchiveInfoInDisk 的磁盘 IO 仍是串行循环。

说明: 事务内的串行 await 是正常的(Drift 要求如此)。问题在于事务外的串行磁盘 IO 循环,可改为并发:

await Future.wait(
  archiveDownloadedDatas.map((a) => _updateArchiveInfoInDisk(a.gid))
);

🟡 P4 — 用户输入正则在主线程执行(同 S3)

文件: lib/src/service/local_block_rule_service.dart:505

每次画廊列表刷新都对所有条目执行正则匹配,若用户配置了复杂规则且列表条目较多,会在主线程上产生明显卡顿。

修复: 使用 compute() 将匹配逻辑移入 Isolate。


🟡 P5 — EhCacheManager 未配置内存缓存上限

文件: lib/src/network/eh_cache_manager.dart

未发现 maxNrOfCacheObjectsstalePeriod 等缓存限制配置,extended_image 使用默认缓存策略,在低内存设备上浏览大量高分辨率图片时可能导致 OOM。

修复: 配置合理的缓存限制:

CacheManager.custom(
  Config(
    key,
    maxNrOfCacheObjects: 200,
    stalePeriod: const Duration(days: 7),
  ),
)

同时为 ExtendedImage 传入 cacheWidth/cacheHeight 缩减内存解码尺寸:

ExtendedImage.network(
  url,
  cacheWidth: MediaQuery.of(context).size.width.toInt() * 2, // 2x for retina
)

🟡 P6 — FutureBuilder 在 build 中使用(重复发起网络请求)

文件: lib/src/widget/eh_gallery_list_card_.dart:245

return FutureBuilder<int>(
  future: someAsyncOperation(),  // ⚠️ 每次 rebuild 都重新发起
  ...
)

若父 Widget 频繁重建,FutureBuilderfuture 参数会在每次 build 中重新创建,导致请求被重复发起。

修复: 将 Future 缓存在 State 中:

late final Future<int> _future = someAsyncOperation(); // initState 中初始化

🟢 P7 — 搜索建议 ScrollController 无 dispose(低风险)

文件: lib/src/pages/search/mixin/search_page_state_mixin.dart:34

ScrollController suggestionBodyController = ScrollController();

需确认 mixin 的宿主 State 在 dispose 中调用了 suggestionBodyController.dispose()。若 mixin 无统一 dispose 钩子,存在轻微泄漏风险。


三、优先修复建议

优先级 问题 工作量
🔴 立即 S1 移除敏感日志 ~10 min
🔴 立即 S2 弱密码哈希(MD5→PBKDF2) ~2h
🔴 立即 P1 build 中创建 ScrollController 泄漏 ~30 min
🟡 本周 S4 敏感数据明文存储 ~4h
🟡 本周 S3/P4 正则在主线程 → Isolate ~2h
🟡 本周 P2 desktop leftTabBarScrollController 未 dispose ~5 min
🟡 本周 P3 磁盘 IO 串行循环 → Future.wait ~30 min
🟢 下周 S5 git 依赖锁定 commit hash ~1h
🟢 下周 P5 图片缓存上限配置 ~30 min
🟢 下周 P6 FutureBuilder future 缓存 ~30 min

四、无明显问题的方面(✅ 通过)

  • SQL 注入:Drift ORM 全程使用类型安全 API,未发现原始字符串拼接 SQL
  • SSL 证书:未发现 badCertificateCallback 绕过
  • HTTP 明文请求:所有请求均通过 HTTPS,无硬编码 HTTP 接口
  • 事务使用:批量数据库写入均正确包裹在 appDb.transaction()
  • 大部分 ScrollControllercomment_pagelog_list_pagemobile_layout_page_v2 等均在 dispose() 中正确释放
  • 路径遍历:下载路径由系统提供的基础路径拼接,未直接使用用户字符串

lietxia and others added 11 commits April 2, 2026 08:53
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant