|
1 | 1 | --- |
2 | 2 | name: python-packaging-license-checker |
3 | | -description: Assess license compatibility for Python package redistribution using SPDX.org license database. Evaluates whether a given license allows building and distributing wheels, with real-time license information lookup. |
4 | | -allowed-tools: WebFetch |
| 3 | +description: Check whether a Python package license is compatible with redistribution |
| 4 | + in Red Hat products, using the Fedora License Data as the authoritative policy source. |
| 5 | + Produces a structured six-field verdict with escalation guidance for non-trivial cases. |
| 6 | +allowed-tools: Bash Skill WebFetch |
5 | 7 | --- |
6 | 8 |
|
7 | 9 | # Python Package License Compatibility Checker |
8 | 10 |
|
9 | | -This skill helps you evaluate whether a Python package license is compatible with redistribution, particularly for building and distributing wheels in enterprise environments. It uses the authoritative SPDX License List for accurate, up-to-date license information. |
| 11 | +Evaluate whether a Python package license permits redistribution in Red Hat products. |
10 | 12 |
|
11 | | -## Assessment Instructions |
12 | | - |
13 | | -When a user provides a license name and asks about compatibility for redistribution, building wheels, or licensing restrictions, follow this methodology: |
14 | | - |
15 | | -### Step-by-Step Process |
16 | | - |
17 | | -1. **Fetch Current SPDX Data**: |
18 | | - ``` |
19 | | - Use WebFetch to query: https://raw.githubusercontent.com/spdx/license-list-data/main/json/licenses.json |
20 | | - ``` |
21 | | - |
22 | | -2. **License Matching**: |
23 | | - - Try exact SPDX ID match first |
24 | | - - Try case-insensitive SPDX ID match |
25 | | - - Try full name matching |
26 | | - - Try partial/fuzzy matching for common variations |
27 | | - |
28 | | -3. **Risk Classification** (check strong copyleft FIRST to prevent misclassification): |
29 | | - ``` |
30 | | - IF (strong_copyleft_pattern): |
31 | | - Risk = High, Status = Restricted/Incompatible |
32 | | - # GPL, AGPL are ALWAYS restricted regardless of OSI/FSF status |
33 | | - ELIF (NOT isOsiApproved): |
34 | | - Risk = High, Status = Restricted/Incompatible |
35 | | - ELIF (isOsiApproved AND weak_copyleft_pattern): |
36 | | - Risk = Medium, Status = Compatible with Requirements |
37 | | - ELIF (isOsiApproved AND isFsfLibre AND permissive_pattern): |
38 | | - Risk = Low, Status = Compatible |
39 | | - ELSE: |
40 | | - Risk = High, Status = Unknown - requires manual review |
41 | | - ``` |
| 13 | +**Policy basis**: Red Hat redistribution policy follows the Fedora Linux license |
| 14 | +policy. Use the Fedora License Data as the authoritative source -- NOT OSI-approval |
| 15 | +status. |
42 | 16 |
|
43 | | -4. **Generate Assessment**: |
44 | | - - Include all SPDX metadata |
45 | | - - Provide clear compatibility guidance |
46 | | - - List specific requirements |
47 | | - - Add Red Hat context where relevant |
| 17 | +## Inputs |
48 | 18 |
|
49 | | -## License Assessment Framework |
| 19 | +- **package_name**: Python package name |
| 20 | +- **source_available**: whether buildable source exists (if already known) |
| 21 | +- **upstream_org**: name and primary country of the maintaining organisation (if already known) |
50 | 22 |
|
51 | | -### Input Processing |
52 | | -Accept various formats and normalize them: |
53 | | -- **SPDX Identifiers**: "MIT", "Apache-2.0", "GPL-3.0-only" |
54 | | -- **Full Names**: "MIT License", "Apache License 2.0", "GNU General Public License v3.0" |
55 | | -- **Common Aliases**: "Apache 2", "BSD 3-Clause", "GPLv3" |
56 | | -- **Case Variations**: Handle case-insensitive matching |
| 23 | +## Step 1: Locate and clone the source repository |
57 | 24 |
|
58 | | -### SPDX Data Analysis |
59 | | -When processing SPDX license data, examine these key fields: |
60 | | -- `licenseId`: Official SPDX identifier |
61 | | -- `name`: Full license name |
62 | | -- `isOsiApproved`: OSI approval status (boolean) |
63 | | -- `isFsfLibre`: FSF Free Software status (boolean) |
64 | | -- `isDeprecatedLicenseId`: Whether license is deprecated (boolean) |
65 | | -- `reference`: URL to full license details |
66 | | -- `seeAlso`: Array of additional reference URLs |
| 25 | +Use the python-packaging-source-finder skill to find the upstream repo URL: |
67 | 26 |
|
68 | | -### License Pattern Definitions |
| 27 | +```text |
| 28 | +Skill: python-packaging-source-finder |
| 29 | +``` |
69 | 30 |
|
70 | | -Use these explicit pattern lists for classification. Match against the SPDX `licenseId` field (case-insensitive). |
| 31 | +Then use the git-shallow-clone skill to clone the repository locally: |
71 | 32 |
|
72 | | -#### Permissive Patterns (permissive_pattern) |
73 | | -Licenses where the `licenseId` contains or matches any of: |
74 | | -- `MIT`, `Apache-`, `BSD-`, `ISC`, `Unlicense`, `0BSD`, `PSF-`, `Python-`, `Zlib`, `BSL-1.0`, `CC0-`, `WTFPL`, `MulanPSL-` |
| 33 | +```text |
| 34 | +Skill: git-shallow-clone |
| 35 | +``` |
75 | 36 |
|
76 | | -#### Weak Copyleft Patterns (weak_copyleft_pattern) |
77 | | -Licenses where the `licenseId` contains or matches any of: |
78 | | -- `LGPL-`, `MPL-`, `EPL-`, `CDDL-`, `CPL-`, `CeCILL-2.1`, `EUPL-` |
| 37 | +This gives a local path to the cloned repo for deterministic license inspection. |
79 | 38 |
|
80 | | -#### Strong Copyleft Patterns (strong_copyleft_pattern) |
81 | | -Licenses where the `licenseId` contains or matches any of: |
82 | | -- `GPL-` (but NOT `LGPL-`), `AGPL-`, `SSPL-`, `OSL-`, `CeCILL-` (but NOT `CeCILL-2.1`), `EUPL-` (when used with strong copyleft intent) |
| 39 | +## Step 2: Read the license from source |
83 | 40 |
|
84 | | -**CRITICAL**: `GPL-2.0`, `GPL-3.0`, `GPL-2.0-only`, `GPL-2.0-or-later`, `GPL-3.0-only`, `GPL-3.0-or-later` are ALL strong copyleft. They are NOT permissive. They MUST be classified as Restricted/Incompatible for commercial wheel redistribution. |
| 41 | +Search the cloned repo root for license files in this order: |
| 42 | +LICENSE, LICENSE.txt, LICENSE.md, COPYING, COPYING.txt, LICENCE, LICENCE.txt |
85 | 43 |
|
86 | | -### Compatibility Assessment Logic |
| 44 | +```bash |
| 45 | +for f in LICENSE LICENSE.txt LICENSE.md COPYING COPYING.txt LICENCE LICENCE.txt; do |
| 46 | + if [ -f "<clone_path>/$f" ]; then echo "<clone_path>/$f"; break; fi |
| 47 | +done |
| 48 | +``` |
87 | 49 |
|
88 | | -Use SPDX flags and the pattern definitions above to determine compatibility: |
| 50 | +Read the first 30 lines of the found file and map to an SPDX identifier. |
89 | 51 |
|
90 | | -#### ✅ Highly Compatible (Low Risk) |
91 | | -- OSI Approved AND FSF Libre AND matches permissive_pattern |
92 | | -- Examples: MIT, Apache-2.0, BSD-3-Clause, ISC, PSF-2.0 |
93 | | -- No copyleft requirements of any kind |
| 52 | +Also check `pyproject.toml` or `setup.cfg` for a `license` field as a secondary |
| 53 | +signal. If the PyPI metadata license differs from the source file, set |
| 54 | +Source verified: MISMATCH (PyPI: [X], repo: [Y]) and use the source file as |
| 55 | +authoritative. |
94 | 56 |
|
95 | | -#### ⚠️ Compatible with Requirements (Medium Risk) |
96 | | -- OSI Approved AND matches weak_copyleft_pattern |
97 | | -- Examples: LGPL-2.1-only, LGPL-3.0-or-later, MPL-2.0 |
98 | | -- File-level or library-level copyleft only |
| 57 | +**Normalise to SPDX ID** using exact then fuzzy matching: |
| 58 | +- Deterministic: "Apache 2" -> Apache-2.0, "GPL v2" -> GPL-2.0-only, |
| 59 | + "MIT License" -> MIT, "LGPLv2.1" -> LGPL-2.1-only |
| 60 | +- Ambiguous (do NOT auto-map): "BSD", "GPLv3" -> set NEEDS-HUMAN-REVIEW. |
| 61 | + These tokens map to multiple SPDX IDs; require human confirmation. |
99 | 62 |
|
100 | | -#### ❌ Restricted/High Risk |
101 | | -- Matches strong_copyleft_pattern (GPL, AGPL) — regardless of OSI or FSF status |
102 | | -- Non-OSI approved licenses |
103 | | -- Proprietary or unclear terms |
104 | | -- GPL licenses require ALL derivative works to be released under the same GPL license, making them incompatible with proprietary or commercial redistribution of binary wheels |
| 63 | +**Compound expressions**: If the license contains `AND` or `OR`: |
| 64 | +- `AND`: split into components; every component must be individually allowed. |
| 65 | + If any single component is `not-allowed`, the whole expression is BLOCKED. |
| 66 | + Document the split in Notes. |
| 67 | +- `OR`: evaluate each component; use the most permissive allowed one. If none are |
| 68 | + allowed, the overall verdict is the best individual verdict. |
105 | 69 |
|
106 | | -### Output Format |
| 70 | +If no license file found and no SPDX mapping possible: Verdict: NEEDS-HUMAN-REVIEW. |
107 | 71 |
|
108 | | -Provide a structured assessment with: |
109 | | - |
110 | | -1. **SPDX Information**: |
111 | | - - Official SPDX ID |
112 | | - - Full license name |
113 | | - - OSI Approved: Yes/No |
114 | | - - FSF Libre: Yes/No |
115 | | - - Deprecated: Yes/No (if applicable) |
116 | | - |
117 | | -2. **Compatibility Assessment**: |
118 | | - - Status: Compatible/Restricted/Incompatible |
119 | | - - Redistribution: Allowed/Restricted/Prohibited |
120 | | - - Commercial Use: Allowed/Restricted/Prohibited |
121 | | - |
122 | | -3. **Requirements**: Key compliance obligations |
123 | | -4. **Risk Level**: Low/Medium/High for enterprise use |
124 | | -5. **Red Hat Context**: Special considerations if applicable |
| 72 | +Clean up the clone when done: |
125 | 73 |
|
| 74 | +```bash |
| 75 | +if [ -n "${CLONE_DIR}" ] && echo "${CLONE_DIR}" | grep -q '^/tmp/'; then |
| 76 | + rm -rf -- "${CLONE_DIR}" |
| 77 | +else |
| 78 | + echo "ERROR: refusing to delete '${CLONE_DIR}' -- not under /tmp/" >&2 |
| 79 | +fi |
| 80 | +``` |
126 | 81 |
|
127 | | -## Red Hat Vendor Agreements |
128 | | - |
129 | | -Red Hat has specific licensing agreements with the following hardware vendors: |
| 82 | +## Step 3: Look up license in Fedora License Data |
130 | 83 |
|
131 | | -- **NVIDIA**: Agreement covers CUDA libraries, runtimes, and related NVIDIA proprietary components |
132 | | -- **Intel Gaudi**: Agreement covers Gaudi AI accelerator software and libraries |
133 | | -- **IBM Spyre**: Agreement covers IBM Spyre AI hardware and associated software components |
| 84 | +Fetch the live data: |
134 | 85 |
|
135 | | -When evaluating packages with dependencies on these vendor-specific components, note that Red Hat has explicit redistribution rights under these agreements. |
| 86 | +```text |
| 87 | +WebFetch: https://gitlab.com/fedora/legal/fedora-license-data/-/jobs/artifacts/main/raw/fedora-licenses.json?job=json |
| 88 | +``` |
136 | 89 |
|
137 | | -## Error Handling |
| 90 | +Search for an entry matching the SPDX ID (case-insensitive on `spdx_abbrev`). |
| 91 | +Each entry has a `status` field which may be a string or an array. Normalise to |
| 92 | +an array and apply fail-closed logic: |
138 | 93 |
|
139 | | -### SPDX Data Fetch Failures |
140 | | -If the SPDX license list cannot be retrieved, exit early and warn the user. |
| 94 | +- If any entry is `"not-allowed"` -> Verdict: BLOCKED |
| 95 | +- If all entries are `"allowed"` (and none `"not-allowed"`) -> proceed to Step 4 |
| 96 | +- If the field is missing, empty, or contains unrecognised values -> Verdict: |
| 97 | + NEEDS-HUMAN-REVIEW |
| 98 | +- SPDX ID not found in data -> Verdict: NEEDS-HUMAN-REVIEW |
141 | 99 |
|
142 | | -### License Not Found in SPDX |
143 | | -When a license identifier is not found in the SPDX license list: |
144 | | -1. Check for common typos or variations |
145 | | -2. Suggest SPDX-compliant alternatives |
146 | | -3. Recommend contacting package maintainer |
147 | | -4. Provide conservative risk assessment |
| 100 | +**Fallback table** (use only when the JSON fetch fails): |
148 | 101 |
|
149 | | -### Deprecated Licenses |
150 | | -For deprecated SPDX licenses: |
151 | | -1. Note the deprecation status |
152 | | -2. Suggest migrating to current equivalent |
153 | | -3. Provide assessment based on deprecated license terms |
154 | | -4. Recommend updating package licensing |
| 102 | +| Status | SPDX identifiers (representative) | |
| 103 | +|---|---| |
| 104 | +| allowed | MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, PSF-2.0, CC0-1.0 | |
| 105 | +| allowed | GPL-2.0-only, GPL-2.0-or-later, GPL-3.0-only, GPL-3.0-or-later | |
| 106 | +| allowed | LGPL-2.0-only, LGPL-2.1-only, LGPL-2.1-or-later, LGPL-3.0-or-later | |
| 107 | +| allowed | AGPL-3.0-only, AGPL-3.0-or-later, MPL-2.0, EUPL-1.2, EPL-2.0 | |
| 108 | +| not-allowed | SSPL-1.0, BUSL-1.1, Commons-Clause | |
| 109 | +| ESCALATE | Any custom/proprietary license not in SPDX | |
155 | 110 |
|
156 | | -For complex licensing scenarios involving multiple packages or custom license terms, recommend consultation with legal counsel. |
| 111 | +When using fallback, append to Notes: "Fedora License Data unreachable; fallback |
| 112 | +table used. Verify at https://docs.fedoraproject.org/en-US/legal/allowed-licenses/" |
| 113 | + |
| 114 | +## Step 4: Verdict refinement (apply in order) |
| 115 | + |
| 116 | +**Rule A -- Proprietary or custom license** (not in Fedora data): |
| 117 | + |
| 118 | +First check the Vendor Agreements table below. If covered by an agreement: |
| 119 | +Verdict: ALLOWED, Notes: "Covered by Red Hat [Vendor] redistribution agreement." |
| 120 | + |
| 121 | +Otherwise: |
| 122 | +- Red Hat / IBM-owned upstream (e.g. Neural Magic): |
| 123 | + Verdict: ESCALATE-TO-LEGAL. |
| 124 | + Notes: "Red Hat-owned proprietary license. Must be relicensed before inclusion. |
| 125 | + Open a ServiceNow opensource-legal ticket." |
| 126 | +- Third-party proprietary: |
| 127 | + Verdict: ESCALATE-TO-LEGAL. |
| 128 | + Notes: "Third-party proprietary license. May be includable as a EULA-listed |
| 129 | + component. Open a legal-review task." |
| 130 | + |
| 131 | +**Rule B -- AGPL-3.0** (status=allowed, SPDX is AGPL-3.0-only or -or-later): |
| 132 | +Verdict: ALLOWED-WITH-CAVEAT. |
| 133 | +Notes: "AGPL-3.0 is allowed for redistribution but is frequently part of a |
| 134 | +dual-licensing strategy, increasing risk. PM sign-off required." |
| 135 | + |
| 136 | +**Rule C -- All other allowed licenses**: |
| 137 | +Verdict: ALLOWED. Notes: omit unless compliance note needed (e.g. LGPL dynamic |
| 138 | +linking does not propagate copyleft). |
| 139 | + |
| 140 | +**Rule D -- Not-allowed**: |
| 141 | +Verdict: BLOCKED. |
| 142 | +Notes: "License is on the Fedora not-allowed list." |
| 143 | + |
| 144 | +## Step 5: Compliance checks |
| 145 | + |
| 146 | +**Build compliance** (independent of license verdict): |
| 147 | +If `source_available` is false or no buildable source exists (no setup.py, |
| 148 | +pyproject.toml, or equivalent): |
| 149 | +- Build compliance: BUILD-COMPLIANCE-FLAG |
| 150 | +- Append to Notes: "No buildable source. Options: (1) request upstream sdist, |
| 151 | + (2) make dependency optional, |
| 152 | + (3) file PSX exception with 6-month deadline per PSS.SBR.02.02." |
| 153 | +- Otherwise: Build compliance: OK |
| 154 | + |
| 155 | +**Export compliance** (independent of license verdict): |
| 156 | +Check the upstream org's primary country against these authoritative sources: |
| 157 | +- US OFAC SDN List: https://sanctionssearch.ofac.treas.gov/ |
| 158 | +- EU Consolidated Sanctions: https://data.europa.eu/data/datasets/consolidated-list-of-persons-groups-and-entities-subject-to-eu-financial-sanctions |
| 159 | +- Red Hat geopolitical policy: consult exportcompliance@redhat.com |
| 160 | + |
| 161 | +If flagged by any source: |
| 162 | +- Export compliance: EXPORT-COMPLIANCE-FLAG |
| 163 | +- Append to Notes: "Upstream org may require export compliance review. Contact |
| 164 | + exportcompliance@redhat.com. Source: <URL checked>; Fetched: <ISO8601 timestamp>. |
| 165 | + Geopolitical sensitivity does not automatically block inclusion." |
| 166 | + |
| 167 | +If sources are unreachable after 2 retries: |
| 168 | +- Export compliance: EXPORT-COMPLIANCE-FLAG |
| 169 | +- Append to Notes: "Export sanctions data unreachable. _Verdict: NEEDS-HUMAN-REVIEW_" |
| 170 | + |
| 171 | +Otherwise: Export compliance: OK |
| 172 | + |
| 173 | +## Output Format |
| 174 | + |
| 175 | +Produce ONLY this six-field block. No preamble, no extra text. |
| 176 | + |
| 177 | +```text |
| 178 | +_License:_ [SPDX expression, or "non-SPDX: [raw string]" if unmappable] |
| 179 | +_Source verified:_ YES | NO | MISMATCH (PyPI: [X], repo: [Y]) |
| 180 | +_Verdict:_ ALLOWED | ALLOWED-WITH-CAVEAT | NEEDS-HUMAN-REVIEW | ESCALATE-TO-LEGAL | BLOCKED |
| 181 | +_Build compliance:_ OK | BUILD-COMPLIANCE-FLAG |
| 182 | +_Export compliance:_ OK | EXPORT-COMPLIANCE-FLAG |
| 183 | +_Notes:_ [Actionable guidance. Omit if verdict is ALLOWED and all flags are OK.] |
| 184 | +``` |
| 185 | + |
| 186 | +## Vendor Agreements |
| 187 | + |
| 188 | +| Vendor | Covered components | |
| 189 | +|---|---| |
| 190 | +| NVIDIA | CUDA libraries, runtimes, NCCL, and related NVIDIA components | |
| 191 | +| Intel Gaudi | Gaudi AI accelerator software and libraries | |
| 192 | +| IBM Spyre | IBM Spyre AI hardware and associated software components | |
| 193 | + |
| 194 | +## Quick Reference |
| 195 | + |
| 196 | +| Scenario | Verdict | Build | |
| 197 | +|---|---|---| |
| 198 | +| MIT, Apache-2.0, BSD-*, ISC, PSF-2.0 | ALLOWED | OK | |
| 199 | +| GPL-*, LGPL-* | ALLOWED | OK | |
| 200 | +| AGPL-3.0-* | ALLOWED-WITH-CAVEAT | OK | |
| 201 | +| SSPL-1.0, BUSL-1.1, Commons-Clause | BLOCKED | -- | |
| 202 | +| RH-owned proprietary | ESCALATE-TO-LEGAL | -- | |
| 203 | +| Third-party proprietary (no vendor agreement) | ESCALATE-TO-LEGAL | -- | |
| 204 | +| PyPI/repo license mismatch | per repo license (MISMATCH noted) | OK | |
| 205 | +| No buildable source | per license | FLAG | |
157 | 206 |
|
158 | | -## Integration Notes |
| 207 | +## Error Handling |
159 | 208 |
|
160 | | -This skill works best when combined with: |
161 | | -- **python-packaging-license-finder** - Use to find license names before compatibility assessment |
| 209 | +- **Fedora data unreachable**: Use fallback table; append note. |
| 210 | +- **Source repo not found**: If python-packaging-source-finder returns null/low |
| 211 | + confidence, set Source verified: NO, verdict: NEEDS-HUMAN-REVIEW. |
| 212 | +- **Source repo private/inaccessible**: Set Source verified: NO. Verdict: |
| 213 | + NEEDS-HUMAN-REVIEW. |
| 214 | +- **Multiple conflicting LICENSE files**: Source verified: MISMATCH, verdict: |
| 215 | + NEEDS-HUMAN-REVIEW. Note the conflicting files. |
0 commit comments