Files
chis_osi/docs/review/2026-07-06-phase0-review.md
ilaandClaude Opus 4.8 12039d8c46 docs: 新增 Phase 0 代码评审文档(T-001~T-005)
按全栈视角审核脚手架/签名/传输/Call/验收,记录达标判定与分级整改项

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:23:15 +08:00

8.1 KiB
Raw Permalink Blame History

Phase 0 代码评审 · 脚手架与契约骨架(T-001~T-005)

  • 评审人:Claude(全栈视角)
  • 日期:2026-07-06
  • 范围:tasks.md Phase 0 全部任务 T-001~T-005,对应 docs/05 阶段 0
  • 方法:静态代码审读 + git 历史核对。本环境离线无 go 1.24 工具链,未能动态跑 go build/test;涉及运行时行为处已标注"未动态复验"。

结论(Gate 判定)

Phase 0 未通过验收门槛(有条件达标)。

T-001~T-004 的设计与实现质量良好、分层清晰、测试到位。但 T-005 及其修复代码整体未提交,导致仓库层面无法复现"Go 侧真实握手成功",违反看板自身的"DONE 需可复现证据 + clean-state"规则;且传输层存在生产阻断项(不支持 https、未处理 gzip)。

任务 达标判定 说明
T-001 骨架 ✅ 达标 go module + 双 mode + viper config + 占位示例,测试覆盖 mode 解析/配置加载
T-002 签名 ✅ 达标 MD5 正确、向量与 Python 一致、ask 不进 headers/日志
T-003 传输(HEAD 版) ✅ 达标(但已被工作区改写取代) 提交版是干净 net/http + SOCKS5、无拟态头
T-004 Call/codes/envelope ✅ 达标(有小瑕疵) 信封/判码/路径正确,测试充分;Envelope 顶层 BaseInfo 冗余
T-005 验收 ❌ 不达标(仓库层面) 代码全部未提交;HEAD 无法复现验收;传输层 gzip/https/死代码

P1 · 阻断项(必须先解决)

P1-1 T-005 代码整体未提交,仓库与"DONE"不符

tasks.md/progress.md/docs/05 均标 T-005 DONE、里程碑 M1"真实握手成功"已宣告,但以下文件全部停留在工作区未入库(HEAD 停在 T-004 2a77a4d):

 M main.go                (新增 -verify-jkda-id-card 入口)
 M osi/transport.go       (改写为裸 HTTP/1.1,+110 行)
 M config/config.go       (OSI_* 环境变量绑定)
 M osi/transport_test.go
?? verify_jkda.go verify_jkda_test.go config/env_test.go

后果:从干净检出 git checkout HEAD 拿到的是 T-004 的 net/http 传输层——正是 progress 记录里"连真机报 EOF"的那一版。也就是说,仓库里的可复现实现不满足 T-005 验收,通过验收的证据只存在于一份未提交的工作区。按看板规则(标 DONE 前须有可运行证据入库 + 过 clean-state 检查),T-005 未真正完成。

整改:把上述文件作为 T-005 的独立 commit 提交;或在提交前先撤回 T-003/004/005 的 DONE 标记。二选一,不能让"文档说完成、仓库无实现"的漂移留存。

P1-2 传输层仅支持 http,https 完全不可用

osi/transport.go(工作区版)第 63-65 行:if target.Scheme != "http" { return ...unsupported },且请求是明文写裸 TCP conn(无 TLS)。沙箱是 http 能过,但生产 OSI 若为 https,此传输层直接无法连接。这是被裸写方案引入的能力倒退——HEAD 的 net/http 版本反而天然支持 https。

整改:在 Phase 0 收口前至少 fail-fast 明确报错并登记为"生产前必须解决"任务;理想是让 https 走 net/http、仅对需要保头大小写的 http 明文路径用裸写。

P1-3 声明 gzip 却不解压(真机一旦压缩即解析失败)

裸写请求固定发送 Accept-Encoding: gzip, deflate, br(第 110 行默认值),但响应用 http.ReadResponse + io.ReadAll 直接读——http.ReadResponse 不会自动解压(自动解压只发生在 http.Transport 自己加 gzip 头时)。当前能跑仅因沙箱返回未压缩;一旦服务端/代理按声明返回 Content-Encoding: gzip,json.Unmarshal 会拿到压缩字节而失败。未动态复验,但逻辑上是确定的定时炸弹。

