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
10 changes: 10 additions & 0 deletions .github/workflows/governance-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ jobs:
|| git clone --depth 200 https://github.com/hyperpolymath/standards.git "$HOME/standards"

- name: Run staleness check
env:
# The gate confirms a pin server-side before ever accusing it of being
# forged, and before accepting a deny-list negative from a clone it
# cannot trust. Unauthenticated that is 60 requests/hour per runner IP;
# exhausting it makes the gate degrade to ::warning (loudly — it never
# silently passes, see check-workflow-staleness.sh). A token removes
# the cliff entirely. Note this only reaches a consumer once it re-pins
# past this commit: the script comes from standards HEAD, but the job
# definition comes from whatever SHA the consumer pins.
GITHUB_TOKEN: ${{ github.token }}
run: |
bash "$HOME/standards/scripts/check-workflow-staleness.sh" .

Expand Down
20 changes: 20 additions & 0 deletions docs/decisions/ADR-003-workflow-pin-staleness-window.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,23 @@ Cost is zero API calls on the happy path — the server is consulted only when
the gate is about to accuse. The deny-list gets the same fallback, so a
degraded runner cannot silently skip it: "could not check" is reported as
SKIPPED, never as passed.

==== Rate limiting, and why it is not a hole

The staleness job is given no token, so the compare call is unauthenticated: 60
requests/hour per runner IP. Exhausting it makes the gate emit `::warning` and
pass — verified by accident while testing, which is exactly the intended
fail-open, and it is *loud*: two warnings naming the pin, never a silent pass.

Three things keep this from mattering:

. The common path never calls the API. A pin that is genuinely deny-listed is
caught by a local *positive*, which is trusted without the network — so the
294 `d7c22711` denials do not depend on it at all.
. The API is consulted only when the gate is about to accuse, or when a
negative comes from a clone it cannot trust.
. `governance-reusable.yml` now passes `GITHUB_TOKEN: ${{ github.token }}` to
the step, which removes the limit. This reaches a consumer only once it
re-pins past that commit — the script is read from `standards` HEAD, but the
job definition comes from the pinned SHA — so it lands naturally with the
propagation that these deny-listed pins need anyway.
22 changes: 18 additions & 4 deletions scripts/check-workflow-staleness.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@
# server-side integrity check (default
# hyperpolymath/standards).
# STALENESS_STANDARDS_BRANCH— branch a pin must be reachable from (default main).
# STALENESS_API_BASE — API root for the server-side integrity check
# (default https://api.github.com; set for GHES,
# or to an unreachable host in tests).
# STALENESS_KNOWN_BAD_BEFORE— override the known-bad deny-list, as space- or
# comma-separated <reusable>:<fix-sha> entries.
# "-" disables it. For the hermetic fixture tests
Expand All @@ -82,6 +85,7 @@
# below). Overridable so a fork can point the gate at its own standards.
STANDARDS_NWO="${STALENESS_STANDARDS_NWO:-hyperpolymath/standards}"
STANDARDS_BRANCH="${STALENESS_STANDARDS_BRANCH:-main}"
STANDARDS_API="${STALENESS_API_BASE:-https://api.github.com}"

