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
28 changes: 15 additions & 13 deletions src/Microsoft.ComponentDetection.Common/FileWritingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ namespace Microsoft.ComponentDetection.Common;
using System.Collections.Concurrent;
using System.Globalization;
using System.IO;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ComponentDetection.Common.Exceptions;
using Newtonsoft.Json;

/// <inheritdoc />
public sealed class FileWritingService : IFileWritingService
Expand All @@ -17,6 +18,13 @@ public sealed class FileWritingService : IFileWritingService
/// The format string used to generate the timestamp for the manifest file.
/// </summary>
public const string TimestampFormatString = "yyyyMMddHHmmssfff";

private static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true,
};

private readonly ConcurrentDictionary<string, StreamWriter> bufferedStreams = new();

private readonly object lockObject = new();
Expand Down Expand Up @@ -50,11 +58,8 @@ public void AppendToFile<T>(string relativeFilePath, T obj)
_ = this.bufferedStreams.TryAdd(relativeFilePath, streamWriter);
}

var serializer = new JsonSerializer
{
Formatting = Formatting.Indented,
};
serializer.Serialize(streamWriter, obj);
var jsonString = JsonSerializer.Serialize(obj, obj.GetType(), JsonSerializerOptions);
streamWriter.Write(jsonString);
}

/// <inheritdoc />
Expand All @@ -79,13 +84,10 @@ public async Task WriteFileAsync(string relativeFilePath, string text, Cancellat
/// <inheritdoc />
public void WriteFile<T>(FileInfo relativeFilePath, T obj)
{
using var streamWriter = new StreamWriter(relativeFilePath.FullName);
using var jsonWriter = new JsonTextWriter(streamWriter);
var serializer = new JsonSerializer
{
Formatting = Formatting.Indented,
};
serializer.Serialize(jsonWriter, obj);
using var stream = relativeFilePath.Create();

// Use runtime type to ensure derived class properties are serialized
JsonSerializer.Serialize(stream, obj, obj.GetType(), JsonSerializerOptions);
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ public class DockerLayer
{
// Summary:
// the command/script that was executed in order to create the layer.
[JsonPropertyName("createdBy")]
[JsonPropertyName("CreatedBy")]
public string CreatedBy { get; set; }

// Summary:
// The Layer hash (docker inspect) that represents the changes between this layer and the previous layer
[JsonPropertyName("diffId")]
[JsonPropertyName("DiffId")]
public string DiffId { get; set; }

// Summary:
// Whether or not this layer was found in the base image of the container
[JsonPropertyName("isBaseImage")]
[JsonPropertyName("IsBaseImage")]
public bool IsBaseImage { get; set; }

// Summary:
// 0-indexed monotonically increasing ID for the order of the layer in the container.
// Note: only includes non-empty layers
[JsonPropertyName("layerIndex")]
[JsonPropertyName("LayerIndex")]
public int LayerIndex { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
namespace Microsoft.ComponentDetection.Contracts.TypedComponent;

using System.Text.Json.Serialization;
using Newtonsoft.Json;
using PackageUrl;

public class CargoComponent : TypedComponent
Expand All @@ -28,24 +27,23 @@ public CargoComponent(string name, string version, string author = null, string
public string Version { get; set; }

#nullable enable
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // Newtonsoft.Json
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] // System.Text.Json
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("author")]
public string? Author { get; set; }

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // Newtonsoft.Json
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] // System.Text.Json
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("license")]
public string? License { get; set; }

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // Newtonsoft.Json
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] // System.Text.Json
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("source")]
public string? Source { get; set; }
#nullable disable

[JsonIgnore]
public override ComponentType Type => ComponentType.Cargo;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl => new PackageURL("cargo", string.Empty, this.Name, this.Version, null, string.Empty);

protected override string ComputeId() => $"{this.Name} {this.Version} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ public ConanComponent(string name, string version, string previous, string packa
[JsonPropertyName("packageSourceURL")]
public string PackageSourceURL => $"https://conan.io/center/recipes/{this.Name}?version={this.Version}";

