Run a dependency count on any modern web application and the number is in the hundreds, often the thousands, once you include the packages your packages depend on. You wrote a fraction of the code that ships to production.
The rest came from strangers, and you trust it with your users' data every time the build runs. Software supply chain security is how you keep using open source, which is the right call, while closing the ways it can be turned against you.
Four incidents that show how it goes wrong
Supply chain attacks are not theoretical. Each of these reached real production systems through a dependency, a build step or a script tag.
event-stream, 2018. The maintainer of a popular npm package handed it to a volunteer, who added a dependency containing code that stole cryptocurrency wallet keys. The package had millions of weekly downloads. Nobody reviewed the new dependency because nobody reviews transitive dependencies.
Log4Shell, 2021. A single flaw in a Java logging library, rated the maximum severity, sat inside countless applications whose developers had never heard of it. The hard part was not the patch. It was finding out which systems contained the library at all.
xz utils, 2024. A contributor spent years earning trust on a compression library used by most Linux distributions, then inserted a backdoor into its build process. It was caught by a developer investigating why remote logins had become slightly slower, weeks before it would have reached stable releases.
polyfill.io, 2024. A domain serving a widely used browser script was sold to a new owner, who began serving malicious code to visitors of more than 100,000 websites that loaded the script directly from the CDN. Every one of those sites was "up to date". The code simply changed underneath them.
The pattern is the same each time. Trust was placed in a name, a URL or a version number, and the thing behind it changed.
Where your dependencies really come from
Developers picture a dependency as a package they chose. In practice the supply chain has several links, and an attacker can pick any of them.
The package itself, and the maintainer's account that publishes it.
Its transitive dependencies, which you never chose and rarely look at.
Install scripts that run with your permissions the moment the package is installed, before any of your code executes.
Build tools and CI actions, which have access to your source and often to your deployment credentials.
Scripts loaded at runtime from a third-party domain, straight into your users' browsers.
Base images for containers, which bundle an entire operating system's worth of packages.
A control that covers only the first link leaves the other five open.
The controls, in the order to apply them
1. Commit a lockfile and install from it
A lockfile pins every package, including transitive ones, to an exact version and checksum. Commit it, and make CI install strictly from it so a build cannot silently pull something newer. This is the single cheapest control and the one most often skipped.
2. Audit for known vulnerabilities in CI
Package managers ship an audit command, and tools such as pip-audit, govulncheck and OWASP Dependency-Check cover other ecosystems. Run one on every pull request and fail the build on high-severity findings. Keep an allowlist with an expiry date for the ones you have assessed and accepted.
3. Automate updates in small steps
Dependabot or Renovate opens a pull request per update. Merging a handful a week, with tests passing, keeps you current without the big-bang upgrade that nobody wants to own. Review major version bumps by hand; wave minor ones through once the suite is green.
4. Produce a software bill of materials
An SBOM is a machine-readable inventory of everything in your build, usually in the CycloneDX or SPDX format. Generate it as a build artifact.
When the next Log4Shell lands, you answer "are we affected?" with a search instead of a week of archaeology. US federal buyers have required SBOMs from vendors since Executive Order 14028, and other enterprise customers are following.
5. Judge a package before adding it
Look at the number of maintainers, the release cadence, the open issue count and whether it has a security policy. The OpenSSF Scorecard automates most of that into a single score. A package with one maintainer and no activity for three years is a liability, however elegant its API.
6. Verify where packages come from
Major registries now support signed provenance, which ties a published package to the source repository and the build that produced it. Prefer packages that publish it, and check it in CI where your tooling allows. The SLSA framework describes the levels of assurance so you can decide how far to go.
7. Pin your CI actions and scope their credentials
A third-party CI action is code that runs with access to your repository and often your cloud. Pin actions to a commit hash rather than a tag, which can be moved. In March 2025 a widely used GitHub Action was modified to leak secrets from the CI logs of every workflow that referenced it by tag. Give workflows only the permissions they need, and use short-lived cloud credentials instead of long-lived keys stored as secrets.
8. Control install scripts and runtime scripts
Install with scripts disabled by default and enable them only for the packages that genuinely need them. For scripts loaded in the browser, host a copy yourself or add a subresource integrity hash so the browser refuses the file if its contents change. The polyfill.io incident would have failed harmlessly on every site that did either.
9. Keep base images small and rebuilt
Choose minimal base images, rebuild them on a schedule so operating system patches arrive, and scan the resulting image the way you scan your packages.
Making it stick
None of this works as a one-off clean-up. It works as a few lines in the CI configuration, a bot that opens pull requests, and a team agreement that red audit results block merges. The US National Institute of Standards and Technology's Secure Software Development Framework is a useful reference when you want to write that agreement down in a form a customer's security team will recognize.
It is also how we run our own projects. Our process includes lockfiles, automated updates and audits in CI for every web application we build. A client's enterprise buyer will ask how we manage third-party components, and "we have a bot and a policy" is a far better answer than "we update when something breaks".
Related reading: How to Pass an Enterprise Security Review as a SaaS Vendor
Your first step on supply chain security
Check three things in your main repository today: is the lockfile committed, does CI run an audit, and are your CI actions pinned to a hash? If the answer to any of them is no, that is the first ticket. If you want an outside review of the whole pipeline, or developers who already work this way, book a 30-minute call.








