SBOMs and Supply Chain Security: Can You Trust Your Dependencies?
Your app is mostly other people's code. Here is how SBOMs, signed provenance, and intake controls tell you, in minutes, whether the next supply chain attack touches you.
By Innovation T Team
The average production app contains far more code written by strangers than by your own team. In our experience, a Node.js service with 30 direct dependencies routinely drags in over a thousand transitive packages, and almost nobody has read any of them. When the next XZ Utils or event-stream moment hits, the winners are the teams who can answer one question in minutes: are we actually running the affected version?
The dependency iceberg
You audit your code. You review pull requests. Then npm install executes arbitrary postinstall scripts from a package six levels deep in your tree, authored by someone whose account is protected by a password from 2014. That is the asymmetry at the heart of supply chain security: your controls cover the ten percent of the codebase you wrote, and your attackers know it.
Three properties make dependencies a uniquely nasty attack surface:
- Transitive invisibility. You chose
express. You did not choose the dozens of packages it brought along, and neither did anyone else on your team. Nobody made a trust decision about most of your tree. - Install-time execution. Many ecosystems run lifecycle scripts at install. Compromise one popular package and you have code execution on every developer laptop and CI runner that pulls it, before the application even starts.
- Trust inheritance. A maintainer's compromised laptop, expired email domain, or handed-over side project becomes your incident. The event-stream attack worked exactly this way: a helpful stranger asked for publish rights on an abandoned package and got them.
You cannot review your way out of this. The tree is too big and it changes weekly. What you can do is build three layers: inventory (know what you ship), verification (prove where it came from), and intake controls (slow down and sanitize what enters). SBOMs are the inventory layer, and everything else builds on top of it.
What an SBOM actually is
A Software Bill of Materials is a machine-readable inventory of every component in a specific artifact: package name, exact version, supplier, cryptographic hashes, license, and the dependency relationships between components. Not a spreadsheet. Not your package.json, which lists intentions rather than resolutions. A structured document tied to one concrete build.
Two formats dominate, and the choice is less dramatic than the internet suggests:
- SPDX (ISO/IEC 5962). The elder statesman. Strongest heritage in license compliance, broad tooling, favored across Linux Foundation ecosystems.
- CycloneDX (from OWASP, now standardized as ECMA-424). Built for security workflows. First-class support for vulnerability references, VEX, services, and hardware and ML components. If your primary use case is vulnerability response, start here.
Both serialize to JSON. Both convert to each other with acceptable fidelity. Pick one, generate it consistently, and refuse to let format debates delay the rollout.
Regulation is doing a lot of the pushing. US federal procurement has required SBOMs from vendors since Executive Order 14028, and the EU Cyber Resilience Act extends similar obligations to nearly anyone selling products with digital elements into the EU. If you sell software, SBOM clauses are arriving in your contracts whether you prepare or not. Preparing is cheaper.
Generate at build time, not on demand
The most common SBOM failure mode: generating one quarterly from a source scan and declaring the box checked. An SBOM disconnected from a specific build artifact is a guess with a file extension. The rules that matter:
- One SBOM per artifact, per build. The SBOM for
api:1.4.2describes exactly that image, resolved lockfile and all. - Scan the artifact, not just the source. A container image contains OS packages (apk, deb, rpm), language dependencies, and stray binaries someone copied in. A source scan sees none of the first category and misses the third entirely.
- Store SBOMs with the release and keep them queryable. Their value compounds when you can search across every deployed version at once.
With Syft, both generation modes are one command each:
# Source directory, lockfile-aware
syft dir:. -o cyclonedx-json > sbom.cdx.json
# Built container image, includes OS packages and binaries
syft registry:ghcr.io/acme/api:1.4.2 -o cyclonedx-json > sbom-image.cdx.json
Then attach the SBOM to the image as a signed attestation, so consumers can verify it was produced by your pipeline and not edited afterward:
cosign attest --predicate sbom-image.cdx.json \
--type cyclonedx ghcr.io/acme/api:1.4.2
Where SBOM accuracy quietly dies
Every generator has blind spots. Know yours:
- Vendored and copied code. A function lifted from a blog post or an inlined library has no package identity. It will not appear.
- Static binaries. Go binaries embed build info that tools can read. A stripped C binary is a black box with a filename.
- Bundled frontend code. After webpack or esbuild runs, the artifact is a minified blob. Generate the SBOM from the lockfile before bundling, then ship both.
- Shaded jars in the JVM world rename packages internally and defeat naive matching.
An SBOM that claims completeness it does not have is worse than no SBOM, because people trust it. Document exactly what your generation step covers and what it cannot see.
From inventory to answers
The payoff arrives when a serious CVE drops. Without SBOMs, you rebuild and rescan every service to learn whether you are exposed, which takes days you do not have. With stored SBOMs, you query documents:
grype sbom:./sbom-image.cdx.json --fail-on high
Better still: load every release SBOM into a Dependency-Track server. It continuously re-evaluates your existing inventory against new advisories, so when the next Log4Shell-class bug lands you get a list of affected services in minutes without touching a single build. That speed difference is the entire game during an incident, and it composes directly with the process work in our incident response playbook.
Expect noise. A scanner matching against an SBOM flags vulnerable versions your code never exercises. This is what VEX (Vulnerability Exploitability eXchange) exists for: a machine-readable statement, published alongside the SBOM, that records your analysis:
{
"@context": "https://openvex.dev/ns/v0.2.0",
"statements": [{
"vulnerability": { "name": "CVE-2026-XXXXX" },
"products": [{ "@id": "pkg:oci/api@sha256%3A3d1f..." }],
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path"
}]
}
Teams that skip VEX drown in findings, start ignoring the scanner, and end up worse off than before they installed it. Triage is not optional overhead. It is the product.
What an SBOM will never catch
Be honest about the limits, because vendors will not be. An SBOM is an inventory of known components. It does nothing against:
- Malicious packages with no CVE. A typosquat or a freshly backdoored release is "clean" until someone discovers it. Your SBOM will list it faithfully and raise no alarm.
- Build system compromise. SolarWinds shipped poisoned builds from a clean dependency list. The malware entered during compilation, below the layer any SBOM describes.
- Maintainer account takeover that publishes a legitimate-looking patch release through the normal channel.
Closing those gaps requires provenance and intake controls, not more inventory.
Demand provenance. Cryptographic evidence of where an artifact came from and which pipeline built it. SLSA provides the maturity model, Sigstore provides the tooling. Concretely: publish your own packages with npm publish --provenance, verify third-party images with cosign verify against the publisher's CI identity, and treat unsigned artifacts from critical suppliers as findings to escalate.
Pin by content, not by name. Names and tags are movable. Digests are not:
# GitHub Actions: pin to a commit SHA, not a tag someone can repoint
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
# Pin base images by digest
FROM node:22-slim@sha256:3d1f9a...
Slow down the intake path. Most malicious package versions are detected and yanked within days of publication. A cooldown converts the community's detection into your protection:
{
"extends": ["config:recommended"],
"minimumReleaseAge": "7 days",
"internalChecksFilter": "strict"
}
That Renovate config means a hijacked release must survive a week of public scrutiny before your bots even propose it. The cost is slightly staler dependencies. The benefit is that you stop being the early victim who discovers the compromise for everyone else.
Neutralize install scripts. Running npm ci --ignore-scripts in CI removes the most common execution primitive, at the cost of occasionally allowlisting a package that genuinely needs a build step. In our experience the tradeoff is worth taking everywhere, developer laptops included.
A 30 day rollout that actually sticks
Ordering matters here. Inventory before gating, gating before signing. Teams that start with signatures and skip lockfiles build a cathedral on sand.
- Days 1 to 3: lockfile discipline. Every repo commits lockfiles. CI installs with
npm ci,pip install --require-hashes, or the ecosystem equivalent, and fails on drift. - Days 4 to 7: generate SBOMs in CI. Add Syft to the pipelines of your five most critical services. CycloneDX JSON, one SBOM per built artifact, stored with the release.
- Days 8 to 12: stand up Dependency-Track. Ingest every SBOM automatically. Route alerts to the channel your on-call rotation actually reads.
- Days 13 to 17: baseline and triage. The first scan will be ugly. Classify every finding: exploitable now, needs a scheduled upgrade, or not affected (write the VEX statement while the analysis is fresh).
- Days 18 to 22: gate the pipeline. Fail builds on new critical vulnerabilities with known exploits. Do not gate on the historical backlog, or the team will route around the gate within a month.
- Days 23 to 26: pin and verify. SHA-pin CI actions and base image digests. Enable provenance verification for your most critical suppliers.
- Days 27 to 30: add the cooldown. Renovate or Dependabot with a minimum release age, plus
--ignore-scriptsin every CI job.
All of this lives inside your delivery pipeline, which is why it belongs to the same conversation as the rest of your controls. Our guides on the DevSecOps pipeline and CI/CD pipelines teams trust cover the surrounding machinery.
How much rigor do you actually need?
Not every team needs SLSA level 3. The framework we apply with clients:
- You sell software or APIs to enterprises or regulated buyers. Full program: per-release SBOMs, Dependency-Track, VEX, signed provenance. Procurement will demand the documents soon, and "we can generate one next quarter" loses deals.
- You run SaaS for smaller customers. SBOM generation, continuous scanning, lockfile and cooldown discipline. Defer VEX until scanner noise becomes a measurable cost.
- Internal tools, small team. Lockfiles,
osv-scannerin CI, SHA-pinned actions. Roughly an hour of setup buys most of the available risk reduction. - You merge AI-generated code heavily. Add a human gate specifically on new dependency introduction. LLMs hallucinate plausible package names, squatters register those names, and slopsquatting is now a real intake vector.
The common thread: controls on the intake path are cheap and durable. Analysis after the fact is expensive and always late. Trust in your dependency tree should be earned by evidence, never assumed, the same principle zero trust applies at the network layer.
How Innovation T can help
Innovation T builds and operates this machinery for teams across Europe and North Africa: SBOM generation wired into CI, Dependency-Track deployments that survive contact with real alert volume, signed provenance, and intake policies tuned so developers stay fast while attackers get slow. We run the 30 day rollout above as a fixed-scope engagement, then hand you a pipeline your team actually maintains.
If a customer just asked you for an SBOM, or you simply cannot answer "are we running the affected version" in under an hour, talk to us. Explore our services or contact us for a supply chain readiness assessment.
Ready to build with Innovation T?
Whether it is security, growth or engineering, our team can help you ship it well.