Skip to content

Commit

Permalink
[Search DV] Display deprecation and vulnerabilities on search page. (#…
Browse files Browse the repository at this point in the history
…9440)

* deprecation and vulnerabilities display on search page

* Properties added to current Deprecations/Vulnerabilities

* nit.

* tooltip added.

* tooltip tests.

* fix.

* nit.

* update deprecated legacy wording.

* fix test.

* AdvisoryUrl, and AlternatePackage fix.

* version range comma index.
  • Loading branch information
dannyjdev committed Apr 26, 2023
1 parent 36cbb88 commit 3ebfdea
Show file tree
Hide file tree
Showing 18 changed files with 671 additions and 69 deletions.
26 changes: 26 additions & 0 deletions src/Bootstrap/dist/css/bootstrap-theme.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions src/Bootstrap/less/theme/common-list-packages.less
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,30 @@
margin-top: 75px;
margin-bottom: 0px;
}

@severe-warning-background-color: rgb(254, 217, 204);
@severe-warning-icon-color: rgb(216, 59, 1);
@warning-background-color: rgb(255, 244, 206);
@package-warning-color: rgb(50, 49, 48);
@badge-border-radius: 2px;

.package-warning {
padding-right: 8px;
padding-left: 8px;
border-radius: @badge-border-radius;
margin-right: 5px;
color: @package-warning-color
}

.package-warning--vulnerable {
.package-warning;
background-color: @severe-warning-background-color;
i {
color: @severe-warning-icon-color;
}
}

.package-warning--deprecated {
.package-warning;
background-color: @warning-background-color;
}
86 changes: 86 additions & 0 deletions src/NuGetGallery/Helpers/SearchResponseHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
using NuGet.Services.Entities;

