Skip to content

Commit

Permalink
feat(API): update version matching for NuGet, PyPI and RubyGems (#2971)
Browse files Browse the repository at this point in the history
[Malicious
package](https://github.com/ossf/malicious-packages/tree/main/osv/malicious)
publishes OSV records for crates.io, npm, NuGet, PyPI, and RubyGems.
Queries for NuGet, PyPI, and RubyGems (npm and crates.io use semantic
versioning, so the matching process is different) only match
vulnerabilities against specific `affected versions`. However, malicious
package records may only provide `affected ranges` instead of individual
versions in some cases (e.g.
https://api.osv.dev/v1/vulns/MAL-2022-7426). OSV also can't enumerate
affected versions for a malicious package as those versions have been
deleted. This causes issues like
#2407

Switching the API query version matching from
`_query_by_generic_version()` to
[`_query_by_comparing_versions()`](https://github.com/google/osv.dev/blob/4d981692feb5e088d12177d97a44fe30701b3854/gcp/api/server.py#L1186)
can address this issue. The `_query_by_comparing_versions()` function
matches [both affected versions and affected
ranges](https://github.com/google/osv.dev/blob/4d981692feb5e088d12177d97a44fe30701b3854/gcp/api/server.py#L1381),
but might slow down the performance for a bit.

Adding this PR after the end-of-year release to give more time to verify
performance on the test instance before rolling out to prod.
  • Loading branch information
hogo6002 authored Dec 16, 2024
1 parent e100a02 commit b04faa3
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 0 deletions.
46 changes: 46 additions & 0 deletions gcp/api/integration_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,52 @@ def test_query_comparing_version(self):
timeout=_TIMEOUT)
self.assertEqual(0, len(response.json()))

def test_malicious_package_matching(self):
""""Test malicious package query"""
# Test matching by affected ranges
mal_2022_7426 = self._get('MAL-2022-7426')

expected_vulns = [
mal_2022_7426,
]

package = 'pymocks'
ecosystem = 'PyPI'

response = requests.post(
_api() + _BASE_QUERY,
data=json.dumps({
'version': '0.0.1',
'package': {
'name': package,
'ecosystem': ecosystem,
}
}),
timeout=_TIMEOUT)
self.assert_results_equal({'vulns': expected_vulns}, response.json())

# Test matching by affected versions
mal_2024_4618 = self._get('MAL-2024-4618')

expected_vulns = [
mal_2024_4618,
]

package = 'psbuiId'
ecosystem = 'NuGet'

response = requests.post(
_api() + _BASE_QUERY,
data=json.dumps({
'version': '1.1.1-beta',
'package': {
'name': package,
'ecosystem': ecosystem,
}
}),
timeout=_TIMEOUT)
self.assert_results_equal({'vulns': expected_vulns}, response.json())

def test_query_invalid_ecosystem(self):
"""Test a query with an invalid ecosystem fails validation."""
response = requests.post(
Expand Down
4 changes: 4 additions & 0 deletions osv/ecosystems/nuget.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,7 @@ def enumerate_versions(self,
self.sort_versions(versions)
return self._get_affected_versions(versions, introduced, fixed,
last_affected, limits)

@property
def supports_comparing(self):
return True
4 changes: 4 additions & 0 deletions osv/ecosystems/pypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ def enumerate_versions(self,

return self._get_affected_versions(versions, introduced, fixed,
last_affected, limits)

@property
def supports_comparing(self):
return True
4 changes: 4 additions & 0 deletions osv/ecosystems/rubygems.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,7 @@ def enumerate_versions(self,
self.sort_versions(versions)
return self._get_affected_versions(versions, introduced, fixed,
last_affected, limits)

@property
def supports_comparing(self):
return True

0 comments on commit b04faa3

Please sign in to comment.