Skip to content

Commit

Permalink
fix(api): support conan purl query (#2961)
Browse files Browse the repository at this point in the history
fixes #2960

Although OSV.dev currently doesn't have any `ConanCenter` records, it's
listed in the OSV schema. So adding support for ConanCenter PURL query.

The other ecosystems listed in the comments cannot be converted into
PURL strings because they are not defined in the PURL spec. Queries for
these ecosystems will return a 400 error with an "unknown PURL
ecosystem" message
  • Loading branch information
hogo6002 authored Dec 5, 2024
1 parent 9e88d39 commit 257b51f
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 10 deletions.
8 changes: 4 additions & 4 deletions gcp/api/integration_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ def test_query_unknown_purl_invalid_semver(self):

self.assert_results_equal({
'code': 3,
'message': 'Invalid PURL.'
'message': 'Unknown PURL ecosystem.'
}, response.json())

def test_query_semver_no_vulns(self):
Expand Down Expand Up @@ -644,7 +644,7 @@ def test_query_with_redundant_ecosystem(self):
self.assert_results_equal(
{
'code': 3,
'message': 'ecosystem specified in a purl query'
'message': 'ecosystem specified in a PURL query'
}, response.json())

def test_query_with_redundant_version(self):
Expand All @@ -661,7 +661,7 @@ def test_query_with_redundant_version(self):
self.assert_results_equal(
{
'code': 3,
'message': 'version specified in params and purl query'
'message': 'version specified in params and PURL query'
}, response.json())

def test_query_with_redundant_package_name(self):
Expand All @@ -677,7 +677,7 @@ def test_query_with_redundant_package_name(self):
self.assert_results_equal(
{
'code': 3,
'message': 'name specified in a purl query'
'message': 'name specified in a PURL query'
}, response.json())

def test_query_batch(self):
Expand Down
12 changes: 9 additions & 3 deletions gcp/api/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ def query_info(query) -> tuple[str, str | None, str | None]:
if query.package.purl:
try:
purl = purl_helpers.parse_purl(query.package.purl) # can raise ValueError
if purl is None:
raise ValueError('purl ecosystem is unknown')
if query.package.ecosystem or query.package.name:
raise ValueError('purl and name/ecosystem cannot both be specified')
if purl.version and query.version:
Expand Down Expand Up @@ -732,22 +734,26 @@ def do_query(query: osv_service_v1_pb2.Query,
'Invalid PURL.',
)

if purl is None:
context.service_context.abort(grpc.StatusCode.INVALID_ARGUMENT,
'Unknown PURL ecosystem.')

if package_name: # Purls already include the package name
context.service_context.abort(
grpc.StatusCode.INVALID_ARGUMENT,
'name specified in a purl query',
'name specified in a PURL query',
)
if ecosystem:
# Purls already include the ecosystem inside
context.service_context.abort(
grpc.StatusCode.INVALID_ARGUMENT,
'ecosystem specified in a purl query',
'ecosystem specified in a PURL query',
)
if purl.version and version:
# version included both in purl and query
context.service_context.abort(
grpc.StatusCode.INVALID_ARGUMENT,
'version specified in params and purl query',
'version specified in params and PURL query',
)

ecosystem = purl.ecosystem
Expand Down
7 changes: 6 additions & 1 deletion osv/purl_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,28 @@
'AlmaLinux': EcosystemPURL('rpm', 'almalinux'),
'Alpine': EcosystemPURL('apk', 'alpine'),
# Android
# Bioconductor
'Bitnami': EcosystemPURL('bitnami', None),
'Chainguard': EcosystemPURL('apk', 'chainguard'),
'ConanCenter': EcosystemPURL('conan', None),
'CRAN': EcosystemPURL('cran', None),
'crates.io': EcosystemPURL('cargo', None),
'Debian': EcosystemPURL('deb', 'debian'),
# GHC
# GIT
'GitHub Actions': EcosystemPURL('github', None),
'Go': EcosystemPURL('golang', None),
'Hackage': EcosystemPURL('hackage', None),
'Hex': EcosystemPURL('hex', None),
# Linux
'Mageia': EcosystemPURL('rpm', 'mageia'),
'Maven': EcosystemPURL('maven', None),
'npm': EcosystemPURL('npm', None),
'NuGet': EcosystemPURL('nuget', None),
'openSUSE': EcosystemPURL('rpm', 'opensuse'),
'OSS-Fuzz': EcosystemPURL('generic', None),
'Packagist': EcosystemPURL('composer', None),
# Photon OS
'Pub': EcosystemPURL('pub', None),
'PyPI': EcosystemPURL('pypi', None),
'Red Hat': EcosystemPURL('rpm', 'redhat'),
Expand Down Expand Up @@ -123,7 +128,7 @@ def parse_purl(purl_str: str) -> ParsedPURL | None:
# information (like vendors) and the namespace might be optional.
ecosystem = PURL_ECOSYSTEM_MAP.get(EcosystemPURL(purl.type, None))
if not ecosystem:
raise ValueError('Invalid ecosystem.')
return None

# For ecosystems with optional namespaces, the namespace might need to be
# included as part of the package name.
Expand Down
16 changes: 14 additions & 2 deletions osv/purl_helpers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ def tests_package_to_purl(self):
self.assertEqual('pkg:cran/commonmark',
purl_helpers.package_to_purl('CRAN', 'commonmark'))

self.assertEqual('pkg:conan/openssl',
purl_helpers.package_to_purl('ConanCenter', 'openssl'))

self.assertEqual('pkg:pypi/django',
purl_helpers.package_to_purl('PyPI', 'django'))

self.assertEqual('pkg:rpm/mageia/python-aiohttp',
purl_helpers.package_to_purl('Mageia', 'python-aiohttp'))

self.assertEqual(
'pkg:maven/org.apache.struts/struts2-core',
purl_helpers.package_to_purl('Maven', 'org.apache.struts:struts2-core'))
Expand Down Expand Up @@ -146,6 +152,9 @@ def test_parse_purl(self):
self.assertEqual(('CRAN', 'commonmark', None),
purl_helpers.parse_purl('pkg:cran/commonmark'))

self.assertEqual(('ConanCenter', 'openssl', '3.0.3'),
purl_helpers.parse_purl('pkg:conan/[email protected]'))

self.assertEqual(('Debian', 'mpg123', '1.26.4-1+deb11u1'),
purl_helpers.parse_purl(
'pkg:deb/debian/[email protected]+deb11u1?arch=source'))
Expand All @@ -163,6 +172,10 @@ def test_parse_purl(self):
self.assertEqual(('Hex', 'acme/foo', '2.3.'),
purl_helpers.parse_purl('pkg:hex/acme/[email protected].'))

self.assertEqual(('Mageia', 'python-aiohttp', None),
purl_helpers.parse_purl(
'pkg:rpm/mageia/python-aiohttp?distro=mageia-9'))

self.assertEqual(('Maven', 'org.apache.struts:struts2-core', '1.0.0'),
purl_helpers.parse_purl(
'pkg:maven/org.apache.struts/[email protected]'))
Expand Down Expand Up @@ -221,8 +234,7 @@ def test_parse_purl(self):
('Wolfi', 'test-package', '1.2.3'),
purl_helpers.parse_purl('pkg:apk/wolfi/[email protected]'))

with self.assertRaises(ValueError):
purl_helpers.parse_purl('pkg:bad/ubuntu/pygments')
self.assertIsNone(purl_helpers.parse_purl('pkg:bad/ubuntu/pygments'))

with self.assertRaises(ValueError):
purl_helpers.parse_purl('purl:apk/wolfi/[email protected]')
Expand Down

0 comments on commit 257b51f

Please sign in to comment.