Require strict ISO 8601 in the age backstop; document the no-stable-release stance

GNU date accepts relative expressions like '2 days ago', which would let a
buggy hook fabricate a release age; the backstop now insists on an ISO 8601
timestamp before date parses it. The provider header now states, rather than
contradicts, the code's behavior for a feed with no stable releases: that is
a loud failure by design, while quarantined releases report no update.
This commit is contained in:
Ryan Hughes
2026-08-24 20:32:20 -04:00
parent fd03757f22
commit 92735d5539
2 changed files with 15 additions and 3 deletions
+11 -1
View File
@@ -171,7 +171,11 @@ release_age_status() {
[[ "${BYPASS_MIN_RELEASE_AGE:-}" != "1" ]] || return 0
local published_at published_epoch
published_at=$(jq -r '.published_at // empty' <<<"$release")
if [[ -z "$published_at" ]] || ! published_epoch=$(date --date="$published_at" +%s 2>/dev/null); then
# Strict ISO 8601 before GNU date sees it: date also accepts relative
# expressions like "2 days ago", which would let a buggy hook fabricate an
# age instead of failing closed.
if [[ ! "$published_at" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[+-][0-9]{2}:?[0-9]{2})$ ]] \
|| ! published_epoch=$(date --date="$published_at" +%s 2>/dev/null); then
return 2
fi
(( $(date +%s) - published_epoch >= min_age )) || return 1
@@ -573,6 +577,12 @@ EOF
rel=$(jq -n '{pkgver: "2.0.0", sha256sums: {}}')
st=0; release_age_status "$rel" 86400 || st=$?
check "missing published_at is unprovable" "2" "$st"
rel=$(jq -n '{pkgver: "2.0.0", published_at: "2 days ago", sha256sums: {}}')
st=0; release_age_status "$rel" 86400 || st=$?
check "relative-date expression is unprovable, not an age" "2" "$st"
rel=$(jq -n --arg p "$(date -u -d '2 days ago' +%Y-%m-%dT%H:%M:%S+00:00)" '{pkgver: "1.9.0", published_at: $p, sha256sums: {}}')
st=0; release_age_status "$rel" 86400 || st=$?
check "numeric-offset ISO timestamp passes" "0" "$st"
echo "Duration parser:"
local agepkg="$TEMP_DIR/selftest-age"
+4 -2
View File
@@ -29,8 +29,10 @@ package_upstream_github_repo() {
# Fetches sit behind functions so the self-test can replace them with fixture
# readers; everything below the fetch is deterministic and testable offline.
# Only the 100 most recent releases are considered -- a bounded search, not
# pagination. A feed whose entire first page is drafts, prereleases, or
# quarantined releases reports no update and waits for the next run.
# pagination. Quarantined releases report no update and wait for the next
# run; a page with no stable release at all (drafts and prereleases only)
# fails the sync instead, because a provider-tracked feed suddenly shipping
# nothing stable is an anomaly worth a loud error, not a silent skip.
github_fetch_releases() {
local repo="$1"
curl -fsSL "https://api.github.com/repos/$repo/releases?per_page=100"