Safeguard
Supply Chain•

How does an SPDX SBOM say which component depends on which?

SPDX records dependencies as relationship lines such as DEPENDS_ON and CONTAINS. How to read them, the main types, and the mistakes that break the graph.

Safeguard Security Team
3 min read

An SPDX SBOM records dependencies as relationship lines in the form A, a relationship type, B. DEPENDS_ON says A needs B to build or run, CONTAINS says B sits inside A, and DESCRIBES links the document to its main subject. Without these lines you have a list, not a graph.

The syntax

Each relationship is three parts: a left identifier, a type keyword, and a right target. Either side can point to another document with a DocumentRef prefix. The right side can also be NONE, meaning no related elements exist, or NOASSERTION, meaning the author makes no claim.

Example from the specification: SPDXRef-grep CONTAINS SPDXRef-make.

The types you will meet most

TypePlain meaning
DESCRIBES, DESCRIBED_BYTies the document to what it documents
DEPENDS_ONThe element needs another to build or run
CONTAINS, CONTAINED_BYOne element is nested in another, like a file in an archive
BUILD_DEPENDENCY_OFA dependency in build scope, such as Maven compile
DEV_DEPENDENCY_OFA dependency used only in development
GENERATED_FROMA binary built from a source file, for example

The scope types matter for noise. A DEV_DEPENDENCY_OF entry tells a reader a package never reaches production, which is a reason to deprioritize its advisories.

Read one in four steps

  1. Find the DESCRIBES line. It names the root, the thing the SBOM is about. The specification makes it mandatory when more than one package or set of files is present.
  2. List every DEPENDS_ON line from the root. Those are your direct dependencies.
  3. Follow each target's own lines to reach transitive ones.
  4. Check scope types to separate build only and dev only packages from runtime ones.

What breaks the graph

  • A package listed with no relationship at all. A tool cannot tell if it is a dependency or a leftover.
  • A missing DESCRIBES line in a multi package file, so the root is unclear.
  • Using CONTAINS where DEPENDS_ON is meant. Containment is about packaging, dependency is about need.
  • Treating NOASSERTION as a statement of no dependencies. It means the author did not say.

A small example helps. Suppose a service describes itself, depends on a web framework, and the framework depends on a parser. The file needs a DESCRIBES line for the service, a DEPENDS_ON line from the service to the framework, and a DEPENDS_ON line from the framework to the parser. Remove the middle line and the parser looks like an orphan, even though it ships. Scanners then report it without a parent, and nobody can tell which upgrade would remove it.

Quick check

What is the difference between DEPENDS_ON and CONTAINS?

DEPENDS_ON is about needing another element to build or run. CONTAINS is about one element being nested inside another.

Does NONE mean the same as NOASSERTION?

No. NONE says there are no related elements. NOASSERTION says the author makes no claim either way.

Safeguard's SBOM and dependency scanning builds a dependency graph from SBOM data, and a free scan is available at gold.safeguard.sh/scan.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.