Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion src/CycloneDX.Spdx/Models/v2_3/ExternalRef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,58 @@ public class ExternalRef
/// <summary>
/// Category for the external reference
/// </summary>
[XmlElement("referenceCategory")]
[XmlIgnore]
public ExternalRefCategory ReferenceCategory { get; set; }

/// <summary>
/// Category for the external reference, adjusted to allow values with hyphens and underscores
/// </summary>
[XmlElement("referenceCategory")]
[JsonIgnore]
public string ReferenceCategoryAsString
{
get
{
string result;
switch (ReferenceCategory)
{
case ExternalRefCategory.PACKAGE_MANAGER:
result = "PACKAGE-MANAGER";
break;
case ExternalRefCategory.PERSISTENT_ID:
result = "PERSISTENT-ID";
break;
default:
result = ReferenceCategory.ToString();
break;
}
return result;
}


set
{
switch (value.ToUpperInvariant())
{
case "OTHER":
ReferenceCategory = ExternalRefCategory.OTHER;
break;
case "SECURITY":
ReferenceCategory = ExternalRefCategory.SECURITY;
break;
case "PACKAGE_MANAGER":
case "PACKAGE-MANAGER":
ReferenceCategory = ExternalRefCategory.PACKAGE_MANAGER;
break;
case "PERSISTENT_ID":
case "PERSISTENT-ID":
ReferenceCategory = ExternalRefCategory.PERSISTENT_ID;
break;
default:
throw new InvalidOperationException();
}
}
}

/// <summary>
/// The unique string with no spaces necessary to access the package-specific information, metadata, or content within the target location. The format of the locator is subject to constraints defined by the &lt;type&gt;.
Expand Down
25 changes: 20 additions & 5 deletions src/CycloneDX.Spdx/Models/v2_3/Package.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,17 @@ public class Package
[XmlElement("attributionTexts")]
public List<string> AttributionTexts { get; set; }

/// <summary>
private DateTime? _builtDate;
/// <summary>0
/// Provides a place for recording the actual date the package was built.
/// </summary>
[XmlElement("builtDate")]
[JsonConverter(typeof(UtcDateTimeConverter))]
public DateTime? BuiltDate { get; set; }
public DateTime? BuiltDate
{
get => _builtDate;
set { _builtDate = Utils.UtcifyDateTime(value); }
}

/// <summary>
/// The checksum property provides a mechanism that can be used to verify that the contents of a File or Package have not changed.
Expand Down Expand Up @@ -152,12 +157,17 @@ public class Package
[XmlElement("hasFiles")]
public List<string> HasFiles { get; set; }

private DateTime? _releaseDate;
/// <summary>
/// Provides a place for recording the date the package was released.
/// </summary>
[XmlElement("releaseDate")]
[JsonConverter(typeof(UtcDateTimeConverter))]
public DateTime? ReleaseDate { get; set; }
public DateTime? ReleaseDate
{
get => _releaseDate;
set { _releaseDate = Utils.UtcifyDateTime(value); }
}

/// <summary>
/// Allows the producer(s) of the SPDX document to describe how the package was acquired and/or changed from the original source.
Expand All @@ -177,12 +187,17 @@ public class Package
[XmlElement("supplier")]
public string Supplier { get; set; }

/// <summary>
private DateTime? _validUntilDate;
/// <summary>
/// Provides a place for recording the end of the support period for a package from the supplier.
/// </summary>
[XmlElement("validUntilDate")]
[JsonConverter(typeof(UtcDateTimeConverter))]
public DateTime? ValidUntilDate { get; set; }
public DateTime? ValidUntilDate
{
get => _validUntilDate;
set { _validUntilDate = Utils.UtcifyDateTime(value); }
}

/// <summary>
/// Provides an indication of the version of the package that is described by this SpdxDocument.
Expand Down
5 changes: 3 additions & 2 deletions src/CycloneDX.Spdx/Models/v2_3/UtcDateTimeConverter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;

Expand All @@ -8,13 +9,13 @@ public class UtcDateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString());
return DateTime.Parse(reader.GetString(), CultureInfo.InvariantCulture);
}

public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
// Convert to UTC and format as "yyyy-MM-ddTHH:mm:ssZ"
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"));
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture));
}
}
}
2 changes: 1 addition & 1 deletion src/CycloneDX.Spdx/Schemas/spdx-2.3.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@
"referenceCategory": {
"description": "Category for the external reference",
"type": "string",
"enum": [ "OTHER", "PERSISTENT_ID", "SECURITY", "PACKAGE_MANAGER" ]
"enum": [ "OTHER", "PERSISTENT_ID", "SECURITY", "PACKAGE_MANAGER", "PACKAGE-MANAGER", "PERSISTENT-ID" ]
},
"referenceLocator": {
"description": "The unique string with no spaces necessary to access the package-specific information, metadata, or content within the target location. The format of the locator is subject to constraints defined by the <type>.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// This file is part of CycloneDX Library for .NET
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.

using System;
using System.Diagnostics.Contracts;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace CycloneDX.Spdx.Serialization.Converters
{

public class HyphenToUnderscoreEnumConverter<T> : JsonConverter<T> where T: struct
{
public override T Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null
|| reader.TokenType != JsonTokenType.String)
{
throw new JsonException();
}

var enumString = reader.GetString();

T enumValue;
var success = Enum.TryParse<T>(enumString.Replace("-", "_"), ignoreCase: true, out enumValue);
if (success)
{
return enumValue;
}
else
{
throw new JsonException();
}
}

public override void Write(
Utf8JsonWriter writer,
T value,
JsonSerializerOptions options)
{
Contract.Requires(writer != null);
writer.WriteStringValue(value.ToString().Replace("_", "-"));
}
}
}
3 changes: 3 additions & 0 deletions src/CycloneDX.Spdx/Serialization/JsonSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using CycloneDX.Spdx.Models.v2_3;
using CycloneDX.Spdx.Serialization.Converters;

