fix(source): 中间段符号链接逃逸堵截(无条件 realpath);注释口径修正

- validateInputPath 原先只在末段是符号链接时 realpath——中间目录段含 junction
  (链接在白名单内、目标在外,Windows 免特权可构造)时词法前缀判定被绕过,
  会读到白名单外内容(评审 PoC 复现)。改为 lstat 存在性探测后无条件
  realpath(abs),整条路径解析到物理位置再做白名单与类型判定;断链场景
  realpath 抛 ENOENT 仍归入 INPUT_NOT_FOUND(语义不变)
- 回归测试:中间段 junction 逃逸用例(incoming/junc → pkg-outside/escape.xml,
  断言 PATH_NOT_ALLOWED + 零落库);已对修复前代码验证为红(逃逸被接受)、
  修复后绿
- 注释口径修正:registerSource 的"任何失败零落库零复制"只在校验阶段成立——
  摄取中途失败不回滚已落位副本(content-addressed、幂等无害),但 DB 写入
  在全部摄取完成后才开始,任何失败零落库不变
- 计划勾选:Task 7 "CLI 执行机制(钉死)"(已实现并双命令冒烟验证)

全套 261 测试绿(+1);typecheck / lint 绿

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-21 12:47:07 +08:00
parent 46b823e667
commit 9732e1b684
3 changed files with 58 additions and 18 deletions
@@ -127,7 +127,7 @@ cms-one/
- 副本落 `storage/snapshots/<kind>/<sha256前2位>/<sha256>`;原输入不再被后续任务读取
- 路径安全:register 的输入路径必须在允许目录(storage/incoming 或显式绝对路径白名单校验——拒绝越界读取);**wxr:probe 例外:只读诊断命令,接受任意可读路径(Task 8 直读 cms 即用此通道),不写任何东西**
- [x] Step 2 实现服务(copy 流式+sha256 双算、媒体清单哈希、DB 写入、报告输出 **artifacts/register/<runId>/reports/**——阶段 2 无 import_jobs,runId 为本次 register 运行生成)+ CLI 接线
- [ ] **CLI 执行机制(钉死)**:根 devDependency 加 `tsx`;根 package.json scripts 加 `"sf": "tsx scripts/sf.ts"`;`commander` **定为根依赖**(CLI 在根,adapter-wp 纯函数包不消费);CLI 经 tsx 直吃 TS 源码(**无需预构建 dist**;apps/api 的 Nest 装饰器源码同样由 tsx 运行)。Task 8 命令相应为 `pnpm sf wxr:probe --wxr ...`
- [x] **CLI 执行机制(钉死)**:根 devDependency 加 `tsx`;根 package.json scripts 加 `"sf": "tsx scripts/sf.ts"`;`commander` **定为根依赖**(CLI 在根,adapter-wp 纯函数包不消费);CLI 经 tsx 直吃 TS 源码(**无需预构建 dist**;apps/api 的 Nest 装饰器源码同样由 tsx 运行)。Task 8 命令相应为 `pnpm sf wxr:probe --wxr ...`
- [x] Step 3 绿(75+新增)→ commit `feat(source): 输入登记服务与 sf CLI(content-addressed 快照)`
### Task 8: 真实 WXR 冒烟(阶段 2 验收)