That's not a full commit ID, so it can still result in a mutable reference if either someone can find a clash[1] or if they can push a tag with that name and it takes priority in the context it is used (this is somewhat complex, e.g. GitHub prohibits pushes of branches and tags which are exactly 40 hex characters long, but other services may not).
[1]: https://people.kernel.org/kees/colliding-with-the-sha-prefix...
"Unable to resolve action `actions/checkout@11bd719`, the provided ref `11bd719` is the shortened version of a commit SHA, which is not supported. Please use the full commit SHA `11bd71901bbe5b1630ceea73d27597364c9af683` instead."
What if a Git commit is created that matches an existing tag? Does Git have a procedure to make a new one? e.g. imagine I pregenerate a few million 8 character tags and wait for a collision
btw: Even if you specify the full commit SHA, this can still be attacked; there have been pre-image attacks against Git commit hashes in the past. At least for older versions of Git, the algorithm was Sha1. Maybe that’s changed but an attacker could always construct a malicious repository with intentionally weak hashes with the intent of later swapping one of them. (But at that point they may as well just push the malicious code in the first place.)
Whilst Git will be default abbreviate commits to 7 characters, that's merely a default; `core.abbrev` can be set to any number to change the default display. Git will also accept any length abbreviated hashes as long as they're unique in the repo.
Also, per this comment on a previous discussion on this incident at https://news.ycombinator.com/item?id=43367987#43369710:
> the real renovate bot immediately took the exfiltration commit from the fake renovate bot and started auto-merging it (updating full SHA1 references)
They are actually releasing this very soon. I’ve seen some of my workflows use an immutable OCI image for some of GH’s actions like actions/checkout.
That protects from npm supply chain stuff, but obviously third-party includes like docker/build-push-action are still a risk.
The fact they've been stalling this for a good 2.5 years is... insane??
But the GitHub documentation [0] makes it clear that tags for major versions are intended to be mutable and be updated to point to new minor versions as they are released, not because it's "easier to read" but because you "can expect an action's patch version to include necessary critical fixes and security patches, while still remaining compatible with their existing workflows" (as long as the author follows their recommended semantic versioning scheme).
So choosing a major-version tag is GitHub's recommended practice precisely because it is mutable and does change.
[0] https://docs.github.com/en/actions/sharing-automations/creat...
It's two sides of the same coin: on one hand, an update can include fixes for bugs and vulnerabilities; on the other hand, an update can also include new bugs and vulnerabilities (or even malicious code). Updating too quickly can be risky. Updating too slowly can also be risky.
Release branches are typically the mutable reference. So I would create a 'v2' release branch, but not a 'v2' tag which gets updated.
Also, by convention, git references starting with 'v' are typically immutable tags and not branches.
But, even given the above, the git-community at large knows that tags can be mutable, and so if we care about that, we reference the sha (malicious collisions excepted).
[0] https://docs.github.com/en/actions/writing-workflows/workflo...
At first I built a workflow out of steps published on GitHub. Use ilammy/mms-dev-cmd, lukka/get-cmake, lukka/run-vcpkg, all to build a project with CMake for Windows targets. Of course I referred to actions by SHA like you should
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756
But one comment stuck with me. Something like, “You should just run your own code on GitHub Actions, rather than piecing it together from publicly available actions.” That made a lot of sense. I ended up writing a driver program for my personal project’s CI builds. One job builds the driver program, and then the next job runs the driver program to do the entire build.I wouldn’t do this if I were getting paid for it… it’s more time-consuming. But it means that I am only minimally tied to GitHub actions. I can run the build driver from my own computer easily enough.
Yes things like that have been discussed before on HN. Also for example use a justfile (or something similar) and then call that from inside the Action to reduce vendor lock-in.
1. SaaS CI/CD products, like GitHub Actions,
2. Run your own Jenkins cluster,
3. Figure out how to orchestrate cloud resources to do this for you.
Maybe there are easy options that I’m missing. I don’t really want to create docker containers just to build some program I’m working on.
Run GitHub actions self hosted (takes 2 mins to setup)
Just ssh in and run it.
So many options.
If you are doing deployments, actions etc does exactly that. Run pure bash commands or whatever.
If you want it for other purposes, you essentially want to run a "server" application but don't want to manage a server. Just use serverless? Write a JS function (or some other languages) and the platform will run it when the event triggers.
Our Action has a single step, it has an "if: false" declaration so it never runs, and no runners are engaged. This immediately completes and fires off a "workflow_job" webhook which triggers the build server to act.
I was looking at Seleniumbase recently, and they tell you that you can use Github Actions for web scraping to bypass a lot of blocks (apparently Github Actions use a residential IP-space)
https://seleniumbase.com/new-video-unlimited-free-web-scrapi...
I’ve never heard of Seleniumbase, but this makes them look like a rinky-dink project.
Your comment makes it like all attempts at hacking are nefarious. Some are in direct response to the makers being assholes and attempting to extort more money from the same sale. You want to lump all hackers into the same box, yet I want to lump all manufacturers into the same evilCorp box.
You're the one lumping them, as well as my comment, into false dichotomies.
You can also see the GitHub IP space here, I don't think it's "residential", unless that terminology includes azure and aws?: https://api.github.com/meta
The worst part of this is that this is BY DESIGN.
I maintain a small handful of actions. You are expected to, as an action maintainer DELETE and RETAG your major versions when you release a new minor or patch version. That is to say for instance your v2 tag should point to the same commit as your latest 2.x.x tag.
Not everyone does this mind you, but this is the default and the expected way of operating.
I was frankly kind of taken aback when I learned this. I know for a fact documentation of this used to exist, but I am failing to find it currently.
You can see GitHub themselves however doing exactly this here, with the v4 and v4.2.2 tags matching here (as of today, v4 will move in future)
Here @v4 is very similar to how you'd tag docker image v4-latest.
Ultimately, it should be a choice. Like you can do with package.json. Pin an exact, allow patch updates, allow minor updates or always latest etc.
Well, it's also a trade-off between security and security. If you specify an immutable commit ID, then if the dependency releases a security update you won't get it until you notice and update the commit ID. If you specify a tag, you'll get it on next build.
I guess we need Dependabot updating commitID's in workflows too? But then they'd update you to the vulnerable new @2, how would they know otherwise if it hadn't been reported yet?
You know what, no this makes perfect sense. This is exactly, perfectly in line with the modern software ethos.
Jesus. I'm so glad that 100% of my GitLab pipelines is code I wrote. It's owned by the company and it lives in our source control and runs on our hardware. I think you'd be nuts to do anything else, honestly.
For entirely related reasons, I'm thrilled that my career is moving in a direction away from devops and software in general. I can't stomach it anymore.
The whole system runs on trust that all those people do the right things. Sometimes that trust is broken. But mostly it's surprisingly fine. Part of the reason is that bad people are the exception and not the norm and all those other people react when we find one, some are mildly paranoid about this, and processes exist for flagging suspicious things (e.g. CVEs).
What we need is not to audit everything ourselves. Because that's humanly impossible. But better trust verification mechanisms and tools. Github has some mechanisms for actions but it still has some vulnerabilities. It's not perfect. But it's better than nothing. Replacing those by auditing/building yourself is going to either result in a lot of work or security with holes in it (i.e. you are moving the problem, not solving it).
You could argue that most GH Actions are simple enough that building yourself is not the end of the world. It depends on what you are doing.
I take the middleground. I use GH actions but only with widely used actions maintained by Github. Actions are just docker containers. So, the advice can be generalized to those. Check where they come from; who is building them; what their release practices are. Etc.
That’s old news. Now they have the AI do it.
Why would you not just copy the couple of lines in to your own config. It's not like you need to subscribe to updates on a command to get changed files. You want the exact opposite so your CI doesn't randomly break due to external changes.
Also, the script from the article doesn’t cover transitive GitHub Actions dependencies. So if a third-party action you’re using relies on a vulnerable action internally, it won’t catch that.
SHA `11bd71901bbe5b1630ceea73d27597364c9af683`
That means absolutely nothing to a human being Whereas
> hashicorp/setup-terraform@v3
Is easy to understand.
How about
> hashicorp/setup-terraform!v3.0.1
This version, and only this version, fails if the version cannot be found.
Or
> hashicorp/setup-terraform@v3-2025-03-26-15-01
Give me the version of the library as it was at this specific time or fail if it can't be found
Presumably it would be wise to check the SHA as well to ensure no changes have taken place maliciously
One could go old school and import the code from the various needed libraries, into the main project. Now you have complete control over what runs and it can be audited and will be in a safe state until explicit action is taken to update the code.
> SHA `11bd71901bbe5b1630ceea73d27597364c9af683` - That means absolutely nothing to a human being
> Presumably it would be wise to check the SHA as well to ensure no changes have taken place maliciously
That's exactly why the first one happens so often - I've checked the dependency at that version, and I want to make sure I only get that version, as spoofing sha's in a Git context is not part of my threat model.
SHA `11bd71901bbe5b1630ceea73d27597364c9af683
as what the script contains as a reference. While i would advocate
hashicorp/setup-terraform@v3-2025-03-26-15-01 Hash: `11bd71901bbe5b1630ceea73d27597364c9af683`
I consider it easier to deal with, .
You also cannot trust a git commit ID as an immutable reference. Even if you have a checksum available, you have to validate it.
[0]: https://docs.github.com/en/actions/security-for-github-actio...
We have some more testing to do before we cut an official release, but it is working correctly for the limited cases we have tested it with. I'd love this group's feedback.
https://github.com/testifysec/witness-run-action/tree/v1.0.1...
Imagine if we could specify both the tag and its commit, and the runner would check, at run-time, whether the specified tag is still pointing to the specified commit. This would essentially "lock" the dependency. Although storing such "locks" inline would probably be a bit too ugly, maybe we could instead collect them all and store them in a separate "file of locks", so to speak. Does anyone know if something like this has been tried before or am I just making up stupid stuff?
First, many GitHub Actions use a Docker image to do their work. This runs into basically the exact same problem in container land. So even if I pin the commit hash of an action, if that action doesn't pin the digest hash of its container image, then it's still ultimately a mutable reference.
Second, lock files don't protect against officially distributed malware when you intentionally update your dependencies. The lock file is designed to produce a repeatable build, and that does protect against a lot of supply-chain attacks when you never change anything, but you generally can't get away with never changing anything forever.
Third, commit hashes are SHA-1, and while it is exceedingly difficult to produce SHA-1 collisions through Git, it isn't impossible and it will only get easier over time. Git already technically has SHA-256 support but it's basically never used in the wild. This needs to be addressed sooner rather than later.
EDIT: The third point may not be as practically pressing as I thought. While SHA-1 is broken and shouldn't be used in new protocols, the techniques to break it faster than brute-force can be detected [1], and GitHub already rejects commits that are flagged by the detector [2]. I still think Git should more aggressively use a better hash, and should also be designed to migrate to new hash algorithms more easily in the future, but at least there are mitigations against attacks.
[1]: https://github.com/cr-marcstevens/sha1collisiondetection
[2]: https://github.blog/news-insights/company-news/sha-1-collisi...
That's the most obfuscated part for me, the globally defined actions that can belong to any organisation in Github.
In Gitlab it was at most a globally defined git repo with templates, but you could somehow understand it better.
it is as easy as running the action and writing a SQL query.
https://yeet.cx/blog/audit-actions-runner/
can also play with it using: