license_policy.py
python
sha256:1d3182b1c5c2833c8ab0d437e03eeadfd3ad6679a2877a6baab8d17442d38936
Bootstrap Scooling Lab repository
Human
patch
43 days ago
| 1 | """License policy helpers for Scooling Lab dependency inventory checks.""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | from dataclasses import dataclass |
| 6 | from pathlib import PurePosixPath |
| 7 | from typing import Iterable |
| 8 | |
| 9 | |
| 10 | APPROVED_LICENSES = frozenset({"apache-2.0", "mit", "bsd-2-clause", "bsd-3-clause"}) |
| 11 | DISALLOWED_PATH_PREFIXES = ("studio", "unsloth_cli") |
| 12 | |
| 13 | |
| 14 | @dataclass(frozen=True, slots=True) |
| 15 | class DependencyEntry: |
| 16 | """Single dependency or source path observed by an inventory scan.""" |
| 17 | |
| 18 | name: str |
| 19 | license_id: str |
| 20 | source_path: str |
| 21 | |
| 22 | |
| 23 | @dataclass(frozen=True, slots=True) |
| 24 | class LicenseAuditResult: |
| 25 | """Deterministic result for a dependency policy audit.""" |
| 26 | |
| 27 | accepted: tuple[DependencyEntry, ...] |
| 28 | rejected: tuple[DependencyEntry, ...] |
| 29 | |
| 30 | @property |
| 31 | def ok(self) -> bool: |
| 32 | """Return true when every scanned dependency passed policy.""" |
| 33 | |
| 34 | return len(self.rejected) == 0 |
| 35 | |
| 36 | |
| 37 | def normalize_license_id(license_id: str) -> str: |
| 38 | """Normalize SPDX-like license names for allowlist matching.""" |
| 39 | |
| 40 | return license_id.strip().lower() |
| 41 | |
| 42 | |
| 43 | def is_approved_license(license_id: str) -> bool: |
| 44 | """Return true when a license is allowed for the initial worker lane.""" |
| 45 | |
| 46 | return normalize_license_id(license_id) in APPROVED_LICENSES |
| 47 | |
| 48 | |
| 49 | def is_allowed_source_path(source_path: str) -> bool: |
| 50 | """Return false for known AGPL-covered or product-prohibited path prefixes.""" |
| 51 | |
| 52 | path = PurePosixPath(source_path.strip()) |
| 53 | first_part = path.parts[0] if path.parts else "" |
| 54 | |
| 55 | return first_part not in DISALLOWED_PATH_PREFIXES |
| 56 | |
| 57 | |
| 58 | def audit_dependency_entries(entries: Iterable[DependencyEntry]) -> LicenseAuditResult: |
| 59 | """Split dependency entries into accepted and rejected policy buckets.""" |
| 60 | |
| 61 | accepted: list[DependencyEntry] = [] |
| 62 | rejected: list[DependencyEntry] = [] |
| 63 | |
| 64 | for entry in entries: |
| 65 | if is_approved_license(entry.license_id) and is_allowed_source_path(entry.source_path): |
| 66 | accepted.append(entry) |
| 67 | else: |
| 68 | rejected.append(entry) |
| 69 | |
| 70 | return LicenseAuditResult(accepted=tuple(accepted), rejected=tuple(rejected)) |
File History
1 commit
sha256:1d3182b1c5c2833c8ab0d437e03eeadfd3ad6679a2877a6baab8d17442d38936
Bootstrap Scooling Lab repository
Human
patch
43 days ago