Supply chain security sounds like a problem for organisations with a security function, and the framing is misleading. A small team running a handful of services typically depends on hundreds of packages it has never read, pulled at build time from registries it does not control, executing installation scripts on machines that hold its deployment credentials.

The exposure is not proportional to company size. It is proportional to dependency count and build automation, and small teams often have more of the first and less scrutiny of the second than the organisations that worry about this publicly.

The uncomfortable arithmetic: your application has perhaps a dozen direct dependencies and several hundred transitive ones. You chose the dozen. You did not choose the rest, you have not read any of them, and any one of them running an install script has the same access as your build process. That is the actual attack surface, and it is not visible from the dependency list you wrote.


Supply Chain Security Starts at Install Time

The dangerous moment is usually not running the code. It is installing it.

Package managers permit scripts to execute during installation, which means a compromised or malicious package runs with the privileges of whoever ran the install. On a developer machine that is their credentials. In a build pipeline it is the deployment keys, which is worse.

Three attack shapes account for most real incidents.

Typosquatting. A package with a name one character from a popular one, published and waiting for a mistyped install command. Cheap to execute and effective at scale.

Account compromise of a legitimate package. A widely used package whose maintainer’s credentials were stolen, updated with something harmful. This is the hardest to defend against because nothing about the name or the download count looks wrong.

Dependency confusion. A public package published under the name of one of your internal ones, where a misconfigured resolver prefers the public registry. Entirely a configuration problem and entirely preventable.

None of these require anyone to target you specifically, which is the point. They are opportunistic and they scale.

Controls That Repay the Effort at Small Scale

Commit the lockfile and install from it. A lockfile pins exact versions and their hashes. Installing from the manifest instead resolves fresh versions at build time, which means your build is not reproducible and an upstream change reaches production without review. Use the install command that fails when the lockfile disagrees rather than the one that updates it.

Disable install scripts where you can. Many ecosystems support this, and most packages do not need them. It is the single largest reduction in exposure available, and the packages that break are worth knowing about.

Automate vulnerability alerts, then triage. Scanners produce more findings than a small team can act on, and the failure mode is not missing alerts, it is ignoring all of them because most are irrelevant. Filter to what is reachable from your code and act on that. A queue nobody reads is worse than no queue.

Pin your build tooling too. The container image, the runtime version and the action or plugin your pipeline calls are all dependencies, and referencing a mutable tag means your build changes without you.

Separate build credentials from everything else. A pipeline that can deploy should not hold credentials that can do anything more, and a compromised build should not be able to reach production data.

What an SBOM Does and Does Not Tell You

A software bill of materials lists what is in your application, and its value is answering one question fast: are we affected by this?

That question used to take days. When a widely used component turns out to be vulnerable, an organisation with a current inventory answers in minutes and one without it spends a week searching. That difference is the whole argument, and it is why SBOM adoption is being pushed as baseline practice rather than as an advanced control.

What it does not do is make you secure. An inventory is a list, not a defence. It does not tell you whether a listed vulnerability is reachable in your code, whether it matters in your configuration, or whether anything has been compromised. Teams that generate one and file it have added a document rather than a control.

Generate it from the build, so it describes what was actually shipped rather than what the manifest claimed. Keep the one matching each release. And accept that its value is entirely in the speed of answering a question you will be asked under pressure.

Where Small Teams Actually Get Hurt

Not usually by an exotic package compromise. By ordinary things.

Secrets in the repository, which remain in history after deletion and are found by automated scanners within minutes of a push. A dependency three years out of date with a published exploit and no upgrade path, because upgrading was deferred until it became a project. Build pipelines with excessive permissions, where a pull request from a fork can run with access it should not have. Nobody watching, so a compromise persists because no alert reaches a person.

The unglamorous conclusion is that keeping dependencies current is most of supply chain security in practice. The software maintenance cost that teams treat as optional is the same work, and deferring it converts a routine upgrade into an emergency with an exploit attached.

A Proportionate Starting Point

For a small team, in order: get secrets out of the repository and rotate anything exposed. Commit lockfiles and install from them strictly. Turn on dependency alerts and triage weekly rather than continuously. Pin build tooling to immutable references. Reduce pipeline permissions to the minimum. Generate an SBOM per release once the rest is in place.

That sequence covers the realistic threats without a security function, and each step is hours rather than weeks. The controls beyond it, provenance attestation, reproducible builds, signed artefacts, are genuinely valuable and belong to a stage where the basics are already stable.

Mecanik reviews and hardens build pipelines as part of our application security work. The finding is almost never a sophisticated compromise. It is a token in a repository and a dependency nobody has updated since it was chosen.



Frequently Asked Questions

Why is installing packages the risky moment? Because package managers allow scripts to run during installation, with the privileges of whoever ran the install. On a developer machine that means their credentials; in a build pipeline it means deployment keys. The code does not have to be executed by your application to do damage.

What is dependency confusion? An attack where a public package is published under the name of one of your internal packages, and a misconfigured resolver prefers the public registry over your private one. It requires nobody to target you specifically and is entirely preventable through resolver configuration.

What does an SBOM actually give you? A fast answer to one question: are we affected by this? With a current inventory that takes minutes; without one it takes a week of searching. It does not tell you whether a listed vulnerability is reachable in your code or whether anything has been compromised, so an SBOM that is generated and filed is a document rather than a control.

Should I commit my lockfile? Yes, and install from it strictly using the command that fails when the lockfile disagrees rather than the one that updates it. Installing from the manifest resolves fresh versions at build time, which makes builds unreproducible and lets an upstream change reach production without any review.

Where should a small team start? Remove secrets from the repository and rotate anything exposed, commit lockfiles and install strictly, enable dependency alerts and triage them weekly, pin build tooling to immutable references, reduce pipeline permissions, then generate an SBOM per release. Each step is hours rather than weeks.