Skip to content

Commit bb81662

Browse files
authored
Fix LanguageRuleIgnoreMap and Add Positive Globs (#665)
* Fix incorrect inversion in LanguageRuleMap codepath * Improve option operation tests * Add Include Globs option. When specified, only files that match one of the include globs and that don't match one of the exclude globs are scanned. * New test cases for glob options. * Update changelog.
1 parent 9fa5269 commit bb81662

File tree

4 files changed

+228
-9
lines changed

4 files changed

+228
-9
lines changed

Changelog.md

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [1.0.50] - 2024-12-05
8+
## Fix
9+
Fixes #664 handling of options from IgnoreRuleMap when using OptionsJson
10+
11+
## New Functionality
12+
Adds `include-globs` argument to require all scanned files match a specific glob pattern #663.
13+
714
## [1.0.49] - 2024-12-03
815
## Rules
916
Fixed false positives and false negatives in outdated/banned SSL/TLS protocols. #649

DevSkim-DotNet/Microsoft.DevSkim.CLI/Commands/AnalyzeCommand.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public int Run()
6565

6666
IEnumerable<FileEntry> fileListing;
6767
Extractor extractor = new Extractor();
68-
ExtractorOptions extractorOpts = new ExtractorOptions() { ExtractSelfOnFail = false, DenyFilters = _opts.Globs };
68+
ExtractorOptions extractorOpts = new ExtractorOptions() { ExtractSelfOnFail = false, AllowFilters = _opts.AllowGlobs, DenyFilters = _opts.Globs };
6969
// Analysing a single file
7070
if (!Directory.Exists(fullPath))
7171
{
@@ -424,7 +424,7 @@ void parseFileEntry(FileEntry fileEntry)
424424
if (serializedAnalyzeCommandOptions.LanguageRuleIgnoreMap.TryGetValue(languageInfo.Name,
425425
out List<string>? maybeRulesToIgnore) && maybeRulesToIgnore is { } rulesToIgnore)
426426
{
427-
var numRemoved = issues.RemoveAll(x => !rulesToIgnore.Contains(x.Rule.Id));
427+
var numRemoved = issues.RemoveAll(x => rulesToIgnore.Contains(x.Rule.Id));
428428
_logger.LogDebug($"Removed {numRemoved} results because of language rule filters.");
429429
}
430430
}

DevSkim-DotNet/Microsoft.DevSkim.CLI/Options/BaseAnalyzeCommandOptions.cs

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public record BaseAnalyzeCommandOptions : LogOptions
4545
[Option('g', "ignore-globs", HelpText = "Comma-separated Globs for files to skip analyzing", Separator = ',', Default = new[] { "**/.git/**", "**/bin/**" })]
4646
public IEnumerable<string> Globs { get; set; } = new[] { "**/.git/**", "**/bin/**" };
4747

48+
[Option("include-globs", HelpText = "If set, files must match one of these globs to be analyzed", Separator = ',', Default = new string[]{})]
49+
public IEnumerable<string> AllowGlobs { get; set; } = new string[]{};
50+
4851
[Option('d', "disable-supression", HelpText = "Disable comment suppressions", Default = false)]
4952
public bool DisableSuppression { get; set; }
5053

DevSkim-DotNet/Microsoft.DevSkim.Tests/OptionsTests.cs

+216-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,214 @@ namespace Microsoft.DevSkim.Tests;
88
[TestClass]
99
public class OptionsTests
1010
{
11+
[TestMethod]
12+
public void TestExcludeGlobs()
13+
{
14+
var serializedOptsExcludeGlobs = new SerializedAnalyzeCommandOptions()
15+
{
16+
Severities = new[] { Severity.Critical | Severity.Important },
17+
ExitCodeIsNumIssues = true,
18+
Globs = new List<string>() {"*.js"}
19+
};
20+
var testContent = "Hello World";
21+
var testRule =
22+
@"[
23+
{
24+
""name"": ""Weak/Broken Hash Algorithm"",
25+
""id"": ""JsonOptionParseTest"",
26+
""description"": ""A test that finds hello"",
27+
""tags"": [
28+
""Tests.JsonOptionsTest""
29+
],
30+
""severity"": ""critical"",
31+
""patterns"": [
32+
{
33+
""pattern"": ""Hello"",
34+
""type"": ""regex"",
35+
""scopes"": [
36+
""code""
37+
]
38+
}
39+
]
40+
}]";
41+
var rulesPath = PathHelper.GetRandomTempFile("json");
42+
var serializedJsonPath = PathHelper.GetRandomTempFile("json");
43+
var csharpTestPath = PathHelper.GetRandomTempFile("cs");
44+
var jsTestPath = PathHelper.GetRandomTempFile("js");
45+
{
46+
using var serializedJsonStream = File.Create(serializedJsonPath);
47+
JsonSerializer.Serialize(serializedJsonStream, serializedOptsExcludeGlobs, new JsonSerializerOptions() { });
48+
using var csharpStream = File.Create(csharpTestPath);
49+
JsonSerializer.Serialize(csharpStream, testContent);
50+
using var jsStream = File.Create(jsTestPath);
51+
JsonSerializer.Serialize(jsStream, testContent);
52+
File.WriteAllText(rulesPath, testRule);
53+
}
54+
55+
// Create an AnalyzeCommandOptions object referencing our serialized options
56+
var analyzeOpts = new AnalyzeCommandOptions()
57+
{
58+
Path = csharpTestPath,
59+
Rules = new[] { rulesPath },
60+
PathToOptionsJson = serializedJsonPath
61+
};
62+
63+
var analyzerWithSerialized = new AnalyzeCommand(analyzeOpts);
64+
// We set exit code is num issues so this should be 1, as csharp files aren't ignored
65+
Assert.AreEqual(1, analyzerWithSerialized.Run());
66+
67+
// Create an AnalyzeCommandOptions object referencing our serialized options
68+
analyzeOpts = new AnalyzeCommandOptions()
69+
{
70+
Path = jsTestPath,
71+
Rules = new[] { rulesPath },
72+
PathToOptionsJson = serializedJsonPath
73+
};
74+
75+
analyzerWithSerialized = new AnalyzeCommand(analyzeOpts);
76+
// We set exit code is num issues so this should be 0, as js files are ignored
77+
Assert.AreEqual(0, analyzerWithSerialized.Run());
78+
}
79+
80+
[TestMethod]
81+
public void TestIncludeGlobs()
82+
{
83+
var serializedOptsExcludeGlobs = new SerializedAnalyzeCommandOptions()
84+
{
85+
Severities = new[] { Severity.Critical | Severity.Important },
86+
ExitCodeIsNumIssues = true,
87+
AllowGlobs = new List<string>() {"*.js"}
88+
};
89+
var testContent = "Hello World";
90+
var testRule =
91+
@"[
92+
{
93+
""name"": ""Weak/Broken Hash Algorithm"",
94+
""id"": ""JsonOptionParseTest"",
95+
""description"": ""A test that finds hello"",
96+
""tags"": [
97+
""Tests.JsonOptionsTest""
98+
],
99+
""severity"": ""critical"",
100+
""patterns"": [
101+
{
102+
""pattern"": ""Hello"",
103+
""type"": ""regex"",
104+
""scopes"": [
105+
""code""
106+
]
107+
}
108+
]
109+
}]";
110+
var rulesPath = PathHelper.GetRandomTempFile("json");
111+
var serializedJsonPath = PathHelper.GetRandomTempFile("json");
112+
var csharpTestPath = PathHelper.GetRandomTempFile("cs");
113+
var jsTestPath = PathHelper.GetRandomTempFile("js");
114+
{
115+
using var serializedJsonStream = File.Create(serializedJsonPath);
116+
JsonSerializer.Serialize(serializedJsonStream, serializedOptsExcludeGlobs, new JsonSerializerOptions() { });
117+
using var csharpStream = File.Create(csharpTestPath);
118+
JsonSerializer.Serialize(csharpStream, testContent);
119+
using var jsStream = File.Create(jsTestPath);
120+
JsonSerializer.Serialize(jsStream, testContent);
121+
File.WriteAllText(rulesPath, testRule);
122+
}
123+
124+
// Create an AnalyzeCommandOptions object referencing our serialized options
125+
var analyzeOpts = new AnalyzeCommandOptions()
126+
{
127+
Path = csharpTestPath,
128+
Rules = new[] { rulesPath },
129+
PathToOptionsJson = serializedJsonPath
130+
};
131+
132+
var analyzerWithSerialized = new AnalyzeCommand(analyzeOpts);
133+
// We set exit code is num issues so this should be 0, as csharp are implicitly ignored
134+
Assert.AreEqual(0, analyzerWithSerialized.Run());
135+
136+
// Create an AnalyzeCommandOptions object referencing our serialized options
137+
analyzeOpts = new AnalyzeCommandOptions()
138+
{
139+
Path = jsTestPath,
140+
Rules = new[] { rulesPath },
141+
PathToOptionsJson = serializedJsonPath
142+
};
143+
144+
analyzerWithSerialized = new AnalyzeCommand(analyzeOpts);
145+
// We set exit code is num issues so this should be 1, as js files are included
146+
Assert.AreEqual(1, analyzerWithSerialized.Run());
147+
}
148+
149+
[TestMethod]
150+
public void TestIncludeAndExcludeGlobs()
151+
{
152+
var serializedOptsExcludeGlobs = new SerializedAnalyzeCommandOptions()
153+
{
154+
Severities = new[] { Severity.Critical | Severity.Important },
155+
ExitCodeIsNumIssues = true,
156+
AllowGlobs = new List<string>() {"*.js"},
157+
Globs = new List<string>() {"*hello.js"}
158+
};
159+
var testContent = "Hello World";
160+
var testRule =
161+
@"[
162+
{
163+
""name"": ""Weak/Broken Hash Algorithm"",
164+
""id"": ""JsonOptionParseTest"",
165+
""description"": ""A test that finds hello"",
166+
""tags"": [
167+
""Tests.JsonOptionsTest""
168+
],
169+
""severity"": ""critical"",
170+
""patterns"": [
171+
{
172+
""pattern"": ""Hello"",
173+
""type"": ""regex"",
174+
""scopes"": [
175+
""code""
176+
]
177+
}
178+
]
179+
}]";
180+
var rulesPath = PathHelper.GetRandomTempFile("json");
181+
var serializedJsonPath = PathHelper.GetRandomTempFile("json");
182+
var helloJsTestPath = PathHelper.GetRandomTempFile("hello.js");
183+
var jsTestPath = PathHelper.GetRandomTempFile("js");
184+
{
185+
using var serializedJsonStream = File.Create(serializedJsonPath);
186+
JsonSerializer.Serialize(serializedJsonStream, serializedOptsExcludeGlobs, new JsonSerializerOptions() { });
187+
using var helloJsStream = File.Create(helloJsTestPath);
188+
JsonSerializer.Serialize(helloJsStream, testContent);
189+
using var jsStream = File.Create(jsTestPath);
190+
JsonSerializer.Serialize(jsStream, testContent);
191+
File.WriteAllText(rulesPath, testRule);
192+
}
193+
194+
// Create an AnalyzeCommandOptions object referencing our serialized options
195+
var analyzeOpts = new AnalyzeCommandOptions()
196+
{
197+
Path = helloJsTestPath,
198+
Rules = new[] { rulesPath },
199+
PathToOptionsJson = serializedJsonPath
200+
};
201+
202+
var analyzerWithSerialized = new AnalyzeCommand(analyzeOpts);
203+
// We set exit code is num issues so this should be 0, as hello.js files are ignored
204+
Assert.AreEqual(0, analyzerWithSerialized.Run());
205+
206+
// Create an AnalyzeCommandOptions object referencing our serialized options
207+
analyzeOpts = new AnalyzeCommandOptions()
208+
{
209+
Path = jsTestPath,
210+
Rules = new[] { rulesPath },
211+
PathToOptionsJson = serializedJsonPath
212+
};
213+
214+
analyzerWithSerialized = new AnalyzeCommand(analyzeOpts);
215+
// We set exit code is num issues so this should be 1, as regular js files are included
216+
Assert.AreEqual(1, analyzerWithSerialized.Run());
217+
}
218+
11219
[TestMethod]
12220
public void TestParsingJsonOptions()
13221
{
@@ -29,7 +237,8 @@ public void TestParsingJsonOptions()
29237
Globs = new List<string>() {"*.js"}
30238
};
31239
// Serialize it to a file
32-
var testContent = "Hello World";
240+
// Include world twice so we can disinguish between the two rules
241+
var testContent = "Hello World World";
33242
var testRule =
34243
@"[
35244
{
@@ -95,8 +304,8 @@ public void TestParsingJsonOptions()
95304
};
96305

