- 新增 topics/quality.md:业务正确性 / 运维 / 前端体验四类结论,含 「私聊取最早 50 条」「Classic() 的 Logger 记 RequestURI」这两个坑的成因, 以及处置覆盖面、Redis 两种故障模式、产品缺口(无 page view / 提问者找不回自己的提问) - 2026-10-29.md:本轮六项修复的改动与验证结果,另记两个实现坑 (limit 兜底不该抬大用户配置;Python bytes 字面量不支持 \uXXXX) - topics/sec.md / 2026-10-27.md:补上一轮遗漏的字体自动发现与 CSP enforce 结论 - MEMORY.md:索引加 quality,处置覆盖面一条标注投票链路已补齐
6.8 KiB
2026-10-27
在 WSL Debian 上把 TamaBox 跑起来,做 P1 修复的端到端复验
用户新搭了 WSL2 Debian,给了 data/data.txt(测试环境的 PG / Redis / OSS 凭据),要求「用它部署测试一下」。
环境(详细踩坑见 topics/build.md 的「本地部署」小节):
wsl.exe -d Debian -u root可免密进 root;apt 装了redis-server / python3 / python3-requests / gcc / libwebp-dev(有 libwebp 后s3.go正常编译,不用 s3stub overlay)。- Go:
/opt/go装 1.27.3,但~/.bashrc里GOROOT=/usr/local/go(1.26.3)→ 必须env -u GOROOT。 - PG:新建 role
tamabox(CREATEDB)+ 库tamabox、nekobox(schemanekobox)。 - 服务:
/etc/systemd/system/tamabox-test.service,源码在~/tb/src,二进制~/tb/tamabox, 日志~/tb/server.log,端口 5877。配置~/tb/src/conf/app.ini(production=true、external_url=http://127.0.0.1:5877、captcha_type=math、enable_text_censor=false、trust_proxy=true)。
验收结果(全 PASS):
- P1-1 外链图片剥离 / 白名单图片保留 / 实体与反斜杠变体;
- P1-2a 4 位密码被拒、6 位生效;P1-2b 验证码先于密码;P1-2c 6 次失败后正确密码仍被锁;
- P1-3 A/B:回环对端 +
CF-Connecting-IP: 1.2.3.4→ 命中黑名单 302;Windows 侧(非回环)同样头 → 忽略并 WARN; - 回归:18 条 XSS 载荷无可执行/无外联、CSRF 无 token 400、越权全 302;
- 单测:
internal/{template,shared,form,conf,db,route}+route/{question,vote}全绿。
这次揪出的新问题(已修):P1-1 的白名单把配置串整串当主机名,而真实配置是
image_bucket_cdn_host = "r2.337845818.best/tamabox"(host/path)→ 自家 R2 图片被一起剥掉。
修法:normalizeHostLike() 归一化(补协议 → url.Parse → Hostname),endpoint 主机自身也入白名单
(R2 path-style 链接是 <endpoint>/<bucket>/…);新增 TestSafeImageSrc_RealConfiguredHosts,
变异验证(退回旧逻辑 → 4 条放行用例变红)。教训:白名单类单测必须照抄生产配置的写法,不能自己编一个好写的形态。
P2-3 从推断升级为实测:/login?to=/\evil.example 登录后 Location: /\evil.example,
按 WHATWG 解析 = http://evil.example/;\\evil.example 同样外跳(//、/// 已被规整)。
同一载荷在正文渲染里是安全的(<a href="/\example.org"> 被剥成 <a>)。修复未做,等用户指示。
产物:
- 报告新增「六、本地部署实测」章节(
outputs/security-audit-2026-10-25.md); - 脚本新增
verify_p1_allow.py(正向放行)、local_seed.py(本地注册审计账号),audit.py改为TB_BASE/TB_EMAIL/TB_PWD/TB_DOMAIN可覆盖 + 验证码 429 退避。
待办:改动仍未 commit / push;P2-3、P2-4、P2-5、P2-6、P2-7 未动。
第二轮:修完 P2/P3 + 全局复查
用户:「把你发现的问题修了,然后全局检查一遍有无其他问题」。
已修(P2-3~P2-8、P3-9~P3-15):都写进 topics/sec.md 的「第二轮修复」。要点:
跳转白名单收敛到 shared.SafeRedirectTarget(url.Parse 会解 %0d%0a,必须逐 rune 查 u.Path);
注册三条失败分支共用 registerSucceeded;CSP 三档 + umami 的 mami.shiroko.one 必须入 script-src;
no-store;securityHeaders 前移;封禁落库 ban_records;常量时间比较;页码上限;429 统一出口。
全局复查新发现 3 个(详见 topics/sec.md):
- 🔴
chat_handler.SendMessage的 POST 缺conv.OwnerID != pageUser.ID—— 被拉黑者换 domain 即可继续骚扰- 触发邮件 + 绕过屏蔽词/日限额。已修,3/3 PASS,并做变异验证(去掉校验后对应用例变红)。
- 🟡 页面密码
next的反斜杠外跳(与 P2-3 同源)。 - 🟡 分享卡无字体时每请求烧数秒 CPU(公开端点)→ 失败结果缓存 1 分钟,实测 3.44s → 0.53s。
验收:verify_p1_fixes.py 4/4、verify_round2.py 19/19、verify_page_next.py 6/6、
verify_chat_owner.py 3/3;单测全绿;全项目编译通过。测试数据已清(库里只剩 [email protected])。
报告:outputs/security-audit-2026-10-25.md 新增「七、第二轮修复与全局复查」。
再待办:仍未 commit / push(26 改 + 9 新);CSP 是否切 enforce;生产机补 glyf 轮廓字体。
收尾复跑发现两个「假红」(已写进 topics/sec.md):① 不给 TB_BASE 时脚本打的是远端
box-dev-api,没部署修复所以 4/4 FAIL;② 页面密码入口限流 10 次/分钟 + p1-2c 的 10 分钟锁定
会让 verify_page_next.py 后两项 429。顺手把该脚本判据从「Location 不含 evil」改成精确比对
/_/<domain>、每条载荷开新会话、429 单独报错。四套最终:4/4、19/19、6/6、3/3。库已清回只剩 tb-local。
第三轮:用户「按最优解决定」→ CSP 切 enforce + 字体落地 + 提交
CSP 默认改 enforce(cspMode() 空值 = enforce;写错值仍退 report)。依据不是猜:
写了 outputs/sec-audit-2026-10-25/csp_sweep.py + csp_sweep.cjs(Playwright + 本机 Chrome,
监听 securitypolicyviolation),report 模式先测一遍、enforce 再测一遍,18 页面 + 2 图片端点零违规。
巡检器自己做了变异自检(页面里主动 fetch("http://csp-selftest.example/x") → 确实报 connect-src),
否则「0 违规」可能只是监听器没挂上。
补的漏项:browser.sentry-cdn.com —— header.html 里 Sentry 在 {{ if ne SentryDSN "" }} 里,
本地没配 DSN 所以巡检碰不到,线上一开就会静默失效;现在按 conf.App.SentryDSN 条件放行 + 单测双向断言。
分享卡字体:补 glyf 系统候选(wqy-zenhei / wqy-microhei / DroidSansFallback / uming)并压到
Noto(CFF) 之前;static/fonts 同时按 CWD 和可执行文件目录找;CheckShareCardFont() 在 runWeb 里
启动即探(命中 Info 带路径,缺失 WARN 给 apt 命令)。实测 apt install fonts-wqy-zenhei 后
零配置即 font=…wqy-zenhei.ttc,/share-card 与单题分享卡从 500 → 200 image/jpeg,
中文/二维码正常(图存 outputs/sec-audit-2026-10-25/sharecard-*.jpg)。
提交:三个 commit(7802bb2 安全修复 / e807dea 字体 / 430c6ef 记忆)。push 失败:
git.shiroko.one 的 https 存取凭据被拒(Failed to authenticate user),SSH 路(id_ed25519_wsl)
也未被该仓库授权 —— 需要用户更新 token 后自己 push。
全量单测:带 DB_TYPE=postgres DB_USER=tamabox DB_PASSWORD=… DB_HOST=127.0.0.1 全绿;
不带 DB 变量时一批 DB 门控用例会报 Unknown database type: ""(不是回归)。