private string PromptForResolvedPath(SarifErrorListItem sarifErrorListItem, string pathFromLogFile) { // Opening the OpenFileDialog causes the TreeView to lose focus, // which in turn causes the TreeViewItem selection to be unpredictable // (because the selection event relies on the TreeViewItem focus.) // We'll save the element which currently has focus and then restore // focus after the OpenFileDialog is closed. var elementWithFocus = Keyboard.FocusedElement as UIElement; string fileName = Path.GetFileName(pathFromLogFile); var openFileDialog = new OpenFileDialog { Title = string.Format(Resources.PromptForResolvedPathDialogTitle, pathFromLogFile), InitialDirectory = sarifErrorListItem != null?Path.GetDirectoryName(sarifErrorListItem.LogFilePath) : null, Filter = $"{fileName}|{fileName}", RestoreDirectory = true, }; try { bool?dialogResult = openFileDialog.ShowDialog(); return(dialogResult == true ? openFileDialog.FileName : null); } finally { elementWithFocus?.Focus(); } }
// return false means cannot resolve local file and will use embedded file. internal bool VerifyFileWithArtifactHash(SarifErrorListItem sarifErrorListItem, string pathFromLogFile, RunDataCache dataCache, string resolvedPath, string embeddedTempFilePath, out string newResolvedPath) { newResolvedPath = null; if (string.IsNullOrEmpty(resolvedPath)) { // cannot find corresponding file in local, then use embedded file newResolvedPath = embeddedTempFilePath; return(true); } if (!dataCache.FileDetails.TryGetValue(pathFromLogFile, out ArtifactDetailsModel fileData)) { // has no embedded file, return the path resolved till now newResolvedPath = resolvedPath; return(true); } string fileHash = this.GetFileHash(this._fileSystem, resolvedPath); if (fileHash.Equals(fileData.Sha256Hash, StringComparison.OrdinalIgnoreCase)) { // found a file in file system which has same hashcode as embeded content. newResolvedPath = resolvedPath; return(true); } bool hasEmbeddedContent = !string.IsNullOrEmpty(embeddedTempFilePath); ResolveEmbeddedFileDialogResult dialogResult = this._promptForEmbeddedFileDelegate(sarifErrorListItem.LogFilePath, hasEmbeddedContent, this.userDialogPreference); switch (dialogResult) { case ResolveEmbeddedFileDialogResult.None: // dialog is cancelled. newResolvedPath = null; return(false); case ResolveEmbeddedFileDialogResult.OpenEmbeddedFileContent: newResolvedPath = embeddedTempFilePath; return(true); case ResolveEmbeddedFileDialogResult.OpenLocalFileFromSolution: newResolvedPath = resolvedPath; return(true); case ResolveEmbeddedFileDialogResult.BrowseAlternateLocation: // if returns null means user cancelled the open file dialog. newResolvedPath = this._promptForResolvedPathDelegate(sarifErrorListItem, pathFromLogFile); return(!string.IsNullOrEmpty(newResolvedPath)); default: return(false); } }
private void UpdateDataContext(SarifErrorListItem sarifErrorListItem) { ThreadHelper.ThrowIfNotOnUIThread(); if (this.Control.DataContext is SarifErrorListItem previousSarifErrorListItem) { previousSarifErrorListItem.Disposed -= this.SarifErrorListItem_Disposed; } // Resetting the data context to null causes the correct tab in the SARIF explorer // window to be selected when the data context is set back to a non-null value. this.Control.DataContext = null; this.Control.DataContext = sarifErrorListItem; if (sarifErrorListItem != null) { sarifErrorListItem.Disposed += this.SarifErrorListItem_Disposed; } this.UpdateSelectionList(sarifErrorListItem); }
internal string GetFilePathFromHttp(SarifErrorListItem sarifErrorListItem, string uriBaseId, RunDataCache dataCache, string pathFromLogFile) { ThreadHelper.ThrowIfNotOnUIThread(); Uri uri = null; if ((uriBaseId != null && dataCache.OriginalUriBasePaths.TryGetValue(uriBaseId, out Uri baseUri) && Uri.TryCreate(baseUri, pathFromLogFile, out uri) && uri.IsHttpScheme()) || // if result location uri is an absolute http url (Uri.TryCreate(pathFromLogFile, UriKind.Absolute, out uri) && uri.IsHttpScheme())) { return(this.HandleHttpFileDownloadRequest( VersionControlParserFactory.ConvertToRawFileLink(uri), sarifErrorListItem.WorkingDirectory)); } return(null); }
internal string DownloadFile(SarifErrorListItem sarifErrorListItem, string fileUrl) { if (string.IsNullOrEmpty(fileUrl)) { return(fileUrl); } var sourceUri = new Uri(fileUrl); string destinationFile = Path.Combine(sarifErrorListItem.WorkingDirectory, sourceUri.LocalPath.Replace('/', '\\').TrimStart('\\')); string destinationDirectory = Path.GetDirectoryName(destinationFile); Directory.CreateDirectory(destinationDirectory); if (!this._fileSystem.FileExists(destinationFile)) { using (var client = new WebClient()) { client.DownloadFile(sourceUri, destinationFile); } } return(destinationFile); }
// Internal rather than private for unit testability. internal string GetRebaselinedFileName(SarifErrorListItem sarifErrorListItem, string uriBaseId, string pathFromLogFile, RunDataCache dataCache, string solutionFullPath = null) { string originalPath = pathFromLogFile; Uri relativeUri = null; if (!string.IsNullOrEmpty(uriBaseId) && Uri.TryCreate(pathFromLogFile, UriKind.Relative, out relativeUri)) { // If the relative file path is relative to an unknown root, // we need to strip the leading slash, so that we can relate // the file path to an arbitrary remapped disk location. if (pathFromLogFile.StartsWith("/")) { pathFromLogFile = pathFromLogFile.Substring(1); } if (dataCache.RemappedUriBasePaths.ContainsKey(uriBaseId)) { pathFromLogFile = new Uri(dataCache.RemappedUriBasePaths[uriBaseId], pathFromLogFile).LocalPath; } else if (dataCache.OriginalUriBasePaths.ContainsKey(uriBaseId)) { pathFromLogFile = new Uri(dataCache.OriginalUriBasePaths[uriBaseId], pathFromLogFile).LocalPath; } if (this._fileSystem.FileExists(pathFromLogFile)) { return(pathFromLogFile); } } // Traverse our remappings and see if we can // make rebaseline from existing data foreach (Tuple <string, string> remapping in dataCache.RemappedPathPrefixes) { string remapped; if (!string.IsNullOrEmpty(remapping.Item1)) { remapped = pathFromLogFile.Replace(remapping.Item1, remapping.Item2); } else { remapped = Path.Combine(remapping.Item2, pathFromLogFile); } if (this._fileSystem.FileExists(remapped)) { return(remapped); } } string resolvedPath = null; if (!string.IsNullOrEmpty(solutionFullPath)) { this.TryResolveFilePathFromSolution(solutionFullPath, originalPath, this._fileSystem, out resolvedPath); } if (resolvedPath == null) { resolvedPath = this._promptForResolvedPathDelegate(sarifErrorListItem, pathFromLogFile); } if (resolvedPath == null) { return(pathFromLogFile); } string fullPathFromLogFile = pathFromLogFile; if (Uri.TryCreate(pathFromLogFile, UriKind.Absolute, out Uri absoluteUri)) { fullPathFromLogFile = Path.GetFullPath(pathFromLogFile); } else { if (!fullPathFromLogFile.StartsWith("/")) { fullPathFromLogFile = "/" + fullPathFromLogFile; } } string commonSuffix = GetCommonSuffix(fullPathFromLogFile.Replace("/", @"\"), resolvedPath); if (commonSuffix == null) { return(pathFromLogFile); } // Trim the common suffix from both paths, and add a remapping that converts // one prefix to the other. string originalPrefix = fullPathFromLogFile.Substring(0, fullPathFromLogFile.Length - commonSuffix.Length); string resolvedPrefix = resolvedPath.Substring(0, resolvedPath.Length - commonSuffix.Length); int uriBaseIdEndIndex = resolvedPath.IndexOf(originalPath.Replace("/", @"\")); if (relativeUri != null && uriBaseIdEndIndex >= 0) { // If we could determine the uriBaseId substitution value, then add it to the map. dataCache.RemappedUriBasePaths[uriBaseId] = new Uri(resolvedPath.Substring(0, uriBaseIdEndIndex), UriKind.Absolute); } else { // If there's no relativeUri/uriBaseId pair or we couldn't determine the uriBaseId value, // map the original prefix to the new prefix. dataCache.RemappedPathPrefixes.Add(new Tuple <string, string>(originalPrefix, resolvedPrefix)); } return(resolvedPath); }
public bool TryResolveFilePath(int resultId, int runIndex, string uriBaseId, string relativePath, out string resolvedPath) { resolvedPath = null; if (!SarifViewerPackage.IsUnitTesting) { #pragma warning disable VSTHRD108 // Assert thread affinity unconditionally ThreadHelper.ThrowIfNotOnUIThread(); #pragma warning restore VSTHRD108 } if (!this.RunIndexToRunDataCache.TryGetValue(runIndex, out RunDataCache dataCache)) { return(false); } SarifErrorListItem sarifErrorListItem = dataCache.SarifErrors.FirstOrDefault(sarifResult => sarifResult.ResultId == resultId); if (sarifErrorListItem == null) { return(false); } if (dataCache.FileDetails.ContainsKey(relativePath)) { // File contents embedded in SARIF. resolvedPath = this.CreateFileFromContents(dataCache.FileDetails, relativePath); } else { if (uriBaseId != null && dataCache.OriginalUriBasePaths.TryGetValue(uriBaseId, out Uri baseUri) && Uri.TryCreate(baseUri, relativePath, out Uri uri) && uri.IsHttpScheme()) { bool allow = this._allowedDownloadHosts.Contains(uri.Host); // File needs to be downloaded, prompt for confirmation if host is not already allowed if (!allow) { MessageDialogCommand result = MessageDialog.Show(Resources.ConfirmDownloadDialog_Title, string.Format(Resources.ConfirmDownloadDialog_Message, uri), MessageDialogCommandSet.YesNo, string.Format(Resources.ConfirmDownloadDialog_CheckboxLabel, uri.Host), out bool alwaysAllow); if (result != MessageDialogCommand.No) { allow = true; if (alwaysAllow) { this.AddAllowedDownloadHost(uri.Host); } } } if (allow) { try { resolvedPath = this.DownloadFile(sarifErrorListItem, uri.ToString()); } catch (WebException wex) { VsShellUtilities.ShowMessageBox(ServiceProvider.GlobalProvider, Resources.DownloadFail_DialogMessage + Environment.NewLine + wex.Message, null, // title OLEMSGICON.OLEMSGICON_CRITICAL, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); return(false); } } } else { string solutionPath = null; DTE2 dte = (DTE2)Package.GetGlobalService(typeof(DTE)); if (dte.Solution != null && dte.Solution.IsOpen) { solutionPath = dte.Solution.FullName; } // User needs to locate file. resolvedPath = this.GetRebaselinedFileName(sarifErrorListItem, uriBaseId, relativePath, dataCache, solutionPath); } if (string.IsNullOrEmpty(resolvedPath) || relativePath.Equals(resolvedPath, StringComparison.OrdinalIgnoreCase)) { return(false); } } // Update all the paths in this run. this.RemapFilePaths(dataCache.SarifErrors, relativePath, resolvedPath); return(true); }
internal static bool CanNavigateTo(SarifErrorListItem sarifError) { throw new NotImplementedException(); }
public bool TryResolveFilePath(int resultId, int runIndex, string uriBaseId, string relativePath, out string resolvedPath) { resolvedPath = null; if (!SarifViewerPackage.IsUnitTesting) { #pragma warning disable VSTHRD108 // Assert thread affinity unconditionally ThreadHelper.ThrowIfNotOnUIThread(); #pragma warning restore VSTHRD108 } if (!this.RunIndexToRunDataCache.TryGetValue(runIndex, out RunDataCache dataCache)) { return(false); } SarifErrorListItem sarifErrorListItem = dataCache.SarifErrors.FirstOrDefault(sarifResult => sarifResult.ResultId == resultId); if (sarifErrorListItem == null) { return(false); } string solutionPath = GetSolutionPath( (DTE2)Package.GetGlobalService(typeof(DTE)), ((IComponentModel)Package.GetGlobalService(typeof(SComponentModel))).GetService <IVsFolderWorkspaceService>()); // File contents embedded in SARIF. bool hasHash = dataCache.FileDetails.TryGetValue(relativePath, out ArtifactDetailsModel model) && !string.IsNullOrEmpty(model?.Sha256Hash); string embeddedTempFilePath = this.CreateFileFromContents(dataCache.FileDetails, relativePath); try { resolvedPath = this.GetFilePathFromHttp(sarifErrorListItem, uriBaseId, dataCache, relativePath); } catch (WebException) { // failed to download the file return(false); } if (string.IsNullOrEmpty(resolvedPath)) { // resolve path, existing file in local disk resolvedPath = this.GetRebaselinedFileName( uriBaseId: uriBaseId, pathFromLogFile: relativePath, dataCache: dataCache, workingDirectory: sarifErrorListItem.WorkingDirectory, solutionFullPath: solutionPath); } // verify resolved file with artifact's Hash if (hasHash) { string currentResolvedPath = resolvedPath; if (!this.VerifyFileWithArtifactHash(sarifErrorListItem, relativePath, dataCache, currentResolvedPath, embeddedTempFilePath, out resolvedPath)) { return(false); } } if (string.IsNullOrEmpty(resolvedPath)) { // User needs to locate file. resolvedPath = this._promptForResolvedPathDelegate(sarifErrorListItem, relativePath); } if (!string.IsNullOrEmpty(resolvedPath)) { // save resolved path to mapping if (!this.SaveResolvedPathToUriBaseMapping(uriBaseId, relativePath, relativePath, resolvedPath, dataCache)) { resolvedPath = relativePath; } } if (string.IsNullOrEmpty(resolvedPath) || relativePath.Equals(resolvedPath, StringComparison.OrdinalIgnoreCase)) { resolvedPath = relativePath; return(false); } // Update all the paths in this run. this.RemapFilePaths(dataCache.SarifErrors, relativePath, resolvedPath); return(true); }