安全同源补漏
定义
安全同源补漏不是泛指“做了一次安全修复”。它特指:当某一类漏洞已经在一个点位上被发现并修补后,继续沿着同一机制、同一数据流或同一防护规则,排查其他入口、模块、实现分支和边界条件,把原本分散、不一致、易漂移的防护补齐成统一治理。
它要解决的问题是:同一种风险往往不会只出现在一个函数或一个命令里。如果只补发现处,而不回头看同类代码路径,就会出现“修了一处、漏了别处”的情况,导致攻击者仍能从平行入口绕过防护。
因此,安全同源补漏强调的不是单点打补丁,而是同漏洞类型的横向清查、规则共用化、校验前置化和行为一致化。
在本文档中的语境
本文讨论的是 Godot MCP Enhanced 在 v0.18.2 与 v0.19.0 两轮发布中表现出来的安全治理方式。原文明确给出的判断是:维护者对安全并不是“一次性处理完就算了”,而是在 9 天内连续做了两轮收敛攻击面的工作。
- v0.18.2 先做了一轮集中的安全加固,包括 GDScript 沙箱、防 ReDoS、命令注入收敛、rate limit 等。
- v0.19.0 则继续补同类缺口,针对前一轮已经暴露出的风险模型,排查其他未统一收口的实现点。
所以这里的“同源”,不是说漏洞编号相同,而是说它们属于同一类安全机制:路径校验、属性阻断、认证/实例写入、文件系统边界等。维护者在第二轮中继续把这些机制的遗漏点补齐,这正是安全同源补漏的典型案例。
本文列出的 4 个具体补漏点
1. UI BLOCKED_PROPS:抽 shared 模块,并在 handler 层前置硬拒
原文给出的补漏点之一是:把 findBlockedProps 抽成 shared 模块,同时把拒绝逻辑前置到 handler 层。
这里体现了两个关键动作:
- 共享实现:把原本可能散落在不同 UI 入口里的
findBlockedProps提取成共用模块。 - 前置拒绝:不是等后续流程跑到更深层再发现问题,而是在 handler 层就直接拒绝。
这说明维护者不是只修某一个 UI 命令里的 blocked props 检查,而是意识到同类规则如果复制在多个入口中,很容易因为版本差异、条件遗漏或后续修改不同步而产生规则漂移。一旦漂移,就会形成防护缺口。
把 findBlockedProps 做成 shared 模块的意义就在于:
- 同一套阻断规则只维护一份;
- 新旧入口更不容易出现“这个入口拦了、另一个入口没拦”的不一致;
- 安全逻辑可以被更早调用,减少危险输入进入后续流程的机会。
这类修法比“补一个 if”更像治理,因为它降低了未来再次漏改同类入口的概率。
2. audio stream_path:统一经 sanitizeResPath 校验
第二个补漏点是 audio 的 stream_path 不再各自零散判断,而是统一走 sanitizeResPath 对 res:// 路径做校验。
这体现的是路径校验统一化。很多安全问题不是“完全没校验”,而是“有的地方校验了,有的地方靠调用者自觉,有的地方用的是另一套弱一点的判断”。一旦路径相关字段分布在不同模块中,这种不一致就很容易留下旁路。
统一经 sanitizeResPath 的意义在于:
- 把
stream_path纳入与其他资源路径一致的校验框架; - 减少某个模块自己拼一套简化校验导致的遗漏;
- 让
res://资源路径的合法性边界在多个调用点保持一致。
这就是典型的同源补漏:不是因为 audio 独自发明了新漏洞,而是它原本处在同类路径风险里,却没有完全并入统一校验链。
3. instance-api-auth symlink:写入前检测符号链接并 unlink
第三个补漏点是 instance-api-auth 的 symlink 问题:通过 lstatSync 检测符号链接,再配合 unlink,防止后续 writeFileSync 跟踪 symlink。
这背后的本质不是“路径字符串看起来是否合法”,而是文件系统对象在真实落盘时可能已经不是你以为的那个目标。
如果写文件逻辑会跟踪符号链接,那么即使前面的路径限制原本想把写入约束在某个安全位置,攻击者仍可能通过事先布置 symlink,把写操作导向其他位置,形成绕过。
因此这里的补漏重点不是普通的字符串过滤,而是:
- 在写入前用
lstatSync判断目标是否是符号链接; - 如果是,就先
unlink处理掉该链接; - 避免
writeFileSync顺着 symlink 写到意料之外的位置。
这说明维护者已经把安全边界从“输入长什么样”推进到“文件系统实际会发生什么”。这也是同源补漏常见的升级路径:从表层校验,收敛到真实执行语义。
4. instance-manager 路径遍历:从 includes('..') 升级到段级判断
第四个补漏点是 instance-manager 的路径遍历判断改进。原先用 includes('..') 过于粗暴,会误拒合法路径,后来改成路径段级判断,即只把 seg === '..' 视为危险。
这里的关键不是“拦得越狠越安全”,而是要正确地识别真正的遍历语义。
includes('..') 的问题在于,它只是在字符串层面查子串:
- 某些合法路径片段只要包含连续两个点,就可能被误杀;
- 它无法区分“路径段本身就是
..”和“文件名、资源名、普通文本里刚好出现了..”; - 这种粗暴规则会带来误报,最终伤害可用性,甚至促使后续开发者绕过这套检查。
而路径遍历真正危险的条件,通常是路径分段里出现了表示上级目录的独立段,也就是 .. 作为一个 segment 存在。因此升级为段级判断 seg === '..',才是从“字符串包含判断”提升到“路径语义判断”。
这类改动很典型地体现了安全与可用性平衡:防护不能只追求看起来严格,而要尽量减少对合法输入的误拒,否则规则本身会变得不可靠。
shared 模块化的治理意义
在这 4 个补漏点里,最具有治理意味的是 shared 模块化,尤其是 findBlockedProps 的提取。
安全规则如果散落在不同入口里各写一份,短期看部署快,长期看风险大:
- 某个入口修了,另一个入口忘了同步;
- 某处新增边界条件,其他复制版本没有更新;
- 不同维护者各自改一版,最终行为分叉。
一旦规则漂移,攻击者就会优先寻找“那条还没跟上修复的路径”。