Skip to content

Commit bdf7ea5

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 6d02afb commit bdf7ea5

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
@@ -81,10 +84,27 @@ async def get_pypi_data_from_purl(
8184
sdist_url = await get_sdist_download_url(
8285
purl=parsed_purl, repos=repos, python_version=python_version
8386
)
87+
88+
def canonicalize_url(url: str):
89+
# Parse the URL into its components
90+
parsed = urlparse(url)
91+
92+
# Canonicalize the path component to resolve ".."
93+
# os.path.normpath will handle segments like '.' and '..'
94+
canonical_path = os.path.normpath(parsed.path)
95+
96+
# Rebuild the URL with the canonicalized path
97+
# We replace the original path with the new one
98+
parsed = parsed._replace(path=canonical_path)
99+
canonical_url = urlunparse(parsed)
100+
101+
return canonical_url
102+
84103
if sdist_url:
85104
valid_distribution_urls.append(sdist_url)
86105

87106
valid_distribution_urls = [url for url in valid_distribution_urls if url]
107+
valid_distribution_urls = list(map(canonicalize_url, valid_distribution_urls))
88108

89109
# if prefer_source is True then only source distribution is used
90110
# in case of no source distribution available then wheel is used
@@ -100,10 +120,22 @@ async def get_pypi_data_from_purl(
100120
]
101121
wheel_url = choose_single_wheel(wheel_urls)
102122
if wheel_url:
103-
valid_distribution_urls.insert(0, wheel_url)
123+
valid_distribution_urls.insert(0, canonicalize_url(wheel_url))
104124

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

127+
# Sanitize all URLs that are relative and canonicalize them
128+
urls_sanitized = {}
129+
for url in urls:
130+
value = urls.get(url)
131+
132+
if url.startswith("https"):
133+
url_sanitized = canonicalize_url(url)
134+
else:
135+
url_sanitized = canonicalize_url(base_path + url)
136+
137+
urls_sanitized[url_sanitized] = value
138+
107139
def remove_credentials_from_url(url: str):
108140
# Parse the URL into its components
109141
parsed = urlparse(url)
@@ -122,10 +154,10 @@ def remove_credentials_from_url(url: str):
122154
# iterate over the valid distribution urls and return the first
123155
# one that is matching.
124156
for dist_url in valid_distribution_urls:
125-
if dist_url not in urls:
157+
if dist_url not in urls_sanitized:
126158
continue
127159

128-
url_data = urls.get(dist_url)
160+
url_data = urls_sanitized.get(dist_url)
129161
digests = url_data.get("digests") or {}
130162

131163
return PackageData(

0 commit comments

Comments
 (0)