Urgent.News

What's breaking now, across thousands of outlets.

Tech

A GitHub Actions tag is a promise, not a fact: pinning by SHA the right way

When you write uses: actions/checkout@v4 in a GitHub Actions workflow, you are not pinning anything. You are trusting a promise. A tag is a movable pointer. Whoever owns that repository can repoint v4 at a different commit tomorrow, after you reviewed it, and every run of your pipeline will silently pull the new code. In a job that holds a registry login, a PyPI token, or a signing key, that is…

Abstract editorial illustration

When you specify a GitHub Actions tag in a workflow, like `uses: actions/checkout@v4`, you are not actually pinning anything. Instead, you are relying on a promise. A tag is essentially a movable pointer, which can be repointed by the repository owner to a different commit at any time after you've reviewed it. This means that every run of your pipeline could potentially pull in new code, which isn't ideal if you're using sensitive tools like a registry login, PyPI token, or signing key.

However, using a SHA (a unique identifier for a specific commit) instead of a tag offers a different level of security. A SHA can only ever refer to one exact tree of code, so even if the upstream repository is compromised and someone force-pushes malicious changes, your pinned SHA will continue to run the code you've audited. This is the core of the argument for pinning with SHA, backed by real-life supply-chain incidents where tag-repointing has been used maliciously.

To pin a SHA, you need to resolve it yourself from the source repository. Simply copying it from a table isn't enough. For each action, you should resolve the SHA against the source of truth using a command like `gh api repos/docker/login-action/commits/v4 --jq .sha`. This ensures that you've verified the code you're using. Once you've done this for all your actions, you'll have a clear record of what's running, rather than a promise that could change on you.

However, there's a catch when it comes to Dependabot. Once you pin an action to a SHA, the human readability goes away. Dependabot, which helps keep dependencies up-to-date, looks for a `# vX` comment to know when to offer version bumps. If you drop this comment, Dependabot will stop notifying you about updates. This is a trade-off: while you gain security by pinning, you also take on the responsibility of manually updating the action when new versions are released.

Not all references are as straightforward as version tags. Some actions, like `pypa/gh-action-pypi-publish@release/v1`, use a branch for continuous fixes. In such cases, pinning to a SHA is still valid, but you need to decide when to move the pin forward. You can keep the `# release/v1` comment to let Dependabot keep nudging you about updates, and you should document your decision to accept this responsibility.

The key takeaway is that pinning actions by SHA is a simple one-line change per use, but it brings significant value. It requires three habits: resolving every SHA yourself, keeping the `# vX` comment for Dependabot, and naming moving-branch pins. By preferring these facts over tags in jobs that handle credentials, you're adopting a more secure approach. This practice was applied in a real project as shown in thingctx#127, and it's worth doing as an audit if you're managing workflows with sensitive jobs.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

The Blind Leading the Blind: Chasing a Production Bug

This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry . Around 2016, I was working as a PHP developer at Racing Post in London.

  • PHP developer at Racing Post in London faced intermittent production failures in 2016
  • PHP 5 and Sybase used for database communication, native extension written in C
  • Extension swallowed native errors, converting failures into empty result sets

More from Sunday 2 August →