第八回:通宵堵十二个洞呆码农差点修到天亮,贴小红书改四次巧媳妇终于挂对了

游戏:xiuxian
作者:纸城
发布时间:2026-03-20 21:30 上海时间

Y 从晚上十二点修到凌晨四点半,一口气堵了十二个安全和质量漏洞,顺手还给项目写了一个 AI 视角的开发日志系列;L 这头忙着打磨结算页、塞渡劫事件、写分享文案,最后花了四个提交才把小红书链接贴对。

以下情节纯属虚构,如有雷同纯属巧合。

开场

3 月 10 号凌晨,Y 还没睡。

前两天搭基础设施的时候速度太快了。Worker、数据库、用户系统、排行榜、遥测——全在四十八小时内上线。快是够快了,但 Y 心里清楚,赶工的时候肯定留了洞。不是那种"可能有问题"的直觉。是他做完安全审查之后,列了一张清单,上面写着十二条。

他看了看时间。晚上十二点。打开 Codex,把清单贴了进去。

"那就修吧。"

十二个洞

Y 一个一个地跟 Codex 过。审查是他自己做的,修法也是他定的,Codex 负责改代码和补测试。

第一个是最严重的:用户资料的更新接口,在没有 JWT token 的情况下会直接接受客户端传来的 `vibe_uid`。意思是——任何人都可以改任何人的昵称。修法很简单,删掉那个 else if 分支,所有请求必须走 `requirePassportAuth()`。但这种洞留在线上哪怕一天都嫌多。

第二个是 XSS。好几处 `innerHTML` 直接拼了来自 config 或 state 的动态内容。天赋卡片的名字和描述、称号徽章的标题、打字机特效的文字——这些地方都可能被恶意内容注入。修法是逐个分类:纯文本用 `textContent`,需要 HTML 结构的用 `escapeHtml()` 包一层。日志系统的 `innerHTML` 留着,因为内容来自受信任的 config。

第三个是数据库竞态。邮箱绑定的逻辑是先查有没有人用这个邮箱,再执行绑定。两个请求同时进来,都查到"没人用",就会把同一个邮箱绑给两个人。修法是单条原子 SQL,`NOT EXISTS` 子查询加 UNIQUE 约束的 try/catch,冲突直接返回 409。

第四个是表达式解析器的 bug。tokenizer 能接受 `.5` 这种省略前导零的小数,但后面的 validator 正则不认。两边的标准不一致。

第五个是 DOM 空指针。`updateStat()`、`log()`、`_flushLogs()` 这几个高频函数都没有检查 DOM 元素是否存在。节点一旦被删或者改了 ID,整个游戏直接崩。

第六个是 devlog API 的 CORS 头漏了。第七个是反馈表单的双击提交防护装得太晚。第八个是称号页面的除零错误。第九个是 `minTizhi` 没有在 `startGame()` 里重置,一直挂着初始化时的 `Infinity`。

还有三个是测试质量问题。一个同义反复的测试——自己造个对象然后断言自己造的属性,没在测引擎行为。一个 Worker 测试的 mock 对参数数量不校验,十个参数和十四个参数都能过。还有冒烟测试里 curl 失败返回 "000",正则把它当成了合法的 HTTP 状态码。

十二个。从凌晨十二点修到四点半。

修完之后 Y 又花了半个小时更新 CLAUDE.md 和 AGENTS.md。不是记流水账。是把这次踩到的坑变成规则:所有修改接口必须走 JWT,用户文本一律 `textContent`,数据库更新必须原子操作,DOM 查找必须 null check,测试不准写同义反复。

把教训写成规矩,下次就不用靠记忆了。

他还给项目写了一个旁白

同一个通宵里,Y 还干了一件有点不一样的事。

他让 AI 给开发日志系统写了三篇文章。不是以自己的名义写的。是以一个叫"Zero"的视角写的——一个阅读提交记录的语言模型,用第三人称分析这个项目是怎么长出来的。Y 给了它 commit 历史和项目上下文,让它自己去看、自己去写。

第一篇讲序章:这不是在歌颂 AI 输出多厉害,而是在算代价和过程。第二篇讲边界:Y 怎么把一坨 HTML 拆成 `app/`、`config/`、`src/`、`docs/` 四个目录,不是为了整洁,是为了让未来的改动知道该落在哪里。第三篇讲工作流:CI/CD、测试、部署脚本、冒烟检查——不是让系统时髦,是让刚稳住的东西不要再倒。

写完他还配了一个 `export-commit-changes.js` 脚本,能把整个提交历史导出成 patch 文件,按作者和日期分目录。这个脚本就是后来写这篇连载用的那个。

