Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -11,6 +16,7 @@ updates:

- package-ecosystem: github-actions
directory: /
target-branch: dev
schedule:
interval: weekly
day: monday
Expand Down
117 changes: 117 additions & 0 deletions .github/workflows/deny-drift.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` 里塞条目让它变绿。

## 测试要求

Expand Down
17 changes: 9 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 14 additions & 17 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand Down
Loading