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: 2 additions & 1 deletion docs/supply-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ signature that could not be checked or a signer never established: a key
taken from the signature itself, a key not held, a file not downloaded. A
key that no publisher states anywhere passes it only while
`tools/source-notes.tsv` records the routes that were tried
(`no-usable-key`); such a note for a key that is held fails it as stale. A
(`no-usable-key`); such a note for a key that is held fails it as stale,
while a signature the run could not fetch leaves its note untried. A
source that publishes no OpenPGP signature is not the gate's: the lock pins
it, and `tools/verify-provenance.sh --strict` checks whatever else its
publisher states.
Expand Down
11 changes: 11 additions & 0 deletions tools/tests/verify-signatures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,17 @@ write_manifest good
fresh_root; run --strict "--notes=${W}/notes-stale.tsv"
expect_fail "a no-usable-key note for a source whose key is held fails --strict" "stale note"

# A noted source whose signature could not be fetched this run: unverifiable,
# and the note untried, not stale.
printf 'fixture payload for unreached\n' > "${SRC}/unreached.tar.gz"
rm -f "${SRC}/unreached.tar.gz.sig" "${SRC}/.signatures/unreached.tar.gz.sig"
printf 'unreached no-usable-key https://example.invalid/ No route to the key was found. Checked 2026-09-28.\n' > "${W}/notes-unreached.tsv"
write_manifest good; add_row unreached sig
fresh_root; run --strict "--notes=${W}/notes-unreached.tsv"
expect_fail "a signature that could not be fetched fails --strict as unverifiable" "unverifiable"
if grep -q "stale note" "$OUT"; then red "a note for a signature that could not be fetched was called stale"; show
else green "a note for a signature that could not be fetched is untried, not stale"; fi

# A manifest row whose file was never downloaded.
write_manifest good notfetched
rm -f "${SRC}/notfetched.tar.gz"
Expand Down
21 changes: 18 additions & 3 deletions tools/verify-signatures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,17 @@ chmod 700 "$GNUPGHOME"

IMPORTED_MARK="${GNUPGHOME}/.kryptik-imported"

quiet_fetch() {
curl -fL --no-progress-meter --connect-timeout 20 \
--retry 2 --retry-delay 2 -o "$2" "$1"
# A host that throttles (freedesktop.org answers 418 to a busy runner, others
# 429 or 503) is asked again after a pause; a 404 is an answer.
quiet_fetch() { # quiet_fetch URL OUT
local code try
for try in 1 2 3; do
if code="$(curl -fsL --connect-timeout 20 --retry 2 --retry-delay 2 \
-o "$2" -w '%{http_code}' "$1" 2>/dev/null)"; then return 0; fi
case "$code" in 418|429|503) sleep $(( try * 5 )) ;; *) break ;; esac
done
rm -f "$2"
return 1
}

# --- keys ------------------------------------------------------------------
Expand Down Expand Up @@ -911,9 +919,16 @@ if [[ "$NOTED" -gt 0 ]]; then
printf ' - %s\n' "${NOTED_LIST[@]}"
fi
# A note that no unheld key needed: the key is held now, or the source went.
# A signature that could not be checked this run tried no note.
untried_this_run() { # untried_this_run NAME
local u
for u in "${UNVERIFIABLE_LIST[@]}"; do [[ "$u" == "$1 ("* ]] && return 0; done
return 1
}
STALE_NOTES=()
for n_pkg in "${!NOTED_NO_KEY[@]}"; do
[[ -n "${NOTE_USED[$n_pkg]:-}" ]] && continue
untried_this_run "$n_pkg" && continue
[[ -n "${SEEN_SOURCE[$n_pkg]:-}" ]] && STALE_NOTES+=("$n_pkg")
done
if [[ "${#STALE_NOTES[@]}" -gt 0 ]]; then
Expand Down
Loading