[JsonIgnore]
public override ComponentType Type => ComponentType.Conan;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl => new PackageURL("conan", string.Empty, this.Name, this.Version, null, string.Empty);

protected override string ComputeId() => $"{this.Name} {this.Version} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public CondaComponent()
[JsonPropertyName("mD5")]
public string MD5 { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.Conda;

protected override string ComputeId() => $"{this.Name} {this.Version} {this.Build} {this.Channel} {this.Subdir} {this.Namespace} {this.Url} {this.MD5} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public DockerImageComponent(string hash, string name = null, string tag = null)
[JsonPropertyName("tag")]
public string Tag { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.DockerImage;

protected override string ComputeId() => $"{this.Name} {this.Tag} {this.Digest}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public DockerReferenceComponent()
[JsonPropertyName("domain")]
public string Domain { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.DockerReference;

public DockerReference FullReference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public DotNetComponent(string sdkVersion, string targetFramework = null, string
[JsonPropertyName("projectType")]
public string ProjectType { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.DotNet;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public GitComponent()
[JsonPropertyName("tag")]
public string Tag { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.Git;

protected override string ComputeId() => $"{this.RepositoryUrl} : {this.CommitHash} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ public GoComponent()

// Commit should be used in place of version when available
// https://github.com/package-url/purl-spec/blame/180c46d266c45aa2bd81a2038af3f78e87bb4a25/README.rst#L610
[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl => new PackageURL("golang", null, this.Name, string.IsNullOrWhiteSpace(this.Hash) ? this.Version : this.Hash, null, null);

[JsonIgnore]
public override ComponentType Type => ComponentType.Go;

protected override string ComputeId() => $"{this.Name} {this.Version} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ public LinuxComponent(string distribution, string release, string name, string v
public string? Author { get; set; }
#nullable disable

[JsonIgnore]
public override ComponentType Type => ComponentType.Linux;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ public MavenComponent()
[JsonPropertyName("version")]
public string Version { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.Maven;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl => new PackageURL("maven", this.GroupId, this.ArtifactId, this.Version, null, null);

protected override string ComputeId() => $"{this.GroupId} {this.ArtifactId} {this.Version} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ public NpmComponent(string name, string version, string hash = null, NpmAuthor a
[JsonPropertyName("author")]
public NpmAuthor Author { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.Npm;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl => new PackageURL("npm", null, this.Name, this.Version, null, null);

protected override string ComputeId() => $"{this.Name} {this.Version} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ public NuGetComponent(string name, string version, string[] authors = null)
[JsonPropertyName("authors")]
public string[] Authors { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.NuGet;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl => new PackageURL("nuget", null, this.Name, this.Version, null, null);

protected override string ComputeId() => $"{this.Name} {this.Version} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public OtherComponent(string name, string version, Uri downloadUrl, string hash)
[JsonPropertyName("hash")]
public string Hash { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.Other;

protected override string ComputeId() => $"{this.Name} {this.Version} {this.DownloadUrl} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ namespace Microsoft.ComponentDetection.Contracts.TypedComponent;

using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using PackageUrl;

public class PipComponent : TypedComponent
Expand All @@ -28,19 +27,19 @@ public PipComponent(string name, string version, string author = null, string li
public string Version { get; set; }

#nullable enable
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // Newtonsoft.Json
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] // System.Text.Json
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("author")]
public string? Author { get; set; }

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] // Newtonsoft.Json
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] // System.Text.Json
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("license")]
public string? License { get; set; }
#nullable disable

[JsonIgnore]
public override ComponentType Type => ComponentType.Pip;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl => new PackageURL("pypi", null, this.Name, this.Version, null, null);

[SuppressMessage("Usage", "CA1308:Normalize String to Uppercase", Justification = "Casing cannot be overwritten.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ public PodComponent(string name, string version, string specRepo = "")
[JsonPropertyName("specRepo")]
public string SpecRepo { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.Pod;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ public RubyGemsComponent(string name, string version, string source = "")
[JsonPropertyName("source")]
public string Source { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.RubyGems;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl => new PackageURL("gem", null, this.Name, this.Version, null, null);

protected override string ComputeId() => $"{this.Name} {this.Version} - {this.Type}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public SpdxComponent(string spdxVersion, Uri documentNamespace, string name, str
this.Path = this.ValidateRequiredInput(path, nameof(this.Path), nameof(ComponentType.Spdx));
}

[JsonIgnore]
public override ComponentType Type => ComponentType.Spdx;

[JsonPropertyName("rootElementId")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ public SwiftComponent(string name, string version, string packageUrl, string has
[JsonPropertyName("version")]
public string Version { get; }

[JsonIgnore]
public override ComponentType Type => ComponentType.Swift;

// Example PackageURL -> pkg:swift/github.com/apple/swift-asn1
// type: swift
// namespace: github.com/apple
// name: swift-asn1
[JsonPropertyName("packageUrl")]
public PackageURL PackageURL => new PackageURL(
type: "swift",
@namespace: this.GetNamespaceFromPackageUrl(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,11 @@ internal TypedComponent()
public abstract ComponentType Type { get; }

/// <summary>Gets the id of the component.</summary>
[JsonProperty("id")] // Newtonsoft.Json
[JsonPropertyName("id")] // System.Text.Json
public string Id => this.id ??= this.ComputeId();

[JsonPropertyName("packageUrl")]
public virtual PackageURL PackageUrl { get; }

[JsonIgnore] // Newtonsoft.Json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ public VcpkgComponent(string spdxid, string name, string version, string triplet
[JsonPropertyName("portVersion")]
public int PortVersion { get; set; }

[JsonIgnore]
public override ComponentType Type => ComponentType.Vcpkg;

[JsonPropertyName("packageUrl")]
public override PackageURL PackageUrl
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ namespace Microsoft.ComponentDetection.Detectors.Linux.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Microsoft.ComponentDetection.Common.Telemetry.Records;
using Microsoft.ComponentDetection.Detectors.Linux.Contracts;
using Newtonsoft.Json;

/// <summary>
/// Filters out invalid ELF binary packages from Mariner 2.0 images that lack proper release/epoch version fields.
Expand Down Expand Up @@ -50,7 +50,7 @@ public IEnumerable<ArtifactElement> Filter(IEnumerable<ArtifactElement> artifact
artifactsList.Remove(elfArtifact);
}

syftTelemetryRecord.ComponentsRemoved = JsonConvert.SerializeObject(removedComponents);
syftTelemetryRecord.ComponentsRemoved = JsonSerializer.Serialize(removedComponents);
}

return artifactsList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ namespace Microsoft.ComponentDetection.Orchestrator.Commands;

using System;
using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ComponentDetection.Common;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
using Microsoft.ComponentDetection.Orchestrator.Services;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Spectre.Console.Cli;

/// <summary>
Expand All @@ -18,6 +18,8 @@ namespace Microsoft.ComponentDetection.Orchestrator.Commands;
public sealed class ScanCommand : AsyncCommand<ScanSettings>
{
private const string ManifestRelativePath = "ScanManifest_{timestamp}.json";
private static readonly JsonSerializerOptions IndentedJsonOptions = new() { WriteIndented = true };

private readonly IFileWritingService fileWritingService;
private readonly IScanExecutionService scanExecutionService;
private readonly ILogger<ScanCommand> logger;
Expand Down Expand Up @@ -89,12 +91,8 @@ private void WriteComponentManifest(ScanSettings settings, ScanResult scanResult

if (settings.PrintManifest)
{
var jsonWriter = new JsonTextWriter(Console.Out);
var serializer = new JsonSerializer
{
Formatting = Formatting.Indented,
};
serializer.Serialize(jsonWriter, scanResult);
var jsonString = JsonSerializer.Serialize(scanResult, IndentedJsonOptions);
Console.WriteLine(jsonString);
}
}
}
Loading
Loading