Skip to content

Commit d574079

Browse files
committed
address review comments
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent c20b09a commit d574079

5 files changed

Lines changed: 61 additions & 52 deletions

File tree

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ django-widget-tweaks>=1.4.8
88
packageurl-python>=0.9.4
99
binaryornot>=0.4.4
1010
GitPython>=3.1.17
11-
univers>=30.3.0
11+
univers>=30.3.1
1212
saneyaml>=0.5.2
1313
beautifulsoup4>=4.9.3
1414
python-dateutil>=2.8.1

vulnerabilities/importers/github.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,6 @@ def process_response(resp: dict, package_type: str) -> Iterable[AdvisoryData]:
331331
)
332332
return
333333

334-
LOGGER.error(f"Found {len(vulnerabilities)} vulnerabilities for package_type: {package_type!r}")
335-
336334
for vulnerability in vulnerabilities:
337335
affected_packages = []
338336
aliases = set()
@@ -458,6 +456,7 @@ def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]:
458456
advisory_data.affected_packages
459457
)
460458
except UnMergeablePackageError:
459+
LOGGER.error(f"Cannot merge with different purls {advisory_data.affected_packages!r}")
461460
return iter([])
462461

463462
pkg_type = purl.type

vulnerabilities/importers/nginx.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,9 @@ def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]:
201201
advisory_data.affected_packages
202202
)
203203
except UnMergeablePackageError:
204+
logger.error(f"Cannot merge with different purls {advisory_data.affected_packages!r}")
204205
return iter([])
206+
205207
all_versions = self.version_api.get("nginx/nginx").valid_versions
206208
affected_purls = []
207209
for affected_version_range in affected_version_ranges:

vulnerabilities/package_managers_2.py

Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,33 @@
33
import traceback
44
import xml.etree.ElementTree as ET
55
from datetime import datetime
6-
from json import JSONDecodeError
76
from typing import List
87
from typing import MutableMapping
98
from typing import Optional
109
from typing import Set
10+
from urllib.parse import urlparse
1111

1212
import requests
1313
from dateutil import parser as dateparser
1414
from django.utils.dateparse import parse_datetime
1515

16-
from vulnerabilities.package_managers import Version as LegacyVersion
1716
from vulnerabilities.package_managers import VersionResponse
1817

1918
LOGGER = logging.getLogger(__name__)
2019

2120

2221
@dataclasses.dataclass(frozen=True)
23-
class Version:
22+
class LegacyVersion:
2423
value: str
2524
release_date: Optional[datetime] = None
2625

2726

27+
@dataclasses.dataclass
28+
class VersionResponse:
29+
valid_versions: Set[str] = dataclasses.field(default_factory=set)
30+
newer_versions: Set[str] = dataclasses.field(default_factory=set)
31+
32+
2833
def get_response(url, type="json"):
2934
resp = requests.get(url=url)
3035
if not resp.status_code == 200:
@@ -81,12 +86,14 @@ def fetch(self, pkg):
8186
self.cache[pkg] = versions
8287
return
8388

