Skip to content

fix(ci): replace the three actions the estate policy rejects at start-up (editorconfig-checker, webfactory/ssh-agent, dtolnay/rust-toolchain) #1147

Open

Description

@hyperpolymath

Four actions are named in rhodium-standard-repositories/actions-allowlist/
allowed-actions.json but are NOT accepted by the live organisation setting,
because they are neither GitHub-owned nor Marketplace-verified creators:

editorconfig-checker/action-editorconfig-checker governance-reusable.yml
webfactory/ssh-agent mirror-reusable.yml (6x)
dtolnay/rust-toolchain mirror-reusable.yml
goto-bus-stop/setup-zig (consumers, see #103)

A rejected uses: does not fail a job — it fails the WORKFLOW, before a
single step runs, with startup_failure and zero check runs. Because these
live in reusable workflows, one rejected action takes out every caller at
once. Measured on hyperpolymath/game-server-admin: Governance and
Mirror to Git Forges produce no check runs at all (game-server-admin#103).

Per the owner's ruling on #103 the allow-list is not to be widened, so the
actions go and their behaviour is kept:

  • EditorConfig check — install the pinned module with go install ...@v4.0.2
    and run the binary. go install verifies against the Go checksum
    database, a stronger guarantee than the action gave. The step stays
    advisory (continue-on-error) exactly as before.
  • ssh-agent — start ssh-agent and ssh-add the key from the caller's
    secret, exporting SSH_AUTH_SOCK/SSH_AGENT_PID for the push steps. No
    third-party code involved.
  • Rust toolchain — GitHub-hosted runners already carry stable Rust, so
    select it with rustup default stable instead of installing another.

actions.lock: the two now-unreferenced dependency blocks are dropped and
their entries removed from the affected workflow sections.
dtolnay/rust-toolchain stays in the dependencies map — rust-ci-reusable.yml
still uses it, and that workflow is out of scope here.

Verified with this repo's own .githooks/validate-actions-lock.sh:
24 SHA-pinned refs checked against 23 lockfile keys, 0 errors.

Why this needs a human with push access

The credential available to the agent that produced this patch is a GitHub
App installation without contents: write on hyperpolymath/standards —
git push returns Permission to hyperpolymath/standards.git denied to hyperpolymath, and POST /git/refs returns Resource not accessible by integration. So this is filed as an issue with the patch attached rather
than as a pull request. git am applies it cleanly on top of a41f3692.

What is broken, precisely

rhodium-standard-repositories/actions-allowlist/allowed-actions.json lists
89 patterns, including all three of the actions below. The live
organisation setting does not accept them: they are neither GitHub-owned nor
created by a verified Marketplace creator. That gap between the file and the
setting is itself worth closing, but the owner's ruling on
game-server-admin#103 was not to widen the allow-list, so the callers change
instead.

A rejected uses: does not fail a job — it fails the workflow, before a
single step runs, with startup_failure and zero check runs. Because
these three sit inside reusable workflows, one rejected action takes out
every caller at once.

Measured on hyperpolymath/game-server-admin at e6836310:

workflow result rejected action
Governance startup_failure, no check runs editorconfig-checker/action-editorconfig-checker@840e866d
Mirror to Git Forges startup_failure, no check runs webfactory/ssh-agent@e8387483, dtolnay/rust-toolchain@02cb101e

Both have been in these reusable workflows since they were created
(#187 for mirror, #81 for governance), so there is no earlier clean
commit to re-pin to.

What the patch does

  • EditorConfig check — go install github.com/editorconfig-checker/editorconfig-checker/v4/cmd/editorconfig-checker@v4.0.2, then run the binary. go install verifies the module against the Go checksum database, which is a stronger guarantee than the action ever gave. The step stays advisory (continue-on-error: true), exactly as before.
  • ssh-agent (6 call sites) — start ssh-agent, ssh-add the key from the caller's secret, export SSH_AUTH_SOCK/SSH_AGENT_PID for the push steps. No third-party code involved.
  • Rust toolchain (Radicle job) — GitHub-hosted runners already ship stable Rust, so rustup default stable instead of installing another.
  • actions.lock — the two now-unreferenced dependency blocks are dropped and their entries removed from the affected workflow sections. dtolnay/rust-toolchain stays in the dependencies map because rust-ci-reusable.yml still uses it.

Verified with this repo's own .githooks/validate-actions-lock.sh:
24 SHA-pinned refs checked against 23 lockfile keys, 0 errors.

Out of scope here, but the same bug

rust-ci-reusable.yml still uses dtolnay/rust-toolchain five times, so
every consumer of that reusable is in the same state. It was left alone
because swapping it changes toolchain selection semantics for Rust repos,
which needs its own decision.

Unified diff (329 lines, applies on a41f369)
diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock
index 4b140c82..0ebc5d2d 100644
--- a/.github/workflows/actions.lock
+++ b/.github/workflows/actions.lock
@@ -66,7 +66,6 @@ workflows:
     '.github/workflows/governance-reusable.yml':
         - 'actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9'
         - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
-        - 'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393'
         - 'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124'
     '.github/workflows/governance.yml': []
     '.github/workflows/hypatia-scan-reusable.yml':
@@ -94,8 +93,6 @@ workflows:
         - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
     '.github/workflows/mirror-reusable.yml':
         - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
-        - 'dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de'
-        - 'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555'
     '.github/workflows/mirror.yml': []
     '.github/workflows/no-js-scan.yml':
         - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
@@ -225,11 +222,6 @@ dependencies:
         commit: 'sha1-02cb101ec7c40f2c49e1d9714d64511d8e1b74de'
         owner_id: 1940490
         repo_id: 260749683
-    'editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393':
-        ref: 'v3.0.0'
-        commit: 'sha1-51f63319f592f97930c73d9c46184d20bd206393'
-        owner_id: 26415196
-        repo_id: 297874902
     'erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124':
         ref: '54075bcc5e249e4758d363f27d099f55d843f124'
         commit: 'sha1-54075bcc5e249e4758d363f27d099f55d843f124'
@@ -285,8 +277,3 @@ dependencies:
         commit: 'sha1-d1b6cd204f8dceda5b3eaad7f1f767be390056cd'
         owner_id: 48932923
         repo_id: 360548653
-    'webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555':
-        ref: 'e83874834305fe9a4a2997156cb26c5de65a8555'
-        commit: 'sha1-e83874834305fe9a4a2997156cb26c5de65a8555'
-        owner_id: 135788
-        repo_id: 208510314
diff --git a/.github/workflows/governance-reusable.yml b/.github/workflows/governance-reusable.yml
index 6150c2b2..f7fe6d62 100644
--- a/.github/workflows/governance-reusable.yml
+++ b/.github/workflows/governance-reusable.yml
@@ -1054,15 +1054,33 @@ jobs:
           else
             echo "No files larger than 1M found."
           fi
+      # The editorconfig-checker ACTION cannot be used here: it is neither
+      # GitHub-owned nor a verified Marketplace creator, so the estate's
+      # Actions policy rejects it at start-up — and a rejected `uses:` takes
+      # the whole caller workflow with it, startup_failure, zero check runs
+      # (game-server-admin#103). The checker itself is a plain Go binary, so
+      # install the pinned module instead. `go install` verifies the module
+      # against the Go checksum database, which is a stronger guarantee than
+      # the action ever offered.
+      #
+      # advisory-by-design, fails OPEN not closed: formatting hygiene is
+      # reported from the reusable estate bundle; the follow-up step below
+      # emits ::warning:: explicitly when this fails instead of silently
+      # swallowing it. Repos opt into blocking formatter checks locally.
+      # hypatia: allow research_extensions/RE005 -- advisory-by-design, fails OPEN; see comment above.
       - name: EditorConfig check
         id: editorconfig_check
-        uses: editorconfig-checker/action-editorconfig-checker@51f63319f592f97930c73d9c46184d20bd206393 # v3.0.0
-        # advisory-by-design, fails OPEN not closed: formatting hygiene is
-        # reported from the reusable estate bundle; the follow-up step below
-        # emits ::warning:: explicitly when this fails instead of silently
-        # swallowing it. Repos opt into blocking formatter checks locally.
-        # hypatia: allow research_extensions/RE005 -- advisory-by-design, fails OPEN; see comment above.
         continue-on-error: true
+        env:
+          EC_VERSION: v4.0.2
+        run: |
+          set -uo pipefail
+          if ! go install \
+            "github.com/editorconfig-checker/editorconfig-checker/v4/cmd/editorconfig-checker@${EC_VERSION}"; then
+            echo "::warning::could not install editorconfig-checker ${EC_VERSION}; EditorConfig check skipped"
+            exit 0
+          fi
+          "$(go env GOPATH)/bin/editorconfig-checker" .
       - name: EditorConfig check - surface result
         if: steps.editorconfig_check.outcome == 'failure'
         run: echo "::warning::EditorConfig check found formatting issues (advisory only — see the 'EditorConfig check' step's own log above; repos opt into blocking locally when ready)."
diff --git a/.github/workflows/mirror-reusable.yml b/.github/workflows/mirror-reusable.yml
index 7338c426..f5aba0cb 100644
--- a/.github/workflows/mirror-reusable.yml
+++ b/.github/workflows/mirror-reusable.yml
@@ -82,10 +82,23 @@ jobs:
       - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
         with:
           fetch-depth: 0
-      - uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
+      # webfactory/ssh-agent is neither GitHub-owned nor a verified Marketplace
+      # creator, so the estate's Actions policy rejects it at start-up and
+      # every caller's mirror workflow dies with startup_failure
+      # (game-server-admin#103). Nothing in it is hard to do by hand: start an
+      # agent and load the key.
+      - name: Start ssh-agent
         if: ${{ env.GITLAB_KEY != '' }}
-        with:
-          ssh-private-key: ${{ secrets.GITLAB_SSH_KEY }}
+        env:
+          GITLAB_KEY: ${{ secrets.GITLAB_SSH_KEY }}
+        run: |
+          set -euo pipefail
+          eval "$(ssh-agent -s)"
+          key="$(mktemp)"
+          printf '%s\n' "$GITLAB_KEY" > "$key"
+          ssh-add "$key"
+          rm -f "$key"
+          printf 'SSH_AUTH_SOCK=%s\nSSH_AGENT_PID=%s\n' "$SSH_AUTH_SOCK" "$SSH_AGENT_PID" >> "$GITHUB_ENV"
       - name: Verify gitlab SSH host
         if: ${{ env.GITLAB_KEY != '' }}
         env:
@@ -139,10 +152,23 @@ jobs:
       - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
         with:
           fetch-depth: 0
-      - uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
+      # webfactory/ssh-agent is neither GitHub-owned nor a verified Marketplace
+      # creator, so the estate's Actions policy rejects it at start-up and
+      # every caller's mirror workflow dies with startup_failure
+      # (game-server-admin#103). Nothing in it is hard to do by hand: start an
+      # agent and load the key.
+      - name: Start ssh-agent
         if: ${{ env.BITBUCKET_KEY != '' }}
-        with:
-          ssh-private-key: ${{ secrets.BITBUCKET_SSH_KEY }}
+        env:
+          BITBUCKET_KEY: ${{ secrets.BITBUCKET_SSH_KEY }}
+        run: |
+          set -euo pipefail
+          eval "$(ssh-agent -s)"
+          key="$(mktemp)"
+          printf '%s\n' "$BITBUCKET_KEY" > "$key"
+          ssh-add "$key"
+          rm -f "$key"
+          printf 'SSH_AUTH_SOCK=%s\nSSH_AGENT_PID=%s\n' "$SSH_AUTH_SOCK" "$SSH_AGENT_PID" >> "$GITHUB_ENV"
       - name: Verify bitbucket SSH host
         if: ${{ env.BITBUCKET_KEY != '' }}
         env:
@@ -190,10 +216,23 @@ jobs:
       - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
         with:
           fetch-depth: 0
-      - uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
+      # webfactory/ssh-agent is neither GitHub-owned nor a verified Marketplace
+      # creator, so the estate's Actions policy rejects it at start-up and
+      # every caller's mirror workflow dies with startup_failure
+      # (game-server-admin#103). Nothing in it is hard to do by hand: start an
+      # agent and load the key.
+      - name: Start ssh-agent
         if: ${{ env.CODEBERG_KEY != '' }}
-        with:
-          ssh-private-key: ${{ secrets.CODEBERG_SSH_KEY }}
+        env:
+          CODEBERG_KEY: ${{ secrets.CODEBERG_SSH_KEY }}
+        run: |
+          set -euo pipefail
+          eval "$(ssh-agent -s)"
+          key="$(mktemp)"
+          printf '%s\n' "$CODEBERG_KEY" > "$key"
+          ssh-add "$key"
+          rm -f "$key"
+          printf 'SSH_AUTH_SOCK=%s\nSSH_AGENT_PID=%s\n' "$SSH_AUTH_SOCK" "$SSH_AGENT_PID" >> "$GITHUB_ENV"
       - name: Verify codeberg SSH host
         if: ${{ env.CODEBERG_KEY != '' }}
         env:
@@ -241,10 +280,23 @@ jobs:
       - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
         with:
           fetch-depth: 0
-      - uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
+      # webfactory/ssh-agent is neither GitHub-owned nor a verified Marketplace
+      # creator, so the estate's Actions policy rejects it at start-up and
+      # every caller's mirror workflow dies with startup_failure
+      # (game-server-admin#103). Nothing in it is hard to do by hand: start an
+      # agent and load the key.
+      - name: Start ssh-agent
         if: ${{ env.SOURCEHUT_KEY != '' }}
-        with:
-          ssh-private-key: ${{ secrets.SOURCEHUT_SSH_KEY }}
+        env:
+          SOURCEHUT_KEY: ${{ secrets.SOURCEHUT_SSH_KEY }}
+        run: |
+          set -euo pipefail
+          eval "$(ssh-agent -s)"
+          key="$(mktemp)"
+          printf '%s\n' "$SOURCEHUT_KEY" > "$key"
+          ssh-add "$key"
+          rm -f "$key"
+          printf 'SSH_AUTH_SOCK=%s\nSSH_AGENT_PID=%s\n' "$SSH_AUTH_SOCK" "$SSH_AGENT_PID" >> "$GITHUB_ENV"
       - name: Verify sourcehut SSH host
         if: ${{ env.SOURCEHUT_KEY != '' }}
         env:
@@ -292,10 +344,23 @@ jobs:
       - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
         with:
           fetch-depth: 0
-      - uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
+      # webfactory/ssh-agent is neither GitHub-owned nor a verified Marketplace
+      # creator, so the estate's Actions policy rejects it at start-up and
+      # every caller's mirror workflow dies with startup_failure
+      # (game-server-admin#103). Nothing in it is hard to do by hand: start an
+      # agent and load the key.
+      - name: Start ssh-agent
         if: ${{ env.DISROOT_KEY != '' }}
-        with:
-          ssh-private-key: ${{ secrets.DISROOT_SSH_KEY }}
+        env:
+          DISROOT_KEY: ${{ secrets.DISROOT_SSH_KEY }}
+        run: |
+          set -euo pipefail
+          eval "$(ssh-agent -s)"
+          key="$(mktemp)"
+          printf '%s\n' "$DISROOT_KEY" > "$key"
+          ssh-add "$key"
+          rm -f "$key"
+          printf 'SSH_AUTH_SOCK=%s\nSSH_AGENT_PID=%s\n' "$SSH_AUTH_SOCK" "$SSH_AGENT_PID" >> "$GITHUB_ENV"
       - name: Verify disroot SSH host
         if: ${{ env.DISROOT_KEY != '' }}
         env:
@@ -343,10 +408,23 @@ jobs:
       - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
         with:
           fetch-depth: 0
-      - uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
+      # webfactory/ssh-agent is neither GitHub-owned nor a verified Marketplace
+      # creator, so the estate's Actions policy rejects it at start-up and
+      # every caller's mirror workflow dies with startup_failure
+      # (game-server-admin#103). Nothing in it is hard to do by hand: start an
+      # agent and load the key.
+      - name: Start ssh-agent
         if: ${{ env.GITEA_KEY != '' }}
-        with:
-          ssh-private-key: ${{ secrets.GITEA_SSH_KEY }}
+        env:
+          GITEA_KEY: ${{ secrets.GITEA_SSH_KEY }}
+        run: |
+          set -euo pipefail
+          eval "$(ssh-agent -s)"
+          key="$(mktemp)"
+          printf '%s\n' "$GITEA_KEY" > "$key"
+          ssh-add "$key"
+          rm -f "$key"
+          printf 'SSH_AUTH_SOCK=%s\nSSH_AGENT_PID=%s\n' "$SSH_AUTH_SOCK" "$SSH_AGENT_PID" >> "$GITHUB_ENV"
       - name: Verify gitea SSH host
         if: ${{ env.GITEA_KEY != '' }}
         env:
@@ -406,11 +484,19 @@ jobs:
       # Caught 26 estate repos on the 2026-05-30 audit. The vars gate
       # answers "is Radicle mirror desired here?"; the secret gate
       # answers "are we configured to actually do it?".
-      - name: Setup Rust
+      # dtolnay/rust-toolchain is neither GitHub-owned nor a verified
+      # Marketplace creator (game-server-admin#103): its presence here
+      # startup_fails every caller of this reusable. GitHub-hosted runners
+      # already ship a stable Rust toolchain, so select that one rather than
+      # installing another.
+      - name: Select the runner's Rust toolchain
         if: ${{ env.RADICLE_KEY != '' }}
-        uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # stable
-        with:
-          toolchain: stable
+        run: |
+          set -euo pipefail
+          echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
+          rustup default stable
+          rustc --version
+          cargo --version
       - name: Install Radicle
         if: ${{ env.RADICLE_KEY != '' }}
         run: |
-- 
2.39.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaves incorrectlygovernancePolicy, rulesets, standards, compliance, and their enforcement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions