diff --git a/ChangeLog.md b/ChangeLog.md index d9c604b6..f3467949 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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. diff --git a/Readme_Mapping.md b/Readme_Mapping.md index bfff419b..aa7cc256 100644 --- a/Readme_Mapping.md +++ b/Readme_Mapping.md @@ -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: diff --git a/capycli/bom/map_bom.py b/capycli/bom/map_bom.py index a8fa8a39..409f3b74 100644 --- a/capycli/bom/map_bom.py +++ b/capycli/bom/map_bom.py @@ -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", "") diff --git a/capycli/common/component_cache.py b/capycli/common/component_cache.py index 365cd775..780e7ffe 100644 --- a/capycli/common/component_cache.py +++ b/capycli/common/component_cache.py @@ -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" diff --git a/capycli/common/purl_store.py b/capycli/common/purl_store.py index fe551594..4d3f9e66 100644 --- a/capycli/common/purl_store.py +++ b/capycli/common/purl_store.py @@ -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: diff --git a/tests/fixtures/sbom_for_mapping1.json b/tests/fixtures/sbom_for_mapping1.json index abea8d9d..ab058209 100644 --- a/tests/fixtures/sbom_for_mapping1.json +++ b/tests/fixtures/sbom_for_mapping1.json @@ -70,7 +70,7 @@ } } ], - "purl": "pkg:pypi/colorama@0.4.3", + "purl": "pkg:pypi/colorama@0.4.3?file_name=colorama-0.4.3.tar.gz", "externalReferences": [ { "url": "https://pypi.org/project/colorama/#files", @@ -119,4 +119,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/tests/test_bom_map2.py b/tests/test_bom_map2.py index 17f35428..b247af09 100644 --- a/tests/test_bom_map2.py +++ b/tests/test_bom_map2.py @@ -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/colorama@0.4.3") 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) @@ -1134,7 +1137,7 @@ def provide_cache_responses(self) -> None: "componentId" : "678dstzd8", "componentType" : "OSS", "externalIds" : { - "package-url" : "pkg:pypi/colorama@0.4.3" + "package-url" : "pkg:pypi/colorama@0.4.3?file_name=colorama-0.4.3.tar.gz" }, "createdOn" : "2016-12-18", "mainlineState" : "SPECIFIC",