-
Notifications
You must be signed in to change notification settings - Fork 693
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Show Infobar when vulnerabilities are found after restore (#5258)
* added info bar when vulnerabilities are found in restore
- Loading branch information
Showing
11 changed files
with
290 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
src/NuGet.Clients/NuGet.SolutionRestoreManager/IVulnerabilitiesNotificationService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
#nullable enable | ||
|
||
namespace NuGet.SolutionRestoreManager | ||
{ | ||
public interface IVulnerabilitiesNotificationService | ||
{ | ||
Task ReportVulnerabilitiesAsync(bool hasVulnerabilitiesInSolution, CancellationToken cancellationToken); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/NuGet.Clients/NuGet.SolutionRestoreManager/Resources.Designer.cs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
src/NuGet.Clients/NuGet.SolutionRestoreManager/VulnerablePackagesInfoBar.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
#nullable enable | ||
|
||
using System; | ||
using System.ComponentModel.Composition; | ||
using System.Globalization; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.VisualStudio; | ||
using Microsoft.VisualStudio.Imaging; | ||
using Microsoft.VisualStudio.Shell; | ||
using Microsoft.VisualStudio.Shell.Interop; | ||
using NuGet.VisualStudio; | ||
using NuGet.VisualStudio.Telemetry; | ||
|
||
namespace NuGet.SolutionRestoreManager | ||
{ | ||
[Export(typeof(IVulnerabilitiesNotificationService))] | ||
[PartCreationPolicy(CreationPolicy.Shared)] | ||
public class VulnerablePackagesInfoBar : IVulnerabilitiesNotificationService, IVsInfoBarUIEvents | ||
{ | ||
private IAsyncServiceProvider _asyncServiceProvider = AsyncServiceProvider.GlobalProvider; | ||
private IVsInfoBarUIElement? _infoBarUIElement; | ||
private bool _infoBarVisible = false; // InfoBar is currently being displayed in the Solution Explorer | ||
private bool _wasInfoBarClosed = false; // InfoBar was closed by the user, using the 'x'(close) in the InfoBar | ||
private bool _wasInfoBarHidden = false; // InfoBar was hid, this is caused because there are no more vulnerabilities to address | ||
private uint? _eventCookie; // To hold the connection cookie | ||
|
||
[Import] | ||
private Lazy<IPackageManagerLaunchService>? PackageManagerLaunchService { get; set; } | ||
|
||
public async Task ReportVulnerabilitiesAsync(bool hasVulnerabilitiesInSolution, CancellationToken cancellationToken) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
// If the infoBar was closed, don't show it for the rest of the VS session | ||
// if the infobar is visible and there are vulnerabilities, no work needed | ||
// if the infobar is not visible and there are no vulnerabilities, no work needed | ||
if (_wasInfoBarClosed || (hasVulnerabilitiesInSolution == _infoBarVisible)) | ||
{ | ||
return; | ||
} | ||
|
||
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); | ||
|
||
// Hide the InfoBar if Vulnerabilities were fixed | ||
if (!hasVulnerabilitiesInSolution && _infoBarVisible) | ||
{ | ||
_wasInfoBarHidden = true; | ||
_infoBarUIElement?.Close(); | ||
return; | ||
} | ||
|
||
try | ||
{ | ||
await CreateInfoBar(); | ||
|
||
_infoBarVisible = true; | ||
_wasInfoBarHidden = false; | ||
} | ||
catch (Exception ex) | ||
{ | ||
await TelemetryUtility.PostFaultAsync(ex, nameof(VulnerablePackagesInfoBar)); | ||
return; | ||
} | ||
} | ||
|
||
private async Task CreateInfoBar() | ||
{ | ||
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); | ||
|
||
// Initialize the InfoBar host in the SolutionExplorer window | ||
IVsInfoBarHost? infoBarHost; | ||
IVsUIShell? uiShell = await _asyncServiceProvider.GetServiceAsync<SVsUIShell, IVsUIShell>(throwOnFailure: true); | ||
int windowFrameCode = uiShell!.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fFindFirst, VSConstants.StandardToolWindows.SolutionExplorer, out var windowFrame); | ||
if (ErrorHandler.Failed(windowFrameCode)) | ||
{ | ||
Exception exception = new Exception(string.Format(CultureInfo.CurrentCulture, "Unable to find Solution Explorer window. HRRESULT {0}", windowFrameCode)); | ||
await TelemetryUtility.PostFaultAsync(exception, nameof(VulnerablePackagesInfoBar)); | ||
return; | ||
} | ||
|
||
object tempObject; | ||
int hostBarCode = windowFrame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out tempObject); | ||
if (ErrorHandler.Failed(hostBarCode)) | ||
{ | ||
Exception exception = new Exception(string.Format(CultureInfo.CurrentCulture, "Unable to find InfoBarHost. HRRESULT {0}", hostBarCode)); | ||
await TelemetryUtility.PostFaultAsync(exception, nameof(VulnerablePackagesInfoBar)); | ||
return; | ||
} | ||
|
||
infoBarHost = (IVsInfoBarHost)tempObject; | ||
|
||
// Create the VulnerabilitiesFound InfoBar | ||
IVsInfoBarUIFactory? infoBarFactory = await _asyncServiceProvider.GetServiceAsync<SVsInfoBarUIFactory, IVsInfoBarUIFactory>(throwOnFailure: false); | ||
if (infoBarFactory == null) | ||
{ | ||
NullReferenceException exception = new NullReferenceException(nameof(infoBarFactory)); | ||
await TelemetryUtility.PostFaultAsync(exception, nameof(VulnerablePackagesInfoBar)); | ||
return; | ||
} | ||
|
||
InfoBarModel infoBarModel = GetInfoBarModel(); | ||
|
||
_infoBarUIElement = infoBarFactory.CreateInfoBar(infoBarModel); | ||
_infoBarUIElement.Advise(this, out uint cookie); | ||
_eventCookie = cookie; | ||
|
||
infoBarHost.AddInfoBar(_infoBarUIElement); | ||
} | ||
|
||
public void OnClosed(IVsInfoBarUIElement infoBarUIElement) | ||
{ | ||
ThreadHelper.ThrowIfNotOnUIThread(); | ||
|
||
if (_eventCookie.HasValue) | ||
{ | ||
infoBarUIElement?.Unadvise(_eventCookie.Value); | ||
infoBarUIElement?.Close(); | ||
_eventCookie = null; | ||
} | ||
|
||
_infoBarVisible = false; | ||
|
||
if (!_wasInfoBarHidden) | ||
{ | ||
_wasInfoBarClosed = true; | ||
} | ||
} | ||
|
||
public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem) | ||
{ | ||
ThreadHelper.ThrowIfNotOnUIThread(); | ||
PackageManagerLaunchService?.Value.LaunchSolutionPackageManager(); | ||
} | ||
|
||
protected InfoBarModel GetInfoBarModel() | ||
{ | ||
return new InfoBarModel( | ||
Resources.InfoBar_TextMessage, | ||
new IVsInfoBarActionItem[] | ||
{ | ||
new InfoBarHyperlink(Resources.InfoBar_HyperlinkMessage), | ||
}, | ||
KnownMonikers.StatusWarning); | ||
} | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
src/NuGet.Clients/NuGet.Tools/PackageManagerLaunchService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.ComponentModel.Composition; | ||
using Microsoft.VisualStudio.Shell; | ||
using Microsoft.VisualStudio.Shell.Interop; | ||
using Microsoft.VisualStudio.Threading; | ||
using NuGet.VisualStudio; | ||
using NuGet.VisualStudio.Telemetry; | ||
|
||
#nullable enable | ||
|
||
namespace NuGetVSExtension | ||
{ | ||
[Export(typeof(IPackageManagerLaunchService))] | ||
[PartCreationPolicy(CreationPolicy.Shared)] | ||
internal class PackageManagerLaunchService : IPackageManagerLaunchService | ||
{ | ||
public void LaunchSolutionPackageManager() | ||
{ | ||
NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(async delegate | ||
{ | ||
await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); | ||
IVsUIShell vsUIShell = await AsyncServiceProvider.GlobalProvider.GetServiceAsync<IVsUIShell, IVsUIShell>(); | ||
|
||
object targetGuid = Guid.Empty; | ||
var guidNuGetDialog = GuidList.guidNuGetDialogCmdSet; | ||
vsUIShell.PostExecCommand( | ||
ref guidNuGetDialog, | ||
(uint)PkgCmdIDList.cmdidAddPackageDialogForSolution, | ||
(uint)0, | ||
ref targetGuid); | ||
}).PostOnFailure(nameof(PackageManagerLaunchService)); | ||
} | ||
} | ||
} |
Oops, something went wrong.