fix: support CycloneDX 1.7 (#4967)

Signed-off-by: Keith Zantow <kzantow@gmail.com>
This commit is contained in:
Keith Zantow 2026-06-11 09:40:42 -04:00 committed by GitHub
parent b08d3c2970
commit 89773c0a12
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 4520 additions and 799 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="http://cyclonedx.org/schema/spdx"
version="1.0-3.27.0">
version="1.0-3.28.0">
<xs:simpleType name="licenseId">
<xs:restriction base="xs:string">
@ -57,6 +57,11 @@
<xs:documentation>Amazon Digital Services License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Advanced-Cryptics-Dictionary">
<xs:annotation>
<xs:documentation>Advanced Cryptics Dictionary License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="AFL-1.1">
<xs:annotation>
<xs:documentation>Academic Free License v1.1</xs:documentation>
@ -122,6 +127,11 @@
<xs:documentation>Aladdin Free Public License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ALGLIB-Documentation">
<xs:annotation>
<xs:documentation>ALGLIB Documentation License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="AMD-newlib">
<xs:annotation>
<xs:documentation>AMD newlib License</xs:documentation>
@ -327,6 +337,11 @@
<xs:documentation>Boehm-Demers-Weiser GC License (without fee)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BOLA-1.1">
<xs:annotation>
<xs:documentation>Buena Onda License Agreement v1.1</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Borceux">
<xs:annotation>
<xs:documentation>Borceux license</xs:documentation>
@ -457,6 +472,11 @@
<xs:documentation>BSD 3-Clause Sun Microsystems</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-3-Clause-Tso">
<xs:annotation>
<xs:documentation>BSD 3-Clause Tso variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-4-Clause">
<xs:annotation>
<xs:documentation>BSD 4-Clause &quot;Original&quot; or &quot;Old&quot; License</xs:documentation>
@ -497,6 +517,11 @@
<xs:documentation>BSD-Inferno-Nettverk</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-Mark-Modifications">
<xs:annotation>
<xs:documentation>BSD Mark Modifications License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-Protection">
<xs:annotation>
<xs:documentation>BSD Protection License</xs:documentation>
@ -527,6 +552,11 @@
<xs:documentation>Boost Software License 1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Buddy">
<xs:annotation>
<xs:documentation>Buddy License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BUSL-1.1">
<xs:annotation>
<xs:documentation>Business Source License 1.1</xs:documentation>
@ -567,6 +597,11 @@
<xs:documentation>Caldera License (without preamble)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="CAPEC-tou">
<xs:annotation>
<xs:documentation>Common Attack Pattern Enumeration and Classification License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Catharon">
<xs:annotation>
<xs:documentation>Catharon License</xs:documentation>
@ -1212,6 +1247,21 @@
<xs:documentation>Erlang Public License v1.1</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ESA-PL-permissive-2.4">
<xs:annotation>
<xs:documentation>European Space Agency Public License v2.4 Permissive (Type 3)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ESA-PL-strong-copyleft-2.4">
<xs:annotation>
<xs:documentation>European Space Agency Public License (ESA-PL) - V2.4 - Strong Copyleft (Type 1)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ESA-PL-weak-copyleft-2.4">
<xs:annotation>
<xs:documentation>European Space Agency Public License v2.4 Weak Copyleft (Type 2)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="etalab-2.0">
<xs:annotation>
<xs:documentation>Etalab Open License 2.0</xs:documentation>
@ -1737,6 +1787,11 @@
<xs:documentation>Historical Permission Notice and Disclaimer - sell variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-sell-variant-critical-systems">
<xs:annotation>
<xs:documentation>HPND - sell variant with safety critical systems clause</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-sell-variant-MIT-disclaimer">
<xs:annotation>
<xs:documentation>HPND sell variant with MIT disclaimer</xs:documentation>
@ -1747,6 +1802,11 @@
<xs:documentation>HPND sell variant with MIT disclaimer - reverse</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-SMC">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - SMC variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-UC">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - University of California variant</xs:documentation>
@ -1762,6 +1822,11 @@
<xs:documentation>HTML Tidy License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="hyphen-bulgarian">
<xs:annotation>
<xs:documentation>hyphen-bulgarian License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="IBM-pibs">
<xs:annotation>
<xs:documentation>IBM PowerPC Initialization and Boot Software</xs:documentation>
@ -1852,6 +1917,11 @@
<xs:documentation>ISC Veillard variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ISO-permission">
<xs:annotation>
<xs:documentation>ISO permission notice</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Jam">
<xs:annotation>
<xs:documentation>Jam License</xs:documentation>
@ -2237,6 +2307,11 @@
<xs:documentation>MIT Open Group variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="MIT-STK">
<xs:annotation>
<xs:documentation>MIT-STK License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="MIT-testregex">
<xs:annotation>
<xs:documentation>MIT testregex Variant</xs:documentation>
@ -2257,6 +2332,11 @@
<xs:documentation>MMIXware License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="MMPL-1.0.1">
<xs:annotation>
<xs:documentation>Minecraft Mod Public License v1.0.1</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Motosoto">
<xs:annotation>
<xs:documentation>Motosoto License</xs:documentation>
@ -2422,6 +2502,11 @@
<xs:documentation>NIST Public Domain Notice with license fallback</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="NIST-PD-TNT">
<xs:annotation>
<xs:documentation>NIST Public Domain Notice TNT variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="NIST-Software">
<xs:annotation>
<xs:documentation>NIST Software License</xs:documentation>
@ -2687,6 +2772,11 @@
<xs:documentation>Open Market License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OpenMDW-1.0">
<xs:annotation>
<xs:documentation>OpenMDW License Agreement v1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OpenPBS-2.3">
<xs:annotation>
<xs:documentation>OpenPBS v2.3 Software License</xs:documentation>
@ -2722,6 +2812,11 @@
<xs:documentation>Open Publication License v1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OSC-1.0">
<xs:annotation>
<xs:documentation>OSC License 1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OSET-PL-2.1">
<xs:annotation>
<xs:documentation>OSET Public License version 2.1</xs:documentation>
@ -2752,11 +2847,21 @@
<xs:documentation>Open Software License 3.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OSSP">
<xs:annotation>
<xs:documentation>OSSP License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="PADL">
<xs:annotation>
<xs:documentation>PADL License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ParaType-Free-Font-1.3">
<xs:annotation>
<xs:documentation>ParaType Free Font Licensing Agreement v1.3</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Parity-6.0.0">
<xs:annotation>
<xs:documentation>The Parity Public License 6.0.0</xs:documentation>
@ -2977,6 +3082,11 @@
<xs:documentation>SGI OpenGL License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="SGMLUG-PM">
<xs:annotation>
<xs:documentation>SGMLUG Parser Materials License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="SGP4">
<xs:annotation>
<xs:documentation>SGP4 Permission Notice</xs:documentation>
@ -3162,6 +3272,11 @@
<xs:documentation>TCP Wrappers License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="TekHVC">
<xs:annotation>
<xs:documentation>TekHVC License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="TermReadKey">
<xs:annotation>
<xs:documentation>TermReadKey License</xs:documentation>
@ -3297,6 +3412,11 @@
<xs:documentation>Unlicense - libwhirlpool variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="UnRAR">
<xs:annotation>
<xs:documentation>UnRAR License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="UPL-1.0">
<xs:annotation>
<xs:documentation>Universal Permissive License v1.0</xs:documentation>
@ -3312,6 +3432,11 @@
<xs:documentation>Vim License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Vixie-Cron">
<xs:annotation>
<xs:documentation>Vixie Cron License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="VOSTROM">
<xs:annotation>
<xs:documentation>VOSTROM Public License for Open Source</xs:documentation>
@ -3352,11 +3477,21 @@
<xs:documentation>Widget Workshop License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="WordNet">
<xs:annotation>
<xs:documentation>WordNet License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Wsuipa">
<xs:annotation>
<xs:documentation>Wsuipa License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="WTFNMFPL">
<xs:annotation>
<xs:documentation>Do What The F*ck You Want To But It&apos;s Not My Fault Public License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="WTFPL">
<xs:annotation>
<xs:documentation>Do What The F*ck You Want To Public License</xs:documentation>
@ -3382,6 +3517,11 @@
<xs:documentation>X11 License Distribution Modification Variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="X11-no-permit-persons">
<xs:annotation>
<xs:documentation>X11 no permit persons clause</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="X11-swapped">
<xs:annotation>
<xs:documentation>X11 swapped final paragraphs</xs:documentation>
@ -3568,6 +3708,11 @@
<xs:documentation>Classpath exception 2.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Classpath-exception-2.0-short">
<xs:annotation>
<xs:documentation>Classpath exception 2.0 - short</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="CLISP-exception-2.0">
<xs:annotation>
<xs:documentation>CLISP exception 2.0</xs:documentation>
@ -3718,6 +3863,11 @@
<xs:documentation>KiCad Libraries Exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="kvirc-openssl-exception">
<xs:annotation>
<xs:documentation>kvirc OpenSSL Exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="LGPL-3.0-linking-exception">
<xs:annotation>
<xs:documentation>LGPL-3.0 Linking Exception</xs:documentation>
@ -3833,6 +3983,11 @@
<xs:documentation>RRDtool FLOSS exception 2.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="rsync-linking-exception">
<xs:annotation>
<xs:documentation>rsync Linking Exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="SANE-exception">
<xs:annotation>
<xs:documentation>SANE Exception</xs:documentation>
@ -3848,6 +4003,16 @@
<xs:documentation>Solderpad Hardware License v2.1</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Simple-Library-Usage-exception">
<xs:annotation>
<xs:documentation>Simple Library Usage Exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="sqlitestudio-OpenSSL-exception">
<xs:annotation>
<xs:documentation>sqlitestudio OpenSSL exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="stunnel-exception">
<xs:annotation>
<xs:documentation>stunnel Exception</xs:documentation>

View File

@ -0,0 +1,59 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.7",
"serialNumber": "urn:uuid:5208fea9-73dd-4624-b596-69fddccdb9e7",
"version": 1,
"metadata": {
"timestamp": "2023-09-29T12:02:02-04:00",
"tools": [
{
"vendor": "anchore",
"name": "syft",
"version": "[not provided]"
}
],
"component": {
"bom-ref": "a0ff99a6af10f11f",
"type": "file",
"name": "go.mod",
"version": "sha256:sha256:dc333f342905248a52e424d8dfd061251d01867d01a4f9d7397144a775ff9ebd"
}
},
"components": [
{
"bom-ref": "pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651?package-id=2ff71a67fb024c86",
"type": "library",
"name": "github.com/wagoodman/go-partybus",
"version": "v0.0.0-20230516145632-8ccac152c651",
"cpe": "cpe:2.3:a:wagoodman:go-partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*",
"purl": "pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651",
"properties": [
{
"name": "syft:package:foundBy",
"value": "go-module-file-cataloger"
},
{
"name": "syft:package:language",
"value": "go"
},
{
"name": "syft:package:metadataType",
"value": "GolangModMetadata"
},
{
"name": "syft:package:type",
"value": "go-module"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:wagoodman:go_partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*"
},
{
"name": "syft:location:0:path",
"value": "/go.mod"
}
]
}
]
}

