|
23 | 23 | from re import compile as re_compile |
24 | 24 | from typing import Any, Optional |
25 | 25 |
|
| 26 | +from cyclonedx.schema import SchemaVersion |
26 | 27 | from cyclonedx.builder.this import this_component as lib_component |
27 | 28 | from cyclonedx.model import ExternalReference, ExternalReferenceType, XsUri |
28 | 29 | from cyclonedx.model.bom import Bom |
@@ -95,29 +96,33 @@ def find_LicenseExpression(licenses: Iterable['License']) -> Optional[LicenseExp |
95 | 96 | return None |
96 | 97 |
|
97 | 98 |
|
98 | | -def licenses_fixup(component: 'Component') -> None: |
| 99 | +def licenses_fixup(component: Component) -> None: |
99 | 100 | """ |
100 | | - CycloneDX 1.7 compliant license handling. |
101 | | -
|
102 | | - Rules: |
103 | | - - A component may have: |
104 | | - 1. One license expression |
105 | | - 2. One or more named licenses |
106 | | - 3. A mix of expression + named licenses (allowed by spec) |
107 | | -
|
108 | | - Behavior: |
109 | | - - Single license expression → leave as-is. |
110 | | - - Only named licenses → leave as-is. |
111 | | - - Mixed expression + named → leave as-is (spec allows this). |
112 | | - - No licenses are moved to evidence unless explicitly desired. |
| 101 | + Skip license fixup for CycloneDX 1.7 and newer. |
| 102 | +
|
| 103 | + In CycloneDX 1.7+, license expressions and named licenses |
| 104 | + may coexist. Older versions still require normalization. |
113 | 105 | """ |
| 106 | + # Detect schema version via internal BOM reference |
| 107 | + bom = getattr(component, "_bom", None) |
| 108 | + if bom is not None: |
| 109 | + schema_version = getattr(bom.metadata, "schema_version", None) |
| 110 | + if schema_version is not None and schema_version >= SchemaVersion.V1_7: |
| 111 | + return |
| 112 | + |
| 113 | + # ---- Legacy behavior (< 1.7) ---- |
114 | 114 | licenses = list(component.licenses) |
115 | | - if not licenses: |
| 115 | + lexp = find_LicenseExpression(licenses) |
| 116 | + if lexp is None: |
116 | 117 | return |
117 | 118 |
|
118 | | - # No forced "fixing" for mixed license states |
119 | | - return |
| 119 | + component.licenses = (lexp,) |
| 120 | + licenses.remove(lexp) |
120 | 121 |
|
| 122 | + if licenses: |
| 123 | + if component.evidence is None: |
| 124 | + component.evidence = ComponentEvidence() |
| 125 | + component.evidence.licenses.update(licenses) |
121 | 126 |
|
122 | 127 | _MAP_KNOWN_URL_LABELS: dict[str, ExternalReferenceType] = { |
123 | 128 | 'bugtracker': ExternalReferenceType.ISSUE_TRACKER, |
|
0 commit comments