updates
This commit is contained in:
771
Backend/venv/lib/python3.12/site-packages/packageurl/validate.py
Normal file
771
Backend/venv/lib/python3.12/site-packages/packageurl/validate.py
Normal file
@@ -0,0 +1,771 @@
|
||||
# Copyright (c) the purl authors
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
# Visit https://github.com/package-url/packageurl-python for support and
|
||||
# download.
|
||||
|
||||
"""
|
||||
Validate each type according to the PURL spec type definitions
|
||||
"""
|
||||
|
||||
|
||||
class BasePurlType:
|
||||
"""
|
||||
Base class for all PURL type classes
|
||||
"""
|
||||
|
||||
type: str
|
||||
"""The type string for this Package-URL type."""
|
||||
|
||||
type_name: str
|
||||
"""The name for this PURL type."""
|
||||
|
||||
description: str
|
||||
"""The description of this PURL type."""
|
||||
|
||||
use_repository: bool = False
|
||||
"""true if this PURL type use a public package repository."""
|
||||
|
||||
default_repository_url: str
|
||||
"""The default public repository URL for this PURL type"""
|
||||
|
||||
namespace_requirement: str
|
||||
""""States if this namespace is required, optional, or prohibited."""
|
||||
|
||||
allowed_qualifiers: dict = {"repository_url", "arch"}
|
||||
"""Set of allowed qualifier keys for this PURL type."""
|
||||
|
||||
namespace_case_sensitive: bool = True
|
||||
"""true if namespace is case sensitive. If false, the canonical form must be lowercased."""
|
||||
|
||||
name_case_sensitive: bool = True
|
||||
"""true if name is case sensitive. If false, the canonical form must be lowercased."""
|
||||
|
||||
version_case_sensitive: bool = True
|
||||
"""true if version is case sensitive. If false, the canonical form must be lowercased."""
|
||||
|
||||
purl_pattern: str
|
||||
"""A regex pattern that matches valid purls of this type."""
|
||||
|
||||
@classmethod
|
||||
def validate(cls, purl, strict=False):
|
||||
"""
|
||||
Validate a PackageURL instance or string.
|
||||
Yields ValidationMessage and performs strict validation if strict=True
|
||||
"""
|
||||
from packageurl import ValidationMessage
|
||||
from packageurl import ValidationSeverity
|
||||
|
||||
if not purl:
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.ERROR,
|
||||
message="No purl provided",
|
||||
)
|
||||
return
|
||||
|
||||
from packageurl import PackageURL
|
||||
|
||||
if not isinstance(purl, PackageURL):
|
||||
try:
|
||||
purl = PackageURL.from_string(purl, normalize_purl=False)
|
||||
except Exception as e:
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.ERROR,
|
||||
message=f"Invalid purl {purl!r} string: {e}",
|
||||
)
|
||||
return
|
||||
|
||||
if not strict:
|
||||
purl = cls.normalize(purl)
|
||||
|
||||
yield from cls._validate_namespace(purl)
|
||||
yield from cls._validate_name(purl)
|
||||
yield from cls._validate_version(purl)
|
||||
if strict:
|
||||
yield from cls._validate_qualifiers(purl)
|
||||
|
||||
messages = cls.validate_using_type_rules(purl, strict=strict)
|
||||
if messages:
|
||||
yield from messages
|
||||
|
||||
@classmethod
|
||||
def _validate_namespace(cls, purl):
|
||||
from packageurl import ValidationMessage
|
||||
from packageurl import ValidationSeverity
|
||||
|
||||
if cls.namespace_requirement == "prohibited" and purl.namespace:
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.ERROR,
|
||||
message=f"Namespace is prohibited for purl type: {cls.type!r}",
|
||||
)
|
||||
|
||||
elif cls.namespace_requirement == "required" and not purl.namespace:
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.ERROR,
|
||||
message=f"Namespace is required for purl type: {cls.type!r}",
|
||||
)
|
||||
|
||||
# TODO: Check pending CPAN PR and decide if we want to upgrade the type definition schema
|
||||
if purl.type == "cpan":
|
||||
if purl.namespace and purl.namespace != purl.namespace.upper():
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.WARNING,
|
||||
message=f"Namespace must be uppercase for purl type: {cls.type!r}",
|
||||
)
|
||||
elif (
|
||||
not cls.namespace_case_sensitive
|
||||
and purl.namespace
|
||||
and purl.namespace.lower() != purl.namespace
|
||||
):
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.WARNING,
|
||||
message=f"Namespace is not lowercased for purl type: {cls.type!r}",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _validate_name(cls, purl):
|
||||
if not cls.name_case_sensitive and purl.name and purl.name.lower() != purl.name:
|
||||
from packageurl import ValidationMessage
|
||||
from packageurl import ValidationSeverity
|
||||
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.WARNING,
|
||||
message=f"Name is not lowercased for purl type: {cls.type!r}",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _validate_version(cls, purl):
|
||||
if not cls.version_case_sensitive and purl.version and purl.version.lower() != purl.version:
|
||||
from packageurl import ValidationMessage
|
||||
from packageurl import ValidationSeverity
|
||||
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.WARNING,
|
||||
message=f"Version is not lowercased for purl type: {cls.type!r}",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def normalize(cls, purl):
|
||||
from packageurl import PackageURL
|
||||
from packageurl import normalize
|
||||
|
||||
type_norm, namespace_norm, name_norm, version_norm, qualifiers_norm, subpath_norm = (
|
||||
normalize(
|
||||
purl.type,
|
||||
purl.namespace,
|
||||
purl.name,
|
||||
purl.version,
|
||||
purl.qualifiers,
|
||||
purl.subpath,
|
||||
encode=False,
|
||||
)
|
||||
)
|
||||
|
||||
return PackageURL(
|
||||
type=type_norm,
|
||||
namespace=namespace_norm,
|
||||
name=name_norm,
|
||||
version=version_norm,
|
||||
qualifiers=qualifiers_norm,
|
||||
subpath=subpath_norm,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def validate_using_type_rules(cls, purl, strict=False):
|
||||
"""
|
||||
Validate using any additional type specific rules.
|
||||
Yield validation messages.
|
||||
Subclasses can override this method to add type specific validation rules.
|
||||
"""
|
||||
return iter([])
|
||||
|
||||
@classmethod
|
||||
def _validate_qualifiers(cls, purl):
|
||||
if not purl.qualifiers:
|
||||
return
|
||||
|
||||
purl_qualifiers_keys = set(purl.qualifiers.keys())
|
||||
allowed_qualifiers_set = cls.allowed_qualifiers
|
||||
|
||||
disallowed = purl_qualifiers_keys - allowed_qualifiers_set
|
||||
|
||||
if disallowed:
|
||||
from packageurl import ValidationMessage
|
||||
from packageurl import ValidationSeverity
|
||||
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.INFO,
|
||||
message=(
|
||||
f"Invalid qualifiers found: {', '.join(sorted(disallowed))}. "
|
||||
f"Allowed qualifiers are: {', '.join(sorted(allowed_qualifiers_set))}"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class AlpmTypeDefinition(BasePurlType):
|
||||
type = "alpm"
|
||||
type_name = "Arch Linux package"
|
||||
description = """Arch Linux packages and other users of the libalpm/pacman package manager."""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url", "arch"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:alpm/.*"
|
||||
|
||||
|
||||
class ApkTypeDefinition(BasePurlType):
|
||||
type = "apk"
|
||||
type_name = "APK-based packages"
|
||||
description = """Alpine Linux APK-based packages"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url", "arch"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:apk/.*"
|
||||
|
||||
|
||||
class BitbucketTypeDefinition(BasePurlType):
|
||||
type = "bitbucket"
|
||||
type_name = "Bitbucket"
|
||||
description = """Bitbucket-based packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://bitbucket.org"
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:bitbucket/.*"
|
||||
|
||||
|
||||
class BitnamiTypeDefinition(BasePurlType):
|
||||
type = "bitnami"
|
||||
type_name = "Bitnami"
|
||||
description = """Bitnami-based packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://downloads.bitnami.com/files/stacksmith"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"distro", "repository_url", "arch"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:bitnami/.*"
|
||||
|
||||
|
||||
class CargoTypeDefinition(BasePurlType):
|
||||
type = "cargo"
|
||||
type_name = "Cargo"
|
||||
description = """Cargo packages for Rust"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://crates.io/"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:cargo/.*"
|
||||
|
||||
|
||||
class CocoapodsTypeDefinition(BasePurlType):
|
||||
type = "cocoapods"
|
||||
type_name = "CocoaPods"
|
||||
description = """CocoaPods pods"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://cdn.cocoapods.org/"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:cocoapods/.*"
|
||||
|
||||
|
||||
class ComposerTypeDefinition(BasePurlType):
|
||||
type = "composer"
|
||||
type_name = "Composer"
|
||||
description = """Composer PHP packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://packagist.org"
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:composer/.*"
|
||||
|
||||
|
||||
class ConanTypeDefinition(BasePurlType):
|
||||
type = "conan"
|
||||
type_name = "Conan C/C++ packages"
|
||||
description = """Conan C/C++ packages. The purl is designed to closely resemble the Conan-native <package-name>/<package-version>@<user>/<channel> syntax for package references as specified in https://docs.conan.io/en/1.46/cheatsheet.html#package-terminology"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://center.conan.io"
|
||||
namespace_requirement = "optional"
|
||||
allowed_qualifiers = {"channel", "rrev", "user", "repository_url", "prev"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:conan/.*"
|
||||
|
||||
|
||||
class CondaTypeDefinition(BasePurlType):
|
||||
type = "conda"
|
||||
type_name = "Conda"
|
||||
description = """conda is for Conda packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://repo.anaconda.com"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"channel", "build", "subdir", "repository_url", "type"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:conda/.*"
|
||||
|
||||
|
||||
class CpanTypeDefinition(BasePurlType):
|
||||
type = "cpan"
|
||||
type_name = "CPAN"
|
||||
description = """CPAN Perl packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://www.cpan.org/"
|
||||
namespace_requirement = "optional"
|
||||
allowed_qualifiers = {"repository_url", "ext", "vcs_url", "download_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:cpan/.*"
|
||||
|
||||
@classmethod
|
||||
def validate_using_type_rules(cls, purl, strict=False):
|
||||
from packageurl import ValidationMessage
|
||||
from packageurl import ValidationSeverity
|
||||
|
||||
if purl.namespace and "::" in purl.name:
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.ERROR,
|
||||
message=f"Name must not contain '::' when Namespace is present for purl type: {cls.type!r}",
|
||||
)
|
||||
if not purl.namespace and "-" in purl.name:
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.ERROR,
|
||||
message=f"Name must not contain '-' when Namespace is absent for purl type: {cls.type!r}",
|
||||
)
|
||||
messages = super().validate_using_type_rules(purl, strict)
|
||||
if messages:
|
||||
yield from messages
|
||||
|
||||
|
||||
class CranTypeDefinition(BasePurlType):
|
||||
type = "cran"
|
||||
type_name = "CRAN"
|
||||
description = """CRAN R packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://cran.r-project.org"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:cran/.*"
|
||||
|
||||
|
||||
class DebTypeDefinition(BasePurlType):
|
||||
type = "deb"
|
||||
type_name = "Debian package"
|
||||
description = """Debian packages, Debian derivatives, and Ubuntu packages"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url", "arch"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:deb/.*"
|
||||
|
||||
|
||||
class DockerTypeDefinition(BasePurlType):
|
||||
type = "docker"
|
||||
type_name = "Docker image"
|
||||
description = """for Docker images"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://hub.docker.com"
|
||||
namespace_requirement = "optional"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:docker/.*"
|
||||
|
||||
|
||||
class GemTypeDefinition(BasePurlType):
|
||||
type = "gem"
|
||||
type_name = "RubyGems"
|
||||
description = """RubyGems"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://rubygems.org"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"repository_url", "platform"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:gem/.*"
|
||||
|
||||
|
||||
class GenericTypeDefinition(BasePurlType):
|
||||
type = "generic"
|
||||
type_name = "Generic Package"
|
||||
description = """The generic type is for plain, generic packages that do not fit anywhere else such as for "upstream-from-distro" packages. In particular this is handy for a plain version control repository such as a bare git repo in combination with a vcs_url."""
|
||||
use_repository = False
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "optional"
|
||||
allowed_qualifiers = {"checksum", "download_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:generic/.*"
|
||||
|
||||
|
||||
class GithubTypeDefinition(BasePurlType):
|
||||
type = "github"
|
||||
type_name = "GitHub"
|
||||
description = """GitHub-based packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://github.com"
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:github/.*"
|
||||
|
||||
|
||||
class GolangTypeDefinition(BasePurlType):
|
||||
type = "golang"
|
||||
type_name = "Go package"
|
||||
description = """Go packages"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:golang/.*"
|
||||
|
||||
|
||||
class HackageTypeDefinition(BasePurlType):
|
||||
type = "hackage"
|
||||
type_name = "Haskell package"
|
||||
description = """Haskell packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://hackage.haskell.org"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:hackage/.*"
|
||||
|
||||
@classmethod
|
||||
def validate_using_type_rules(cls, purl, strict=False):
|
||||
from packageurl import ValidationMessage
|
||||
from packageurl import ValidationSeverity
|
||||
|
||||
if "_" in purl.name:
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.WARNING,
|
||||
message=f"Name cannot contain underscores for purl type:{cls.type!r}",
|
||||
)
|
||||
messages = super().validate_using_type_rules(purl, strict)
|
||||
if messages:
|
||||
yield from messages
|
||||
|
||||
|
||||
class HexTypeDefinition(BasePurlType):
|
||||
type = "hex"
|
||||
type_name = "Hex"
|
||||
description = """Hex packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://repo.hex.pm"
|
||||
namespace_requirement = "optional"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:hex/.*"
|
||||
|
||||
|
||||
class HuggingfaceTypeDefinition(BasePurlType):
|
||||
type = "huggingface"
|
||||
type_name = "HuggingFace models"
|
||||
description = """Hugging Face ML models"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = True
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:huggingface/.*"
|
||||
|
||||
|
||||
class LuarocksTypeDefinition(BasePurlType):
|
||||
type = "luarocks"
|
||||
type_name = "LuaRocks"
|
||||
description = """Lua packages installed with LuaRocks"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "optional"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:luarocks/.*"
|
||||
|
||||
|
||||
class MavenTypeDefinition(BasePurlType):
|
||||
type = "maven"
|
||||
type_name = "Maven"
|
||||
description = """PURL type for Maven JARs and related artifacts."""
|
||||
use_repository = True
|
||||
default_repository_url = "https://repo.maven.apache.org/maven2/"
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url", "type", "classifier"}
|
||||
namespace_case_sensitive = True
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:maven/.*"
|
||||
|
||||
|
||||
class MlflowTypeDefinition(BasePurlType):
|
||||
type = "mlflow"
|
||||
type_name = ""
|
||||
description = """MLflow ML models (Azure ML, Databricks, etc.)"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"repository_url", "run_id", "model_uuid"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:mlflow/.*"
|
||||
|
||||
|
||||
class NpmTypeDefinition(BasePurlType):
|
||||
type = "npm"
|
||||
type_name = "Node NPM packages"
|
||||
description = """PURL type for npm packages."""
|
||||
use_repository = True
|
||||
default_repository_url = "https://registry.npmjs.org/"
|
||||
namespace_requirement = "optional"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:npm/.*"
|
||||
|
||||
|
||||
class NugetTypeDefinition(BasePurlType):
|
||||
type = "nuget"
|
||||
type_name = "NuGet"
|
||||
description = """NuGet .NET packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://www.nuget.org"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:nuget/.*"
|
||||
|
||||
|
||||
class OciTypeDefinition(BasePurlType):
|
||||
type = "oci"
|
||||
type_name = "OCI image"
|
||||
description = """For artifacts stored in registries that conform to the OCI Distribution Specification https://github.com/opencontainers/distribution-spec including container images built by Docker and others"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"repository_url", "tag", "arch"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:oci/.*"
|
||||
|
||||
|
||||
class PubTypeDefinition(BasePurlType):
|
||||
type = "pub"
|
||||
type_name = "Pub"
|
||||
description = """Dart and Flutter pub packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://pub.dartlang.org"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:pub/.*"
|
||||
|
||||
@classmethod
|
||||
def validate_using_type_rules(cls, purl, strict=False):
|
||||
from packageurl import ValidationMessage
|
||||
from packageurl import ValidationSeverity
|
||||
|
||||
if not all(c.isalnum() or c == "_" for c in purl.name):
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.WARNING,
|
||||
message=f"Name contains invalid characters but should only contain letters, digits, or underscores for purl type: {cls.type!r}",
|
||||
)
|
||||
|
||||
if " " in purl.name:
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.WARNING,
|
||||
message=f"Name contains spaces but should use underscores instead for purl type: {cls.type!r}",
|
||||
)
|
||||
messages = super().validate_using_type_rules(purl, strict)
|
||||
if messages:
|
||||
yield from messages
|
||||
|
||||
|
||||
class PypiTypeDefinition(BasePurlType):
|
||||
type = "pypi"
|
||||
type_name = "PyPI"
|
||||
description = """Python packages"""
|
||||
use_repository = True
|
||||
default_repository_url = "https://pypi.org"
|
||||
namespace_requirement = "prohibited"
|
||||
allowed_qualifiers = {"file_name", "repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:pypi/.*"
|
||||
|
||||
@classmethod
|
||||
def validate_using_type_rules(cls, purl, strict=False):
|
||||
from packageurl import ValidationMessage
|
||||
from packageurl import ValidationSeverity
|
||||
|
||||
if "_" in purl.name:
|
||||
yield ValidationMessage(
|
||||
severity=ValidationSeverity.WARNING,
|
||||
message=f"Name cannot contain underscores for purl type:{cls.type!r}",
|
||||
)
|
||||
messages = super().validate_using_type_rules(purl, strict)
|
||||
if messages:
|
||||
yield from messages
|
||||
|
||||
|
||||
class QpkgTypeDefinition(BasePurlType):
|
||||
type = "qpkg"
|
||||
type_name = "QNX package"
|
||||
description = """QNX packages"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = False
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:qpkg/.*"
|
||||
|
||||
|
||||
class RpmTypeDefinition(BasePurlType):
|
||||
type = "rpm"
|
||||
type_name = "RPM"
|
||||
description = """RPM packages"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url", "arch", "epoch"}
|
||||
namespace_case_sensitive = False
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:rpm/.*"
|
||||
|
||||
|
||||
class SwidTypeDefinition(BasePurlType):
|
||||
type = "swid"
|
||||
type_name = "Software Identification (SWID) Tag"
|
||||
description = """PURL type for ISO-IEC 19770-2 Software Identification (SWID) tags."""
|
||||
use_repository = False
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "optional"
|
||||
allowed_qualifiers = {"tag_creator_name", "tag_creator_regid", "tag_version", "tag_id", "patch"}
|
||||
namespace_case_sensitive = True
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:swid/.*"
|
||||
|
||||
|
||||
class SwiftTypeDefinition(BasePurlType):
|
||||
type = "swift"
|
||||
type_name = "Swift packages"
|
||||
description = """Swift packages"""
|
||||
use_repository = True
|
||||
default_repository_url = ""
|
||||
namespace_requirement = "required"
|
||||
allowed_qualifiers = {"repository_url"}
|
||||
namespace_case_sensitive = True
|
||||
name_case_sensitive = True
|
||||
version_case_sensitive = True
|
||||
purl_pattern = "pkg:swift/.*"
|
||||
|
||||
|
||||
DEFINITIONS_BY_TYPE = {
|
||||
"alpm": AlpmTypeDefinition,
|
||||
"apk": ApkTypeDefinition,
|
||||
"bitbucket": BitbucketTypeDefinition,
|
||||
"bitnami": BitnamiTypeDefinition,
|
||||
"cargo": CargoTypeDefinition,
|
||||
"cocoapods": CocoapodsTypeDefinition,
|
||||
"composer": ComposerTypeDefinition,
|
||||
"conan": ConanTypeDefinition,
|
||||
"conda": CondaTypeDefinition,
|
||||
"cpan": CpanTypeDefinition,
|
||||
"cran": CranTypeDefinition,
|
||||
"deb": DebTypeDefinition,
|
||||
"docker": DockerTypeDefinition,
|
||||
"gem": GemTypeDefinition,
|
||||
"generic": GenericTypeDefinition,
|
||||
"github": GithubTypeDefinition,
|
||||
"golang": GolangTypeDefinition,
|
||||
"hackage": HackageTypeDefinition,
|
||||
"hex": HexTypeDefinition,
|
||||
"huggingface": HuggingfaceTypeDefinition,
|
||||
"luarocks": LuarocksTypeDefinition,
|
||||
"maven": MavenTypeDefinition,
|
||||
"mlflow": MlflowTypeDefinition,
|
||||
"npm": NpmTypeDefinition,
|
||||
"nuget": NugetTypeDefinition,
|
||||
"oci": OciTypeDefinition,
|
||||
"pub": PubTypeDefinition,
|
||||
"pypi": PypiTypeDefinition,
|
||||
"qpkg": QpkgTypeDefinition,
|
||||
"rpm": RpmTypeDefinition,
|
||||
"swid": SwidTypeDefinition,
|
||||
"swift": SwiftTypeDefinition,
|
||||
}
|
||||
Reference in New Issue
Block a user