Skip to content

fix(deps): dompurify 顶到 3.4.13,清掉 GHSA-55q2-fjhq-7xh7 (#6407) - #6427

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-6407-dompurify-override
Aug 7, 2026
Merged

fix(deps): dompurify 顶到 3.4.13,清掉 GHSA-55q2-fjhq-7xh7 (#6407)#6427
hotlong merged 1 commit into
mainfrom
claude/issue-6407-dompurify-override

Conversation

@hotlong

@hotlong hotlong commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #6407

Validate Package Dependencies 里的 OSV-Scanner 在 pnpm-lock.yaml 上命中 GHSA-55q2-fjhq-7xh7(5.1 medium,DOMPurify 的 IN_PLACE hook 移除会留下一棵仍可执行的 detached subtree,XSS)。公告有 fixed version(3.4.13),按 osv-scanner.toml 抬头写明的纪律 —— "When an advisory HAS a fixed version, you take the fix … This file exists for the other case only" —— 一律顶版本,不走豁免路线。本 PR 只做这一件事。

落点与 override 条目

dompurifymermaid 的传递依赖:apps/docs 声明 mermaid ^11.16.0,实装的 mermaid@11.16.1 声明 dompurify ^3.3.3没有任何工作区包直接声明 dompurify(grep '"dompurify"' --include=package.json 零命中),所以落点是 override,且没有需要同步的 publishable manifest。

before:pnpm-workspace.yaml 里没有 dompurify 条目(lock 解析到 3.4.12)。

after:

'dompurify@<4.0.0': '^3.4.13'

体例来源:照抄本文件 override 块头部自己写死的形状 —— "pkg@>=affected floor next major above the target: ^patched",并取同文件里 floor 为 0 的那一类既有条目的写法(fast-uri@<4.0.0 -> ^3.1.5hono@<5.0.0 -> ^4.12.34uuid@<12.0.0 -> ^11.1.1)。本公告的 OSV affected range 是 introduced: 0 -> fixed: 3.4.13,即 floor 就是包的下界,所以只需声明上界,用 pkg@<4.0.0 这种裸上界形,而不是带 >= 下界的形。

上界放在 4.0.0 这个 major 边界,⛔ 不是 exclusive 的 fixed version:写成 <3.4.13 会在 3.4.13 自己被公告的那天静默失配 —— undici >=7.23.0 <7.28.0 和 brace-expansion 5.0.8 两具活体标本(#4961 / #5032)。以后只挪 target。check:override-consistency 的 self-expiring 普查没有点名本条(见下面门禁段)。

顺带一条实测:^3.4.13 落在 mermaid 自己的 ^3.3.3 范围之内,所以这是把传递副本 dedupe 到已修补线,而不是把 mermaid 顶过它支持的范围。

required 还是 noisy —— 实测结论(issue 正文与 #5617 存档的矛盾)

issue 正文称它是一条 required check;#5617 的 required-set 审计存档记录该 job 带 paths: 过滤 ⇒ 永不可 required。两者不可能同时为真。实测:

结论:它今天是「全车道噪声」,不是「全车道停摆」。 三条独立证据:

  1. .github/workflows/validate-deps.yml 确有 paths: 过滤(第 3-19 行):pull_request 只在改到 **/package.jsonpnpm-lock.yamlpnpm-workspace.yaml.changeset/config.jsonosv-scanner.toml 与两个 scripts/check-*.mjs、以及该 workflow 自身时触发。⇒ 不碰这些路径的 PR 上该 context 根本不出现,一旦 required 就会永久阻塞 —— 与 红的 ESLint job 没有拦住合并:PR #5584 在 ESLint 已红 19 分钟的情况下过队合入 —— main 的 required-status-check 集需要维护者核查 #5617 的存档一致。
  2. 红了照样合并(决定性):issue 就是从 PR fix(spec): packages/spec/scripts/** 进入类型检查程序 —— 新增 tsconfig.scripts.json,修掉全部 43 条错误 #6398 撞出来的。该 PR 的 Validate Package Dependencies = failure(job 92935337053,16:49:29Z 结束),而 PR 17:30:13Z 正常合并。required check 红是合不掉的。
  3. 车道未停:立单(17:01Z)之后本车道仍有 15 个 PR 成功合并(fix(metadata): sys_view_definition 的「活跃行唯一」补运行时 partial UNIQUE 迁移 (#5839) #6415 17:56Z、feat(spec)!: ADR-0049 enforce-or-remove —— ui/widget + ui/i18n 家族 13 退 1 留 (#5055) #6385 17:54Z、fix(spec): 正文里裸露的源码路径,../ 前缀回到链接里面 (#6229) #6408 17:36Z、fix(objectql): ScopedContext.transaction 补齐 ADR-0067 D2 的 ambient join (#6168) #6403 17:27Z …)。

因此紧迫度的正确读法是:它不阻塞合并,但会在每一个碰到上述路径的 PR 上留一片红,污染 CI 信号、逼每个 agent 停下来辨认「这红是不是我造成的」。修还是要修,而且要快;但不需要因此复核 required 集 —— 该 job 本来就不在里面,也不应该在。⚠️ 副作用提醒:正因为它不是 required,本 PR 自己也不会被它挡住。

反向验证(先申报,后执行)

四条声明的预期方向在跑之前就定死,结果如下。

声明 A —— 公告消失,且不引入新公告

⚠️ 偏差如实申报:CI 用的是 google/osv-scanner-action 的在线扫描,而本容器的 egress 策略把 api.osv.dev 挡在 403(CONNECT tunnel failed, response 403,proxy status 端点已记录)。⛔ 不把推断写成实测,改用同版本的本地离线 DB 复现:go install github.com/google/osv-scanner/v2/cmd/osv-scanner@v2.3.8(与 CI action 所钉版本一致),DB 走 osv-vulnerabilities.storage.googleapis.com(可达,HTTP 200),扫的是同一个 pnpm-lock.yaml、同一份 osv-scanner.toml filter。

before(EXIT=1):

Scanned /home/user/objectstack-6407/pnpm-lock.yaml file and found 1387 packages
Loaded filter from: /home/user/objectstack-6407/osv-scanner.toml
Loaded npm local db from /root/.cache/osv-scanner/npm/all.zip

Total 1 package affected by 1 known vulnerability (0 Critical, 0 High, 1 Medium, 0 Low, 0 Unknown) from 1 ecosystem.
1 vulnerability can be fixed.

+-------------------------------------+------+-----------+-----------+---------+---------------+----------------+
| OSV URL                             | CVSS | ECOSYSTEM | PACKAGE   | VERSION | FIXED VERSION | SOURCE         |
+-------------------------------------+------+-----------+-----------+---------+---------------+----------------+
| https://osv.dev/GHSA-55q2-fjhq-7xh7 | 5.1  | npm       | dompurify | 3.4.12  | 3.4.13        | pnpm-lock.yaml |
+-------------------------------------+------+-----------+-----------+---------+---------------+----------------+

与 issue 正文引用的 CI 输出逐字节一致(同样的 1 Medium、同样的 5.1、同样的 3.4.12 -> 3.4.13),证明本地复现是可信的等价物而非近似。

after(EXIT=0):

Scanned /home/user/objectstack-6407/pnpm-lock.yaml file and found 1387 packages
Loaded filter from: /home/user/objectstack-6407/osv-scanner.toml
Loaded npm local db from /root/.cache/osv-scanner/npm/all.zip

No issues found

A 结果:PASS。 该条消失,退出码 1 -> 0,且包总数不变(1387 -> 1387)、无任何新公告。

声明 B —— override 真的生效(陷阱 1 的整个教训所在)

不看 pnpm-workspace.yaml 写了什么,只看重新生成的 pnpm-lock.yaml 解析成了什么:

$ grep -n dompurify pnpm-lock.yaml
36:  dompurify@<4.0.0: ^3.4.13          <- override 进了 lock 的 overrides 段
5781:  dompurify@3.4.13:                <- packages 段
12094:  dompurify@3.4.13:               <- snapshots 段
13654:      dompurify: 3.4.13           <- mermaid 的依赖块

改前同一条 grep 是三处 3.4.12。磁盘上再验一次实装解析:

$ node -e "console.log(require('.../mermaid@11.16.1/node_modules/dompurify/package.json').version)"
3.4.13
$ find node_modules -name dompurify -type l   # 活链接全部指向 3.4.13
node_modules/.pnpm/mermaid@11.16.1/node_modules/dompurify -> ../../dompurify@3.4.13/node_modules/dompurify
node_modules/.pnpm/node_modules/dompurify     -> ../dompurify@3.4.13/node_modules/dompurify

B 结果:PASS。 (⚠️ 如实记录一处噪声:虚拟 store 里还留着一个 node_modules/.pnpm/dompurify@3.4.12 目录,是改动前那次 install 的孤儿残留 —— 上面的 find 显示没有任何活链接指向它,lock 里也没有 3.4.12,CI 是全新 install,不受影响。)

声明 C —— mermaid 未被弄坏

dompurify 是 mermaid 的传递依赖,顶版本后 mermaid 相关功能不得回归。跑 Build Docs 这个 job 的实际命令(.github/workflows/ci.yml:1143):

$ pnpm --filter @objectstack/docs build
DOCS_BUILD_EXIT=0

390+ 个页面全部预渲染成功,含全部带 mermaid 代码块的文档页(content/docs/protocol/diagram.mdxkernel/architecture.mdxapi/data-flow.mdx 等)。

另外核实过:仓内其余 mermaid 命中(packages/spec/src/automation/flow.zod.ts:468 的一句注释、packages/cli/src/utils/collect-docs.test.ts:60 的一个 markdown fence 标签)都只是这个词,与 npm 包无耦合,不构成额外取证面。

C 结果:PASS。

声明 D —— 锁文件最小变更

$ git diff --stat
 pnpm-lock.yaml      |  9 +++++----
 pnpm-workspace.yaml | 18 ++++++++++++++++++

lock 侧 4 行实质变更,全部集中在 dompurify:overrides 段 +1 行、packages 段的 version+integrity、snapshots 段的 key、mermaid 依赖块的解析版本。无大范围重排,无其他包被动。

D 结果:PASS。

门禁

门禁 命令 EXIT
override 一致性 pnpm check:override-consistency 0
控制字节 pnpm check:nul-bytes 0
YAML 解析 yaml.parse() 两个文件 + 读回 override 值 0
lock 与声明自洽 pnpm install --frozen-lockfile 0

check:override-consistency 的两条普查里都没有点到本条:idle 普查没有(mermaid 在消费它),self-expiring 普查也没有(<4.0.0 在 target ^3.4.13 之上,正是该文件说的 durable 形状)。该普查列出的 14 条是既有条目(better-auth 家族 + 三条待 #5835 式裁决的零消费 pin),本 PR 未触碰。

check:nul-bytes 扫了 6063 个受版本控制的文本文件;另按字节纪律对两个改动文件单独自扫 [\x00-\x08\x0b\x0c\x0e-\x1f\x7f],零命中。

不在本 PR 里

  • 没动 osv-scanner.toml —— 本公告有 fix,按该文件抬头的纪律豁免路线不适用。
  • 没动任何工作区包的 package.json 依赖声明 —— 没有包直接声明 dompurify,不存在需要同步的 publishable manifest。
  • 没动 .github/workflows/** —— 只读核实了 paths: 形状(上面 required-vs-noisy 段)。
  • 没改 required 集,没碰 content/docs/releases/**
  • 没有 changeset:只改 override 与 lock,不改任何工作区包的源码/公开 API/声明依赖,发布物为零 ⇒ 走 skip-changeset。最接近的先例 finding: pnpm override 台账两项体检事实 —— 26/28 条自失效上界形状、3 条 #5835 裁决范围外的空转 override #6095(同样只改 pnpm-workspace.yaml + pnpm-lock.yaml 的 override 选择器)同样没带 changeset。mermaid 渲染行为不变(^3.4.13 落在 mermaid 自己的 ^3.3.3 之内,是安全补丁而非行为变更),故不构成用户可见变化。

Generated by Claude Code

`Validate Package Dependencies` 的 OSV-Scanner 在 pnpm-lock.yaml 上命中
GHSA-55q2-fjhq-7xh7(5.1 medium):dompurify 的 IN_PLACE hook 移除会留下
一棵仍可执行的 detached subtree(XSS)。公告有 fixed version(3.4.13),
按 osv-scanner.toml 抬头写明的纪律,这类一律「take the fix」,不走豁免。

dompurify 是 mermaid 的传递依赖(apps/docs 声明 mermaid ^11.16.0,
mermaid@11.16.1 声明 dompurify ^3.3.3),没有任何工作区包直接声明它,
所以落点是 pnpm-workspace.yaml 的 override 块 —— 本仓不读 package.json
里的 overrides。

选择器上界放在 4.0.0 这个 major 边界而不是 exclusive 的 fixed version:
写成 `<3.4.13` 会在 3.4.13 自己被公告的那天静默失配(undici 7.28.0 与
brace-expansion 5.0.8 两具活体标本,#4961 / #5032)。以后只挪 target。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 7, 2026 6:10pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate Package Dependencies 在所有 PR 上红:dompurify@3.4.12 新公告 GHSA-55q2-fjhq-7xh7(已有 fix 3.4.13,经 mermaid 传递引入)

2 participants