Repo .git was damaged (refs/ missing, index truncated, object store near-empty). Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak, then full worktree committed as a rollback baseline.
78 lines
4.4 KiB
Markdown
78 lines
4.4 KiB
Markdown
# TamaBox-v4 P0/P1 修复报告(2026-09-01)
|
||
|
||
第三轮审计的全部 P0×2 + P1×6 已修复,并在验证过程中额外发现并修复了一处
|
||
被 webp 屏障遮蔽的**隐藏编译回归**(见「额外发现」)。全模块构建/vet/测试全绿。
|
||
|
||
## 🔴 P0 修复
|
||
|
||
### P0-1 profile.go 导出函数编译错误
|
||
- `route/user/profile.go`:`personalData` 是 `[][]interface{}`,原代码把 `interface{}` 直接传给
|
||
`sanitizeCell(string)` 导致编译失败。
|
||
- 改为逐单元格类型断言:字符串净化防公式注入,非字符串(`time.Time` 等)原样写入。
|
||
|
||
### P0-2 history-template 复合游标未同步(翻页损坏)
|
||
- 采用更彻底的方案:`route/question/page.go` 的 `ListAPI` 响应改为
|
||
`{data: [...], next_cursor: "..."}`,由**服务端直接下发** `EncodePinCursor` 编码的复合游标。
|
||
- 前端 JS 不再用 `new Date().getTime()*1e6` 自行拼游标——JS 双精度浮点无法精确表示纳秒级
|
||
UnixNano(毫秒以下精度丢失),时间戳相同的提问会在翻页边界被跳过。
|
||
- JS 层已适配 `ctx.JSON` 的统一包装 `{code, data, message}`(内层才是分页负载)。
|
||
|
||
## 🟠 P1 修复
|
||
|
||
### P1-1 upload_images.go 级联删除查询不存在的列
|
||
- `DeleteByQuestionID` 重写:先查 `upload_image_questions` 关联行拿 imageID,
|
||
再事务内软删 `UploadImage` 主体并删除关联行(原代码直接在错误表上 `WHERE question_id`)。
|
||
|
||
### P1-2 conversation.go 重复删除二次回退配额
|
||
- `MarkDeletedByOwner` 增加幂等守卫:`conv.IsDeletedByOwner == true` 直接返回,
|
||
不再重复 `IncrementQuota(-1)`。
|
||
|
||
### P1-3 questions.go 非 PinFirst 游标 PG 报错 + ASC 方向反
|
||
- 游标改用 `strconv.ParseUint` 校验(原 `id < ?` 传 string,PG 下 `integer < text` 无操作符)。
|
||
- 排序方向规范化提前,ASC 用 `id > ?`、DESC 用 `id < ?`;解析失败忽略游标(与 PinFirst 一致)。
|
||
|
||
### P1-4 home.html 供应链 XSS
|
||
- 远程公告改 `x-text` 纯文本渲染,移除 `x-html`;meta 行与正文拆成两个元素分别渲染。
|
||
|
||
### P1-5 tracing span 泄露 query string
|
||
- span 名从 `RequestURI` 改为 `URL.Path`,`?t=<token>`、`?code=` 等不再落入 trace。
|
||
|
||
### P1-6 password.go(第一轮遗留项)
|
||
- legacy HMAC-SHA1 比较改用 `crypto/subtle.ConstantTimeCompare`(常量时间,防时序侧信道)。
|
||
- `encodePasswordBcrypt` 失败不再静默降级为 HMAC-SHA1 弱哈希:密码超 72 字节按 bcrypt
|
||
原生语义截断;bcrypt 生成失败记日志并返回空串(账号校验失败比弱哈希安全,且
|
||
`users.go:390` 的 `newHash != ""` 守卫保证空串不会被写库)。
|
||
|
||
## ⚠️ 额外发现:internal/route 隐藏编译回归(P0 级)
|
||
|
||
P1-8 批次(autoban 重构)引入、一直被 webp 屏障遮蔽的编译错误,本次通过
|
||
`go build -overlay` 全模块类型检查暴露:
|
||
|
||
- `internal/route/route.go`:使用 `autoban` / `mail` / `shared` 三个标识符但缺少 import;
|
||
- `internal/route/autoban.go`:`autoban.Autoban` 类型不存在,应为 `autoban.Banner`。
|
||
|
||
**若不修复,Linux 上的完整构建同样会失败。** 两处已修复。
|
||
|
||
## 验证方式(重要)
|
||
|
||
Windows 沙箱缺 libwebp,`internal/storage` 及其依赖包无法正常编译,导致其中的类型错误
|
||
(P0-1、internal/route 回归)在常规 `go build` 下被遮蔽。本次引入 **overlay 桩验证法**:
|
||
|
||
- `dev/webpstub/`:chai2010/webp 的签名兼容桩(仅 `Encode`/`Options`),保留在仓库供复用;
|
||
- `go build -overlay=<临时 overlay.json> ./route/... ./internal/... .` 将
|
||
`internal/storage/s3.go` 的 import 临时替换为桩包,实现全模块完整类型检查。
|
||
|
||
验证结果:
|
||
- 全模块 `go build`(overlay)✅
|
||
- 全模块 `go vet`(overlay)✅
|
||
- 全部可运行测试通过(templates/form/i18n/cache/autoban/censor 等,`go test` sweep)✅
|
||
- 含 `TestTemplatesParse` / `TestRenderPublicQuestionList` / `TestRenderPublicQuestionItem` /
|
||
`TestRenderUserQuestionList` / `TestTemplateCrossReferences`
|
||
- `internal/db` 测试需 `DB_TYPE` 环境变量 + 真实数据库,属既有环境限制,非回归。
|
||
|
||
## 待办提醒
|
||
|
||
1. **Linux/CI 环境跑一次 `go build ./...` + `go test ./...` 最终确认**(overlay 桩只覆盖类型检查,
|
||
webp CGO 真实编译仍需 libwebp 环境)。
|
||
2. P2 9 项(mail header、上传后缀白名单、孤儿文件、dbutil pageSize 上限等)尚未处理。
|