Files
tamabox/outputs/TamaBox-v4-remediation-2026-08-27.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

66 lines
5.7 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-08-27)
基于 `outputs/TamaBox-v4-code-review-2026-08-27.md` 的 P2/P3 findings,落实 4 项修复。
改动遵循「最小、可读、可回滚」原则;每项均给出根因、改法、验证状态。
---
## 已落地(4 项)
### P3-1 autoban 404 探测失效 ✅
**文件**:`internal/route/autoban.go`
**根因**:`autobanMiddleware` 内 `defer` 在该 handler **返回时**触发,而 Flamego `run()` 会在 handler 返回后自动推进链路,因此 `defer` 执行时下游路由尚未运行,`rw.Status()` 恒为 `0` → `KindNotFound` 分支永远进不去。敏感路径封禁因 `kind` 在 body 内同步置位,不受影响。
**修复**:在 `defer` 之后、函数返回前加 `ctx.Next()`。经核对 `[email protected]`:`Next()` 即 `c.index++; c.run()`,会把下游全部跑完并让 `c.index` 越过末尾,外层循环随后只「超调」一次即退出,**不会重复执行下游**。
**验证**:`internal/security/autoban` 单测通过(安全核心逻辑);`gofmt` 语法校验通过。
### P3-4 草稿/归档提交提示误导 ✅
**文件**:`route/vote/vote.go`(`Submit`,约 647 行)
**根因**:`!isVoteActive` 仅二分「已结束 / 尚未开始」。草稿(`draft`)与归档(`archived`)状态均 `Status != published` 且通常无 `EndAt`,双双落入「尚未开始」,而重定向目标 `/vote/{LinkToken}` 本身会拦截草稿/归档 → 死循环式跳转。
**修复**:按 `vote.Status` 精确分支:
- `draft` → 「该投票尚未发布」
- `archived` → 「该投票已归档,无法参与」
- 其余:`isVoteEnded` → 「该投票已结束」/ 否则「该投票尚未开始」
**验证**:`gofmt` 语法校验通过;状态常量 `db.VoteStatusDraft/Archived` 已核实存在。
### P2-2 匿名投票强制验证码 ✅
**方向**(用户拍板):匿名投票始终要求验证码(或行为验证码)。
**文件**:`route/vote/vote.go`(`Submit`)、`route/user/vote.go`(`parseVoteMeta` / `renderVoteFormWithInput`)、`templates/user/vote-form.html`
**根因**:原逻辑 `if vote.RequireCaptcha` 仅在创建者开启验证码时才校验匿名提交;关闭后匿名用户清除 `vote_fp` Cookie 即可换签名 token 重复投票,仅剩 `limit*10` 的 IP 兜底。
**修复**:
1. `Submit` 匿名分支门槛由 `if vote.RequireCaptcha` 改为 `if conf.Recaptcha.CaptchaType != ""`——**只要全局配置了验证码,匿名提交一律校验**;未配置(无密钥部署)时不阻断,避免整崩。
2. `parseVoteMeta`(草稿创建/编辑、发布中编辑三路共用的唯一 `require_captcha` 读取点)与 `renderVoteFormWithInput` 强制 `requireCaptcha := true`,使存储值与策略一致。
3. 模板复选框置为 `checked disabled`,对 owner 明确「不可关闭」。
**对齐既有决策**:`db.Votes.RequireCaptcha` gorm 默认 `true`,`votes_test.go:63` 断言「匿名验证码默认开启(决策 #3)」,i18n 即「匿名投票需通过验证码」。本修复使该决策不可被 UI 关闭。
**验证**:`gofmt` 语法校验通过;`conf.Recaptcha.CaptchaType` 字段在 `internal/context/context.go:269` 已使用,类型无误。
### P2-1 结果缓存写穿失效 ✅(务实版)
**文件**:`internal/cache/cache.go`、`route/vote/vote.go`(`Submit`)
**根因**:`LoadResults` 对 `multiple/rating/rank/form` 类型需 `ListByVoteID` 全量加载并在 Go 层逐条 `json.Unmarshal` 聚合(O(N));`resultsCache`(30s TTL)仅在过期时重算,新提交后结果页最多陈旧 30s,且每次窗口到期都要重算一次全量。
**修复**:
1. 给 `TTLCache` 新增 `Delete(key)` 方法。
2. `Submit` 成功 `Create` 提交后调用 `resultsCache.Delete(strconv.FormatUint(vote.ID,10))`——**写穿式失效**:新投票后立即清除该投票结果缓存,结果页随即反映,且把 O(N) 重算成本收敛到「每次新投票一次」。
**未做(需独立迁移)**:真正的 O(1) SQL 下推受限于选项选择结果存于 JSON `FormData` 大字段(跨 MySQL/PG 做 JSON 聚合既脆弱又依赖具体版本)。彻底消除 O(N) 需把选择项正态化到独立表(如 `vote_submission_selections(vote_id, submission_id, option_id)`),以 `GROUP BY option_id` 聚合。建议作为后续独立 schema 迁移评估,**不在本次改动内**。
**验证**:`internal/cache` 单测通过(含新 `Delete`);`gofmt` 语法校验通过。
---
## 本次未改(留作独立决策)
- **P3-3 死配置 `MaxVotesPerFingerprint`**:全链路写死为 1、提交侧从不读取,属历史遗留。鉴于设计本意即「一人一票」,建议整体移除该字段与表单接线;涉及 DB schema 迁移,需你确认后再做。
- **P3-2 进程内缓存跨实例陈旧**:`TTLCache` 为进程本地,多实例下清缓存不跨实例。可接受最终一致 / 缩短 TTL / 迁 Redis——架构决策,不急。
---
## 构建与验证状态
- ✅ 语法:`gofmt -e -l` 对 4 个改动的 .go 文件均无报错。
- ✅ 单元:`go test ./internal/security/autoban/... ./internal/cache/...` 全绿。
- ⚠️ 全量构建受阻(**既有环境问题,非本次改动**):`github.com/chai2010/webp` 依赖 libwebp 本机 C 库(CGO),当前沙箱缺该库,报 `undefined: webpGetInfo` 等。该依赖经传递链被 `route/vote`、`route/user`、`internal/route` 引入,故这几个包在本环境无法完整 `go build`。在带 libwebp 的 CI/Docker 环境中可正常构建。建议你在本地/CI 跑一次全量 `go build ./...` 与 `go test ./...` 做最终确认。
## 改动文件清单
- `internal/route/autoban.go`
- `internal/cache/cache.go`
- `route/vote/vote.go`
- `route/user/vote.go`
- `templates/user/vote-form.html`