凌晨四点半。Y 关电脑的时候,天快亮了。

L 那边在磨细节

3 月 11 号,L 醒来就打开 Kimi 开始干活了。

结算页面又被她让 Kimi 调了一轮。padding 从 20 缩到 16,字号从 32 降到 28,按钮间距从 15 改成 12,所有元素往紧了收。手机上能看到更多内容,不用滚那么多。

然后她让 Kimi 做了一件挺巧的事:在天道对话结束的时候,把玩家的选择发到后端。三个选项对应三句反馈:

A:"天道不公,全靠看脸!——属性平衡太差,暴毙率太高,苟活全看脸!"

B:"这死亡频率,我怕不是拿了个恐怖片剧本?——死亡太随机,苟活全看脸!"

C:"挺好玩的,但我发现了个 Bug……——疑似发现游戏 Bug"

这不是随便写的吐槽。这是把彩蛋变成了用户反馈入口。玩家以为自己在跟天道吵架,其实选项内容会以邮件形式发到运营邮箱。带转世次数、带 vibe_uid、带 IP、带时间戳。玩家不知道。天道知道。

同一天她还修了暂停按钮的显示问题——加了 Unicode 变体选择符,强制文本渲染,不让系统把 `⏸` 变成 emoji 样式。这种问题只有在不同设备上反复测才能发现。

然后她写了十一条分享文案,随机显示在结算截图上:

"赛博修仙,全靠开盲盒。来看看你会抽到什么神仙开局?"

"一分钟体验无厘头修仙,来抽取你的专属奇葩称号。"

"用电饭煲炼丹?这个修仙世界不太正常,点开测测你的运势。"

"道友请留步!我看你骨骼惊奇,点这里抽一次你的本命灵根!"

L 写这种东西的速度跟写事件文案一样快。不带犹豫。像在群里发消息。

渡劫事件扩了一圈

前一回里 L 给真仙境界加了不可豁免的劫难事件。这回她跟 G 老师聊完之后,让 Kimi 把范围拉宽了——不只是真仙,渡劫期也开始有专属劫难了。

"三九天劫降临!劫雷劈下,你以肉身硬抗,体质被完全摧毁……"

"六九天劫降临!无尽雷海将你淹没,仙体崩溃在即……"

"心魔劫降临!你道心不稳,走火入魔,真灵即将消散……"

"天人五衰降临!肉身腐朽,元神枯萎,你即将陨落……"

全部标了 `fullTizhiDamage: true`,体质直接归零,不给抢救机会。从第九境界开始,每一步都是在走钢丝。

分享截图也改了。死因从单独的块移到了属性区里面,canvas 不再裁成正方形,整张图直接用。在手机上分享出去好看多了。

四次才把小红书贴对

3 月 12 号,L 决定把小红书链接放上去。

这件事听起来很简单。一个链接。贴上去就行。

第一次她贴了一个短链:`xhslink.com/m/7iKTpXtyZmL`。提交。

半小时后第二次提交。换成了完整的个人主页 URL,但带了一大堆微信分享追踪参数:`xsec_token=...&xsec_source=app_share&wechatWid=...&wechatOrigin=menu`。这种链接从微信里复制出来的时候就长这样。

一个半小时后第三次。把追踪参数删了。只留干净的 `xiaohongshu.com/user/profile/65dc687c000000000500d6ee`。

又过了一个半小时,第四次。Copilot 帮她把所有文件里的链接统一成了新的短链 `xhslink.com/m/3JD5TW9GUgt`,包括 `index.html`、`homepage.mjs`、`game-config.js`、`game-engine.js` 和测试文件。

四个提交。一个链接。

这件事很小。但它说明了一个事实:游戏要开始往外走了。之前所有的改动都是内部的——调数值、修 Bug、搭架子。贴小红书链接是第一次把一个入口指向外面的世界。L 准备开始让别人看到这个东西了。

两个人在同一个夜晚做的事

回头看这两天。

Y 在凌晨修安全漏洞、写项目规范、给 AI 造一个观察者视角。L 在白天磨结算页面、把彩蛋变成反馈通道、写分享文案、扩写渡劫事件、贴社交链接。

两个人连作息都不重叠。Y 是夜猫子,凌晨出活。L 是白天选手,上午开工。

但拼在一起看:Y 让 Codex 把地基灌实了,L 让 Kimi 把门面打开了。一个朝内,一个朝外。都在为同一个方向使劲——让这个游戏从两个人的东西,变成可以给别人玩的东西。两个人谁都没有手写过一行代码。但每一个决定——修什么、怎么修、留什么、砍什么——都是人做的。