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
10 changes: 5 additions & 5 deletions src/CycloneDX.Core/CycloneDX.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="JsonSchema.Net" Version="5.3.1" />
<PackageReference Include="protobuf-net" Version="3.2.45" />
<PackageReference Include="protobuf-net.BuildTools" Version="3.2.12" PrivateAssets="all" IncludeAssets="runtime;build;native;contentfiles;analyzers;buildtransitive" />
<PackageReference Include="JsonSchema.Net" Version="9.1.1" />
<PackageReference Include="protobuf-net" Version="3.2.56" />
<PackageReference Include="protobuf-net.BuildTools" Version="3.2.52" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
</ItemGroup>

<!--The below packages are natively provided by the framework hence are not need for the frameworks which they are included in. What is included can be checked via https://apisof.net/-->
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="System.Text.Json" Version="10.0.3" />
</ItemGroup>

<ItemGroup>
Expand Down
69 changes: 40 additions & 29 deletions src/CycloneDX.Core/Json/Validator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ namespace CycloneDX.Json
/// </summary>
public static class Validator
{
private static readonly Dictionary<SpecificationVersion, JsonSchema> _bomSchemas = new Dictionary<SpecificationVersion, JsonSchema>();

static Validator()
{
// I think the global schema registry is not thread safe
// well, I'm pretty sure, it's the only thing I can think of that would explain the sporadic test failures
// might as well just do it once on initialisation
// The global schema registry is not thread safe, and JsonSchema.FromText
// registers schemas internally. Pre-load everything once at initialisation
// to avoid concurrent registration conflicts during parallel test execution.
var assembly = typeof(Validator).GetTypeInfo().Assembly;
using (var spdxStream = assembly.GetManifestResourceStream("CycloneDX.Core.Schemas.spdx.schema.json"))
using (var spdxStreamReader = new StreamReader(spdxStream))
Expand All @@ -55,6 +57,26 @@ static Validator()
var cryptoDefsSchema = JsonSchema.FromText(cryptoDefsStreamReader.ReadToEnd());
SchemaRegistry.Global.Register(new Uri("http://cyclonedx.org/schema/cryptography-defs.schema.json"), cryptoDefsSchema);
}

// Pre-load all BOM schemas (v1.2+) so they are never parsed concurrently
var jsonVersions = new[]
{
SpecificationVersion.v1_2,
SpecificationVersion.v1_3,
SpecificationVersion.v1_4,
SpecificationVersion.v1_5,
SpecificationVersion.v1_6,
SpecificationVersion.v1_7,
};
foreach (var version in jsonVersions)
{
var versionString = SchemaVersionResourceFilenameString(version);
using (var stream = assembly.GetManifestResourceStream($"CycloneDX.Core.Schemas.bom-{versionString}.schema.json"))
using (var reader = new StreamReader(stream))
{
_bomSchemas[version] = JsonSchema.FromText(reader.ReadToEnd());
}
}
}

/// <summary>
Expand All @@ -71,14 +93,9 @@ public static async Task<ValidationResult> ValidateAsync(Stream jsonStream, Spec
}

var schemaVersionString = SchemaVersionResourceFilenameString(specificationVersion);
var assembly = typeof(Validator).GetTypeInfo().Assembly;

using (var schemaStream = assembly.GetManifestResourceStream($"CycloneDX.Core.Schemas.bom-{schemaVersionString}.schema.json"))
{
var jsonSchema = await JsonSchema.FromStream(schemaStream).ConfigureAwait(false);
var jsonDocument = await JsonDocument.ParseAsync(jsonStream).ConfigureAwait(false);
return Validate(jsonSchema, jsonDocument, schemaVersionString);
}
var jsonSchema = _bomSchemas[specificationVersion];
var jsonDocument = await JsonDocument.ParseAsync(jsonStream).ConfigureAwait(false);
return Validate(jsonSchema, jsonDocument, schemaVersionString);
}

/// <summary>
Expand Down Expand Up @@ -162,25 +179,19 @@ public static ValidationResult Validate(string jsonString, SpecificationVersion
}

var schemaVersionString = SchemaVersionResourceFilenameString(specificationVersion);
var assembly = typeof(Validator).GetTypeInfo().Assembly;

using (var schemaStream = assembly.GetManifestResourceStream($"CycloneDX.Core.Schemas.bom-{schemaVersionString}.schema.json"))
using (var schemaStreamReader = new StreamReader(schemaStream))
var jsonSchema = _bomSchemas[specificationVersion];
try
{
var jsonSchema = JsonSchema.FromText(schemaStreamReader.ReadToEnd());
try
{
var jsonDocument = JsonDocument.Parse(jsonString);
return Validate(jsonSchema, jsonDocument, schemaVersionString);
}
catch (JsonException exc)
var jsonDocument = JsonDocument.Parse(jsonString);
return Validate(jsonSchema, jsonDocument, schemaVersionString);
}
catch (JsonException exc)
{
return new ValidationResult
{
return new ValidationResult
{
Valid = false,
Messages = new List<string> { exc.Message }
};
}
Valid = false,
Messages = new List<string> { exc.Message }
};
}
}