# ── Known-bad pins: hard fail regardless of age ─────────────────────────────
# A recency *window* is only a proxy for "this pin might contain a known
Expand Down Expand Up @@ -213,8 +217,12 @@
clone_is_complete() {
[ "$HAVE_HISTORY" = true ] || return 1
local gd
gd=$(git -C "$STANDARDS_DIR" rev-parse --git-dir 2>/dev/null) || return 1
[ -e "$STANDARDS_DIR/$gd/shallow" ] || [ -e "$gd/shallow" ] && return 1
# --absolute-git-dir, not --git-dir: the latter returns a path relative to the
# CWD, which here is the *consumer's* checkout. actions/checkout is shallow by
# default, so a relative ".git/shallow" test would see the consumer's marker
# and call every standards clone incomplete.
gd=$(git -C "$STANDARDS_DIR" rev-parse --absolute-git-dir 2>/dev/null) || return 1
[ -e "$gd/shallow" ] && return 1

Check failure on line 225 in scripts/check-workflow-staleness.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AZ-FnXSxZhtVMTUNzL8h&open=AZ-FnXSxZhtVMTUNzL8h&pullRequest=524
[ -n "$(git -C "$STANDARDS_DIR" config --get remote.origin.partialclonefilter 2>/dev/null)" ] && return 1
[ "$(git -C "$STANDARDS_DIR" config --get remote.origin.promisor 2>/dev/null)" = "true" ] && return 1
return 0
Expand All @@ -235,7 +243,7 @@
api_compare() {
local pin="$1" head="${2:-$STANDARDS_BRANCH}" url body code st ahead behind
command -v curl >/dev/null 2>&1 || return 1
url="https://api.github.com/repos/${STANDARDS_NWO}/compare/${pin}...${head}"
url="${STANDARDS_API}/repos/${STANDARDS_NWO}/compare/${pin}...${head}"
local -a auth=()
[ -n "${GITHUB_TOKEN:-}" ] && auth=(-H "Authorization: Bearer ${GITHUB_TOKEN}")
body=$(curl -sS --max-time 20 -w '\n%{http_code}' \
Expand Down Expand Up @@ -298,7 +306,13 @@
KNOWN_BAD_FIX="$fix_full"
return 0
fi
continue
# Local says "not affected". A local POSITIVE is trustworthy (no false
# positives observed), but a local NEGATIVE here is a FALSE GREEN — the
# exact failure this list exists to prevent — and the same
# `merge-base --is-ancestor` call is measured unreliable on the partial
# clone CI actually uses. Trust the negative only from a complete clone;
# otherwise fall through and confirm with the server.
clone_is_complete && continue
fi

# Degraded clone: ask the server rather than skip. A check that quietly
Expand Down
50 changes: 49 additions & 1 deletion scripts/tests/check-workflow-staleness-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@
bash "$CHECK_SCRIPT" "$repo" 2>&1)
rc=$?
set -e
if [ "$rc" -eq "$expected" ] && printf '%s' "$out" | grep -qE "$regex"; then
# A leading '!' inverts the match: assert the gate did NOT say this.
local want=0
case "$regex" in '!'*) want=1; regex="${regex#!}" ;; esac

Check failure on line 96 in scripts/tests/check-workflow-staleness-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a default case (*) to handle unexpected values.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AZ-FnXXOZhtVMTUNzL8i&open=AZ-FnXXOZhtVMTUNzL8i&pullRequest=524
local got=0
printf '%s' "$out" | grep -qE "$regex" || got=1
if [ "$rc" -eq "$expected" ] && [ "$got" -eq "$want" ]; then

Check failure on line 99 in scripts/tests/check-workflow-staleness-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AZ-FnXXOZhtVMTUNzL8k&open=AZ-FnXXOZhtVMTUNzL8k&pullRequest=524

Check failure on line 99 in scripts/tests/check-workflow-staleness-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AZ-FnXXOZhtVMTUNzL8j&open=AZ-FnXXOZhtVMTUNzL8j&pullRequest=524
echo "PASS: $desc"
PASS=$((PASS + 1))
else
Expand Down Expand Up @@ -214,6 +219,49 @@
write_pin "$R/.github/workflows/scorecard.yml" "scorecard-reusable.yml" "$C4"
run_case "clean multi-reusable repo passes" 0 "$R"

# ── 13. THE PRODUCTION PATH: partial clone, where a local negative is not
# trustworthy ──────────────────────────────────────────────────────────
# In CI the governance reusable clones standards with `--filter=tree:0`, so the
# checkout is ALWAYS partial — and `merge-base --is-ancestor` is measured
# unreliable there (awesome-haskell, four consecutive runs). Cases 1-12 all run
# against a complete `git init` fixture, so none of them exercise the code that
# actually protects production. Mark the fixture partial to reach it.
#
# The API is pointed at a closed port so these stay hermetic and fast: the
# question is what the gate does when it can neither trust the clone nor reach
# the server.
UNREACHABLE="http://127.0.0.1:1"
git -C "$FIX" config remote.origin.promisor true

# 13a. A local POSITIVE is still trusted — a deny must not need the network.
R="$TEST_DIR/partial-deny"; mk_repo "$R"
write_pin "$R/.github/workflows/governance.yml" "governance-reusable.yml" "$C1"
run_case_out "partial clone: local positive still denies without the network" 1 \
'predates' "$R" \
STALENESS_WINDOW_COMMITS=9999 STALENESS_WINDOW_DAYS=9999 \
STALENESS_API_BASE="$UNREACHABLE" \
STALENESS_KNOWN_BAD_BEFORE="governance-reusable.yml:$C3"

# 13b. A local NEGATIVE must NOT be silently accepted. Unverifiable is reported
# as SKIPPED — "could not check" must never read as "passed".
R="$TEST_DIR/partial-unchecked"; mk_repo "$R"
write_pin "$R/.github/workflows/governance.yml" "governance-reusable.yml" "$C4"
run_case_out "partial clone: unverifiable deny-list check is reported SKIPPED, not passed" 0 \
'SKIPPED, not passed' "$R" \
STALENESS_WINDOW_COMMITS=9999 STALENESS_WINDOW_DAYS=9999 \
STALENESS_API_BASE="$UNREACHABLE" \
STALENESS_KNOWN_BAD_BEFORE="governance-reusable.yml:$C3"

# 13c. A complete clone's negative IS trusted — no warning, no network.
git -C "$FIX" config --unset remote.origin.promisor
R="$TEST_DIR/complete-quiet"; mk_repo "$R"
write_pin "$R/.github/workflows/governance.yml" "governance-reusable.yml" "$C4"
run_case_out "complete clone: local negative is trusted silently (no SKIPPED warning)" 0 \
'!SKIPPED' "$R" \
STALENESS_WINDOW_COMMITS=9999 STALENESS_WINDOW_DAYS=9999 \
STALENESS_API_BASE="$UNREACHABLE" \
STALENESS_KNOWN_BAD_BEFORE="governance-reusable.yml:$C3"

echo "----------------------------------------"
echo "$PASS/$TOTAL test cases passed."
[ "$PASS" -eq "$TOTAL" ] || { echo "Some regression tests FAILED."; exit 1; }
Expand Down
Loading