Skip to content

Commit 4cc3135

Browse files
pragnya17ppandrate
andauthored
Spdx 3.0 Parser for SBOM files (#860)
* Parsing spdx 3.0 with Unit tests and support for parsing with different compliance standards * Fix unit test syntax issues, clean up json parser * remove unnecessary changes, clean up assemblies * fix bugs in generator tests * addressing pr comments * remove unused param * make sure compliance standard has error handling and use enums --------- Co-authored-by: ppandrate <ppandrate@microsoft.com>
1 parent 3d38ef0 commit 4cc3135

37 files changed

+1585
-98
lines changed
Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace Microsoft.Sbom.JsonAsynchronousNodeKit;
2121
/// This class is not Thread-safe since the stream and JsonReaders assume a single forward-only reader.
2222
/// Because of the use of recursion in the GetObject method, this class is also not suitable for parsing very deep json objects.
2323
/// </remarks>
24-
internal class LargeJsonParser
24+
public class LargeJsonParser
2525
{
2626
private const int DefaultReadBufferSize = 4096;
2727
private readonly Stream stream;
@@ -43,7 +43,6 @@ public LargeJsonParser(
4343
this.stream = stream ?? throw new ArgumentNullException(nameof(stream));
4444
this.handlers = handlers ?? throw new ArgumentNullException(nameof(handlers));
4545
this.jsonSerializerOptions = jsonSerializerOptions ?? new JsonSerializerOptions();
46-
4746
this.buffer = new byte[bufferSize];
4847

4948
// Validate buffer is not of 0 length.
@@ -153,6 +152,7 @@ private ParserStateResult HandleExplicitProperty(ref Utf8JsonReader reader, stri
153152
var handler = this.handlers![propertyName];
154153

155154
object? result;
155+
156156
switch (handler.Type)
157157
{
158158
case ParameterType.String:
@@ -268,11 +268,23 @@ private IEnumerable<object> GetArray(Type type)
268268

269269
private object GetObject(Type type, ref Utf8JsonReader reader)
270270
{
271-
var jsonObject = ParserUtils.ParseObject(this.stream, ref this.buffer, ref reader);
272-
object? result = jsonObject;
273-
if (type != typeof(JsonNode))
271+
object? result = null;
272+
273+
if (type == typeof(string))
274+
{
275+
result = reader.GetString();
276+
}
277+
else
274278
{
275-
result = jsonObject.Deserialize(type, this.jsonSerializerOptions);
279+
var jsonObject = ParserUtils.ParseObject(this.stream, ref this.buffer, ref reader);
280+
if (type != typeof(JsonNode))
281+
{
282+
result = jsonObject.Deserialize(type, this.jsonSerializerOptions);
283+
}
284+
else
285+
{
286+
result = jsonObject;
287+
}
276288
}
277289

278290
if (result is null)

src/Microsoft.Sbom.Contracts/Contracts/Spdx22Metadata.cs renamed to src/Microsoft.Sbom.Contracts/Contracts/SpdxMetadata.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Microsoft.Sbom.Contracts;
99
/// <summary>
1010
/// The object representation of all the metadata in an SPDX document.
1111
/// </summary>
12-
public class Spdx22Metadata
12+
public class SpdxMetadata
1313
{
1414
/// <summary>
1515
/// The version of the SPDX specification used in this document.

src/Microsoft.Sbom.Extensions/ISbomParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public interface ISbomParser
2727
/// </summary>
2828
/// <param name="stream"></param>
2929
/// <returns></returns>
30-
Spdx22Metadata GetMetadata();
30+
SpdxMetadata GetMetadata();
3131

3232
/// <summary>
3333
/// This function is called by the sbom tool upon initialization to get all the

src/Microsoft.Sbom.Parsers.Spdx22SbomParser/Parser/SPDXParser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,14 @@ internal SPDXParser(
172172
return null;
173173
}
174174

175-
public Spdx22Metadata GetMetadata()
175+
public SpdxMetadata GetMetadata()
176176
{
177177
if (!this.parsingComplete)
178178
{
179179
throw new ParserException($"{nameof(this.GetMetadata)} can only be called after Parsing is complete to ensure that a whole object is returned.");
180180
}
181181

182-
var spdxMetadata = new Spdx22Metadata();
182+
var spdxMetadata = new SpdxMetadata();
183183
foreach (var kvp in this.metadata)
184184
{
185185
switch (kvp.Key)

src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Constants.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ internal static class Constants
1616
internal const string SPDXDocumentNameFormatString = "{0} {1}";
1717
internal const string PackageSupplierFormatString = "Organization: {0}";
1818

19+
internal const string SPDXContextHeaderName = "@context";
20+
internal const string SPDXGraphHeaderName = "@graph";
21+
1922
/// <summary>
2023
/// Use if SPDX creator
2124
/// - made an attempt to retrieve the info but cannot determine correct values.
@@ -24,6 +27,8 @@ internal static class Constants
2427
/// </summary>
2528
internal const string NoAssertionValue = "NOASSERTION";
2629

30+
internal const int ReadBufferSize = 4096;
31+
2732
/// <summary>
2833
/// The <see cref="NoAssertionValue"/> value as a list with a single item.
2934
/// </summary>

src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Entities/Element.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ protected Element()
4545

4646
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
4747
[JsonPropertyName("name")]
48-
public string Name { get; set; }
48+
public virtual string Name { get; set; }
4949

5050
/// <summary>
5151
/// Gets or sets unique Identifier for elements in SPDX document.
@@ -63,7 +63,7 @@ protected Element()
6363
/// </summary>
6464
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
6565
[JsonPropertyName("verifiedUsing")]
66-
public List<PackageVerificationCode> VerifiedUsing { get; set; }
66+
public virtual List<PackageVerificationCode> VerifiedUsing { get; set; }
6767

6868
[JsonRequired]
6969
[JsonPropertyName("type")]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Text.Json.Serialization;
6+
7+
namespace Microsoft.Sbom.Parsers.Spdx30SbomParser.Entities.Enums;
8+
9+
/// <summary>
10+
/// Defines the different supported compliance standards.
11+
/// </summary>
12+
[JsonConverter(typeof(JsonStringEnumConverter))]
13+
public enum ComplianceStandard
14+
{
15+
NTIA,
16+
None,
17+
}

src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Entities/File.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,8 @@ public File()
2222
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2323
[JsonPropertyName("mediaType")]
2424
public object MediaType { get; set; }
25+
26+
[JsonRequired]
27+
[JsonPropertyName("name")]
28+
public override string Name { get; set; }
2529
}

src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Entities/FormatEnforcedSPDX3.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class FormatEnforcedSPDX3
2525
/// </summary>
2626
[JsonRequired]
2727
[JsonPropertyName("@graph")]
28-
public List<Element> Graph { get; set; }
28+
public IEnumerable<Element> Graph { get; set; }
2929

3030
/// <summary>
3131
/// Required as part of the Core profile.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Collections;
5+
using System.Collections.Generic;
6+
using System.Text.Json.Serialization;
7+
8+
namespace Microsoft.Sbom.Parsers.Spdx30SbomParser.Entities;
9+
10+
/// <summary>
11+
/// Refers to any object that stores content on a computer.
12+
/// The type of content can optionally be provided in the contentType property.
13+
/// https://spdx.github.io/spdx-spec/v3.0.1/model/Software/Classes/File/
14+
/// An NTIA file specifically describes a file compliant with the NTIA SBOM standard.
15+
/// </summary>
16+
public class NTIAFile : File
17+
{
18+
public NTIAFile()
19+
{
20+
Type = "software_File";
21+
}
22+
23+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
24+
[JsonPropertyName("software_contentType")]
25+
public object ContentType { get; set; }
26+
27+
/// <summary>
28+
/// Make verification code required for Files. This is an internal requirement, not a requirement from SPDX.
29+
/// </summary>
30+
[JsonRequired]
31+
[JsonPropertyName("verifiedUsing")]
32+
public override List<PackageVerificationCode> VerifiedUsing { get; set; }
33+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Collections.Generic;
5+
using System.Text.Json.Serialization;
6+
using Microsoft.Sbom.Parsers.Spdx30SbomParser.Entities.Enums;
7+
8+
namespace Microsoft.Sbom.Parsers.Spdx30SbomParser.Entities;
9+
10+
/// <summary>
11+
/// The SpdxDocument provides a convenient way to express information about collections of SPDX Elements that could potentially be serialized as complete units (e.g., all in-scope SPDX data within a single JSON-LD file).
12+
/// https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/SpdxDocument/
13+
/// An NTIA SpdxDocument specifically describes a SpdxDocument entity compliant with the NTIA SBOM standard.
14+
/// </summary>
15+
public class NTIASpdxDocument : SpdxDocument
16+
{
17+
public NTIASpdxDocument()
18+
{
19+
Type = nameof(SpdxDocument);
20+
}
21+
22+
[JsonRequired]
23+
[JsonPropertyName("name")]
24+
public override string Name { get; set; }
25+
}

src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Entities/NamespaceMap.cs

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Entities/Package.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,9 @@ public Package()
2222
/// </summary>
2323
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2424
[JsonPropertyName("suppliedBy")]
25-
public string SuppliedBy { get; set; }
25+
public virtual string SuppliedBy { get; set; }
26+
27+
[JsonRequired]
28+
[JsonPropertyName("name")]
29+
public override string Name { get; set; }
2630
}

src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Entities/Spdx30Relationship.cs renamed to src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Entities/Relationship.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ namespace Microsoft.Sbom.Parsers.Spdx30SbomParser.Entities;
1313
/// Defines relationships between elements in the current SBOM.
1414
/// https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/Relationship/
1515
/// </summary>
16-
public class Spdx30Relationship : Element
16+
public class Relationship : Element
1717
{
1818
/// <summary>
19-
/// Initializes a new instance of the <see cref="Spdx30Relationship"/> class.
19+
/// Initializes a new instance of the <see cref="Relationship"/> class.
2020
/// </summary>
21-
public Spdx30Relationship()
21+
public Relationship()
2222
{
2323
Type = nameof(Relationship);
2424
}

src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Entities/Software.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
using System.Collections.Generic;
55
using System.Text.Json.Serialization;
66
using Microsoft.Sbom.Parsers.Spdx30SbomParser.Entities;
7-
using Newtonsoft.Json;
8-
using JsonIgnoreAttribute = System.Text.Json.Serialization.JsonIgnoreAttribute;
97

108
/// <summary>
119
/// Class defined as specified in: https://spdx.github.io/spdx-spec/v3.0.1/model/Software/Software/
@@ -37,8 +35,8 @@ public abstract class Software : Element
3735
public string ContentIdentifierValue { get; set; }
3836

3937
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
40-
[JsonProperty(PropertyName = "software_copyrightText")]
41-
public string CopyrightText { get; set; }
38+
[JsonPropertyName("software_copyrightText")]
39+
public virtual string CopyrightText { get; set; }
4240

4341
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
4442
[JsonPropertyName("software_downloadLocation")]
@@ -62,7 +60,7 @@ public abstract class Software : Element
6260

6361
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
6462
[JsonPropertyName("software_packageVersion")]
65-
public string PackageVersion { get; set; }
63+
public virtual string PackageVersion { get; set; }
6664

6765
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
6866
[JsonPropertyName("software_primaryPurpose")]
@@ -77,6 +75,6 @@ public abstract class Software : Element
7775
public File SnippetFromFile { get; set; }
7876

7977
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
80-
[JsonPropertyName("software_copyrightText")]
78+
[JsonPropertyName("software_sourceInfo")]
8179
public string SourceInfo { get; set; }
8280
}

src/Microsoft.Sbom.Parsers.Spdx30SbomParser/Entities/SpdxDocument.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class SpdxDocument : Element
2323

2424
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2525
[JsonPropertyName("namespaceMap")]
26-
public List<NamespaceMap> NamespaceMap { get; set; }
26+
public Dictionary<string, string> NamespaceMap { get; set; }
2727

2828
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2929
[JsonPropertyName("element")]
@@ -35,5 +35,5 @@ public class SpdxDocument : Element
3535

3636
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
3737
[JsonPropertyName("rootElement")]
38-
public List<Element> RootElement { get; set; }
38+
public List<string> RootElement { get; set; }
3939
}

0 commit comments

Comments
 (0)