Skip to content

Commit f214eff

Browse files
committed
fixup
1 parent 9bcd1d3 commit f214eff

2 files changed

Lines changed: 62 additions & 14 deletions

File tree

dep_checker/npm_audit.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -497,25 +497,41 @@ def fetch_global_advisories(self, packages: List[Dict[str, str]]) -> List[Dict]:
497497
requested = tuple(sorted({f"{package['name']}@{package['version']}" for package in packages}))
498498
if requested in cache:
499499
return cache[requested]
500-
query = urllib.parse.urlencode([
501-
("ecosystem", "npm"),
502-
("affects", ",".join(requested)),
503-
("per_page", "100"),
504-
])
505-
url = f"https://api.github.com/advisories?{query}"
500+
506501
headers = {
507502
"Accept": "application/vnd.github+json",
508503
"X-GitHub-Api-Version": GITHUB_API_VERSION,
509504
"User-Agent": "nsolid-dependency-vuln-assessments",
510505
}
511-
request = urllib.request.Request(url, headers=headers)
512-
with urllib.request.urlopen(request, timeout=min(self.timeout, 30)) as response:
513-
payload = json.load(response)
514-
if not isinstance(payload, list):
515-
logger.warning(f"Global advisory query returned non-list payload for {len(requested)} packages: {payload}")
516-
return []
517-
cache[requested] = [item for item in payload if isinstance(item, dict)]
518-
return cache[requested]
506+
advisories: List[Dict] = []
507+
seen_ids: set[str] = set()
508+
batch_size = 25
509+
510+
for index in range(0, len(requested), batch_size):
511+
batch = requested[index:index + batch_size]
512+
query = urllib.parse.urlencode(
513+
[("ecosystem", "npm"), *( ("affects[]", item) for item in batch ), ("per_page", "100")]
514+
)
515+
url = f"https://api.github.com/advisories?{query}"
516+
request = urllib.request.Request(url, headers=headers)
517+
with urllib.request.urlopen(request, timeout=min(self.timeout, 30)) as response:
518+
payload = json.load(response)
519+
if not isinstance(payload, list):
520+
logger.warning(
521+
f"Global advisory query returned non-list payload for batch {index // batch_size + 1} of {((len(requested) - 1) // batch_size) + 1}: {payload}"
522+
)
523+
continue
524+
for item in payload:
525+
if not isinstance(item, dict):
526+
continue
527+
advisory_id = item.get("ghsa_id") or item.get("cve_id") or id(item)
528+
if advisory_id in seen_ids:
529+
continue
530+
seen_ids.add(advisory_id)
531+
advisories.append(item)
532+
533+
cache[requested] = advisories
534+
return advisories
519535

520536
def preferred_global_advisory_cve(self, advisory: Dict) -> Optional[str]:
521537
for identifier in advisory.get("identifiers") or []:

dep_checker/test_npm_audit.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,37 @@ def read(self):
393393
npm_audit.urllib.request.urlopen = original_urlopen
394394

395395

396+
397+
def test_fetch_global_advisories_uses_repeated_affects_params() -> None:
398+
class FakeResponse:
399+
def __enter__(self):
400+
return self
401+
402+
def __exit__(self, exc_type, exc, tb):
403+
return False
404+
405+
def read(self):
406+
return b'[]'
407+
408+
seen = {}
409+
original_urlopen = npm_audit.urllib.request.urlopen
410+
411+
def fake_urlopen(request, timeout=None):
412+
seen["url"] = request.full_url
413+
return FakeResponse()
414+
415+
npm_audit.urllib.request.urlopen = fake_urlopen
416+
try:
417+
checker = NPMAuditChecker(Path("/tmp"), timeout=60)
418+
checker.fetch_global_advisories([
419+
{"name": "ip-address", "version": "10.2.0"},
420+
{"name": "lodash", "version": "4.17.21"},
421+
])
422+
assert "affects%5B%5D=ip-address%4010.2.0" in seen["url"], seen
423+
assert "affects%5B%5D=lodash%404.17.21" in seen["url"], seen
424+
finally:
425+
npm_audit.urllib.request.urlopen = original_urlopen
426+
396427
def test_global_advisories_are_merged_with_ghad_results() -> None:
397428
class FakeClient:
398429
def execute(self, query, variable_values):
@@ -769,6 +800,7 @@ def test_npm_audit_basic() -> None:
769800
test_enolock_falls_back_to_install_and_normal_audit()
770801
test_nvd_primary_merges_ghad_aliases()
771802
test_fetch_global_advisories_returns_empty_for_non_list_payload()
803+
test_fetch_global_advisories_uses_repeated_affects_params()
772804
test_global_advisories_are_merged_with_ghad_results()
773805
test_query_failure_is_skipped_per_package()
774806
test_invalid_advisory_range_or_version_is_skipped()

0 commit comments

Comments
 (0)