Description
CycloneDX CLI fails to convert a valid SPDX 2.3 JSON document when a package contains the following field:
"primaryPackagePurpose": "OPERATING-SYSTEM"
OPERATING-SYSTEM is the valid value defined by the SPDX 2.3 specification.
The CLI throws an unhandled System.Text.Json.JsonException while deserializing the SPDX document.
Replacing the valid SPDX value: OPERATING-SYSTEM with the non-standard value: OPERATING_SYSTEM allows the conversion to proceed.
Environment
CycloneDX CLI version: 0.32.0
Platform: Linux x64 / WSL Ubuntu 24.04
Input format: SPDX JSON 2.3
Output format: CycloneDX JSON
Requested output version: CycloneDX 1.6
Command
./cyclonedx-linux-x64 convert \
--input-format spdxjson \
--output-format json \
--output-version v1_6 \
--input-file minimal.spdx.json \
--output-file output.cdx.json
Minimal reproducer
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "operating-system-test",
"documentNamespace": "https://example.com/spdx/operating-system-test",
"creationInfo": {
"created": "2026-07-14T08:00:00Z",
"creators": [
"Tool: minimal-reproducer"
]
},
"packages": [
{
"name": "Example Linux",
"SPDXID": "SPDXRef-OperatingSystem",
"versionInfo": "1.0",
"downloadLocation": "NONE",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"primaryPackagePurpose": "OPERATING-SYSTEM"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-OperatingSystem"
}
]
}
Actual result
The conversion terminates with the following exception:
Unhandled exception: System.Text.Json.JsonException: The JSON value could not be converted to System.Nullable`1[CycloneDX.Spdx.Models.v2_3.PrimaryPackagePurposeType]. Path: $.packages[0].primaryPackagePurpose
at System.Text.Json.ThrowHelper.ThrowJsonException(String message)
at System.Text.Json.Serialization.Converters.EnumConverter`1.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.Serialization.Converters.NullableConverter`1.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize[TReadBufferState,TStream](TReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack, T& value)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync[TReadBufferState,TStream](TStream utf8Json, TReadBufferState bufferState, CancellationToken cancellationToken)
at CycloneDX.Spdx.Serialization.JsonSerializer.DeserializeAsync(Stream jsonStream)
at CycloneDX.Cli.CliUtils.InputBomHelper(String filename, ConvertFormat format)
at CycloneDX.Cli.Commands.ConvertCommand.Convert(ConvertCommandOptions options)
Expected result
The CLI should successfully deserialize the SPDX 2.3 document and convert the package to a CycloneDX component.
Analysis
The SPDX model defines the C# enum member as:
OPERATING_SYSTEM
while the SPDX 2.3 JSON representation requires:
OPERATING-SYSTEM
The SPDX JSON serializer currently registers HyphenToUnderscoreEnumConverter<T> for some enum types, such as:
HyphenToUnderscoreEnumConverter<ExternalRefCategory>
HyphenToUnderscoreEnumConverter<ChecksumAlgorithm>
However, no equivalent converter appears to be registered for:
PrimaryPackagePurposeType
The generic JsonStringEnumConverter therefore attempts to match OPERATING-SYSTEM directly against the C# enum member OPERATING_SYSTEM and fails.
The issue likely originates in CycloneDX.Spdx.Serialization.JsonSerializer from the cyclonedx-dotnet-library, rather than in the CLI conversion logic itself.
Possible fix
Register the existing converter for PrimaryPackagePurposeType before the generic JsonStringEnumConverter:
options.Converters.Add(
new HyphenToUnderscoreEnumConverter<PrimaryPackagePurposeType>());
A regression test should verify both directions:
Deserializing:
"primaryPackagePurpose": "OPERATING-SYSTEM"
into:
PrimaryPackagePurposeType.OPERATING_SYSTEM
Serializing the enum back to the SPDX-compliant value:
"primaryPackagePurpose": "OPERATING-SYSTEM"
Workaround
As a temporary workaround, replacing:
"primaryPackagePurpose": "OPERATING-SYSTEM"
with:
"primaryPackagePurpose": "OPERATING_SYSTEM"
allows the conversion to complete, but the resulting temporary SPDX input is not compliant with the SPDX 2.3 specification.
Description
CycloneDX CLI fails to convert a valid SPDX 2.3 JSON document when a package contains the following field:
"primaryPackagePurpose": "OPERATING-SYSTEM"
OPERATING-SYSTEM is the valid value defined by the SPDX 2.3 specification.
The CLI throws an unhandled System.Text.Json.JsonException while deserializing the SPDX document.
Replacing the valid SPDX value:
OPERATING-SYSTEMwith the non-standard value:OPERATING_SYSTEMallows the conversion to proceed.Environment
CycloneDX CLI version:
0.32.0Platform:
Linux x64 / WSL Ubuntu 24.04Input format:
SPDX JSON 2.3Output format:
CycloneDX JSONRequested output version:
CycloneDX 1.6Command
Minimal reproducer
Actual result
The conversion terminates with the following exception:
Expected result
The CLI should successfully deserialize the SPDX 2.3 document and convert the package to a CycloneDX component.
Analysis
The SPDX model defines the C# enum member as:
OPERATING_SYSTEMwhile the SPDX 2.3 JSON representation requires:
OPERATING-SYSTEMThe SPDX JSON serializer currently registers
HyphenToUnderscoreEnumConverter<T>for some enum types, such as:However, no equivalent converter appears to be registered for:
PrimaryPackagePurposeTypeThe generic
JsonStringEnumConvertertherefore attempts to matchOPERATING-SYSTEMdirectly against the C# enum memberOPERATING_SYSTEMand fails.The issue likely originates in
CycloneDX.Spdx.Serialization.JsonSerializerfrom thecyclonedx-dotnet-library, rather than in the CLI conversion logic itself.Possible fix
Register the existing converter for
PrimaryPackagePurposeTypebefore the genericJsonStringEnumConverter:A regression test should verify both directions:
Deserializing:
into:
Serializing the enum back to the SPDX-compliant value:
"primaryPackagePurpose": "OPERATING-SYSTEM"Workaround
As a temporary workaround, replacing:
"primaryPackagePurpose": "OPERATING-SYSTEM"with:
"primaryPackagePurpose": "OPERATING_SYSTEM"allows the conversion to complete, but the resulting temporary SPDX input is not compliant with the SPDX 2.3 specification.