All checks were successful
jouleclaw fabrics / cuda (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 32s
jouleclaw fabrics / vulkan (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 15s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 7s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 7s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m48s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 1m0s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 1m36s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 2m13s
wai-quantum on every processor / emulated · aarch64-unknown-linux-gnu (pull_request) Successful in 1m32s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 1m38s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
WAI conformance / does this change touch it (pull_request) Successful in 1m26s
wai-quantum on every processor / emulated · armv7-unknown-linux-gnueabihf (pull_request) Successful in 2m43s
WAI energy receipts / does this change touch it (pull_request) Successful in 35s
WAI JPEG-AI whole decode (pinned inputs, required) / does this change touch it (pull_request) Successful in 29s
WAI neural sink gates / does this change touch it (pull_request) Successful in 31s
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 21s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 9s
wai-quantum command line / does this change touch it (pull_request) Successful in 9s
wai-quantum CUDA member / does this change touch it (pull_request) Successful in 10s
wai-quantum dataflow member / does this change touch it (pull_request) Successful in 12s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 4m9s
wai-quantum signal processor / does this change touch it (pull_request) Successful in 6s
wai-quantum FPGA member / does this change touch it (pull_request) Successful in 7s
wai-quantum HIP member / does this change touch it (pull_request) Successful in 6s
wai-quantum Metal member / does this change touch it (pull_request) Successful in 7s
wai-quantum npu-cores member / does this change touch it (pull_request) Successful in 6s
wai-quantum OpenCL member / does this change touch it (pull_request) Successful in 6s
wai-quantum neural-processor member / does this change touch it (pull_request) Successful in 7s
wai-quantum-qpu / does this change touch it (pull_request) Successful in 6s
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Successful in 23s
wai-quantum service / does this change touch it (pull_request) Successful in 7s
wai-quantum matrix-extension member / does this change touch it (pull_request) Successful in 8s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 6s
wai-quantum HIP member / member · HIP (framework-128gb) (pull_request) Successful in 20s
wai-quantum tensor-engine member / does this change touch it (pull_request) Successful in 6s
wai-quantum portable tensor program / does this change touch it (pull_request) Successful in 7s
wai-quantum CPU vector units / does this change touch it (pull_request) Successful in 6s
wai-quantum WebGPU member / does this change touch it (pull_request) Successful in 5s
WAI mbt2018_mean integer decode (regenerated vectors) / does this change touch it (pull_request) Successful in 5s
wai-web wasm32 / does this change touch it (pull_request) Successful in 5s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Successful in 23s
wai-quantum on every processor / emulated · loongarch64-unknown-linux-gnu (pull_request) Successful in 3m39s
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Successful in 20s
wai-quantum npu-cores member / member · npu-cores, neural processor (framework-128gb silicon) (pull_request) Successful in 1m12s
wai-quantum OpenCL member / member · OpenCL GPU (framework-128gb) (pull_request) Successful in 13s
wai-quantum portable tensor program / tensor program · integrated GPU (framework-128gb) (pull_request) Successful in 8s
wai-quantum CPU vector units / member · vector units, avx2 and avx512 (framework-128gb silicon) (pull_request) Successful in 10s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m55s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 46s
wai-quantum WebGPU member / member · DX12 and Vulkan (cbio-windows) (pull_request) Successful in 12m1s
wai-quantum on every processor / emulated · powerpc64-unknown-linux-gnu (pull_request) Successful in 5m22s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 22s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m19s
wai-quantum on every processor / emulated · powerpc64le-unknown-linux-gnu (pull_request) Successful in 6m3s
wai-quantum on every processor / emulated · riscv64gc-unknown-linux-gnu (pull_request) Successful in 4m20s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 3m10s
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Successful in 7m42s
wai-quantum on every processor / emulated · s390x-unknown-linux-gnu (pull_request) Successful in 4m22s
wai-quantum on every processor / emulated · sparc64-unknown-linux-gnu (pull_request) Successful in 6m35s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 7s
wai-quantum on every processor / emulated · i686-unknown-linux-gnu (pull_request) Successful in 5m0s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m39s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 34s
wai-quantum WebGPU member / member · discrete GPU, Vulkan (jetson-hub) (pull_request) Successful in 24m31s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 18m22s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 3m8s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 32m7s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 33s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m36s
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Successful in 29s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 10m45s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 5m57s
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Successful in 1m20s
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Successful in 2m13s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 22m22s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m23s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 6m57s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 1m40s
wai-quantum command line / command line · linux (pull_request) Successful in 1m23s
wai-pack / wai-pack · linux (pull_request) Successful in 1m42s
wai-quantum CUDA member / member · CUDA absent (linux) (pull_request) Successful in 25s
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:b140) (pull_request) Successful in 4m1s
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:401e) (pull_request) Successful in 5m8s
wai-quantum FPGA member / member · FPGA simulation absent (linux) (pull_request) Successful in 20s
wai-quantum Metal member / member · Metal absent (linux) (pull_request) Successful in 23s
wai-quantum OpenCL member / member · OpenCL absent (linux) (pull_request) Successful in 21s
wai-quantum neural-processor member / member · neural processor absent (linux) (pull_request) Successful in 22s
wai-quantum-qpu / clients against their contracts (linux) (pull_request) Successful in 24s
wai-quantum service / service · linux (pull_request) Successful in 46s
wai-quantum matrix-extension member / member · matrix extension, emulated (aarch64 Linux) (pull_request) Successful in 20s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 2m24s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 53m7s
wai-quantum signal processor / processor and member · signal processor, emulated (pull_request) Successful in 14m30s
wai-quantum portable tensor program / tensor program · CPU, mutants, GPU builds (pull_request) Successful in 29s
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Successful in 8m20s
wai-quantum CPU vector units / member · vector unit, rvv 1024-bit (riscv64, emulated) (pull_request) Successful in 21s
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Successful in 42s
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Successful in 38s
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Successful in 21s
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Successful in 20s
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Successful in 23s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 2m31s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Successful in 3m33s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 4m16s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 5m21s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m56s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 5m11s
jouleclaw fabrics / metal (macOS, Apple GPU) (pull_request) Successful in 53s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 5m57s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 12m24s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 17s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 39s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 3m51s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 2m11s
wai-pack / wai-pack · macos (pull_request) Successful in 58s
wai-quantum command line / command line · macos (pull_request) Successful in 27s
wai-quantum dataflow member / member · dataflow device absent (macos) (pull_request) Successful in 12s
wai-quantum FPGA member / member · FPGA datapath, simulated (macos) (pull_request) Successful in 1m4s
wai-quantum Metal member / member · Metal (macos) (pull_request) Successful in 24s
wai-quantum OpenCL member / member · OpenCL GPU (macos) (pull_request) Successful in 18s
wai-quantum neural-processor member / member · neural processor (macos) (pull_request) Successful in 5m30s
wai-quantum service / service · macos (pull_request) Successful in 36s
wai-quantum matrix-extension member / member · matrix extension absent (macos) (pull_request) Successful in 8s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 1m58s
wai-quantum portable tensor program / tensor program · Metal and the arm64 CPU (macos) (pull_request) Successful in 16s
wai-quantum CPU vector units / member · vector unit absent (macos) (pull_request) Successful in 11s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 2m16s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 1m31s
The 142 vulnerability instances this branch started from were not the result of
a decision. They accumulated because a vulnerable crate usually arrives
transitively, in a lockfile nobody edited, and nothing in CI was looking. The
fixes in this branch are worth little if the backlog can rebuild quietly.
`scripts/check-advisories.py` runs `cargo audit --json` over every tracked
Cargo.lock and fails on any advisory not named in its ALLOW table. It reads the
advisory database and the lockfiles; it builds nothing.
ALLOW is the substance. An advisory may be left in place — upstream has no
fixed release, the vulnerable call is unreachable, it is dev-only — but only in
writing, with the reason beside the id. Two rules keep that from rotting:
- an unlisted advisory fails, so a new one gets looked at;
- a listed advisory that no longer appears anywhere ALSO fails, so an excuse
cannot outlive the exposure it was written for. An allow list that is never
pruned stops describing the tree and starts hiding it.
It currently allows 18, each with its reason: zip and rsa as unreachable,
hpke-rs 0.2.0 as dev-only, and quick-xml/libcrux-kem/h2/hickory-proto/pyo3 as
pinned by dependents that need an upstream release. The quick-xml 0.37.5 entry
is marked as a known remaining exposure on the ISO 20022 ingestion path rather
than a dismissal, so it reads as debt and not as a verdict.
Warnings (unmaintained, unsound, yanked — 67 of them) are counted and printed
but do not fail the run: gating on maintenance advice would bury the reachable
defects in noise. Printing the count keeps the number visible instead of
unknown.
A lockfile that cannot be audited at all fails under its own heading, so an
error is never read as "clean" — the same failure-closed shape as
check-lockfiles.sh, and the mistake this sweep made by hand when a broken
output parser reported "0 vulnerabilities" while criticals were printing.
The self-test asserts the check passes a reviewed tree and fails each way it
should: an unlisted advisory, an allowance matching nothing, and a document
that does not parse.
Co-Authored-By: Claude Opus 5 <[email protected]>
228 lines
11 KiB
Python
Executable file
228 lines
11 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#
|
|
# check-advisories.py — no workspace carries a security advisory nobody has read.
|
|
#
|
|
# This tree holds many workspaces, and a vulnerable crate usually arrives
|
|
# transitively, in a lockfile nobody edited, for a reason nobody chose. Without
|
|
# a gate the backlog only grows, and it grew: a sweep in October 2026 found 142
|
|
# vulnerability instances across 47 lockfiles, among them a WASM runtime with a
|
|
# filesystem sandbox escape in the one component whose job is confinement.
|
|
# Nothing had failed, because nothing was looking.
|
|
#
|
|
# For every tracked Cargo.lock this runs
|
|
#
|
|
# cargo audit --file <lock> --json
|
|
#
|
|
# and fails on any advisory that is not in ALLOW below. It reads the advisory
|
|
# database and the lockfile; it builds nothing.
|
|
#
|
|
# ALLOW is the whole point. An advisory may be left in place — upstream has no
|
|
# fixed release, or the vulnerable call is unreachable here — but only in
|
|
# writing, with the reason next to the id. Two rules keep that honest:
|
|
#
|
|
# * an advisory that is not listed fails the run, so a new one has to be
|
|
# looked at rather than accumulated;
|
|
# * a listed advisory that no longer appears anywhere ALSO fails the run, so
|
|
# an entry cannot outlive the exposure it excused. An allow list that is
|
|
# never pruned stops describing the tree and starts hiding it.
|
|
#
|
|
# Warnings (unmaintained, unsound, yanked) are counted and printed but do not
|
|
# fail the run: they are advice about maintenance, not a reachable defect, and
|
|
# gating on them would bury the vulnerabilities in noise. They are printed so
|
|
# the number stays visible rather than unknown.
|
|
#
|
|
# check-advisories.py every tracked Cargo.lock, less SKIP
|
|
# check-advisories.py DIR ... exactly these directories
|
|
# check-advisories.py --from-json F... audit JSON already captured (self-test)
|
|
#
|
|
# Exit 0: every advisory found is allowed, and every allowed advisory was found.
|
|
# Exit 1: otherwise — including when a lockfile could not be audited at all,
|
|
# which is reported under its own heading so an error is never read as "clean".
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
# Advisories left in place on purpose. Each needs the reason, and each must
|
|
# still be reported by at least one lockfile or this check fails.
|
|
ALLOW = {
|
|
# ── Unreachable here ────────────────────────────────────────────────────
|
|
# All three are faults in `extract()`: SUID/SGID bits preserved from the
|
|
# archive, symlinks whose targets escape the destination, and permissions
|
|
# applied through those symlinks. Nothing in this tree extracts an archive
|
|
# — there is no `ZipArchive` or `ZipStreamReader` call site. zip arrives
|
|
# under candle-core and the wasm toolchain.
|
|
"RUSTSEC-2026-0367": "zip: extract-only; nothing here extracts an archive",
|
|
"RUSTSEC-2026-0368": "zip: extract-only; nothing here extracts an archive",
|
|
"RUSTSEC-2026-0369": "zip: extract-only; nothing here extracts an archive",
|
|
"RUSTSEC-2025-0168": "zip: extract-only path canonicalization; nothing here extracts",
|
|
# The Marvin attack recovers an RSA key from the timing of private-key
|
|
# decryption. There is no fixed release of `rsa` at all. It reaches
|
|
# sandbox-rs and wai-rs transitively and neither holds an RSA private key
|
|
# nor decrypts: openpay's unused direct dependency was removed outright.
|
|
"RUSTSEC-2023-0071": "rsa: no fixed release; no private-key decryption here",
|
|
# ── Not in a shipped artifact ──────────────────────────────────────────
|
|
# The production MLS path (joule-moq-mls) resolves the patched hpke-rs
|
|
# 0.7.0. These four are the old 0.2.0, reached only through a
|
|
# [dev-dependencies] edge of joule-code-collab, which still pins the
|
|
# openmls 0.6/0.3 stack. Test graph only; aligning it to the 0.9.1/0.6.0
|
|
# stack already used in-repo would remove them.
|
|
"RUSTSEC-2026-0069": "hpke-rs 0.2.0: dev-dependency only; production uses 0.7.0",
|
|
"RUSTSEC-2026-0070": "hpke-rs 0.2.0: dev-dependency only; production uses 0.7.0",
|
|
"RUSTSEC-2026-0071": "hpke-rs 0.2.0: dev-dependency only; production uses 0.7.0",
|
|
"RUSTSEC-2026-0072": "hpke-rs-rust-crypto 0.2.0: dev-dependency only",
|
|
# ── Pinned by a dependency; needs an upstream release ──────────────────
|
|
# hpke-rs-libcrux pins `=0.7.0`, which pins libcrux-kem 0.0.9. Both are
|
|
# panics on short or malformed hybrid keys — denial of service, not memory
|
|
# corruption. Fixed by an openmls/hpke-rs release, not from here.
|
|
"RUSTSEC-2026-0330": "libcrux-kem: pinned by hpke-rs-libcrux =0.7.0; upstream",
|
|
"RUSTSEC-2026-0331": "libcrux-kem: pinned by hpke-rs-libcrux =0.7.0; upstream",
|
|
# quick-xml is 0.41 everywhere the version is ours to choose. openpay also
|
|
# resolves 0.37.5 through open-payments-iso20022 1.0.10 (no later release
|
|
# exists), and smart-byte resolves 0.39.4 through plist -> netdev ->
|
|
# netwatch -> iroh. NOTE: the openpay path is the same ISO 20022 surface we
|
|
# harden directly, so this entry is a known remaining exposure on a payment
|
|
# ingestion path, not a dismissal. Drop it the moment upstream ships.
|
|
"RUSTSEC-2026-0194": "quick-xml 0.37.5/0.39.4: pinned by open-payments-iso20022 and iroh",
|
|
"RUSTSEC-2026-0195": "quick-xml 0.37.5/0.39.4: NsReader unused here; dependents pin the version",
|
|
# h2 0.3.27 under jouleclaw and 0.4.14 under smart-byte, both transitive;
|
|
# the fix is a major move for the dependent.
|
|
"RUSTSEC-2026-0258": "h2: transitive; fixed version is a major move for the dependent",
|
|
# hickory-proto 0.25.2 under smart-byte via iroh. -0118 has no fixed
|
|
# release; -0119 needs 0.26, which iroh does not take yet.
|
|
"RUSTSEC-2026-0118": "hickory-proto: no fixed release; transitive under iroh",
|
|
"RUSTSEC-2026-0119": "hickory-proto: 0.26 not taken by iroh yet",
|
|
# pyo3 0.28.3 under eoc-rs. Both need 0.29, a breaking move for the binding.
|
|
"RUSTSEC-2026-0176": "pyo3: 0.29 is a breaking move for the binding",
|
|
"RUSTSEC-2026-0177": "pyo3: 0.29 is a breaking move for the binding",
|
|
}
|
|
|
|
# Lockfiles cargo does not read: a vendored crate's own lock, kept with the
|
|
# vendored source. Each must still exist, or the entry is stale.
|
|
SKIP = (
|
|
"jouledesk/jouledesk-rs/vendor/libwebrtc",
|
|
"joule-code/joule-code-rs/vendor/wtransport",
|
|
)
|
|
|
|
|
|
def discover():
|
|
root = subprocess.run(
|
|
["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True
|
|
).stdout.strip()
|
|
if not root:
|
|
sys.exit("check-advisories: not in a git checkout.")
|
|
os.chdir(root)
|
|
for d in SKIP:
|
|
if not os.path.isfile(os.path.join(d, "Cargo.lock")):
|
|
sys.exit(
|
|
f"check-advisories: the skip list names {d}, which holds no "
|
|
"Cargo.lock. Remove the entry."
|
|
)
|
|
out = subprocess.run(["git", "ls-files"], capture_output=True, text=True).stdout
|
|
locks = [
|
|
p
|
|
for p in out.splitlines()
|
|
if p == "Cargo.lock" or p.endswith("/Cargo.lock")
|
|
if os.path.dirname(p) not in SKIP
|
|
]
|
|
return sorted(locks)
|
|
|
|
|
|
def audit(lock):
|
|
"""Return (parsed_json, error_text). Exactly one is None."""
|
|
try:
|
|
p = subprocess.run(
|
|
["cargo", "audit", "--file", lock, "--json", "--color", "never"],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=600,
|
|
)
|
|
except FileNotFoundError:
|
|
return None, "cargo is not on PATH"
|
|
except subprocess.TimeoutExpired:
|
|
return None, "cargo audit timed out"
|
|
# cargo audit exits non-zero when it finds something, so the exit status is
|
|
# not the signal; absence of parseable JSON is.
|
|
try:
|
|
return json.loads(p.stdout), None
|
|
except json.JSONDecodeError:
|
|
tail = (p.stderr or p.stdout or "").strip().splitlines()
|
|
return None, "; ".join(tail[-3:]) or "no JSON on stdout"
|
|
|
|
|
|
def main():
|
|
args = sys.argv[1:]
|
|
from_json = False
|
|
if args and args[0] == "--from-json":
|
|
from_json, args = True, args[1:]
|
|
|
|
if from_json:
|
|
sources = [(f, None) for f in args]
|
|
elif args:
|
|
sources = [(os.path.join(d, "Cargo.lock"), None) for d in args]
|
|
else:
|
|
sources = [(l, None) for l in discover()]
|
|
if not sources:
|
|
sys.exit("check-advisories: found no Cargo.lock to check.")
|
|
|
|
found = {} # id -> [(crate, version, lock)]
|
|
unchecked = [] # (lock, why)
|
|
warn_count = 0
|
|
|
|
for lock, _ in sources:
|
|
if from_json:
|
|
try:
|
|
with open(lock) as fh:
|
|
data, err = json.load(fh), None
|
|
except Exception as e: # noqa: BLE001
|
|
data, err = None, str(e)
|
|
else:
|
|
data, err = audit(lock)
|
|
if data is None:
|
|
unchecked.append((lock, err))
|
|
continue
|
|
for v in (data.get("vulnerabilities") or {}).get("list") or []:
|
|
adv, pkg = v.get("advisory") or {}, v.get("package") or {}
|
|
found.setdefault(adv.get("id", "?"), []).append(
|
|
(pkg.get("name", "?"), pkg.get("version", "?"), lock)
|
|
)
|
|
for kind, items in (data.get("warnings") or {}).items():
|
|
warn_count += len(items or [])
|
|
|
|
unexpected = {k: v for k, v in found.items() if k not in ALLOW}
|
|
stale = [k for k in ALLOW if k not in found]
|
|
|
|
if unexpected:
|
|
print("Advisories that are not in the allow list:\n")
|
|
for adv in sorted(unexpected):
|
|
for crate, ver, lock in unexpected[adv]:
|
|
print(f" {adv} {crate} {ver}")
|
|
print(f" {lock}")
|
|
print(
|
|
"\nFix each, or add it to ALLOW in scripts/check-advisories.py with the\n"
|
|
"reason it can be left — unreachable, dev-only, or pinned upstream."
|
|
)
|
|
if stale:
|
|
print("\nAllow-list entries that no longer match anything:\n")
|
|
for adv in sorted(stale):
|
|
print(f" {adv} — {ALLOW[adv]}")
|
|
print(
|
|
"\nThe exposure is gone. Remove the entry, so the list keeps describing\n"
|
|
"the tree instead of hiding it."
|
|
)
|
|
if unchecked:
|
|
print("\nCould not be audited (this is not a verdict on the lockfile):\n")
|
|
for lock, why in unchecked:
|
|
print(f" {lock}: {why}")
|
|
|
|
if unexpected or stale or unchecked:
|
|
return 1
|
|
print(
|
|
f"check-advisories: {len(sources)} lockfiles, "
|
|
f"{len(found)} allowed advisories, {warn_count} warnings (not gated)."
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|