Skip to content

测试与质量审计报告 ​

审计日期:2026-07-11
范围:Go 后端 / Vue 前端 / Flutter 移动端 全栈
方法:工具实证(go test/vet/cover、vue-tsc、vitest、flutter analyze)+ 六维度并行代码审计 agent 交叉验证,所有发现均经代码路径确认


一、测试基础设施基线(实证) ​

工具结果说明
go test -short✅ 47 包全绿short 模式跳过真实网络用例
go vet ./...✅ 零告警
go test -cover🟡 总 42.2%严重失衡,见下
vue-tsc --noEmit✅ 零错误
vitest✅ 12 passed / 3 文件覆盖极薄
flutter analyze✅ No issues

覆盖率失衡(handler 层裸奔) ​

包覆盖率包覆盖率
api/v1(所有 HTTP handler)4.5%🔴ws(WebSocket 广播)1.8%
app(装配/启动)3.1%🔴agent(RPC 文件管理)3.6%
db/oper9.7%🟠workflow/nodes13.2%

测试投在「纯逻辑/中间件」(api 中间件 98.5%、event 95%、module 100%、keepseed 87.9%),而「HTTP handler + 并发」是真空地带——这与安全/契约 P1 全集中在 api/v1 高度吻合。

race detector 现状(重要) ​

  • CI 已配置:ci.yml:19 go test ./... -race,ubuntu runner 默认 CGO_ENABLED=1(自带 gcc),可正常运行 race detector。
  • 但手动触发:ci.yml 触发条件是 workflow_dispatch,非 push/pull_request,故不会随每次提交自动验证。
  • 本地 Windows 不可用:项目 CGO_ENABLED=0(纯 Go SQLite 驱动),-race 强制依赖 cgo,Windows 无 gcc → 本地开发无法跑 race。
  • 建议:① 将 ci.yml 触发扩展为 on: [workflow_dispatch, pull_request] 让 race 在 PR 自动跑;② race job 加 -short 跳过不稳的真实网络用例,聚焦并发逻辑。

二、六维度发现汇总 ​

维度评级P0P1P2P3核心结论
并发与竞态B+0385mutex 纪律严明,3 个真实数据竞争潜伏
安全B+0347TMDB SSRF/插件签名/未签名安装
健壮性B07183抓取解析+HTTP超时零发现极扎实;chain 吞 error + 输入校验是短板
契约一致性B+101014Flutter 通知配置数据破坏 + Flutter 系统性滞后 Web
测试质量C+————现有测试扎实但广度不足:前端3%/Flutter23%/后端65%无同级测试

三、本轮已修复(2026-07-11) ​

P0(功能必坏) ​

  1. Flutter 通知设置保存静默清空 DingTalk/Feishu/Email
    • 根因:Flutter NotifyConfig 只定义 5 通道,_buildInput() 漏发 3 通道 → 后端 mergeNotifySettings 用零值覆写。
    • 修复:notification.dart 补全 3 通道模型(含 toJson)+ notification_settings_page.dart _buildInput() 用 collection-if 回发 3 通道当前值。

P1(跨维度) ​

安全

  • TMDB 越权:router.go PUT /media/config 加 RequireAdmin()(原任何 VIP 用户可改 TMDB 域名指向攻击者服务器,泄露 API key + 内网扫描)。
  • system/logs 非 admin:GET/DELETE /system/logs 加 RequireAdmin()(原普通用户可读/清日志掩盖痕迹)。
  • notif/test 非 admin:POST /notification/test 加 RequireAdmin()。
  • 插件签名信任模型(P1-S2/S3):market.go 新增 verifySignatureTrusted——用独立信任锚(内置官方公钥 OfficialMarketPublicKey + 用户配置 plugin.trusted_public_keys)验证,不再用市场索引自带的 idx.PublicKey(被验证数据本身,可篡改);未签名插件需 plugin.allow_unsigned=true 或 MarketInstallOptions.AllowUnsigned 显式 opt-in;新增 GET/PUT /plugins/trust-config(RequireAdmin)管理配置。

并发(race detector 会标记,CI 可捕获)

  • Broadcaster.notice 数据竞争:broadcaster.go SetNotice + detectDone 改为经 stateMu 同步(消除 SetNotice 写与 Run goroutine 读的无同步访问)。
  • Webhook 脱离请求上下文:webhook.go 工作流执行 context 从 c.Request.Context() 派生(原 context.Background() 10 分钟,客户端断开不取消)。