View File

@ -1,7 +1,7 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.7",
"serialNumber": "urn:uuid:redacted",
"version": 1,
"metadata": {

View File

@ -1,7 +1,7 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.7",
"serialNumber": "urn:uuid:redacted",
"version": 1,
"metadata": {

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="urn:uuid:098e8516-ecd5-4130-9d5f-c32ba1ddb0dd" version="1">
<metadata>
<timestamp>2023-09-29T11:48:10-04:00</timestamp>
<tools>
<tool>
<vendor>anchore</vendor>
<name>syft</name>
<version>[not provided]</version>
</tool>
</tools>
<component bom-ref="a0ff99a6af10f11f" type="file">
<name>go.mod</name>
<version>sha256:sha256:dc333f342905248a52e424d8dfd061251d01867d01a4f9d7397144a775ff9ebd</version>
</component>
</metadata>
<components>
<component bom-ref="pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651?package-id=2ff71a67fb024c86" type="library">
<name>github.com/wagoodman/go-partybus</name>
<version>v0.0.0-20230516145632-8ccac152c651</version>
<cpe>cpe:2.3:a:wagoodman:go-partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*</cpe>
<purl>pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651</purl>
<properties>
<property name="syft:package:foundBy">go-module-file-cataloger</property>
<property name="syft:package:language">go</property>
<property name="syft:package:metadataType">GolangModMetadata</property>
<property name="syft:package:type">go-module</property>
<property name="syft:cpe23">cpe:2.3:a:wagoodman:go_partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*</property>
<property name="syft:location:0:path">/go.mod</property>
</properties>
</component>
</components>
</bom>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="redacted" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="redacted" version="1">
<metadata>
<timestamp>redacted</timestamp>
<tools>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="redacted" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="redacted" version="1">
<metadata>
<timestamp>redacted</timestamp>
<tools>

View File

@ -9,8 +9,6 @@ import (
"github.com/anchore/syft/syft/sbom"
)
const DefaultVersion = "1.6"
type Encoder struct {
version cyclonedx.SpecVersion
format cyclonedx.BOMFileFormat

View File

@ -2,6 +2,10 @@ package cyclonedxutil
import (
"fmt"
"maps"
"slices"
"strconv"
"strings"
"github.com/CycloneDX/cyclonedx-go"
@ -13,59 +17,81 @@ const (
JSONFormatID sbom.FormatID = "cyclonedx-json"
)
const DefaultVersion = "1.7"
var commonVersions = map[string]cyclonedx.SpecVersion{
"1.2": cyclonedx.SpecVersion1_2,
"1.3": cyclonedx.SpecVersion1_3,
"1.4": cyclonedx.SpecVersion1_4,
"1.5": cyclonedx.SpecVersion1_5,
"1.6": cyclonedx.SpecVersion1_6,
DefaultVersion: cyclonedx.SpecVersion1_7,
}
var allVersions = func() map[string]cyclonedx.SpecVersion {
out := map[string]cyclonedx.SpecVersion{
"1.0": cyclonedx.SpecVersion1_0,
"1.1": cyclonedx.SpecVersion1_1,
}
maps.Copy(out, commonVersions)
return out
}()
func SupportedVersions(id sbom.FormatID) []string {
versions := []string{
"1.2",
"1.3",
"1.4",
"1.5",
"1.6",
versionSet := commonVersions
if id == XMLFormatID {
versionSet = allVersions
}
if id != JSONFormatID {
// JSON format not supported for version < 1.2
versions = append([]string{"1.0", "1.1"}, versions...)
versions := make([]string, 0, len(versionSet))
for _, v := range versionSet {
versions = append(versions, v.String())
}
slices.SortFunc(versions, versionSort)
return versions
}
func SpecVersionFromString(v string) (cyclonedx.SpecVersion, error) {
switch v {
case "1.0":
return cyclonedx.SpecVersion1_0, nil
case "1.1":
return cyclonedx.SpecVersion1_1, nil
case "1.2":
return cyclonedx.SpecVersion1_2, nil
case "1.3":
return cyclonedx.SpecVersion1_3, nil
case "1.4":
return cyclonedx.SpecVersion1_4, nil
case "1.5":
return cyclonedx.SpecVersion1_5, nil
case "1.6":
return cyclonedx.SpecVersion1_6, nil
if specVersion, ok := allVersions[v]; ok {
return specVersion, nil
}
return -1, fmt.Errorf("unsupported CycloneDX version %q", v)
}
func VersionFromSpecVersion(spec cyclonedx.SpecVersion) string {
switch spec {
case cyclonedx.SpecVersion1_0:
return "1.0"
case cyclonedx.SpecVersion1_1:
return "1.1"
case cyclonedx.SpecVersion1_2:
return "1.2"
case cyclonedx.SpecVersion1_3:
return "1.3"
case cyclonedx.SpecVersion1_4:
return "1.4"
case cyclonedx.SpecVersion1_5:
return "1.5"
case cyclonedx.SpecVersion1_6:
return "1.6"
for version, specVersion := range allVersions {
if specVersion == spec {
return version
}
}
return ""
}
func versionSort(a string, b string) int {
partsA := strings.Split(a, ".")
partsB := strings.Split(b, ".")
lenA := len(partsA)
lenB := len(partsB)
for i := range max(lenA, lenB) {
if i >= lenA {
return -1 // 1 < 1.x
}
if i >= lenB {
return 1 // 1.x > 1
}
partA, errA := strconv.ParseInt(partsA[i], 10, 64)
partB, errB := strconv.ParseInt(partsB[i], 10, 64)
if errA != nil || errB != nil {
// string compare if we can't parse one of the sides
strcmp := strings.Compare(partsA[i], partsB[i])
if strcmp == 0 {
continue
}
return strcmp // not equal
}
if partA == partB {
continue
}
return int(partA - partB)
}
return 0
}

View File

@ -0,0 +1,107 @@
package cyclonedxutil
import (
"go/constant"
"go/types"
"regexp"
"slices"
"testing"
"github.com/CycloneDX/cyclonedx-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/tools/go/packages"
)
// Test_allSpecVersionsMapped loads the cyclonedx-go package's type information and enumerates every
// SpecVersion* constant it declares, asserting each one is present in our version maps. Upgrading
// cyclonedx-go to a release that adds a new spec version will fail this test until versions.go is updated.
func Test_allSpecVersionsMapped(t *testing.T) {
const pkgPath = "github.com/CycloneDX/cyclonedx-go"
pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName | packages.NeedTypes}, pkgPath)
require.NoError(t, err)
require.Len(t, pkgs, 1)
pkg := pkgs[0]
require.Empty(t, pkg.Errors, "errors loading %s", pkgPath)
require.NotNil(t, pkg.Types, "no type info loaded for %s", pkgPath)
// allVersions is the superset (json/common versions merged in), so it covers every spec version we map.
mapped := make(map[cyclonedx.SpecVersion]struct{})
for syftVersion, sv := range allVersions {
// make sure the key matches the version it's mapped to
require.Contains(t, sv.String(), syftVersion, "version %q is not correctly mapped to cyclonedx SpecVersion %q", syftVersion, sv.String())
mapped[sv] = struct{}{}
}
specVersionConst := regexp.MustCompile(`^SpecVersion\d`)
scope := pkg.Types.Scope()
found := 0
for _, name := range scope.Names() {
if !specVersionConst.MatchString(name) {
continue
}
c, ok := scope.Lookup(name).(*types.Const)
if !ok {
continue
}
found++
val, ok := constant.Int64Val(c.Val())
require.Truef(t, ok, "could not read int value of cyclonedx.%s", name)
_, ok = mapped[cyclonedx.SpecVersion(val)]
assert.Truef(t, ok, "cyclonedx.%s is not mapped in versions.go; add it when upgrading cyclonedx-go", name)
}
require.NotZero(t, found, "no SpecVersion* constants found in %s", pkgPath)
}
func Test_versionSort(t *testing.T) {
// versionSort returns <0 when a<b, 0 when a==b, >0 when a>b; assert on sign rather than exact value.
sign := func(n int) int {
switch {
case n < 0:
return -1
case n > 0:
return 1
default:
return 0
}
}
tests := []struct {
name string
a string
b string
want int
}{
{name: "equal single part", a: "1", b: "1", want: 0},
{name: "equal two parts", a: "1.4", b: "1.4", want: 0},
{name: "minor less than", a: "1.3", b: "1.4", want: -1},
{name: "minor greater than", a: "1.6", b: "1.5", want: 1},
{name: "major less than", a: "1.7", b: "2.0", want: -1},
{name: "major greater than", a: "2.0", b: "1.7", want: 1},
{name: "fewer parts sorts first", a: "1", b: "1.0", want: -1},
{name: "more parts sorts last", a: "1.0", b: "1", want: 1},
{name: "numeric not lexical (10 > 9)", a: "1.10", b: "1.9", want: 1},
{name: "multi-digit major", a: "10.0", b: "9.0", want: 1},
{name: "three parts equal", a: "1.4.0", b: "1.4.0", want: 0},
{name: "three parts patch differs", a: "1.4.1", b: "1.4.0", want: 1},
{name: "non-numeric equal falls through", a: "1.x", b: "1.x", want: 0},
{name: "non-numeric string compare", a: "1.a", b: "1.b", want: -1},
{name: "non-numeric side string compare", a: "1.2", b: "1.x", want: -1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, sign(versionSort(tt.a, tt.b)))
// comparator must be antisymmetric: swapping operands negates the sign.
assert.Equal(t, -tt.want, sign(versionSort(tt.b, tt.a)))
})
}
}
func Test_versionSort_sortsSlice(t *testing.T) {
versions := []string{"1.10", "1.2", "1", "2.0", "1.9", "1.0"}
slices.SortFunc(versions, versionSort)
assert.Equal(t, []string{"1", "1.0", "1.2", "1.9", "1.10", "2.0"}, versions)
}