diff --git a/src/build/Workleap.DotNet.CodingStandards.props b/src/build/Workleap.DotNet.CodingStandards.props
index a144399..e0b296b 100644
--- a/src/build/Workleap.DotNet.CodingStandards.props
+++ b/src/build/Workleap.DotNet.CodingStandards.props
@@ -17,8 +17,9 @@
true
true
-
+
true
+ true
true
diff --git a/tests/Workleap.DotNet.CodingStandards.Tests/CodingStandardTests.cs b/tests/Workleap.DotNet.CodingStandards.Tests/CodingStandardTests.cs
index 42e3252..9813269 100644
--- a/tests/Workleap.DotNet.CodingStandards.Tests/CodingStandardTests.cs
+++ b/tests/Workleap.DotNet.CodingStandards.Tests/CodingStandardTests.cs
@@ -25,6 +25,42 @@ public async Task WarningsAsErrorOnGitHubActions()
Assert.True(data.HasError("RS0030"));
}
+ [Fact]
+ public async Task MSBuildWarningsAsErrorOnDebugConfiguration()
+ {
+ using var project = new ProjectBuilder(fixture, testOutputHelper);
+ project.AddCsprojFile(packageReferences: new Dictionary { { "Azure.Identity", "1.10.4" } });
+ project.AddFile("sample.cs", """
+ namespace sample;
+ public static class Sample
+ {
+ public static void Main(string[] args)
+ {
+ }
+ }
+ """);
+ var data = await project.BuildAndGetOutput();
+ Assert.True(data.HasWarning("NU1902"));
+ }
+
+ [Fact]
+ public async Task MSBuildWarningsAsErrorOnReleaseConfiguration()
+ {
+ using var project = new ProjectBuilder(fixture, testOutputHelper);
+ project.AddCsprojFile(packageReferences: new Dictionary { { "Azure.Identity", "1.10.4" } });
+ project.AddFile("sample.cs", """
+ namespace sample;
+ public static class Sample
+ {
+ public static void Main(string[] args)
+ {
+ }
+ }
+ """);
+ var data = await project.BuildAndGetOutput(["--configuration", "Release"]);
+ Assert.True(data.HasError("NU1902"));
+ }
+
[Fact]
public async Task NamingConvention_Invalid()
{
diff --git a/tests/Workleap.DotNet.CodingStandards.Tests/Helpers/ProjectBuilder.cs b/tests/Workleap.DotNet.CodingStandards.Tests/Helpers/ProjectBuilder.cs
index ac33beb..d4c6b84 100644
--- a/tests/Workleap.DotNet.CodingStandards.Tests/Helpers/ProjectBuilder.cs
+++ b/tests/Workleap.DotNet.CodingStandards.Tests/Helpers/ProjectBuilder.cs
@@ -2,6 +2,7 @@
using Xunit.Abstractions;
using System.Text.Json;
using CliWrap;
+using Xunit.Sdk;
namespace Workleap.DotNet.CodingStandards.Tests.Helpers;
@@ -46,14 +47,27 @@ public void AddFile(string relativePath, string content)
File.WriteAllText(this._directory.GetPath(relativePath), content);
}
- public void AddCsprojFile(Dictionary? properties = null)
+ public void AddCsprojFile(Dictionary? properties = null, Dictionary? packageReferences = null)
{
- var element = new XElement("PropertyGroup");
+ var propertyElement = new XElement("PropertyGroup");
if (properties != null)
{
foreach (var prop in properties)
{
- element.Add(new XElement(prop.Key), prop.Value);
+ propertyElement.Add(new XElement(prop.Key), prop.Value);
+ }
+ }
+
+ var referencesElement = new XElement("ItemGroup");
+ if (packageReferences != null)
+ {
+ foreach (var reference in packageReferences)
+ {
+ var packageReference = new XElement("PackageReference");
+ packageReference.SetAttributeValue("Include", reference.Key);
+ packageReference.SetAttributeValue("Version", reference.Value);
+
+ referencesElement.Add(packageReference);
}
}
@@ -66,11 +80,12 @@ public void AddCsprojFile(Dictionary? properties = null)
enable
{SarifFileName},version=2.1
- {element}
+ {propertyElement}
+ {referencesElement}
""";
@@ -92,9 +107,52 @@ public async Task BuildAndGetOutput(string[]? buildArguments = null)
var bytes = await File.ReadAllBytesAsync(this._directory.GetPath(SarifFileName));
var sarif = JsonSerializer.Deserialize(bytes) ?? throw new InvalidOperationException("The sarif file is invalid");
+
+ this.AppendAdditionalResult(sarif);
+
this._testOutputHelper.WriteLine("Sarif result:\n" + string.Join("\n", sarif.AllResults().Select(r => r.ToString())));
return sarif;
}
public void Dispose() => this._directory.Dispose();
+
+ private void AppendAdditionalResult(SarifFile sarifFile)
+ {
+ if (this._testOutputHelper is not TestOutputHelper testOutputHelper || sarifFile.Runs == null)
+ {
+ return;
+ }
+
+ var outputLines = testOutputHelper.Output.Split(Environment.NewLine);
+ var customRunResults = new List();
+
+ // These rules (for nuget package vulnerability) are not parsed in the sarif file automatically
+ // See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1901-nu1904
+ var scannedRules = new List { "NU1901", "NU1902", "NU1903", "NU1904" }
+ .ToDictionary(x => x, x => $"{x}:");
+
+ foreach (var outputLine in outputLines)
+ {
+ foreach (var scannedRule in scannedRules)
+ {
+ var scannedRuleIndex = outputLine.IndexOf(scannedRule.Value, StringComparison.OrdinalIgnoreCase);
+ if (scannedRuleIndex == -1)
+ {
+ continue;
+ }
+
+ var previousColonIndex = outputLine.LastIndexOf(':', scannedRuleIndex);
+ var ruleLevel = outputLine.Substring(previousColonIndex + 1, scannedRuleIndex - previousColonIndex - 1).Trim();
+
+ var message = outputLine[(scannedRuleIndex + scannedRule.Value.Length + 1)..];
+ customRunResults.Add(new SarifFileRunResult { Level = ruleLevel, RuleId = scannedRule.Key, Message = new SarifFileRunResultMessage { Text = message } });
+ }
+ }
+
+ var distinctRules = customRunResults
+ .DistinctBy(x => new { x.RuleId, x.Level })
+ .ToArray();
+
+ sarifFile.Runs = sarifFile.Runs.Append(new SarifFileRun { Results = distinctRules }).ToArray();
+ }
}