Some checks failed
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 9s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 6s
jouleclaw fabrics / cuda (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 17s
jouleclaw fabrics / vulkan (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 17s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 4m9s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 5m46s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 39s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Failing after 10m26s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Failing after 10m44s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 9m33s
standards workspaces build + test / eoc-rs (eoc/eoc-rs, -p eoc-meter --features cuda, eoc-rs, linux) (pull_request) Successful in 3m13s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 3m37s
jouleclaw fabrics / metal (macOS, Apple GPU) (pull_request) Successful in 1m51s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 2m5s
standards workspaces build + test / smart-byte-rs (smart-byte/smart-byte-rs, smart-byte-rs, linux) (pull_request) Successful in 4m10s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 7m43s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 7m49s
standards workspaces build + test / openpay (openpay, openpay, linux-arm64) (pull_request) Successful in 5m48s
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been cancelled
The advisory gate from #443 found something on its first real run, two days after landing: RUSTSEC-2026-0385 and -0387, both published 2026-10-23, neither of which existed when the sweep was done. That is the gate doing the job it was added for, so this is the answer rather than a new allowance. ctutils 0.4.2/0.4.3 -> 0.4.4 RUSTSEC-2026-0385, non-constant-time mask generation and `u128` comparisons. Reaches openpay's wallet crates through digest -> hmac/hkdf, so it sits under HMAC on payment paths. Fixed in four lockfiles. crypto-bigint 0.7.3 -> 0.7.6 RUSTSEC-2026-0387, secret-dependent branches in subtraction, zero counting, division and Montgomery multiplication — operations whose whole purpose is to be constant-time. The 0.5.5 line of crypto-bigint cannot follow: elliptic-curve 0.13 pins it, so reaching >=0.7.6 means moving the p256/elliptic-curve stack across openpay, sandbox, wai and jouleclaw. That is an upgrade, not a bump. It is allowed with that reason recorded, on paths that sign and derive keys, so it reads as debt. Writing that allowance exposed a weakness in the check itself: ALLOW was keyed by advisory id alone, so an entry excusing the stuck 0.5.5 line would have gone on excusing the advisory if a patched 0.7.x copy later regressed to an unpatched release — the same shape as sealing by name instead of by digest. An entry may now pin the exact crate/version pairs it covers: "RUSTSEC-...": {"why": "...", "only": ("crypto-bigint 0.5.5",)} Any other version of that crate fails, and a named pair that disappears makes the entry stale, so the pin cannot keep describing a tree that moved. The version-blind string form is kept for reasons that genuinely do not depend on the version, such as a call we never make, and the two forms are documented against each other. The self-test now builds its documents by importing the check's real ALLOW rather than scraping it with grep, so the two cannot drift, and covers the two new failure modes: a version-pinned allowance offered a version it does not name, and one whose named pair is gone. Also moves off every yanked dependency, each a patch bump: chacha20 0.10.0/0.10.1 -> 0.10.2 (a cipher, in six lockfiles), der 0.8.0 -> 0.8.2, spin 0.9.8 -> 0.9.9 and 0.10.0 -> 0.10.1, iroh-blobs 0.101.0 -> 0.101.1. A yank is the author withdrawing a release; none of these needed to stay on one. No yanked dependency remains anywhere in the tree. All 47 lockfiles current; the six affected workspaces check clean and `mls_keying` passes. Co-Authored-By: Claude Opus 5 <[email protected]>
103 lines
4.9 KiB
Shell
Executable file
103 lines
4.9 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# check-advisories-selftest.sh — check-advisories.py can fail.
|
|
#
|
|
# A check that cannot fail is decoration. This feeds the check pre-captured
|
|
# `cargo audit --json` documents through `--from-json` and asserts that it
|
|
# 1. passes when every advisory found is allowed and every allowance is used;
|
|
# 2. fails when an advisory is reported that the allow list does not name
|
|
# (the case this check exists for);
|
|
# 3. fails when an allow-list entry matches nothing, so an excuse cannot
|
|
# outlive the exposure it was written for;
|
|
# 4. fails when a version-pinned allowance is offered a version it does not
|
|
# name — the whole point of pinning it;
|
|
# 5. fails when a version-pinned allowance names a pair that is gone, so the
|
|
# pin cannot keep describing a tree that moved;
|
|
# 6. fails, and says it could not audit, when the document does not parse:
|
|
# an error is never read as "clean".
|
|
#
|
|
# The documents are generated from the check's own ALLOW table, imported rather
|
|
# than scraped, so the two cannot drift apart. Nothing here runs cargo or reads
|
|
# the advisory database, so it needs no network and no lockfile.
|
|
set -uo pipefail
|
|
|
|
here=$(cd "$(dirname "$0")" && pwd)
|
|
check="$here/check-advisories.py"
|
|
tmp=$(mktemp -d)
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
|
|
# gen <out> [mutation] — a cargo-audit document built from the real ALLOW.
|
|
# (none) every allowance, each at a version it covers
|
|
# extra plus an advisory nobody allows
|
|
# drop-one one allowance missing, so it matches nothing
|
|
# wrong-ver a version-pinned allowance at a version it does not name
|
|
# drop-pinned a version-pinned allowance's named pair removed
|
|
gen() {
|
|
python3 -I - "$check" "$1" "${2:-none}" <<'PY'
|
|
import importlib.util, json, sys
|
|
path, out, mode = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
spec = importlib.util.spec_from_file_location("chk", path)
|
|
m = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(m) # __name__ != "__main__", so main() stays put
|
|
|
|
rows = []
|
|
for adv, entry in m.ALLOW.items():
|
|
if isinstance(entry, str):
|
|
rows.append((adv, "c", "1.0.0"))
|
|
else:
|
|
for pair in entry["only"]:
|
|
crate, ver = pair.rsplit(" ", 1)
|
|
rows.append((adv, crate, ver))
|
|
|
|
pinned = [a for a, e in m.ALLOW.items() if not isinstance(e, str)]
|
|
if mode == "extra":
|
|
rows.append(("RUSTSEC-2099-0001", "c", "1.0.0"))
|
|
elif mode == "drop-one":
|
|
rows.pop(0)
|
|
elif mode == "wrong-ver":
|
|
assert pinned, "no version-pinned allowance to test"
|
|
rows = [(a, c, ("9.9.9" if a == pinned[0] else v)) for a, c, v in rows]
|
|
elif mode == "drop-pinned":
|
|
assert pinned, "no version-pinned allowance to test"
|
|
rows = [(a, c, v) for a, c, v in rows if a != pinned[0]]
|
|
rows.append((pinned[0], "c", "1.0.0")) # present, but not at a named pair
|
|
|
|
doc = {"vulnerabilities": {"found": bool(rows), "count": len(rows), "list": [
|
|
{"advisory": {"id": a, "title": "t"}, "package": {"name": c, "version": v}}
|
|
for a, c, v in rows]}, "warnings": {}}
|
|
open(out, "w").write(json.dumps(doc))
|
|
PY
|
|
}
|
|
|
|
fails=0
|
|
expect() { # expect <pass|fail> <what> <needle> <file>
|
|
local want=$1 what=$2 needle=$3 file=$4 out rc
|
|
out=$(python3 -I "$check" --from-json "$file" 2>&1); rc=$?
|
|
if [ "$want" = pass ] && [ $rc -ne 0 ]; then
|
|
echo "FAIL $what: the check failed"; printf '%s\n' "$out" | sed 's/^/ /'; fails=$((fails+1)); return
|
|
fi
|
|
if [ "$want" = fail ] && [ $rc -eq 0 ]; then
|
|
echo "FAIL $what: the check passed"; printf '%s\n' "$out" | sed 's/^/ /'; fails=$((fails+1)); return
|
|
fi
|
|
if [ -n "$needle" ] && ! grep -q -- "$needle" <<<"$out"; then
|
|
echo "FAIL $what: the output does not say '$needle'"; printf '%s\n' "$out" | sed 's/^/ /'; fails=$((fails+1)); return
|
|
fi
|
|
echo "ok $what"
|
|
}
|
|
|
|
gen "$tmp/all.json" || exit 2
|
|
gen "$tmp/extra.json" extra || exit 2
|
|
gen "$tmp/short.json" drop-one || exit 2
|
|
gen "$tmp/wrongver.json" wrong-ver || exit 2
|
|
gen "$tmp/dropped.json" drop-pinned || exit 2
|
|
printf 'this is not json\n' > "$tmp/broken.json"
|
|
|
|
expect pass "every advisory allowed and every allowance used passes" "" "$tmp/all.json"
|
|
expect fail "an advisory the allow list does not name fails" "not in the allow list" "$tmp/extra.json"
|
|
expect fail "an allowance that matches nothing fails" "no longer match anything" "$tmp/short.json"
|
|
expect fail "a version-pinned allowance at an unnamed version fails" "not in the allow list" "$tmp/wrongver.json"
|
|
expect fail "a version-pinned allowance whose named pair is gone fails" "no longer" "$tmp/dropped.json"
|
|
expect fail "a document that does not parse fails as unaudited, not as clean" "Could not be audited" "$tmp/broken.json"
|
|
|
|
if [ $fails -ne 0 ]; then echo "check-advisories-selftest: $fails failure(s)"; exit 1; fi
|
|
echo "check-advisories-selftest: the check passes a reviewed tree and fails each way it should."
|