private void LogSimpleResult(SarifLogger sarifLogger) { ReportingDescriptor rule = new ReportingDescriptor { Id = "RuleId" }; sarifLogger.Log(rule, CreateSimpleResult(rule)); }
private void LogError(JsonError error) { IRule rule = GetRuleForError(error); Result result = MakeResultFromError(error); _logger.Log(rule, result); _messages.Add(result.FormatForVisualStudio(rule)); }
private static void StreamOwnershipHelper(bool closeWriterOnDispose) { string expectedText = GetResourceContents("SimpleExample.sarif"); MemoryStream memoryStream = new MemoryStream(); var streamWriter = new StreamWriter(memoryStream); using (var logger = new SarifLogger( streamWriter, loggingOptions: LoggingOptions.PrettyPrint, dataToRemove: OptionallyEmittedData.NondeterministicProperties, closeWriterOnDispose: closeWriterOnDispose)) { logger.Log( new ReportingDescriptor { Id = "MyId" }, new Result { Message = new Message { Text = "My text" }, RuleId = "MyId" }); } // Important. Force streamwriter to commit everything. streamWriter.Flush(); memoryStream.Seek(0, SeekOrigin.Begin); using (var streamReader = new StreamReader(memoryStream)) using (var jsonTextReader = new JsonTextReader(streamReader)) { var jsonSerializer = new JsonSerializer(); SarifLog sarifLog = jsonSerializer.Deserialize <SarifLog>(jsonTextReader); // The tool metadata generated by the logger is very environment specific. // For example, this output changes depending on whether the test is // executed from within Visual Studio or via the command-line within // the AppVeyor environment. We therefore normalize this information. sarifLog.Runs[0].Tool = new Tool { Driver = new ToolComponent { Name = "SarifLoggerTests", Rules = new List <ReportingDescriptor> { new ReportingDescriptor { Id = "MyId" } } }, }; // Prove we did it. string actualText = JsonConvert.SerializeObject(sarifLog, formatting: Formatting.Indented); actualText.Should().BeEquivalentTo(expectedText); } }
private static void Main(string[] args) { if (args.Length < 1) { Console.WriteLine(@"Usage: SarifTrim [inputFilePath] [outputFilePath?] [removeParts?] removeParts: Semicolon-separated sections to remove. Options: UriBaseIds;CodeFlows;RelatedLocations;Graphs;GraphTraversals;Stacks;WebRequests;WebResponses"); return; } string inputFilePath = args[0]; string outputFilePath = (args.Length > 1 ? args[1] : Path.ChangeExtension(inputFilePath, $"trimmed{Path.GetExtension(inputFilePath)}")); // Split on comma, remove whitespace, and put in case-insensitive HashSet HashSet <string> removeParts = new HashSet <string>((args.Length > 2 ? args[2] : "").Split(';').Select(entry => entry.Trim()), StringComparer.OrdinalIgnoreCase); SarifLog log = null; using (new ConsoleWatch($"Loading \"{inputFilePath}\"...")) { log = SarifLog.LoadDeferred(inputFilePath); } using (new ConsoleWatch($"Trimming \"{inputFilePath}\" into \"{outputFilePath}\"...")) { Run run = log.Runs[0]; SarifConsolidator consolidator = new SarifConsolidator(run); consolidator.MessageLengthLimitChars = 1024; consolidator.RemoveUriBaseIds = (removeParts.Contains("UriBaseIds")); consolidator.RemoveCodeFlows = (removeParts.Contains("CodeFlows")); consolidator.RemoveRelatedLocations = (removeParts.Contains("RelatedLocations")); consolidator.RemoveGraphs = (removeParts.Contains("Graphs")); consolidator.RemoveStacks = (removeParts.Contains("Stacks")); consolidator.RemoveWebRequests = (removeParts.Contains("WebRequests")); consolidator.RemoveWebResponses = (removeParts.Contains("WebResponses")); // Consolidate the SarifLog per settings using (SarifLogger logger = new SarifLogger(outputFilePath, LogFilePersistenceOptions.OverwriteExistingOutputFile, tool: run.Tool, run: run)) { foreach (Result result in run.Results) { consolidator.Trim(result); logger.Log(result.GetRule(run), result); } } if (consolidator.UniqueThreadFlowLocations < consolidator.TotalThreadFlowLocations) { Console.WriteLine($"Consolidated {consolidator.TotalThreadFlowLocations:n0} ThreadFlowLocations into {consolidator.UniqueThreadFlowLocations:n0} unique ones."); } if (consolidator.UniqueLocations < consolidator.TotalLocations) { Console.WriteLine($"Consolidated {consolidator.TotalLocations:n0} Locations into {consolidator.UniqueLocations:n0} unique per-Result Locations."); } } }
private static void ReportResult(Result result, SarifLogger logger) { ReportingDescriptor rule = RuleFactory.GetRuleFromRuleId(result.RuleId); Console.Error.WriteLine( result.FormatForVisualStudio(rule)); logger.Log(rule, result); }
public void SarifLogger_DoNotScrapeFilesFromNotifications() { var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: null, dataToInsert: OptionallyEmittedData.Hashes, prereleaseInfo: null, invocationTokensToRedact: null, invocationPropertiesToLog: null)) { var toolNotification = new Notification { PhysicalLocation = new PhysicalLocation { FileLocation = new FileLocation { Uri = new Uri(@"file:///file0.cpp") } } }; sarifLogger.LogToolNotification(toolNotification); var configurationNotification = new Notification { PhysicalLocation = new PhysicalLocation { FileLocation = new FileLocation { Uri = new Uri(@"file:///file0.cpp") } } }; sarifLogger.LogConfigurationNotification(configurationNotification); string ruleId = "RuleId"; var rule = new Rule() { Id = ruleId }; var result = new Result() { RuleId = ruleId }; sarifLogger.Log(rule, result); } } string logText = sb.ToString(); var sarifLog = JsonConvert.DeserializeObject <SarifLog>(logText); sarifLog.Runs[0].Files.Should().BeNull(); }
public void SarifLogger_WritesFileDataWithUnrecognizedEncoding() { var sb = new StringBuilder(); string file; string fileText = "using System;"; using (var tempFile = new TempFile(".cs")) using (var textWriter = new StringWriter(sb)) { file = tempFile.Name; File.WriteAllText(file, fileText); using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: new string[] { file }, dataToInsert: OptionallyEmittedData.TextFiles, prereleaseInfo: null, invocationTokensToRedact: null, invocationPropertiesToLog: null, defaultFileEncoding: "ImaginaryEncoding")) { string ruleId = "RuleId"; var rule = new Rule() { Id = ruleId }; var result = new Result() { RuleId = ruleId }; sarifLogger.Log(rule, result); } } string logText = sb.ToString(); string fileDataKey = new Uri(file).AbsoluteUri; byte[] fileBytes = Encoding.Default.GetBytes(fileText); var sarifLog = JsonConvert.DeserializeObject <SarifLog>(logText); FileData fileData = sarifLog.Runs[0].Files[fileDataKey]; fileData.MimeType.Should().Be(MimeType.CSharp); fileData.Contents.Binary.Should().Be(Convert.ToBase64String(fileBytes)); fileData.Contents.Text.Should().BeNull(); }
/// <summary> /// Used to generate and persist the SARIF file. /// </summary> /// <param name="path"> The path to store the SARIF file </param> /// <param name="ecId"> The guid of the element to traverse from </param> /// <param name="deleteResultsFile"> Should the results file be deleted </param> public static void GenerateAndPersistSarifFile(string path, Guid ecId, bool deleteResultsFile) { Run run = new Run(); run.BaselineInstanceGuid = Guid.NewGuid().ToString(); run.Files = GenerateEmbeddedFiles(path, ecId, deleteResultsFile, run.BaselineInstanceGuid); List <String> fileKeys = new List <string>(run.Files.Keys); // Generate tool info Tool tool = GenerateToolInfo(); // Traverse the snapshot info to get the info we need. List <Tuple <Rule, Result> > elementResults = TraverseTreeDepthFirst(ecId); // Set the standards list. Standards temporarily not to be set till sarif sdk can provide a fix. //run.SetProperty<Dictionary<string, A11yCriteria>>(StandardsKey, ResultsFileSarifMapper.A11yCriteriaList); run.Invocations = GenerateInvocationInfo(); // Add all rule, result pairs to the log file. var sb = new StringBuilder(); List <Attachment> attachmentList = GetResultAttachments(fileKeys); using (var textWriter = new StringWriter(sb, CultureInfo.InvariantCulture)) { using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: null, loggingOptions: LoggingOptions.PrettyPrint | LoggingOptions.Verbose, // <-- use PrettyPrint to generate readable (multi-line, indented) JSON run: run, tool: tool, invocationTokensToRedact: null, invocationPropertiesToLog: null)) { foreach (Tuple <Rule, Result> scanResult in elementResults) { if (scanResult.Item1 != null && scanResult.Item2 != null) { scanResult.Item2.Attachments = attachmentList; sarifLogger.Log(scanResult.Item1, scanResult.Item2); } } } } // Persist the log file File.WriteAllText(path, sb.ToString()); }
public void SarifLogger_WritesFileData() { var sb = new StringBuilder(); string file; using (var tempFile = new TempFile(".cpp")) using (var textWriter = new StringWriter(sb)) { file = tempFile.Name; File.WriteAllText(file, "#include \"windows.h\";"); using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: new string[] { file }, dataToInsert: OptionallyEmittedData.Hashes, prereleaseInfo: null, invocationTokensToRedact: null, invocationPropertiesToLog: null)) { string ruleId = "RuleId"; var rule = new Rule() { Id = ruleId }; var result = new Result() { RuleId = ruleId }; sarifLogger.Log(rule, result); } } string logText = sb.ToString(); string fileDataKey = new Uri(file).AbsoluteUri; var sarifLog = JsonConvert.DeserializeObject <SarifLog>(logText); sarifLog.Runs[0].Files[fileDataKey].MimeType.Should().Be(MimeType.Cpp); sarifLog.Runs[0].Files[fileDataKey].Hashes[0].Algorithm.Should().Be("md5"); sarifLog.Runs[0].Files[fileDataKey].Hashes[0].Value.Should().Be("4B9DC12934390387862CC4AB5E4A2159"); sarifLog.Runs[0].Files[fileDataKey].Hashes[1].Algorithm.Should().Be("sha-1"); sarifLog.Runs[0].Files[fileDataKey].Hashes[1].Value.Should().Be("9B59B1C1E3F5F7013B10F6C6B7436293685BAACE"); sarifLog.Runs[0].Files[fileDataKey].Hashes[2].Algorithm.Should().Be("sha-256"); sarifLog.Runs[0].Files[fileDataKey].Hashes[2].Value.Should().Be("0953D7B3ADA7FED683680D2107EE517A9DBEC2D0AF7594A91F058D104B7A2AEB"); }
public void SarifLogger_LogsSpecifiedInvocationProperties() { var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: null, dataToInsert: OptionallyEmittedData.Hashes, prereleaseInfo: null, invocationTokensToRedact: null, invocationPropertiesToLog: new[] { "WorkingDirectory", "ProcessId" })) { string ruleId = "RuleId"; var rule = new Rule() { Id = ruleId }; var result = new Result() { RuleId = ruleId }; sarifLogger.Log(rule, result); } } string logText = sb.ToString(); var sarifLog = JsonConvert.DeserializeObject <SarifLog>(logText); Invocation invocation = sarifLog.Runs[0].Invocations[0]; // StartTime and EndTime should still be logged. invocation.StartTime.Should().NotBe(DateTime.MinValue); invocation.EndTime.Should().NotBe(DateTime.MinValue); // Specified properties should be logged. invocation.WorkingDirectory.Should().NotBeNull(); invocation.ProcessId.Should().NotBe(0); // Other properties should be empty. invocation.CommandLine.Should().BeNull(); invocation.ExecutableLocation.Should().BeNull(); }
public void SarifLogger_AcceptsSubrulesInResultRuleId() { var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger(textWriter)) { var rule = new ReportingDescriptor { Id = "RuleId" }; var result = new Result { RuleId = "RuleId/1" }; Action action = () => sarifLogger.Log(rule, result); action.Should().NotThrow(); } } }
public void SarifLogger_ResultAndRuleIdMismatch() { var sb = new StringBuilder(); using (var writer = new StringWriter(sb)) using (var sarifLogger = new SarifLogger(writer, LoggingOptions.Verbose)) { var rule = new Rule() { Id = "ActualId" }; var result = new Result() { RuleId = "IncorrectRuleId", Message = "test message" }; Assert.Throws <ArgumentException>(() => sarifLogger.Log(rule, result)); } }
public void SarifLogger_TreatsInvocationPropertiesCaseInsensitively() { var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: null, dataToInsert: OptionallyEmittedData.Hashes, prereleaseInfo: null, invocationTokensToRedact: null, invocationPropertiesToLog: new[] { "WORKINGDIRECTORY", "prOCessID" })) { string ruleId = "RuleId"; var rule = new Rule() { Id = ruleId }; var result = new Result() { RuleId = ruleId }; sarifLogger.Log(rule, result); } } string logText = sb.ToString(); var sarifLog = JsonConvert.DeserializeObject <SarifLog>(logText); Invocation invocation = sarifLog.Runs[0].Invocations?[0]; // Specified properties should be logged. invocation.WorkingDirectory.Should().NotBeNull(); invocation.ProcessId.Should().NotBe(0); }
public void SarifLogger_WritesSarifLoggerVersion() { var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: new string[] { @"foo.cpp" }, loggingOptions: LoggingOptions.None, prereleaseInfo: null, invocationTokensToRedact: null, invocationPropertiesToLog: null)) { string ruleId = "RuleId"; var rule = new Rule() { Id = ruleId }; var result = new Result() { RuleId = ruleId }; sarifLogger.Log(rule, result); } } string output = sb.ToString(); var sarifLog = JsonConvert.DeserializeObject <SarifLog>(output); string sarifLoggerLocation = typeof(SarifLogger).Assembly.Location; string expectedVersion = FileVersionInfo.GetVersionInfo(sarifLoggerLocation).FileVersion; sarifLog.Runs[0].Tool.SarifLoggerVersion.Should().Be(expectedVersion); }
public void SarifLogger_DoNotScrapeFilesFromNotifications() { var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: null, dataToInsert: OptionallyEmittedData.Hashes, invocationTokensToRedact: null, invocationPropertiesToLog: null)) { var toolNotification = new Notification { Locations = new List <Location> { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(@"file:///file.cpp") } } } }, Message = new Message { Text = "A notification was raised." } }; sarifLogger.LogToolNotification(toolNotification); var configurationNotification = new Notification { Locations = new List <Location> { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(@"file:///file.cpp") } } } }, Message = new Message { Text = "A notification was raised." } }; sarifLogger.LogConfigurationNotification(configurationNotification); string ruleId = "RuleId"; var rule = new ReportingDescriptor { Id = ruleId }; var result = new Result { RuleId = ruleId, Message = new Message { Text = "Some testing occurred." } }; sarifLogger.Log(rule, result); } } string logText = sb.ToString(); var sarifLog = JsonConvert.DeserializeObject <SarifLog>(logText); sarifLog.Runs[0].Artifacts.Should().BeNull(); }
public void SarifLogger_ScrapesFilesFromResult() { var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: null, dataToInsert: OptionallyEmittedData.Hashes, invocationTokensToRedact: null, invocationPropertiesToLog: null)) { string ruleId = "RuleId"; var rule = new ReportingDescriptor { Id = ruleId }; var result = new Result { RuleId = ruleId, Message = new Message { Text = "Some testing occurred." }, AnalysisTarget = new ArtifactLocation { Uri = new Uri(@"file:///file0.cpp") }, Locations = new[] { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(@"file:///file1.cpp") } } }, }, Fixes = new[] { new Fix { ArtifactChanges = new[] { new ArtifactChange { ArtifactLocation = new ArtifactLocation { Uri = new Uri(@"file:///file2.cpp") }, Replacements = new[] { new Replacement { DeletedRegion = new Region { StartLine = 1 } } } } }, } }, RelatedLocations = new[] { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(@"file:///file3.cpp") } } } }, Stacks = new[] { new Stack { Frames = new[] { new StackFrame { Location = new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(@"file:///file4.cpp") } } } } } } }, CodeFlows = new[] { new CodeFlow { ThreadFlows = new[] { new ThreadFlow { Locations = new[] { new ThreadFlowLocation { Location = new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(@"file:///file5.cpp") } } } } } } } } } }; sarifLogger.Log(rule, result); } } string logText = sb.ToString(); var sarifLog = JsonConvert.DeserializeObject <SarifLog>(logText); int fileCount = 6; for (int i = 0; i < fileCount; ++i) { string fileName = @"file" + i + ".cpp"; string fileDataKey = "file:///" + fileName; sarifLog.Runs[0].Artifacts.Where(f => f.Location.Uri.AbsoluteUri.ToString().Contains(fileDataKey)).Any().Should().BeTrue(); } sarifLog.Runs[0].Artifacts.Count.Should().Be(fileCount); }
/// <summary> /// Constructs a sample SARIF log and writes it to the specified location. /// </summary> /// <param name="options">Create verb options.</param> /// <returns>Exit code</returns> static int CreateSarifLogFile(CreateOptions options) { // We'll use this source file for several defect results -- the // SampleSourceFiles folder should be a child of the project folder, // two levels up from the folder that contains the EXE (e.g., bin\Debug). var artifactLocation = new ArtifactLocation { Uri = new Uri($"file://{AppDomain.CurrentDomain.BaseDirectory}../../SampleSourceFiles/AnalysisSample.cs") }; // Create a list of rules that will be enforced during your analysis #region Rules list var rules = new List <ReportingDescriptor>() { new ReportingDescriptor { Id = "CA1819", Name = new Message { Text = "Properties should not return arrays" }, FullDescription = new Message { Text = "Arrays returned by properties are not write-protected, even if the property is read-only. To keep the array tamper-proof, the property must return a copy of the array. Typically, users will not understand the adverse performance implications of calling such a property." }, MessageStrings = new Dictionary <string, MultiformatMessageString> { { "Default", new MultiformatMessageString { Text = "The property {0} returns an array." } } } }, new ReportingDescriptor { Id = "CA1820", Name = new Message { Text = "Test for empty strings using string length" }, FullDescription = new Message { Text = "Comparing strings by using the String.Length property or the String.IsNullOrEmpty method is significantly faster than using Equals." }, MessageStrings = new Dictionary <string, MultiformatMessageString> { { "Default", new MultiformatMessageString { Text = "The test for an empty string is performed by a string comparison rather than by testing String.Length." } } } }, new ReportingDescriptor { Id = "CA2105", Name = new Message { Text = "Array fields should not be read only" }, FullDescription = new Message { Text = "When you apply the read-only (ReadOnly in Visual Basic) modifier to a field that contains an array, the field cannot be changed to reference a different array. However, the elements of the array stored in a read-only field can be changed." }, MessageStrings = new Dictionary <string, MultiformatMessageString> { { "Default", new MultiformatMessageString { Text = "The array-valued field {0} is marked readonly." } } } }, new ReportingDescriptor { Id = "CA2215", Name = new Message { Text = "Dispose methods should call base class dispose" }, FullDescription = new Message { Text = "If a type inherits from a disposable type, it must call the Dispose method of the base type from its own Dispose method." }, MessageStrings = new Dictionary <string, MultiformatMessageString> { { "Default", new MultiformatMessageString { Text = "The Dispose method does not call the base class Dispose method." } } } } }; #endregion // Regions will be calculated by your analysis process #region Regions var regions = new List <Region>() { new Region // CA1819 { StartLine = 17, StartColumn = 16, EndColumn = 32 }, new Region // CA1820 { StartLine = 26, StartColumn = 21, EndColumn = 44 }, new Region // CA2105 { StartLine = 14, StartColumn = 17, EndColumn = 25 }, new Region // CA2215 { StartLine = 37, StartColumn = 9, EndColumn = 9 } }; #endregion #region Message arguments string[][] messageArguments = new string[][] { new string[] { "MyIntArray" }, null, new string[] { "_myStringArray" }, null }; #endregion // Sets of fixes corresponding to each rule // Multiple fixes can be provided for the user to choose from #region Fixes IList <Fix[]> fixes = new List <Fix[]> { null, // no suggested fixes for CA1819 new[] { new Fix // CA1820 { Description = new Message { Text = "Replace empty string test with test for zero length." }, Changes = new[] { new ArtifactChange { ArtifactLocation = artifactLocation, Replacements = new[] { new Replacement { DeletedRegion = new Region { StartLine = 26, StartColumn = 38, EndColumn = 44 }, InsertedContent = new ArtifactContent { Text = ".Length == 0" } } } } }, } }, null, // no suggested fix for CA2105 new[] { new Fix // CA2215 { Description = new Message { Text = "Call base.Dispose in the derived's class's Dispose method." }, Changes = new[] { new ArtifactChange { ArtifactLocation = artifactLocation, Replacements = new[] { new Replacement { DeletedRegion = new Region { StartLine = 37, StartColumn = 1, EndColumn = 1 }, InsertedContent = new ArtifactContent { Text = @" base.Dispose();\n" } } } } } } } }; #endregion // The SarifLogger will write the JSON-formatted log to this StringBuilder var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger( textWriter, loggingOptions: LoggingOptions.PrettyPrint, // Use PrettyPrint to generate readable (multi-line, indented) JSON dataToInsert: OptionallyEmittedData.TextFiles | // Embed source file content directly in the log file -- great for portability of the log! OptionallyEmittedData.Hashes, tool: null, run: null, analysisTargets: null, invocationTokensToRedact: null, invocationPropertiesToLog: null, defaultFileEncoding: null)) { // Create one result for each rule for (int i = 0; i < rules.Count; i++) { ReportingDescriptor rule = rules[i]; Region region = regions[i]; var result = new Result() { RuleId = rule.Id, AnalysisTarget = new ArtifactLocation { Uri = new Uri(@"file://d:/src/module/example.dll") }, // This is the file that was analyzed Message = new Message { MessageId = "Default", Arguments = messageArguments[i] }, Locations = new[] { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = artifactLocation, Region = region } }, }, Fixes = fixes[i], RelatedLocations = new[] { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { // Because this file doesn't exist, it will be included in the files list but will only have a path and MIME type // This is the behavior you'll see any time a file can't be located/accessed Uri = new Uri($"file://{AppDomain.CurrentDomain.BaseDirectory}/../../../SampleSourceFiles/SomeOtherSourceFile.cs"), }, Region = new Region { StartLine = 147, StartColumn = 19, EndLine = 147, EndColumn = 40 } } } }, Stacks = new[] { new Stack { Frames = new[] { new StackFrame { // The method that contains the defect Location = new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = artifactLocation, Region = new Region { StartLine = 212 } } } }, new StackFrame { // The method that calls the one above, e.g. ComputeSomeValue() Location = new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = artifactLocation, Region = new Region { StartLine = 452 // Fake example } } } }, new StackFrame { // The method that calls the one above, e.g. Main() Location = new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = artifactLocation, Region = new Region { StartLine = 145 } } } } } } } }; // Let's add a CodeFlow for the first defect (properties shouldn't return arrays) // This flow shows where the array is declared, and where it is returned by a property if (i == 0) { var codeFlow = new CodeFlow { // This is what a single-threaded result looks like // TIP: use SarifUtilities.CreateSingleThreadedCodeFlow to reduce repetition // Multi-threaded example coming soon! ThreadFlows = new[] { new ThreadFlow { Locations = new[] { new ThreadFlowLocation { // This is the defect statement's location Location = new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = artifactLocation, Region = region } }, Importance = ThreadFlowLocationImportance.Essential }, new ThreadFlowLocation { // This is the declaration of the array Location = new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = artifactLocation, Region = new Region { StartLine = 12 } } }, NestingLevel = 1, Importance = ThreadFlowLocationImportance.Important } } } } }; result.CodeFlows = new[] { codeFlow }; } sarifLogger.Log(rule, result); } } } File.WriteAllText(options.OutputFilePath, sb.ToString()); return(0); }
public void SarifLogger_ScrapesFilesFromResult() { var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: null, dataToInsert: OptionallyEmittedData.Hashes, prereleaseInfo: null, invocationTokensToRedact: null, invocationPropertiesToLog: null)) { string ruleId = "RuleId"; var rule = new Rule() { Id = ruleId }; var result = new Result() { RuleId = ruleId, AnalysisTarget = new FileLocation { Uri = new Uri(@"file:///file0.cpp") }, Locations = new[] { new Location { PhysicalLocation = new PhysicalLocation { FileLocation = new FileLocation { Uri = new Uri(@"file:///file1.cpp") } } }, }, Fixes = new[] { new Fix { FileChanges = new[] { new FileChange { FileLocation = new FileLocation { Uri = new Uri(@"file:///file2.cpp") } } } } }, RelatedLocations = new[] { new Location { PhysicalLocation = new PhysicalLocation { FileLocation = new FileLocation { Uri = new Uri(@"file:///file3.cpp") } } } }, Stacks = new[] { new Stack { Frames = new[] { new StackFrame { Location = new Location { PhysicalLocation = new PhysicalLocation { FileLocation = new FileLocation { Uri = new Uri(@"file:///file4.cpp") } } } } } } }, CodeFlows = new[] { new CodeFlow { ThreadFlows = new[] { new ThreadFlow { Locations = new[] { new ThreadFlowLocation { Location = new Location { PhysicalLocation = new PhysicalLocation { FileLocation = new FileLocation { Uri = new Uri(@"file:///file5.cpp") } } } } } } } } } }; sarifLogger.Log(rule, result); } } string logText = sb.ToString(); var sarifLog = JsonConvert.DeserializeObject <SarifLog>(logText); int fileCount = 6; for (int i = 0; i < fileCount; ++i) { string fileName = @"file" + i + ".cpp"; string fileDataKey = "file:///" + fileName; sarifLog.Runs[0].Files.Should().ContainKey(fileDataKey, "file data for " + fileName + " should exist in files collection"); } sarifLog.Runs[0].Files.Count.Should().Be(fileCount); }
public void SarifLogger_WritesFileContentsForAnalysisTargets() { var sb = new StringBuilder(); // Create a temporary file whose extension signals that it is textual. // This ensures that the ArtifactContents.Text property, rather than // the Binary property, is populated, so the test of the Text property // at the end will work. using (var tempFile = new TempFile(".txt")) { string tempFilePath = tempFile.Name; string tempFileDirectory = Path.GetDirectoryName(tempFilePath); string tempFileName = Path.GetFileName(tempFilePath); File.WriteAllText(tempFilePath, "#include \"windows.h\";"); var run = new Run { OriginalUriBaseIds = new Dictionary <string, ArtifactLocation> { [TempFileBaseId] = new ArtifactLocation { Uri = new Uri(tempFileDirectory, UriKind.Absolute) } }, // To get text contents, we also need to specify an encoding that // Encoding.GetEncoding() will accept. DefaultEncoding = "UTF-8" }; var rule = new ReportingDescriptor { Id = TestData.TestRuleId }; // Create a result that refers to an artifact whose location is specified // by a relative reference together with a uriBaseId. var result = new Result { RuleId = rule.Id, Message = new Message { Text = "Testing." }, Locations = new List <Location> { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(tempFileName, UriKind.Relative), UriBaseId = TempFileBaseId } } } } }; using (var textWriter = new StringWriter(sb)) { // Create a logger that inserts artifact contents. using (var sarifLogger = new SarifLogger( textWriter, run: run, dataToInsert: OptionallyEmittedData.TextFiles)) { sarifLogger.Log(rule, result); } // The logger should have populated the artifact contents. string logText = sb.ToString(); SarifLog sarifLog = JsonConvert.DeserializeObject <SarifLog>(logText); sarifLog.Runs[0].Artifacts[0].Contents?.Text.Should().NotBeNullOrEmpty(); } } }
/// <summary> /// Constructs a sample SARIF log and writes it to the specified location. /// </summary> /// <param name="options">Create verb options.</param> /// <returns>Exit code</returns> static int CreateSarifLogFile(CreateOptions options) { // We'll use this source file for several defect results -- the // SampleSourceFiles folder should be at the same level as the project folder // Because this file can actually be accessed by this app, its // content will be embedded in the log file var fileLocation = new FileLocation { Uri = new Uri($"file://{AppDomain.CurrentDomain.BaseDirectory}/../../../../SampleSourceFiles/AnalysisSample.cs") }; // Create a list of rules that will be enforced during your analysis #region Rules list var rules = new List <Rule>() { new Rule { Id = "CA1819", Name = new Message { Text = "Properties should not return arrays" }, FullDescription = new Message { Text = "Arrays returned by properties are not write-protected, even if the property is read-only. To keep the array tamper-proof, the property must return a copy of the array. Typically, users will not understand the adverse performance implications of calling such a property." } }, new Rule { Id = "CA1820", Name = new Message { Text = "Test for empty strings using string length" }, FullDescription = new Message { Text = "Comparing strings by using the String.Length property or the String.IsNullOrEmpty method is significantly faster than using Equals." } }, new Rule { Id = "CA2105", Name = new Message { Text = "Array fields should not be read only" }, FullDescription = new Message { Text = "When you apply the read-only (ReadOnly in Visual Basic) modifier to a field that contains an array, the field cannot be changed to reference a different array. However, the elements of the array stored in a read-only field can be changed." } }, new Rule { Id = "CA2215", Name = new Message { Text = "Dispose methods should call base class dispose" }, FullDescription = new Message { Text = "If a type inherits from a disposable type, it must call the Dispose method of the base type from its own Dispose method." } }, //new Rule //{ // Id ="CA1816", // Name = new Message { Text = "Call GC.SuppressFinalize correctly" }, // FullDescription = new Message { Text = "A method that is an implementation of Dispose does not call GC.SuppressFinalize, or a method that is not an implementation of Dispose calls GC.SuppressFinalize, or a method calls GC.SuppressFinalize and passes something other than this (Me in Visual Basic)." } //}, //new Rule //{ // Id ="CA2006", // Name = new Message { Text = "Use SafeHandle to encapsulate native resources" }, // FullDescription = new Message { Text = "Use of IntPtr in managed code might indicate a potential security and reliability problem. All uses of IntPtr must be reviewed to determine whether use of a SafeHandle, or similar technology, is required in its place." } //} }; #endregion // Regions will be calculated by your analysis process #region Regions var regions = new List <Region>() { new Region // CA1819 { StartLine = 16, StartColumn = 19, EndLine = 16, EndColumn = 38, Offset = 331, // Offset should account for the BOM, if present in source file Length = 19 }, new Region // CA1820 { StartLine = 23, StartColumn = 21, EndLine = 23, EndColumn = 44, Offset = 507, // Offset should account for the BOM, if present in source file Length = 23 }, new Region // CA2105 { StartLine = 11, StartColumn = 9, EndLine = 11, EndColumn = 50, Offset = 198, // Offset should account for the BOM, if present in source file Length = 41 }, new Region // CA2215 { StartLine = 32, StartColumn = 9, EndLine = 32, EndColumn = 30, Offset = 646, // Offset should account for the BOM, if present in source file Length = 21 } }; #endregion // Sets of fixes corresponding to each rule // Multiple fixes can be provided for the user to choose from #region Fixes IList <Fix[]> fixes = new List <Fix[]> { null, // no suggested fixes for CA1819 new[] { new Fix // CA1820 { Description = new Message { Text = "Replace empty string test with test for zero length." }, FileChanges = new[] { new FileChange { FileLocation = fileLocation, Replacements = new[] { new Replacement { DeletedLength = 6, InsertedBytes = ".Length == 0", Offset = 524 } } } }, } }, null, // no suggested fix for CA2105 new[] { new Fix // CA2215 { Description = new Message { Text = "Call base.Dispose in the derived's class's Dispose method." }, FileChanges = new[] { new FileChange { FileLocation = fileLocation, Replacements = new[] { new Replacement { DeletedLength = 0, InsertedBytes = @"\nbase.Dispose();", Offset = 656 } } } } } } }; #endregion // The SarifLogger will write the JSON-formatted log to this StringBuilder var sb = new StringBuilder(); using (var textWriter = new StringWriter(sb)) { using (var sarifLogger = new SarifLogger( textWriter, analysisTargets: null, loggingOptions: LoggingOptions.PersistTextFileContents | // <-- embed source file content directly in the log file -- great for portability of the log! LoggingOptions.ComputeFileHashes | LoggingOptions.PrettyPrint, // <-- use PrettyPrint to generate readable (multi-line, indented) JSON prereleaseInfo: null, invocationTokensToRedact: null, invocationPropertiesToLog: null)) { // Create one result for each rule for (int i = 0; i < rules.Count; i++) { Rule rule = rules[i]; Region region = regions[i]; var result = new Result() { RuleId = rule.Id, AnalysisTarget = new FileLocation { Uri = new Uri(@"file://d:/src/module/foo.dll") }, // This is the file that was analyzed Locations = new[] { new Location { PhysicalLocation = new PhysicalLocation { FileLocation = fileLocation, Region = region } }, }, Fixes = fixes[i], RelatedLocations = new[] { new Location { PhysicalLocation = new PhysicalLocation { FileLocation = new FileLocation { // Because this file doesn't exist, it will be included in the files list but will only have a path and MIME type // This is the behavior you'll see any time a file can't be located/accessed Uri = new Uri($"file://{AppDomain.CurrentDomain.BaseDirectory}/../../../SampleSourceFiles/SomeOtherSourceFile.cs"), }, Region = new Region { StartLine = 147, StartColumn = 19, EndLine = 147, EndColumn = 40, Offset = 1245, Length = 21 } } } }, Stacks = new[] { new Stack { Frames = new[] { new StackFrame { // The method that contains the defect Location = new Location { PhysicalLocation = new PhysicalLocation { FileLocation = fileLocation, Region = new Region { StartLine = 212 } } } }, new StackFrame { // The method that calls the one above, e.g. ComputeSomeValue() Location = new Location { PhysicalLocation = new PhysicalLocation { FileLocation = fileLocation, Region = new Region { StartLine = 452 // Fake example } } } }, new StackFrame { // The method that calls the one above, e.g. Main() Location = new Location { PhysicalLocation = new PhysicalLocation { FileLocation = fileLocation, Region = new Region { StartLine = 145 } } } } } } } }; // Let's add a CodeFlow for the first defect (properties shouldn't return arrays) // This flow shows where the array is declared, and where it is returned by a property if (i == 0) { var codeFlow = new CodeFlow { // This is what a single-threaded result looks like // TIP: use SarifUtilities.CreateSingleThreadedCodeFlow to reduce repetition // Multi-threaded example coming soon! ThreadFlows = new[] { new ThreadFlow { Locations = new[] { new CodeFlowLocation { // This is the defect statement's location Location = new Location { PhysicalLocation = new PhysicalLocation { FileLocation = fileLocation, Region = region } }, Step = 1, Importance = CodeFlowLocationImportance.Essential }, new CodeFlowLocation { // This is the declaration of the array Location = new Location { PhysicalLocation = new PhysicalLocation { FileLocation = fileLocation, Region = new Region { StartLine = 12 } } }, NestingLevel = 1, Step = 2, Importance = CodeFlowLocationImportance.Important } } } } }; result.CodeFlows = new[] { codeFlow }; } sarifLogger.Log(rule, result); } } } File.WriteAllText(options.OutputFilePath, sb.ToString()); return(0); }