zh CN 安全策略 - chiba233/yumeDSL GitHub Wiki

安全策略

Home | 贡献指南


受支持的版本

版本 是否支持
1.x
0.x 否(开发阶段)

更详细的安全注意事项与安全 UGC 教程请参考:Security WikiSafe UGC Chat 教程


报告漏洞

请不要为安全漏洞创建公开 Issue。

请通过私密渠道上报:

  • GitHub 私密漏洞报告:访问 Security 页面并点击“Report a vulnerability”。
  • 邮件:向仓库维护者发送详细信息(见 GitHub 主页联系方式)。

报告内容

报告安全问题时,请提供:

  1. 漏洞描述
  2. 复现步骤
  3. 受影响版本
  4. 影响评估(如已知)

预期流程

  • 收到报告后 48 小时内确认
  • 7 天内给出状态更新
  • 对已确认漏洞提供修复或缓解计划

范围

本策略覆盖 yume-dsl-rich-text。以下情况不在覆盖范围

  • 你在解析器之上构建的渲染层漏洞(属于你的应用代码)
  • 纯性能回退或资源放大,但仍落在已记录的 fallback / budget / coarse diff 语义内的情况
  • 超大输入、超大单次编辑、或高重复对抗性输入导致的拒绝服务风险(请在应用层设置 depthLimit、输入大小限制、编辑大小限制与请求级超时)

已知安全注意事项

1. 渲染层仍然是主要信任边界

  • URL 不会被自动验证:如果你把 link 渲染成 <a>,必须在渲染层净化 url
  • raw 内容会原样透传:如果你把它渲染成 HTML,必须自行转义 / 消毒
  • 换句话说:解析成功不等于内容安全yume-dsl-rich-text 负责结构正确性,不负责 HTML / URL 安全策略

2. handlers / syntax / tagName 属于受信任配置,不是用户输入面

这个库允许调用方提供:

  • handlers
  • syntax
  • tagName
  • createId
  • tracker

这些能力是为了宿主应用自定义解析行为,不是为了让终端用户动态提供解析代码或解析规则。

从实现上看:

  • handlers 里的函数会直接在解析 / 渲染流程里执行
  • createId 会参与 parse lifecycle
  • syntax / tagName 会直接影响扫描和识别路径
  • tracker / baseOffset 会直接影响位置结果

因此,不要把这些对象暴露为不受信任输入,也不要在 session 生命周期中原地修改它们。安全模型默认它们是应用内部的受信任配置。

3. depthLimit 只是第一层资源护栏,不是完整 DoS 防线

源码里 depthLimit 默认值是 50。它主要防的是异常深嵌套路径,不代表:

  • 文档总大小已经受控
  • 单次编辑成本已经受控
  • diff 成本已经受控

如果输入不受信任,除了 depthLimit 之外,你仍然需要限制总文档大小单次编辑大小请求级执行时间

4. 需要按 heap / CPU 预算理解风险,而不只是按输入字节数理解

这个库的结构化解析、position 跟踪、增量文档、zone、signature 与 diff 数据,都会引入明显高于原始源码大小的内存占用。

也就是说,真正需要约束的不是只有“输入几 KB / 几 MB”,还包括:

  • 解析后 AST / token 树的体积
  • position 数据
  • 增量 session 持有的前一代 / 当前代快照
  • diff 结果里的 ops / patches / dirtySpan

对于不受信任输入场景,请把heap 预算并发解析数视为一等约束,而不是只限制请求体大小。

5. 增量 session 的设计目标是“正确新快照优先”,不是“恒定低成本”

createIncrementalSession(...) 的首要目标是推进到正确的新文档快照。为此它允许并公开以下行为:

  • mode 可能是 incremental,也可能是 full-fallback
  • fallbackReason 可能是业务内预期值,例如 INTERNAL_FULL_REBUILDAUTO_LARGE_EDITAUTO_COOLDOWN
  • auto 策略会根据近期样本自动偏向 full rebuild,而不是承诺始终走增量路径

默认自适应参数也说明了它是运行时策略,不是安全边界:

  • sampleWindowSize = 24
  • minSamplesForAdaptation = 6
  • maxFallbackRate = 0.35
  • switchToFullMultiplier = 1.1
  • fullPreferenceCooldownEdits = 12
  • maxEditRatioForIncremental = 0.2
  • softZoneNodeCap = 64

这些值的含义是“当增量不划算时尽快退回更稳的 full rebuild”,而不是“任何输入都保持便宜”。

6. applyEditWithDiff(...) 的 budgets 保证的是“有界退化”,不是“细粒度稳定输出”

当前 diff refinement 的默认预算是:

  • refinementDepthCap = 64
  • maxComparedNodes = 20000
  • maxAnchorCandidates = 128
  • maxOps = 512
  • maxSubtreeNodes = 256
  • maxMilliseconds = 8

这些预算的作用是:一旦细粒度 diff 不划算,就尽快退化。退化后的语义是允许变粗的,而不是继续承诺“小脏区、细粒度 patch、低延迟”。

从源码行为看,以下情况都属于设计内行为,而不是安全漏洞本身:

  • 返回 root-level splice
  • patches 退化成一次 replace
  • unchangedRanges 为空
  • dirtySpanOld / dirtySpanNew 扩大到接近整篇文档
  • refinement 抛错后退回 conservative diff

因此,调用方不能applyEditWithDiff(...) 当成以下任一保证:

  1. 永远是细粒度 diff
  2. 永远只覆盖编辑窗口附近
  3. 永远比 full rebuild 便宜
  4. 永远适合直接暴露给不受信任流量

7. 高重复 raw / block 与深层 inline 仍然是资源压力热点

当前实现已经有 fallback、budget 与 coarse diff 机制,但高重复 raw / block、深层 inline、以及对锚点不友好的编辑模式,仍可能明显放大:

  • CPU 比较成本
  • dirty span
  • diff 退化频率
  • full-fallback 触发率

这类行为应该被理解为需要在部署层约束和监控的资源风险,而不是单靠解析器内部策略兜底。

8. 面向不受信任输入的最低部署要求

如果你把本库用于 UGC、协作编辑、聊天消息、富文本草稿或其他不受信任输入场景,至少应同时做到:

  1. 限制文档总大小
  2. 限制单次编辑大小 / 编辑比例
  3. 为请求、任务或 worker 设置 wall-clock 超时与取消机制
  4. 服务器侧对 applyEditWithDiff(...) 使用更保守的 budgets,或直接禁用 diff
  5. 不要把 raw 直接当作可信 HTML / DOM 片段
  6. 不要让终端用户直接控制 handlerssyntaxtagNamecreateIdtracker
  7. 监控 full-fallback 频率、异常大 dirty span、异常延迟与异常 heap 增长,并把它们视为资源告警信号

许可证

本项目基于 MIT 许可证 发布。安全修复以尽力而为方式提供。