"GitHub's own security guidance recommends pinning actions to full commit SHAs as the only truly immutable way to consume an action"
Why doesn't GitHub just enforce immutable versioning for actions? If you don't want immutable releases, you don't get to publish an Action. They could decide to enforce this and mitigate this class of issue.
Why doesn't GitHub just enforce immutable versioning for actions?
I always wish these arguments came with a requirement to include a response to "well, what about the other side of the coin?", otherwise, you've now forced me to ask: well?
The two sides of the coin: Security wants pinned versions, like you have, so that compromises aren't pulled in. Security does not want¹ pinned versions, so that security updates are pulled in.
The trick, of course, is some solution that allows the latter without the former, that doesn't just destroy dev productivity. And remember, …there is no evil bit.
(… I need to name this Law. "The Paradox of Pinning"?)
(¹it might not be so explicitly state, but a desire to have constant updated-ness w/ security patches amounts to an argument against pinning.)
> it might not be so explicitly state, but a desire to have constant updated-ness w/ security patches amounts to an argument against pinning
When you want to update, you update the hashes too. This isn’t an issue in any other packaging ecosystem, where locking (including hashing) is a baseline expectation. The main issue is developer ergonomics, which comes back to GitHub Actions providing very poor package management primitives out of the box.
(This is the key distinction between updating and passively being updated because you have mutable pointers to package state. The latter gets confused for the former, but you almost always want the former.)
This isn't a bad distinction that you've made, I just think even lockfiles (what you're suggesting, essentially) still fall prey to the same paradox I'm suggesting.
Yes, lockfiles prevent "inadvertent" upgrades, in the sense that you get the "pinned" version in the lockfile. So if we go with the lockfile, we're now on the "pinned" side of the paradoxical coin. Yes, we no longer get auto-pwned by supply chain, but security's problem is "why are we not keeping up to date with patches?" now, since the lockfile effectively prevents them.
And then you see tooling get developed, like what Github has in the form of Dependabot, which will automatically update that lockfile. Now we're just back to the other side of the paradoxical coin, just with more steps.
(This isn't to say we shouldn't do lockfiles. Lockfiles bring a lot of other benefits, and I am generally in favor of them. But I don't think they solve this problem.)
I don’t think this is a paradox, it’s just a process. You use lockfiles to establish consistent resolutions, and then you use dependency management tooling to update those lockfiles according to various constraints/policies like compatibility, release age, known vulnerabilities, etc.
(Another framing is that you might want floating constraints for compatibility reasons, but when actually running software you basically never want dependencies changing implicitly beneath you, even if they fix things. Fixes should always be legible, whether they’re security relevant or not.)
Their question isn't about pinned versions, it's about immutable versions. The question is why it is possible to change what commit "v5" refers to, not "why would you want to write v5".
You already don't get updates pulled in with the system unless they swap the version out from under you, which is not a normal way to deploy.
Version tags should obviously be immutable, and if you want to be automatically updated you can select 1.0.*, if you don't you just pick the version tag.
It amounts to an argument against pinning in a (IMO) weird world view where the package maintainer is responsible for the security of users' systems. That feels wrong. The user should be responsible for the security of their system, and for setting their own update policy. I don't want a volunteer making decisions about when I get updates on my machine, and I'm pretty security minded. Sure, make the update available, but I'll decide when to actually install it.
In a more broad sense I think computing needs to move away from these centralised models where 'random person in Nebraska'[0] is silently doing a bunch of work for everyone, even with good intentions. Decisions should be deferred to the user as much as possible.
Auto upgrade to version deemed OK by security team. Basically you need to get updates that patch exploits then wait and be more patient for feature upgrades.
This recommendation is currently broken. Even when you pin the full commit SHA for an action, that action may still pull in transitive dependencies (other actions) that aren't pinned.
A better question perhaps is why we’ve allowed ourselves to be so vulnerable by a single provider (GitHub). Supply chain attacks would have a significantly smaller blast radius if people start using their own forges. GitHub as a social network is no longer a good idea
Even then, that's only immutable for the workflow config. Many workflows then go on to pull in mutable inputs downstream (eg: default to "latest" version).
I think that GitHub should set up Actions so that whenever you run a Github Actions step, it checks to see if either you have pinned it to a SHA or if the repository has immutable tags configured. If not, put a giant warning at the top of every pipeline run so that people are aware of the issue.
Because the true name of the feature is VisualSourceSafe actions. It's all over the code of the runner if you take a second to look, and the runner, like the rest of the feature, is of typical early 2000s Microsoft quality, which is to say, none at all.
I assume this is because it is modeled after git tags, and at this point it would be a major change to move away from this. But it should probably get started at some point.
My initial thought is that if this isn't a new compromise, Trivy must not have rotated the old credentials. They claim, however,
> We rotated secrets and tokens, but the process wasn't atomic and attackers may have been privy to refreshed tokens
… does anyone know what exactly they're talking about, here? To my knowledge, GH does not divulge new tokens after they're issued, but it depends on the exact auth type we're talking about, and GH has an absurd number of different types of tokens/keys one can use.
This is a good wake-up call (or reminder) that many “supply chain security” products are no more secure or responsibly engineered than the stacks they’re intended to protect. This is a characteristic of security software in general, but the rise of these kinds of “run us everywhere” tools/products invite new and exciting ways for an attacker to compromise large numbers of users in a single campaign.
So the first incident was on March 19th and the second incident is March 22nd —- evidently the attackers maintained persistence through maybe two separate credential rotation efforts.
Friendly reminder that just because someone is building security software it doesn't mean they are competent and won't cause more harm than good.
Every month the security team wants me to give full code or cloud access to some new scanner they want to trial. They love the fancy dashboards and lengthy reports but if I allowed just 10% of what they wanted we would be pwned on the regular...
Well, not my best 2 weeks at work, now I have to fill out a dozen forms and sit trough a shitload of meeting, just because they got pwned (twice, or once, but really badly :D )
Why do people still use others untrusted Actions, especially without hashes? Just have an LLM write whatever script you need to do it yourself using the necessary tools.
Granted, if the underlying CLI tool itself is compromised, then avoiding the associated Action won't help you.
This has always been my big "WTH?" whenever I see people using github actions. "You're literally taking someone else's script and ruining it against your codebase"
Wasn't this discovered already last week, on Friday, that the threat actor had replaced the legit images with malware images? And republished 75 out of 76 tags?
second breach in a month from the same initial credential compromise. the first rotation didn't fully revoke access. the attacker walked right back in. no persistence needed.
> This allowed the threat actor to perform authenticated operations, including force-updating tags
Hey look, infrastructure underpinning the security of thousands of products, being compromised in a way a simple setting could have prevented (Do not allow overriding tags is an old GH setting). Yet another reason we need a Software Building Code. I wonder how many more of these reasons we'll find in 2026.
83 comments
Why doesn't GitHub just enforce immutable versioning for actions? If you don't want immutable releases, you don't get to publish an Action. They could decide to enforce this and mitigate this class of issue.
>
Why doesn't GitHub just enforce immutable versioning for actions?I always wish these arguments came with a requirement to include a response to "well, what about the other side of the coin?", otherwise, you've now forced me to ask: well?
The two sides of the coin: Security wants pinned versions, like you have, so that compromises aren't pulled in. Security does not want¹ pinned versions, so that security updates are pulled in.
The trick, of course, is some solution that allows the latter without the former, that doesn't just destroy dev productivity. And remember, …there is no evil bit.
(… I need to name this Law. "The Paradox of Pinning"?)
(¹it might not be so explicitly state, but a desire to have constant updated-ness w/ security patches amounts to an argument against pinning.)
> it might not be so explicitly state, but a desire to have constant updated-ness w/ security patches amounts to an argument against pinning
When you want to update, you update the hashes too. This isn’t an issue in any other packaging ecosystem, where locking (including hashing) is a baseline expectation. The main issue is developer ergonomics, which comes back to GitHub Actions providing very poor package management primitives out of the box.
(This is the key distinction between updating and passively being updated because you have mutable pointers to package state. The latter gets confused for the former, but you almost always want the former.)
Yes, lockfiles prevent "inadvertent" upgrades, in the sense that you get the "pinned" version in the lockfile. So if we go with the lockfile, we're now on the "pinned" side of the paradoxical coin. Yes, we no longer get auto-pwned by supply chain, but security's problem is "why are we not keeping up to date with patches?" now, since the lockfile effectively prevents them.
And then you see tooling get developed, like what Github has in the form of Dependabot, which will automatically update that lockfile. Now we're just back to the other side of the paradoxical coin, just with more steps.
(This isn't to say we shouldn't do lockfiles. Lockfiles bring a lot of other benefits, and I am generally in favor of them. But I don't think they solve this problem.)
(Another framing is that you might want floating constraints for compatibility reasons, but when actually running software you basically never want dependencies changing implicitly beneath you, even if they fix things. Fixes should always be legible, whether they’re security relevant or not.)
You already don't get updates pulled in with the system unless they swap the version out from under you, which is not a normal way to deploy.
In a more broad sense I think computing needs to move away from these centralised models where 'random person in Nebraska'[0] is silently doing a bunch of work for everyone, even with good intentions. Decisions should be deferred to the user as much as possible.
[0]: https://xkcd.com/2347/
Example:
https://github.com/github-community-projects/issue-metrics/b...
> Why doesn't GitHub just enforce immutable versioning for actions?
You can't. They can execute arbitrary code. They can download another bash file via Curl and execute that.
Allowing it to be updated can also fix security problems.
It’s basically all the same arguments as static vs dynamic linking.
Plus, I believe I saw that the one action was getting the latest version of trivy anyway.
> We rotated secrets and tokens, but the process wasn't atomic and attackers may have been privy to refreshed tokens
… does anyone know what exactly they're talking about, here? To my knowledge, GH does not divulge new tokens after they're issued, but it depends on the exact auth type we're talking about, and GH has an absurd number of different types of tokens/keys one can use.
Trivy ecosystem supply chain temporarily compromised - https://news.ycombinator.com/item?id=47450142 - March 2026 (35 comments)
> On March 22, 2026, a threat actor used compromised credentials to publish a malicious Trivy v0.69.5 and v0.69.6 DockerHub images. (
https://github.com/aquasecurity/trivy/security/advisories/GH...)So the first incident was on March 19th and the second incident is March 22nd —- evidently the attackers maintained persistence through maybe two separate credential rotation efforts.
Every month the security team wants me to give full code or cloud access to some new scanner they want to trial. They love the fancy dashboards and lengthy reports but if I allowed just 10% of what they wanted we would be pwned on the regular...
Granted, if the underlying CLI tool itself is compromised, then avoiding the associated Action won't help you.
> This allowed the threat actor to perform authenticated operations, including force-updating tags
Hey look, infrastructure underpinning the security of thousands of products, being compromised in a way a simple setting could have prevented (Do not allow overriding tags is an old GH setting). Yet another reason we need a Software Building Code. I wonder how many more of these reasons we'll find in 2026.
Please don’t scare people like this!