# 项目审查与解决方案 > 本文件保留修复前的审查证据。当前状态见 [本地修复结果](LOCAL_REPAIR_RESULTS_2026-09-14.md),未部署服务器。 审查日期:2026-09-14。对象:当前 `sub2新站` 源码。结论:前端可构建,但权限边界、更新来源和 Live 计费存在需要优先处理的问题,当前全量测试也未通过,不宜据此认定达到正式发布条件。 本次进行了源码检查、前端类型检查/ESLint/构建、前后端测试。没有修改业务代码、操作线上服务器或执行生产数据库迁移。安全问题依据本地调用链确认,没有向线上发送越权或破坏性请求。仓库尚无提交,全部源码为未跟踪文件,因此本报告不是 Git 差异审查,也无法仅凭 Git 判断问题首次引入的版本。 ## 1. P1:普通管理员可以调用系统更新、回滚和重启 **证据**:`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,更新服务/重启函数调用次数为零;超级管理员在满足二次验证后可正常使用。测试使用替身,不实际更新或退出进程。 ## 2. P1:管理员账号保护可通过 API Key、订阅接口绕过 **证据**:`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/订阅;批量接口和并发角色变更同样受限,拒绝后数据库和缓存均无变更。 ## 3. P1:定制站的内置更新仍会下载原版程序 **证据**:`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 部署是否允许写入程序目录,本次未在服务器验证;只读目录可能使更新报错,并不能修正更新来源。 **解决方案**:定制版本先在服务端禁用原版在线更新/下载回滚,返回明确的部署说明。后续建立独立发行渠道,使用可比较的版本号,校验产品标识、资产校验和和数据库兼容版本。容器部署通过固定镜像标签或摘要更新,回滚按定制站流程执行。 **验收**:定制版本不会请求或应用原版更新资产;不兼容发行包在替换前被拒绝;回滚后登录、角色、保护策略和新增表仍兼容。 ## 4. P1(启用 Live 时):Live 会话绕过实际费用结算 **证据**:`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 限额及上游成本记录一致。 ## 5. P2:智能测试记录不能还原具体保护策略 **证据**:`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。 **验收**:连续运行不同策略,历史详情能区分并重现执行条件;排队后改变策略不会造成记录与实际调用不一致;公开用户接口不泄露内部快照。 ## 6. P2:全量测试不通过,部分测试与重构后的代码脱节 **本次实测**: - 前端:278 个测试文件,272 通过、6 失败;2036 项测试,1991 通过、45 失败。 - 36 项 `SettingsView` 测试和 1 项 `GroupsView.codexManifest` 测试未初始化/模拟新增的认证 Pinia store。实际应用在 `frontend/src/main.ts` 已安装 Pinia,因此这些失败不能直接推断为生产页面崩溃。 - 4 项首页安全测试仍用字符串搜索要求 `HomeView.vue` 直接调用 `sanitizeUrl`,而实际代码已迁移到 `useRelayBrand.ts`;该处仍在净化 URL,本次没有据此报告 XSS。 - 其余前端失败包括 3 项订阅卡翻译断言、1 项渠道供应商数量断言。 - 后端 `go test -tags=unit ./... -timeout=180s` 退出码为 1,失败包为 `internal/pkg/tlsfingerprint`、`internal/repository`、`internal/server`、`internal/service`。 - TLS 两项测试仍要求指针相等,而构造器已主动克隆 Profile;API 契约测试遗漏新增安全策略字段。服务测试还存在 Grok 媒体资格、OpenAI 模型别名、MiniMax 协议/地址等预期与实现不一致,需按产品契约逐项确认。 - 3 项备份测试因本机 Windows PATH 中没有 `sh` 失败,属于环境限制,不据此判定 Linux 备份失效。 **解决方案**:修复测试的依赖注入与授权场景;安全测试改为检验危险 URL 的实际渲染行为,TLS 测试验证克隆值与外部修改隔离;按明确的接口/模型契约修复代码或断言,不批量接受快照。为依赖 shell 的测试明确 Linux 执行环境或替换为跨平台测试辅助进程。 **验收**:全量前端测试和 Linux 后端单元测试通过,且保留危险 URL、跨角色操作和真实协议负例。`SettingsView.spec.ts` 已在根 Makefile 的 CI 必测清单中,当前失败会阻断前端 CI。 ## 7. P2:前端依赖安全检查引用了缺失脚本 **证据**:`.github/workflows/security-scan.yml:56` 调用 `tools/check_pnpm_audit_exceptions.py`,当前源码中该文件不存在。上一步 `pnpm audit ... || true` 依赖该脚本决定最终结果。 **影响**:依赖安装成功后,安全工作流会在脚本调用处失败,无法得到有效的漏洞豁免判定。这是检查失效/阻断,不是“已确认当前依赖可被利用”。 **解决方案**:恢复并纳入版本管理的检查脚本,校验审计 JSON、豁免编号/包名/到期日;审计请求失败、JSON 无效或未豁免高危项均应让 CI 失败。评估已过期的豁免,而不是仅延长日期。 **验收**:分别用无问题、允许且未过期、过期、未知高危、无效 JSON 的样本验证退出码;干净检出可执行该工作流。 ## 8. P2:新增数据库并发与清理测试没有接入 CI **证据**:`.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 报告能看到三个功能的数据库用例、并发用例和迁移用例执行成功,关键测试零跳过。 ## 修复顺序 1. 先收紧系统与资源所有者权限;禁用定制站的原版在线更新;收费环境关闭尚未结算的 Live。 2. 修复现有失败测试、恢复安全检查脚本,并把数据库验证接入 CI,建立可以信任的发布门槛。 3. 补齐智能测试执行快照,再按固定账号/模型/输入/代理开展保护策略对照。 每项修复建议单独提交,安全变更先用本地替身和隔离数据库验收,再进入测试部署。当前 Git 没有提交,开始修复前应先建立不含秘密的源码基线,并确认部署版本与该基线的对应关系。 ## 验证范围与限制 - 已通过:前端 `vue-tsc --noEmit`、全量 ESLint 错误检查、语言键测试、Vite 生产构建,以及 4 个后端包的账号保护/智能测试/权限定向回归。前端定向 426 项也通过,但已被更完整的全量结果补充。 - Vite 构建输出在临时目录 `C:/Users/30214/AppData/Local/Temp/sub2-review-20260914-frontend`,用时 16.68 秒。账号管理页面脚本约 788 KB、gzip 167 KB,建议后续按功能拆分;没有将构建体积提示当作功能故障。 - 没有新装或升级依赖,没有用本机 pnpm 11 重写锁文件;ESLint 通过直接调用现有安装版本完成,避免 PowerShell 未加引号的逗号参数影响检查。 - 本次未执行真实 PostgreSQL 集成验收、Linux/race 检查、真实上游调用、支付回调、线上 HTTPS/端口检查和最新依赖漏洞扫描。因此不对这些部分给出“已安全/已通过”的结论。 - 未对整个大型仓库逐行穷尽检查;上述问题是本轮有具体证据和可操作方案的发现。