Skip to content

Commit b830cf9

Browse files
authored
Fixes Sarif Output Format (#550)
1 parent bd25052 commit b830cf9

File tree

4 files changed

+98
-60
lines changed

4 files changed

+98
-60
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,4 @@ output.html
270270
result.json
271271
AppInspector.Tests/logs/
272272
RulesPacker/appinspector.log.txt
273+
AppInspector.CLI/out.sarif

AppInspector.CLI/AppInspector.CLI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767

6868
<ItemGroup>
6969
<PackageReference Include="DotLiquid" Version="2.2.692" />
70-
<PackageReference Include="Sarif.Sdk" Version="4.1.0" />
70+
<PackageReference Include="Sarif.Sdk" Version="4.2.0" />
7171
<PackageReference Include="Serilog" Version="2.12.0" />
7272
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
7373
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />

AppInspector.CLI/Writers/AnalyzeSarifWriter.cs

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,45 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
5555

5656
if (result is AnalyzeResult analyzeResult)
5757
{
58-
SarifLog log = new();
59-
var sarifVersion = SarifVersion.Current;
60-
log.SchemaUri = sarifVersion.ConvertToSchemaUri();
61-
log.Version = sarifVersion;
58+
SarifLog log = new()
59+
{
60+
Version = SarifVersion.Current
61+
};
62+
6263
log.Runs = new List<Run>();
63-
var run = new Run();
64+
// Convert Base Path to Forward Slashes to be a valid URI
65+
66+
var run = new Run()
67+
{
68+
Tool = new Tool
69+
{
70+
Driver = new ToolComponent
71+
{
72+
Name = "Application Inspector",
73+
InformationUri = new Uri("https://github.com/microsoft/ApplicationInspector/"),
74+
Organization = "Microsoft",
75+
Version = Helpers.GetVersionString()
76+
}
77+
}
78+
};
79+
if (!string.IsNullOrEmpty(basePath))
80+
{
81+
if (Path.DirectorySeparatorChar == '\\')
82+
{
83+
basePath = basePath.Replace("\\","/");
84+
if (!basePath.EndsWith("/"))
85+
{
86+
basePath = $"{basePath}/";
87+
}
88+
89+
}
90+
91+
run.OriginalUriBaseIds = new Dictionary<string, ArtifactLocation>()
92+
{
93+
94+
{ "ROOT", new ArtifactLocation() { Uri = new Uri($"file://{basePath}") } }
95+
};
96+
}
6497

6598
if (Uri.TryCreate(cliAnalyzeCmdOptions.RepositoryUri, UriKind.RelativeOrAbsolute, out var uri))
6699
{
@@ -69,7 +102,11 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
69102
new()
70103
{
71104
RepositoryUri = uri,
72-
RevisionId = cliAnalyzeCmdOptions.CommitHash
105+
RevisionId = cliAnalyzeCmdOptions.CommitHash,
106+
MappedTo = new ArtifactLocation()
107+
{
108+
UriBaseId = "ROOT"
109+
}
73110
}
74111
};
75112
}
@@ -81,22 +118,17 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
81118
{
82119
RepositoryUri = analyzeResult.Metadata.RepositoryUri,
83120
RevisionId = analyzeResult.Metadata.CommitHash ?? string.Empty,
84-
Branch = analyzeResult.Metadata.Branch ?? string.Empty
121+
Branch = analyzeResult.Metadata.Branch ?? string.Empty,
122+
MappedTo = new ArtifactLocation()
123+
{
124+
UriBaseId = "ROOT"
125+
}
85126
}
86127
};
87128
}
88129

