DEV Community

Shunsuke Suzuki
Shunsuke Suzuki

Posted on

How to pin GitHub Actions to a full commit length SHA across all repositories in your organization

Last weekend, the popular GitHub Action tj-actions/changed-files was compromised.

The issue was solved, but similar incidents could happen again in the future.
To prevent such issues, pinning action versions by full commit hash is recommended.

👉 GitHub Docs:

Pin actions to a full length commit SHA
Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release.
Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload.
When selecting a SHA, you should verify it is from the action's repository and not a repository fork.

This post introduces how to pin GitHub Action versions across all repositories in your organization.

pinact

You can pin GitHub Actions using a CLI tool called pinact.
It's so easy to use:

pinact run
Enter fullscreen mode Exit fullscreen mode

If you want to fix composite actions and documents, you can specify file paths.

pinact run action.yaml README.md
Enter fullscreen mode Exit fullscreen mode

You can also update actions using the -u option:

pinact run -u
Enter fullscreen mode Exit fullscreen mode

For more details, check out the pinact repository.

multi-gitter

You can apply pinact to all repositories using multi-gitter.

Create the following files:

config.yaml
run.sh
main.sh
Enter fullscreen mode Exit fullscreen mode

config.yaml:

e.g.

git-type: cmd
branch: pin-github-actions
skip-forks: true
org:
  - szksh-lab # Update with your organization
# repo:
#   - szksh-lab/test-1 # For test
labels:
  - pin-github-actions
pr-title: "ci: Pin versions of GitHub Actions to full commit hash"
pr-body: |
  ## What?

  This PR pins versions of GitHub Actions to full commit hash by pinact.
  In general, this PR doesn't change the behavior of the workflows, so you can merge this safely.

  ## Why?

  Last weekend, the popular GitHub Action tj-actions/changed-files action was compromised.

  - https://semgrep.dev/blog/2025/popular-github-action-tj-actionschanged-files-is-compromised/
  - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised

  All tags were tampered and they pointed to a revision with malicious code.

  The issue was solved, but similar issues could happen again.
  To avoid this kind of issues, we should pin versions of GitHub Actions.
  This is a common practice of GitHub Actions.

  https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions

  > Pin actions to a full length commit SHA
  > Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release.
  > Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload.
  > When selecting a SHA, you should verify it is from the action's repository and not a repository fork.

  ## How was this pull request created?

  This pull request was created by [pinact](https://github.com/suzuki-shunsuke/pinact) and [multi-gitter](https://github.com/lindell/multi-gitter).

  ## Due Date

  Please merge this pull request by 2025-03-31.
  If it's difficult, please ask at the Slack channel #sre.

  ## Contact

  If you have any question, please ask at the Slack channel #sre.
Enter fullscreen mode Exit fullscreen mode

run.sh:

#!/usr/bin/env bash

set -eu

export GITHUB_TOKEN=$(gh auth token)

multi-gitter run ./main.sh \
    --config config.yaml
Enter fullscreen mode Exit fullscreen mode

main.sh:

#!/usr/bin/env bash

set -eu

pinact run
Enter fullscreen mode Exit fullscreen mode

Make main.sh executable:

chmod +x main.sh
Enter fullscreen mode Exit fullscreen mode

Before running it across all repositories, test it on one repository:

config.yaml:

# org:
#   - szksh-lab
repo:
  - szksh-lab/test-1 # Please fix
Enter fullscreen mode Exit fullscreen mode

Run run.sh to create a pull request:

bash run.sh
Enter fullscreen mode Exit fullscreen mode

Once confirmed, apply it to all repositories.

bash run.sh
Enter fullscreen mode Exit fullscreen mode

Example Pull Request: https://github.com/szksh-lab/test-1/pull/23

For more details of multi-gitter, please see the multi-gitter repository.

Continuous Updates

You can update actions by Dependabot or Renovate.

Dependabot:

Renovate:

You can also run pinact in CI using pinact-action.

Conclusion

To improve security, you should pin GitHub Action versions to a full-length commit hash.
You can pin GitHub Actions across all repositories in your GitHub Organizations using pinact and multi-gitter.

Top comments (0)