Files
tamabox/outputs/re-audit-2026-09-01.md
T
tamakyi e3e59e7c69 chore: baseline snapshot before moderation-strictness feature
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.
2026-09-10 10:56:27 +08:00

74 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TamaBox-v4 第三轮审计报告(2026-09-01)
本轮聚焦前两轮未覆盖的区域(上传/存储/邮件/基础设施、模板层)+ 对 8/31~9/1 修复批次做回归复审。
所有 P0/P1 结论均已逐一核对源码确认,并纠正了子代理的一处误报(见 P1-3 说明)。
## 验证基线
- `go build`:不依赖 storage 的 14 个包全部通过;`go test`(form/i18n/cache/autoban/censor)全绿。
- ⚠️ 注意:本轮两个 P0 都藏在 webp 屏障遮住的包里(route/user、route/db),Windows 下编译不到——**务必在 Linux/带 libwebp 环境跑一次 `go build ./...`**,否则下面 P0-1 会直接让部署失败。
---
## 🔴 P0(2 项,均为上一批修复引入的回归)
### P0-1 profile.go:335 — 导出函数无法编译(构建失败)
`personalData` 是 `[][]interface{}`,`sanitizeCell(row[0])` 把 `interface{}` 传给 `string` 参数 → **编译错误**。webp CGO 屏蔽了本机报错,Linux/Docker 构建同样会失败。
**修复**:遍历单元格时先做 `s, ok := v.(string)` 类型断言,非字符串(如 `user.CreatedAt` 的 time.Time)跳过净化原样写入。
### P0-2 history-template.html:29 — 复合游标未同步,「加载更多」翻页损坏
P2-13 把游标改成 `<isPinned>.<createdAtNano>.<id>` 复合格式,但前端 JS 仍回传纯 `data[data.length-1].id` → 服务端 `parsePinCursor` 解析失败 → 游标被忽略 → 每次都返回第一页(内容重复、无法翻页)。
**修复**:JS 按 `${q.is_pinned?1:0}.${new Date(q.created_at).getTime()*1e6}.${q.id}` 组装游标;或服务端兼容裸 id 格式(解析失败时回退为 id 游标)。
---
## 🟠 P1(6 项)
### P1-1 upload_images.go:167 — 级联删除查询了不存在的列
`DeleteByQuestionID` 在 `upload_images` 表上执行 `WHERE question_id=?`,但该列实际在关联表 `upload_image_questions`(:54)。PG 报列不存在 → 级联失败(目前仅记日志不阻断删除),**关联记录永久残留**。
**修复**:改为先 `UploadImageQuestion` 按 question_id 查出 imageID 列表,再软删对应 `UploadImage`(并顺带删 link 行)。
(纠正误报:子代理称"提问无法删除",实际 `questions.DeleteByID` 只记日志不返回错误,删除本身仍成功。)
### P1-2 conversation.go:205 — 重复删除导致配额二次回退
会话删除是标志位软删(`is_deleted_by_owner`),重复 POST 同一删除请求,`First()` 仍命中 → 每次都 `IncrementQuota(-1)`。守卫保证了不为负,但多条会话反复删可把计数错误清零。
**修复**:`First()` 后若 `conv.IsDeletedByOwner == true` 直接幂等返回。
### P1-3 questions.go:222 — 非 PinFirst 游标路径在 PG 下报错 + ASC 方向错误
`q.Where("id < ?", cursorStr)` 传 string,PG 中 `integer < text` 无操作符 → 管理后台 `/api/.../questions` 带游标请求 500。且 ASC(sort=oldest)时仍用 `id < ?`,方向相反 → 翻页停在第一页。
**修复**:`strconv.ParseUint` 校验游标;按 `opts.SortOrder` 选择 `id < ?`(DESC)或 `id > ?`(ASC)。
### P1-4 home.html:8-12 — 远程内容经 `x-html` 无消毒渲染(供应链 XSS)
首页从外部 API(`api-direct.337845818.best/...importantinfomation.json`)取 `log.content` 后 `x-html` 直出。该域被劫持或 JSON 中混入用户上报内容时,首页即任意 JS 执行。
**修复**:改 `x-text`(内容纯文本时),或引入 DOMPurify 白名单过滤后再 `x-html`;顺带补上 `class=uk-article-meta` 缺失的引号。
### P1-5 tracing/flamego.go:42 — span 名含 query string,私密 token 落日志
span 取 `RequestURI`,`?t=<问题token>`、找回密码 `?code=` 等会原样写入 trace;配合 `AlwaysSample` + stdout 全量输出,1C1G 上还是 IO 负担。
**修复**:span 名只用 `URL.Path`;生产关闭 stdout exporter 或降采样。
### P1-6(遗留提醒)password.go — legacy 密码 `==` 非常量时间比较
第一轮报告的 P1-6 至今未修(你当时未勾选)。bcrypt 失败静默降级 HMAC 路径同样在。建议尽快处理。
---
## 🟡 P2(择要 9 项)
1. **mail.go:123**:`SetHeader("To", to)` 建议 `SetAddressHeader`(防 header 注入的纵深防御)。
2. **page.go:664/673**:上传 key 保留用户可控后缀(可为 .svg/.html),建议后缀白名单。
3. **孤儿文件**:图片上传 S3 成功但 DB 失败仅记日志;删除从不清理 S3 对象 → 存储只增不减。
4. **conf/db.go:16**:PostgresDsn 密码未转义 + `sslmode=disable` 写死。
5. **dbutil/pagination.go:19**:`LimitOffset` 无 pageSize 上限,可被超大值打 DB。
6. **storage/oss.go**:全项目已无调用方(遗留代码),建议删除。
7. **autoban 双计数**:敏感路径未命中路由时被中间件和 f.NotFound 各计一次 → 封禁提前触发;NotFound 应跳过 IsSensitivePath 命中的路径。
8. **captcha VerifyOnly**:Attempts 读改写非原子,并发可绕 5 次上限(单会话低风险)。
9. **questions 删除链路非事务**:级联失败仅记日志可留孤儿,建议包 `Transaction`;另 `sanitizeCSVCell`(vote.go:1194)缺 `\t\r\n` 分支,与 profile.go 净化规则不一致。
---
## ✅ 本轮确认无问题的部分
- 模板层:41 处 POST 表单 CSRF token 全覆盖;`toJSON`/内联 JS 均经 html/template script-context 转义,无逃逸;Markdown 白名单消毒;探索页 AJAX 分支 escapeHTML 正确。
- storage/s3.go:LimitReader 8MB + 魔数 + 像素上限 + 随机 key + 重试,凭证不入日志。
- cache:RWMutex + maxItems + TTL,无泄漏。
- 回归确认正确:XFF 从右向左剥离、autoban 单实例单次 Next、配额归属(提问→箱主、私聊→OwnerID 一致)、追问服务端校验、proxy 编码绕过防护、pixel GET-only、chat 重校验覆盖全部写路径。
## 建议修复顺序
P0-1/P0-2(我引入的回归,必修)→ P1-1/P1-2/P1-3(数据一致性)→ P1-4/P1-5 → P1-6 遗留项 → P2 择要。