Skip to content

Commit 5195460

Browse files
EmilienMclaude
andcommitted
feat: Rewrite license-checker skill to use Fedora License Data
Replace SPDX OSI-approval classification with Fedora License Data as the authoritative policy source for Red Hat redistribution decisions. License detection is now deterministic: the skill clones the source repo via python-packaging-source-finder and git-shallow-clone, then reads LICENSE files directly instead of relying on PyPI metadata. This addresses seven documented gaps: correct policy basis, AGPL-3.0 caveat handling, dual-license AND/OR parsing, source repo verification with mismatch detection, proprietary license escalation paths, build compliance flagging, and export compliance flagging. Output is a structured six-field verdict block. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Emilien Macchi <emacchi@redhat.com>
1 parent 0d41f30 commit 5195460

File tree

2 files changed

+182
-128
lines changed

2 files changed

+182
-128
lines changed

docs/data.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@
142142
},
143143
{
144144
"name": "python-packaging-license-checker",
145-
"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.",
145+
"description": "Check whether a Python package license is compatible with redistribution in Red Hat products, using the Fedora License Data as the authoritative policy source. Produces a structured six-field verdict with escalation guidance for non-trivial cases.",
146146
"category": "general",
147147
"file_path": "helpers/skills/python-packaging-license-checker/SKILL.md",
148148
"id": "python-packaging-license-checker",
149-
"allowed_tools": "WebFetch"
149+
"allowed_tools": "Bash Skill WebFetch"
150150
},
151151
{
152152
"name": "python-packaging-license-finder",
Lines changed: 180 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,161 +1,215 @@
11
---
22
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
57
---
68

79
# Python Package License Compatibility Checker
810

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.
1012

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.
4216

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
4818

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)
5022

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
5724

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:
6726

68-
### License Pattern Definitions
27+
```text
28+
Skill: python-packaging-source-finder
29+
```
6930

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:
7132

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+
```
7536

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.
7938

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
8340

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
8543

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+
```
8749

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.
8951

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.
9456

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.
9962

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.
10569

106-
### Output Format
70+
If no license file found and no SPDX mapping possible: Verdict: NEEDS-HUMAN-REVIEW.
10771

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:
12573

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+
```
12681

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
13083

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:
13485

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+
```
13689

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:
13893

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
14199

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):
148101

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 |
155110

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 |
157206

158-
## Integration Notes
207+
## Error Handling
159208

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

Comments
 (0)