agentsclimarketplace

Pin github actions

Skill emaarco/hogwarts/plugins/felix-felicis/skills/pin-github-actions

A magical place where my skills, rules, and plugins for AI agents are defined, which magically boost productivity 🏰πŸͺ„

Install
npx -y skills add emaarco/hogwarts --skill pin-github-actions

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Supply-chain audit: verifies every GitHub Actions reference is pinned to a full commit SHA (not a mutable tag or branch), reports unpinned uses with evidence, and optionally rewrites them to SHA + version comment.

SKILL.md

5.7 KB, as published. Nobody here has run it

Skill: pin-github-actions

Audits a repository's GitHub Actions and verifies that every action reference is pinned to a full-length commit SHA. Mutable tags (@v4, @v4.2.2) and branches (@main) can be silently repointed by whoever controls the upstream repo β€” the tj-actions/changed-files compromise (CVE-2025-30066, March 2025) repointed existing tags to a malicious commit that dumped CI secrets; only SHA-pinned consumers were unaffected. SHA pinning is the GitHub-recommended hardening and the OpenSSF Scorecard Pinned-Dependencies check.

Run this when asked to "check if actions are pinned", harden CI supply chain, or as the GitHub-Actions slice of a release/supply-chain audit (see the sibling skill release-audit).

Pinning rule

A reference is pinned only when the ref after @ is a full 40-hex-character git commit SHA, ideally followed by a comment naming the human-readable version:

uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

(The SHA/version above only illustrates the format β€” never copy it; resolve the current SHA fresh via the Phase 3 commands.)

Reference formVerdict
owner/repo@<40-hex-sha>βœ… pinned
owner/repo/path@<40-hex-sha> (action in subdir)βœ… pinned
owner/repo/.github/workflows/x.yml@<40-hex-sha> (reusable workflow)βœ… pinned
docker://image@sha256:<digest>βœ… pinned
owner/repo@v4 / @v4.2.2 (tag)❌ mutable
owner/repo@main / @master (branch)❌ mutable
docker://image:tag❌ mutable
./.github/actions/foo (local, same repo)βœ… N/A β€” no pin needed

First-party actions/* and github/* are lower-risk but still in scope β€” pin them too (Scorecard makes no exception). If the team deliberately allows major-tag for first-party actions, record it as a documented exception, not a silent gap.

Phase 1 β€” Detect

Find every action reference across workflows and composite/local actions:

# Workflows + composite action definitions
grep -rEno '^[[:space:]]*-?[[:space:]]*uses:[[:space:]]*\S+' \
  .github/workflows .github/actions 2>/dev/null

Also scan action.yml / action.yaml anywhere in the repo (composite actions can uses: other actions).

Phase 2 β€” Classify & report

For each uses: line, strip any inline comment, take the ref after the last @, and classify it against the table above. A ref is pinned iff it matches ^[0-9a-f]{40}$ (or sha256: for docker://). Local ./ references need no pin.

Report findings as a table, sorted unpinned-first, with severity:

  • High β€” third-party action pinned to a branch (@main) or a floating tag (@v4); fully attacker-controllable.
  • Medium β€” third-party action pinned to an immutable-looking patch tag (@v4.2.2); still mutable in principle (tags can be force-moved).
  • Low β€” first-party actions/* / github/* on a tag.
  • OK / N/A β€” SHA-pinned, or local action.
File:lineReferenceCurrent refVerdictSeverity

End with a one-line summary: N references Β· M pinned Β· K unpinned.

Phase 3 β€” Fix (only when the user asks)

For each unpinned third-party reference, resolve the ref to its current SHA and rewrite in place, preserving the original version as a trailing comment:

# Resolve a tag/branch to its commit SHA
gh api repos/<owner>/<repo>/commits/<ref> --jq '.sha'
# Offline / no gh: git ls-remote https://github.com/<owner>/<repo> <ref>

Then edit the line to uses: owner/repo@<sha> # <original-ref>. Resolve the SHA from the upstream tag, not from a third-party mirror, and only after confirming the tag currently points where expected. Re-run Phase 1 to confirm zero unpinned references remain.

Phase 4 β€” Recommend automation & enforcement

SHA pins are static, so leave the repo a way to keep them current and to fail the build on regressions:

  • Keep pins fresh β€” Dependabot updates SHA pins and the version comment automatically:
    # .github/dependabot.yml
    version: 2
    updates:
      - package-ecosystem: github-actions
        directory: "/"
        schedule: { interval: weekly }
    
    (Renovate's helpers:pinGitHubActionDigests preset is the equivalent.)
  • Bulk-pin existing repos β€” pinact or ratchet pin rewrite all tags to SHAs in one pass.
  • Enforce in CI β€” fail PRs that introduce unpinned actions, e.g. zgosalvez/github-actions-ensure-sha-pinned-actions (itself SHA-pinned), or OpenSSF Scorecard's Pinned-Dependencies check.

Sources

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.