Software Engineering

The SBOM You Do Not Have: Inventorying a Legacy Codebase Before You Migrate

TuniCyberLabs Team
9 min read

You cannot secure or migrate what you have never listed. How to build a software bill of materials for an old system, what CycloneDX and SPDX give you, and how inventory becomes a prioritised plan.

When a serious vulnerability lands in the news, the first question is always the same: are we affected? Most organisations running a legacy system answer it with a week of archaeology and a guess. A software bill of materials turns that week into a query, and turns "the system is old" into a costed plan.

What an SBOM actually is, in plain terms

A software bill of materials is a machine-readable list of every component inside a piece of software, with versions, licences and the relationships between them. Think of the ingredients panel on food packaging, except this one has to be precise enough to answer legal and security questions while people are shouting.

A useful SBOM carries four things:

  • Components and versions, direct and transitive, because the dangerous one is usually four levels down where nobody chose it deliberately.
  • Identifiers a machine can match, such as package URLs, CPE strings or file hashes. A readable name is not enough to look a component up reliably, and "we use that logging library" is not an answer.
  • Relationships. Which component pulled in which. Without the dependency graph you cannot tell whether a flagged package is one you chose or one you inherited, and that decides whether the fix is a version bump or a redesign.
  • Licences, which matter more in a migration than teams expect, particularly when you rehost code or hand the system to a new supplier.

CISA guidance on SBOM sets out the baseline fields and terminology. Use it so your inventory matches what customers, auditors and insurers will ask for, rather than a shape you invented.

Why the systems that need it most are hardest to inventory

Legacy systems resist inventory for structural reasons, not because your team was careless. There are no lockfiles, libraries were pasted into the source tree by hand, binaries have no build script, and production was deployed manually years ago. The information is not missing, it is scattered across a filesystem and two people's memories.

  • Vendored and forked libraries. A copy of a library from a decade ago, lightly modified so it could never be updated cleanly. No package manager knows it exists, so no scanner reports it and no advisory reaches you.
  • No reproducible build. The artefact in production cannot be rebuilt from the repository, so a build-time SBOM would describe something that is not running.
  • Drift between environments. Staging and production disagree. Your inventory has to describe what runs, not what the documentation claims should run.
  • Dead code that still ships. Modules that no longer serve traffic but are still deployed, still reachable, and still vulnerable.

That fog is why legacy security estimates are unreliable, and why the starting point in The Small Business Website Security Checklist for 2026 is knowing what you actually have.

CycloneDX and SPDX, and which one to pick

Both are open standards for expressing an SBOM and both are good. CycloneDX leans toward security use cases and carries vulnerability and dependency relationship data comfortably. SPDX has deep roots in licence compliance and formal supply chain provenance. Pick one, stay consistent, convert if a customer demands the other.

  • CycloneDX is compact, has a rich tooling ecosystem, and models services and external references well. If your driver is "which components are exploitable", start here.
  • SPDX is more expressive about licensing and provenance, and is the format most often requested in formal procurement and regulated supply chain contexts.
  • Do not invent a spreadsheet format. It cannot be queried by tooling, diffed between releases, or handed to a customer who asked for an SBOM, and it rots silently.
  • Store it with the artefact. An SBOM on somebody's laptop is a document. One produced by the build and stored beside the release is an asset.

How to build the first inventory of an old system

Do it in layers, not in one heroic pass, and accept that version one will be incomplete. Generate what tooling can find, then fill the gaps by hand where generators are structurally blind. An eighty percent accurate SBOM that exists this month beats a perfect one that never ships.

1. Package managers first. Run generation against every manifest and lockfile: composer, npm, pip, maven, gradle, nuget, bundler. The cheap sixty percent, and it takes an afternoon. 2. Scan what actually runs. Analyse the deployed artefact, container image or server filesystem, not just the repository. This catches vendored libraries, hand-installed packages, and drift between what you committed and what you shipped. 3. Inventory the platform. Operating system packages, language runtime, web server, database engine, appliance firmware. These are software too, and they are where end of life bites hardest. 4. Interview the humans. An hour with the longest-serving engineer surfaces the scheduled job, the partner integration and the forgotten subdomain no scanner found. 5. Reconcile against reality. Diff the inventory against running processes, listening ports and outbound connections. Anything running that is not on your list is the most interesting thing you find all month.

Then enrich it. OWASP Dependency-Check maps components to known vulnerability identifiers, and the NIST National Vulnerability Database is the source those identifiers resolve against. Enrichment turns a list of names into a list of problems.

The things generators will always miss

SBOM tooling reports what package managers declare. It does not know about copy-pasted code, statically linked libraries, third-party JavaScript loaded in your users' browsers, the contents of a container base image, or the external APIs your system cannot function without. Those gaps are where the unpleasant surprises live.

Add these by hand, every time:

  • Front-end scripts loaded from third parties. Analytics, chat widgets, tag managers, payment scripts. They execute in your customers' browsers with your privileges, so compromising any of them compromises your checkout page.
  • Container base images, plus everything the base installed on your behalf. The application layer is often clean while the base is three years stale.
  • Build and CI dependencies. Compromise of a build tool is compromise of the product, the mechanism behind most genuine supply chain incidents. Typosquatted and confused package names are how a malicious dependency gets in.
  • External services, and where their credentials live. An SBOM plus an integration register plus a secrets map beats any one alone.

If your system sits on vendor SaaS, the same discipline applies to data and contracts, covered in Escaping SaaS: The Complete Guide to Migrating Your Business to Custom Software.