89130
var artifacts = new List<Artifact>();
90-
run.Tool = new Tool
91-
{
92-
Driver = new ToolComponent
93-
{
94-
Name = "Application Inspector",
95-
InformationUri = new Uri("https://github.com/microsoft/ApplicationInspector/"),
96-
Organization = "Microsoft",
97-
Version = Helpers.GetVersionString()
98-
}
99-
};
131+
100132
var reportingDescriptors = new List<ReportingDescriptor>();
101133
run.Results = new List<CodeAnalysis.Sarif.Result>();
102134
foreach (var match in analyzeResult.Metadata.Matches)
@@ -114,14 +146,15 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
114146
Name = match.Rule.Name,
115147
DefaultConfiguration = new ReportingConfiguration
116148
{
117-
Level = GetSarifFailureLevel(match.Rule.Severity)
149+
Level = FailureLevel.Note
118150
}
119151
};
120152
reportingDescriptor.Tags.AddRange(match.Rule.Tags);
153+
reportingDescriptor.SetProperty("AppInspector:Severity", match.Rule.Severity.ToString());
121154
reportingDescriptors.Add(reportingDescriptor);
122155
}
123156

124-
sarifResult.Level = GetSarifFailureLevel(match.Rule.Severity);
157+
sarifResult.Level = FailureLevel.Note;
125158
sarifResult.RuleId = match.Rule.Id;
126159
sarifResult.Tags.AddRange(match.Rule.Tags);
127160
sarifResult.Message = new Message
@@ -134,7 +167,7 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
134167
var fileName = match.FileName;
135168
if (basePath is not null)
136169
{
137-
fileName = Path.GetRelativePath(basePath, fileName);
170+
fileName = Path.GetRelativePath(basePath, fileName).Replace("\\","/");
138171
}
139172

140173
if (Uri.TryCreate(fileName, UriKind.RelativeOrAbsolute, out var outUri))
@@ -150,6 +183,10 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
150183
Uri = outUri
151184
}
152185
};
186+
if (basePath != null)
187+
{
188+
artifact.Location.UriBaseId = "ROOT";
189+
}
153190
artifactIndex = artifact.Location.Index;
154191
artifact.Tags.AddRange(match.Rule.Tags);
155192
if (match.LanguageInfo is { } languageInfo)
@@ -164,30 +201,37 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
164201
artifacts[artifactIndex].Tags.AddRange(match.Rule.Tags);
165202
}
166203

