Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HDF to Sarif converter #2340

Merged
merged 13 commits into from
May 3, 2021
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
3 changes: 3 additions & 0 deletions src/ReleaseHistory.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# SARIF Package Release History (SDK, Driver, Converters, and Multitool)

## **v2.4.7** [Sdk](https://www.nuget.org/packages/Sarif.Sdk/2.4.7) | [Driver](https://www.nuget.org/packages/Sarif.Driver/2.4.7) | [Converters](https://www.nuget.org/packages/Sarif.Converters/2.4.7) | [Multitool](https://www.nuget.org/packages/Sarif.Multitool/2.4.7) | [Multitool Library](https://www.nuget.org/packages/Sarif.Multitool.Library/2.4.7)
* FEATURE: Add Hdf converter. [#2340](https://github.com/microsoft/sarif-sdk/pull/2340)

## **v2.4.6** [Sdk](https://www.nuget.org/packages/Sarif.Sdk/2.4.6) | [Driver](https://www.nuget.org/packages/Sarif.Driver/2.4.6) | [Converters](https://www.nuget.org/packages/Sarif.Converters/2.4.6) | [Multitool](https://www.nuget.org/packages/Sarif.Multitool/2.4.6) | [Multitool Library](https://www.nuget.org/packages/Sarif.Multitool.Library/2.4.6)
* FEATURE: Add CWE relationship in FlawFinder converter. [#2332](https://github.com/microsoft/sarif-sdk/pull/2332)
* FEATURE: Add `ResultLevelKind` which will handle `FailureLevel` and `ResultKind`. [#2331](https://github.com/microsoft/sarif-sdk/pull/2331)
Expand Down
1 change: 1 addition & 0 deletions src/Sarif.Converters/BuiltInConverterFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private static Dictionary<string, Lazy<ToolFileConverterBase>> CreateBuiltInConv
CreateConverterRecord<FortifyFprConverter>(result, ToolFormat.FortifyFpr);
CreateConverterRecord<FxCopConverter>(result, ToolFormat.FxCop);
CreateConverterRecord<FlawFinderConverter>(result, ToolFormat.FlawFinder);
CreateConverterRecord<HdfConverter>(result, ToolFormat.Hdf);
CreateConverterRecord<PREfastConverter>(result, ToolFormat.PREfast);
CreateConverterRecord<PylintConverter>(result, ToolFormat.Pylint);
CreateConverterRecord<SemmleQLConverter>(result, ToolFormat.SemmleQL);
Expand Down
182 changes: 182 additions & 0 deletions src/Sarif.Converters/HdfConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using Microsoft.CodeAnalysis.Sarif.Converters.HdfModel;

using Newtonsoft.Json.Linq;

namespace Microsoft.CodeAnalysis.Sarif.Converters
{
public class HdfConverter : ToolFileConverterBase
Copy link
Collaborator

@eddynaka eddynaka May 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done both, thanks

{
private const string PeriodString = ".";

private const string ToolInformationUri = "https://saf.cms.gov/#/normalize";

public override string ToolName => "Heimdall Tools";

public override void Convert(Stream input, IResultLogWriter output, OptionallyEmittedData dataToInsert)
{
input = input ?? throw new ArgumentNullException(nameof(input));
output = output ?? throw new ArgumentNullException(nameof(output));

using var textReader = new StreamReader(input);
string jsonString = textReader.ReadToEnd();
var hdfFile = HdfFile.FromJson(jsonString);

(List<ReportingDescriptor>, List<Result>) rulesAndResults = ExtractRulesAndResults(hdfFile);

var run = new Run
{
Tool = new Tool
{
Driver = new ToolComponent
{
Name = hdfFile.Platform.Name,
Version = hdfFile.Version,
InformationUri = new Uri(ToolInformationUri),
Rules = rulesAndResults.Item1,
SupportedTaxonomies = new List<ToolComponentReference>() { new ToolComponentReference() { Name = "NIST SP800-53 v5", Guid = "AAFBAB93-5201-419E-8443-D4925C542398" } }
}
},
ExternalPropertyFileReferences = new ExternalPropertyFileReferences()
{
Taxonomies = new List<ExternalPropertyFileReference>()
{
new ExternalPropertyFileReference()
{
Location = new ArtifactLocation()
{
Uri = new Uri("https://raw.githubusercontent.com/sarif-standard/taxonomies/main/NIST_SP800-53_v5.sarif"),
},
Guid = "AAFBAB93-5201-419E-8443-D4925C542398"
}
}
},
Results = rulesAndResults.Item2,
};

PersistResults(output, rulesAndResults.Item2, run);
}

private static (List<ReportingDescriptor>, List<Result>) ExtractRulesAndResults(HdfFile hdfFile)
{
var rules = new List<ReportingDescriptor>();
var ruleIds = new HashSet<string>();
var results = new List<Result>();

foreach (ExecJsonControl execJsonControl in hdfFile.Profiles.SelectMany(p => p.Controls))
{
string ruleId = execJsonControl.Id;
if (!ruleIds.Contains(ruleId))
{
(ReportingDescriptor, IList<Result>) ruleAndResult = SarifRuleAndResultFromHdfControl(execJsonControl);
rules.Add(ruleAndResult.Item1);
ruleIds.Add(ruleId);
results.AddRange(ruleAndResult.Item2);
}
}

return (rules, results);
}

private static (ReportingDescriptor, IList<Result>) SarifRuleAndResultFromHdfControl(ExecJsonControl execJsonControl)
{
var reportingDescriptor = new ReportingDescriptor
{
Id = execJsonControl.Id,
Name = execJsonControl.Title,
ShortDescription = new MultiformatMessageString
{
Text = AppendPeriod(execJsonControl.Desc),
},
DefaultConfiguration = new ReportingConfiguration
{
Level = SarifLevelFromHdfImpact(execJsonControl.Impact),
},
HelpUri = null,
Relationships = new List<ReportingDescriptorRelationship>(
((JArray)execJsonControl.Tags["nist"])
.Select(p => p.ToString().Trim()).Where(p => !string.IsNullOrWhiteSpace(p)).ToList().OrderBy(o => o)
.Select(s => new ReportingDescriptorRelationship()
{
Target = new ReportingDescriptorReference()
{
Id = s,
ToolComponent = new ToolComponentReference()
{
Name = "NIST",
Guid = "AAFBAB93-5201-419E-8443-D4925C542398"
}
},
Kinds = new List<string>() { "relevant" },
}))
};

var results = new List<Result>(execJsonControl.Results.Count);
foreach (ControlResult controlResult in execJsonControl.Results)
{
var result = new Result
{
RuleId = execJsonControl.Id,
Message = new Message
{
Text = AppendPeriod(controlResult.CodeDesc),
},
Level = SarifLevelFromHdfImpact(execJsonControl.Impact),
Rank = SarifRankFromHdfImpact(execJsonControl.Impact),
};
results.Add(result);
}
return (reportingDescriptor, results);
}

private static FailureLevel SarifLevelFromHdfImpact(double impact)
{
// level 4 & 5
// Hdf: Critical, High
if (impact >= 0.7)
shaopeng-gh marked this conversation as resolved.
Show resolved Hide resolved
{
return FailureLevel.Error;
}
// level 3
// Hdf: Medium
else if (impact >= 0.5)
{
return FailureLevel.Warning;
}
// level 2
// Hdf: Low
else if (impact >= 0.3)
{
return FailureLevel.Warning;
}
// level 0, 1
// Hdf: Best_Practice, Information
else
{
return FailureLevel.Note;
}
}

private static double SarifRankFromHdfImpact(double impact) =>
shaopeng-gh marked this conversation as resolved.
Show resolved Hide resolved
/*
SARIF rank Hdf Level SARIF level Default Viewer Action
0.0 0 note Does not display by default
0.3 0.3 note Does not display by default
0.5 0.5 warning Displays by default, does not break build / other processes
0.7 0.7 error Displays by default, breaks build/ other processes
1.0 1.0 error Displays by default, breaks build/ other processes
*/
// both range from 0 to 1, return as it is
impact;

private static string AppendPeriod(string text) =>
text.EndsWith(PeriodString, StringComparison.OrdinalIgnoreCase) ? text : text + PeriodString;
}
}
21 changes: 21 additions & 0 deletions src/Sarif.Converters/HdfModel/ControlDescription.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Globalization;

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Microsoft.CodeAnalysis.Sarif.Converters.HdfModel
{
public partial class ControlDescription
{
[JsonProperty("data", Required = Required.Default)]
public string Data { get; set; }

[JsonProperty("label", Required = Required.Default)]
public string Label { get; set; }
}
}
33 changes: 33 additions & 0 deletions src/Sarif.Converters/HdfModel/ControlGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Globalization;

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Microsoft.CodeAnalysis.Sarif.Converters.HdfModel
{
public partial class ControlGroup
{
/// <summary>
/// The control IDs in this group
/// </summary>
[JsonProperty("controls", Required = Required.Always)]
public List<string> Controls { get; set; }

/// <summary>
/// The unique identifier of the group
/// </summary>
[JsonProperty("id", Required = Required.Always)]
public string Id { get; set; }

/// <summary>
/// The name of the group
/// </summary>
[JsonProperty("title")]
public string Title { get; set; }
}
}
42 changes: 42 additions & 0 deletions src/Sarif.Converters/HdfModel/ControlResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Globalization;

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Microsoft.CodeAnalysis.Sarif.Converters.HdfModel
{
public partial class ControlResult
{
[JsonProperty("backtrace")]
public List<string> Backtrace { get; set; }

[JsonProperty("code_desc", Required = Required.Always)]
public string CodeDesc { get; set; }

[JsonProperty("exception", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
public string Exception { get; set; }

[JsonProperty("message", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
public string Message { get; set; }

[JsonProperty("resource", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
public string Resource { get; set; }

[JsonProperty("run_time", Required = Required.Default)]
public double RunTime { get; set; }

[JsonProperty("skip_message", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
public string SkipMessage { get; set; }

[JsonProperty("start_time", Required = Required.Default)]
public string StartTime { get; set; }

[JsonProperty("status", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
public ControlResultStatus? Status { get; set; }
}
}
14 changes: 14 additions & 0 deletions src/Sarif.Converters/HdfModel/ControlResultStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Globalization;

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Microsoft.CodeAnalysis.Sarif.Converters.HdfModel
{
public enum ControlResultStatus { Error, Failed, Passed, Skipped };
}
63 changes: 63 additions & 0 deletions src/Sarif.Converters/HdfModel/ControlResultStatusConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Globalization;

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Microsoft.CodeAnalysis.Sarif.Converters.HdfModel
{
internal class ControlResultStatusConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(ControlResultStatus) || t == typeof(ControlResultStatus?);

public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}

string value = serializer.Deserialize<string>(reader);
return value switch
{
"error" => ControlResultStatus.Error,
"failed" => ControlResultStatus.Failed,
"passed" => ControlResultStatus.Passed,
"skipped" => ControlResultStatus.Skipped,
_ => throw new Exception("Cannot unmarshal type ControlResultStatus"),
};
}

public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (ControlResultStatus)untypedValue;
switch (value)
{
case ControlResultStatus.Error:
serializer.Serialize(writer, "error");
return;
case ControlResultStatus.Failed:
serializer.Serialize(writer, "failed");
return;
case ControlResultStatus.Passed:
serializer.Serialize(writer, "passed");
return;
case ControlResultStatus.Skipped:
serializer.Serialize(writer, "skipped");
return;
}
throw new Exception("Cannot marshal type ControlResultStatus");
}

public static readonly ControlResultStatusConverter Singleton = new ControlResultStatusConverter();
}
}
Loading