-
-
Notifications
You must be signed in to change notification settings - Fork 32
Description
Description
When a Go module depends on two different versions of the same package (e.g., a newer version depends on an older version), cyclonedx-gomod incorrectly merges them into a single component and creates a circular dependency reference instead of creating separate component entries for each version.
Environment
- cyclonedx-gomod version: v1.9.0
- Go version: 1.25.0
- OS: macOS (also likely affects other platforms)
- Command used:
cyclonedx-gomod mod -licenses -test -output sbom.yml -output-version "1.4" -verbose=true cyclonedx-gomod mod -licenses -test -output sbom.yml -output-version "1.5" -verbose=true cyclonedx-gomod mod -licenses -test -output sbom.yml -output-version "1.6" -verbose=true
Steps to Reproduce
- Use a Go project that depends on
github.com/tedsuo/ifrit@v0.0.0-20230516164442-7862c310ad26 - Note that this version itself depends on
github.com/tedsuo/ifrit@v0.0.0-20230330192023-5cba443a66c4(older version) - Verify with
go mod graph:Output:go mod graph | grep -E "tedsuo/ifrit.*tedsuo/ifrit"
github.com/tedsuo/ifrit@v0.0.0-20230516164442-7862c310ad26 github.com/tedsuo/ifrit@v0.0.0-20230330192023-5cba443a66c4 - Run cyclonedx-gomod with various output versions (1.4, 1.5, 1.6) - all produce the same issue
- Examine the generated SBOM
Expected Behavior
The SBOM should contain:
-
Two separate component entries:
- Component for
pkg:golang/github.com/tedsuo/ifrit@v0.0.0-20230516164442-7862c310ad26 - Component for
pkg:golang/github.com/tedsuo/ifrit@v0.0.0-20230330192023-5cba443a66c4
- Component for
-
Correct dependency relationship:
<dependency ref="pkg:golang/github.com/tedsuo/ifrit@v0.0.0-20230516164442-7862c310ad26?type=module"> <!-- other dependencies --> <dependency ref="pkg:golang/github.com/tedsuo/ifrit@v0.0.0-20230330192023-5cba443a66c4?type=module"></dependency> </dependency>
Actual Behavior
The SBOM only contains:
-
One component entry (only the newer version):
- Component for
pkg:golang/github.com/tedsuo/ifrit@v0.0.0-20230516164442-7862c310ad26 - Missing component for the older version
- Component for
-
Circular dependency (self-reference):
<dependency ref="pkg:golang/github.com/tedsuo/ifrit@v0.0.0-20230516164442-7862c310ad26?type=module"> <dependency ref="pkg:golang/github.com/onsi/ginkgo/v2@v2.27.3?type=module"></dependency> <dependency ref="pkg:golang/github.com/onsi/gomega@v1.38.3?type=module"></dependency> <!-- This is a circular reference - should point to older version --> <dependency ref="pkg:golang/github.com/tedsuo/ifrit@v0.0.0-20230516164442-7862c310ad26?type=module"></dependency> <dependency ref="pkg:golang/golang.org/x/net@v0.48.0?type=module"></dependency> <dependency ref="pkg:golang/google.golang.org/grpc@v1.78.0?type=module"></dependency> </dependency>
Root Cause
The tool appears to be deduplicating packages by name only, without considering version differences. When building the dependency graph, it then creates a reference to the deduplicated component, resulting in a self-reference rather than a reference to the distinct older version.
Impact
- SBOM validation failures: SBOMs with circular dependencies may fail validation in some tools
- Inaccurate dependency graphs: Downstream tools cannot accurately analyze the true dependency tree
- Security scanning issues: Vulnerability scanners may miss issues in the older version since it's not represented as a separate component
Additional Context
This is valid Go module behavior - a package can legitimately depend on an older version of itself. Go's module system handles this correctly via Minimal Version Selection (MVS), and go mod graph correctly shows both versions as distinct nodes in the dependency graph.
The SBOM generator should preserve this version-specific dependency information rather than collapsing it into a single component.
Workaround
Currently, the only workaround is to manually edit the generated SBOM to:
- Add the missing component entry for the older version
- Update the dependency reference to point to the correct version
However, this must be redone every time the SBOM is regenerated.
Related Issues
I do not know of any related issues at this time, but this may be a common scenario for Go projects that use older versions of dependencies.