Skip to content

fix(conda): Add prefix to owned files list for environments not in scan root - #5225

Open
marcoesters wants to merge 9 commits into
anchore:mainfrom
marcoesters:fix-conda-file-ownership-paths
Open

fix(conda): Add prefix to owned files list for environments not in scan root#5225
marcoesters wants to merge 9 commits into
anchore:mainfrom
marcoesters:fix-conda-file-ownership-paths

Conversation

@marcoesters

@marcoesters marcoesters commented Aug 24, 2026

Copy link
Copy Markdown

Description

Problem description

The conda cataloger reads the list of owned files from conda metadata files. These file paths are taken to determine ownership overlap. The files paths in the metadata are stored relative to the environment location (prefix), and syft currently does not prepend the environment location. This leads to different SBOMs depending on whether the environment is in the root directory of the file system or image scan.

To reproduce using syft 1.51.0:

  1. Create a conda environment: conda create -p /tmp/opt/testenv python
  2. Run syft scan /tmp/opt/ -o spdx-json@2.3, which will result in the following SBOM: sbom.opt.json
  3. Run syft scan /tmp/opt/testenv, which will result in the following, much larger SBOM: sbom.testenv.json

The latter contains relationships between the conda package, which are missing in the scan of /tmp/opt:

{
  "relationships": [
    {
      "spdxElementId": "SPDXRef-Package-conda-python-0372d4f949da77f2",
      "relatedSpdxElement": "SPDXRef-Package-binary-python-cf5530976b920e28",
      "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"
    }
  ]
}

Proposed solution

Add the environment location (Prefix) to the CondaMetaPackage struct. The environment location is always two directories above the metadata JSON file. The prefix is then used in the OwnedFiles() method to construct the full path relative to the scan root directory.

I chose this over fixing the paths in the Files slice directly because it is the most flexible approach. Adding the prefix to all path types in conda metadata may not always be the correct approach, so keeping the prefix separate allows us to add it on a case by case basis. The downside is that this the prefix is not an actual part of conda's metadata and requires a new schema version just for a helper property.

Using the code in this PR to scan the test environment yields the following SBOM: sbom.new.json

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • I have added unit tests that cover changed behavior
  • I have tested my code in common scenarios and confirmed there are no regressions
  • I have added comments to my code, particularly in hard-to-understand sections

Signed-off-by: Marco Esters <mesters@anaconda.com>
Signed-off-by: Marco Esters <mesters@anaconda.com>
Signed-off-by: Marco Esters <mesters@anaconda.com>
@oss-housekeeper

Copy link
Copy Markdown

Schema Change Detection

New Schemas

  • schema/json/schema-16.1.11.json

Signed-off-by: Marco Esters <mesters@anaconda.com>
@marcoesters
marcoesters force-pushed the fix-conda-file-ownership-paths branch from 866954d to 31bd46e Compare August 24, 2026 21:20
@marcoesters
marcoesters marked this pull request as ready for review August 24, 2026 21:23
@CAOShurong

Copy link
Copy Markdown
Contributor

Exact-head verification (head 31bd46e9, go1.26.7, windows-amd64)

  • go build ./... ✅ · go vet ./syft/pkg/... ./internal/relationship/...
  • New relationship case conda-owns-python-package-by-dist-info passes: go test ./internal/relationship/... → ok
  • go test ./syft/pkg/cataloger/conda/... fails at this head, but fails identically on origin/main (2293641e): both failures (multiple_packages_in_conda_meta, badly_formatted_conda_meta_json_file) are pre-existing Windows path-separator mismatches between the test expectations (forward slashes) and RealPath values (backslashes). Not introduced by this PR — just noting that these suites can't show green locally on Windows.

The core fix direction is sound, and the reported symptom reproduces logically from the code path: on main, OwnedFiles() returns env-relative paths that only match child-package realpaths when the env happens to sit at the scan root. Findings below.

1. SchemaVer classification looks incorrect

Adding "Prefix" to the required array of the CondaMetaPackage definition means a document that validated against schema-16.1.10.json (no Prefix key anywhere) will fail validation against the new schema-16.1.11.json. Per schema/json/README.md:

  • MODEL: … breaking schema change …
  • REVISION: increment when you make a schema change which may prevent interaction with some historical data
  • ADDITION: increment when you make a schema change that is compatible with all historical data

A newly-required property is REVISION territory (16.2.0), not 16.1.11. Two ways out:

  • drop "Prefix" from required → the change genuinely becomes ADDITION-compatible and 16.1.11 stands; or
  • keep it required and re-version as 16.2.0.

Since the cataloger itself always populates the field, dropping it from required costs almost nothing and keeps the cheaper version bump.

2. JSON key casing will be frozen once published

Sibling metadata fields serialize lowercase (json:"md5,omitempty", json:"sha256,omitempty"), but Prefix string has no struct tag, so it emits as "Prefix" — a mixed-case key in the public SBOM surface. Schema versions are never edited after release, so this is the last cheap moment to decide: json:"prefix" (kept in required, or optional per point 1) seems more consistent with the rest of CondaMetaPackage.

3. Comment is still garbled after the "fix typo" commit

// Record the location of the conda environment so that paths
// because files are stored relative to the environment location,
// not relative syft's scan root.

The first line dangles (“so that paths …”), and “relative syft's” is missing “to”. Suggested:

// Record the location of the conda environment, because files are
// stored relative to the environment location, not relative to
// syft's scan root.

4. Minor: path.Join silently mangles unusual Files entries

path.Join(m.Prefix, f) cleans .. segments and turns an absolute entry into <prefix>/C:/.... conda info/files entries are relative-clean in practice, so this is likely theoretical — a leading-separator / IsAbs guard would make it explicit.

Nice find overall — the prefix-vs-scan-root divergence is exactly the kind of ownership-overlap gap that silently drops relationships from SBOMs.

Signed-off-by: Marco Esters <mesters@anaconda.com>
Signed-off-by: Marco Esters <mesters@anaconda.com>
Signed-off-by: Marco Esters <mesters@anaconda.com>
Signed-off-by: Marco Esters <mesters@anaconda.com>
@marcoesters

Copy link
Copy Markdown
Author

Thank you for the review! I think in the end Prefix, is not needed in the schema since this is just a helper property and not part of the metadata file. If it must be in the schema, I will mark it as optional with lower-case prefix, like you suggested. The tests pass without adding Prefix to the schema, but please let me know that the preferred course of action is.

I think I addressed point 3 and 4.

Signed-off-by: Marco Esters <mesters@anaconda.com>
@marcoesters
marcoesters force-pushed the fix-conda-file-ownership-paths branch from 42acb78 to 7396760 Compare August 25, 2026 03:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants