anchore / anchore/syft

CycloneDX output omits ownership-by-file-overlap relationships preserved in SPDX, leading to false-positive Grype matches

Open
#4,895 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
9.6k
Forks
954
Avg merge
23h 27m
Merged PRs (30d)
48

Description

## Summary

When generating an SBOM for an RPM-based container image (e.g. `redhat/ubi9`), Syft's SPDX-JSON output preserves `ownership-by-file-overlap` relationships between distro RPMs (e.g. `python3-requests`) and the language-ecosystem packages they bundle (e.g. `requests`). The CycloneDX-JSON output does not preserve these relationships — they are absent from `dependencies[]`, component `pedigree`/`evidence`, and component `properties`.

Grype consumers of the resulting CycloneDX SBOM produce extra Python-ecosystem GHSA matches that are *not* produced when scanning the same image via SPDX-JSON or by direct image scan. The matches are likely false positives for packages that the distro vendor is responsible for patching.

## Reproduction

```bash
# Generate both SBOMs
syft redhat/ubi9 -o spdx-json=ubi9.spdx.json -o cyclonedx-json=ubi9.cdx.json -q

# Scan each via Grype
grype sbom:ubi9.spdx.json -o json --quiet > ubi9.spdx-results.json
grype sbom:ubi9.cdx.json -o json --quiet > ubi9.cdx-results.json
grype redhat/ubi9 -o json --quiet > ubi9.direct-results.json
```

## Observed

| Source | Match rows for `{idna, requests, setuptools, urllib3}` |
|---|---|
| SPDX-JSON SBOM | 0 |
| CycloneDX-JSON SBOM | **15** |
| Direct image scan | 0 |

All 15 CycloneDX-only matches go through `matcher: python-matcher`, `type: exact-direct-match`. They are present only when Grype reads the CycloneDX-format SBOM.

## Investigation

The SPDX SBOM contains explicit ownership relationships:

```json
{
"spdxElementId": "SPDXRef-Package-rpm-python3-requests-...",
"relatedSpdxElement": "SPDXRef-Package-python-requests-...",
"relationshipType": "OTHER",
"comment": "ownership-by-file-overlap: indicates that the parent package claims ownership of a child package since the parent metadata indicates overlap with a location that a cataloger found the child package by"
}
```

There are four such relationships in this SBOM, one per `python3-*` RPM that ships a Python package.

The CycloneDX SBOM has no equivalent: the string `ownership-by-file-overlap` does not appear anywhere. `dependencies[]` only models the Python-side dependency graph (e.g. `requests` -> `idna`, `urllib3`, etc.), not the RPM -> Python ownership relationship.

## Environment

- syft v1.44.0 (windows/amd64)
- grype v0.112.0 (windows/amd64)
- Image: `redhat/ubi9:latest` at time of test

## Notes

- I don't know whether CycloneDX 1.6 has a natural representation for this kind of \"parent RPM owns child language-ecosystem package\" relationship. `dependencies[]` only models \"dependsOn\" semantics, and component-level fields like `pedigree` are about origin/lineage rather than ownership. Possibly this needs a property convention or a different encoding choice.
- Haven't verified whether the same asymmetry exists for non-Python language ecosystems bundled in distro packages (`gem`, `npm`, etc.).
- This is distinct from #2911 (which is about the `EXCLUDE_BINARY_OVERLAP_BY_OWNERSHIP` toggle, a separate concern).
- This was found while validating a separate CycloneDX fix (#4791); happy to share additional reproduction artifacts on request.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the issue with the listed syft and grype commands against redhat/ubi9, then compare the SPDX-JSON and CycloneDX-JSON relationship data. Trace the existing SPDX and CycloneDX SBOM generation paths and determine how ownership-by-file-overlap can be represented in CycloneDX. Done means the relationship is preserved without introducing the CycloneDX-only false-positive matches.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
devtools, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.