namespace CycloneDX.Spdx.Serialization
{
Expand All @@ -38,6 +40,7 @@ public static JsonSerializerOptions GetJsonSerializerOptions_v2_3()
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,

};
options.Converters.Add(new HyphenToUnderscoreEnumConverter<ExternalRefCategory>());
options.Converters.Add(new JsonStringEnumConverter());
return options;
}
Expand Down
44 changes: 44 additions & 0 deletions src/CycloneDX.Spdx/Utils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// This file is part of CycloneDX Library for .NET
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.

using System;

namespace CycloneDX.Spdx
{
static internal class Utils
{
internal static DateTime? UtcifyDateTime(DateTime? value)
{
if (value is null)
{
return null;
}
else if (value.Value.Kind == DateTimeKind.Unspecified)
{
return DateTime.SpecifyKind(value.Value, DateTimeKind.Utc);
}
else if (value.Value.Kind == DateTimeKind.Local)
{
return value.Value.ToUniversalTime();
}
else
{
return value;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
"downloadLocation": "https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz",
"externalRefs": [
{
"referenceCategory": "PACKAGE_MANAGER",
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:maven/org.apache.jena/apache-jena@3.12.0",
"referenceType": "purl"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
},
{
"comment": "This is the external ref for Acme",
"referenceCategory": "PERSISTENT_ID",
"referenceCategory": "PERSISTENT-ID",
"referenceLocator": "acmecorp/acmenator/4.1.3-alpha",
"referenceType": "swh"
}
Expand Down Expand Up @@ -144,7 +144,7 @@
"downloadLocation": "https://search.maven.org/remotecontent?filepath=org/apache/jena/apache-jena/3.12.0/apache-jena-3.12.0.tar.gz",
"externalRefs": [
{
"referenceCategory": "PACKAGE_MANAGER",
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:maven/org.apache.jena/apache-jena@3.12.0",
"referenceType": "purl"
}
Expand Down
2 changes: 2 additions & 0 deletions tests/CycloneDX.Spdx.Tests/JsonSerializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class JsonSerializerTests
{
[Theory]
[InlineData("document")]
[InlineData("document-with-hyphens-in-external-reference-category")]
public void JsonRoundTripTest(string baseFilename)
{
var resourceFilename = Path.Join("Resources", "v2.3", baseFilename + ".json");
Expand All @@ -42,6 +43,7 @@ public void JsonRoundTripTest(string baseFilename)

[Theory]
[InlineData("document")]
[InlineData("document-with-hyphens-in-external-reference-category")]
public async Task JsonAsyncRoundTripTest(string baseFilename)
{
var resourceFilename = Path.Join("Resources", "v2.3", baseFilename + ".json");
Expand Down
20 changes: 12 additions & 8 deletions tests/CycloneDX.Spdx.Tests/JsonValidatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ namespace CycloneDX.Spdx.Tests
public class JsonValidatorTests
{
[Theory]
[InlineData("v2.2")]
[InlineData("v2.3")]
public void ValidateJsonStringTest(string version)
[InlineData("v2.2", "document")]
[InlineData("v2.2", "document-with-hyphens-in-external-reference-category")]
[InlineData("v2.3", "document")]
[InlineData("v2.3", "document-with-hyphens-in-external-reference-category")]
public void ValidateJsonStringTest(string version, string baseFilename)
{
var resourceFilename = Path.Join("Resources", version, "document" + ".json");
var resourceFilename = Path.Join("Resources", version, baseFilename + ".json");
var document = File.ReadAllText(resourceFilename);

var result = JsonValidator.Validate(document);
Expand All @@ -41,11 +43,13 @@ public void ValidateJsonStringTest(string version)
}

[Theory]
[InlineData("v2.2")]
[InlineData("v2.3")]
public async Task ValidateJsonStreamTest(string version)
[InlineData("v2.2", "document")]
[InlineData("v2.2", "document-with-hyphens-in-external-reference-category")]
[InlineData("v2.3", "document")]
[InlineData("v2.3", "document-with-hyphens-in-external-reference-category")]
public async Task ValidateJsonStreamTest(string version, string baseFilename)
{
var resourceFilename = Path.Join("Resources", version, "document" + ".json");
var resourceFilename = Path.Join("Resources", version, baseFilename + ".json");
using (var jsonStream = File.OpenRead(resourceFilename))
{
var validationResult = await JsonValidator.ValidateAsync(jsonStream).ConfigureAwait(false);
Expand Down
Loading