Safeguard
Security Guides•

How do I read the dependencies block in a CycloneDX SBOM?

The CycloneDX dependencies block lists each component's direct dependencies by bom-ref. Learn to read it, and why a missing entry does not mean no dependencies.

Safeguard Security Team
4 min read

In a CycloneDX SBOM, the dependencies list holds one entry per component. Each entry has a ref and a dependsOn array of other refs. It records direct dependencies only, so you rebuild the full tree by following the links. A component missing from the list has unknown dependencies, not zero.

How the pieces connect

Every component can carry a bom-ref, an identifier that must be unique within the file. The dependencies block uses those identifiers to draw edges. A small example:

"components": [
  { "bom-ref": "app", "type": "application", "name": "shop", "version": "2.1.0" },
  { "bom-ref": "pkg:npm/express@4.18.2", "type": "library", "name": "express", "version": "4.18.2" },
  { "bom-ref": "pkg:npm/qs@6.11.0", "type": "library", "name": "qs", "version": "6.11.0" }
],
"dependencies": [
  { "ref": "app", "dependsOn": ["pkg:npm/express@4.18.2"] },
  { "ref": "pkg:npm/express@4.18.2", "dependsOn": ["pkg:npm/qs@6.11.0"] },
  { "ref": "pkg:npm/qs@6.11.0", "dependsOn": [] }
]

Read it as: shop depends on express, express depends on qs, and qs depends on nothing. The last line is the important one.

Empty versus absent

The CycloneDX 1.6 reference is explicit. Components that have no dependencies of their own must be declared as empty elements in the graph. Components not represented in the graph may have unknown dependencies. The reference recommends consumers treat that as opaque, not as a sign the component is dependency free, and suggests using compositions to indicate unknown graphs.

That gives you two states that look similar but mean different things:

PatternMeaning
"dependsOn": []The author states this component has no dependencies
No entry at allDependencies are unknown or were not recorded

The NTIA minimum elements report makes the same point from the policy side: the data should distinguish a component with no further dependencies from one where dependencies are unknown, and by default the data should be read as incomplete.

A reading routine

  1. Find the root. It is usually the component under metadata.component. Locate its entry in the dependencies list.
  2. Walk one level. List its dependsOn refs and look each one up in components.
  3. Go deeper on what matters. You do not have to walk the whole tree. Pick the components you care about, such as a web framework or a parser, and trace upward to see which direct dependency pulls them in.
  4. Check for refs that match nothing. If a dependsOn value does not match any bom-ref, the file is internally inconsistent.
  5. Count entries versus components. If there are 400 components and 6 dependency entries, the graph is thin.

Useful jq commands

List components with no dependency entry:

jq -r '[.dependencies[].ref] as $r | .components[] | select([.["bom-ref"]] | inside($r) | not) | .name' bom.json

Show direct dependencies of the root:

jq '.dependencies[] | select(.ref == "app")' bom.json

Swap app for the real root ref from the file.

What you learn from a flat graph

If every component lists the root as its only parent, the generator probably read a lock file or a package list without resolving the tree. That is still useful as an inventory, but it will not tell you why a given library is present, which makes it harder to plan a fix.

Where Safeguard fits

Safeguard provides SBOM and dependency scanning with reachability analysis, which helps separate dependencies that are actually called from ones that are only present. A free scan is at gold.safeguard.sh/scan.

Quick check

Does dependsOn list all transitive dependencies?

No. Each entry lists the direct dependencies of one component. You follow the chain to see the rest.

What is a bom-ref?

It is an identifier for a component, unique within the file, used to refer to that component elsewhere in the same SBOM. Many tools use the PURL as the value.

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.