健壮性

  • auth Encrypt 吞错:auth.go issueForHxpt 加密失败现中止登录(原落空串 → 定期 VIP 重验解密失败 → 用户静默掉 VIP + 脏数据)。
  • 分页 limit 无上限 DoS:notice/checkin_record/rss/workflow 4 处加上限封顶(1000/1000/500/500),checkin_record 补 offset 负数钳制。
  • 公开端点无 body 限制:新增 body_limit.go MaxBody 中间件(64MB 兜底),挂载到 engine 全局链(防未认证 POST /auth/hxpt/login 塞 100MB JSON)。
  • chain 层吞 DB error:workflow.go Update/Delete 在 DB 读失败时中止返回错误(防旧触发器残留致重复触发/ghost 触发器 panic);subscribe.go 去重链 HitExists 失败时跳过种子(宁可漏推不可重复下载),AddHit/MarkChecked/SetStatus 改 slog 不静默。

回归验证 ​

  • go build ./... exit 0;flutter analyze 全量 No issues。
  • 全量 go test -short + go vet:唯一 FAIL 是 TestExternalHTTPPluginAutoRestart(internal/plugins)——该测试启动真实子进程测自动重启时序,全包并行高负载下 39s 超时 fail,单独重跑 5.5s 绿,属预先存在的 flaky 时序测试,与本次改动无关(本次未改 plugins)。
  • 建议给该 flaky 测试加 testing.Short() 守卫或放宽内部 deadline,提升 race/负载下的鲁棒性。

四、未修与后续建议 ​

建议单独评估的 P1(涉及信任模型或行为改变) ​

  • P1 插件 zip 炸弹(market.go unzipSafe):io.Copy 无单条目/总解压上限。参照 ptd.go 的 16MB/条目、64MB 总量保护补上。

P2 节选 ​

  • 并发:groupchat 重试 timer 不可取消(卸载后仍发站消息)、Email.Send 取消时 goroutine 泄漏、RTSP 多处未同步写入、chain 所有内存缓存缺 singleflight(冷启动雪崩)、FlipWS 半开连接永久阻塞、Broadcaster.Run 用 context.Background() 关闭不取消。
  • 安全:HXPT_EMERGENCY_ADMIN 无密码后门、前端 :href="javascript:" XSS(Rss/Freeleech/Reseed/Search 4 组件,建议加 safeUrl() 工具)、JWT+AES 密钥复用无 HKDF 隔离、SMTP 头注入。
  • 健壮性:keepseed 默认值违反文档(保种不暂停)、assessment/medal BatchUpsert 吞错致数据不入库、多个 oper struct 缺 max= binding 标签。
  • 契约:Flutter 完全缺插件模块、缺 harem/assessment/messages 单站刷新、accept-host-key 两端无 UI(SSH host key 变更后永久报错)、RSS check 吞错误显示"新增0条"、harem permanent_count/temporary_count 未解析。

测试体系改进 ​

  1. race detector 自动化:ci.yml 加 pull_request 触发 + race job 加 -short。
  2. 补测靶点(HIGH-RISK 未测 Top):plugins/external.go(993行)、market.go(784)、db/models.go(595)、chain/medal.go(580)、site/types.go(533)、api/v1 handler 层(覆盖率从 4.5% 起步)。
  3. 前端补测:savept.ts(200 行正则启发式)、http.ts 契约层、siteError.ts。

2026-07-12 落地:见文末「六、测试覆盖补强」——api/v1 4.5%→10.4%、site→71.4%、plugins→60.2%;前端 API 层补 40 例;并顺手补了关键 max= binding 硬化。db/models.go 经核实为纯声明式(零方法零函数),无可测逻辑,审计按行数误判。

  1. 契约对齐自动化:考虑 OpenAPI 生成或三端契约快照测试,根治"Flutter 滞后 Web"。

五、值得肯定的防线 ​

  • 站点抓取解析(25 文件逐行验证):选择器 .Eq(N) 前 .Length() 守卫、regexp FindStringSubmatch 全检查长度、ParseUserInfoFields 空提取不覆盖有效值——零 panic/nil 发现。
  • HTTP 超时:site/entertainment/iyuu 全走受保护 httpclient.Client(30s 兜底 + SSRF 拨号守卫);media/cookiecloud 直用 http.Client 也都显式设 Timeout——零发现。
  • mutex 规范:每个共享 map 都有专用锁,复制后上锁一致,无复制 mutex。
  • SSRF/SQL注入/命令注入/路径穿越/JWT/CORS/CSWSH/Webhook HMAC/v-html 经多轮审计验证稳健。

六、测试覆盖补强(2026-07-12,测试覆盖专项) ​

落实第四节「测试体系改进 #2/#3」。六维度审计 agent 交叉测绘可测面后,以最低 DI 成本补齐裸奔的高价值靶点,并把审计新发现的 max= 缺口顺手硬化。全量 go test -short ./... + go vet ./... + vitest + vue-tsc 全绿,零回归。