Turning inventory into a prioritised migration plan

An SBOM by itself changes nothing. Value comes from the join: components crossed with exposure crossed with evidence of real-world exploitation. That join converts "the system is old and scary" into a ranked list of specific work items in a defensible order, which is what actually unlocks budget.

  • Is it exploited in the wild? Check the CISA Known Exploited Vulnerabilities catalogue. Confirmed exploitation outranks a scarier-looking severity score nobody is using.
  • Is it reachable? Internet-facing and pre-authentication beats internal and admin-only. A critical flaw in a component that only runs during a nightly batch job is not your emergency.
  • Will it ever be fixed? If upstream is dead, patching is off the table and the item belongs in the migration plan, not the patch queue. That distinction alone reorganises most roadmaps.
  • What does replacing it cost? A dead utility library with a drop-in successor is a Tuesday afternoon. A dead framework in the request path is a quarter of engineering time.

Cross those four and the plan writes itself: dead plus reachable plus exploited first, dead plus reachable next, everything else scheduled honestly. For a public website, the sequencing work is in Leaving WordPress: A Migration Playbook That Does Not Lose Your SEO.

Keeping the inventory alive after the first pass

A one-off SBOM is out of date the day after it is generated. The version that pays for itself is produced by your build pipeline, stored with every release, and monitored continuously against new disclosures, so "are we affected" becomes a query somebody runs in a meeting rather than a project somebody scopes.

  • Generate in CI on every build, and fail the build when a component violates policy: a banned licence, an unmaintained project, a known vulnerable version.
  • Store one SBOM per release. During an incident you often need to know what was running in March, not what is running today.
  • Monitor continuously. Your inventory does not change when a new vulnerability is published, but your risk does. The monitoring, not the generation, is where the value sits.
  • Give it an owner. An SBOM with no owner rots at the speed of the documentation it replaced.

That continuous piece is what a serious maintenance arrangement should include and usually does not, a gap we pulled apart in What Website Maintenance Should Actually Include (and What You Are Probably Paying For).

What good looks like after ninety days

Ninety days is enough to move from guesswork to control without stopping feature work. You are not aiming for a perfect graph of every byte you run. You are aiming to answer three questions quickly and honestly, in front of a customer or a regulator, without a week of investigation.

  • What do we run? A current, machine-readable inventory covering application dependencies, runtimes, platform software and third-party scripts.
  • What is dangerous right now? The subset that is unsupported, reachable, and known to be exploited, ranked and assigned to named owners with dates.
  • What is the plan? A migration sequence driven by that ranking rather than by whichever system annoys the engineering team most, with cost attached to each step.

Get there and the conversation changes. You stop asking for money to rewrite an old system, and start presenting a prioritised risk reduction plan with evidence behind every line.

How TuniCyberLabs helps

We build the inventory for legacy estates that never had one: automated generation in CycloneDX or SPDX, manual discovery for what tooling cannot see, enrichment against public vulnerability data, and a ranked migration plan for your board. Then we run the migration onto supported runtimes, SBOM wired in. Engineering in Tunisia, company in Estonia, delivery across the EU and North Africa.

If you cannot answer "are we affected" in under an hour today, see how our modernisation and security engineering work fixes that.

TAGS
SBOMCycloneDXSPDXLegacy SystemsSoftware Supply ChainMigration PlanningDependency Management

Frequently Asked Questions

What is a software bill of materials?

+

A software bill of materials is a machine-readable inventory of every component inside an application, with versions, machine-matchable identifiers, licences and the dependency relationships between them. It exists so that when a vulnerability is published you can answer the question "are we affected" with a query rather than a week of investigation, and so buyers and auditors can see what they are actually receiving.

Should we use CycloneDX or SPDX?

+

Either is fine, and the choice matters far less than consistency. CycloneDX leans toward security use cases and models vulnerabilities, services and dependency relationships comfortably. SPDX is more expressive about licensing and formal provenance, and is frequently requested in procurement and regulated contexts. Pick the one matching your main driver, generate it automatically, and convert if a customer asks for the other.

Can we generate an SBOM for a system with no lockfiles?

+

Yes, but not from the repository alone. Scan the deployed artefact, container image or server filesystem, so you capture vendored libraries, hand-installed packages and platform software that no manifest describes. Then add what tooling cannot see: third-party browser scripts, base image contents, build dependencies and external integrations. The first version will be incomplete, and it is still far better than nothing.

What does an SBOM typically miss?

+

Anything a package manager did not declare. Copy-pasted code, statically linked libraries, third-party JavaScript loaded in your users browsers, the contents of a container base image, build and CI tooling, and the external APIs your system depends on. Those gaps are where most of the unpleasant surprises live, so treat manual discovery as part of the process rather than an optional extra.

How does an SBOM help a migration, not just security?

+

It converts a vague statement, that the system is old, into a ranked list of components with versions, support status, exposure and replacement cost. That ranking lets you sequence a migration by risk instead of by preference, defend the sequence to a board, and estimate the work with evidence rather than instinct. It also shows which parts are cheap wins.

How often should the SBOM be regenerated?

+

On every build, automatically, with one stored per release. Manual generation produces a document that is stale within days and owned by nobody. The ongoing value comes from continuous monitoring rather than from generation: your inventory does not change when a new vulnerability is published, but your risk does, and only monitoring tells you that in time to act.

Need help with
this topic
?

Our team specializes in the technologies and strategies discussed in this article. Let’s talk about how we can help your business.

Get in Touch