Skip to content

Commit

Permalink
SARIF output format improvements #1739 #1740 (#1741)
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite authored Jan 26, 2024
1 parent c095da8 commit cf9f33e
Show file tree
Hide file tree
Showing 15 changed files with 641 additions and 362 deletions.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,24 @@
"deserializes",
"Gitter",
"hashtable",
"HEADREF",
"Kubernetes",
"Multiformat",
"Newtonsoft",
"NOTCOUNT",
"proxied",
"PSDATA",
"PSRULE",
"pwsh",
"quickstart",
"REPOROOT",
"runspace",
"runspaces",
"SARIF",
"SBOM",
"subselector",
"unencrypted",
"Worktree",
"xunit"
],
"[csharp]": {
Expand Down
5 changes: 5 additions & 0 deletions docs/CHANGELOG-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers

What's changed since pre-release v3.0.0-B0137:

- General improvements:
- SARIF output has been improved to include effective configuration from a run by @BernieWhite.
[#1739](https://github.com/microsoft/PSRule/issues/1739)
- SARIF output has been improved to include file hashes for source files from a run by @BernieWhite.
[#1740](https://github.com/microsoft/PSRule/issues/1740)
- Engineering:
- Bump YamlDotNet to v15.1.0.
[#1737](https://github.com/microsoft/PSRule/pull/1737)
Expand Down
7 changes: 7 additions & 0 deletions docs/analysis-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,4 +257,11 @@ To add the scans tab to build results the [SARIF SAST Scans Tab][2] extension ne

[2]: https://marketplace.visualstudio.com/items?itemName=sariftools.scans

### Verifying configuration

:octicons-milestone-24: v3.0.0

The configuration used to run PSRule is included in properties of the run.
This can be used to verify the configuration used to run PSRule.

*[SARIF]: Static Analysis Results Interchange Format
4 changes: 4 additions & 0 deletions src/PSRule.Types/Options/ExecutionActionPreference.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

namespace PSRule.Options;

/// <summary>
/// Determines the action to take for execution behaviors.
/// See <see cref="ExecutionOption"/> for the specific behaviors that are configurable.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum ExecutionActionPreference
{
/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions src/PSRule.Types/Options/LanguageMode.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

namespace PSRule.Options;

/// <summary>
/// Configures the language mode PowerShell code executes as within PSRule runtime.
/// Does not affect YAML or JSON expressions.
/// <seealso href="https://microsoft.github.io/PSRule/latest/concepts/PSRule/en-US/about_PSRule_Options/#executionlanguagemode"/>
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum LanguageMode
{
/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions src/PSRule.Types/Options/SessionState.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

namespace PSRule.Options;

/// <summary>
/// Configures how the initial PowerShell sandbox for executing rules is created.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public enum SessionState
{
/// <summary>
Expand Down
44 changes: 39 additions & 5 deletions src/PSRule/Common/GitHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,17 @@ public static bool TryRepository(out string value, string path = null)
}

// Try .git/
return false;
return TryGetOriginUrl(path, out value);
}

public static bool TryGetChangedFiles(string baseRef, string filter, string options, out string[] files)
{
// Get current tip
var source = TryRevision(out var source_sha) ? source_sha : "HEAD";
var source = TryRevision(out var source_sha) ? source_sha : GIT_HEAD;
var target = !string.IsNullOrEmpty(baseRef) ? baseRef : "HEAD^";

var bin = GetGitBinary();
var args = GetGitArgs(target, source, filter, options);
var args = GetDiffArgs(target, source, filter, options);
var tool = ExternalTool.Get(null, bin);

files = Array.Empty<string>();
Expand All @@ -157,7 +157,7 @@ private static string GetGitBinary()
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "git" : "git.exe";
}

private static string GetGitArgs(string target, string source, string filter, string options)
private static string GetDiffArgs(string target, string source, string filter, string options)
{
return $"diff --diff-filter={filter} --ignore-submodules=all --name-only --no-renames {target}";
}
Expand Down Expand Up @@ -195,8 +195,42 @@ private static bool TryCommit(string path, out string value, out bool isRef)
if (lines == null || lines.Length == 0)
return false;

isRef = lines[0].StartsWith(GIT_REF_PREFIX, System.StringComparison.OrdinalIgnoreCase);
isRef = lines[0].StartsWith(GIT_REF_PREFIX, StringComparison.OrdinalIgnoreCase);
value = isRef ? lines[0].Substring(5) : lines[0];
return true;
}

/// <summary>
/// Try to get the origin URL from the git config.
/// </summary>
private static bool TryGetOriginUrl(string path, out string value)
{
value = null;

try
{
var bin = GetGitBinary();
var args = GetWorktreeConfigArgs();
var tool = ExternalTool.Get(null, bin);

string[] lines = null;
if (!tool.WaitForExit(args, out var exitCode) || exitCode != 0)
return false;

lines = tool.GetOutput().Split(new string[] { System.Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var origin = lines.Where(line => line.StartsWith("remote.origin.url=", StringComparison.OrdinalIgnoreCase)).FirstOrDefault()?.Split('=')?[1];
value = origin;
}
catch
{
// Fail silently.
}

return value != null;
}

private static string GetWorktreeConfigArgs()
{
return "config --worktree --list";
}
}
20 changes: 19 additions & 1 deletion src/PSRule/Common/HashAlgorithmExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Security.Cryptography;
Expand All @@ -12,4 +12,22 @@ public static string GetDigest(this HashAlgorithm algorithm, byte[] buffer)
var hash = algorithm.ComputeHash(buffer);
return string.Join("", hash.Select(b => b.ToString("x2")).ToArray());
}

public static string GetFileDigest(this HashAlgorithm algorithm, string path)
{
return algorithm.GetDigest(File.ReadAllBytes(path));
}

public static HashAlgorithm GetHashAlgorithm(this Options.HashAlgorithm algorithm)
{
if (algorithm == Options.HashAlgorithm.SHA256)
return SHA256.Create();

return algorithm == Options.HashAlgorithm.SHA384 ? SHA384.Create() : SHA512.Create();
}

public static string GetHashAlgorithmName(this Options.HashAlgorithm algorithm)
{
return algorithm == Options.HashAlgorithm.SHA256 ? "sha-256" : algorithm == Options.HashAlgorithm.SHA384 ? "sha-384" : "sha-512";
}
}
12 changes: 10 additions & 2 deletions src/PSRule/Common/JsonConverters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ internal sealed class FieldMapJsonConverter : JsonConverter
{
public override bool CanRead => true;

public override bool CanWrite => false;
public override bool CanWrite => true;

public override bool CanConvert(Type objectType)
{
Expand All @@ -552,7 +552,15 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
if (value is not FieldMap map) return;

writer.WriteStartObject();
foreach (var field in map)
{
writer.WritePropertyName(field.Key);
serializer.Serialize(writer, field.Value);
}
writer.WriteEndObject();
}

private static void ReadFieldMap(FieldMap map, JsonReader reader)
Expand Down
2 changes: 1 addition & 1 deletion src/PSRule/Configuration/ConfigurationOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static implicit operator ConfigurationOption(Hashtable hashtable)
}

/// <summary>
/// Merge two option instances by repacing any unset properties from <paramref name="o1"/> with <paramref name="o2"/> values.
/// Merge two option instances by replacing any unset properties from <paramref name="o1"/> with <paramref name="o2"/> values.
/// Values from <paramref name="o1"/> that are set are not overridden.
/// </summary>
internal static ConfigurationOption Combine(ConfigurationOption o1, ConfigurationOption o2)
Expand Down
2 changes: 2 additions & 0 deletions src/PSRule/Configuration/FieldMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

using System.Collections;
using System.Dynamic;
using Newtonsoft.Json;

namespace PSRule.Configuration;

/// <summary>
/// A mapping of fields to property names.
/// </summary>
[JsonConverter(typeof(FieldMapJsonConverter))]
public sealed class FieldMap : DynamicObject, IEnumerable<KeyValuePair<string, string[]>>
{
private readonly Dictionary<string, string[]> _Map;
Expand Down
Loading

0 comments on commit cf9f33e

Please sign in to comment.