Safeguard
Security Guides•

SPDX DEPENDS_ON, DEPENDENCY_OF or CONTAINS: which relationship fits?

DEPENDS_ON points from consumer to need, DEPENDENCY_OF reverses it, and CONTAINS describes packaging. How to choose, using the spec's definitions.

Safeguard Security Team
4 min read

Use DEPENDS_ON when package A needs package B to build and run, and DEPENDENCY_OF for the same fact written in the other direction. Use CONTAINS when one artifact physically holds another, like an archive holding a file. Mixing them up changes what a reader thinks your software actually needs.

What the spec says

SPDX 2.3.1 lists relationship types in a table. The relevant ones are quoted here in short.

RelationshipMeaning in the spec
DEPENDS_ONA depends on B, which A needs in order to build and run
DEPENDENCY_OFA is a dependency of B, explicitly stated in a machine-readable file; use when a package manager does not define scopes
CONTAINSA contains B, such as an archive containing a source file
DESCRIBESThe document describes A

Note that CONTAINS in the spec's example is about packaging, an ARCHIVE file bar.tgz containing a SOURCE file foo.c. It is not a statement about a dependency.

Direction matters

Every relationship line has a left element, a type and a right element.

Relationship: SPDXRef-app DEPENDS_ON SPDXRef-lodash
Relationship: SPDXRef-lodash DEPENDENCY_OF SPDXRef-app

Both lines say the same thing. Pick one style per document so your tooling does not have to guess. Reversed direction is a common bug, where a document claims a library depends on the application.

When to choose which

Use DEPENDS_ON for a library your code imports and a container image needs at run time. Use DEPENDENCY_OF when you are translating a manifest where the dependency is listed under its parent and no scope information is available. Use CONTAINS when you describe what is inside an archive, image layer or firmware bundle, where the inner element came along as part of the package rather than being declared as a dependency.

A shaded or vendored library is a good test. It is physically inside your artifact, so CONTAINS describes it truthfully. If it also came from a manifest, you may record DEPENDS_ON too.

Missing relationships are not statements of absence

The spec is careful here. NOASSERTION on the right side means the author is not asserting whether other connected elements exist. NONE means no other elements are connected. Neither is mandatory, and if no relationship of a given type is stated, the author is not presumed to be asserting anything about it.

So a package with no outgoing DEPENDS_ON lines is not proven to have no dependencies. Treat it as unknown unless NONE is stated.

A practical check

Take one service. List its top five direct dependencies from the lockfile, then confirm each shows up as a relationship from the root package in the SPDX file. If any are missing, the graph is incomplete, and any blast radius search built on it will be too.

Safeguard can generate SBOMs and run dependency scanning across your repositories, so the fields discussed here are checked on real projects instead of by hand. You can try the free scan at gold.safeguard.sh/scan.

Try it yourself

Open the demo, click through the component list, and look at how each package is identified and what is missing.

Click through it at your own pace, or open it full screen.

Quick check

Is CONTAINS the same as a dependency?

No. In the spec CONTAINS describes physical packaging, such as an archive holding a file. DEPENDS_ON describes a build or run dependency.

Does a package with no DEPENDS_ON lines have no dependencies?

Not necessarily. The spec says absent relationships are not an assertion either way. Only NONE states there are none.

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.