167-
sarifResult.Locations = new List<Location>
204+
Location location = new()
168205
{
169-
new()
206+
PhysicalLocation = new PhysicalLocation
170207
{
171-
PhysicalLocation = new PhysicalLocation
208+
ArtifactLocation = new ArtifactLocation
172209
{
173-
ArtifactLocation = new ArtifactLocation
174-
{
175-
Index = artifactIndex
176-
},
177-
Region = new Region
210+
Index = artifactIndex,
211+
Uri = outUri
212+
},
213+
Region = new Region
214+
{
215+
StartLine = match.StartLocationLine,
216+
StartColumn = match.StartLocationColumn,
217+
EndLine = match.EndLocationLine,
218+
EndColumn = match.EndLocationColumn,
219+
Snippet = new ArtifactContent
178220
{
179-
StartLine = match.StartLocationLine,
180-
StartColumn = match.StartLocationColumn,
181-
EndLine = match.EndLocationLine,
182-
EndColumn = match.EndLocationColumn,
183-
Snippet = new ArtifactContent
184-
{
185-
Text = match.Sample
186-
}
221+
Text = match.Sample
187222
}
188223
}
189224
}
190225
};
226+
if (basePath != null)
227+
{
228+
location.PhysicalLocation.ArtifactLocation.UriBaseId = "ROOT";
229+
}
230+
sarifResult.SetProperty("AppInspector:Severity", match.Rule.Severity.ToString());
231+
sarifResult.Locations = new List<Location>
232+
{
233+
location
234+
};
191235
}
192236
}
193237
}
@@ -200,7 +244,7 @@ public override void WriteResults(Result result, CLICommandOptions commandOption
200244
log.Runs.Add(run);
201245
try
202246
{
203-
JsonSerializer.Serialize(StreamWriter.BaseStream, log);
247+
log.Save(StreamWriter.BaseStream);
204248
}
205249
catch (Exception e)
206250
{

AppInspector.RulesEngine/RuleProcessor.cs

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ public List<MatchRecord> AnalyzeFile(TextContainer textContainer, FileEntry file
181181
EndLocationColumn = endLocation.Column,
182182
MatchingPattern = oatRule.AppInspectorRule.Patterns[patternIndex],
183183
Excerpt = numLinesContext > 0
184-
? ExtractExcerpt(textContainer, startLocation.Line, numLinesContext)
184+
? ExtractExcerpt(textContainer, startLocation, endLocation, boundary, numLinesContext)
185185
: string.Empty,
186186
Sample = numLinesContext > -1
187187
? ExtractTextSample(textContainer.FullContent, boundary.Index, boundary.Length)
@@ -399,7 +399,7 @@ List<MatchRecord> ProcessBoundary(ClauseCapture cap)
399399
: startLocation.Line + 1, //match is on last line
400400
MatchingPattern = oatRule.AppInspectorRule.Patterns[patternIndex],
401401
Excerpt = numLinesContext > 0
402-
? ExtractExcerpt(textContainer, startLocation.Line, numLinesContext)
402+
? ExtractExcerpt(textContainer, startLocation, endLocation, boundary, numLinesContext)
403403
: string.Empty,
404404
Sample = numLinesContext > -1
405405
? ExtractTextSample(textContainer.FullContent, boundary.Index, boundary.Length)
@@ -554,37 +554,30 @@ private string ExtractTextSample(string fileText, int index, int length)
554554
/// from the template
555555
/// </summary>
556556
/// <returns></returns>
557-
private static string ExtractExcerpt(TextContainer text, int startLineNumber, int context = 3)
557+
private static string ExtractExcerpt(TextContainer text, Location start, Location end, Boundary matchBoundary, int context = 3)
558558
{
559559
if (context == 0)
560560
{
561561
return string.Empty;
562562
}
563563

564-
if (startLineNumber < 0)
565-
{
566-
startLineNumber = 0;
567-
}
568-
569-
if (startLineNumber >= text.LineEnds.Count)
570-
{
571-
startLineNumber = text.LineEnds.Count - 1;
572-
}
573-
564+
int startLineNumber =
565+
start.Line < 0 ? 0 : start.Line > text.LineEnds.Count ? text.LineEnds.Count - 1 : start.Line;
566+
int endLineNUmber =
567+
end.Line < 0 ? 0 : end.Line > text.LineEnds.Count ? text.LineEnds.Count - 1 : end.Line;
568+
// First we try to include the number of lines of context requested
574569
var excerptStartLine = Math.Max(0, startLineNumber - context);
575-
var excerptEndLine = Math.Min(text.LineEnds.Count - 1, startLineNumber + context);
570+
var excerptEndLine = Math.Min(text.LineEnds.Count - 1, endLineNUmber + context);
576571
var startIndex = text.LineStarts[excerptStartLine];
577572
var endIndex = text.LineEnds[excerptEndLine] + 1;
573+
// Maximum number of characters to capture on each side
578574
var maxCharacterContext = context * 100;
579-
// Only gather 100*lines context characters to avoid gathering super long lines
580-
if (text.LineStarts[startLineNumber] - startIndex > maxCharacterContext)
581-
{
582-
startIndex = Math.Max(0, startIndex - maxCharacterContext);
583-
}
584-
585-
if (endIndex - text.LineEnds[startLineNumber] > maxCharacterContext)
575+
// If the number of characters captured for context is larger than 100*number of lines,
576+
// instead gather an appropriate number of characters
577+
if (endIndex - startIndex - matchBoundary.Length > maxCharacterContext * 2)
586578
{
587-
endIndex = Math.Min(text.FullContent.Length - 1, endIndex + maxCharacterContext);
579+
startIndex = Math.Max(0, matchBoundary.Index - maxCharacterContext);
580+
endIndex = Math.Max(0, matchBoundary.Index + matchBoundary.Length + maxCharacterContext);
588581
}
589582

590583
return text.FullContent[startIndex..endIndex];

0 commit comments

Comments
 (0)