diff --git a/src/Microsoft.ComponentDetection.Detectors/spdx/Spdx22ComponentDetector.cs b/src/Microsoft.ComponentDetection.Detectors/spdx/Spdx22ComponentDetector.cs index ee53ccc8e..5a3b4bb1f 100644 --- a/src/Microsoft.ComponentDetection.Detectors/spdx/Spdx22ComponentDetector.cs +++ b/src/Microsoft.ComponentDetection.Detectors/spdx/Spdx22ComponentDetector.cs @@ -6,14 +6,13 @@ namespace Microsoft.ComponentDetection.Detectors.Spdx; using System.IO; using System.Linq; using System.Security.Cryptography; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.ComponentDetection.Contracts; using Microsoft.ComponentDetection.Contracts.Internal; using Microsoft.ComponentDetection.Contracts.TypedComponent; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; /// /// Spdx22ComponentDetector discover SPDX SBOM files in JSON format and create components with the information about @@ -44,7 +43,7 @@ public Spdx22ComponentDetector( public override IList SearchPatterns => ["*.spdx.json"]; - protected override Task OnFileFoundAsync(ProcessRequest processRequest, IDictionary detectorArgs, CancellationToken cancellationToken = default) + protected override async Task OnFileFoundAsync(ProcessRequest processRequest, IDictionary detectorArgs, CancellationToken cancellationToken = default) { this.Logger.LogDebug("Discovered SPDX2.2 manifest file at: {ManifestLocation}", processRequest.ComponentStream.Location); var file = processRequest.ComponentStream; @@ -56,31 +55,22 @@ protected override Task OnFileFoundAsync(ProcessRequest processRequest, IDiction // Reset buffer to starting position after hash generation. file.Stream.Seek(0, SeekOrigin.Begin); - using var sr = new StreamReader(file.Stream); - using var reader = new JsonTextReader(sr); - var serializer = new JsonSerializer(); - try { - var document = serializer.Deserialize(reader); - if (document != null) + using var document = await JsonDocument.ParseAsync(file.Stream, cancellationToken: cancellationToken).ConfigureAwait(false); + var root = document.RootElement; + + if (this.IsSPDXVersionSupported(root)) { - if (this.IsSPDXVersionSupported(document)) - { - var sbomComponent = this.ConvertJObjectToSbomComponent(processRequest, document, hash); - processRequest.SingleFileComponentRecorder.RegisterUsage(new DetectedComponent(sbomComponent)); - } - else - { - this.Logger.LogWarning("Discovered SPDX at {ManifestLocation} is not SPDX-2.2 document, skipping", processRequest.ComponentStream.Location); - } + var sbomComponent = this.ConvertJsonElementToSbomComponent(processRequest, root, hash); + processRequest.SingleFileComponentRecorder.RegisterUsage(new DetectedComponent(sbomComponent)); } else { - this.Logger.LogWarning("Discovered SPDX file at {ManifestLocation} is not a valid document, skipping", processRequest.ComponentStream.Location); + this.Logger.LogWarning("Discovered SPDX at {ManifestLocation} is not SPDX-2.2 document, skipping", processRequest.ComponentStream.Location); } } - catch (JsonReaderException) + catch (JsonException) { this.Logger.LogWarning("Unable to parse file at {ManifestLocation}, skipping", processRequest.ComponentStream.Location); } @@ -89,25 +79,40 @@ protected override Task OnFileFoundAsync(ProcessRequest processRequest, IDiction { this.Logger.LogError(e, "Error while processing SPDX file at {ManifestLocation}", processRequest.ComponentStream.Location); } - - return Task.CompletedTask; } - private bool IsSPDXVersionSupported(JObject document) => this.supportedSPDXVersions.Contains(document["spdxVersion"]?.ToString(), StringComparer.OrdinalIgnoreCase); + private bool IsSPDXVersionSupported(JsonElement document) + { + if (document.TryGetProperty("spdxVersion", out var versionElement)) + { + var version = versionElement.GetString(); + return this.supportedSPDXVersions.Contains(version, StringComparer.OrdinalIgnoreCase); + } - private SpdxComponent ConvertJObjectToSbomComponent(ProcessRequest processRequest, JObject document, string fileHash) + return false; + } + + private SpdxComponent ConvertJsonElementToSbomComponent(ProcessRequest processRequest, JsonElement document, string fileHash) { - var sbomNamespace = document["documentNamespace"]?.ToString(); - var rootElements = document["documentDescribes"]?.ToObject(); - var name = document["name"]?.ToString(); - var spdxVersion = document["spdxVersion"]?.ToString(); + var sbomNamespace = document.TryGetProperty("documentNamespace", out var nsElement) ? nsElement.GetString() : null; + var name = document.TryGetProperty("name", out var nameElement) ? nameElement.GetString() : null; + var spdxVersion = document.TryGetProperty("spdxVersion", out var spdxVersionElement) ? spdxVersionElement.GetString() : null; + + string[] rootElements = null; + if (document.TryGetProperty("documentDescribes", out var describesElement) && describesElement.ValueKind == JsonValueKind.Array) + { + rootElements = describesElement.EnumerateArray() + .Select(e => e.GetString()) + .Where(s => s != null) + .ToArray(); + } - if (rootElements?.Length > 1) + if (rootElements is { Length: > 1 }) { this.Logger.LogWarning("SPDX file at {ManifestLocation} has more than one element in documentDescribes, first will be selected as root element.", processRequest.ComponentStream.Location); } - if (rootElements != null && rootElements.Length == 0) + if (rootElements is { Length: 0 }) { this.Logger.LogWarning("SPDX file at {ManifestLocation} does not have root elements in documentDescribes section, considering SPDXRef-Document as a root element.", processRequest.ComponentStream.Location); }