Expand Down Expand Up @@ -232,7 +243,7 @@ private static ValidationResult Validate(JsonSchema schema, JsonDocument jsonDoc
continue;
}

if (detail.HasErrors)
if (detail.Errors != null)
{
foreach (var error in detail.Errors)
{
Expand Down
2 changes: 2 additions & 0 deletions src/CycloneDX.Core/Models/AttachedText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ namespace CycloneDX.Models
public class AttachedText
{
[XmlAttribute("content-type")]
#pragma warning disable PBN0020 // DefaultValue would change protobuf serialization behavior
[ProtoMember(1)]
public string ContentType { get; set; } = "text/plain";
#pragma warning restore PBN0020

[XmlAttribute("encoding")]
[ProtoMember(2)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,45 @@ public class RelatedCryptoMaterialProperties
[ProtoMember(4)]
public string AlgorithmRef { get; set; }

private DateTime? _creationDate;
[XmlElement("creationDate")]
[ProtoMember(5)]
public DateTime CreationDate { get; set; }
public DateTime? CreationDate
{
get => _creationDate;
set { _creationDate = BomUtils.UtcifyDateTime(value); }
}
public bool ShouldSerializeCreationDate() { return CreationDate != null; }

private DateTime? _activationDate;
[XmlElement("activationDate")]
[ProtoMember(6)]
public DateTime ActivationDate { get; set; }
public DateTime? ActivationDate
{
get => _activationDate;
set { _activationDate = BomUtils.UtcifyDateTime(value); }
}
public bool ShouldSerializeActivationDate() { return ActivationDate != null; }

private DateTime? _updateDate;
[XmlElement("updateDate")]
[ProtoMember(7)]
public DateTime UpdateDate { get; set; }
public DateTime? UpdateDate
{
get => _updateDate;
set { _updateDate = BomUtils.UtcifyDateTime(value); }
}
public bool ShouldSerializeUpdateDate() { return UpdateDate != null; }

private DateTime? _expirationDate;
[XmlElement("expirationDate")]
[ProtoMember(8)]
public DateTime ExpirationDate { get; set; }
public DateTime? ExpirationDate
{
get => _expirationDate;
set { _expirationDate = BomUtils.UtcifyDateTime(value); }
}
public bool ShouldSerializeExpirationDate() { return ExpirationDate != null; }

[XmlElement("value")]
[ProtoMember(9)]
Expand Down
2 changes: 2 additions & 0 deletions src/CycloneDX.Core/Models/Swid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ public class Swid
public string Name { get; set; }

[XmlAttribute("version")]
#pragma warning disable PBN0020 // DefaultValue would change protobuf serialization behavior
[ProtoMember(3)]
public string Version { get; set; } = "0.0";
#pragma warning restore PBN0020

[XmlAttribute("tagVersion")]
[ProtoMember(4)]
Expand Down
2 changes: 1 addition & 1 deletion src/CycloneDX.Spdx.Interop/CycloneDX.Spdx.Interop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
6 changes: 3 additions & 3 deletions src/CycloneDX.Spdx/CycloneDX.Spdx.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="JsonSchema.Net" Version="5.3.1" />
<PackageReference Include="JsonSchema.Net" Version="9.1.1" />
</ItemGroup>

<!--The below packages are natively provided by the framework hence are not need for the frameworks which they are included in. What is included can be checked via https://apisof.net/-->
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="System.Text.Json" Version="10.0.3" />
</ItemGroup>

<ItemGroup>
Expand Down
6 changes: 1 addition & 5 deletions src/CycloneDX.Spdx/CycloneDX.Spdx.csproj.user
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<ItemGroup>
<EmbeddedResource Update="Schemas\spdx-2.3.schema.xsd">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup />
</Project>
55 changes: 28 additions & 27 deletions src/CycloneDX.Spdx/Validation/JsonValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ namespace CycloneDX.Spdx.Validation
{
public static class JsonValidator
{
private static readonly JsonSchema _spdxSchema;

static JsonValidator()
{
// Pre-load the SPDX schema once to avoid concurrent JsonSchema.FromText
// calls registering schemas in the global registry in parallel.
var assembly = typeof(JsonValidator).GetTypeInfo().Assembly;
using (var schemaStream = assembly.GetManifestResourceStream("CycloneDX.Spdx.Schemas.spdx-2.3.schema.json"))
using (var schemaStreamReader = new StreamReader(schemaStream))
{
_spdxSchema = JsonSchema.FromText(schemaStreamReader.ReadToEnd());
}
}

/// <summary>
/// Validate the stream contents represent a valid SPDX JSON document.
/// </summary>
Expand All @@ -36,42 +50,29 @@ public static class JsonValidator
/// <returns></returns>
public static async Task<ValidationResult> ValidateAsync(Stream jsonStream)
{
var assembly = typeof(JsonValidator).GetTypeInfo().Assembly;

using (var schemaStream = assembly.GetManifestResourceStream($"CycloneDX.Spdx.Schemas.spdx-2.3.schema.json"))
{
var jsonSchema = await JsonSchema.FromStream(schemaStream).ConfigureAwait(false);
var jsonDocument = await JsonDocument.ParseAsync(jsonStream).ConfigureAwait(false);
return Validate(jsonSchema, jsonDocument);
}
var jsonDocument = await JsonDocument.ParseAsync(jsonStream).ConfigureAwait(false);
return Validate(_spdxSchema, jsonDocument);
}

/// <summary>
/// Validate the string contents represent a valid SPDX JSON document.
/// </summary>
/// <param name="jsonString"></param>
/// <returns></returns>
public static ValidationResult Validate(string jsonString)
{
var assembly = typeof(JsonValidator).GetTypeInfo().Assembly;

using (var schemaStream = assembly.GetManifestResourceStream($"CycloneDX.Spdx.Schemas.spdx-2.3.schema.json"))
using (var schemaStreamReader = new StreamReader(schemaStream))
try
{
var jsonSchema = JsonSchema.FromText(schemaStreamReader.ReadToEnd());
try
{
var jsonDocument = JsonDocument.Parse(jsonString);
return Validate(jsonSchema, jsonDocument);
}
catch (JsonException exc)
var jsonDocument = JsonDocument.Parse(jsonString);
return Validate(_spdxSchema, jsonDocument);
}
catch (JsonException exc)
{
return new ValidationResult
{
return new ValidationResult
{
Valid = false,
Messages = new List<string> { exc.Message }
};
}
Valid = false,
Messages = new List<string> { exc.Message }
};
}
}

Expand All @@ -93,7 +94,7 @@ private static ValidationResult Validate(JsonSchema schema, JsonDocument jsonDoc
// there will be no nested results
foreach (var detail in result.Details)
{
if (detail.HasErrors)
if (detail.Errors != null)
{
foreach (var error in detail.Errors)
{
Expand Down
2 changes: 1 addition & 1 deletion src/CycloneDX.Utils/CycloneDX.Utils.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
12 changes: 6 additions & 6 deletions tests/CycloneDX.Core.Tests/CycloneDX.Core.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
<PackageReference Include="coverlet.msbuild" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Snapshooter.Xunit" Version="0.13.0" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="Snapshooter.Xunit" Version="1.3.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PackageReference Include="coverlet.collector" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
4 changes: 2 additions & 2 deletions tests/CycloneDX.Core.Tests/Json/v1.2/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ public async Task JsonRoundTripAsyncTest(string filename)
using (var ms = new MemoryStream())
using (var sr = new StreamReader(ms))
{
var bom = await Serializer.DeserializeAsync(jsonBomStream).ConfigureAwait(false);
await Serializer.SerializeAsync(bom, ms).ConfigureAwait(false);
var bom = await Serializer.DeserializeAsync(jsonBomStream);
await Serializer.SerializeAsync(bom, ms);
ms.Position = 0;
Snapshot.Match(sr.ReadToEnd(), SnapshotNameExtension.Create(filename));
}
Expand Down
2 changes: 1 addition & 1 deletion tests/CycloneDX.Core.Tests/Json/v1.2/ValidationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public async Task ValidateJsonStreamTest(string filename)
var resourceFilename = Path.Join("Resources", "v1.2", filename);
using (var jsonStream = File.OpenRead(resourceFilename))
{
var validationResult = await Validator.ValidateAsync(jsonStream, SpecificationVersion.v1_2).ConfigureAwait(false);
var validationResult = await Validator.ValidateAsync(jsonStream, SpecificationVersion.v1_2);

Assert.True(validationResult.Valid);
}
Expand Down
4 changes: 2 additions & 2 deletions tests/CycloneDX.Core.Tests/Json/v1.3/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ public async Task JsonRoundTripAsyncTest(string filename)
using (var ms = new MemoryStream())
using (var sr = new StreamReader(ms))
{
var bom = await Serializer.DeserializeAsync(jsonBomStream).ConfigureAwait(false);
await Serializer.SerializeAsync(bom, ms).ConfigureAwait(false);
var bom = await Serializer.DeserializeAsync(jsonBomStream);
await Serializer.SerializeAsync(bom, ms);
ms.Position = 0;
Snapshot.Match(sr.ReadToEnd(), SnapshotNameExtension.Create(filename));
}
Expand Down
Loading