Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

# CaPyCli - Clearing Automation Python Command Line Tool for SW360

## NEXT

* `bom map` will provide the `purl` from SW360 in the output BOM's components
(due to a missing code path, PURL from input BOM was copied to mapping result instead)

## 2.9.0

* drop support for Python 3.8, so we can update urllib3 to fix CVE-2025-50181 and -50182.
Expand Down
5 changes: 0 additions & 5 deletions Readme_Mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,6 @@ also by the `package-url`s of their releases.

PackageURL subpath and qualifiers are currently ignored during PURL matching.

Note that we consider PackageURLs as *unique identifier*, so if two releases or
components have the *same* external ID, a warning will be printed and the
external ids will be completely ignored and mapping will continue with the
other search steps like by file hash, by name and version etc.

## Example 1: Very Simple, Full Match

The input SBOM contains two releases:
Expand Down
11 changes: 5 additions & 6 deletions capycli/bom/map_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,12 +515,11 @@ def update_bom_item(self, component: Optional[Component], match: Dict[str, Any])
if version:
component.version = version

value_match = match.get("RepositoryId", "")
if not value_match.startswith("pkg:"):
value_match = None

if value_match:
component.purl = PackageURL.from_string(value_match)
try:
# take over purl from match to SBOM if valid
component.purl = PackageURL.from_string(purl)
except ValueError:
pass

# update if current is empty
value_match = match.get("Language", "")
Expand Down
2 changes: 1 addition & 1 deletion capycli/common/component_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def convert_release_details(cls, client: sw360.SW360, details: Dict[str, Any]) -
if "externalIds" in details:
release["ExternalIds"] = details["externalIds"]
else:
release["ExternalIds"] = []
release["ExternalIds"] = {}
release["CreatedOn"] = cls.get_value_or_default(details, "createdOn")
release["MainlineState"] = cls.get_value_or_default(
details, "mainlineState"
Expand Down
8 changes: 2 additions & 6 deletions capycli/common/purl_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,8 @@ def get_by_version(self, purl: PackageURL) -> List[Dict[str, Any]]:
"""
Retrieve entries by version from the cache.

If qualifiers are provided in the PackageURL, only entries matching
all qualifiers will be returned. If no qualifiers match, the method
falls back to returning all entries for the specified version.

:param purl: The PackageURL object containing type, namespace, name, version, and qualifiers.
:return: A list of matching entries or None if no matches are found.
:param purl: The PackageURL object containing type, namespace, name, and version.
:return: List of entries (dicts with "purl" and "href") matching the version.
"""
entries = self.get_by_name(purl)
if entries and purl.version in entries:
Expand Down
4 changes: 2 additions & 2 deletions tests/fixtures/sbom_for_mapping1.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
}
}
],
"purl": "pkg:pypi/[email protected]",
"purl": "pkg:pypi/[email protected]?file_name=colorama-0.4.3.tar.gz",
"externalReferences": [
{
"url": "https://pypi.org/project/colorama/#files",
Expand Down Expand Up @@ -119,4 +119,4 @@
]
}
]
}
}
5 changes: 4 additions & 1 deletion tests/test_bom_map2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,9 @@ def test_mapping_multiple_match_by_id(self) -> None:
assert sbom is not None
assert len(sbom.components) == 1
assert sbom.components[0].version == "0.4.3"
# assure we get the PURL from the release, not from input BOM
# (which has PURL with file_name qualifier)
assert sbom.components[0].purl == PackageURL.from_string("pkg:pypi/[email protected]")
prop = CycloneDxSupport.get_property_value(sbom.components[0], CycloneDxSupport.CDX_PROP_MAPRESULT)
assert prop == MapResult.FULL_MATCH_BY_ID
prop = CycloneDxSupport.get_property_value(sbom.components[0], CycloneDxSupport.CDX_PROP_COMPONENT_ID)
Expand Down Expand Up @@ -1134,7 +1137,7 @@ def provide_cache_responses(self) -> None:
"componentId" : "678dstzd8",
"componentType" : "OSS",
"externalIds" : {
"package-url" : "pkg:pypi/[email protected]"
"package-url" : "pkg:pypi/[email protected]?file_name=colorama-0.4.3.tar.gz"
},
"createdOn" : "2016-12-18",
"mainlineState" : "SPECIFIC",
Expand Down