97306
var analyzerWithSerialized = new AnalyzeCommand(analyzeOpts);
98-
// We set exit code is num issues so this should be 1, from the 1 rule that isn't ignored
99-
Assert.AreEqual(1, analyzerWithSerialized.Run());
307+
// We set exit code is num issues so this should be 2, from the two matchs for the rule that isn't ignored
308+
Assert.AreEqual(2, analyzerWithSerialized.Run());
100309
// Create an AnalyzeCommandOptions object that references the path to the file which ignores a specific rule
101310
analyzeOpts = new AnalyzeCommandOptions()
102311
{
@@ -117,8 +326,8 @@ public void TestParsingJsonOptions()
117326
PathToOptionsJson = serializedJsonPath
118327
};
119328
analyzerWithSerialized = new AnalyzeCommand(analyzeOpts);
120-
// This should be 2, because 2 rules aren't ignored
121-
Assert.AreEqual(2, analyzerWithSerialized.Run());
329+
// This should be 3, because no rules are ignored
330+
Assert.AreEqual(3, analyzerWithSerialized.Run());
122331
// Try the js which it should find both
123332
analyzeOpts = new AnalyzeCommandOptions()
124333
{
@@ -140,8 +349,8 @@ public void TestParsingJsonOptions()
140349
PathToOptionsJson = serializedJsonPath2
141350
};
142351
analyzerWithSerialized = new AnalyzeCommand(analyzeOpts);
143-
// This should be 2, because the globs dont exclude cs files
144-
Assert.AreEqual(2, analyzerWithSerialized.Run());
352+
// This should be 3, because the globs dont exclude cs files
353+
Assert.AreEqual(3, analyzerWithSerialized.Run());
145354
// set of options to test enumerable parsing
146355
analyzeOpts = new AnalyzeCommandOptions()
147356
{

0 commit comments

Comments
 (0)