整改:要么不声明压缩(发 Accept-Encoding: identity),要么按 Content-Encoding 真解压。Python requests 是自动解压的,照抄它的头却不照抄它的解压是半截移植。


P2 · 重要(尽快处理)

P2-1 t.client/roundTripper 是死代码

工作区 NewTransport 仍构造 roundTripper 和 http.Client 存入 t.client(第 37-50 行),并在其上接了 SOCKS5 DialContext——但裸写版 PostJSON 从头到尾只用 t.dialer,t.client 与 roundTripper 上的代理接线全部不被执行。初级维护者会被"这里有个 http.Client 却什么都不做"严重误导,也埋下"改了 client 以为生效实则没用"的坑。

整改:删除 t.client/roundTripper 构造,只保留 dialer+timeout;或反过来回到 net/http 由 client 承载代理。二者留一,不要并存。

P2-2 裸写方案的根因未定论,且立论未被"真机级"验证

progress 记"net/http 规范化头名 → EOF"。但 servlet 的 getHeader 本就大小写不敏感,"头名大小写导致 EOF"的因果并不牢靠,EOF 更可能来自代理/keep-alive/Content-Length 等。头大小写保留本身有测试(transport_test.go:90-122 裸 TCP 断言 \r\norgCode:,这点做得好),但"保留大小写才能连上真机"这个因果没有留下失败/成功对照的抓包证据。

整改:在 progress 或本目录补一份"net/http 版失败请求 vs 裸写版成功请求"的抓包/头部对照,坐实根因;否则这段重量级 hack 的存续缺乏依据,后续无人敢动。

P2-3 验收入口把真实身份证放上命令行

-verify-jkda-id-card <身份证> 会让真实身份证进入 shell history、ps 进程列表、init.sh 回显。属 PII 泄露面,与项目安全红线(不落真实身份证)相抵。

整改:改从环境变量或 stdin 读取待查身份证,命令行不接收 PII。


P3 · 建议(可排期)

  • contract.Envelope 顶层 BaseInfo 字段冗余:真实契约里 baseInfo 只存在于 uploadinfo 内,顶层这个 BaseInfo any(envelope.go:6)是早期"查询发裸 baseInfo"误解的残留,留着易被误用。建议删除。
  • Client.Call 对 nil transport 静默兜底(client.go:52-58):自动建一个无代理传输,真机(内网 host 必须走代理)会连不上却不报配置缺失。建议 nil 时直接报错,逼调用方显式注入。
  • UA 伪装 python-requests/2.32.4(transport.go:109 默认值):与"去掉网页拟态头"的设计取向轻微冲突,且写死版本号。若真机不校验 UA,建议用中性 chis_osi/<ver>;若校验,注释写清依据。
  • init.sh 在本机 Git Bash 跑不通(progress T-005 记:标准库路径/构建缓存权限异常)。统一验证入口在主力开发机上无法运行,等于形同虚设,建议要么修好要么在 current-state 显著标注"本机用 PowerShell 等价命令"。

做得好的地方(保持)

  • 签名层干净正确:SignPassword 与 Python hashlib 向量一致(008ace...),ask 严格不进 headers。
  • 判码按去前导零 == "1" 实现,正确兼容实测 "01";405 归可重试;HTTP 5xx/429 归可重试——与 docs/01 §1、docs/04 §6 对齐。
  • 信封结构与 docs/04 §8 实测契约一致(serviceId+uploadinfo{baseInfo,manageInfo},operateUnit 缺省回落 orgCode)。
  • 测试覆盖到位:签名向量、请求头大小写(裸 TCP)、信封字段、成功/405 分支、时间戳 13 位、URL 拼接、env 覆盖。
  • 配置:config.yaml.example 全占位无密钥;OSI_* 环境变量覆盖;真实 config.yaml 已 gitignore。

收口所需动作(建议作为 Phase 0 补完任务)

  1. [P1-1] 提交 T-005 全部代码为独立 commit,或撤回 DONE 标记(二选一)。
  2. [P1-3] 传输层去掉 gzip 声明或实现解压。
  3. [P1-2] https 支持或 fail-fast + 登记生产前任务。
  4. [P2-1] 删除 t.client 死代码。
  5. [P2-3] 验收身份证改走 env/stdin。
  6. [P2-2] 补根因抓包对照。
  7. [P3] 删 Envelope.BaseInfo、nil-transport 改报错、UA 处理、init.sh 说明。

完成 13 后,Phase 0 方可视为真正达标、里程碑 M1 成立;47 可并入 T-201 之前的一轮小整改。