From b830cf902b1d049c7c769d95121c0c4380dd5088 Mon Sep 17 00:00:00 2001 From: Gabe Stocco <98900+gfs@users.noreply.github.com> Date: Wed, 21 Jun 2023 22:08:07 -0700 Subject: [PATCH] Fixes Sarif Output Format (#550) --- .gitignore | 1 + AppInspector.CLI/AppInspector.CLI.csproj | 2 +- .../Writers/AnalyzeSarifWriter.cs | 118 ++++++++++++------ AppInspector.RulesEngine/RuleProcessor.cs | 37 +++--- 4 files changed, 98 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index ff9d32a..26a0f65 100644 --- a/.gitignore +++ b/.gitignore @@ -270,3 +270,4 @@ output.html result.json AppInspector.Tests/logs/ RulesPacker/appinspector.log.txt +AppInspector.CLI/out.sarif diff --git a/AppInspector.CLI/AppInspector.CLI.csproj b/AppInspector.CLI/AppInspector.CLI.csproj index 90e8802..0f26fa0 100644 --- a/AppInspector.CLI/AppInspector.CLI.csproj +++ b/AppInspector.CLI/AppInspector.CLI.csproj @@ -67,7 +67,7 @@ - + diff --git a/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs b/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs index 6123b0d..6d24003 100644 --- a/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs +++ b/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs @@ -55,12 +55,45 @@ public class AnalyzeSarifWriter : CommandResultsWriter if (result is AnalyzeResult analyzeResult) { - SarifLog log = new(); - var sarifVersion = SarifVersion.Current; - log.SchemaUri = sarifVersion.ConvertToSchemaUri(); - log.Version = sarifVersion; + SarifLog log = new() + { + Version = SarifVersion.Current + }; + log.Runs = new List(); - var run = new Run(); + // Convert Base Path to Forward Slashes to be a valid URI + + var run = new Run() + { + Tool = new Tool + { + Driver = new ToolComponent + { + Name = "Application Inspector", + InformationUri = new Uri("https://github.com/microsoft/ApplicationInspector/"), + Organization = "Microsoft", + Version = Helpers.GetVersionString() + } + } + }; + if (!string.IsNullOrEmpty(basePath)) + { + if (Path.DirectorySeparatorChar == '\\') + { + basePath = basePath.Replace("\\","/"); + if (!basePath.EndsWith("/")) + { + basePath = $"{basePath}/"; + } + + } + + run.OriginalUriBaseIds = new Dictionary() + { + + { "ROOT", new ArtifactLocation() { Uri = new Uri($"file://{basePath}") } } + }; + } if (Uri.TryCreate(cliAnalyzeCmdOptions.RepositoryUri, UriKind.RelativeOrAbsolute, out var uri)) { @@ -69,7 +102,11 @@ public class AnalyzeSarifWriter : CommandResultsWriter new() { RepositoryUri = uri, - RevisionId = cliAnalyzeCmdOptions.CommitHash + RevisionId = cliAnalyzeCmdOptions.CommitHash, + MappedTo = new ArtifactLocation() + { + UriBaseId = "ROOT" + } } }; } @@ -81,22 +118,17 @@ public class AnalyzeSarifWriter : CommandResultsWriter { RepositoryUri = analyzeResult.Metadata.RepositoryUri, RevisionId = analyzeResult.Metadata.CommitHash ?? string.Empty, - Branch = analyzeResult.Metadata.Branch ?? string.Empty + Branch = analyzeResult.Metadata.Branch ?? string.Empty, + MappedTo = new ArtifactLocation() + { + UriBaseId = "ROOT" + } } }; } var artifacts = new List(); - run.Tool = new Tool - { - Driver = new ToolComponent - { - Name = "Application Inspector", - InformationUri = new Uri("https://github.com/microsoft/ApplicationInspector/"), - Organization = "Microsoft", - Version = Helpers.GetVersionString() - } - }; + var reportingDescriptors = new List(); run.Results = new List(); foreach (var match in analyzeResult.Metadata.Matches) @@ -114,14 +146,15 @@ public class AnalyzeSarifWriter : CommandResultsWriter Name = match.Rule.Name, DefaultConfiguration = new ReportingConfiguration { - Level = GetSarifFailureLevel(match.Rule.Severity) + Level = FailureLevel.Note } }; reportingDescriptor.Tags.AddRange(match.Rule.Tags); + reportingDescriptor.SetProperty("AppInspector:Severity", match.Rule.Severity.ToString()); reportingDescriptors.Add(reportingDescriptor); } - sarifResult.Level = GetSarifFailureLevel(match.Rule.Severity); + sarifResult.Level = FailureLevel.Note; sarifResult.RuleId = match.Rule.Id; sarifResult.Tags.AddRange(match.Rule.Tags); sarifResult.Message = new Message @@ -134,7 +167,7 @@ public class AnalyzeSarifWriter : CommandResultsWriter var fileName = match.FileName; if (basePath is not null) { - fileName = Path.GetRelativePath(basePath, fileName); + fileName = Path.GetRelativePath(basePath, fileName).Replace("\\","/"); } if (Uri.TryCreate(fileName, UriKind.RelativeOrAbsolute, out var outUri)) @@ -150,6 +183,10 @@ public class AnalyzeSarifWriter : CommandResultsWriter Uri = outUri } }; + if (basePath != null) + { + artifact.Location.UriBaseId = "ROOT"; + } artifactIndex = artifact.Location.Index; artifact.Tags.AddRange(match.Rule.Tags); if (match.LanguageInfo is { } languageInfo) @@ -164,30 +201,37 @@ public class AnalyzeSarifWriter : CommandResultsWriter artifacts[artifactIndex].Tags.AddRange(match.Rule.Tags); } - sarifResult.Locations = new List + Location location = new() { - new() + PhysicalLocation = new PhysicalLocation { - PhysicalLocation = new PhysicalLocation + ArtifactLocation = new ArtifactLocation { - ArtifactLocation = new ArtifactLocation + Index = artifactIndex, + Uri = outUri + }, + Region = new Region + { + StartLine = match.StartLocationLine, + StartColumn = match.StartLocationColumn, + EndLine = match.EndLocationLine, + EndColumn = match.EndLocationColumn, + Snippet = new ArtifactContent { - Index = artifactIndex - }, - Region = new Region - { - StartLine = match.StartLocationLine, - StartColumn = match.StartLocationColumn, - EndLine = match.EndLocationLine, - EndColumn = match.EndLocationColumn, - Snippet = new ArtifactContent - { - Text = match.Sample - } + Text = match.Sample } } } }; + if (basePath != null) + { + location.PhysicalLocation.ArtifactLocation.UriBaseId = "ROOT"; + } + sarifResult.SetProperty("AppInspector:Severity", match.Rule.Severity.ToString()); + sarifResult.Locations = new List + { + location + }; } } } @@ -200,7 +244,7 @@ public class AnalyzeSarifWriter : CommandResultsWriter log.Runs.Add(run); try { - JsonSerializer.Serialize(StreamWriter.BaseStream, log); + log.Save(StreamWriter.BaseStream); } catch (Exception e) { diff --git a/AppInspector.RulesEngine/RuleProcessor.cs b/AppInspector.RulesEngine/RuleProcessor.cs index 1be53ec..b881967 100644 --- a/AppInspector.RulesEngine/RuleProcessor.cs +++ b/AppInspector.RulesEngine/RuleProcessor.cs @@ -181,7 +181,7 @@ public class RuleProcessor EndLocationColumn = endLocation.Column, MatchingPattern = oatRule.AppInspectorRule.Patterns[patternIndex], Excerpt = numLinesContext > 0 - ? ExtractExcerpt(textContainer, startLocation.Line, numLinesContext) + ? ExtractExcerpt(textContainer, startLocation, endLocation, boundary, numLinesContext) : string.Empty, Sample = numLinesContext > -1 ? ExtractTextSample(textContainer.FullContent, boundary.Index, boundary.Length) @@ -399,7 +399,7 @@ public class RuleProcessor : startLocation.Line + 1, //match is on last line MatchingPattern = oatRule.AppInspectorRule.Patterns[patternIndex], Excerpt = numLinesContext > 0 - ? ExtractExcerpt(textContainer, startLocation.Line, numLinesContext) + ? ExtractExcerpt(textContainer, startLocation, endLocation, boundary, numLinesContext) : string.Empty, Sample = numLinesContext > -1 ? ExtractTextSample(textContainer.FullContent, boundary.Index, boundary.Length) @@ -554,37 +554,30 @@ public class RuleProcessor /// from the template /// /// - private static string ExtractExcerpt(TextContainer text, int startLineNumber, int context = 3) + private static string ExtractExcerpt(TextContainer text, Location start, Location end, Boundary matchBoundary, int context = 3) { if (context == 0) { return string.Empty; } - if (startLineNumber < 0) - { - startLineNumber = 0; - } - - if (startLineNumber >= text.LineEnds.Count) - { - startLineNumber = text.LineEnds.Count - 1; - } - + int startLineNumber = + start.Line < 0 ? 0 : start.Line > text.LineEnds.Count ? text.LineEnds.Count - 1 : start.Line; + int endLineNUmber = + end.Line < 0 ? 0 : end.Line > text.LineEnds.Count ? text.LineEnds.Count - 1 : end.Line; + // First we try to include the number of lines of context requested var excerptStartLine = Math.Max(0, startLineNumber - context); - var excerptEndLine = Math.Min(text.LineEnds.Count - 1, startLineNumber + context); + var excerptEndLine = Math.Min(text.LineEnds.Count - 1, endLineNUmber + context); var startIndex = text.LineStarts[excerptStartLine]; var endIndex = text.LineEnds[excerptEndLine] + 1; + // Maximum number of characters to capture on each side var maxCharacterContext = context * 100; - // Only gather 100*lines context characters to avoid gathering super long lines - if (text.LineStarts[startLineNumber] - startIndex > maxCharacterContext) + // If the number of characters captured for context is larger than 100*number of lines, + // instead gather an appropriate number of characters + if (endIndex - startIndex - matchBoundary.Length > maxCharacterContext * 2) { - startIndex = Math.Max(0, startIndex - maxCharacterContext); - } - - if (endIndex - text.LineEnds[startLineNumber] > maxCharacterContext) - { - endIndex = Math.Min(text.FullContent.Length - 1, endIndex + maxCharacterContext); + startIndex = Math.Max(0, matchBoundary.Index - maxCharacterContext); + endIndex = Math.Max(0, matchBoundary.Index + matchBoundary.Length + maxCharacterContext); } return text.FullContent[startIndex..endIndex];