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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ jobs:
with:
command: check

- name: Advisory ignore deadlines
run: node scripts/check-ignore-deadlines.mjs

coverage:
name: Coverage
needs: fmt
Expand Down
10 changes: 8 additions & 2 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ db-urls = ["https://github.com/rustsec/advisory-db"]
# Fail only on unmaintained *direct* deps; transitive noise (paste/im-rc/…) stays warn-level notes via ignore if needed.
unmaintained = "workspace"
ignore = [
# Transitive via extism → wasmtime ^43; no safe bump until Extism moves.
{ id = "RUSTSEC-2026-0222", reason = "wasmtime pin owned by extism 1.30" },
# Transitive via extism 1.30, which pins wasmtime ^43. RUSTSEC-2026-0222
# (GHSA-hgjw-h833-99q9) is patched only in >= 46.0.2 and >= 47.0.3; no
# 43.x release fixes it, and extism 1.30.0 is the newest extism release,
# so no bump is possible until extism itself moves. Time-boxed: CI fails
# when the date below passes, forcing a re-evaluation (extism on a
# patched wasmtime, or a local patch).
# expires: 2026-10-31
{ id = "RUSTSEC-2026-0222", reason = "wasmtime 43.x has no patched release; owned by extism 1.30" },
]

[licenses]
Expand Down
51 changes: 51 additions & 0 deletions scripts/check-ignore-deadlines.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env node
/**
* Advisory ignores in deny.toml must be time-boxed.
*
* Every `{ id = "RUSTSEC-…" }` entry in [advisories].ignore needs an
* `# expires: YYYY-MM-DD` line in the comment block directly above it, and
* that date must be in the future. When the date passes, CI fails, forcing a
* re-evaluation of the advisory instead of a silent indefinite exemption.
*/

import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'

const root = join(fileURLToPath(new URL('.', import.meta.url)), '..')
const target = process.argv[2] ? join(root, process.argv[2]) : join(root, 'deny.toml')

const lines = readFileSync(target, 'utf8').split(/\r?\n/)

const pad = (n) => String(n).padStart(2, '0')
const now = new Date()
const today = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`

let fails = 0
for (let i = 0; i < lines.length; i++) {
const entry = lines[i].match(/^\s*\{\s*id\s*=\s*"(RUSTSEC-[^"]+)"/)
if (!entry) continue
const id = entry[1]

let expires = null
for (let j = i - 1; j >= 0; j--) {
if (!lines[j].trimStart().startsWith('#')) break
const m = lines[j].match(/expires:\s*(\d{4}-\d{2}-\d{2})\b/)
if (m) expires = m[1]
}

if (!expires) {
console.error(
`deny.toml:${i + 1}: advisory ${id} is ignored without a time-box — add "# expires: YYYY-MM-DD" to its comment block`,
)
fails++
} else if (expires < today) {
console.error(
`deny.toml:${i + 1}: ignore for ${id} expired on ${expires} — re-evaluate the advisory and remove or renew the ignore`,
)
fails++
}
}

if (fails > 0) process.exit(1)
console.log('check-ignore-deadlines: clean')
Loading