覆盖率变化(实证) ​

包前后
api/v1(HTTP handler)4.5%10.4%
site—71.4%
plugins—60.2%
前端 API 层3%新增 40 例(http/savept/siteError)

api/v1 的 nil 依赖早返回测试到 10.4% 即触顶——剩余 90% 的语句在调用 chain/DB 的深路径里,需 chain/DB 桩才能继续推(独立工程)。本次聚焦安全/契约/校验面(与 P1 集中区吻合)。

新增测试文件(9 个,275 Go 用例 + 40 前端用例) ​

HIGH-RISK 纯逻辑(零 DI)

  • types_test.go:parseRatioFloat、InviteStatus 可购买算术、InviteeInfo.IsDead/IsPending/IsLowRatio/HasNoTrafficData 穷尽表测(补全 i18n/边界;harem_test.go 原仅各 1 例)。
  • external_test.go:ACL 核心 hasPluginPermission(*/host:*/plugin:*/X:* 全分支)、replaceVars/restartDelay/resolveWorkDir/hostPathValue/cloneTimePtr、host 鉴权 authorized、writeHostJSON/decodeHostJSON、RuntimeStatus/DefaultConfig/hostContext、pluginOutputWriter 行切分(含 4000 截断)。

api/v1 handler 层

  • helpers_test.go:16 个未导出纯 helper 表测——clampDays、classifyCommandRisk(安全关键高/中/低分级)、parseLimit、splitCSV、maskSecret、mediaImageReferer、uniqueTargetIDs、commandRunSource/Status、atoiDefault/parseUintSlice、normalizedURLHost/Key、definitionScore、mergeStrings、parseMailbox、boolStr、pluginAssetScope。
  • binding_test.go(扩展):rssReq/wfReq/webhookReq 全字段 max= 越界拒 + 9 个 required 结构体必填校验 + TestMaxHardenedBounds 回归硬化。
  • handlers_earlyreturn_test.go:nil/最小依赖构造 handler 命中早返回——SystemHandler(nil update)、Subscribe.Create 手动必填、Workflow/Webhook.Create 坏 JSON、CommandRun.Create 4 道门(空命令/空 targets/>50/高危缺确认)、Media.Recommend nil 优雅、Auth.HxptCaptcha 503。
  • handlers_httptest_test.go:全 gin 分派 e2e 校验——Dashboard.History 坏 id、Downloader.Add 坏 id/缺 URL、Media.Search/Discover 缺 keyword、Notification.Test 缺 Title、KeepSeed.Find 缺 Name。

前端

  • savept.test.ts:状态元信息/MP 状态/站庆文案/公告 4 分区/tokenizeNoticeText 站点名高亮分词。
  • siteError.test.ts:抓取错误友好化全分支。
  • http.test.ts:JWT 注入 + 错误文案映射 + 401/403-VIP 清 token(adapter 注伪响应,stub location 规避 jsdom 导航)。

顺手硬化:关键 max= binding(审计「缺 max=」缺口) ​

给高危请求结构体补 max=/dive,max=(参照 subReq/rssReq 金标准选宽松上界,仅拒异常超长输入;[]string 用 dive,max= 限单元素,不限个数以免误伤批量操作,body_limit 中间件 64MB 兜底总量):

  • mediaConfigReq(5 个 secret)、notifyConfigReq(~20 嵌套字段)、hashesReq + downloader_ops 全部 Hashes/Tags/Names/URLs(dive,max=)、addReq(URL/Category/Tags/SavePath)、hxptLoginReq/hxptCookieLoginReq、configReq.IYUUToken、settingReq.SavePath、commandRunRequest(Command/Source/Note)、execRequest.Command/powerRequest.Action、pluginMarketSourcesReq.Sources/pluginTrustConfigReq.TrustedPublicKeys/pluginAction/Event/Bridge/Install 各串字段。

binding_test.go::TestMaxHardenedBounds 断言超长必拒、合法通过。其余低危结构体(entertainment 数值类、plugin map 类)记入 backlog,留给「binding 校验 max=」全量工程。

关键发现 ​

  • db/models.go(595 行)无任何方法/函数,纯声明式 struct——审计「按行数排高危」误判,无逻辑可测,编译器已校验 tag。HIGH-RISK Top 应以「可测逻辑密度」而非行数排序。
  • hasPluginPermission 实际行为:*/host:*/plugin:* 在 switch 直命中→对任意 required 全授权(超级授权);其余 X:* 走后缀前缀匹配仅授 X 类别。测试据此断言。
  • parseRatioFloat 的逗号是千分位剥离("1,000.5"→1000.5),非欧洲小数点("1,5"→15)。