diff --git a/.github/dependabot.yml b/.github/dependabot.yml index dab1f26..881c306 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,12 @@ version: 2 updates: + # target-branch 必须显式写成 dev:不写时 dependabot 打的是**仓库默认分支**(main), + # 依赖更新会直接落到发布分支上,dev 的锁文件只能等下一次 dev→main 才对得齐。 + # 2026-09-19 那批 bump PR 全部开在 main 上、又被 main 的 advisory 门禁一起拦红, + # 根因就在这种错位:dev 才是集成分支,bump 应当先在 dev 上过门禁。 - package-ecosystem: cargo directory: / + target-branch: dev schedule: interval: weekly day: monday @@ -11,6 +16,7 @@ updates: - package-ecosystem: github-actions directory: / + target-branch: dev schedule: interval: weekly day: monday diff --git a/.github/workflows/deny-drift.yml b/.github/workflows/deny-drift.yml new file mode 100644 index 0000000..7587d17 --- /dev/null +++ b/.github/workflows/deny-drift.yml @@ -0,0 +1,117 @@ +name: Deny drift + +# 上游 RustSec advisory 库每天都在变,而 cargo-deny 每次运行都会重新拉它。于是 +# **没有任何人改代码**,dev 上的 advisory 门禁也会自己变红,而最先撞上的是所有基于 +# dev 的 PR:2026-09-14 的 RUSTSEC-2026-0285(rustls < 0.23.45)就是这样一次性拦掉 +# 9 个 dependabot PR——连 actions/cache 这种根本不碰 Cargo.lock 的 PR 也红在同一个 +# 检查上,看起来像 PR 的问题,实际是 dev 的锁文件落后于上游公告。 +# +# 这个 workflow 把同一份检查搬到 dev 自己的定时任务上:漂移先在这里暴露并开 issue, +# 而不是在几十个无关 PR 上重复红。失败时的处理步骤见它打开的 issue 正文,也见 +# CONTRIBUTING.md「CI 说明」。 +# +# 注意:GitHub 的 schedule 触发器**只在默认分支(main)上生效**。所以这个检查要等本文件 +# 进入 main 之后才会自动跑;在那之前可以用 workflow_dispatch 手动触发。 +on: + schedule: + # 每周日 12:00 UTC(北京时间周日 20:00)。dependabot 的 cargo 检查是周一 02:00 + # (Asia/Shanghai),比它早 6 小时:漂移 issue 一般先于那一批 PR 出现。 + - cron: '0 12 * * 0' + workflow_dispatch: + +permissions: + contents: read + issues: write + +env: + # issue 的固定标题:脚本靠它去重,不要在标题里放会变的数字/日期。 + ISSUE_TITLE: '[deny] dev 的 cargo-deny 门禁因上游 advisory 漂移变红' + +jobs: + deny-drift: + name: cargo-deny 漂移检查(dev) + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + with: + # 定时任务本身由 main 上的本文件触发,但检查的必须是 dev 的工作树与 + # dev 的 deny.toml。 + ref: dev + + # continue-on-error 是刻意的:这一步失败正是要观测的信号,结论交给下一步决定 + # 是开 issue 还是关 issue,而不是让 job 在开 issue 之前就结束。 + - name: cargo-deny check (dev) + id: deny + continue-on-error: true + uses: EmbarkStudios/cargo-deny-action@v2 + with: + command: check + arguments: --workspace + + - name: 漂移开 issue,恢复后自动关闭 + uses: actions/github-script@v7 + env: + OUTCOME: ${{ steps.deny.outcome }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + TITLE: ${{ env.ISSUE_TITLE }} + with: + script: | + const outcome = process.env.OUTCOME; + const title = process.env.TITLE; + const runUrl = process.env.RUN_URL; + const repo = context.repo; + + // 按标题查已有的开启中 issue,避免每次告警都新建一个 + const { data } = await github.rest.search.issuesAndPullRequests({ + q: `repo:${repo.owner}/${repo.repo} is:issue is:open in:title "${title}"`, + }); + const existing = data.items[0]; + + if (outcome !== 'success') { + const body = [ + '`dev` 上的 `cargo-deny` 失败了,而**没有任何代码改动**。最常见的原因是上游', + 'RustSec advisory 库新增了一条针对锁文件里某个版本的公告(其次是某个 crate 被 yank)。', + '', + '**先确认这不是某个 PR 的错**:advisory 检查是全局的,此时所有基于 `dev` 的 PR', + '都会红在同一个 `Security (cargo-deny)` 上。', + '', + '处理步骤:', + '1. 看本次运行日志里 `error[vulnerability]` / `error[yanked]` 的条目与它的 `Solution:` 行:', + ` ${runUrl}`, + '2. 有修复版就在 `dev` 上单独提一个 fix PR 升级它(例:`cargo update -p rustls --precise 0.23.45`,', + ' 连带还会升 rustls-webpki / aws-lc-rs),并确认 `cargo deny check` 四段全绿;', + '3. 上游确实没有修复版时才写进 `deny.toml` 的 `ignore`,并在注释里写清影响面与跟踪条件;', + '4. 顺手删掉本次输出里 `warning[advisory-not-detected]` 指向的条目——该警告说明那条已经', + ' 随着依赖升级失效了。', + '', + `本次检查:${new Date().toISOString()}`, + ].join('\n'); + + if (existing) { + await github.rest.issues.createComment({ + ...repo, issue_number: existing.number, body, + }); + } else { + await github.rest.issues.create({ ...repo, title, body }); + } + return; + } + + // 门禁恢复全绿:关掉遗留的告警 issue,别留下永久红灯 + if (existing) { + await github.rest.issues.createComment({ + ...repo, + issue_number: existing.number, + body: `\`dev\` 的 cargo-deny 已恢复全绿,自动关闭。\n\n${runUrl}`, + }); + await github.rest.issues.update({ + ...repo, issue_number: existing.number, state: 'closed', + }); + } + + # 上面靠 continue-on-error 保住了 job,以便无论如何都能更新 issue;这里再把 run + # 变红,让 GitHub 的「scheduled workflow failed」通知也发出来——issue 有可能被漏看。 + - name: 漂移时让 run 变红 + if: steps.deny.outcome != 'success' + run: exit 1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 471769c..5829ec2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,6 +57,10 @@ git worktree add ../SoulMem-book doc/book - PR 还会按本次改动范围运行 cargo-mutants,杀灭率低于 90% 会直接失败。 - 每周(每隔一周)自动执行一次全量 mutants,并上传报告 artifact。 - 依赖安全由 Dependabot(自动更新 PR)和 cargo-deny(安全公告 + 许可证)把关。 +- `Deny drift` workflow 每周在 `dev` 上单独跑一次同一份 cargo-deny。上游 advisory 库 + 漂移(新增公告、crate 被 yank)时它会开 issue 并让 run 变红。**这类失败与具体 PR 无关**: + advisory 检查是全局的,基于 `dev` 的所有 PR 会一起红在同一个 `Security (cargo-deny)` 上, + 所以先看 issue 正文的处理步骤,不要靠往 `deny.toml` 的 `ignore` 里塞条目让它变绿。 ## 测试要求 diff --git a/Cargo.lock b/Cargo.lock index 73cfa8c..bc7d5ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -537,9 +537,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -548,14 +548,15 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.41.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" dependencies = [ "cc", "cmake", "dunce", "fs_extra", + "pkg-config", ] [[package]] @@ -6854,9 +6855,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" dependencies = [ "aws-lc-rs", "log", @@ -6919,9 +6920,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", diff --git a/deny.toml b/deny.toml index b32df3c..5e8138a 100644 --- a/deny.toml +++ b/deny.toml @@ -2,28 +2,25 @@ [advisories] version = 2 yanked = "deny" -# unmaintained 只作提示,不阻塞:依赖树里多处传递依赖(backoff/instant/ -# number_prefix/paste/rand_os 等)被 embed_anything 0.6.7 钉死,无安全升级。 +# unmaintained 直接关闭:树上有一批无人维护的传递依赖(现存 number_prefix、 +# paste 等;backoff/instant/rand_os 已随依赖升级消失),逐条维护这份名单只会反复 +# 漂移。这不影响安全结论:真正可利用的漏洞仍由 [advisories] 拦截,只有显式 ignore +# 才放行。 unmaintained = "none" -# 以下漏洞均来自 embed_anything 0.6.7 的传递依赖,无法在保持该版本的 -# 前提下升级修复;升级 embed_anything(如 feature/test_framework 分支的 -# 0.7.1)后应移除对应条目: +# ignore 只写「上游确实没有修复版」的条目,并写清影响面与跟踪条件。 +# cargo-deny 每次运行都会对**没有命中任何 crate** 的条目报 +# `warning[advisory-not-detected]`——出现该警告说明对应的传递依赖已被升级掉, +# 该条目应当删除(RUSTSEC-2026-0098/0099/0104 就是这样在 embed_anything 升到 +# 0.7.1、rustls-webpki 从 0.101 升到 0.103 之后失效并被移除的)。 # - RUSTSEC-2026-0187: lopdf 解析深层 PDF 对象时栈溢出;pdf-extract 0.9.0 钉死 # lopdf 0.36,0.42+ 不可达。SoulMem 不解析不可信 PDF。 -# - RUSTSEC-2026-0098/0099: rustls-webpki 0.101 的 name-constraint 校验问题; -# 仅在被错误签发的证书 + 自定义 name-constraint 场景下可被利用,且 rustls 0.21 -# 钉死 webpki 0.101,需随 aws-sdk/rustls 升级(embed_anything 依赖链)才能修复。 -# - RUSTSEC-2026-0104: rustls-webpki CRL 解析可达 panic;应用不使用 CRL,不受影响。 +# - RUSTSEC-2023-0071: rsa crate Marvin Attack(时序侧信道,CVSS 7.4)。 +# 经 jsonwebtoken 10.4.0 ← surrealdb 链传递引入;rsa 上游 patched=[] 自 2023 +# 年至今无修复版(最新 0.9.10 仍受影响,0.10 尚在 rc)。影响面:攻击者可观测 +# 时序时恢复私钥。SoulMem 仅在本地/可信环境使用 SurrealDB 嵌入式存储, +# 不暴露给可观测时序的网络攻击者,故忽略并跟踪上游修复。 ignore = [ "RUSTSEC-2026-0187", - "RUSTSEC-2026-0098", - "RUSTSEC-2026-0099", - "RUSTSEC-2026-0104", - # RUSTSEC-2023-0071: rsa crate Marvin Attack(时序侧信道,CVSS 7.4)。 - # 经 jsonwebtoken 10.4.0 ← surrealdb 链传递引入;rsa 上游 patched=[] 自 2023 - # 年至今无修复版(最新 0.9.10 仍受影响,0.10 尚在 rc)。影响面:攻击者可观测 - # 时序时恢复私钥。SoulMem 仅在本地/可信环境使用 SurrealDB 嵌入式存储, - # 不暴露给可观测时序的网络攻击者,故忽略并跟踪上游修复。 "RUSTSEC-2023-0071", ]