A few weeks ago I read Efron Licht’s fantastic Github and the Crime against Software and had to shake my head at the inexorable entropic collapse of modern software engineering. Then, having learned nothing, I turned around and published the TTRPG app I built to learn Rust on Github. My users are not that technical, so the repository needs Github Releases with binaries to download. To convince my users that the binaries aren’t just totally_not_virus.exe, I decided to build the binaries with Github Actions.
Things immediately get sloppy
Learning yet another CI provider’s syntax is a pain, but Github helpfully provides a starter:
Which generates the following YAML:
Running this build resulted in an interesting warning:
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/Huh? How does the official starter end up generating something deprecated? On a hunch, I asked ChatGPT to generate me a basic Github Actions CI Yaml for Rust. Sure enough, there was the outdated actions/checkout@v4.
…
Wait a damn minute, how the hell can checking out the repository - checking it out from Github using Github Actions - a CI that already has to look into the repo to see the Yaml file it’s executing! - how can the act of checking out that repository be deprecated?
Wait, why do I need to depend on an external Github Action to checkout the repository?
Wait, why does checkout have versions?
Wait, why is checking out the repository pulling in a node runtime???
Why checking out the repository is pulling in a node runtime
If you were naive like me, you might expect Github Actions to be reusable snippets of YAML. Define your shell script once and let others reuse it elsewhere; Gitlab’s CI lets you do this. Actually even Github Actions lets you do this!
Unfortunately, Github Actions you can pull in as dependencies are not repurposed YAML templates. These are full programs, invariably existing to replace a 1-2 line snippet of shell code. Actions/checkout - the official way to checkout your repository for Github Actions - has seven thousand lines of typescript.
I needed to create Github Releases for my app. The solution I went with was to use the Github CLI -
run: |
gh release create "${{ github.ref_name }}" --generate-notesDo not copy! This was only not vulnerable to script injection because the ref_name was a tag that only I could push. I’m not even writing about script injection because everything else seems so much worse.
But why use Github’s CLI when I can instead add a unvetted external dependency to a node.js app to do this for me? There’s no shortage of candidates - while the official create-release Action is no longer maintained, it helpfully links you to four other security liabilities.

Interestingly, the size of actions/checkout is not that much of an outlier. Woodpecker CI’s plugin-git has 1624 lines of Go; the git-plugin for Jenkins has 52883 lines of Java! Gitlab CI’s git integration will probably also be large.
But Woodpecker and Gitlab-CI don’t end up failing on this front, because:
Why would you need an external action to checkout a repository?
Getting your code checked out just works with Gitlab-CI and Woodpecker. These competitors have unsurprisingly identified this as a requisite core feature for a CI platform. Users can configure the checkout strategy, but the code for checking out the repository is just part of the CI. It is not possible to pull in a deprecated version of Gitlab CI’s GIT_STRATEGY because why, why would you ever make that even an option are you crazy?
While you’re at it, why not make checking out your code also result in surprise side-effects that make your pipeline vulnerable? Wait, I was joking, please don’t actually
actions/checkout also adds git authentication to all git calls for the rest of the job
Git checkout should not have to mean “attackers get to use my credentials to control my build pipeline”, but here we are. To be fair, Github Actions can be configured in a way to lower the permissions of the leaked key. Unfortunately, Github Actions also have an option that allow attackers to use trusted PRs to execute arbitrary Actions on your repository with a pwn-request attack. In 2021, Github Security Labs four parter(!) on Github Actions security vulnerabilities discussed this vulnerability exclusively in part one. Clearly some security engineers were concerned!
This concern did not extend to the naming of this extremely dangerous option. Quick quiz - one of these workflow triggers will be almost ubiquitous, one of them will expose you to a pwn attack. Which is it?
This is just security malpractice. Hope whoever wrote any given repo’s CI realized that this was a danger and didn’t accidentally write in the “pwn me plz” option.
Supply Chain Attacks on Github Actions are even more dangerous
Supply chain attacks are getting worse. The numbers will depend on any given study’s methodology, but numbers like a 73% increase from 2025 seem common. Supply chain attacks are currently number 3 in OWASP’s Top Ten of 2025, where
[Supply chain security] was top-ranked in the Top 10 community survey with exactly 50% respondents ranking it #1.
Supply chain attacks inside a CI pipeline are particularly juicy. CI/CD machines are running with elevated privileges, are stuffed with secrets, and have access to production. Moreover, they’re running in an area that SAST and dependency scanners are typically blind to, and that many developers don’t take seriously.
At least some Supply Chain Attacks can be mitigated by controlling the package manager and runners. But who controls them? Github - at this point clearly not a trustworthy security partner. There’s simply no straightforward way for me, a user, to verify or audit what Github does. Conversely, hacker groups like TeamPCP have already exfiltrated Github’s source code and can actively search for exploits.
Andrew Nesbitt has an great analysis of Github Actions’ package manager as well as some of the relevant security studies pertaining to Github Actions, it’s excellent and a quick read. The takeaway is sobering:
Nearly every GitHub Actions user is running third-party code with no verification, no lockfile, and no visibility into what that code depends on.
Conclusion - CI is an afterthought
Opting in to supply chain attacks on the machines you use to push to production is something to think twice about. Unfortunately, John-Mary Coder building another CRUD App at GeneriCo doesn’t even think once about it. Even otherwise fit teams seem to turn their brains off when it comes to tasks that seem like unusual one-offs, such as building their CI pipelines. When that’s the reality on the ground, the damage caused by offering an unsafe-by-default CI offering is immense.
I’ve removed Github Actions from my hobby project, but that’s small beans. If you’re using Github Actions for something more important, think seriously about moving to an alternative. Thankfully, as much of a pain in the ass as every script-wrapping YAML CI is, that’s all they’re doing at the end of the day. There are alternatives out there, it’s not rocket science. At least not usually.