namespace NuGetGallery.Helpers
{
public static class SearchResponseHelper
{
public static ICollection<PackageDeprecation> GetDeprecationsOrNull(JToken docDeprecation)
{
PackageDeprecation deprecation = null;
if (docDeprecation != null)
{
var docReasons = docDeprecation.Value<JArray>("Reasons");
if (docReasons != null && docReasons.HasValues)
{
PackageDeprecationStatus status = PackageDeprecationStatus.NotDeprecated;
foreach (var reason in docReasons)
{
if (Enum.TryParse<PackageDeprecationStatus>(reason.Value<string>(), out var pdStatus))
{
status |= pdStatus;
}
}

var docAlternatePackage = docDeprecation["AlternatePackage"];
Package alternatePackage = null;
if (docAlternatePackage != null)
{
var range = docAlternatePackage.Value<string>("Range");
var id = docAlternatePackage.Value<string>("Id");
if (!string.IsNullOrEmpty(range) && !string.IsNullOrEmpty(id))
{
var version = string.Empty;
var commaIndex = range.IndexOf(",");
if (range.StartsWith("[") && commaIndex > 0)
{
var startIndex = 1;
version = range.Substring(startIndex, commaIndex - startIndex);
}

alternatePackage = new Package()
{
Id = id,
Version = version
};
}
}

deprecation = new PackageDeprecation()
{
CustomMessage = docDeprecation.Value<string>("Message"),
Status = status,
AlternatePackage = alternatePackage
};
}
}

return deprecation == null ? null : new List<PackageDeprecation>() { deprecation };
}

public static ICollection<VulnerablePackageVersionRange> GetVulnerabilities(JArray docVulnerabilities)
{
var vulnerabilities = new List<VulnerablePackageVersionRange>();
if (docVulnerabilities != null)
{
vulnerabilities = docVulnerabilities.Select(v => new VulnerablePackageVersionRange()
{
Vulnerability = new PackageVulnerability()
{
AdvisoryUrl = v.Value<string>("AdvisoryUrl"),
Severity = (PackageVulnerabilitySeverity)v.Value<int>("Severity")
}
})
.ToList();
}

return vulnerabilities;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using NuGet.Services.Entities;
using NuGet.Versioning;
using NuGetGallery.Helpers;

namespace NuGetGallery
{
Expand Down Expand Up @@ -217,60 +218,11 @@ private DisplayPackageViewModel SetupCommon(
viewModel.MaxVulnerabilitySeverity = default;
}

viewModel.PackageWarningIconTitle =
GetWarningIconTitle(viewModel.Version, deprecation, maxVulnerabilitySeverity);
viewModel.PackageWarningIconTitle = WarningTitleHelper.GetWarningIconTitle(viewModel.Version, deprecation, maxVulnerabilitySeverity);

return viewModel;
}

private static string GetWarningIconTitle(
string version,
PackageDeprecation deprecation,
PackageVulnerabilitySeverity? maxVulnerabilitySeverity)
{
// We want a tooltip title for the warning icon, which concatenates deprecation and vulnerability information cleanly
var deprecationTitle = "";
if (deprecation != null)
{
deprecationTitle = version;
var isLegacy = deprecation.Status.HasFlag(PackageDeprecationStatus.Legacy);
var hasCriticalBugs = deprecation.Status.HasFlag(PackageDeprecationStatus.CriticalBugs);
if (hasCriticalBugs)
{
if (isLegacy)
{
deprecationTitle += " is deprecated because it's legacy and has critical bugs";
}
else
{
deprecationTitle += " is deprecated because it has critical bugs";
}
}
else if (isLegacy)
{
deprecationTitle += " is deprecated because it's legacy and no longer maintained";
}
else
{
deprecationTitle += " is deprecated";
}
}

if (maxVulnerabilitySeverity.HasValue)
{
var severity = Enum.GetName(typeof(PackageVulnerabilitySeverity), maxVulnerabilitySeverity)?.ToLowerInvariant() ?? "unknown";
var vulnerabilitiesTitle = $"{version} has at least one vulnerability with {severity} severity.";

return string.IsNullOrEmpty(deprecationTitle)
? vulnerabilitiesTitle
: $"{deprecationTitle}; {vulnerabilitiesTitle}";
}

return string.IsNullOrEmpty(deprecationTitle)
? string.Empty
: $"{deprecationTitle}.";
}

private static string GetPushedBy(Package package, User currentUser, Dictionary<User, string> pushedByCache)
{
var userPushedBy = package.User;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Linq;
using NuGet.Services.Entities;
using NuGetGallery.Helpers;

namespace NuGetGallery
{
Expand Down Expand Up @@ -39,6 +40,19 @@ private ListPackageItemViewModel SetupInternal(ListPackageItemViewModel viewMode
viewModel.MinClientVersion = package.MinClientVersion;
viewModel.Owners = package.PackageRegistration?.Owners?.Select(GetBasicUserViewModel).ToList();
viewModel.IsVerified = package.PackageRegistration?.IsVerified;
viewModel.IsDeprecated = package.Deprecations?.Count > 0;
viewModel.IsVulnerable = package.VulnerablePackageRanges?.Count > 0;

if (viewModel.IsDeprecated)
{
viewModel.DeprecationTitle = WarningTitleHelper.GetDeprecationTitle(package.Version, package.Deprecations.First().Status);
}

if (viewModel.IsVulnerable)
{
var maxVulnerabilitySeverity = package.VulnerablePackageRanges.Max(vpr => vpr.Vulnerability.Severity);
viewModel.VulnerabilityTitle = WarningTitleHelper.GetVulnerabilityTitle(package.Version, maxVulnerabilitySeverity);
}

viewModel.CanDisplayPrivateMetadata = CanPerformAction(currentUser, package, ActionsRequiringPermissions.DisplayPrivatePackageMetadata);
viewModel.CanEdit = CanPerformAction(currentUser, package, ActionsRequiringPermissions.EditPackage);
Expand Down Expand Up @@ -68,9 +82,10 @@ private static bool CanPerformAction(User currentUser, Package package, ActionRe

private static BasicUserViewModel GetBasicUserViewModel(User user)
{
return new BasicUserViewModel {
Username = user.Username,
EmailAddress = user.EmailAddress,
return new BasicUserViewModel
{
Username = user.Username,
EmailAddress = user.EmailAddress,
IsOrganization = user is Organization,
IsLocked = user.IsLocked
};
Expand Down
69 changes: 69 additions & 0 deletions src/NuGetGallery/Helpers/WarningTitleHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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 NuGet.Services.Entities;

namespace NuGetGallery.Helpers
{
public static class WarningTitleHelper
{
public static string GetWarningIconTitle(
string version,
PackageDeprecation deprecation,
PackageVulnerabilitySeverity? maxVulnerabilitySeverity)
{
// We want a tooltip title for the warning icon, which concatenates deprecation and vulnerability information cleanly
var deprecationTitle = "";
if (deprecation != null)
{
deprecationTitle = GetDeprecationTitle(version, deprecation.Status);
}

if (maxVulnerabilitySeverity.HasValue)
{
var vulnerabilitiesTitle = GetVulnerabilityTitle(version, maxVulnerabilitySeverity.Value);
return string.IsNullOrEmpty(deprecationTitle)
? vulnerabilitiesTitle
: $"{deprecationTitle.TrimEnd('.')}; {vulnerabilitiesTitle}";
}

return string.IsNullOrEmpty(deprecationTitle) ? string.Empty : deprecationTitle;
}

public static string GetVulnerabilityTitle(string version, PackageVulnerabilitySeverity maxVulnerabilitySeverity)
{
var severity = Enum.GetName(typeof(PackageVulnerabilitySeverity), maxVulnerabilitySeverity)?.ToLowerInvariant() ?? "unknown";
return $"{version} has at least one vulnerability with {severity} severity.";
}

public static string GetDeprecationTitle(string version, PackageDeprecationStatus status)
{
var deprecationTitle = version;
var isLegacy = status.HasFlag(PackageDeprecationStatus.Legacy);
var hasCriticalBugs = status.HasFlag(PackageDeprecationStatus.CriticalBugs);

if (hasCriticalBugs)
{
if (isLegacy)
{
deprecationTitle += " is deprecated because it is no longer maintained and has critical bugs";
}
else
{
deprecationTitle += " is deprecated because it has critical bugs";
}
}
else if (isLegacy)
{
deprecationTitle += " is deprecated because it is no longer maintained";
}
else
{
deprecationTitle += " is deprecated";
}

return $"{deprecationTitle}.";
}
}
}
Loading

0 comments on commit 3ebfdea

Please sign in to comment.