public ResultDiffingVisitor(SarifLog sarifLog) { this.AbsentResults = new HashSet<Result>(Result.ValueComparer); this.SharedResults = new HashSet<Result>(Result.ValueComparer); this.NewResults = new HashSet<Result>(Result.ValueComparer); VisitSarifLog(sarifLog); }
public void ResultDiffingVisitor_DetectsAbsentAndNewResults() { var result1 = new Result { RuleId = "TST0001" }; var result2 = new Result { RuleId = "TST0002" }; var result3 = new Result { RuleId = "TST0003" }; var sarifLog = new SarifLog { Runs = new[] { new Run { Results = new List<Result> { result1, result2 } } } }; var visitor = new ResultDiffingVisitor(sarifLog); result2 = new Result { RuleId = "TST0002" }; result3 = new Result { RuleId = "TST0003" }; var newResults = new Result[] { result2, result3 }; visitor.Diff(newResults); Assert.AreEqual(visitor.AbsentResults.Count, 1); Assert.AreEqual(visitor.AbsentResults.First().RuleId, result1.RuleId); Assert.AreEqual(visitor.NewResults.Count, 1); Assert.AreEqual(visitor.NewResults.First().RuleId, result3.RuleId); }
public MultipleRunsPerSarifTests() { var testLog = new SarifLog { Runs = new List <Run> { new Run { Tool = new Tool { Name = "Test", SemanticVersion = "1.0" }, Results = new List <Result> { new Result { RuleId = "C0001", Message = "Error 1", Locations = new List <Location> { new Location { AnalysisTarget = new PhysicalLocation { Uri = new Uri("file:///item1.cpp") } } } } } }, new Run { Tool = new Tool { Name = "Test", SemanticVersion = "1.0" }, Results = new List <Result> { new Result { RuleId = "C0002", Message = "Error 2", Locations = new List <Location> { new Location { AnalysisTarget = new PhysicalLocation { Uri = new Uri("file:///item2.cpp") } } } } } } } }; TestUtilities.InitializeTestEnvironment(testLog); }
private static SarifLog Baseline(SarifLog baseline, SarifLog current) { return(matcher.Match(new[] { baseline }, new[] { current }).FirstOrDefault()); }
public static async Task InitializeTestEnvironmentAsync(SarifLog sarifLog) { InitializeTestEnvironment(); await ErrorListService.ProcessSarifLogAsync(sarifLog, string.Empty, cleanErrors : true, openInEditor : false); }
private void RunConverter(StringBuilder sb, string toolFormat, string inputFileName, TestMode testMode) { string expectedFileName = inputFileName + ".sarif"; string generatedFileName = inputFileName + ".actual.sarif"; try { this.converter.ConvertToStandardFormat(toolFormat, inputFileName, generatedFileName, LoggingOptions.OverwriteExistingOutputFile | LoggingOptions.PrettyPrint); } catch (Exception ex) { sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "The converter {0} threw an exception for input \"{1}\".", toolFormat, inputFileName)); sb.AppendLine(ex.ToString()); return; } string expectedSarif = File.ReadAllText(expectedFileName); string actualSarif = File.ReadAllText(generatedFileName); if (expectedSarif == actualSarif) { JsonSerializerSettings settings = new JsonSerializerSettings() { ContractResolver = SarifContractResolver.Instance, Formatting = Formatting.Indented }; // Make sure we can successfully deserialize what was just generated SarifLog actualLog = JsonConvert.DeserializeObject <SarifLog>(actualSarif, settings); actualSarif = JsonConvert.SerializeObject(actualLog, settings); bool success; switch (testMode) { case TestMode.CompareFileContents: success = expectedSarif == actualSarif; break; case TestMode.CompareObjectModels: SarifLog expectedLog = JsonConvert.DeserializeObject <SarifLog>(expectedSarif, settings); success = SarifLogEqualityComparer.Instance.Equals(expectedLog, actualLog); break; default: throw new ArgumentException($"Invalid test mode: {testMode}", nameof(testMode)); } if (success) { return; } else { File.WriteAllText(generatedFileName, actualSarif); } } string errorMessage = "The output of the {0} converter did not match for input {1}."; sb.AppendLine(string.Format(CultureInfo.CurrentCulture, errorMessage, toolFormat, inputFileName)); sb.AppendLine("Check differences with:"); sb.AppendLine(GenerateDiffCommand(expectedFileName, generatedFileName)); }
public int Run(FileWorkItemsOptions options, IFileSystem fileSystem) { using (var filingContext = new SarifWorkItemContext()) { if (!string.IsNullOrEmpty(options.ConfigurationFilePath)) { filingContext.LoadFromXml(options.ConfigurationFilePath); } if (!ValidateOptions(options, filingContext, fileSystem)) { return(FAILURE); } // For unit tests: allow us to just validate the options and return. if (s_validateOptionsOnly) { return(SUCCESS); } string logFileContents = fileSystem.ReadAllText(options.InputFilePath); EnsureValidSarifLogFile(logFileContents, options.InputFilePath); if (options.SplittingStrategy != SplittingStrategy.None) { filingContext.SplittingStrategy = options.SplittingStrategy; } if (options.ShouldFileUnchanged != null) { filingContext.ShouldFileUnchanged = options.ShouldFileUnchanged.Value; } if (options.DataToRemove.ToFlags() != OptionallyEmittedData.None) { filingContext.DataToRemove = options.DataToRemove.ToFlags(); } if (options.DataToInsert.ToFlags() != OptionallyEmittedData.None) { filingContext.DataToInsert = options.DataToInsert.ToFlags(); } SarifLog sarifLog = null; using (var filer = new SarifWorkItemFiler(filingContext.HostUri, filingContext)) { sarifLog = filer.FileWorkItems(logFileContents); } // By the time we're here, we have updated options.OutputFilePath with the // options.InputFilePath argument (in the presence of --inline) and validated // that we can write to this location with one exception: we do not currently // handle inlining to a read-only location. string outputFilePath = options.OutputFilePath; if (!string.IsNullOrEmpty(outputFilePath)) { Formatting formatting = options.PrettyPrint ? Formatting.Indented : Formatting.None; string sarifLogText = JsonConvert.SerializeObject(sarifLog, formatting); fileSystem.WriteAllText(outputFilePath, sarifLogText); } } return(SUCCESS); }
private SarifLog CreatePartitionLog(T partitionValue) { SarifLog partitionLog; if (deepClone) { // Save time and space by not cloning the runs unless and until necessary. We will // only need to clone the runs that have results in this partition. IList <Run> originalRuns = originalLog.Runs; originalLog.Runs = null; partitionLog = originalLog.DeepClone(); originalLog.Runs = originalRuns; } else { partitionLog = new SarifLog(originalLog) { Runs = null }; } partitionLog.Runs = new List <Run>(); partitionLog.SetProperty(PartitionValuePropertyName, partitionValue); var artifactIndexRemappingDictionaries = new List <Dictionary <int, int> >(); for (int iOriginalRun = 0; iOriginalRun < partitionRunInfos.Count; ++iOriginalRun) { // Are there results for this run in this partition? if (partitionRunInfos[iOriginalRun].ResultDictionary.TryGetValue(partitionValue, out List <Result> results)) { // Yes, so we'll need a copy of the original run in which the results, and // certain run-level collections such as Run.Artifacts, have been replaced. Run originalRun = originalLog.Runs[iOriginalRun]; Run partitionRun; if (deepClone) { // Save time and space by only cloning the necessary results and associated // collection elements. We already cloned the relevant results in VisitResult. IList <Result> originalResults = originalRun.Results; IList <Artifact> originalArtifacts = originalRun.Artifacts; originalRun.Results = null; originalRun.Artifacts = null; partitionRun = originalRun.DeepClone(); originalRun.Results = originalResults; originalRun.Artifacts = originalArtifacts; } else { partitionRun = new Run(originalRun) { Results = null, Artifacts = null }; } partitionRun.Results = results; // Construct a mapping from the indices in the original run to the indices // in the partition run. This includes both the indices relevant to the // results in this partition, and indices that appear in all partitions // because they are mentioned outside of any result (we refer to these as // "global" indices). IEnumerable <int> allPartitionArtifactIndices = partitionRunInfos[iOriginalRun].GlobalArtifactIndices; if (partitionRunInfos[iOriginalRun].ArtifactIndicesDictionary.TryGetValue(partitionValue, out HashSet <int> partitionResultArtifactIndices)) { allPartitionArtifactIndices = allPartitionArtifactIndices.Union(partitionResultArtifactIndices); } var partitionArtifactIndexRemappingDictionary = new Dictionary <int, int>(); artifactIndexRemappingDictionaries.Add(partitionArtifactIndexRemappingDictionary); List <int> allPartitionArtifactIndicesList = allPartitionArtifactIndices .OrderBy(index => index) .ToList(); int numPartitionIndices = 0; foreach (int originalIndex in allPartitionArtifactIndicesList) { partitionArtifactIndexRemappingDictionary.Add(originalIndex, numPartitionIndices++); } // Copy the artifacts corresponding to the complete set of indices. var artifacts = new List <Artifact>(); foreach (int originalIndex in allPartitionArtifactIndicesList) { Artifact originalArtifact = originalRun.Artifacts[originalIndex]; Artifact partitionArtifact = deepClone ? originalArtifact.DeepClone() : new Artifact(originalArtifact); artifacts.Add(partitionArtifact); } if (artifacts.Any()) { partitionRun.Artifacts = artifacts; } partitionLog.Runs.Add(partitionRun); } } // Traverse the entire log, fixing the index mappings for indices that appear // in the remapping dictionaries. var remappingVisitor = new PartitionedIndexRemappingVisitor(artifactIndexRemappingDictionaries); remappingVisitor.VisitSarifLog(partitionLog); return(partitionLog); }
private static void SaveLogFile(string filePath, SarifLog log) { SaveLogFile(filePath, JsonConvert.SerializeObject(log)); }
// The sarifLog parameter contains exactly a set of results that are intended to be filed as a single work item // and this log will be attached to the work item. public SarifWorkItemModel(SarifLog sarifLog, SarifWorkItemContext context = null, Guid guid = default(Guid)) { if (sarifLog == null) { throw new ArgumentNullException(nameof(sarifLog)); } this.SarifLog = sarifLog; this.Context = context ?? new SarifWorkItemContext(); this.Guid = (guid == default(Guid)) ? Guid.NewGuid() : guid; var visitor = new ExtractAllArtifactLocationsVisitor(); visitor.VisitSarifLog(sarifLog); foreach (ArtifactLocation location in visitor.AllArtifactLocations) { if (location?.Uri != null) { LocationUris ??= new List <Uri>(); LocationUris.Add(location.Uri); } } // Shared GitHub/Azure DevOps concepts this.LabelsOrTags = new List <string>(); foreach (string tag in context.AdditionalTags) { this.LabelsOrTags.Add(tag); } // Note that we provide a simple file name here. The filers will // add a prefix to the file name that includes other details, // such as the id of the filed item. this.Attachment = new Microsoft.WorkItems.Attachment { Name = "ScanResults.sarif", Text = JsonConvert.SerializeObject(sarifLog, Formatting.Indented), }; this.Title = sarifLog.Runs?[0]?.CreateWorkItemTitle(this.Context.ShouldFileUnchanged); // TODO: Provide a useful SARIF-derived discussion entry // for the preliminary filing operation. // // https://github.com/microsoft/sarif-sdk/issues/1756 // this.CommentOrDiscussion = $"Default {nameof(this.CommentOrDiscussion)}"; string descriptionFooter = this.Context.DescriptionFooter; this.BodyOrDescription = Environment.NewLine + sarifLog.CreateWorkItemDescription(this.Context, LocationUris) + descriptionFooter; // These properties are Azure DevOps-specific. All ADO work item board // area paths are rooted by the project name, as are iterations. this.Area = this.RepositoryOrProject; this.Iteration = this.RepositoryOrProject; // Milestone is a shared concept between GitHub and AzureDevOps. For both // environments this field is an open-ended text field. As such, there is // no useful default. An empty string here will prompt filers to skip // updating this field. this.Milestone = string.Empty; // No defaults are provided for custom fields. This dictionary is used // to provide values for non-standard fields as defined in an Azure // DevOps work item template. Because this data by definition addresses // non-generalized needs, there are no useful defaults we can provide. // // this.CustomFields }
private static void Main(string[] args) { bool deferred = bool.Parse(args[0]); string filePath = args[1]; SarifLog log = null; Console.WriteLine($"Loading {filePath}{(deferred ? " (deferred)" : "")}..."); Measure(() => { // Use 'SarifDeferredContractResolver' and 'JsonPositionedTextReader' to load a deferred version of the same object graph. JsonSerializer serializer = new JsonSerializer(); if (deferred) { serializer.ContractResolver = SarifDeferredContractResolver.Instance; } using (JsonTextReader reader = (deferred ? new JsonPositionedTextReader(filePath) : new JsonTextReader(new StreamReader(filePath)))) { log = serializer.Deserialize <SarifLog>(reader); } return($"Loaded {filePath} ({(new FileInfo(filePath).Length / BytesPerMB):n1} MB)"); }); // Enumerate collections as normal. Enumeration is efficient. Indexing to items works, but is slower, as a file seek is required per item read. Run run = log.Runs[0]; Measure(() => { int messageLengthTotal = 0; // Fastest: Enumerate foreach (Result result in run.Results) { messageLengthTotal += result?.Message?.Text?.Length ?? 0; } // Slower: Count and indexer //int messageCount = run.Results.Count; //for (int i = 0; i < messageCount; ++i) //{ // Result result = run.Results[i]; // messageLengthTotal += result?.Message?.Text?.Length ?? 0; //} return($"Enumerated {run.Results.Count:n0} Results message total {messageLengthTotal / BytesPerMB:n0}MB"); }); Measure(() => { int fileCount = 0; int uriLengthTotal = 0; if (run.Artifacts != null) { // Fastest: Enumerate foreach (Artifact artifact in run.Artifacts) { uriLengthTotal += artifact?.Location?.Uri?.OriginalString?.Length ?? 0; fileCount++; } // Slower: Keys and indexer //foreach (var key in run.Files.Keys) //{ // FileData file = run.Files[key]; // uriLengthTotal += file?.ArtifactLocation?.Uri?.OriginalString?.Length ?? 0; // fileCount++; //} } return($"Enumerated {fileCount:n0} Files, URI total {uriLengthTotal / BytesPerMB:n0}MB."); }); GC.KeepAlive(log); }
public static bool AreSufficientlySimiliar(SarifLog baseline, string baselineResultGuid, SarifLog current, string currentResultGuid) { Result bResult = baseline.FindByGuid(baselineResultGuid); Result cResult = current.FindByGuid(currentResultGuid); if (bResult == null || cResult == null) { return(false); } ExtractedResult bExtractedResult = new ExtractedResult(bResult, bResult.Run); ExtractedResult cExtractedResult = new ExtractedResult(cResult, cResult.Run); bool outcome = bExtractedResult.IsSufficientlySimilarTo(cExtractedResult); return(outcome); }
public LogInSeries(SarifLog log, int logIndex, string filePath) { Log = log; LogIndex = logIndex; FilePath = filePath; }
private int WriteRunToErrorList(Run run, string logFilePath, SarifLog sarifLog) { if (!SarifViewerPackage.IsUnitTesting) { #pragma warning disable VSTHRD108 // Assert thread affinity unconditionally ThreadHelper.ThrowIfNotOnUIThread(); #pragma warning restore VSTHRD108 } int runIndex = CodeAnalysisResultManager.Instance.GetNextRunIndex(); var dataCache = new RunDataCache(runIndex, logFilePath, sarifLog); CodeAnalysisResultManager.Instance.RunIndexToRunDataCache.Add(runIndex, dataCache); CodeAnalysisResultManager.Instance.CacheUriBasePaths(run); var sarifErrors = new List <SarifErrorListItem>(); var dte = Package.GetGlobalService(typeof(DTE)) as DTE2; var projectNameCache = new ProjectNameCache(dte?.Solution); this.StoreFileDetails(run.Artifacts); if (run.Results != null) { foreach (Result result in run.Results) { result.Run = run; var sarifError = new SarifErrorListItem(run, runIndex, result, logFilePath, projectNameCache); sarifErrors.Add(sarifError); } } if (run.Invocations != null) { foreach (Invocation invocation in run.Invocations) { if (invocation.ToolConfigurationNotifications != null) { foreach (Notification configurationNotification in invocation.ToolConfigurationNotifications) { var sarifError = new SarifErrorListItem(run, runIndex, configurationNotification, logFilePath, projectNameCache); sarifErrors.Add(sarifError); } } if (invocation.ToolExecutionNotifications != null) { foreach (Notification toolNotification in invocation.ToolExecutionNotifications) { if (toolNotification.Level != FailureLevel.Note) { var sarifError = new SarifErrorListItem(run, runIndex, toolNotification, logFilePath, projectNameCache); sarifErrors.Add(sarifError); } } } } } if (run.HasAbsentResults()) { this.ShowFilteredCategoryColumn(); } if (run.HasSuppressedResults()) { this.ShowFilteredSuppressionStateColumn(); } (dataCache.SarifErrors as List <SarifErrorListItem>).AddRange(sarifErrors); SarifTableDataSource.Instance.AddErrors(sarifErrors); // This causes already open "text views" to be tagged when SARIF logs are processed after a view is opened. SarifLocationTagHelpers.RefreshTags(); return(sarifErrors.Count); }
internal static async Task ProcessSarifLogAsync(SarifLog sarifLog, string logFilePath, bool cleanErrors, bool openInEditor) { // The creation of the data models must be done on the UI thread (for now). // VS's table data source constructs are indeed thread safe. // The object model (which is eventually handed to WPF\XAML) could also // be constructed on any thread as well. // However the current implementation of the data model and // the "run data cache" have not been augmented to support this // and are not thread safe. // This work could be done in the future to do even less work on the UI // thread if needed. if (!SarifViewerPackage.IsUnitTesting) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); } // Clear previous data if (cleanErrors) { CleanAllErrors(); } bool hasResults = false; foreach (Run run in sarifLog.Runs) { // run.tool is required, add one if it's missing if (run.Tool == null) { run.Tool = new Tool { Driver = new ToolComponent { Name = Resources.UnknownToolName, }, }; } if (Instance.WriteRunToErrorList(run, logFilePath, sarifLog) > 0) { hasResults = true; } } if (openInEditor && !SarifViewerPackage.IsUnitTesting) { SdkUIUtilities.OpenDocument(ServiceProvider.GlobalProvider, logFilePath, usePreviewPane: false); } if (hasResults) { if (!SarifViewerPackage.IsUnitTesting) { // We cannot show UI during unit-tests. SdkUIUtilities.ShowToolWindowAsync(new Guid(ToolWindowGuids80.ErrorList), activate: false).FileAndForget(Constants.FileAndForgetFaultEventNames.ShowErrorList); } } SarifLogsMonitor.Instance.StartWatch(logFilePath); RaiseLogProcessed(ExceptionalConditionsCalculator.Calculate(sarifLog)); }
public static async Task ProcessLogFileCoreAsync(string filePath, string toolFormat, bool promptOnLogConversions, bool cleanErrors, bool openInEditor) { SarifLog log = null; string logText = null; string outputPath = null; bool saveOutputFile = true; if (toolFormat.MatchesToolFormat(ToolFormat.None)) { await RetryInvokeAsync( async() => { using (var logStreamReader = new StreamReader(filePath, Encoding.UTF8)) { logText = await logStreamReader.ReadToEndAsync().ConfigureAwait(continueOnCapturedContext: false); } }, retryInterval : TimeSpan.FromMilliseconds(300), maxAttemptCount : 5); Match match = MatchVersionProperty(logText); if (match.Success) { string inputVersion = match.Groups["version"].Value; if (inputVersion == SarifUtilities.V1_0_0) { // They're opening a v1 log, so we need to transform it. // Ask if they'd like to save the v2 log. MessageDialogCommand response = promptOnLogConversions ? await PromptToSaveProcessedLogAsync(Resources.TransformV1_DialogMessage).ConfigureAwait(continueOnCapturedContext: false) : MessageDialogCommand.No; if (response == MessageDialogCommand.Cancel) { return; } var settingsV1 = new JsonSerializerSettings() { ContractResolver = SarifContractResolverVersionOne.Instance, }; SarifLogVersionOne v1Log = JsonConvert.DeserializeObject <SarifLogVersionOne>(logText, settingsV1); var transformer = new SarifVersionOneToCurrentVisitor(); transformer.VisitSarifLogVersionOne(v1Log); log = transformer.SarifLog; if (response == MessageDialogCommand.Yes) { // Prompt for a location to save the transformed log. outputPath = await PromptForFileSaveLocationAsync(Resources.SaveTransformedV1Log_DialogTitle, filePath).ConfigureAwait(continueOnCapturedContext: false); if (string.IsNullOrEmpty(outputPath)) { return; } } logText = JsonConvert.SerializeObject(log); } else if (inputVersion != VersionConstants.StableSarifVersion) { // It's an older v2 version, so send it through the pre-release compat transformer. // Ask if they'd like to save the transformed log. MessageDialogCommand response = promptOnLogConversions ? await PromptToSaveProcessedLogAsync(string.Format(Resources.TransformPrereleaseV2_DialogMessage, VersionConstants.StableSarifVersion)).ConfigureAwait(continueOnCapturedContext: false) : MessageDialogCommand.No; if (response == MessageDialogCommand.Cancel) { return; } log = PrereleaseCompatibilityTransformer.UpdateToCurrentVersion(logText, Formatting.Indented, out logText); if (response == MessageDialogCommand.Yes) { // Prompt for a location to save the transformed log. outputPath = await PromptForFileSaveLocationAsync(Resources.SaveTransformedPrereleaseV2Log_DialogTitle, filePath).ConfigureAwait(continueOnCapturedContext: false); if (string.IsNullOrEmpty(outputPath)) { return; } } } else { // Since we didn't do any pre-processing, we don't need to write to a temp location. outputPath = filePath; saveOutputFile = false; } } else { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); // The version property wasn't found within the first 100 characters. // Per the spec, it should appear first in the sarifLog object. VsShellUtilities.ShowMessageBox(ServiceProvider.GlobalProvider, Resources.VersionPropertyNotFound_DialogTitle, null, // title OLEMSGICON.OLEMSGICON_QUERY, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); return; } } else { // They're opening a non-SARIF log, so we need to convert it. // Ask if they'd like to save the converted log. MessageDialogCommand response = promptOnLogConversions ? await PromptToSaveProcessedLogAsync(Resources.ConvertNonSarifLog_DialogMessage).ConfigureAwait(continueOnCapturedContext: false) : MessageDialogCommand.No; if (response == MessageDialogCommand.Cancel) { return; } // The converter doesn't have async methods, so spin // up a task to do this. await System.Threading.Tasks.Task.Run(() => { var sb = new StringBuilder(); using (FileStream fileStream = File.OpenRead(filePath)) { using (var outputTextWriter = new StringWriter(sb)) using (var outputJson = new JsonTextWriter(outputTextWriter)) using (var output = new ResultLogJsonWriter(outputJson)) { var converter = new ToolFormatConverter(); converter.ConvertToStandardFormat(toolFormat, fileStream, output); } logText = sb.ToString(); if (response == MessageDialogCommand.Yes) { // Prompt for a location to save the converted log. outputPath = PromptForFileSaveLocationAsync(Resources.SaveConvertedLog_DialogTitle, filePath).Result; } } }).ConfigureAwait(continueOnCapturedContext: false); } if (string.IsNullOrEmpty(outputPath)) { outputPath = Path.GetTempFileName() + ".sarif"; } if (saveOutputFile) { await SaveLogFileAsync(outputPath, logText).ConfigureAwait(continueOnCapturedContext: false); } if (log == null) { log = JsonConvert.DeserializeObject <SarifLog>(logText); } await ProcessSarifLogAsync(log, outputPath, cleanErrors : cleanErrors, openInEditor : openInEditor).ConfigureAwait(continueOnCapturedContext: false); }
protected override string ConstructTestOutputFromInputResource(string inputResourceName, object parameter) { string v2LogText = GetResourceText(inputResourceName); string inputLogDirectory = this.OutputFolderPath; string inputLogFileName = Path.GetFileName(inputResourceName); string inputLogFilePath = Path.Combine(this.OutputFolderPath, inputLogFileName); string actualLogFilePath = Guid.NewGuid().ToString(); string ruleUnderTest = Path.GetFileNameWithoutExtension(inputLogFilePath).Split('.')[1]; // All SARIF rule prefixes require update to current release. // All rules with JSON prefix are low level syntax/deserialization checks. // We can't transform these test inputs as that operation fixes up errors in the file. // Also, don't transform the tests for SARIF1011 or SARIF2008, because these rules // examine the actual contents of the $schema property. string[] shouldNotTransform = { "SARIF1011", "SARIF2008" }; bool updateInputsToCurrentSarif = IsSarifRule(ruleUnderTest) && !shouldNotTransform.Contains(ruleUnderTest); var validateOptions = new ValidateOptions { SarifOutputVersion = SarifVersion.Current, TargetFileSpecifiers = new[] { inputLogFilePath }, OutputFilePath = actualLogFilePath, Quiet = true, UpdateInputsToCurrentSarif = updateInputsToCurrentSarif, PrettyPrint = true, Optimize = true, Verbose = true // Turn on note-level rules. }; var mockFileSystem = new Mock <IFileSystem>(); mockFileSystem.Setup(x => x.DirectoryExists(inputLogDirectory)).Returns(true); mockFileSystem.Setup(x => x.DirectoryGetDirectories(It.IsAny <string>())).Returns(new string[0]); mockFileSystem.Setup(x => x.DirectoryGetFiles(inputLogDirectory, inputLogFileName)).Returns(new string[] { inputLogFilePath }); mockFileSystem.Setup(x => x.FileReadAllText(inputLogFilePath)).Returns(v2LogText); mockFileSystem.Setup(x => x.FileReadAllText(It.IsNotIn <string>(inputLogFilePath))).Returns <string>(path => File.ReadAllText(path)); mockFileSystem.Setup(x => x.FileWriteAllText(It.IsAny <string>(), It.IsAny <string>())); // Some rules are disabled by default, so create a configuration file that explicitly // enables the rule under test. using (TempFile configFile = CreateTempConfigFile(ruleUnderTest, parameter)) { validateOptions.ConfigurationFilePath = configFile.Name; mockFileSystem.Setup(x => x.FileExists(validateOptions.ConfigurationFilePath)).Returns(true); var validateCommand = new ValidateCommand(mockFileSystem.Object); int returnCode = validateCommand.Run(validateOptions); if (validateCommand.ExecutionException != null) { Console.WriteLine(validateCommand.ExecutionException.ToString()); } returnCode.Should().Be(0); } string actualLogFileContents = File.ReadAllText(actualLogFilePath); SarifLog actualLog = JsonConvert.DeserializeObject <SarifLog>(actualLogFileContents); Run run = actualLog.Runs[0]; // First, we'll strip any validation results that don't originate with the rule under test. // But leave the results that originate from JSchema! Also, be careful because output files // from "valid" test cases don't have any results. run.Results = run.Results ?.Where(r => IsRelevant(r.RuleId, ruleUnderTest)) ?.ToList(); // Next, remove any rule metadata for those rules. The output files from "valid" test // cases don't have any rules. run.Tool.Driver.Rules = run.Tool.Driver.Rules ?.Where(r => IsRelevant(r.Id, ruleUnderTest)) ?.ToList(); // Since there's only one rule in the metadata, the ruleIndex for all remaining results // must be 0. foreach (Result result in run.Results) { result.RuleIndex = 0; } // Next, we'll remove non-deterministic information, most notably, timestamps emitted for the invocation data. var removeTimestampsVisitor = new RemoveOptionalDataVisitor(OptionallyEmittedData.NondeterministicProperties); removeTimestampsVisitor.Visit(actualLog); // Finally, we'll elide non-deterministic build root details var rebaseUrisVisitor = new RebaseUriVisitor("TEST_DIR", new Uri(inputLogDirectory)); rebaseUrisVisitor.Visit(actualLog); // There are differences in log file output depending on whether we are invoking xunit // from within Visual Studio or at the command-line via xunit.exe. We elide these differences. ToolComponent driver = actualLog.Runs[0].Tool.Driver; driver.Name = "SARIF Functional Testing"; driver.Version = null; driver.FullName = null; driver.SemanticVersion = null; driver.DottedQuadFileVersion = null; driver.Product = null; driver.Organization = null; driver.Properties?.Clear(); actualLog.Runs[0].OriginalUriBaseIds = null; return(JsonConvert.SerializeObject(actualLog, Formatting.Indented)); }
public static void ProcessLogFile(string filePath, Solution solution, string toolFormat = ToolFormat.None, bool promptOnLogConversions = true) { SarifLog log = null; string logText; string outputPath = null; bool saveOutputFile = true; if (toolFormat.MatchesToolFormat(ToolFormat.None)) { logText = File.ReadAllText(filePath); Match match = MatchVersionProperty(logText); if (match.Success) { string inputVersion = match.Groups["version"].Value; if (inputVersion == SarifUtilities.V1_0_0) { // They're opening a v1 log, so we need to transform it. // Ask if they'd like to save the v2 log. MessageDialogCommand response = promptOnLogConversions ? PromptToSaveProcessedLog(Resources.TransformV1_DialogMessage) : MessageDialogCommand.No; if (response == MessageDialogCommand.Cancel) { return; } JsonSerializerSettings settingsV1 = new JsonSerializerSettings() { ContractResolver = SarifContractResolverVersionOne.Instance }; SarifLogVersionOne v1Log = JsonConvert.DeserializeObject <SarifLogVersionOne>(logText, settingsV1); var transformer = new SarifVersionOneToCurrentVisitor(); transformer.VisitSarifLogVersionOne(v1Log); log = transformer.SarifLog; if (response == MessageDialogCommand.Yes) { // Prompt for a location to save the transformed log. outputPath = PromptForFileSaveLocation(Resources.SaveTransformedV1Log_DialogTitle, filePath); if (string.IsNullOrEmpty(outputPath)) { return; } } logText = JsonConvert.SerializeObject(log); } else if (inputVersion != VersionConstants.StableSarifVersion) { // It's an older v2 version, so send it through the pre-release compat transformer. // Ask if they'd like to save the transformed log. MessageDialogCommand response = promptOnLogConversions ? PromptToSaveProcessedLog(string.Format(Resources.TransformPrereleaseV2_DialogMessage, VersionConstants.StableSarifVersion)) : MessageDialogCommand.No; if (response == MessageDialogCommand.Cancel) { return; } log = PrereleaseCompatibilityTransformer.UpdateToCurrentVersion(logText, Formatting.Indented, out logText); if (response == MessageDialogCommand.Yes) { // Prompt for a location to save the transformed log. outputPath = PromptForFileSaveLocation(Resources.SaveTransformedPrereleaseV2Log_DialogTitle, filePath); if (string.IsNullOrEmpty(outputPath)) { return; } } } else { // Since we didn't do any pre-processing, we don't need to write to a temp location. outputPath = filePath; saveOutputFile = false; } } else { // The version property wasn't found within the first 100 characters. // Per the spec, it should appear first in the sarifLog object. VsShellUtilities.ShowMessageBox(SarifViewerPackage.ServiceProvider, Resources.VersionPropertyNotFound_DialogTitle, null, // title OLEMSGICON.OLEMSGICON_QUERY, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); return; } } else { // They're opening a non-SARIF log, so we need to convert it. // Ask if they'd like to save the converted log. MessageDialogCommand response = promptOnLogConversions ? PromptToSaveProcessedLog(Resources.ConvertNonSarifLog_DialogMessage) : MessageDialogCommand.No; if (response == MessageDialogCommand.Cancel) { return; } var converter = new ToolFormatConverter(); var sb = new StringBuilder(); using (var input = new MemoryStream(File.ReadAllBytes(filePath))) { var outputTextWriter = new StringWriter(sb); var outputJson = new JsonTextWriter(outputTextWriter); var output = new ResultLogJsonWriter(outputJson); input.Seek(0, SeekOrigin.Begin); converter.ConvertToStandardFormat(toolFormat, input, output); // This is serving as a flush mechanism. output.Dispose(); logText = sb.ToString(); if (response == MessageDialogCommand.Yes) { // Prompt for a location to save the converted log. outputPath = PromptForFileSaveLocation(Resources.SaveConvertedLog_DialogTitle, filePath); } } } if (string.IsNullOrEmpty(outputPath)) { outputPath = Path.GetTempFileName() + ".sarif"; } if (saveOutputFile) { SaveLogFile(outputPath, logText); } if (log == null) { log = JsonConvert.DeserializeObject <SarifLog>(logText); } ProcessSarifLog(log, outputPath, solution, showMessageOnNoResults: promptOnLogConversions); SarifTableDataSource.Instance.BringToFront(); }
protected override void Analyze(SarifLog log, string logPointer) { AnalyzeSchema(log.SchemaUri, logPointer); }
private SarifLog CreatePartitionLog(T partitionValue) { SarifLog partitionLog; if (deepClone) { // Save time and space by not cloning the runs unless and until necessary. We will // only need to clone the runs that have results in this partition. IList <Run> originalRuns = originalLog.Runs; originalLog.Runs = null; partitionLog = originalLog.DeepClone(); originalLog.Runs = originalRuns; } else { partitionLog = new SarifLog(originalLog.SchemaUri, originalLog.Version, runs: null, originalLog.InlineExternalProperties, originalLog.Properties); } partitionLog.Runs = new List <Run>(); partitionLog.SetProperty(PartitionValuePropertyName, partitionValue); var artifactIndexRemappingDictionaries = new List <Dictionary <int, int> >(); for (int iOriginalRun = 0; iOriginalRun < partitionRunInfos.Count; ++iOriginalRun) { // Are there results for this run in this partition? if (partitionRunInfos[iOriginalRun].ResultDictionary.TryGetValue(partitionValue, out List <Result> results)) { // Yes, so we'll need a copy of the original run in which the results, and // certain run-level collections such as Run.Artifacts, have been replaced. Run originalRun = originalLog.Runs[iOriginalRun]; Run partitionRun; if (deepClone) { // Save time and space by only cloning the necessary results and associated // collection elements. We already cloned the relevant results in VisitResult. IList <Result> originalResults = originalRun.Results; IList <Artifact> originalArtifacts = originalRun.Artifacts; originalRun.Results = null; originalRun.Artifacts = null; partitionRun = originalRun.DeepClone(); originalRun.Results = originalResults; originalRun.Artifacts = originalArtifacts; } else { partitionRun = new Run(originalRun.Tool, originalRun.Invocations, originalRun.Conversion, originalRun.Language, originalRun.VersionControlProvenance, originalRun.OriginalUriBaseIds, artifacts: null, originalRun.LogicalLocations, originalRun.Graphs, results: null, originalRun.AutomationDetails, originalRun.RunAggregates, originalRun.BaselineGuid, originalRun.RedactionTokens, originalRun.DefaultEncoding, originalRun.DefaultSourceLanguage, originalRun.NewlineSequences, originalRun.ColumnKind, originalRun.ExternalPropertyFileReferences, originalRun.ThreadFlowLocations, originalRun.Taxonomies, originalRun.Addresses, originalRun.Translations, originalRun.Policies, originalRun.WebRequests, originalRun.WebResponses, originalRun.SpecialLocations, originalRun.Properties); } partitionRun.Results = results; // Construct a mapping from the indices in the original run to the indices // in the partition run. This includes both the indices relevant to the // results in this partition, and indices that appear in all partitions // because they are mentioned outside of any result (we refer to these as // "global" indices). IEnumerable <int> allPartitionArtifactIndices = partitionRunInfos[iOriginalRun].GlobalArtifactIndices; if (partitionRunInfos[iOriginalRun].ArtifactIndicesDictionary.TryGetValue(partitionValue, out HashSet <int> partitionResultArtifactIndices)) { allPartitionArtifactIndices = allPartitionArtifactIndices.Union(partitionResultArtifactIndices); } var partitionArtifactIndexRemappingDictionary = new Dictionary <int, int>(); artifactIndexRemappingDictionaries.Add(partitionArtifactIndexRemappingDictionary); List <int> allPartitionArtifactIndicesList = allPartitionArtifactIndices .OrderBy(index => index) .ToList(); int numPartitionIndices = 0; foreach (int originalIndex in allPartitionArtifactIndicesList) { partitionArtifactIndexRemappingDictionary.Add(originalIndex, numPartitionIndices++); } // Copy the artifacts corresponding to the complete set of indices. var artifacts = new List <Artifact>(); foreach (int originalIndex in allPartitionArtifactIndicesList) { Artifact originalArtifact = originalRun.Artifacts[originalIndex]; Artifact partitionArtifact = deepClone ? originalArtifact.DeepClone() : new Artifact(originalArtifact); artifacts.Add(partitionArtifact); } if (artifacts.Any()) { partitionRun.Artifacts = artifacts; } partitionLog.Runs.Add(partitionRun); } } // Traverse the entire log, fixing the index mappings for indices that appear // in the remapping dictionaries. var remappingVisitor = new PartitionedIndexRemappingVisitor(artifactIndexRemappingDictionaries); remappingVisitor.VisitSarifLog(partitionLog); return(partitionLog); }
protected override void Analyze(SarifLog log, string logPointer) { AnalyzeUri(log.SchemaUri, logPointer.AtProperty(SarifPropertyName.Schema)); }
private void TestWorkItemFiler(SarifLog sarifLog, SarifWorkItemContext context, bool adoClient) { // ONE. Create test data that the low-level ADO client mocks // will flow back to the SARIF work item filer. var attachmentReference = new AttachmentReference() { Id = Guid.NewGuid(), Url = Guid.NewGuid().ToString() }; var workItem = new WorkItem { Id = DateTime.UtcNow.Millisecond, Links = new ReferenceLinks() }; // The fake URI to the filed item that we'll expect the filer to receive string bugUriText = "https://example.com/" + Guid.NewGuid().ToString(); string bugHtmlUriText = "https://example.com/" + Guid.NewGuid().ToString(); Uri bugUri = new Uri(bugUriText, UriKind.RelativeOrAbsolute); Uri bugHtmlUri = new Uri(bugHtmlUriText, UriKind.RelativeOrAbsolute); workItem.Url = bugUriText; workItem.Links.AddLink("html", bugHtmlUriText); // TWO. Reset variables to capture whether we enter all expected client methods. ConnectCalled = false; CreateWorkItemCalled = CreateAttachmentCount = UpdateIssueCount = 0; // THREE. Create a default mock SARIF filer and client. SarifWorkItemFiler filer = CreateMockSarifWorkItemFiler(context).Object; // FOUR. Based on which client we are using (ADO or GitHub), create the correct context. // This implies created both the connection mocks and the mocks for filing, updating, and attaching work items. // We are required to put this mock behind an interface due to an inability to mock these types directly. FilingClient filingClient; if (adoClient == true) { filingClient = CreateAdoMocksAndFilingClient(attachmentReference, workItem, filer); } else { filingClient = CreateGitHubMocksAndFilingClient(bugUriText, bugHtmlUriText, filer); } string sarifLogText = JsonConvert.SerializeObject(sarifLog); SarifLog updatedSarifLog = filer.FileWorkItems(sarifLogText); // Did we see all the execution we expected? ConnectCalled.Should().BeTrue(); int expectedWorkItemsCount = context.GetProperty(ExpectedWorkItemsCount); CreateWorkItemCalled.Should().Be(expectedWorkItemsCount); CreateAttachmentCount.Should().Be(adoClient ? expectedWorkItemsCount : 0); // This property is a naive mechanism to ensure that the code // executed comprehensively (i.e., that execution was not limited // due to unhandled exceptions). This is required because we have // not really implemented a proper async API with appropriate handling // for exceptions and other negative conditions. I wouldn't expect this // little helper to survive but it closes the loop for the current // rudimentary in-flight implementation. filer.FilingResult.Should().Be(FilingResult.Succeeded); filer.FiledWorkItems.Count.Should().Be(expectedWorkItemsCount); foreach (WorkItemModel filedWorkItem in filer.FiledWorkItems) { // Finally, make sure that our test data flows back properly through the filer. filedWorkItem.Attachment.Should().NotBeNull(); JsonConvert.SerializeObject(filedWorkItem.Attachment.Text).Should().NotBeNull(); filedWorkItem.Uri.Should().Be(bugUri); filedWorkItem.HtmlUri.Should().Be(bugHtmlUri); } // Validate that we updated the SARIF log with work itme URIs. // updatedSarifLog.Should().NotBeEquivalentTo(sarifLog); foreach (Run run in updatedSarifLog.Runs) { foreach (Result result in run.Results) { result.WorkItemUris.Should().NotBeNull(); result.WorkItemUris.Count.Should().Be(1); result.WorkItemUris[0].Should().Be(bugHtmlUri); result.TryGetProperty(SarifWorkItemFiler.PROGRAMMABLE_URIS_PROPERTY_NAME, out List <Uri> programmableUris) .Should().BeTrue(); programmableUris.Should().NotBeNull(); programmableUris.Count.Should().Be(1); programmableUris[0].Should().Be(bugUri); } } }
private static void WriteResultToSarifLog(string inputPath, StreamWriter outputStream, SarifLog log, MethodDefinition moveNextMethod, DependencyPTGDomain depAnalysisResult, SongTaoDependencyAnalysis dependencyAnalysis, IDictionary <string, ClassDefinition> processorMap) { var results = new List <Result>(); var escapes = depAnalysisResult.Dependencies.A1_Escaping.Select(traceable => traceable.ToString()); var inputUses = new HashSet <Traceable>(); var outputModifies = new HashSet <Traceable>(); if (!depAnalysisResult.IsTop) { if (depAnalysisResult.Dependencies.A4_Ouput.Any()) { var outColumnMap = new MapSet <TraceableColumn, Traceable>(); var outColumnControlMap = new MapSet <TraceableColumn, Traceable>(); foreach (var outColum in depAnalysisResult.Dependencies.A4_Ouput.Keys) { var outColumns = depAnalysisResult.GetTraceables(outColum).OfType <TraceableColumn>() .Where(t => t.TableKind == ProtectedRowKind.Output); foreach (var column in outColumns) { if (!outColumnMap.ContainsKey(column)) { outColumnMap.AddRange(column, depAnalysisResult.Dependencies.A4_Ouput[outColum]); } else { outColumnMap.AddRange(column, outColumnMap[column].Union(depAnalysisResult.Dependencies.A4_Ouput[outColum])); } if (!outColumnControlMap.ContainsKey(column)) { outColumnControlMap.AddRange(column, depAnalysisResult.Dependencies.A4_Ouput_Control[outColum]); } else { outColumnControlMap.AddRange(column, outColumnControlMap[column].Union(depAnalysisResult.Dependencies.A4_Ouput_Control[outColum])); } } } foreach (var entryOutput in outColumnMap) { var result = new Result(); result.Id = "SingleColumn"; var column = entryOutput.Key; var columnString = column.ToString(); var dependsOn = entryOutput.Value; var controlDepends = new HashSet <Traceable>(); result.SetProperty("column", columnString); result.SetProperty("data depends", dependsOn.Select(traceable => traceable.ToString())); if (outColumnControlMap.ContainsKey(column)) { var controlDependsOn = outColumnControlMap[column]; result.SetProperty("control depends", controlDependsOn.Select(traceable => traceable.ToString())); } else { result.SetProperty("control depends", new string[] { }); } result.SetProperty("escapes", escapes); results.Add(result); inputUses.AddRange(dependsOn.Where(t => t.TableKind == ProtectedRowKind.Input)); outputModifies.Add(column); } //foreach (var outColum in depAnalysisResult.Dependencies.A4_Ouput.Keys) //{ // //var outColumns = depAnalysisResult.Dependencies.A2_Variables[outColum].OfType<TraceableColumn>() // // .Where(t => t.TableKind == ProtectedRowKind.Output); // var outColumns = depAnalysisResult.GetTraceables(outColum).OfType<TraceableColumn>() // .Where(t => t.TableKind == ProtectedRowKind.Output); // foreach (var column in outColumns) // { // var result = new Result(); // result.Id = "SingleColumn"; // var columnString = column.ToString(); // var dependsOn = depAnalysisResult.Dependencies.A4_Ouput[outColum]; // //dependsOn.AddRange(traceables); // result.SetProperty("column", columnString); // result.SetProperty("data depends", dependsOn.Select(traceable => traceable.ToString())); // if (depAnalysisResult.Dependencies.A4_Ouput_Control.ContainsKey(outColum)) // { // var controlDependsOn = depAnalysisResult.Dependencies.A4_Ouput_Control[outColum]; // result.SetProperty("control depends", controlDependsOn.Where(t => !(t is Other)).Select(traceable => traceable.ToString())); // inputUses.AddRange(controlDependsOn.Where(t => t.TableKind == ProtectedRowKind.Input)); // } // else // { // result.SetProperty("control depends", new string[] { }); // } // result.SetProperty("escapes", escapes); // results.Add(result); // inputUses.AddRange(dependsOn.Where(t => t.TableKind == ProtectedRowKind.Input)); // outputModifies.Add(column); // } //} } else { var result = new Result(); result.Id = "SingleColumn"; result.SetProperty("column", "_EMPTY_"); result.SetProperty("escapes", escapes); results.Add(result); } var resultSummary = new Result(); resultSummary.Id = "Summary"; //var inputsString = inputUses.OfType<TraceableColumn>().Select(t => t.ToString()); //var outputsString = outputModifies.OfType<TraceableColumn>().Select(t => t.ToString()); //result2.SetProperty("Inputs", inputsString); //result2.SetProperty("Outputs", outputsString); var inputsString = dependencyAnalysis.InputColumns.Select(t => t.ToString()); var outputsString = dependencyAnalysis.OutputColumns.Select(t => t.ToString()); resultSummary.SetProperty("Inputs", inputsString); resultSummary.SetProperty("Outputs", outputsString); var inputSchemaString = InputSchema.Columns.Select(t => t.ToString()); var outputSchemaString = OutputSchema.Columns.Select(t => t.ToString()); resultSummary.SetProperty("SchemaInputs", inputSchemaString); resultSummary.SetProperty("SchemaOutputs", outputSchemaString); results.Add(resultSummary); if (outputStream != null) { outputStream.WriteLine("Class: [{0}] {1}", moveNextMethod.ContainingType.FullPathName(), moveNextMethod.ToSignatureString()); if (depAnalysisResult.IsTop) { outputStream.WriteLine("Analysis returns TOP"); } outputStream.WriteLine("Inputs: {0}", String.Join(", ", inputsString)); outputStream.WriteLine("Outputs: {0}", String.Join(", ", outputsString)); } } else { var result = new Result(); result.Id = "SingleColumn"; result.SetProperty("column", "_TOP_"); result.SetProperty("depends", "_TOP_"); results.Add(result); var resultEmpty = new Result(); resultEmpty.Id = "Summary"; resultEmpty.SetProperty("Inputs", "_TOP_"); resultEmpty.SetProperty("Outputs", "_TOP_"); resultEmpty.SetProperty("SchemaInputs", "_TOP_"); resultEmpty.SetProperty("SchemaOutputs", "_TOP_"); results.Add(resultEmpty); } var id = String.Format("[{0}] {1}", moveNextMethod.ContainingType.FullPathName(), moveNextMethod.ToSignatureString()); // Very clumsy way to find the process number and the processor name from the MoveNext method. // But it is the process number and processor name that allow us to link these results to the information // in the XML file that describes the job. // Climb the containing type chain from the MoveNext method until we find the entry in the dictionary whose // value matches one of the classes. var done = false; foreach (var kv in processorMap) { if (done) { break; } var c = moveNextMethod.ContainingType as ClassDefinition; while (c != null) { if (kv.Value == c) { id = kv.Value.Name + "|" + kv.Key; done = true; break; } c = c.ContainingType as ClassDefinition; } } AddRunToSarifOutput(log, inputPath, id, results); }
public int Run(ResultMatchSetOptions options) { int returnCode = SUCCESS; options.OutputFolderPath = options.OutputFolderPath ?? Path.Combine(options.FolderPath, "Out"); ISarifLogMatcher matcher = ResultMatchingBaselinerFactory.GetDefaultResultMatchingBaseliner(); Formatting formatting = options.PrettyPrint ? Formatting.Indented : Formatting.None; // Remove previous results. if (_fileSystem.DirectoryExists(options.OutputFolderPath) && options.Force) { _fileSystem.DeleteDirectory(options.OutputFolderPath, true); } // Create output folder. _fileSystem.CreateDirectory(options.OutputFolderPath); string previousFileName = ""; string previousGroup = ""; SarifLog previousLog = null, currentLog = null; foreach (string filePath in Directory.GetFiles(options.FolderPath, "*.sarif")) { string fileName = Path.GetFileName(filePath); string currentGroup = GetGroupName(fileName); try { currentLog = ReadSarifFile <SarifLog>(_fileSystem, filePath); // Compare each log with the previous one in the same group. if (currentGroup.Equals(previousGroup) && currentLog?.Runs?[0]?.Results.Count != 0 && previousLog?.Runs?[0]?.Results.Count != 0) { Console.WriteLine(); Console.WriteLine($"{previousFileName} -> {fileName}:"); SarifLog mergedLog = matcher.Match(new[] { previousLog }, new[] { currentLog }).First(); // Write the same and different count and different IDs. WriteDifferences(mergedLog); // Write the log, if there were any changed results if (mergedLog.Runs[0].Results.Any(r => r.BaselineState != BaselineState.Unchanged)) { string outputFilePath = Path.Combine(options.OutputFolderPath, fileName); if (DriverUtilities.ReportWhetherOutputFileCanBeCreated(outputFilePath, options.Force, _fileSystem)) { WriteSarifFile(_fileSystem, mergedLog, outputFilePath, formatting); } else { returnCode = FAILURE; } } } } catch (Exception ex) when(!Debugger.IsAttached) { Console.WriteLine(ex.ToString()); returnCode = FAILURE; } previousFileName = fileName; previousGroup = currentGroup; previousLog = currentLog; } return(returnCode); }
public OverallBaseliningTests() { SampleLog = GetLogFromResource(SampleFilePath); }
public FeedbackModel(string ruleId, string toolName, string toolVersion, IEnumerable <string> snippets, FeedbackType feedbackType, string summary, SarifLog log) { this.RuleId = ruleId; this.ToolName = toolName; this.ToolVersion = toolVersion; this.SendSnippet = true; this.Snippets = snippets; this.Comment = string.Empty; this.FeedbackType = feedbackType; this.Summary = summary; this.SarifLog = log; }
public void Overall_Identical() { SarifLog output = Baseline(SampleLog.DeepClone(), SampleLog.DeepClone()); output.Runs[0].Results.Where(result => result.BaselineState != BaselineState.Unchanged).Should().BeEmpty(); }
protected virtual void Analyze(SarifLog log, string logPointer) { }
protected override string ConstructTestOutputFromInputResource(string inputResourceName, object parameter) { PrereleaseCompatibilityTransformer.UpdateToCurrentVersion( GetResourceText(inputResourceName), formatting: Formatting.Indented, out string transformedLog); SarifLog actualLog = PrereleaseCompatibilityTransformer.UpdateToCurrentVersion(transformedLog, formatting: Formatting.None, out transformedLog); // For CoreTests only - this code rewrites the log persisted URI to match the test environment if (inputResourceName == "Inputs.CoreTests-Relative.sarif") { Uri originalUri = actualLog.Runs[0].OriginalUriBaseIds["TESTROOT"].Uri; string uriString = originalUri.ToString(); string currentDirectory = Environment.CurrentDirectory; currentDirectory = currentDirectory.Substring(0, currentDirectory.IndexOf(@"\bld\")); uriString = uriString.Replace("REPLACED_AT_TEST_RUNTIME", currentDirectory); actualLog.Runs[0].OriginalUriBaseIds["TESTROOT"] = new ArtifactLocation { Uri = new Uri(uriString, UriKind.Absolute) }; var visitor = new InsertOptionalDataVisitor(_currentOptionallyEmittedData); visitor.Visit(actualLog.Runs[0]); // Restore the remanufactured URI so that file diffing matches actualLog.Runs[0].OriginalUriBaseIds["TESTROOT"] = new ArtifactLocation { Uri = originalUri }; } else if (inputResourceName == "Inputs.CoreTests-Absolute.sarif") { Uri originalUri = actualLog.Runs[0].Artifacts[0].Location.Uri; string uriString = originalUri.ToString(); string currentDirectory = Environment.CurrentDirectory; currentDirectory = currentDirectory.Substring(0, currentDirectory.IndexOf(@"\bld\")); uriString = uriString.Replace("REPLACED_AT_TEST_RUNTIME", currentDirectory); actualLog.Runs[0].Artifacts[0].Location = new ArtifactLocation { Uri = new Uri(uriString, UriKind.Absolute) }; var visitor = new InsertOptionalDataVisitor(_currentOptionallyEmittedData); visitor.Visit(actualLog.Runs[0]); // Restore the remanufactured URI so that file diffing matches actualLog.Runs[0].Artifacts[0].Location = new ArtifactLocation { Uri = originalUri }; } else { var visitor = new InsertOptionalDataVisitor(_currentOptionallyEmittedData); visitor.Visit(actualLog.Runs[0]); } // Verify and remove Guids, because they'll vary with every run and can't be compared to a fixed expected output if (_currentOptionallyEmittedData.HasFlag(OptionallyEmittedData.Guids)) { for (int i = 0; i < actualLog.Runs[0].Results.Count; ++i) { Result result = actualLog.Runs[0].Results[i]; if (String.IsNullOrEmpty(result.Guid)) { Assert.True(false, $"Results[{i}] had no Guid assigned, but OptionallyEmittedData.Guids flag was set."); } result.Guid = null; } } return(JsonConvert.SerializeObject(actualLog, Formatting.Indented)); }
public int Run(TransformOptions transformOptions) { try { if (transformOptions.SarifOutputVersion != SarifVersion.OneZeroZero && transformOptions.SarifOutputVersion != SarifVersion.Current) { Console.WriteLine(MultitoolResources.ErrorInvalidTransformTargetVersion); return(1); } OptionallyEmittedData dataToInsert = transformOptions.DataToInsert.ToFlags(); // NOTE: we don't actually utilize the dataToInsert command-line data yet... string fileName = CommandUtilities.GetTransformedOutputFileName(transformOptions); var formatting = transformOptions.PrettyPrint ? Formatting.Indented : Formatting.None; string inputFilePath = transformOptions.InputFilePath; string inputVersion = SniffVersion(inputFilePath); // If the user wants to transform to current v2, we check to see whether the input // file is v2 or pre-release v2. We upgrade both formats to current v2. // // Correspondingly, if the input file is v2 of any kind, we first ensure that it is // current v2, then drop it down to v1. // // We do not support transforming to any obsoleted pre-release v2 formats. if (transformOptions.SarifOutputVersion == SarifVersion.Current) { if (inputVersion == "1.0.0") { SarifLogVersionOne actualLog = ReadSarifFile <SarifLogVersionOne>(_fileSystem, transformOptions.InputFilePath, SarifContractResolverVersionOne.Instance); var visitor = new SarifVersionOneToCurrentVisitor(); visitor.VisitSarifLogVersionOne(actualLog); WriteSarifFile(_fileSystem, visitor.SarifLog, fileName, formatting); } else { // We have a pre-release v2 file that we should upgrade to current. PrereleaseCompatibilityTransformer.UpdateToCurrentVersion( _fileSystem.ReadAllText(inputFilePath), formatting: formatting, out string sarifText); _fileSystem.WriteAllText(fileName, sarifText); } } else { if (inputVersion == "1.0.0") { SarifLogVersionOne logV1 = ReadSarifFile <SarifLogVersionOne>(_fileSystem, transformOptions.InputFilePath, SarifContractResolverVersionOne.Instance); logV1.SchemaUri = SarifVersion.OneZeroZero.ConvertToSchemaUri(); WriteSarifFile(_fileSystem, logV1, fileName, formatting, SarifContractResolverVersionOne.Instance); } else { string currentSarifVersion = SarifUtilities.StableSarifVersion; string sarifText = _fileSystem.ReadAllText(inputFilePath); SarifLog actualLog = null; if (inputVersion != currentSarifVersion) { // Note that we don't provide formatting here. It is not required to indent the v2 SARIF - it // will be transformed to v1 later, where we should apply the indentation settings. actualLog = PrereleaseCompatibilityTransformer.UpdateToCurrentVersion( sarifText, formatting: Formatting.None, out sarifText); } else { actualLog = JsonConvert.DeserializeObject <SarifLog>(sarifText); } var visitor = new SarifCurrentToVersionOneVisitor(); visitor.VisitSarifLog(actualLog); WriteSarifFile(_fileSystem, visitor.SarifLogVersionOne, fileName, formatting, SarifContractResolverVersionOne.Instance); } } } catch (Exception ex) { Console.WriteLine(ex); return(1); } return(0); }
/// <summary> /// Helper function that accepts a single baseline and current SARIF log and matches them. /// </summary> /// <param name="previousLog">Array of SARIF logs representing the baseline run</param> /// <param name="currentLog">Array of SARIF logs representing the current run</param> /// <returns>A SARIF log with the merged set of results.</returns> public SarifLog Match(SarifLog previousLog, SarifLog currentLog) { return(Match(previousLogs: new[] { previousLog }, currentLogs: new[] { currentLog }).FirstOrDefault()); }
public void ExtractAllArtifactLocationsVisitor_ExtractsMultipleLocations() { var sarifLog = new SarifLog { Runs = new[] { new Run { Results = new[] { new Result { RuleId = TestData.RuleIds.Rule1, BaselineState = BaselineState.New, Locations = new [] { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(TestData.FileLocations.Location1), } } } } }, new Result { RuleId = TestData.RuleIds.Rule2, BaselineState = BaselineState.Updated, Locations = new [] { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(TestData.FileLocations.Location2), } } } } }, new Result { RuleId = TestData.RuleIds.Rule2, BaselineState = BaselineState.New, Locations = new [] { new Location { PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = new Uri(TestData.FileLocations.Location3), } } } } } } } } }; var visitor = new ExtractAllArtifactLocationsVisitor(); visitor.VisitSarifLog(sarifLog); visitor.AllArtifactLocations.Count.Should().Be(3); foreach (Result result in sarifLog.Runs[0].Results) { visitor.AllArtifactLocations.Contains(result.Locations[0].PhysicalLocation.ArtifactLocation).Should().BeTrue(); } }