Files
tamabox/outputs/p1-fixes-2026-08-31.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

52 lines
4.2 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.
# P1 修复落地记录(P1-3 / 4 / 5 / 7 / 8 / 9 / 10)
- **日期**:2026-08-31
- **范围**:按用户要求顺序实施 7 个 P1 项(P1-6 密码比较不在本次范围;P1-3 按要求**不使用 Turnstile/recaptcha**)。
- **P0-1(IP 伪造)暂缓**,按用户指示留待后续。
## 1. P1-3 验证码限流 + 常量时间比较
**文件**:`internal/captcha/math.go`、`internal/route/route.go`
- 答案比对改为 `crypto/subtle.ConstantTimeCompare`,消除时序侧信道。
- `VerifyOnly`(模态框预校验)累加尝试次数,达到 `maxCaptchaAttempts=5` 即销毁该验证码,防止无限轮询爆破。
- `GenerateMath` 处理 `rand.Int` 错误(不再忽略)。
- `/api/captcha/generate`、`/api/captcha/verify` 加上 `rateLimitMiddleware(20, 1m)`。
- ⚠️ 这是**缓解**而非根治:数学答案仍返回给客户端,脚本理论上可读题。根治依赖 P0-1(IP 信任校验)落地后整体提权。
## 2. P1-4 追问"等主人回复"服务端校验
**文件**:`internal/db/follow_ups.go`、`route/question/follow_up.go`
- 新增 `FollowUps.GetLatestByQuestionID`(`Order(created_at DESC) Limit 1`)。
- `PublishFollowUp` 在计数校验后,取该问题最新一条追问;若其 `Answer == ""` 直接拒绝(不再仅依赖前端 `waitingFollowUpReply` 隐藏表单)。
## 3. P1-5 审核可信域名精确匹配
**文件**:`internal/security/censor/text.go`
- `RemoveTrustedURL` 改为边界感知:仅当可信域名后紧跟 `/`、`?`、`#` 或位于字符串结尾时才剥离,避免 `https://github.com.evil.tld` 被误判为 `github.com` 子域而漏审。
## 4. P1-7 删除回退功能配额计数
**文件**:`internal/db/feature_control.go`、`internal/db/questions.go`、`internal/db/conversation.go`
- `IncrementQuota` 负 delta 增加 `WHERE quota_count > 0` 守卫,防止计数减到负数。
- `questions.DeleteByID` 删除提问后回退 `FeatureQuestion` 配额 -1。
- `conversations.MarkDeletedByOwner` 删除私聊会话后回退 `FeatureChat` 配额 -1(并先取 OwnerID,已删除/不存在时幂等返回)。
## 5. P1-8 自动封禁仅计真正 404
**文件**:`internal/route/autoban.go`、`internal/route/route.go`
- `autobanMiddleware` 改为接收共享 `*autoban.Autoban` 实例(由 `New()` 创建一次)。
- 真正的 404 探测计数移入 `f.NotFound` 处理器单独记录;不再把业务性 `302 → /`(语言切换 / 登出 / 投票不存在 / 黑名单跳转)误判为路径扫描导致误封。
## 6. P1-9 服务反代路径穿越 / query / 超时
**文件**:`route/service/proxy.go`
- `forwardPath` 经 `path.Clean` 归一化,`singleJoiningSlash` 安全拼接,杜绝 `../` 越出白名单前缀。
- 透传原始 `RawQuery`(此前静默丢弃)。
- 复用带超时的共享 `Transport`(`ResponseHeaderTimeout=15s`、`IdleConnTimeout=30s`),不再每请求新建。
## 7. P1-10 像素追踪路由收紧
**文件**:`internal/route/route.go`、`internal/context/context.go`、`route/pixel/pixel.go`
- `/api/v1/pixel/{**}` 由 `f.Any` 改为 `f.Get` + `rateLimitMiddleware(30, 1m)`,仅放行只读 GET,并补限流。
- `context.go` 去掉对 `/api/v1/pixel/` 的 CSRF 豁免(GET 本不需 CSRF,写方法已被路由层禁止)。
- `pixel.go`:路径 `path.Clean` 后必须仍在 `/api/` 之下,否则拒绝;响应头仅透传白名单(Content-Type/Length/Encoding/Disposition/Cache-Control/ETag/Expires/Last-Modified/Pragma/Vary),**不透传 Set-Cookie 等**。
## 验证状态
- ✅ `go build` / `go vet` 全绿(不依赖 storage 的 5 个包):`internal/captcha`、`internal/security/censor`、`internal/db`、`route/pixel`、`route/service`。
- ✅ 单元测试通过:`internal/security/censor`、`internal/security/autoban`。
- ✅ 12 个改动文件 `gofmt -w` 已格式化。
- ⚠️ `internal/route/route.go`、`internal/route/autoban.go`、`internal/context/context.go`、`route/question/follow_up.go` 因传递依赖 `internal/storage`(`github.com/chai2010/webp` 缺 libwebp,Windows 沙箱 CGO 限制)**无法在此环境完整编译**;已用 `gofmt -e` 确认无语法错误,请在 Linux / 带 libwebp 环境做完整 `go build ./...` 验证。