Skip to content

fix(actions-lock): support the v0.0.3 per-dependency hostname field - #938

Merged
hyperpolymath merged 1 commit into
mainfrom
claude/gallant-ramanujan-27wzn9
Oct 20, 2026
Merged

hyperpolymath merged 1 commit into
mainfrom
claude/gallant-ramanujan-27wzn9

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Problem

#934 added v0.0.3 to ActionsLock's version allow-list, but changed nothing else. v0.0.3 adds one field: an optional, single-quoted hostname: on each dependency (source: github/actions-lockfile schema/lockfile-v0.0.3.json and its README). The line parser rejects that line as :malformed_dependency_entry. Any v0.0.3 lock that carries a hostname therefore fails closed as invalid_actions_lock, in both workflow_audit and workflow_hardening.

The only change from v0.0.2 to v0.0.3 is hostname (confirmed by diffing the two schemas). Its rules for github.com:

  • omitted → bound to the home host (github.com on github.com);
  • github.com → allowed;
  • anything else → rejected on github.com. The syntax allows only github.com or a lowercase <tenant>.ghe.com.

Change

lib/rules/actions_lock.ex, the single parser both rule modules call:

Input Result
no hostname line accepted; dependency records hostname: "github.com" (v0.0.2 too, as the reference parser does in memory)
hostname: 'github.com' accepted; workflow association unchanged
hostname: 'octocorp.ghe.com' {:unsupported_dependency_hostname, h}. Fails closed because owner_id/repo_id are host-scoped and the owner/repo@ref association keys carry no host
non-canonical (GitHub.com, https://…, :443, '', a.b.ghe.com, …), unquoted or double-quoted :malformed_dependency_hostname
duplicate hostname {:duplicate_dependency_field, _, :hostname}
hostname in a v0.0.2 lock {:hostname_not_in_version, "v0.0.2"}
version: 'v0.0.4' / v0.1.0 / v1.0.0 still {:unsupported_version, v}

Tests:

  • New fixture test/fixtures/actions_lock/standards-b3ce78e-v0.0.3.lock: a byte-for-byte copy of hyperpolymath/standards' .github/workflows/actions.lock at b3ce78e (sha256 d61f6436…). It is licensed through a new .reuse/dep5 entry that also says not to hand-edit it.
  • 9 parser tests, plus one caller-level test each in workflow_audit_test.exs and workflow_hardening_test.exs.

Verification

Run locally with Elixir 1.17.3 / OTP 25. hex.pm is blocked in the session, so deps were built from their GitHub tags at the mix.lock versions.

  • mix test: 1837 tests, 0 failures, 242 excluded (:verisim_data, as usual). Baseline on main was 1826 / 0.
  • Against main's parser, 10 of the 11 new tests fail. The one that passes is the regression guard for later versions.
  • mix format --check-formatted: clean for all touched files.
  • Scanned hyperpolymath/standards at b3ce78e with HYPATIA_FORMAT=json ./hypatia-cli.sh scan, then replayed the validate-hypatia-baseline step locally (path relativisation plus standards' own apply-baseline.sh, BLOCKING_THRESHOLD=info):
    • Hypatia 51ab649: gate fails, and the only kept findings are the two invalid_actions_lock … {:unsupported_version, "v0.0.3"}. This reproduces run 39340237981.
    • This branch: gate exit 0. Both invalid_actions_lock findings are gone, and nothing else changed.

Note for the standards side

standards' governance gate does not build Hypatia HEAD. governance-reusable.yml defaults hypatia-ref to the vetted SHA 51ab6496… (2026-10-01), which predates #934. So this PR alone does not turn standards green. The hypatia-ref default (and hypatia-scan-reusable.yml's, which it is kept in step with) has to move to a commit that includes v0.0.3 support. That should be this PR's merge commit, or main if this PR is not taken.


Generated by Claude Code

#934 added v0.0.3 to the version allow-list, but the line parser still
rejected the one field v0.0.3 introduces (github/actions-lockfile
schema/lockfile-v0.0.3.json): an optional, single-quoted `hostname:` on
each dependency. Any producer-written hostname therefore failed closed
as :malformed_dependency_entry.

Parse it the way the format specifies for github.com:
- omitted -> the home host; every dependency now records `hostname`,
  defaulting to "github.com" (v0.0.2 included, as the reference parser
  does in memory);
- 'github.com' -> accepted;
- a well-formed '*.ghe.com' tenant -> {:unsupported_dependency_hostname, h}.
  owner_id/repo_id are host-scoped and the owner/repo@ref association
  keys carry no host, so a tenant pin cannot be validated here;
- non-canonical, unquoted, empty, or duplicate -> malformed / duplicate;
- `hostname` in a v0.0.2 lock -> {:hostname_not_in_version, "v0.0.2"}.
Versions after v0.0.3 still fail closed with :unsupported_version.

Adds a verbatim fixture of hyperpolymath/standards'
.github/workflows/actions.lock at b3ce78e (sha256 d61f6436...), covered
by a new .reuse/dep5 entry, plus caller-level tests for workflow_audit
and workflow_hardening.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KUZiEB43T3E9fJAXqwbSDm
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 651904ea-8844-408c-8b6c-55414895c6b8
📥 Commits

Reviewing files that changed from the base of the PR and between 25758e6 and 464fe1e.

⛔ Files ignored due to path filters: 1
  • test/fixtures/actions_lock/standards-b3ce78e-v0.0.3.lock is excluded by !**/*.lock
📒 Files selected for processing: 5
  • .reuse/dep5
  • lib/rules/actions_lock.ex
  • test/rules/actions_lock_test.exs
  • test/workflow_audit_test.exs
  • test/workflow_hardening_test.exs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout: 55
  • GitHub Check: Dogfooding compliance summary
  • GitHub Check: Escript packaging soundness
  • GitHub Check: GitGuardian Security Checks
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: governance / JS runtimes — deno
  • GitHub Check: governance / Code quality + docs
  • GitHub Check: governance / Debt ratchet
  • GitHub Check: governance / JS runtimes — bun
  • GitHub Check: governance / Licence consistency
  • GitHub Check: governance / Actions lockfile verify
  • GitHub Check: governance / Trusted-base reduction policy
  • GitHub Check: governance / JS runtimes — pnpm
  • GitHub Check: governance / Guix packaging policy (Nix retired)
  • GitHub Check: governance / Security policy checks
  • GitHub Check: governance / Workflow security linter
  • GitHub Check: governance / Well-Known (RFC 9116 + RSR)
  • GitHub Check: governance / UUID v8 conformance
  • GitHub Check: governance / Allowlist Preflight
  • GitHub Check: governance / Check Workflow Staleness
  • GitHub Check: governance / Language / package anti-pattern policy
  • GitHub Check: governance / Exemption ratchet
  • GitHub Check: governance / JS runtimes — environment provisions Bun first
  • GitHub Check: governance / Live Actions policy (credentialed advisory)
  • GitHub Check: scan / gitleaks
  • GitHub Check: governance / UUID v7 conformance
  • GitHub Check: scan / rust-secrets
  • GitHub Check: scan / shell-secrets
  • GitHub Check: hypatia / Hypatia Neurosymbolic Analysis
  • GitHub Check: criterion + baseline gate
  • GitHub Check: Estate rules and sweep structure
  • GitHub Check: stress-test
  • GitHub Check: E2E — Rust CLI Scan
  • GitHub Check: k9iser manifest + build
  • GitHub Check: E2E — Elixir Scanner Pipeline
  • GitHub Check: zig build test (FFI + wire contract)
  • GitHub Check: Build Test Images
  • GitHub Check: Rust Check & Clippy
  • GitHub Check: Rust Format
  • GitHub Check: Rust Dependency Audit
  • GitHub Check: CodeQL Analysis (actions)
  • GitHub Check: Secret Detection (Gitleaks)
  • GitHub Check: Generate SBOM
  • GitHub Check: Container Security (Trivy) (deploy/Containerfile)
  • GitHub Check: Cargo check + clippy + fmt
  • GitHub Check: Clippy
  • GitHub Check: Check
  • GitHub Check: Format
  • GitHub Check: Secret Detection (TruffleHog)
  • GitHub Check: abi-codegen-drift
  • GitHub Check: Test
  • GitHub Check: Validate Documentation
  • GitHub Check: Build AsciiDoc
  • GitHub Check: analyze (javascript-typescript, none)
  • GitHub Check: analyze (actions, none)
  • GitHub Check: Build AsciiDoc
⚠️ CI failures not shown inline: 15

GitHub Actions: Governance / 0_governance _ Validate Hypatia Baseline.txt: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1mif [ ! -d "$HOME/hypatia" ]; then�[0m
 �[36;1m  git init "$HOME/hypatia"�[0m
 �[36;1m  git -C "$HOME/hypatia" remote add origin https://github.com/hyperpolymath/hypatia.git�[0m
 �[36;1m  git -C "$HOME/hypatia" fetch --depth 1 origin "$HYPATIA_SHA"�[0m
 �[36;1m  git -C "$HOME/hypatia" checkout --detach FETCH_HEAD�[0m
 �[36;1mfi�[0m
 �[36;1mACTUAL_SHA=$(git -C "$HOME/hypatia" rev-parse HEAD)�[0m
 �[36;1mif [ "$ACTUAL_SHA" != "$HYPATIA_SHA" ]; then�[0m
 �[36;1m  echo "::error::Hypatia cached source does not match the resolved commit"�[0m
...
 ##[group]Run cd "$HOME/hypatia"
 �[36;1mcd "$HOME/hypatia"�[0m
 �[36;1mif [ ! -x hypatia ]; then�[0m
 �[36;1m  if ! (mix deps.get && mix escript.build); then�[0m
 �[36;1m    echo "::error::Hypatia scanner build failed at commit $(git rev-parse HEAD) — see upstream hyperpolymath/hypatia"�[0m
 �[36;1m    exit 1�[0m
 �[36;1m  fi�[0m
 �[36;1mfi�[0m
 �[36;1mif [ ! -x hypatia ]; then�[0m
 �[36;1m  echo "::error::Hypatia scanner binary is missing after build"�[0m
...
 ##[group]Run echo "Scanning repository: hyperpolymath/hypatia (checking baseline)"
...
 �[36;1m# scan's own exit code…�[0m
 �[36;1mHYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . > hypatia-findings.raw.json || true�[0m
 �[36;1m# …but never swallow a genuine scanner crash into a false pass: require a�[0m
 �[36;1m# valid JSON array before trusting the output as "the findings".�[0m
 �[36;1mif ! jq -e 'type == "array"' hypatia-findings.raw.json >/dev/null 2>&1; then�[0m
 �[36;1m  echo "::error::hypatia scan did not produce a valid JSON findings array (scanner error, not a baseline result)"�[0m
...
       "rule_module": "research_extensions",
       "severity": "medium",
       "baseline_status": "new"
     },
     {
 ##[error]Gate failed: 30 unfiltered finding(s) at or above 'info' (30 kept in total).
...
       "baseline_note": "Ingested from OSSF Scorecard's Pinned-Dependencies check, which insp...

GitHub Actions: Governance / governance _ Validate Hypatia Baseline: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1mif [ ! -d "$HOME/hypatia" ]; then�[0m
 �[36;1m  git init "$HOME/hypatia"�[0m
 �[36;1m  git -C "$HOME/hypatia" remote add origin https://github.com/hyperpolymath/hypatia.git�[0m
 �[36;1m  git -C "$HOME/hypatia" fetch --depth 1 origin "$HYPATIA_SHA"�[0m
 �[36;1m  git -C "$HOME/hypatia" checkout --detach FETCH_HEAD�[0m
 �[36;1mfi�[0m
 �[36;1mACTUAL_SHA=$(git -C "$HOME/hypatia" rev-parse HEAD)�[0m
 �[36;1mif [ "$ACTUAL_SHA" != "$HYPATIA_SHA" ]; then�[0m
 �[36;1m  echo "::error::Hypatia cached source does not match the resolved commit"�[0m

GitHub Actions: Governance / governance _ Validate Hypatia Baseline: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run cd "$HOME/hypatia"
 �[36;1mcd "$HOME/hypatia"�[0m
 �[36;1mif [ ! -x hypatia ]; then�[0m
 �[36;1m  if ! (mix deps.get && mix escript.build); then�[0m
 �[36;1m    echo "::error::Hypatia scanner build failed at commit $(git rev-parse HEAD) — see upstream hyperpolymath/hypatia"�[0m
 �[36;1m    exit 1�[0m
 �[36;1m  fi�[0m
 �[36;1mfi�[0m
 �[36;1mif [ ! -x hypatia ]; then�[0m
 �[36;1m  echo "::error::Hypatia scanner binary is missing after build"�[0m

GitHub Actions: Governance / governance _ Validate Hypatia Baseline: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run echo "Scanning repository: hyperpolymath/hypatia (checking baseline)"
 �[36;1mecho "Scanning repository: hyperpolymath/hypatia (checking baseline)"�[0m
 �[36;1m# Move the baseline filter OUT of the scanned tree, then delete the�[0m
 �[36;1m# standards checkout, so `hypatia scan .` only ever sees the CALLER's�[0m
 �[36;1m# own files. Without this, `.standards-checkout/` (the tooling we�[0m
 �[36;1m# checked out to get apply-baseline.sh) is itself scanned, and�[0m
 �[36;1m# standards' own files get reported as the caller's findings (a banned�[0m
 �[36;1m# `.ts`, `shell_download` bootstrap.sh scripts, etc.).�[0m
 �[36;1m# Prefer the CALLER's own scripts/apply-baseline.sh when present�[0m
 �[36;1m# (self-lint: standards validating itself must run the tree under�[0m
 �[36;1m# test, not main's copy — a new baseline severity the main-pinned�[0m
 �[36;1m# script doesn't know would fail closed here while passing�[0m
 �[36;1m# everywhere else). Consumers without the script keep the�[0m
 �[36;1m# main-pinned fallback.�[0m
 �[36;1mif [ -f scripts/apply-baseline.sh ]; then�[0m
 �[36;1m  cp scripts/apply-baseline.sh "$RUNNER_TEMP/apply-baseline.sh"�[0m
 �[36;1melse�[0m
 �[36;1m  cp .standards-checkout/scripts/apply-baseline.sh "$RUNNER_TEMP/apply-baseline.sh"�[0m
 �[36;1mfi�[0m
 �[36;1mrm -rf .standards-checkout�[0m
 �[36;1m# hypatia's `scan` exits non-zero whenever it finds anything — that is�[0m
 �[36;1m# by design, and under `bash -e` it would abort this step at this line,�[0m
 �[36;1m# before the baseline filter (the real gate) ever runs. Tolerate the�[0m
 �[36;1m# scan's own exit code…�[0m
 �[36;1mHYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . > hypatia-findings.raw.json || true�[0m
 �[36;1m# …but never swallow a genuine scanner crash into a false pass: require a�[0m
 �[36;1m# valid JSON array before trusting the output as "the findings".�[0m
 �[36;1mif ! jq -e 'type == "array"' hypatia-findings.raw.json >/dev/null 2>&1; then�[0m
 �[36;1m  echo "::...

GitHub Actions: Governance / 4_governance _ Code quality + docs.txt: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run set -eo pipefail
 �[36;1mset -eo pipefail�[0m
 �[36;1m# Arming policy, and the evidence it rests on: standards#991.�[0m
 �[36;1m#�[0m
 �[36;1m#   retired-filename -> BLOCKS. A STABLE predicate:�[0m
 �[36;1m#   the retired `.a2ml` spelling of the launcher standard was�[0m
 �[36;1m#   deleted upstream on 2026-09-22�[0m
 �[36;1m#   (standards#952) and stays deleted, so a caller that is clean�[0m
 �[36;1m#   today cannot become defective without editing the citation�[0m
 �[36;1m#   itself. Measured 2026-09-22 over EVERY clone in the estate --�[0m
 �[36;1m#   595 scanned, 553 carrying an origin/main. 432 reference this�[0m
 �[36;1m#   reusable workflow, but only 12 do so at a MUTABLE ref (@main),�[0m
 �[36;1m#   and a new step reaches ONLY those 12: a caller pinned at a SHA�[0m
 �[36;1m#   freezes this whole file, this step included, so it can never�[0m
 �[36;1m#   receive the step at all. The real gate was run against all 12:�[0m
 �[36;1m#   12/12 rc=0, retired=0. Five slugs do carry the retired literal�[0m
 �[36;1m#   (tma-mark2, canonical-ums, the-nash-equilibrium,�[0m
 �[36;1m#   launch-scaffolder, trigger) and their overlap with the armed 12�[0m
 �[36;1m#   is ZERO -- so arming this tier reds ZERO live callers. A�[0m
 �[36;1m#   known-answer positive control fired (rc=1) on three of those�[0m
 �[36;1m#   defective repos through the identical harness, so the twelve�[0m
 �[36;1m#   zeros are a real measurement and not a broken probe.�[0m
 �[36;1m#�[0m
 �[36;1m#   stale-version -> WARNS, and does not block. A TIME-DEPENDENT�[0m
 �[36;1m#   predicate: the gate compares against its own CURRENT_VERSION, so�[0m
 �[36;1m#   every correctly-citing caller flips to defect the moment the�[0m
 �[36;1m#   standard bumps, having done nothing. A baked-in cutoff DATE does�[0m
 �[36;1m#   not cure that -- the #505 split above can use one because its�[0m
 �[36;1m#   missing-CONTRIBUTING population is static, while this population�[0m
 �[36;1m#   is regenerated at every...

GitHub Actions: Governance / governance _ Code quality + docs: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run set -eo pipefail
 �[36;1mset -eo pipefail�[0m
 �[36;1m# Arming policy, and the evidence it rests on: standards#991.�[0m
 �[36;1m#�[0m
 �[36;1m#   retired-filename -> BLOCKS. A STABLE predicate:�[0m
 �[36;1m#   the retired `.a2ml` spelling of the launcher standard was�[0m
 �[36;1m#   deleted upstream on 2026-09-22�[0m
 �[36;1m#   (standards#952) and stays deleted, so a caller that is clean�[0m
 �[36;1m#   today cannot become defective without editing the citation�[0m
 �[36;1m#   itself. Measured 2026-09-22 over EVERY clone in the estate --�[0m
 �[36;1m#   595 scanned, 553 carrying an origin/main. 432 reference this�[0m
 �[36;1m#   reusable workflow, but only 12 do so at a MUTABLE ref (@main),�[0m
 �[36;1m#   and a new step reaches ONLY those 12: a caller pinned at a SHA�[0m
 �[36;1m#   freezes this whole file, this step included, so it can never�[0m
 �[36;1m#   receive the step at all. The real gate was run against all 12:�[0m
 �[36;1m#   12/12 rc=0, retired=0. Five slugs do carry the retired literal�[0m
 �[36;1m#   (tma-mark2, canonical-ums, the-nash-equilibrium,�[0m
 �[36;1m#   launch-scaffolder, trigger) and their overlap with the armed 12�[0m
 �[36;1m#   is ZERO -- so arming this tier reds ZERO live callers. A�[0m
 �[36;1m#   known-answer positive control fired (rc=1) on three of those�[0m
 �[36;1m#   defective repos through the identical harness, so the twelve�[0m
 �[36;1m#   zeros are a real measurement and not a broken probe.�[0m
 �[36;1m#�[0m
 �[36;1m#   stale-version -> WARNS, and does not block. A TIME-DEPENDENT�[0m
 �[36;1m#   predicate: the gate compares against its own CURRENT_VERSION, so�[0m
 �[36;1m#   every correctly-citing caller flips to defect the moment the�[0m
 �[36;1m#   standard bumps, having done nothing. A baked-in cutoff DATE does�[0m
 �[36;1m#   not cure that -- the #505 split above can use one because its�[0m
 �[36;1m#   missing-CONTRIBUTING population is static, while this population�[0m
 �[36;1m#   is regenerated at every...

GitHub Actions: Governance / 9_governance _ Security policy checks.txt: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run set -uo pipefail
 �[36;1mset -uo pipefail�[0m
 �[36;1mDIR=.github/canonical-references�[0m
 �[36;1mif [ ! -d "$DIR" ]; then�[0m
 �[36;1m  echo "ℹ️  [R5] no $DIR/ — skipped (repo has not opted in)"�[0m
 �[36;1m  exit 0�[0m
 �[36;1mfi�[0m
 �[36;1m# Rule files are read with yq, never a hand parser (YAML-POLICY Y-1); this�[0m
 �[36;1m# gate previously parsed them with Python, which the estate bans.�[0m
 �[36;1mif ! command -v yq >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then�[0m
 �[36;1m  echo "❌ [R5] yq and jq are required on the runner for YAML rule parsing"�[0m
 �[36;1m  exit 2�[0m
 �[36;1mfi�[0m
 �[36;1mmapfile -t files < <(find "$DIR" -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) ! -name '.*' | LC_ALL=C sort)�[0m
 �[36;1mif [ "${#files[@]}" -eq 0 ]; then�[0m
 �[36;1m  echo "ℹ️  [R5] $DIR/ has no .yml/.yaml rules — skipped"�[0m
 �[36;1m  exit 0�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1merr=$(mktemp)�[0m
 �[36;1mtrap 'rm -f "$err"' EXIT�[0m
 �[36;1mtotal=0�[0m
 �[36;1mfor rf in "${files[@]}"; do�[0m
 �[36;1m  if ! cfg=$(yq -o json '.' "$rf" 2>&1); then�[0m
 �[36;1m    echo "❌ [R5] $rf: not parseable YAML: $cfg"; total=$((total + 1)); continue�[0m
 �[36;1m  fi�[0m
 �[36;1m  if [ "$(jq -r 'type' <<<"$cfg")" != object ]; then�[0m
 �[36;1m    echo "❌ [R5] $rf: top-level must be a mapping"; total=$((total + 1)); continue�[0m
 �[36;1m  fi�[0m
 �[36;1m  rid=$(jq -r --arg b "$(basename "$rf")" 'if has("id") then .id | tostring else $b end' <<<"$cfg")�[0m
 �[36;1m  desc=$(jq -r '.description // ""' <<<"$cfg")�[0m
 �[36;1m  canon=$(jq -r '.canonical_pointer // ""' <<<"$cfg")�[0m
 �[36;1m  mapfile -t pats < <(jq -r '(.patterns // [])[]' <<<"$cfg")�[0m
 �[36;1m  mapfile -t includes < <(jq -r '((.scope // {}).include // [])[]' <<<"$cfg")�[0m
 �[36;1m  if [ "${#pats[@]}" -eq 0 ] || [ "${#includes[@]}" -eq 0 ]; then�[0m
 �[36;1m    echo "❌ [R5:$rid] missing patterns or scope.include in $rf"�[0m
 �[36;1m    total=$((total + 1)); continue�[0m
 �[36...

GitHub Actions: Governance / governance _ Security policy checks: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run set -uo pipefail
 �[36;1mset -uo pipefail�[0m
 �[36;1mDIR=.github/canonical-references�[0m
 �[36;1mif [ ! -d "$DIR" ]; then�[0m
 �[36;1m  echo "ℹ️  [R5] no $DIR/ — skipped (repo has not opted in)"�[0m
 �[36;1m  exit 0�[0m
 �[36;1mfi�[0m
 �[36;1m# Rule files are read with yq, never a hand parser (YAML-POLICY Y-1); this�[0m
 �[36;1m# gate previously parsed them with Python, which the estate bans.�[0m
 �[36;1mif ! command -v yq >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then�[0m
 �[36;1m  echo "❌ [R5] yq and jq are required on the runner for YAML rule parsing"�[0m
 �[36;1m  exit 2�[0m
 �[36;1mfi�[0m
 �[36;1mmapfile -t files < <(find "$DIR" -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' \) ! -name '.*' | LC_ALL=C sort)�[0m
 �[36;1mif [ "${#files[@]}" -eq 0 ]; then�[0m
 �[36;1m  echo "ℹ️  [R5] $DIR/ has no .yml/.yaml rules — skipped"�[0m
 �[36;1m  exit 0�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1merr=$(mktemp)�[0m
 �[36;1mtrap 'rm -f "$err"' EXIT�[0m
 �[36;1mtotal=0�[0m
 �[36;1mfor rf in "${files[@]}"; do�[0m
 �[36;1m  if ! cfg=$(yq -o json '.' "$rf" 2>&1); then�[0m
 �[36;1m    echo "❌ [R5] $rf: not parseable YAML: $cfg"; total=$((total + 1)); continue�[0m
 �[36;1m  fi�[0m
 �[36;1m  if [ "$(jq -r 'type' <<<"$cfg")" != object ]; then�[0m
 �[36;1m    echo "❌ [R5] $rf: top-level must be a mapping"; total=$((total + 1)); continue�[0m
 �[36;1m  fi�[0m
 �[36;1m  rid=$(jq -r --arg b "$(basename "$rf")" 'if has("id") then .id | tostring else $b end' <<<"$cfg")�[0m
 �[36;1m  desc=$(jq -r '.description // ""' <<<"$cfg")�[0m
 �[36;1m  canon=$(jq -r '.canonical_pointer // ""' <<<"$cfg")�[0m
 �[36;1m  mapfile -t pats < <(jq -r '(.patterns // [])[]' <<<"$cfg")�[0m
 �[36;1m  mapfile -t includes < <(jq -r '((.scope // {}).include // [])[]' <<<"$cfg")�[0m
 �[36;1m  if [ "${#pats[@]}" -eq 0 ] || [ "${#includes[@]}" -eq 0 ]; then�[0m
 �[36;1m    echo "❌ [R5:$rid] missing patterns or scope.include in $rf"�[0m
 �[36;1m    total=$((total + 1)); continue�[0m
 �[36...

GitHub Actions: Governance / 11_governance _ Well-Known (RFC 9116 + RSR).txt: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run SECTXT=""
 �[36;1mSECTXT=""�[0m
 �[36;1m[ -f ".well-known/security.txt" ] && SECTXT=".well-known/security.txt"�[0m
 �[36;1m[ -f "security.txt" ] && SECTXT="security.txt"�[0m
 �[36;1mif [ -z "$SECTXT" ]; then�[0m
 �[36;1m  echo "::warning::No security.txt found."�[0m
 �[36;1m  exit 0�[0m
 �[36;1mfi�[0m
 �[36;1mgrep -q "^Contact:" "$SECTXT" || { echo "::error::Missing Contact field"; exit 1; }�[0m
 �[36;1mif ! grep -q "^Expires:" "$SECTXT"; then�[0m
 �[36;1m  echo "::error::Missing Expires field"�[0m
...
 �[36;1mfi�[0m
 �[36;1mEXPIRES=$(grep "^Expires:" "$SECTXT" | cut -d: -f2- | tr -d ' ' | head -2)�[0m
 �[36;1mif date -d "$EXPIRES" > /dev/null 2>&1; then�[0m
 �[36;1m  DAYS=$(( ($(date -d "$EXPIRES" +%s) - $(date +%s)) / 86400 ))�[0m
 �[36;1m  if [ $DAYS -lt 0 ]; then�[0m
 �[36;1m    echo "::error::security.txt EXPIRED"�[0m
...
 ##[group]Run MIXED=$(grep -rE 'src="http://|href="http://' --include="*.html" --include="*.htm" . 2>/dev/null | grep -vE 'localhost|127\.0\.0\.1|example\.com|lol/|node_modules/|third-party/|vendor/' | head -5)
 �[36;1mMIXED=$(grep -rE 'src="http://|href="http://' --include="*.html" --include="*.htm" . 2>/dev/null | grep -vE 'localhost|127\.0\.0\.1|example\.com|lol/|node_modules/|third-party/|vendor/' | head -5)�[0m
 �[36;1mif [ -n "$MIXED" ]; then�[0m
 �[36;1m  echo "::error::Mixed content (HTTP in HTML)"�[0m

GitHub Actions: Governance / governance _ Well-Known (RFC 9116 + RSR): fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run SECTXT=""
 �[36;1mSECTXT=""�[0m
 �[36;1m[ -f ".well-known/security.txt" ] && SECTXT=".well-known/security.txt"�[0m
 �[36;1m[ -f "security.txt" ] && SECTXT="security.txt"�[0m
 �[36;1mif [ -z "$SECTXT" ]; then�[0m
 �[36;1m  echo "::warning::No security.txt found."�[0m
 �[36;1m  exit 0�[0m
 �[36;1mfi�[0m
 �[36;1mgrep -q "^Contact:" "$SECTXT" || { echo "::error::Missing Contact field"; exit 1; }�[0m
 �[36;1mif ! grep -q "^Expires:" "$SECTXT"; then�[0m
 �[36;1m  echo "::error::Missing Expires field"�[0m
...
 �[36;1mfi�[0m
 �[36;1mEXPIRES=$(grep "^Expires:" "$SECTXT" | cut -d: -f2- | tr -d ' ' | head -2)�[0m
 �[36;1mif date -d "$EXPIRES" > /dev/null 2>&1; then�[0m
 �[36;1m  DAYS=$(( ($(date -d "$EXPIRES" +%s) - $(date +%s)) / 86400 ))�[0m
 �[36;1m  if [ $DAYS -lt 0 ]; then�[0m
 �[36;1m    echo "::error::security.txt EXPIRED"�[0m

GitHub Actions: Governance / governance _ Well-Known (RFC 9116 + RSR): fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run MIXED=$(grep -rE 'src="http://|href="http://' --include="*.html" --include="*.htm" . 2>/dev/null | grep -vE 'localhost|127\.0\.0\.1|example\.com|lol/|node_modules/|third-party/|vendor/' | head -5)
 �[36;1mMIXED=$(grep -rE 'src="http://|href="http://' --include="*.html" --include="*.htm" . 2>/dev/null | grep -vE 'localhost|127\.0\.0\.1|example\.com|lol/|node_modules/|third-party/|vendor/' | head -5)�[0m
 �[36;1mif [ -n "$MIXED" ]; then�[0m
 �[36;1m  echo "::error::Mixed content (HTTP in HTML)"�[0m

GitHub Actions: Governance / 12_governance _ Actions lockfile verify.txt: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run set -uo pipefail
 �[36;1mset -uo pipefail�[0m
 �[36;1m# Stage the gate, the verifier and the exemption ledger. When THIS�[0m
 �[36;1m# repository is standards, its own working tree already holds all�[0m
 �[36;1m# three (self-lint); every other caller uses the pinned checkout.�[0m
 �[36;1mif [ "$GITHUB_REPOSITORY" = hyperpolymath/standards ]; then�[0m
 �[36;1m  SRC=scripts�[0m
 �[36;1m  LEDGERSRC=.machine_readable�[0m
 �[36;1m  echo "Using this repository's own gate + verifier + ledger (standards self-lint)."�[0m
 �[36;1melse�[0m
 �[36;1m  SRC=.standards-lock/scripts�[0m
 �[36;1m  LEDGERSRC=.standards-lock/.machine_readable�[0m
 �[36;1mfi�[0m
 �[36;1mfor f in check-actions-lock-gate.sh update-actions-lock.sh; do�[0m
 �[36;1m  if [ ! -f "$SRC/$f" ]; then�[0m
 �[36;1m    echo "::error::actions-lock gate: $f not found in $SRC (pinned standards checkout failed?)"�[0m

GitHub Actions: Governance / governance _ Actions lockfile verify: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run set -uo pipefail
 �[36;1mset -uo pipefail�[0m
 �[36;1m# Stage the gate, the verifier and the exemption ledger. When THIS�[0m
 �[36;1m# repository is standards, its own working tree already holds all�[0m
 �[36;1m# three (self-lint); every other caller uses the pinned checkout.�[0m
 �[36;1mif [ "$GITHUB_REPOSITORY" = hyperpolymath/standards ]; then�[0m
 �[36;1m  SRC=scripts�[0m
 �[36;1m  LEDGERSRC=.machine_readable�[0m
 �[36;1m  echo "Using this repository's own gate + verifier + ledger (standards self-lint)."�[0m
 �[36;1melse�[0m
 �[36;1m  SRC=.standards-lock/scripts�[0m
 �[36;1m  LEDGERSRC=.standards-lock/.machine_readable�[0m
 �[36;1mfi�[0m
 �[36;1mfor f in check-actions-lock-gate.sh update-actions-lock.sh; do�[0m
 �[36;1m  if [ ! -f "$SRC/$f" ]; then�[0m
 �[36;1m    echo "::error::actions-lock gate: $f not found in $SRC (pinned standards checkout failed?)"�[0m

GitHub Actions: Governance / 15_governance _ Workflow security linter.txt: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run # GitHub Actions REJECTS a workflow with duplicate keys: the run is
 �[36;1m# GitHub Actions REJECTS a workflow with duplicate keys: the run is�[0m
 �[36;1m# `failure` with no jobs, no log and no check run. Nothing else here�[0m
 �[36;1m# can see it, because yaml.safe_load silently keeps the LAST�[0m
 �[36;1m# duplicate and reports success — so the file "parses" and every�[0m
 �[36;1m# other lint passes. Measured 2026-08-05: nine workflows in hypatia�[0m
 �[36;1m# were dead this way, including a CodeQL workflow with zero�[0m
 �[36;1m# successful runs in its entire lifetime.�[0m
 �[36;1mset -euo pipefail�[0m
 �[36;1mSCRIPT=".standards-dupkey/scripts/check-workflow-duplicate-keys.sh"�[0m
 �[36;1m# Self-hosting fallback: when THIS repository is standards, its own�[0m
 �[36;1m# working tree already holds the script, and during a rename that copy�[0m
 �[36;1m# is the only correct one — the pinned main checkout still has the old�[0m
 �[36;1m# name. Preferring the fetched copy keeps every other caller on the�[0m
 �[36;1m# canonical version.�[0m
 �[36;1mif [ ! -f "$SCRIPT" ] && [ -f scripts/check-workflow-duplicate-keys.sh ]; then�[0m
 �[36;1m  SCRIPT="scripts/check-workflow-duplicate-keys.sh"�[0m
 �[36;1m  echo "Using this repository's own copy (standards self-lint)."�[0m
 �[36;1mfi�[0m
 �[36;1mif [ ! -f "$SCRIPT" ]; then�[0m
 �[36;1m  echo "::error::duplicate-key checker not found — neither fetched from" \�[0m

GitHub Actions: Governance / governance _ Workflow security linter: fix(actions-lock): support the v0.0.3 per-dependency hostname field

Conclusion: failure

View job details

##[group]Run # GitHub Actions REJECTS a workflow with duplicate keys: the run is
 �[36;1m# GitHub Actions REJECTS a workflow with duplicate keys: the run is�[0m
 �[36;1m# `failure` with no jobs, no log and no check run. Nothing else here�[0m
 �[36;1m# can see it, because yaml.safe_load silently keeps the LAST�[0m
 �[36;1m# duplicate and reports success — so the file "parses" and every�[0m
 �[36;1m# other lint passes. Measured 2026-08-05: nine workflows in hypatia�[0m
 �[36;1m# were dead this way, including a CodeQL workflow with zero�[0m
 �[36;1m# successful runs in its entire lifetime.�[0m
 �[36;1mset -euo pipefail�[0m
 �[36;1mSCRIPT=".standards-dupkey/scripts/check-workflow-duplicate-keys.sh"�[0m
 �[36;1m# Self-hosting fallback: when THIS repository is standards, its own�[0m
 �[36;1m# working tree already holds the script, and during a rename that copy�[0m
 �[36;1m# is the only correct one — the pinned main checkout still has the old�[0m
 �[36;1m# name. Preferring the fetched copy keeps every other caller on the�[0m
 �[36;1m# canonical version.�[0m
 �[36;1mif [ ! -f "$SCRIPT" ] && [ -f scripts/check-workflow-duplicate-keys.sh ]; then�[0m
 �[36;1m  SCRIPT="scripts/check-workflow-duplicate-keys.sh"�[0m
 �[36;1m  echo "Using this repository's own copy (standards self-lint)."�[0m
 �[36;1mfi�[0m
 �[36;1mif [ ! -f "$SCRIPT" ]; then�[0m
 �[36;1m  echo "::error::duplicate-key checker not found — neither fetched from" \�[0m
📝 Summary

Summary by CodeRabbit

  • New Features

    • Added support for Actions lockfiles using versions v0.0.2 and v0.0.3.
    • v0.0.3 lockfiles can include dependency hostnames, with omitted values defaulting to github.com.
    • Added validation for supported GitHub and GitHub Enterprise hostname formats.
    • Dependency auditing now preserves and validates hostname information, rejecting unsupported or malformed lockfile entries.
  • Bug Fixes

    • Improved workflow validation for mismatched or non-authorised hostnames.
    • Added safeguards to reject invalid lockfiles rather than accepting potentially unsafe configurations.

Actions lock parsing now supports optional dependency hostnames in v0.0.3 lockfiles. Omitted hostnames default to github.com. Parsing and workflow validation tests cover accepted GitHub hostnames and rejection of unsupported or malformed hostnames.

Changes: Actions lock hostname support

Layer / File(s) Summary
Parse and retain dependency hostnames
lib/rules/actions_lock.ex
The parser accepts hostname fields in v0.0.3, validates their syntax and value, and defaults omitted fields to github.com.
Verify hostname parsing
test/rules/actions_lock_test.exs, .reuse/dep5
Tests cover v0.0.3 fixtures, hostname defaults, accepted and rejected hostname forms, and unsupported versions. REUSE metadata documents fixture provenance and replacement instructions.
Verify workflow lock validation
test/workflow_audit_test.exs, test/workflow_hardening_test.exs
Tests expect GitHub-hosted locks to pass and locks with a GHE tenant hostname to be rejected.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 464fe

No specific issue remains that would prevent merging after normal checks.

Security architecture risk: ⚪ Minimal · up to 464fe

The new field accepts only github.com. Other hosts invalidate the complete lock, while workflow-specific association and dependency-validation requirements remain intact. No material security risk was identified in this change.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The changed acceptance decision affects workflow pin validation using supplied lock content or a repository-local actions.lock. A rejected hostname invalidates that whole lock rather than permitting associations from its remaining dependencies.

Security Findings and Attack Paths

  • inferred — The examined cross-host identity-confusion path is blocked: a tenant hostname cannot populate the hostless association keys through parsed input. Unlike the base, the head accepts explicit github.com declarations, but it does not accept identities from another host. No introduced or worsened attack path was established.

Trust Boundaries and Controls

  • observed — The parser owns hostname validation. Although pinned? does not independently inspect hostname, both reviewed consumers use parser-produced locks. Unsupported hosts fail before association checks, and successful association still requires the current workflow to list the normalized ref and a matching dependency to exist.
Pre-merge checks: Passed 5
Check name Status Explanation
Description check Passed The description clearly explains the v0.0.3 hostname support, parser behaviour, tests, verification, and related standards configuration.
Title check Passed The title clearly and concisely identifies the main change: support for the v0.0.3 per-dependency hostname field.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 4…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 4 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches
🛠️ Fix failing CI checks
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Owner Author

governance / Validate Hypatia Baseline is red here, but this PR doesn't cause it. It is red identically on main.

  • The job builds the scanner at standards' pinned hypatia-ref default, 51ab6496bf47e30a0576d503df31fec30f3cfe56 (log: Resolved hypatia scanner commit: 51ab6496…). The code in this PR is never run by this check.
  • On this PR (run 39414941831) and on main at 25758e6 (run 39339044206) the gate keeps the same 30 findings with the same type counts: 18 RE001, 5 RE004, 3 RE005, 2 invalid_actions_lock, 2 WH013. main has failed this job on every push since 5173ca7 (10-15).
  • No fix exists yet. The planned fix is a hyperpolymath/standards PR that moves the hypatia-ref defaults in governance-reusable.yml and hypatia-scan-reusable.yml to this PR's merge SHA. That clears the 2 invalid_actions_lock findings: replaying the gate locally on standards with this branch exits 0. I have not re-run the job, because it is deterministic on that pin and would fail the same way.
  • Separately, the other 28 kept findings on Hypatia's own repo (RE001/RE004/RE005/WH013) are on main too and outside this PR's scope. The pin bump alone will not turn Hypatia's own gate green.

Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Security Status is also red here, but this PR doesn't cause it either.

  • The security-status aggregator job in .github/workflows/security-policy.yml has no checkout step. Its self-check step runs awk/sed on .github/workflows/security-policy.yml in an empty workspace and exits 2 (awk: fatal: cannot open file … No such file or directory).
  • It fails the same way on main on every recent run: 25758e6 (push and the 10-20 schedule), ade0950 and fd7db07. This PR touches no workflow files.
  • No fix exists yet. I'm not widening this PR for it. Proposed patch: add the same pinned checkout the other jobs in this workflow use as the first step of security-status:
    steps:
      - name: Checkout
        uses: actions/checkout@<same ref as the other jobs in this file>
        with:
          sparse-checkout: .github/workflows/security-policy.yml
          sparse-checkout-cone-mode: false
      - name: "Self-check — no orphan jobs (every job is in this needs list)"

Use exactly the ref the sibling jobs already use, so the workflow's actions.lock entry stays valid and no relock is needed. If the ref differs, regenerate the lock with gh actions-lock and don't edit it by hand. No re-run, because the failure is deterministic.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Add Carrot credits or activate Agent usage billing to use Autofix

@hyperpolymath
hyperpolymath marked this pull request as ready for review October 20, 2026 12:08
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Coding task failed

The task could not be completed. Open the task for details or retry.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-20T12:10:59.541529Z 464fe1e Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Agent ran but produced no code changes.

The CI failures may require manual intervention.

View agent analysis
I didn't change any files. None of these failures come from this PR's code, and the ones I could trace back need changes to workflow files, which I'm not allowed to edit.

**This branch has no diff against `origin/main`.** `git diff origin/main HEAD` is empty. Main already has this same change, squash-merged as `aa34a4c` (#938). The failing checks were therefore running on the same tree as main, so a source or test edit here can't be what fixes them.

What the evidence shows for each failing check:

- **Security Status** (`awk`/`sed`: "cannot open `.github/workflows/security-policy.yml`"): that file is in the repo. The job's step reads it from a working directory where it isn't present, most likely because that job never checks out the repo. The fix belongs in `.github/workflows/security-policy.yml`.
- **Governance / Validate Hypatia Baseline** ("30 unfiltered finding(s)", e.g. `research_extensions`): the job builds Hypatia at a pinned commit and scans the whole repo. The excerpt doesn't show any of the 30 findings pointing at files this PR changed, and the tree is the same as main. Clearing them would mean triaging the full scan output, which is outside this PR.
- **Other Governance jobs** (launcher-standard currency, canonical-references R5, security.txt / mixed content, actions-lock gate, duplicate-key linter): these all run scripts from the reusable workflow in hyperpolymath/standards or from a pinned checkout of it. The excerpts show only the step scripts and the final error lines, and none of them points at a file this PR touched.
- **Rust License & Ban Check** (`install-action` bail), **Rust Dependency Audit** (cargo-audit report or advisories) and **TruffleHog** (an empty or degenerate commit range, "BASE and HEAD commits are the same"): these are tool-install problems, advisory-database results, or the scan range. That last one fits this PR, because its head adds nothing on top of main.

To fix them:
1. Close or rebase this PR, since main already has the change.
2. Fix the `security-policy.yml` job so it checks out the repo before the aggregator-needs step. That's a workflow edit, so someone with workflow write access has to make it.
3. Treat the Governance and Rust-audit failures as main-level issues and look at their full logs on main.

@hyperpolymath
hyperpolymath merged commit aa34a4c into main Oct 20, 2026
90 of 92 checks passed
@hyperpolymath
hyperpolymath deleted the claude/gallant-ramanujan-27wzn9 branch October 20, 2026 12:08
hyperpolymath added a commit that referenced this pull request Oct 20, 2026
…lf-check (#939)

## Problem

`Security Status` (`security-status` in
`.github/workflows/security-policy.yml`) fails on `main` and on every
PR: for example main runs 39357599189, 39339043314, 39323370164 and
39276944530, and job 118603122869 on #938.

Its first step, "Self-check — no orphan jobs", runs `awk`/`sed` over
`.github/workflows/security-policy.yml`, but the job has no checkout
step. So the step exits 2 with `awk: fatal: cannot open file
'.github/workflows/security-policy.yml'`, and the job fails whatever the
scans found.

## Change

- Adds a sparse checkout of just that file as the job's first step
(`sparse-checkout: .github/workflows/security-policy.yml`,
`sparse-checkout-cone-mode: false`, `persist-credentials: false`).
- Uses `actions/checkout@v7.0.1`, the same ref every sibling job in this
file uses. That ref is already in this workflow's `actions.lock` entry,
so the lockfile is unchanged and did not need regenerating.
- Leaves `needs:` and `if: always()` alone.

## Validation (local)

- Ran the self-check script on the edited file. It prints `jobs in
workflow: 9` and then `ok: every non-aggregator job is named in the
aggregator's needs`.
- Ran `actionlint` 1.7.7 on the file before and after the change: no
findings either time.

Done when, on this PR: Security Status is green, its self-check log
shows the `ok:` line, and `governance / Actions lockfile verify` stays
green.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01SYkPCGtUuCvgyns6KgzyMX)_

Co-authored-by: Claude <noreply@anthropic.com>
hyperpolymath added a commit to hyperpolymath/standards that referenced this pull request Oct 20, 2026
… support) (#1315)

## Problem
`governance / Validate Hypatia Baseline` is red on `main` (`b3ce78e`,
run 39340237981) and on every open PR, including #1309, #1310 and #1311.
The only blocking findings are:

```
invalid_actions_lock (workflow_audit, high):     {:line, 4, {:unsupported_version, "v0.0.3"}}
invalid_actions_lock (workflow_hardening, high): actions.lock failed closed: {:line, 4, {:unsupported_version, "v0.0.3"}}
```

The baseline job does not build Hypatia HEAD. Both
`governance-reusable.yml` and `hypatia-scan-reusable.yml` default
`hypatia-ref` to the vetted SHA `51ab6496…` (2026-10-01), and that
commit's lockfile parser accepts only `v0.0.2`. `gh actions-lock` now
writes `v0.0.3`. The lockfile is tool-owned, and this finding is
deliberately not baselinable (#1239, #1305), so the scanner pin is what
has to move.

## Change
Both `hypatia-ref` defaults move from
`51ab6496bf47e30a0576d503df31fec30f3cfe56` to
`aa34a4cc6ba39d873710ff39763fdb9cde715f9d`. They move together, as their
descriptions require. `aa34a4cc` is hypatia `main` after
hyperpolymath/hypatia#938, which adds proper `v0.0.3` support, including
the one field `v0.0.3` introduces (the optional per-dependency
`hostname`). Unknown versions still fail closed.

No `uses:` ref changes, so `actions.lock` is untouched.

## Blast radius (measured, not assumed)
The bump carries every Hypatia commit since `51ab6496` (23 of them,
including the `research_extensions` rules and
hyperpolymath/hypatia#922's finding-shape change), and a pin move broke
every caller once before (hyperpolymath/hypatia#895). So I replayed the
`validate-hypatia-baseline` step locally at both pins on three repos:
scan, path relativisation, then `apply-baseline.sh` with
`BLOCKING_THRESHOLD=info`.

| Repo | Kept at `51ab6496` | Kept at `aa34a4cc` | Change |
|---|---|---|---|
| standards (`d55304a`) | 2 × `invalid_actions_lock` | **0** | gate
passes |
| echidna | 2 × `invalid_actions_lock`, 1 `DependencyPinning` | same 3 |
none* |
| hypatia | 2 × `invalid_actions_lock` + 28 pre-existing (RE001/4/5,
WH013) | the 28 | −2 |

(The table leaves out `GS002`/`GS005`, which come from my detached local
checkouts.)

- On all three repos the raw findings only *lose* `invalid_actions_lock`
and the derived `DependencyPinning`. There is no new finding type and no
severity change.
- All emitted severities stay inside `hypatia-scan-reusable.yml`'s
accepted set. The new `support`/`evidence`/`level` finding fields are
additive, and the only `.level` the reusable reads is the SARIF one.
- \*echidna's lockfile now parses, but it has two real dangling edges
(`slsa-framework/slsa-github-generator@f7dd8c54…` and
`hyperpolymath/panic-attack@b689a74d…` are listed under `workflows:`
with no top-level record, the startup-failure case
`scripts/close-lock.sh` documents). That is an echidna repair, not
something this PR changes.

## Checks run locally
- `scripts/tests/governance-reusable-contract-test.sh`: PASS
- `scripts/tests/hypatia-blocking-gate-test.sh`: PASS
- `scripts/tests/apply-baseline-test.sh`: 52 passed, 0 failed
- `scripts/tests/check-workflow-duplicate-keys-test.sh`: 17 passed, 0
failed with mikefarah yq v4.47.2 (checksum-verified). It fails 13/4 on
`main` too when only the Python `yq` is on PATH.
`scripts/check-workflow-duplicate-keys.sh` on both files: clean.
- `actionlint` 1.7.7 on both files: output identical to `main`.
- `yq '.on.workflow_call.inputs."hypatia-ref".default'` → `aa34a4cc…` in
both files.

## After merge
Open PRs (#1309, #1310, #1311) need `main` merged into them. Re-running
their failed jobs won't help, because a re-run reuses the PR's old merge
commit, which still carries `51ab6496`.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01KUZiEB43T3E9fJAXqwbSDm)_

Co-authored-by: Claude <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Oct 20, 2026
…940)

## Summary

PR 1 of a stack that triages the 30 findings `governance / Validate
Hypatia Baseline` reports on every PR (e.g. #938 job 118601725176, #939
job 118615176756).

The gate doesn't run this repo's scanner. It builds hypatia at the
reusable's default `hypatia-ref`, **`51ab649`**. That commit's
`ActionsLock` parser accepts only v0.0.2, so this repo's tool-owned
v0.0.3 `actions.lock` fails closed as 2 high findings:

```
invalid_actions_lock (workflow_audit)     {:line, 4, {:unsupported_version, "v0.0.3"}}
invalid_actions_lock (workflow_hardening) actions.lock failed closed: {:line, 4, ...}
```

The parser was already fixed by #938 (`aa34a4c`). This PR passes
`hypatia-ref: aa34a4c…` to both standards reusables (`governance.yml`,
`hypatia-scan.yml`).

- `uses:` refs are unchanged, so `actions.lock` is untouched.
- The value matches hyperpolymath/standards#1315, which moves the estate
default to the same SHA.
- Once the standards callers are re-pinned past #1315 (a `gh
actions-lock` run), this input is redundant and should be dropped.

## Type of change

- [x] 🔧 Build / CI / tooling

## How has this been verified?

- Both reusables at the pinned `333b06a0` declare a `hypatia-ref` string
input (`git show
333b06a0:.github/workflows/{governance,hypatia-scan}-reusable.yml`).
- `yq` reads `hypatia-ref: aa34a4c…` from both callers. `actionlint`
1.7.7 is clean on both files, before and after.
- The blast radius at `aa34a4c` was measured in standards#1315 by
replaying the baseline step: on this repo it removes the 2
`invalid_actions_lock` findings and adds nothing. **The gate stays red
on this PR** because of the 28 other findings. The stacked PRs fix those
at their source:
- WH013 rule precision for PAT-authenticated writes, plus the
`inbox-steward-intake.yml` repair (masked pushes, and appended text that
corrupts `.hypatia-baseline.json`);
- RE005 rule precision for `trap` cleanup, plus two real masks removed;
  - RE004 digest pins;
- RE001 harden-runner in block mode, which needs an owner `gh
actions-lock` run.

## Checklist

- [ ] My commits are **signed**. The session's signing shim is
platform-mediated; GitHub signs the squash merge.
- [x] I ran the project's own checks/tests locally and they pass
(actionlint; no Elixir change).
- [x] New files carry the correct SPDX (no new files).
- [x] Docs are updated, and no public claim now overstates what the code
does.
- [x] I have not introduced a soundness hole.

## Notes for reviewers

The pin now lives in two places (here and the standards default) until
the re-pin. The comment in `governance.yml` says when to drop it.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01CYR5bf5FBLS7cXQK72aVNM)_

Co-authored-by: Claude <noreply@anthropic.com>
hyperpolymath pushed a commit that referenced this pull request Oct 20, 2026
Brings in #938 (ActionsLock v0.0.3 per-dependency hostname), #939
(security-status checks out the workflow file before its self-check) and
#940 (hypatia-ref aa34a4c passed to the standards scanner reusables).

Conflicts:
- lib/rules/actions_lock.ex: this branch and #938 each added v0.0.3
  hostname handling; the textual merge kept both, leaving this branch's
  clauses unreachable behind #938's. #938's parser is taken whole (it
  checks the hostname grammar, the version and duplicates), and this
  branch's one other addition, workflow_refs/2 (PI004 per workflow), is
  re-applied on top.
- test/rules/actions_lock_test.exs: a hostname line in a v0.0.2 lock is
  {:hostname_not_in_version, "v0.0.2"} under #938, not
  :malformed_dependency_entry.
- .github/workflows/hypatia-scan.yml: #940's `with: hypatia-ref` input and
  this branch's secrets comment, both kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xn9nqHVbUTRLgBdedoXAc8
hyperpolymath added a commit to hyperpolymath/echidna that referenced this pull request Oct 20, 2026
…stale reusable pins upstream (#446)

## Summary

`.github/workflows/actions.lock` (v0.0.3) was not transitively closed.
It listed two job-level reusable refs under `workflows:`, and neither
had a top-level record:

- `slsa-framework/slsa-github-generator@f7dd8c54…`
(`generator-generic-ossf-slsa3-publish.yml`)
- `hyperpolymath/panic-attack@b689a74…` (`security-scan.yml`)

Since hyperpolymath/hypatia#938 (scanner `aa34a4cc`), Hypatia reports
this as `invalid_actions_lock` / `{:workflow_dependencies_missing, …}`
in both `workflow_audit` and `workflow_hardening`. That keeps
`governance / Validate Hypatia Baseline` red once the hypatia-ref bump
from hyperpolymath/standards#1315 lands.

**Why the lock couldn't simply be regenerated.** I ran the estate repair
chain (`gh actions-lock` → `relock-sha-keys` → `complete-job-refs` →
`close-lock` → `prune-stale`, standards `3c00687a`) on main's pins.
close-lock stops with exit 3 on two leaf refs:

- `dtolnay/rust-toolchain@e2a55d2…`: pinned by standards@75bd5994
`mirror-reusable.yml`
- `dtolnay/rust-toolchain@02cb101e…`: pinned by panic-attack@b689a74
`scan-and-report.yml`

dtolnay moved its `v1` tag, so no tag names either commit any more.
close-lock correctly refuses to write a self-SHA leaf (cf. standards
cf8f9121). Upstream main of both repos now pins `683b588`, the current
`v1`. The fix is therefore to move the stale caller pins, then run the
chain again.

## Changes

- `security-scan.yml`: panic-attack `scan-and-report.yml` `b689a74` →
`8955f615`. Only one line changes in the called file: the dtolnay pin.
- `governance`, `hypatia-scan`, `mirror`, `scorecard`, `secret-scanner`,
`spark-theatre-gate`: standards reusables `75bd5994` → `3c00687a`
(standards#1315). This also brings in `hypatia-ref` `aa34a4cc`. The
`workflow_call` inputs and secrets are unchanged;
`hypatia-scan-reusable` gains a `verdict` output.
- `actions.lock`: the chain's output for this exact tree, regenerated in
CI (sha256 `58d32e2d…`). It was not edited by hand.
- Deleted `zz-one-off-lock-regen.yml`, a scratch workflow left behind by
#441. It was reused on this branch to run the chain (this session has no
GitHub API access), then removed.

## RSR Quality Checklist

### Required

- [x] Tests pass: no code changes; lock checks below
- [x] Code is formatted: workflow lines changed only in their pin and
comment
- [x] Linter is clean: `actionlint` clean on all workflows
- [x] No banned language patterns
- [x] No `unsafe` blocks without `// SAFETY:` comments: n/a
- [x] No banned functions: n/a
- [x] SPDX license headers present on all new/modified source files
- [x] No secrets, credentials, or `.env` files included

### As Applicable

- [ ] Docs/deeds/roadmap: n/a (CI pins only)

## Testing

Measured in [run
39423933176](https://github.com/hyperpolymath/echidna/actions/runs/39423933176),
on three variants of the caller pins:

| Variant | close-lock | check-lock-sync, gate @75bd59 and @3c00687a |
Hypatia@aa34a4cc `invalid_actions_lock` |
|---|---|---|---|
| v0: pins as on main | 2 unresolvable | pass | 2 → 2 |
| v1: panic-attack bump only | 1 unresolvable (`e2a55d2`) | pass | 2 → 2
|
| **v2: this PR** | **0 dangling** (re-run idempotent) | **pass** | **2
→ 0** |

- Locally, on the final tree: `check-lock-sync.sh` passes, and an
independent count of edges with no matching key gives 0.
- Known gap: raw `gh actions-lock --verify` (v0.2.0) fails on every
variant, main's current lock included. It cannot see job-level reusable
`uses:`, so it calls exactly the records close-lock requires "unused".
The estate verifier `update-actions-lock.sh --verify-local` (what the
governance gate runs) accepts that coverage and passes. With the
maintainer's agreement, that wrapper counts as the verify criterion
here.
- Risk to watch on this PR's CI: callers now run 17 newer standards
commits, so Governance or Hypatia may surface new baseline findings
unrelated to the lock.

---
_Generated by [Claude
Code](https://claude.ai/code/session_015jzCHZx4Rt65NDaGLDrjLp)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Oct 20, 2026
## Summary

This is the last step of the baseline-gate stack. `governance.yml` and
`hypatia-scan.yml` pass `hypatia-ref: aa34a4c` (#940), which predates
the rule fixes that clear this repo's own false positives. This PR moves
both to **hypatia `main` at `23dc74d`**, which contains:

- #938: ActionsLock accepts v0.0.3 locks (already in `aa34a4c`);
- #942: WH013 judges writes per job and skips PAT/app-token writes. This
clears `estate-rescan.yml`; `inbox-steward-intake.yml` was also repaired
in #943;
- #947: RE005 skips `trap` handlers, and job extraction survives
comments between jobs.

`uses:` refs are unchanged, so `actions.lock` is untouched.

## What this PR's governance run will show

This PR's own `governance / Validate Hypatia Baseline` run is the
measurement: it builds the scanner at `23dc74d`. The jump from `aa34a4c`
also brings #931's new rules (WF026, WF027, PIN-003), which haven't been
measured on this repo yet. hex.pm is blocked in my session, so I can't
build the escript to pre-run them.

**Expected remaining findings, by class:**
- **RE001 (harden-runner):** most of the original 18 jobs, including the
2 dispatched label workflows, which must stay action-free.
- These need an owner decision; the harden-runner steps were removed
again in #948 because the lock has no record.
- **WH013, RE004, RE005, `invalid_actions_lock`:** expected 0.

I'll post the measured list here once the run completes.

## Type of change

- [x] 🔧 Build / CI / tooling

## How has this been verified?

- `actionlint` 1.7.7 is clean on both files.
- `23dc74d` is `origin/main` at the time of writing. `git log
aa34a4c..23dc74d` contains #942, #947, #945, #931, #941 and #936.

## Checklist

- [ ] My commits are **signed**. The session's signing shim is
platform-mediated.
- [x] I ran the project's own checks/tests locally and they pass.
- [x] New files carry the correct SPDX (none added).
- [x] Docs are updated (the pin comment names what it carries).
- [x] I have not introduced a soundness hole.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01CYR5bf5FBLS7cXQK72aVNM)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants