本文件保留修复前的审查证据。当前状态见 本地修复结果,未部署服务器。
审查日期:2026-09-14。对象:当前 sub2新站 源码。结论:前端可构建,但权限边界、更新来源和 Live 计费存在需要优先处理的问题,当前全量测试也未通过,不宜据此认定达到正式发布条件。
本次进行了源码检查、前端类型检查/ESLint/构建、前后端测试。没有修改业务代码、操作线上服务器或执行生产数据库迁移。安全问题依据本地调用链确认,没有向线上发送越权或破坏性请求。仓库尚无提交,全部源码为未跟踪文件,因此本报告不是 Git 差异审查,也无法仅凭 Git 判断问题首次引入的版本。
证据:backend/internal/server/middleware/user_hierarchy.go:108 的超级管理员限制仅覆盖管理密钥、备份、数据管理、设置及插件写入;backend/internal/server/routes/admin.go:703 注册的 /system/update、/system/rollback、/system/restart 未受该限制。处理器没有再检查超级管理员身份。重启最终调用 backend/internal/pkg/sysutil/restart.go,在 Linux 执行 os.Exit(0)。
触发与影响:已经完成后台协议确认、拥有有效普通管理员 JWT 的用户可以提交上述请求。重启会中断服务;更新/回滚在可写二进制部署下可以替换程序,影响远超普通账号管理权限。系统操作锁和幂等机制不能替代授权。
解决方案:对系统变更接口显式挂载超级管理员权限检查,并复用敏感操作二次验证。处理器或服务入口保留独立身份检查;前端同步隐藏无权限按钮。
验收:普通管理员调用三个接口均返回 403,更新服务/重启函数调用次数为零;超级管理员在满足二次验证后可正常使用。测试使用替身,不实际更新或退出进程。
证据:user_hierarchy.go:37 只对 /api/v1/admin/users 下的写请求检查目标用户角色。backend/internal/server/routes/admin.go:223 的 PUT /admin/api-keys/:id 和第 715 行的订阅管理接口均在该范围之外。backend/internal/service/admin_group.go:1342 直接读取任意 Key 并修改分组,未检查 Key 所属用户是否为管理员;subscription_service.go 的撤销、分配等操作也没有等价的操作者/目标角色校验。
触发与影响:普通管理员取得另一管理员的 Key ID 后,提交 {"group_id":0} 即可进入解绑流程;同样可以通过订阅 ID 撤销管理账号订阅。与项目文档“普通管理员不能操作超级管理员”的边界冲突。这里的 API Key 是网关调用 Key,不应混同于全局管理密钥。
解决方案:建立按资源所有者授权的公共入口:先把 Key ID、订阅 ID 解析为用户 ID,再校验操作者与目标角色。将认证操作者传入这些管理服务,并在最终写入事务内重新确认角色,避免只补路径黑名单。普通用户自助操作、兑换和系统分配使用各自明确的权限入口。
验收:普通管理员可以管理普通用户资源,不能修改任何管理员的 Key/订阅;批量接口和并发角色变更同样受限,拒绝后数据库和缓存均无变更。
证据:backend/internal/service/update_service.go:33 将更新仓库固定为 Wei-Shaw/sub2api,第 165 行的更新流程下载该仓库发行资产并替换当前可执行文件。第 656 行的版本解析对 chengchuan-features-20260912、relay-v3-dev 这类版本会得到零值版本。docs/MANAGEMENT_FEATURES.md 已明确记录旧程序与 super_admin 的回滚兼容要求,但内置更新不执行这些步骤。
触发与影响:可写二进制部署中,点击更新可能将澄川定制程序替换为上游发行版,丢失定制功能,并产生角色和数据库兼容风险。当前 Docker 部署是否允许写入程序目录,本次未在服务器验证;只读目录可能使更新报错,并不能修正更新来源。
解决方案:定制版本先在服务端禁用原版在线更新/下载回滚,返回明确的部署说明。后续建立独立发行渠道,使用可比较的版本号,校验产品标识、资产校验和和数据库兼容版本。容器部署通过固定镜像标签或摘要更新,回滚按定制站流程执行。
验收:定制版本不会请求或应用原版更新资产;不兼容发行包在替换前被拒绝;回滚后登录、角色、保护策略和新增表仍兼容。
证据:backend/internal/handler/openai_live.go:82 只在建连前检查支付资格;backend/internal/service/openai_live.go:827 的结束流程直接写用量日志,没有调用常规结算管道,费用与 token 数均保持零值。现有 openai_live_lifecycle_test.go:268 甚至明确断言 TotalCost、ActualCost 为零。
触发与影响:分组启用 allow_live 且用户通过初始资格检查后,可以持续消耗上游会话资源,本地余额和订阅消耗不随该会话结算。并发限制和会话时长上限不能替代累计计费。本次未确认线上是否启用了该功能;如果产品有意免费,仍需单独制定免费额度与成本上限。
解决方案:在结算完成前对收费分组关闭 Live。确定计费口径后接入预占、增量用量/时长核算、余额不足终止及最终幂等结算,覆盖断线、重启和租约到期。明确免费的场景使用独立预算策略。
验收:正常结束和异常结束均只结算一次;低余额无法无限续开;余额、订阅、Key 限额及上游成本记录一致。
证据:backend/internal/service/intelligent_test_types.go:8 的 ConfigSnapshot 仅保存提示词、模型、评估器、答案与超时;第 22 行的记录只用 AntiDegradation bool 描述保护状态。backend/migrations/247_intelligent_tests.sql:29 也没有策略版本、身份模式、TLS、代理、并发快照字段。intelligent_test_runner.go:104 执行时读取当前账号,随后再次读取账号进行实际调用。
触发与影响:同一账号从 legacy 切换到 mode1 或其他策略后,历史记录都只显示保护开启,无法可靠区分测试时到底使用哪种策略。排队期间改配置还会改变实际执行条件。这削弱了项目策略矩阵要求的对照实验与故障复盘能力。
解决方案:新增不可变的执行快照,保存策略 ID/版本、身份模式、TLS 模板摘要、代理引用、有效并发、实际模型和提示词摘要。快照必须来自真正发送请求所用的同一份账号副本;分别保存提交时和执行时状态,明确排队期间变更的处理规则。不得保存凭据、身份种子或带认证信息的代理 URL。
验收:连续运行不同策略,历史详情能区分并重现执行条件;排队后改变策略不会造成记录与实际调用不一致;公开用户接口不泄露内部快照。
本次实测:
SettingsView 测试和 1 项 GroupsView.codexManifest 测试未初始化/模拟新增的认证 Pinia store。实际应用在 frontend/src/main.ts 已安装 Pinia,因此这些失败不能直接推断为生产页面崩溃。HomeView.vue 直接调用 sanitizeUrl,而实际代码已迁移到 useRelayBrand.ts;该处仍在净化 URL,本次没有据此报告 XSS。go test -tags=unit ./... -timeout=180s 退出码为 1,失败包为 internal/pkg/tlsfingerprint、internal/repository、internal/server、internal/service。sh 失败,属于环境限制,不据此判定 Linux 备份失效。解决方案:修复测试的依赖注入与授权场景;安全测试改为检验危险 URL 的实际渲染行为,TLS 测试验证克隆值与外部修改隔离;按明确的接口/模型契约修复代码或断言,不批量接受快照。为依赖 shell 的测试明确 Linux 执行环境或替换为跨平台测试辅助进程。
验收:全量前端测试和 Linux 后端单元测试通过,且保留危险 URL、跨角色操作和真实协议负例。SettingsView.spec.ts 已在根 Makefile 的 CI 必测清单中,当前失败会阻断前端 CI。
证据:.github/workflows/security-scan.yml:56 调用 tools/check_pnpm_audit_exceptions.py,当前源码中该文件不存在。上一步 pnpm audit ... || true 依赖该脚本决定最终结果。
影响:依赖安装成功后,安全工作流会在脚本调用处失败,无法得到有效的漏洞豁免判定。这是检查失效/阻断,不是“已确认当前依赖可被利用”。
解决方案:恢复并纳入版本管理的检查脚本,校验审计 JSON、豁免编号/包名/到期日;审计请求失败、JSON 无效或未豁免高危项均应让 CI 失败。评估已过期的豁免,而不是仅延长日期。
验收:分别用无问题、允许且未过期、过期、未知高危、无效 JSON 的样本验证退出码;干净检出可执行该工作流。
证据:.github/workflows/backend-ci.yml:37 仅执行 make test-unit 和 make test-integration。账号保护/角色事务测试使用单独的 protectionintegration build tag;用户清理、智能测试和账号保护还分别要求 USER_CLEANUP_TEST_DSN、INTELLIGENT_TESTS_TEST_DSN、PROTECTION_TEST_DSN。CI 没有配置这些变量,测试缺少 DSN 时调用 t.Skip。
影响:即使普通单元测试与集成测试将来全绿,清理并发、互相降级、排队幂等和迁移保护仍可能没有执行。历史验证日志不能代替当前提交的验收。
解决方案:增加独立 PostgreSQL 测试 job,创建专用测试数据库、设置上述 DSN,并显式运行 -tags=protectionintegration。在该 job 中缺少测试环境应报错;检查 JSON 测试报告,确保关键用例确实执行而非跳过。涉及删除/迁移的测试只对专用数据库执行。
验收:CI 报告能看到三个功能的数据库用例、并发用例和迁移用例执行成功,关键测试零跳过。
每项修复建议单独提交,安全变更先用本地替身和隔离数据库验收,再进入测试部署。当前 Git 没有提交,开始修复前应先建立不含秘密的源码基线,并确认部署版本与该基线的对应关系。
vue-tsc --noEmit、全量 ESLint 错误检查、语言键测试、Vite 生产构建,以及 4 个后端包的账号保护/智能测试/权限定向回归。前端定向 426 项也通过,但已被更完整的全量结果补充。C:/Users/30214/AppData/Local/Temp/sub2-review-20260914-frontend,用时 16.68 秒。账号管理页面脚本约 788 KB、gzip 167 KB,建议后续按功能拆分;没有将构建体积提示当作功能故障。