private void Visit(ThreadFlowLocation threadFlowLocation, string threadFlowLocationPointer) { Analyze(threadFlowLocation, threadFlowLocationPointer); if (threadFlowLocation.Location != null) { Visit(threadFlowLocation.Location, threadFlowLocationPointer.AtProperty(SarifPropertyName.Location)); } if (threadFlowLocation.Taxa != null) { string taxaPointer = threadFlowLocationPointer.AtProperty(SarifPropertyName.Taxa); Context.CurrentReportingDescriptorKind = SarifValidationContext.ReportingDescriptorKind.Taxon; try { for (int i = 0; i < threadFlowLocation.Taxa.Count; ++i) { Visit(threadFlowLocation.Taxa[i], taxaPointer.AtIndex(i)); } } finally { Context.CurrentReportingDescriptorKind = SarifValidationContext.ReportingDescriptorKind.None; } } if (threadFlowLocation.WebRequest != null) { Visit(threadFlowLocation.WebRequest, threadFlowLocationPointer.AtProperty(SarifPropertyName.WebRequest)); } if (threadFlowLocation.WebResponse != null) { Visit(threadFlowLocation.WebResponse, threadFlowLocationPointer.AtProperty(SarifPropertyName.WebResponse)); } }
private void ParseLocationsFromTraces(Result result) { CodeFlow codeFlow = null; string nodeLabel = null; string lastNodeId = null; bool? isDefault = null; while (!AtEndOf(_strings.Unified)) { if (AtStartOf(_strings.Trace)) { codeFlow = SarifUtilities.CreateSingleThreadedCodeFlow(); result.CodeFlows.Add(codeFlow); while (!AtEndOf(_strings.Trace)) { if (AtStartOf(_strings.NodeRef)) { string nodeId = _reader.GetAttribute(_strings.IdAttribute); if (!string.IsNullOrWhiteSpace(nodeId)) { var tfl = new ThreadFlowLocation(); _tflToNodeIdDictionary.Add(tfl, nodeId); codeFlow.ThreadFlows[0].Locations.Add(tfl); } _reader.Read(); } else if (AtStartOf(_strings.Node)) { if (isDefault == null) { // We haven't found the default node yet, so check this one. string isDefaultValue = _reader.GetAttribute(_strings.IsDefaultAttribute); if (!string.IsNullOrWhiteSpace(isDefaultValue) && bool.TryParse(isDefaultValue, out bool val) && val == true) { // This is the default, set the flag so we know to add a result location isDefault = val; } } nodeLabel = _reader.GetAttribute(_strings.LabelAttribute); _reader.Read(); } else if (AtStartOf(_strings.SourceLocation)) { // Note: SourceLocation is an empty element (it has only attributes), // so we can't call AtStartOfNonEmpty here. string snippetId = _reader.GetAttribute(_strings.SnippetAttribute); PhysicalLocation physicalLocation = ParsePhysicalLocationFromSourceInfo(); // Step past the empty SourceLocation element. _reader.Read(); string actionType = null; if (AtStartOf(_strings.Action)) { actionType = _reader.GetAttribute(_strings.TypeAttribute); actionType = actionType ?? string.Empty; // We use empty string to indicates there is an // Action element without a type attribute. // If we don't have a label, get the <Action> value if (string.IsNullOrWhiteSpace(nodeLabel)) { nodeLabel = _reader.ReadElementContentAsString(); } } if (actionType == string.Empty) { if (codeFlow.ThreadFlows[0].Locations.Count > 0) { // If there is no type attribute on the Action element, we treat // it as a note about the prior node. ThreadFlowLocation tfl = codeFlow.ThreadFlows[0].Locations.Last(); // Annotate the location with the Action text. if (tfl?.Location != null) { tfl.Location.Annotations = new List <Region>(); Region region = physicalLocation.Region; region.Message = new Message { Text = nodeLabel }; tfl.Location.Annotations.Add(region); } } } else { var location = new Location { PhysicalLocation = physicalLocation }; if (isDefault == true) { result.Locations = new List <Location>(); result.Locations.Add(location.DeepClone()); result.RelatedLocations.Add(location.DeepClone()); // Keep track of the snippet associated with the default location. // That's the snippet that we'll associate with the result. lastNodeId = snippetId; isDefault = false; // This indicates we have already found the default node. } var tfl = new ThreadFlowLocation { Kinds = new List <string> { actionType }, Location = location }; if (!string.IsNullOrWhiteSpace(nodeLabel)) { tfl.Location.Message = new Message { Text = nodeLabel }; } // Remember the id of the snippet associated with this location. // We'll use it to fill the snippet text when we read the Snippets element later on. if (!string.IsNullOrEmpty(snippetId)) { _tflToSnippetIdDictionary.Add(tfl, snippetId); } codeFlow.ThreadFlows[0].Locations.Add(tfl); } } else { _reader.Read(); } } } else { _reader.Read(); } } if (result.RelatedLocations.Any()) { Location relatedLocation = result.RelatedLocations.Last(); if (relatedLocation != null) { relatedLocation.PhysicalLocation.Id = 1; } } if (!string.IsNullOrEmpty(lastNodeId)) { _resultToSnippetIdDictionary.Add(result, lastNodeId); } }
internal AnnotatedCodeLocationVersionOne CreateAnnotatedCodeLocationVersionOne(ThreadFlowLocation v2ThreadFlowLocation) { AnnotatedCodeLocationVersionOne annotatedCodeLocation = null; if (v2ThreadFlowLocation != null) { annotatedCodeLocation = CreateAnnotatedCodeLocationVersionOne(v2ThreadFlowLocation.Location); annotatedCodeLocation = annotatedCodeLocation ?? new AnnotatedCodeLocationVersionOne(); annotatedCodeLocation.Importance = Utilities.CreateAnnotatedCodeLocationImportance(v2ThreadFlowLocation.Importance); annotatedCodeLocation.Module = v2ThreadFlowLocation.Module; annotatedCodeLocation.Properties = v2ThreadFlowLocation.Properties; annotatedCodeLocation.State = v2ThreadFlowLocation.State; annotatedCodeLocation.Step = v2ThreadFlowLocation.ExecutionOrder; } return(annotatedCodeLocation); }
private void ProcessLine(string logFileLine, ref int nestingLevel, Result result) { var codeFlow = result.CodeFlows[0]; const int STEP = 0; const int URI = 1; const int LINE = 2; // const int IMPORTANCE = 3; This value not persisted to SARIF const int STATE = 4; const int KIND1 = 5; // When KIND1 == "Atomic" the 6th slot is the // the remainder of the kind id, e.g., Atomic Assigment const int KIND2 = 6; // When KIND1 == "Call" the 6th and 7th slots are: const int CALLER = 6; const int CALLEE = 7; int step; string[] tokens = logFileLine.Split(' '); if (int.TryParse(tokens[STEP], out step)) { // If we find a numeric value as the first token, // this is a general step. Uri uri = null; string uriText = tokens[URI].Trim('"'); if (!uriText.Equals("?", StringComparison.Ordinal)) { if (File.Exists(uriText)) { uriText = Path.GetFullPath(uriText); } uri = new Uri(uriText, UriKind.RelativeOrAbsolute); } // We assume a valid line here. This code will throw if not. int line = int.Parse(tokens[LINE]); string sdvKind = tokens[KIND1]; if (sdvKind.Equals("Atomic", StringComparison.Ordinal)) { // For multipart SDV kinds 'Atomic XXX', we // map using the second value only, e.g, // 'Assignment' or 'Conditional' sdvKind = tokens[KIND2]; } sdvKind = sdvKind.Trim(); var threadFlowLocation = new ThreadFlowLocation { Importance = ThreadFlowLocationImportance.Unimportant, Location = new Location { Message = new Message() } }; if (uri != null) { threadFlowLocation.Location.PhysicalLocation = new PhysicalLocation { ArtifactLocation = new ArtifactLocation { Uri = uri }, Region = new Region { StartLine = line } }; } if (sdvKind == "Call") { string extraMsg = $"{tokens[KIND1]} {tokens[CALLER]} {tokens[CALLEE]}"; string caller, callee; if (ExtractCallerAndCallee(extraMsg.Trim(), out caller, out callee)) { threadFlowLocation.Location.FullyQualifiedLogicalName = caller; threadFlowLocation.Location.Message.Text = callee; threadFlowLocation.SetProperty("target", callee); _callers.Push(caller); } else { Debug.Assert(false); } threadFlowLocation.NestingLevel = nestingLevel++; if (uri == null) { threadFlowLocation.Importance = ThreadFlowLocationImportance.Unimportant; } else if (IsHarnessOrRulesFiles(uriText)) { threadFlowLocation.Importance = ThreadFlowLocationImportance.Important; } else { threadFlowLocation.Importance = ThreadFlowLocationImportance.Essential; } } else if (sdvKind == "Return") { Debug.Assert(_callers.Count > 0); threadFlowLocation.NestingLevel = nestingLevel--; threadFlowLocation.Location.FullyQualifiedLogicalName = _callers.Pop(); } else { threadFlowLocation.NestingLevel = nestingLevel; threadFlowLocation.Location.Message.Text = sdvKind; } string separatorText = "^====Auto====="; string state = tokens[STATE]; string[] stateTokens = state.Split(new string[] { separatorText }, StringSplitOptions.RemoveEmptyEntries); if (stateTokens.Length > 0) { if (stateTokens.Length == 2) { threadFlowLocation.SetProperty("currentDataValues", stateTokens[0]); threadFlowLocation.SetProperty("permanentDataValues", stateTokens[1]); } else { Debug.Assert(stateTokens.Length == 1); if (stateTokens[0].StartsWith(separatorText)) { threadFlowLocation.SetProperty("permanentDataValues", stateTokens[0]); } else { threadFlowLocation.SetProperty("currentDataValues", stateTokens[0]); } } } codeFlow.ThreadFlows[0].Locations.Add(threadFlowLocation); } else { // This is the defect message. const int LEVEL = 0; string levelText = tokens[LEVEL]; result.Level = ConvertToFailureLevel(levelText); // Everything on the line following defect level comprises the message result.Message = new Message { Text = logFileLine.Substring(levelText.Length).Trim() }; // SDV currently produces 'pass' notifications when // the final line is prefixed with 'Error'. We'll examine // the message text to detect this condition if (result.Message.Text.Contains("is satisfied")) { result.Level = FailureLevel.None; result.Kind = ResultKind.Pass; } // Finally, populate this result location with the // last observed location in the code flow. IList <ThreadFlowLocation> locations = result.CodeFlows[0].ThreadFlows[0].Locations; for (int i = locations.Count - 1; i >= 0; --i) { if (locations[i].Location?.PhysicalLocation != null) { result.Locations.Add(new Location { PhysicalLocation = locations[i].Location.PhysicalLocation }); break; } } } }
private void GenerateCodeFlows(Defect defect, Result result) { List <SFA> sfas = defect?.Path?.SFAs; if (sfas == null || sfas.Count == 0) { return; } int step = 0; var locations = new List <ThreadFlowLocation>(); bool pathUsesKeyEvents = defect.Path.SFAs.Any(x => !string.IsNullOrWhiteSpace(x?.KeyEvent?.Id)); foreach (var sfa in defect.Path.SFAs) { var region = new Region() { StartColumn = sfa.Column + 1, StartLine = sfa.Line }; var uri = new Uri($"{sfa.FilePath}{sfa.FileName}", UriKind.Relative); var fileLocation = new PhysicalLocation(id: 0, fileLocation: new FileLocation(uri: uri, uriBaseId: null), region: region, contextRegion: null); var threadFlowLocation = new ThreadFlowLocation { Location = new Location { PhysicalLocation = fileLocation }, Step = ++step }; if (pathUsesKeyEvents) { if (string.IsNullOrWhiteSpace(sfa.KeyEvent?.Id)) { threadFlowLocation.Importance = ThreadFlowLocationImportance.Unimportant; } else { threadFlowLocation.SetProperty("keyEventId", sfa.KeyEvent.Id); if (Enum.TryParse(sfa.KeyEvent.Importance, true, out ThreadFlowLocationImportance importance)) { threadFlowLocation.Importance = importance; } if (!string.IsNullOrWhiteSpace(sfa.KeyEvent.Message) && threadFlowLocation.Location?.Message != null) { threadFlowLocation.Location.Message.Text = sfa.KeyEvent.Message; } } } locations.Add(threadFlowLocation); } result.CodeFlows = new List <CodeFlow>() { SarifUtilities.CreateSingleThreadedCodeFlow(locations) }; }
protected virtual void Analyze(ThreadFlowLocation threadFlowLocation, string threadFlowLocationPointer) { }
private void ParseLocationFromTrace(Result result) { CodeFlow codeFlow = result.CodeFlows.First(); int step = 0; string nodeLabel = null; string lastNodeId = null; _reader.Read(); while (!AtEndOf(_strings.Trace)) { if (AtStartOf(_strings.NodeRef)) { string nodeId = _reader.GetAttribute(_strings.IdAttribute); if (!string.IsNullOrWhiteSpace(nodeId)) { var tfl = new ThreadFlowLocation { Step = ++step }; _tflToNodeIdDictionary.Add(tfl, nodeId); codeFlow.ThreadFlows[0].Locations.Add(tfl); } _reader.Read(); } else if (AtStartOf(_strings.Node)) { nodeLabel = _reader.GetAttribute(_strings.LabelAttribute); _reader.Read(); } else if (AtStartOf(_strings.SourceLocation)) { // Note: SourceLocation is an empty element (it has only attributes), // so we can't call AtStartOfNonEmpty here. string snippetId = _reader.GetAttribute(_strings.SnippetAttribute); PhysicalLocation physicalLocation = ParsePhysicalLocationFromSourceInfo(); // Step past the empty SourceLocation element. _reader.Read(); // If we don't have a label, get the <Action> value if (string.IsNullOrWhiteSpace(nodeLabel)) { nodeLabel = _reader.ReadElementContentAsString(); } var tfl = new ThreadFlowLocation { Step = ++step, Location = new Location { Message = new Message { Text = nodeLabel }, PhysicalLocation = physicalLocation } }; // Remember the id of the snippet associated with this location. // We'll use it to fill the snippet text when we read the Snippets element later on. if (!string.IsNullOrEmpty(snippetId)) { _tflToSnippetIdDictionary.Add(tfl, snippetId); } codeFlow.ThreadFlows[0].Locations.Add(tfl); // Keep track of the snippet associated with the last location in the // CodeFlow; that's the snippet that we'll associate with the Result // as a whole. lastNodeId = snippetId; } else { _reader.Read(); } } if (codeFlow.ThreadFlows[0].Locations.Any()) { result.Locations.Add(new Location { // TODO: Confirm that the traces are ordered chronologically // (so that we really do want to use the last one as the // overall result location). PhysicalLocation = codeFlow.ThreadFlows[0].Locations.Last().Location?.PhysicalLocation.DeepClone() }); result.RelatedLocations.Add(new Location { // Links embedded in the result message refer to related physicalLocation.id PhysicalLocation = codeFlow.ThreadFlows[0].Locations.Last().Location?.PhysicalLocation.DeepClone() }); result.RelatedLocations.Last().PhysicalLocation.Id = 1; if (!string.IsNullOrEmpty(lastNodeId)) { _resultToSnippetIdDictionary.Add(result, lastNodeId); } } }
public Node(ThreadFlowLocation tfl, string snippetId) { this.ThreadFlowLocation = tfl; this.SnippetId = snippetId; }