From 5bb06d641c7d0b0e8cb01a5e64a65339fc1e77cb Mon Sep 17 00:00:00 2001 From: root Date: Sun, 27 Sep 2026 22:24:59 +0800 Subject: [PATCH] docs(plan): mark W4-E1 security items fixed in v0.1 acceptance Co-Authored-By: Claude Opus 5.5 --- docs/plan/04-v0.1-acceptance.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/plan/04-v0.1-acceptance.md b/docs/plan/04-v0.1-acceptance.md index c18e310b..76e8eed4 100644 --- a/docs/plan/04-v0.1-acceptance.md +++ b/docs/plan/04-v0.1-acceptance.md @@ -41,17 +41,17 @@ | # | 问题 | 位置 | 修法 | |---|---|---|---| -| S1 | 持 `organize.execute` 的 API Key 可以 `POST /approvals` 再 `PATCH /approvals/{id}` 自己批准,再去调 `download.submit` 或 `organize.execute`,单次审批就形同虚设 | `packages/contracts/src/api.ts`(approvals 的 scope),`modules/agent/routes.ts:~280-311` | 把审批的决定动作(PATCH 改为 approved)的 scope 改成 `admin`,或者用 `requireAdminSession`;并禁止 `decided_by` 与计划发起人是同一个 api_key | -| S2 | 服务端 logger 的脱敏覆盖面比 agent 和 adapters 的实现窄。实测 `redactString("/rss?rsskey=abc&torrent_pass=dead&authkey=zz&downhash=q1&c_secure_pass=cc")` 只遮住了 passkey。现在一共有 4 份脱敏实现(W1 的 S8 还没解决,反而多了一份) | `apps/server/src/logger.ts:6-8`,`packages/agent/src/redact.ts`,`packages/adapters/src/{rss,browser}/redact.ts` | 在 contracts(或新建的共享包)里保留唯一一份实现,取 agent 版的参数全集;logger 改为复用它;在 `logger.test.ts` 加上述 5 类参数的用例 | -| S3 | TMDB 网络错误时,`TmdbError` 的 message 直接拼上 Deno fetch 的错误信息。Deno 的这条信息里带完整 URL,也就带着 `api_key`;`cause` 里同样有(W1 的 S7 没有解决) | `packages/adapters/src/tmdb/client.ts:171-175` | message 先经过 redact,`cause` 不透传;或者强制使用 Bearer(accessToken) | -| S4 | 服务端授权器的单次审批分支(payloadHash/revision 变化后失效、过期、用后标为 consumed、approvalId 与计划不符)**没有任何测试**。AT15/AT16 在产品层也没有被验证 | `apps/server/src/modules/agent/authorize.ts:115-137` | 新增 `test/agent/authorize.test.ts`,对真实 PG 覆盖 6 条正反例;再加一条 e2e:审批后改计划 → 被拒 → 重新审批 → 成功且只消费一次 | +| S1 | 持 `organize.execute` 的 API Key 可以 `POST /approvals` 再 `PATCH /approvals/{id}` 自己批准,再去调 `download.submit` 或 `organize.execute`,单次审批就形同虚设 | `packages/contracts/src/api.ts`(approvals 的 scope),`modules/agent/routes.ts:~280-311` | 把审批的决定动作(PATCH 改为 approved)的 scope 改成 `admin`,或者用 `requireAdminSession`;并禁止 `decided_by` 与计划发起人是同一个 api_key **已修(f48e1aa):`POST /approvals`、`PATCH /approvals/{id}` 标 `auth:"session"`,只接受管理员会话,API Key 一律 403** | +| S2 | 服务端 logger 的脱敏覆盖面比 agent 和 adapters 的实现窄。实测 `redactString("/rss?rsskey=abc&torrent_pass=dead&authkey=zz&downhash=q1&c_secure_pass=cc")` 只遮住了 passkey。现在一共有 4 份脱敏实现(W1 的 S8 还没解决,反而多了一份) | `apps/server/src/logger.ts:6-8`,`packages/agent/src/redact.ts`,`packages/adapters/src/{rss,browser}/redact.ts` | 在 contracts(或新建的共享包)里保留唯一一份实现,取 agent 版的参数全集;logger 改为复用它;在 `logger.test.ts` 加上述 5 类参数的用例 **已修(f48e1aa):键表与规则收到 `@umt/contracts` 的 `redact.ts`,logger 与 `redactForModel` 共用;adapters 的 rss/browser 两份仍独立,留到 M2 收口** | +| S3 | TMDB 网络错误时,`TmdbError` 的 message 直接拼上 Deno fetch 的错误信息。Deno 的这条信息里带完整 URL,也就带着 `api_key`;`cause` 里同样有(W1 的 S7 没有解决) | `packages/adapters/src/tmdb/client.ts:171-175` | message 先经过 redact,`cause` 不透传;或者强制使用 Bearer(accessToken) **已修(f48e1aa):message 经 `redactUrl`,`cause` 只留脱敏后的 name/message** | +| S4 | 服务端授权器的单次审批分支(payloadHash/revision 变化后失效、过期、用后标为 consumed、approvalId 与计划不符)**没有任何测试**。AT15/AT16 在产品层也没有被验证 | `apps/server/src/modules/agent/authorize.ts:115-137` | 新增 `test/agent/authorize.test.ts`,对真实 PG 覆盖 6 条正反例;再加一条 e2e:审批后改计划 → 被拒 → 重新审批 → 成功且只消费一次 **已修(f48e1aa):`test/agent/approvals.test.ts` 覆盖 8 条拒绝分支与改计划→重批→只消费一次→不可重放;e2e 版留给 E2 系列** | | S5 | M1 要求的"一部常规剧集"没有端到端链路。E1 和 E2 只覆盖电影;剧集只在识别单测(resolve)和整理单测(organize-move)里分别出现过 | `apps/server/test/e2e/` | 新增 E7:RSS 剧集 S01E01 → 识别出 series 和单元 → Slot → qB → Copy 入库;第二集改译名后仍进旧目录 | | S6 | AT24(管理标签被移除、qB 任务消失)在服务端没有测试。代码在 `downloads.ts:359-383` 里,只有 domain 层的 `download-state` 单测覆盖 | `__tests__/vertical.test.ts` | 补两个断言:去掉标签后 `managed=false`、历史保留、之后不再暂停也不再清理;任务消失后 `removed`,并且库文件还在 | -| S7 | 前端 Agent 页在生产构建里也能用 `?mock=1` 打开 mock 模式(sessionStorage 会记住),可能把演示数据当成真实运行展示,与 AT78 冲突 | `apps/web/app/api/agent.ts:37-48` | 只在 `import.meta.env.DEV` 或设置了 `VITE_AGENT_MOCK` 时启用 | +| S7 | 前端 Agent 页在生产构建里也能用 `?mock=1` 打开 mock 模式(sessionStorage 会记住),可能把演示数据当成真实运行展示,与 AT78 冲突 | `apps/web/app/api/agent.ts:37-48` | 只在 `import.meta.env.DEV` 或设置了 `VITE_AGENT_MOCK` 时启用 **已修(f48e1aa):只在 DEV 构建或后端 `pi.provider = "fake"` 时生效,生产构建忽略并清掉记住的开关(`app/api/agent-mock.test.ts`)** | | S8 | 引擎面板只有 cancel,没有 retry/rerun;§3.1-10 要求 "cancel/retry 操作"(02 backlog 也列了这一项) | `apps/web/app/routes/console/engine*.tsx`,`modules/auth-settings/engine-routes.ts` | 加 `POST /engine/runs/{id}/rerun`,用我们自己的 enqueue、带新幂等键实现,并写审计 | | S9 | 账户 TOTP 在控制台没有入口(只有登录页的第二步),02 backlog 里是应修项 | `apps/web/app/routes/console/layout.tsx` | 账户弹层接上 `GET/POST /auth/totp` | -| S10 | API 注册表把 `/auth/setup|login|session|logout` 标成 `read`,但实际上前三个免鉴权;`/auth/password`、`/auth/totp` 标成 `admin`,实际要求 admin 会话。按注册表生成的 scope 文档会误导外部集成方 | `packages/contracts/src/api.ts` | EndpointDef 增加 `auth: "public" | "session" | scope`,并加测试断言它与 `PUBLIC_AUTH_PATHS` 一致 | -| S11 | 文档与实际不一致:任务书 §2、§4 还写着 `pnpm ci`;deploy.md 的验证记录写"21 个迁移",现在是 22 个(0017 号空缺);ci 耗时写"约 75 秒",实测约 135 秒;testkit README 没提 emby/browser mock;adapters 和 web 的 README 各只有 3 行 | `docs/plan/00-first-version-plan.md`,`docs/runbook/deploy.md:128`,`packages/testkit/README.md`,`packages/adapters/README.md` | 统一更新;adapters README 列出 8 个子模块的入口,并链接各子目录的 README | +| S10 | API 注册表把 `/auth/setup|login|session|logout` 标成 `read`,但实际上前三个免鉴权;`/auth/password`、`/auth/totp` 标成 `admin`,实际要求 admin 会话。按注册表生成的 scope 文档会误导外部集成方 | `packages/contracts/src/api.ts` | EndpointDef 增加 `auth: "public" | "session" | scope`,并加测试断言它与 `PUBLIC_AUTH_PATHS` 一致 **已修(f48e1aa):`EndpointDef.auth` 为 `public`/`session`,`api-registry.test` 断言与 `PUBLIC_AUTH_PATHS` 一致、session 端点拒绝 Bearer** | +| S11 | 文档与实际不一致:任务书 §2、§4 还写着 `pnpm ci`;deploy.md 的验证记录写"21 个迁移",现在是 22 个(0017 号空缺);ci 耗时写"约 75 秒",实测约 135 秒;testkit README 没提 emby/browser mock;adapters 和 web 的 README 各只有 3 行 | `docs/plan/00-first-version-plan.md`,`docs/runbook/deploy.md:128`,`packages/testkit/README.md`,`packages/adapters/README.md` | 统一更新;adapters README 列出 8 个子模块的入口,并链接各子目录的 README **已修(f48e1aa):任务书 pnpm→deno task、ci 耗时、deploy.md 迁移数、testkit/adapters/web README;adapters 实际 7 个子模块** | ### 1.3 建议