Skip to content

Commit 95e7eac

Browse files
authored
Add rule verifier check to require a description. (#544)
* Add rule verifier check to require a description. GitHub Sarif Upload Action requires the Help.Text property of each Rule object in the sarif to be populated. We populate this in DevSkim with either the Recommendation, if present, or the Description, if not present. However, previously it was possible to have neither. This adds a verification step to require a description to ensure there is a value to use for sarif export. * Adds test to fail on rules without description * Description is now a required field in each rule, SemVer relevant * Description field may not be null
1 parent 5cb7095 commit 95e7eac

File tree

5 files changed

+56
-9
lines changed

5 files changed

+56
-9
lines changed

AppInspector.CLI/Writers/AnalyzeSarifWriter.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
using Location = Microsoft.CodeAnalysis.Sarif.Location;
1616
using Result = Microsoft.ApplicationInspector.Commands.Result;
1717

18-
namespace Microsoft.ApplicationInspector.CLI;
18+
namespace Microsoft.ApplicationInspector.CLI.Writers;
1919

2020
internal static class AnalyzeSarifWriterExtensions
2121
{
@@ -49,10 +49,9 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
4949
throw new ArgumentNullException(nameof(StreamWriter));
5050
}
5151

52-
string? basePath = null;
53-
if (commandOptions is CLIAnalyzeCmdOptions cLIAnalyzeCmdOptions)
52+
if (commandOptions is CLIAnalyzeCmdOptions cliAnalyzeCmdOptions)
5453
{
55-
basePath = cLIAnalyzeCmdOptions.BasePath;
54+
string? basePath = cliAnalyzeCmdOptions.BasePath;
5655

5756
if (result is AnalyzeResult analyzeResult)
5857
{
@@ -63,14 +62,14 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
6362
log.Runs = new List<Run>();
6463
var run = new Run();
6564

66-
if (Uri.TryCreate(cLIAnalyzeCmdOptions.RepositoryUri, UriKind.RelativeOrAbsolute, out var uri))
65+
if (Uri.TryCreate(cliAnalyzeCmdOptions.RepositoryUri, UriKind.RelativeOrAbsolute, out var uri))
6766
{
6867
run.VersionControlProvenance = new List<VersionControlDetails>
6968
{
7069
new()
7170
{
7271
RepositoryUri = uri,
73-
RevisionId = cLIAnalyzeCmdOptions.CommitHash
72+
RevisionId = cliAnalyzeCmdOptions.CommitHash
7473
}
7574
};
7675
}
@@ -106,7 +105,7 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
106105

107106
if (match.Rule is not null)
108107
{
109-
if (!reportingDescriptors.Any(r => r.Id == match.Rule.Id))
108+
if (reportingDescriptors.All(r => r.Id != match.Rule.Id))
110109
{
111110
ReportingDescriptor reportingDescriptor = new()
112111
{

AppInspector.RulesEngine/Rule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public class Rule
6060
/// Human readable description for the rule
6161
/// </summary>
6262
[JsonPropertyName("description")]
63-
public string? Description { get; set; } = "";
63+
public string Description { get; set; } = "";
6464

6565
/// <summary>
6666
/// Languages that the rule does not apply to

AppInspector.RulesEngine/RulesVerifier.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ public RuleStatus CheckIntegrity(ConvertedOatRule convertedOatRule)
168168

169169
// Check that regexes for filenames are valid
170170
foreach (var pattern in (IList<string>?)rule.FileRegexes ?? Array.Empty<string>())
171+
{
171172
try
172173
{
173174
_ = new Regex(pattern, RegexOptions.Compiled);
@@ -179,6 +180,7 @@ public RuleStatus CheckIntegrity(ConvertedOatRule convertedOatRule)
179180
errors.Add(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_REGEX_FAIL, rule.Id ?? "", pattern ?? "",
180181
e.Message));
181182
}
183+
}
182184

183185
//valid search pattern
184186
foreach (var searchPattern in rule.Patterns ?? Array.Empty<SearchPattern>())
@@ -365,6 +367,13 @@ public RuleStatus CheckIntegrity(ConvertedOatRule convertedOatRule)
365367
errors.Add($"Rule must specify MustNotMatch when `RequireMustNotMatch` is set. {rule.Id}");
366368
}
367369
}
370+
371+
// Require Description so the Sarif is valid for GitHub sarif upload action
372+
if (string.IsNullOrEmpty(rule.Description))
373+
{
374+
_logger?.LogError("Rule must contain a Description. {0}", rule.Id);
375+
errors.Add($"Rule must contain a Description. {rule.Id}");
376+
}
368377

369378
return new RuleStatus
370379
{

AppInspector.Tests/Commands/TestVerifyRulesCmd.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Diagnostics.CodeAnalysis;
33
using System.IO;
4+
using System.Linq;
45
using CommandLine;
56
using Microsoft.ApplicationInspector.Commands;
67
using Microsoft.ApplicationInspector.Common;
@@ -40,6 +41,27 @@ public class TestVerifyRulesCmd
4041
}
4142
]
4243
}
44+
]";
45+
46+
private readonly string _ruleWithoutDescription = @"[
47+
{
48+
""name"": ""Rule: No Description"",
49+
""id"": ""AI_TEST_NO_DESCRIPTION"",
50+
""tags"": [
51+
""Test.Tags.NoDescription""
52+
],
53+
""severity"": ""Important"",
54+
""patterns"": [
55+
{
56+
""confidence"": ""Medium"",
57+
""modifiers"": [
58+
""i""
59+
],
60+
""pattern"": ""windows"",
61+
""type"": ""String"",
62+
}
63+
]
64+
}
4365
]";
4466

4567
// Rules are a List<Rule> so they must be contained in []
@@ -420,6 +442,23 @@ public void NoDefaultNoCustomRules()
420442
{
421443
Assert.ThrowsException<OpException>(() => new VerifyRulesCommand(new VerifyRulesOptions()));
422444
}
445+
446+
[TestMethod]
447+
public void NoDescription()
448+
{
449+
var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
450+
File.WriteAllText(path, _ruleWithoutDescription);
451+
VerifyRulesOptions options = new()
452+
{
453+
CustomRulesPath = path
454+
};
455+
456+
VerifyRulesCommand command = new(options, _factory);
457+
var result = command.GetResult();
458+
File.Delete(path);
459+
Assert.AreEqual(1, result.Unverified.Count());
460+
Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode);
461+
}
423462

424463
[TestMethod]
425464
public void CustomRules()

version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
3-
"version": "1.8",
3+
"version": "1.9",
44
"publicReleaseRefSpec": [
55
"^refs/heads/main$",
66
"^refs/heads/v\\d+(?:\\.\\d+)?$"

0 commit comments

Comments
 (0)