If you have been paying attention to the tech headlines this week, you likely saw that a popular JavaScript library axios was compromised by a supply chain attack. The attacker definitely knew what they were doing, hiding their scripts behind innocent looking names.
This has been the latest in a slew of supply chain attacks this year. As a software developer, it’s important to understand how these attacks impact your projects and that you take precautions to limit your vulnerability. The greatest likelihood is that these supply chain attacks will get much more frequent and worse as LLMs become increasingly more sophisticated. So what’s a developer to do?
Minimum Release Age
Probably the simplest thing you can do to limit your exposure is to set a minimum release age requirement via your favorite package manager. For example, in the JavaScript ecosystem you can set:
# using bun, set in bunfig.toml
[install]
minimumReleaseAge = 604800 # 7 days in seconds
# using pnpm, set in pnpm-workspace.yaml
minimumReleaseAge: 10080 # 7 days in minutes
# using npm, set in .npmrc (comments go on their own line)
# 7 days in minutes
min-release-age=10080
depsguard (authored by DevSec company Arnica) is a very simple tool I recommend to help you configure all this in one shot.
Dependency Pinning
When you add a dependency to your project, the current version is written to your project manifest. By default the package manager uses a version range, usually represented by a caret character ^. A caret range signifies to the package manager it is safe to install the latest minor or patch version of the dependency within the same major version.
For example, if your package.json contains a dependency acme@^2.1.10 the package manager will install the most recent version up to but not including v3.0.0. So installable versions include v2.1.11, v2.2.0, v2.9.3 and so on. Even the narrower tilde range (~2.1.10), which stops before v2.2.0, still accepts any new patch release.
This is a problem if there is a vulnerability in one of those versions. Your package manager will blindly install because semantic version ranges are primarily concerned with managing compatibility of the dependency, not protecting you from malicious code.
Version pinning removes version ranges to “pin” your project to a known version. Simply remove the caret from your project manifest to specify an exact dependency version for your project to use. This also ensures compatibility of the dependency to a version you have been actively developing and/or testing with.
The biggest downside to version pinning is that upgrading dependencies becomes a manual effort. Your dependencies become stale quicker and require more time from you to keep upstream patches integrated into your project.
To mitigate this, I recommend using an automated dependency tool like renovate or GitHub’s dependabot.
No Silver Bullet
Unfortunately, there is no single best way to avoid supply chain attacks. But by taking some of the above precautions, you can greatly reduce the likelihood of being hacked.
Stay vigilant out there!