# 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`(schema `nekobox`)。 - 服务:`/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 链接是 `//…`);新增 `TestSafeImageSrc_RealConfiguredHosts`, 变异验证(退回旧逻辑 → 4 条放行用例变红)。**教训:白名单类单测必须照抄生产配置的写法,不能自己编一个好写的形态。** **P2-3 从推断升级为实测**:`/login?to=/\evil.example` 登录后 `Location: /\evil.example`, 按 WHATWG 解析 = `http://evil.example/`;`\\evil.example` 同样外跳(`//`、`///` 已被规整)。 同一载荷在正文渲染里是安全的(`` 被剥成 ``)。修复未做,等用户指示。 **产物**: - 报告新增「六、本地部署实测」章节(`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`): 1. 🔴 `chat_handler.SendMessage` 的 POST 缺 `conv.OwnerID != pageUser.ID` —— 被拉黑者换 domain 即可继续骚扰 + 触发邮件 + 绕过屏蔽词/日限额。已修,3/3 PASS,并做变异验证(去掉校验后对应用例变红)。 2. 🟡 页面密码 `next` 的反斜杠外跳(与 P2-3 同源)。 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;单测全绿;全项目编译通过。测试数据已清(库里只剩 `tb-local@example.com`)。 **报告**:`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」改成精确比对 `/_/`、每条载荷开新会话、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: ""`(不是回归)。