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
12 changes: 12 additions & 0 deletions .changeset/osv-exemption-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
---

ci(deps): 给 OSV 门禁的逃生口立三条约定并机械强制 (#4965)

`validate-deps.yml` 的 OSV 门禁对无修复版本的公告,唯一出路是 `osv-scanner.toml` 的
`[[IgnoredVulns]]`;但该文件此前不存在,逃生口一次没走过,也就没有约定 —— 而没有约定的
逃生口会在第一次紧急情况下被随手用坏,然后永久留在那里。本次新增零豁免的
`osv-scanner.toml`(三条约定写在文件头 + 一个注释掉的模板条目)与
`scripts/check-osv-exemptions.mjs`:`ignoreUntil` 强制(默认 30 天、上限 90 天,缺失/
加引号/过期/超上限一律判红),`reason` 强制且必须带 advisory 链接与一句"为什么不可修"。
零豁免状态下门禁行为与本次改动前逐条一致。仅 CI 配置,不发布任何包。
27 changes: 24 additions & 3 deletions .github/workflows/validate-deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ on:
- 'pnpm-workspace.yaml'
- 'scripts/check-changeset-fixed.mjs'
- 'scripts/check-override-consistency.mjs'
# The OSV exemption ledger and its check: a PR that touches either must
# run this workflow, or an exemption could be added without the gate
# that governs it ever running on the PR that adds it.
- 'osv-scanner.toml'
- 'scripts/check-osv-exemptions.mjs'
# Re-run when the workflow itself changes, so edits to these gates are
# exercised on the PR that introduces them.
- '.github/workflows/validate-deps.yml'
Expand Down Expand Up @@ -80,9 +85,25 @@ jobs:
# reads pnpm-lock.yaml directly against the OSV database and exits non-zero
# when any advisory matches, restoring a working blocking gate.
#
# Note: OSV-Scanner blocks on any severity, not just high/critical. To
# accept a specific advisory, add an osv-scanner.toml `[[IgnoredVulns]]`
# entry rather than lowering the gate.
# Note: OSV-Scanner blocks on any severity, not just high/critical. When
# the advisory has a fixed version you take the fix. The ONLY escape
# hatch, for an advisory with no fix available, is an `[[IgnoredVulns]]`
# entry in `osv-scanner.toml` at the repo root — never lowering the gate.
#
# That escape hatch is governed by three conventions (#4965), stated in
# full in the header of osv-scanner.toml: `ignoreUntil` mandatory
# (default 30 days, ceiling 90), `reason` mandatory with an advisory link
# plus a sentence on why it cannot be fixed, and exemptions land in their
# own `osv-exemption`-labelled PR. The scanner enforces none of that — it
# reads a missing `ignoreUntil` as "ignore forever", silently — so the
# step below runs first and fails on any exemption that is missing,
# quoted, expired, over the ceiling, or unexplained. `--self-test` proves
# the check in both directions.
- name: Verify OSV exemptions carry an expiry and a reason
run: |
node scripts/check-osv-exemptions.mjs --self-test
node scripts/check-osv-exemptions.mjs

- name: Audit dependencies for known vulnerabilities (OSV-Scanner)
uses: google/osv-scanner-action/osv-scanner-action@9fd1bcce27f67e3bd819a0a7620e332803dc43bc # v2.3.8
with:
Expand Down
85 changes: 85 additions & 0 deletions osv-scanner.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# OSV-Scanner exemption ledger — the only escape hatch for the OSV gate.
#
# The `Validate Package Dependencies` workflow (.github/workflows/
# validate-deps.yml) runs OSV-Scanner against pnpm-lock.yaml and blocks on any
# advisory at any severity. When an advisory HAS a fixed version, you take the
# fix — that path stays untouched (#4945 was cleared in ten minutes that way).
# This file exists for the other case only: an advisory with no fix available
# yet, where the alternative is pinning a required check red indefinitely, and
# a permanently red required check is worth exactly as much as no scan at all.
#
# This ledger currently holds ZERO exemptions. That is the intended steady
# state, not a coincidence — read the whole header before you change it.
#
# ---------------------------------------------------------------------------
# Three conventions govern every entry (decided on #4965)
# ---------------------------------------------------------------------------
#
# 1. `ignoreUntil` is MANDATORY. Default 30 days, hard ceiling 90.
#
# OSV-Scanner treats a missing `ignoreUntil` as "ignore forever" (verified
# against v2.3.8: an entry with no `ignoreUntil` filters the advisory out
# and the scan exits 0, with no warning, for the rest of the repo's life).
# A silent permanent exemption is the same failure as a permanently red
# job, only quieter — nobody ever looks again. With a date set, the scanner
# stops filtering the moment it passes and the gate goes red on its own,
# which is the one mechanical device that keeps "temporary" temporary.
#
# Enforced by scripts/check-osv-exemptions.mjs: a missing, quoted, expired,
# or too-distant `ignoreUntil` fails the gate before the scanner runs.
#
# 2. `reason` is MANDATORY and has a format:
#
# "<advisory URL> — <one sentence on why it cannot be fixed or routed
# around right now>"
#
# The URL and the sentence are for whoever reads this at renewal time: they
# have to decide whether the premise still holds, and "not exploitable"
# with no link and no argument gives them nothing to check. Say what blocks
# the fix (no upstream release / the patch is only in a major we cannot
# take yet / transitive through X) — not merely that it is inconvenient.
#
# Enforced by scripts/check-osv-exemptions.mjs: the reason must carry at
# least one https:// link and a real sentence beside it.
#
# 3. An exemption lands in its OWN pull request, labelled `osv-exemption`.
#
# Never bundled into a feature or dependency-bump PR. Accepting a known
# vulnerability is a security decision and it needs a review that is about
# exactly that decision — inside a 40-file PR it gets rubber-stamped along
# with everything else. The same applies to renewing one: a renewal is the
# original decision made again, with the same review.
#
# This one is DISCIPLINE, not a gate — no cheap mechanical check exists for
# it, and a naive "osv-scanner.toml must change alone" rule would punish
# the good case of deleting an exemption in the PR that finally fixes the
# dependency. Removing an exemption may ride along with the fix; adding or
# renewing one may not.
#
# ---------------------------------------------------------------------------
# Template — copy the block below, uncomment, fill in
# ---------------------------------------------------------------------------
#
# [[IgnoredVulns]]
# id = "GHSA-xxxx-xxxx-xxxx"
# ignoreUntil = 2026-09-02
# reason = "https://github.com/advisories/GHSA-xxxx-xxxx-xxxx — upstream has no fixed release; the only patched version is foo@6, which needs the ESM migration tracked in #1234. Re-check when foo 6 lands."
#
# Notes on the syntax, both learned the hard way from v2.3.8:
#
# * `ignoreUntil` must be a BARE TOML date (2026-09-02), never quoted. A
# quoted value makes the scanner discard THIS ENTIRE FILE — it prints
# "Ignored invalid config file ... because: toml: ..." and scans on as if
# no config existed. Fail-safe in direction, but every other exemption in
# the file silently stops applying.
# * An exemption also covers the advisory's aliases, so one GHSA id is
# usually enough; do not add the CVE alias as a second entry (the scanner
# only warns about duplicate ids and honours the first).
#
# Before you add an entry, check the fix really does not exist: `pnpm why
# <pkg>` for the path, then the advisory page for a patched range. Most of the
# time an override in pnpm-workspace.yaml is the answer and no exemption is
# needed at all.
#
# Verify locally: node scripts/check-osv-exemptions.mjs
# Prove the check: node scripts/check-osv-exemptions.mjs --self-test
Loading
Loading