diff --git a/convert.go b/convert.go index f0f861b..78a7329 100644 --- a/convert.go +++ b/convert.go @@ -196,6 +196,17 @@ func convertLicenses(licenses *Licenses, specVersion SpecVersion) { *licenses = converted } } + + if specVersion < SpecVersion1_5 { + for i := range *licenses { + choice := &(*licenses)[i] + if choice.License != nil { + choice.License.BOMRef = "" + choice.License.Licensing = nil + choice.License.Properties = nil + } + } + } } // serviceConverter modifies a Service such that it adheres to a given SpecVersion. diff --git a/cyclonedx.go b/cyclonedx.go index 19a3970..4ff3a9d 100644 --- a/cyclonedx.go +++ b/cyclonedx.go @@ -332,10 +332,13 @@ const ( ) type License struct { - ID string `json:"id,omitempty" xml:"id,omitempty"` - Name string `json:"name,omitempty" xml:"name,omitempty"` - Text *AttachedText `json:"text,omitempty" xml:"text,omitempty"` - URL string `json:"url,omitempty" xml:"url,omitempty"` + BOMRef string `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"` + ID string `json:"id,omitempty" xml:"id,omitempty"` + Name string `json:"name,omitempty" xml:"name,omitempty"` + Text *AttachedText `json:"text,omitempty" xml:"text,omitempty"` + URL string `json:"url,omitempty" xml:"url,omitempty"` + Licensing *Licensing `json:"licensing,omitempty" xml:"licensing,omitempty"` + Properties *[]Property `json:"properties,omitempty" xml:"properties>property,omitempty"` } type Licenses []LicenseChoice @@ -345,6 +348,38 @@ type LicenseChoice struct { Expression string `json:"expression,omitempty" xml:"-"` } +type LicenseType string + +const ( + LicenseTypeAcademic LicenseType = "academic" + LicenseTypeAppliance LicenseType = "appliance" + LicenseTypeClientAccess LicenseType = "client-access" + LicenseTypeConcurrentUser LicenseType = "concurrent-user" + LicenseTypeCorePoints LicenseType = "core-points" + LicenseTypeCustomMetric LicenseType = "custom-metric" + LicenseTypeDevice LicenseType = "device" + LicenseTypeEvaluation LicenseType = "evaluation" + LicenseTypeNamedUser LicenseType = "named-user" + LicenseTypeNodeLocked LicenseType = "node-locked" + LicenseTypeOEM LicenseType = "oem" + LicenseTypeOther LicenseType = "other" + LicenseTypePerpetual LicenseType = "perpetual" + LicenseTypeProcessorPoints LicenseType = "processor-points" + LicenseTypeSubscription LicenseType = "subscription" + LicenseTypeUser LicenseType = "user" +) + +type Licensing struct { + AltIDs *[]string `json:"altIds,omitempty" xml:"altIds>altId,omitempty"` + Licensor *OrganizationalEntityOrContact `json:"licensor,omitempty" xml:"licensor,omitempty"` + Licensee *OrganizationalEntityOrContact `json:"licensee,omitempty" xml:"licensee,omitempty"` + Purchaser *OrganizationalEntityOrContact `json:"purchaser,omitempty" xml:"purchaser,omitempty"` + PurchaseOrder string `json:"purchaseOrder,omitempty" xml:"purchaseOrder,omitempty"` + LicenseTypes *[]LicenseType `json:"licenseTypes,omitempty" xml:"licenseTypes>licenseType,omitempty"` + LastRenewal string `json:"lastRenewal,omitempty" xml:"lastRenewal,omitempty"` + Expiration string `json:"expiration,omitempty" xml:"expiration,omitempty"` +} + // MediaType defines the official media types for CycloneDX BOMs. // See https://cyclonedx.org/specification/overview/#registered-media-types type MediaType int @@ -391,6 +426,11 @@ type OrganizationalEntity struct { Contact *[]OrganizationalContact `json:"contact,omitempty" xml:"contact,omitempty"` } +type OrganizationalEntityOrContact struct { + Organization *OrganizationalEntity `json:"organization,omitempty" xml:"organization,omitempty"` + Individual *OrganizationalContact `json:"individual,omitempty" xml:"individual,omitempty"` +} + type Patch struct { Diff *Diff `json:"diff,omitempty" xml:"diff,omitempty"` Resolves *[]Issue `json:"resolves,omitempty" xml:"resolves>issue,omitempty"` diff --git a/testdata/snapshots/cyclonedx-go-TestRoundTripJSON-func1-valid-license-licensing.json b/testdata/snapshots/cyclonedx-go-TestRoundTripJSON-func1-valid-license-licensing.json new file mode 100644 index 0000000..b220db7 --- /dev/null +++ b/testdata/snapshots/cyclonedx-go-TestRoundTripJSON-func1-valid-license-licensing.json @@ -0,0 +1,59 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "library", + "publisher": "Acme Inc", + "group": "com.acme", + "name": "cryptographic-provider", + "version": "2.2.0", + "licenses": [ + { + "license": { + "bom-ref": "acme-license-1", + "name": "Acme Commercial License", + "licensing": { + "altIds": [ + "acme", + "acme-license" + ], + "licensor": { + "organization": { + "name": "Acme Inc", + "contact": [ + { + "name": "Acme Licensing Fulfillment", + "email": "licensing@example.com" + } + ] + } + }, + "licensee": { + "organization": { + "name": "Example Co." + } + }, + "purchaser": { + "individual": { + "name": "Samantha Wright", + "email": "samantha.wright@gmail.com", + "phone": "800-555-1212" + } + }, + "purchaseOrder": "PO-12345", + "licenseTypes": [ + "appliance" + ], + "lastRenewal": "2022-04-13T20:20:39+00:00", + "expiration": "2023-04-13T20:20:39+00:00" + } + } + } + ] + } + ] +} + diff --git a/testdata/snapshots/cyclonedx-go-TestRoundTripJSON-func1-valid-properties.json b/testdata/snapshots/cyclonedx-go-TestRoundTripJSON-func1-valid-properties.json index 7ac4f9f..5130bae 100644 --- a/testdata/snapshots/cyclonedx-go-TestRoundTripJSON-func1-valid-properties.json +++ b/testdata/snapshots/cyclonedx-go-TestRoundTripJSON-func1-valid-properties.json @@ -28,6 +28,31 @@ "type": "library", "name": "acme-library", "version": "1.0.0", + "licenses": [ + { + "license": { + "id": "Apache-2.0", + "properties": [ + { + "name": "Foo", + "value": "Bar" + }, + { + "name": "Foo", + "value": "You" + }, + { + "name": "Foo", + "value": "Two" + }, + { + "name": "Bar", + "value": "Foo" + } + ] + } + } + ], "properties": [ { "name": "Foo", diff --git a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-func1-valid-license-licensing.xml b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-func1-valid-license-licensing.xml new file mode 100644 index 0000000..681362b --- /dev/null +++ b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-func1-valid-license-licensing.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bom xmlns="http://cyclonedx.org/schema/bom/1.5" serialNumber="urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79" version="1"> + <components> + <component type="library"> + <publisher>Acme Inc</publisher> + <group>com.acme</group> + <name>cryptographic-provider</name> + <version>2.2.0</version> + <licenses> + <license bom-ref="acme-license-1"> + <name>Acme Commercial License</name> + <licensing> + <altIds> + <altId>acme</altId> + <altId>acme-license</altId> + </altIds> + <licensor> + <organization> + <name>Acme Inc</name> + <contact> + <name>Acme Licensing Fulfillment</name> + <email>licensing@example.com</email> + </contact> + </organization> + </licensor> + <licensee> + <organization> + <name>Example Co.</name> + </organization> + </licensee> + <purchaser> + <individual> + <name>Samantha Wright</name> + <email>samantha.wright@gmail.com</email> + <phone>800-555-1212</phone> + </individual> + </purchaser> + <purchaseOrder>PO-12345</purchaseOrder> + <licenseTypes> + <licenseType>appliance</licenseType> + </licenseTypes> + <lastRenewal>2022-04-13T20:20:39+00:00</lastRenewal> + <expiration>2023-04-13T20:20:39+00:00</expiration> + </licensing> + </license> + </licenses> + </component> + </components> +</bom> diff --git a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-func1-valid-properties.xml b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-func1-valid-properties.xml index dfcd58f..e66d784 100644 --- a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-func1-valid-properties.xml +++ b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-func1-valid-properties.xml @@ -12,6 +12,17 @@ <component type="library"> <name>acme-library</name> <version>1.0.0</version> + <licenses> + <license> + <id>Apache-2.0</id> + <properties> + <property name="Foo">Bar</property> + <property name="Foo">You</property> + <property name="Foo">Two</property> + <property name="Bar">Foo</property> + </properties> + </license> + </licenses> <properties> <property name="Foo">Bar</property> <property name="Bar">Foo</property> diff --git a/testdata/valid-license-licensing.json b/testdata/valid-license-licensing.json new file mode 100644 index 0000000..84c4719 --- /dev/null +++ b/testdata/valid-license-licensing.json @@ -0,0 +1,55 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "library", + "publisher": "Acme Inc", + "group": "com.acme", + "name": "cryptographic-provider", + "version": "2.2.0", + "licenses": [ + { + "license": { + "bom-ref": "acme-license-1", + "name": "Acme Commercial License", + "licensing": { + "altIds": [ + "acme", "acme-license" + ], + "licensor": { + "organization": { + "name": "Acme Inc", + "contact": [ + { + "name": "Acme Licensing Fulfillment", + "email": "licensing@example.com" + } + ] + } + }, + "licensee": { + "organization": { + "name": "Example Co." + } + }, + "purchaser": { + "individual": { + "name": "Samantha Wright", + "email": "samantha.wright@gmail.com", + "phone": "800-555-1212" + } + }, + "purchaseOrder": "PO-12345", + "licenseTypes": ["appliance"], + "lastRenewal": "2022-04-13T20:20:39+00:00", + "expiration": "2023-04-13T20:20:39+00:00" + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/testdata/valid-license-licensing.xml b/testdata/valid-license-licensing.xml new file mode 100644 index 0000000..75d070e --- /dev/null +++ b/testdata/valid-license-licensing.xml @@ -0,0 +1,49 @@ +?xml version="1.0"?> +<bom serialNumber="urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79" version="1" xmlns="http://cyclonedx.org/schema/bom/1.5"> + <components> + <component type="library"> + <publisher>Acme Inc</publisher> + <group>com.acme</group> + <name>cryptographic-provider</name> + <version>2.2.0</version> + <licenses> + <license bom-ref="acme-license-1"> + <name>Acme Commercial License</name> + <licensing> + <altIds> + <altId>acme</altId> + <altId>acme-license</altId> + </altIds> + <licensor> + <organization> + <name>Acme Inc</name> + <contact> + <name>Acme Licensing Fulfillment</name> + <email>licensing@example.com</email> + </contact> + </organization> + </licensor> + <licensee> + <organization> + <name>Example Co.</name> + </organization> + </licensee> + <purchaser> + <individual> + <name>Samantha Wright</name> + <email>samantha.wright@gmail.com</email> + <phone>800-555-1212</phone> + </individual> + </purchaser> + <purchaseOrder>PO-12345</purchaseOrder> + <licenseTypes> + <licenseType>appliance</licenseType> + </licenseTypes> + <lastRenewal>2022-04-13T20:20:39+00:00</lastRenewal> + <expiration>2023-04-13T20:20:39+00:00</expiration> + </licensing> + </license> + </licenses> + </component> + </components> +</bom> \ No newline at end of file diff --git a/testdata/valid-properties.json b/testdata/valid-properties.json index 3a33ccf..24ce5de 100644 --- a/testdata/valid-properties.json +++ b/testdata/valid-properties.json @@ -28,6 +28,31 @@ "type": "library", "name": "acme-library", "version": "1.0.0", + "licenses": [ + { + "license": { + "id": "Apache-2.0", + "properties": [ + { + "name": "Foo", + "value": "Bar" + }, + { + "name": "Foo", + "value": "You" + }, + { + "name": "Foo", + "value": "Two" + }, + { + "name": "Bar", + "value": "Foo" + } + ] + } + } + ], "properties": [ { "name": "Foo", diff --git a/testdata/valid-properties.xml b/testdata/valid-properties.xml index 85abf9e..91a1916 100644 --- a/testdata/valid-properties.xml +++ b/testdata/valid-properties.xml @@ -12,6 +12,17 @@ <component type="library"> <name>acme-library</name> <version>1.0.0</version> + <licenses> + <license> + <id>Apache-2.0</id> + <properties> + <property name="Foo">Bar</property> + <property name="Foo">You</property> + <property name="Foo">Two</property> + <property name="Bar">Foo</property> + </properties> + </license> + </licenses> <properties> <property name="Foo">Bar</property> <property name="Bar">Foo</property>