generated from aboutcode-org/skeleton
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #228 from nexB/217-validate-endpoint
Add endpoint for PURL validation
- Loading branch information
Showing
3 changed files
with
154 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -682,6 +682,101 @@ def _reindex_package(package, reindexed_packages): | |
return Response(response_data) | ||
|
||
|
||
class PurlValidateViewSet(viewsets.ViewSet): | ||
""" | ||
Take a `purl` and check whether it's valid PackageURL or not. | ||
Optionally set `check_existence` to true to check whether the package exists in real world. | ||
**Note:** As of now `check_existence` only supports `apache`, `composer`, `deb`, `gem`, | ||
`github`, `golang`, `maven`, `npm`, `nuget`and `pypi` ecosystems. | ||
**Input example:** | ||
{ | ||
"purl": "pkg:npm/[email protected]", | ||
"check_existence": true, | ||
} | ||
Response contains: | ||
- valid | ||
- True, if input PURL is a valid PackageURL. | ||
- exists | ||
- True, if input PURL exists in real world and `check_existence` flag is enabled. | ||
""" | ||
def get_view_name(self): | ||
return 'Validate PURL' | ||
|
||
def list(self, request): | ||
purl = request.query_params.get("purl") | ||
check_existence = request.query_params.get("check_existence") or False | ||
|
||
message_valid = "The provided PackageURL is valid." | ||
message_not_valid = "The provided PackageURL is not valid." | ||
message_valid_and_exists = ( | ||
"The provided Package URL is valid, and the package exists in the upstream repo." | ||
) | ||
message_valid_but_does_not_exist = ( | ||
"The provided PackageURL is valid but does not exist in the upstream repo." | ||
) | ||
message_error_no_purl = ( | ||
"PackageURL (purl) is required. Please provide a PackageURL in the request." | ||
) | ||
|
||
if not purl: | ||
return Response( | ||
{ | ||
"error": "Bad Request", | ||
"message": message_error_no_purl, | ||
}, | ||
status=status.HTTP_400_BAD_REQUEST, | ||
) | ||
|
||
# validate purl | ||
try: | ||
package_url = PackageURL.from_string(purl) | ||
except ValueError: | ||
return Response( | ||
{ | ||
"valid": False, | ||
"message": message_not_valid, | ||
"purl": purl, | ||
} | ||
) | ||
|
||
exists = None | ||
message = message_valid | ||
if check_existence: | ||
exists = False | ||
lookups = purl_to_lookups(purl) | ||
packages = Package.objects.filter(**lookups) | ||
if packages.exists(): | ||
exists = True | ||
else: | ||
versionless_purl = PackageURL( | ||
type=package_url.type, | ||
namespace=package_url.namespace, | ||
name=package_url.name, | ||
) | ||
all_versions = get_all_versions_plain(versionless_purl) | ||
if (all_versions and not package_url.version) or ( | ||
package_url.version in all_versions | ||
): | ||
# True, if requested purl has no version and any version of package exists upstream. | ||
# True, if requested purl.version exists upstream. | ||
exists = True | ||
message = message_valid_and_exists if exists else message_valid_but_does_not_exist | ||
|
||
return Response( | ||
{ | ||
"valid": True, | ||
"exists": exists, | ||
"message": message, | ||
"purl": purl, | ||
} | ||
) | ||
|
||
|
||
def get_resolved_purls(packages, supported_ecosystems): | ||
""" | ||
Take a list of dict containing purl or version-less purl along with vers | ||
|
@@ -779,7 +874,7 @@ def resolve_versions(parsed_purl, vers): | |
|
||
return result | ||
|
||
def get_all_versions(purl: PackageURL): | ||
def get_all_versions_plain(purl: PackageURL): | ||
""" | ||
Return all the versions available for the given purls. | ||
""" | ||
|
@@ -796,14 +891,22 @@ def get_all_versions(purl: PackageURL): | |
return | ||
|
||
all_versions = versionAPI().fetch(package_name) or [] | ||
return [ version.value for version in all_versions ] | ||
|
||
def get_all_versions(purl): | ||
""" | ||
Return all the versions available for the given purls as | ||
proper Version objects from `univers`. | ||
""" | ||
all_versions = get_all_versions_plain(purl) | ||
versionClass = VERSION_CLASS_BY_PACKAGE_TYPE.get(purl.type) | ||
|
||
result = [] | ||
for package_version in all_versions: | ||
for version in all_versions: | ||
try: | ||
result.append(versionClass(package_version.value)) | ||
result.append(versionClass(version)) | ||
except InvalidVersion: | ||
logger.warning(f"Invalid version '{package_version.value}' for '{purl}'") | ||
logger.warning(f"Invalid version '{version}' for '{purl}'") | ||
pass | ||
|
||
return result | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1107,3 +1107,48 @@ def test_api_resource_checksum_filter(self): | |
self.resource2.name, | ||
]) | ||
self.assertEquals(expected_names, names) | ||
|
||
class PurlValidateApiTestCase(TestCase): | ||
|
||
def setUp(self): | ||
self.package_data = { | ||
'type': 'npm', | ||
'namespace': '', | ||
'name': 'foobar', | ||
'version': '1,1.0', | ||
'qualifiers': '', | ||
'subpath': '', | ||
'download_url': '', | ||
'filename': 'Foo.zip', | ||
'sha1': 'testsha1', | ||
'md5': 'testmd5', | ||
'size': 101, | ||
} | ||
self.package = Package.objects.create(**self.package_data) | ||
self.package.refresh_from_db() | ||
|
||
def test_api_purl_validation(self): | ||
data1 = { | ||
"purl": "pkg:npm/[email protected]", | ||
"check_existence": True, | ||
} | ||
response1 = self.client.get(f"/api/validate/", data=data1) | ||
|
||
data2 = { | ||
"purl": "pkg:npm/[email protected]", | ||
"check_existence": True, | ||
} | ||
response2 = self.client.get(f"/api/validate/", data=data2) | ||
|
||
self.assertEquals(True, response1.data["valid"]) | ||
self.assertEquals(True, response1.data["exists"]) | ||
self.assertEquals( | ||
"The provided Package URL is valid, and the package exists in the upstream repo.", | ||
response1.data["message"], | ||
) | ||
|
||
self.assertEquals(False, response2.data["valid"]) | ||
self.assertEquals( | ||
"The provided PackageURL is not valid.", response2.data["message"] | ||
) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters