133 lines
3.3 KiB
Python
133 lines
3.3 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from depaudit.checks import LicenseInfo
|
|
|
|
SPDX_LICENSES = {
|
|
"MIT": "mit",
|
|
"Apache-2.0": "apache-2.0",
|
|
"Apache-2": "apache-2.0",
|
|
"BSD-2-Clause": "bsd-2-clause",
|
|
"BSD-3-Clause": "bsd-3-clause",
|
|
"BSD": "bsd",
|
|
"ISC": "isc",
|
|
"GPL-2.0": "gpl-2.0",
|
|
"GPL-3.0": "gpl-3.0",
|
|
"GPL-3": "gpl-3.0",
|
|
"LGPL-2.1": "lgpl-2.1",
|
|
"LGPL-3.0": "lgpl-3.0",
|
|
"MPL-2.0": "mpl-2.0",
|
|
"MPL-2": "mpl-2.0",
|
|
"AGPL-3.0": "agpl-3.0",
|
|
"AGPL-3": "agpl-3.0",
|
|
"OSL-3.0": "osl-3.0",
|
|
"CC0": "cc0-1.0",
|
|
"CC-BY-4.0": "cc-by-4.0",
|
|
"CC-BY-SA-4.0": "cc-by-sa-4.0",
|
|
}
|
|
|
|
RESTRICTIVE_LICENSES = {
|
|
"GPL-1.0",
|
|
"GPL-2.0",
|
|
"GPL-3.0",
|
|
"AGPL-1.0",
|
|
"AGPL-3.0",
|
|
"SSPL",
|
|
"OSL-1.0",
|
|
"OSL-2.0",
|
|
"OSL-3.0",
|
|
"QPL-1.0",
|
|
"CPOL-1.02",
|
|
}
|
|
|
|
PERMISSIVE_LICENSES = {
|
|
"MIT",
|
|
"Apache-2.0",
|
|
"BSD-2-Clause",
|
|
"BSD-3-Clause",
|
|
"ISC",
|
|
"MPL-2.0",
|
|
"CC0",
|
|
"Unlicense",
|
|
}
|
|
|
|
|
|
def normalize_license(license_str: str) -> str:
|
|
license_str = license_str.strip()
|
|
|
|
if "OR" in license_str or "AND" in license_str:
|
|
licenses = []
|
|
for lic in license_str.replace("(", "").replace(")", "").split():
|
|
if lic in SPDX_LICENSES:
|
|
licenses.append(SPDX_LICENSES[lic])
|
|
elif lic.upper() in SPDX_LICENSES:
|
|
licenses.append(SPDX_LICENSES[lic.upper()])
|
|
return " AND ".join(licenses) if licenses else license_str
|
|
|
|
if license_str.upper() in SPDX_LICENSES:
|
|
return SPDX_LICENSES[license_str.upper()]
|
|
|
|
for spdx_name, alias in SPDX_LICENSES.items():
|
|
if license_str.lower() == spdx_name.lower() or license_str.lower() == alias.lower():
|
|
return alias
|
|
|
|
return license_str
|
|
|
|
|
|
def check_license(
|
|
package_name: str,
|
|
license_str: str | None,
|
|
source: str = "unknown",
|
|
allowlist: list[str] | None = None,
|
|
blocklist: list[str] | None = None,
|
|
) -> LicenseInfo:
|
|
allowlist = allowlist or []
|
|
blocklist = blocklist or []
|
|
|
|
normalized = None
|
|
if license_str:
|
|
normalized = normalize_license(license_str)
|
|
|
|
is_spdx = normalized in SPDX_LICENSES.values() if normalized else False
|
|
|
|
license_info = LicenseInfo(
|
|
package_name=package_name,
|
|
license_type=normalized or license_str,
|
|
license_expression=None,
|
|
source=source,
|
|
is_spdx_compliant=is_spdx,
|
|
)
|
|
|
|
if normalized in RESTRICTIVE_LICENSES:
|
|
license_info.license_expression = "restricted"
|
|
|
|
return license_info
|
|
|
|
|
|
def validate_license_compliance(
|
|
license_info: LicenseInfo,
|
|
allowlist: list[str],
|
|
blocklist: list[str],
|
|
) -> tuple[bool, str]:
|
|
license_type = license_info.license_type
|
|
|
|
if license_type is None:
|
|
return False, "Unknown license"
|
|
|
|
normalized = normalize_license(license_type)
|
|
normalized_blocklist = {normalize_license(lic) for lic in blocklist}
|
|
normalized_allowlist = {normalize_license(lic) for lic in allowlist}
|
|
|
|
if normalized in normalized_blocklist:
|
|
return False, f"License {normalized} is in blocklist"
|
|
|
|
if normalized in normalized_allowlist:
|
|
return True, "License is allowed"
|
|
|
|
if normalized in SPDX_LICENSES.values():
|
|
return True, "SPDX compliant license"
|
|
|
|
return False, f"License {license_type} is not in allowlist"
|