Skip to content

Commit cde7c76

Browse files
committed
fix: Canonicalize the URls returned by repositories
Some artifactories (e.g. JFrog), return relative URLs in the package metadata. This commit canonicalizes them to compare them. Signed-off-by: Nicolas Nobelis <nicolas.nobelis@bosch.com>
1 parent 9f184a1 commit cde7c76

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

src/python_inspector/package_data.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
# See https://aboutcode.org for more information about nexB OSS projects.
1010
#
1111

12+
import os
13+
from urllib.parse import urlparse, urlunparse
14+
1215
from typing import Dict
1316
from typing import List
1417
from typing import Optional
@@ -76,10 +79,27 @@ async def get_pypi_data_from_purl(
7679
sdist_url = await get_sdist_download_url(
7780
purl=parsed_purl, repos=repos, python_version=python_version
7881
)
82+
83+
def canonicalize_url(url: str):
84+
# Parse the URL into its components
85+
parsed = urlparse(url)
86+
87+
# Canonicalize the path component to resolve ".."
88+
# os.path.normpath will handle segments like '.' and '..'
89+
canonical_path = os.path.normpath(parsed.path)
90+
91+
# Rebuild the URL with the canonicalized path
92+
# We replace the original path with the new one
93+
parsed = parsed._replace(path=canonical_path)
94+
canonical_url = urlunparse(parsed)
95+
96+
return canonical_url
97+
7998
if sdist_url:
8099
valid_distribution_urls.append(sdist_url)
81100

82101
valid_distribution_urls = [url for url in valid_distribution_urls if url]
102+
valid_distribution_urls = list(map(canonicalize_url, valid_distribution_urls))
83103

84104
# if prefer_source is True then only source distribution is used
85105
# in case of no source distribution available then wheel is used
@@ -95,10 +115,22 @@ async def get_pypi_data_from_purl(
95115
]
96116
wheel_url = choose_single_wheel(wheel_urls)
97117
if wheel_url:
98-
valid_distribution_urls.insert(0, wheel_url)
118+
valid_distribution_urls.insert(0, canonicalize_url(wheel_url))
99119

100120
urls = {url.get("url"): url for url in response.get("urls") or []}
101121

122+
# Sanitize all URLs that are relative and canonicalize them
123+
urls_sanitized = {}
124+
for url in urls:
125+
value = urls.get(url)
126+
127+
if url.startswith("https"):
128+
url_sanitized = canonicalize_url(url)
129+
else:
130+
url_sanitized = canonicalize_url(base_path + url)
131+
132+
urls_sanitized[url_sanitized] = value
133+
102134
def remove_credentials_from_url(url: str):
103135
# Parse the URL into its components
104136
parsed = urlparse(url)
@@ -117,10 +149,10 @@ def remove_credentials_from_url(url: str):
117149
# iterate over the valid distribution urls and return the first
118150
# one that is matching.
119151
for dist_url in valid_distribution_urls:
120-
if dist_url not in urls:
152+
if dist_url not in urls_sanitized:
121153
continue
122154

123-
url_data = urls.get(dist_url)
155+
url_data = urls_sanitized.get(dist_url)
124156
digests = url_data.get("digests") or {}
125157

126158
return PackageData(

0 commit comments

Comments
 (0)