84-
for version, download_items in response["releases"].items():
89+
for version, download_items in response["releases"].items() or {}:
8590
if download_items:
8691
latest_download_item = max(
8792
download_items,
8893
key=lambda download_item: dateparser.parse(
8994
download_item["upload_time_iso_8601"]
95+
if "upload_time_iso_8601" in download_item
96+
else LOGGER.error(f"{download_item} has no upload_time_iso_8601")
9097
),
9198
)
9299
versions.add(
@@ -110,34 +117,11 @@ def fetch(self, pkg):
110117
self.cache[pkg] = versions
111118
return
112119
for release in response:
113-
versions.add(
114-
LegacyVersion(
115-
value=release["number"],
116-
release_date=dateparser.parse(release["created_at"]),
117-
)
118-
)
119-
120-
self.cache[pkg] = versions
121-
122-
123-
class NpmVersionAPI(VersionAPI):
124-
125-
package_type = "npm"
126-
127-
def fetch(self, pkg):
128-
url = f"https://registry.npmjs.org/{pkg}"
129-
versions = set()
130-
response = get_response(url=url)
131-
if not response:
132-
self.cache[pkg] = versions
133-
return
134-
for version in response.get("versions", []):
135-
release_date = response.get("time", {}).get(version)
136-
if release_date:
137-
release_date = dateparser.parse(release_date)
138-
versions.add(LegacyVersion(value=version, release_date=release_date))
120+
if release["number"] and release["published_at"]:
121+
release_date = dateparser.parse(release["published_at"])
122+
versions.add(LegacyVersion(value=release["number"], release_date=release_date))
139123
else:
140-
versions.add(LegacyVersion(value=version, release_date=None))
124+
LOGGER.error(f"Failed to parse release {release}")
141125

142126
self.cache[pkg] = versions
143127

@@ -213,19 +197,23 @@ def nuget_url(pkg_name: str) -> str:
213197
def extract_versions(resp: dict) -> Set[LegacyVersion]:
214198
all_versions = set()
215199
try:
216-
for entry_group in resp["items"]:
217-
for entry in entry_group["items"]:
218-
all_versions.add(
219-
LegacyVersion(
220-
value=entry["catalogEntry"]["version"],
221-
release_date=dateparser.parse(entry["catalogEntry"]["published"]),
200+
for entry_group in resp["items"] or []:
201+
for entry in entry_group["items"] or []:
202+
catalog_entry = entry["catalogEntry"] or {}
203+
version = catalog_entry.get("version")
204+
release_date = dateparser.parse(catalog_entry.get("published"))
205+
if version and release_date:
206+
all_versions.add(
207+
LegacyVersion(
208+
value=version,
209+
release_date=release_date,
210+
)
222211
)
223-
)
224212
# FIXME: json response for YamlDotNet.Signed triggers this exception.
225213
# Some packages with many versions give a response of a list of endpoints.
226214
# In such cases rather, we should collect data from those endpoints.
227215
except KeyError:
228-
pass
216+
LOGGER.error(f"Failed to parse nuget response for {resp!r}")
229217

230218
return all_versions
231219

@@ -239,7 +227,7 @@ def __init__(self, cache: MutableMapping[str, Set[LegacyVersion]] = None):
239227
self.module_name_by_package_name = {}
240228

241229
@staticmethod
242-
def trim_url_path(url_path: str) -> Optional[str]:
230+
def trim_go_url_path(url_path: str) -> Optional[str]:
243231
"""
244232
Return a trimmed Go `url_path` removing trailing
245233
package references and keeping only the module
@@ -254,17 +242,21 @@ def trim_url_path(url_path: str) -> Optional[str]:
254242
This functions trims the trailing part(s) of a package URL
255243
and returns the remaining the module name.
256244
For example:
257-
>>> module = "https://github.com/xx/a"
258-
>>> assert GoproxyVersionAPI.trim_url_path("https://github.com/xx/a/b") == module
245+
>>> module = "github.com/xx/a"
246+
>>> assert GoproxyVersionAPI.trim_go_url_path("https://github.com/xx/a/b") == module
259247
"""
260248
# some advisories contains this prefix in package name, e.g. https://github.com/advisories/GHSA-7h6j-2268-fhcm
261249
if url_path.startswith("https://pkg.go.dev/"):
262-
url_path = url_path.removeprefix("https://pkg.go.dev/")
263-
parts = url_path.split("/")
264-
if len(parts) >= 2:
265-
return "/".join(parts[:-1])
266-
else:
250+
url_path = url_path[len("https://pkg.go.dev/") :]
251+
parsed_url_path = urlparse(url_path)
252+
path = parsed_url_path.path
253+
parts = path.split("/")
254+
if len(parts) < 3:
255+
LOGGER.error(f"Not a valid Go URL path {url_path} trim_go_url_path")
267256
return None
257+
else:
258+
joined_path = "/".join(parts[:3])
259+
return f"{parsed_url_path.netloc}{joined_path}"
268260

269261
@staticmethod
270262
def escape_path(path: str) -> str:
@@ -318,8 +310,8 @@ def fetch(self, pkg: str):
318310
url = f"https://proxy.golang.org/{escaped_pkg}/@v/list"
319311
resp_text = get_response(url=url, type="text")
320312
if not resp_text:
321-
escaped_pkg = GoproxyVersionAPI.trim_url_path(escaped_pkg)
322-
trimmed_pkg = GoproxyVersionAPI.trim_url_path(trimmed_pkg) or ""
313+
escaped_pkg = GoproxyVersionAPI.trim_go_url_path(escaped_pkg)
314+
trimmed_pkg = GoproxyVersionAPI.trim_go_url_path(trimmed_pkg) or ""
323315
continue
324316
break
325317
if resp_text is None or escaped_pkg is None or trimmed_pkg is None:
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import pytest
2+
3+
from vulnerabilities.package_managers_2 import GoproxyVersionAPI
4+
5+
6+
@pytest.mark.parametrize(
7+
"url_path", ["https://pkg.go.dev/https://github.com/xx/a/b", "https://github.com/xx/a/b"]
8+
)
9+
def test_trim_go_url_path(url_path):
10+
assert GoproxyVersionAPI.trim_go_url_path(url_path) == "github.com/xx/a"
11+
12+
13+
def test_trim_go_url_path_failure(caplog):
14+
url_path = "https://github.com"
15+
assert GoproxyVersionAPI.trim_go_url_path(url_path) == None
16+
assert "Not a valid Go URL path" in caplog.text

0 commit comments

Comments
 (0)