Ejemplo n.º 1
0
        private void LogSimpleResult(SarifLogger sarifLogger)
        {
            ReportingDescriptor rule = new ReportingDescriptor {
                Id = "RuleId"
            };

            sarifLogger.Log(rule, CreateSimpleResult(rule));
        }
Ejemplo n.º 2
0
        private void LogError(JsonError error)
        {
            IRule  rule   = GetRuleForError(error);
            Result result = MakeResultFromError(error);

            _logger.Log(rule, result);
            _messages.Add(result.FormatForVisualStudio(rule));
        }
Ejemplo n.º 3
0
        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);
                }
        }
Ejemplo n.º 4
0
        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.");
                }
            }
        }
Ejemplo n.º 5
0
        private static void ReportResult(Result result, SarifLogger logger)
        {
            ReportingDescriptor rule = RuleFactory.GetRuleFromRuleId(result.RuleId);

            Console.Error.WriteLine(
                result.FormatForVisualStudio(rule));

            logger.Log(rule, result);
        }
Ejemplo n.º 6
0
        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();
        }
Ejemplo n.º 7
0
        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());
        }
Ejemplo n.º 9
0
        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");
        }
Ejemplo n.º 10
0
        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();
        }
Ejemplo n.º 11
0
        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();
                }
            }
        }
Ejemplo n.º 12
0
        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));
                }
        }
Ejemplo n.º 13
0
        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);
        }
Ejemplo n.º 14
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);
        }
Ejemplo n.º 15
0
        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();
        }
Ejemplo n.º 16
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,
                           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);
        }
Ejemplo n.º 17
0
        /// <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);
        }
Ejemplo n.º 18
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);
        }
Ejemplo n.º 19
0
        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();
                }
            }
        }
Ejemplo n.º 20
0
        /// <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);
        }