If you followed recent news, a new supply-chain attack affected recent versions of several TanStack packages.
This article proposes a simple approach to reduce the likelihood and impact of this kind of security breach in your applications.
Summary
- What is a supply-chain attack?
- What happened?
- Why did it affect other projects?
- How version locking reduces risks
- Limitations of this solution
- Conclusion
What is a supply-chain attack?
A software package depends on multiple elements throughout its lifecycle:
Developer -> Computer -> GitHub Repository -> Package Registry (NPM)
A supply-chain attack happens when one of these elements is compromised, allowing malicious code to propagate through the rest of the chain.
In practice, this often means an attacker manages to inject malicious code into a package that many other projects depend on.
What happened?
An attacker submitted a malicious pull request targeting one of the TanStack packages.
The pull request contained hidden malicious JavaScript code. During the CI process, a GitHub Action workflow executed and the attack poisoned the GitHub Actions cache.
The malicious code was then able to capture sensitive credentials, including GitHub tokens.
Using those credentials, the attacker gained elevated permissions and published compromised package versions to NPM.
As a result, developers installing the affected versions unknowingly downloaded malicious code into their projects.
Why did it affect other projects
By default, Node.js projects usually allow dependency updates within the same major version.
For example:
npm install solid-js
This produces the following entry in package.json:
{
"dependencies": {
"solid-js": "^1.9.5"
}
}
The ^ character means:
Allow future minor and patch updates automatically.
So even if your project originally used 1.9.5, reinstalling dependencies later may install a newer version such as 1.11.2.
This can happen in many situations:
- A new developer joins the team and installs dependencies
- A CI/CD pipeline runs
npm install - A GitHub Action executes automated tests
- You reinstall dependencies after deleting
node_modules - You run
npm update
If a malicious version is published during that time window, your project may automatically fetch it.
How version locking reduces risks
The goal is simple:
If version
X.Y.Zworked yesterday, install that exact same version tomorrow.
NPM provides a --save-exact flag for this purpose:
npm install --save-exact solid-js
This produces:
{
"dependencies": {
"solid-js": "1.9.5"
}
}
Notice that the ^ is gone.
Now every npm install will use the exact same version unless you explicitly change it yourself.
You can also lock a specific version manually:
npm install --save-exact solid-js@1.8.0
This approach exists in many ecosystems.
PHP
composer require laravel/reverb:1.10.1
Ruby
gem install sidekiq -v 8.1.4
Python
poetry add requests@2.34.0
Limitations to this solution
As always in software engineering, there is no free lunch.
This approach comes with several trade-offs:
- You must review and update dependencies manually
- You should run
npm outdatedregularly to stay up to date on security fixes - Locking direct dependencies does not fully protect you from compromised transitive dependencies
- Security vulnerabilities may remain unnoticed longer if updates are delayed
In other words, version locking reduces the attack surface, but it does not eliminate supply-chain risks entirely.
Conclusion
Supply-chain attacks require active monitoring and good security practices.
Exact version locking is not a silver bullet, but it helps reduce unpredictability and limits the risk of accidentally installing newly compromised package versions.
An additional benefit is build reproducibility:
- team members use the same dependency versions
- CI environments become more predictable
- debugging becomes easier
- unexpected dependency regressions are reduced
However, this approach also increases maintenance overhead because dependency upgrades must be reviewed more carefully.
For some teams, this trade-off is worth it. For others, it may feel too restrictive.
The important part is understanding the risks and making an intentional decision rather than relying on default package manager behavior.
Top comments (0)