Skip to content

Commit

Permalink
Surface vulnerabilities in package detail page
Browse files Browse the repository at this point in the history
  • Loading branch information
drewgillies committed Dec 21, 2020
1 parent aeb04b5 commit d31453f
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 23 deletions.
42 changes: 42 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.

44 changes: 44 additions & 0 deletions src/Bootstrap/less/theme/page-display-package.less
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,50 @@
}
}

.vulnerabilities-container {
.vulnerabilities-expander {
display: flex;
justify-content: space-between;
vertical-align: middle;
width: 100%;

.vulnerabilities-expander-container {
display: flex;

.vulnerabilities-expander-icon {
position: unset;
top: unset;
}

.vulnerabilities-expander-info-right {
padding-left: 15px;
}
}
}

.vulnerabilities-content-container {
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid lightgray;

.vulnerabilities-list {
border-collapse: unset;
}
}

.vulnerabilities-severity-critical {
color: red
}

.vulnerabilities-severity-high {
color: red
}

.vulnerabilities-severity-moderate {
color: blue
}
}

.failed-validation-alert-list {
margin-top: 15px;
margin-bottom: 15px;
Expand Down
3 changes: 3 additions & 0 deletions src/NuGetGallery/NuGetGallery.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2305,6 +2305,9 @@
<ItemGroup>
<Content Include="App_Data\Files\Content\Query-Hint-Configuration.json" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Packages\_DisplayPackageVulnerabilities.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
Expand Down
28 changes: 21 additions & 7 deletions src/NuGetGallery/Scripts/gallery/page-display-package.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,36 @@ $(function () {
window.nuget.configureExpander("rename-content-container", "ChevronDown", null, "ChevronUp");
configureExpanderWithEnterKeydown($('#show-rename-content-container'));

// Configure the vulnerability information container
var expanderAttributes = ['data-toggle', 'data-target', 'aria-expanded', 'aria-controls', 'tabindex'];
var vulnerabilitiesContainer = $('#show-vulnerabilities-content-container');
if ($('#vulnerabilities-content-container').children().length) {
// If the deprecation information container has content, configure it as an expander.
window.nuget.configureExpander("vulnerabilities-content-container", "ChevronDown", null, "ChevronUp");
configureExpanderWithEnterKeydown(vulnerabilitiesContainer);
} else {
// If the container does not have content, remove its expander attributes
expanderAttributes.forEach(attribute => vulnerabilitiesContainer.removeAttr(attribute));

// The expander should not be clickable when it doesn't have content
vulnerabilitiesContainer.find('.vulnerabilities-expander').removeAttr('role');

$('#vulnerabilities-expander-icon-right').hide();
}

// Configure the deprecation information container
var container = $('#show-deprecation-content-container');
var deprecationContainer = $('#show-deprecation-content-container');
if ($('#deprecation-content-container').children().length) {
// If the deprecation information container has content, configure it as an expander.
window.nuget.configureExpander("deprecation-content-container", "ChevronDown", null, "ChevronUp");
configureExpanderWithEnterKeydown(container)
configureExpanderWithEnterKeydown(deprecationContainer);
}
else {
// If the container does not have content, remove its expander attributes
var expanderAttributes = ['data-toggle', 'data-target', 'aria-expanded', 'aria-controls', 'tabindex'];
for (var i in expanderAttributes) {
container.removeAttr(expanderAttributes[i]);
}
expanderAttributes.forEach(attribute => deprecationContainer.removeAttr(attribute));

// The expander should not be clickable when it doesn't have content
container.find('.deprecation-expander').removeAttr('role');
deprecationContainer.find('.deprecation-expander').removeAttr('role');

$('#deprecation-expander-icon-right').hide();
}
Expand Down
1 change: 1 addition & 0 deletions src/NuGetGallery/ViewModels/DisplayPackageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public bool HasNewerRelease

public PackageDeprecationStatus DeprecationStatus { get; set; }
public IReadOnlyCollection<PackageVulnerability> Vulnerabilities { get; set; }
public bool VulnerabilitiesExist => Vulnerabilities != null && Vulnerabilities.Any();
public PackageVulnerabilitySeverity MaxVulnerabilitySeverity { get; set; }
public string AlternatePackageId { get; set; }
public string AlternatePackageVersion { get; set; }
Expand Down
53 changes: 37 additions & 16 deletions src/NuGetGallery/Views/Packages/DisplayPackage.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,11 @@
)
}

@if (Model.IsPackageVulnerabilitiesEnabled && Model.VulnerabilitiesExist)
{
@Html.Partial("_DisplayPackageVulnerabilities")
}

@if (Model.IsPackageDeprecationEnabled && Model.DeprecationStatus != PackageDeprecationStatus.NotDeprecated)
{
@Html.Partial("_DisplayPackageDeprecation")
Expand Down Expand Up @@ -359,7 +364,7 @@

@AppendAvailableSymbolsMessage(hasSymbolsPackageAvailable)
</text>
)
)
}
else if (Model.LatestSymbolsPackage.StatusKey == PackageStatus.FailedValidation)
{
Expand Down Expand Up @@ -404,7 +409,7 @@
@ViewHelpers.AlertWarning(
@<text>
The owner has unlisted this package.
This could mean that the package is deprecated or shouldn't be used anymore.
This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
</text>
)
}
Expand Down Expand Up @@ -684,9 +689,9 @@
{
<th aria-hidden="true" abbr="Signature Information"></th>
}
@if (Model.IsPackageDeprecationEnabled)
@if (Model.IsPackageDeprecationEnabled || Model.IsPackageVulnerabilitiesEnabled)
{
<th aria-hidden="true" abbr="Deprecation Information"></th>
<th aria-hidden="true" abbr ="Package Warnings" />
}
</tr>
</thead>
Expand Down Expand Up @@ -753,39 +758,55 @@
</td>
}
}
@if (Model.IsPackageDeprecationEnabled)

@if (Model.IsPackageDeprecationEnabled || Model.IsPackageVulnerabilitiesEnabled)
{
if (packageVersion.DeprecationStatus == PackageDeprecationStatus.NotDeprecated)
{
<td class="package-icon-cell" aria-hidden="true"></td>
}
else
var deprecationTitle = "";
var vulnerabilitiesTitle = "";

if (Model.IsPackageDeprecationEnabled && packageVersion.DeprecationStatus != PackageDeprecationStatus.NotDeprecated)
{
var deprecationTitle = packageVersion.Version;
deprecationTitle = packageVersion.Version;
var isLegacy = packageVersion.DeprecationStatus.HasFlag(PackageDeprecationStatus.Legacy);
var hasCriticalBugs = packageVersion.DeprecationStatus.HasFlag(PackageDeprecationStatus.CriticalBugs);
if (hasCriticalBugs)
{
if (isLegacy)
{
deprecationTitle += " is deprecated because it's legacy and has critical bugs.";
deprecationTitle += " is deprecated because it's legacy and has critical bugs";
}
else
{
deprecationTitle += " is deprecated because it has critical bugs.";
deprecationTitle += " is deprecated because it has critical bugs";
}
}
else if (isLegacy)
{
deprecationTitle += " is deprecated because it's legacy and no longer maintained.";
deprecationTitle += " is deprecated because it's legacy and no longer maintained";
}
else
{
deprecationTitle += " is deprecated.";
deprecationTitle += " is deprecated";
}
}

if (Model.IsPackageVulnerabilitiesEnabled && packageVersion.VulnerabilitiesExist)
{
var severity = Enum.GetName(typeof(PackageVulnerabilitySeverity), packageVersion.MaxVulnerabilitySeverity).ToLowerInvariant();
vulnerabilitiesTitle = string.Format("{0} has at least one vulnerability with {1} severity.", packageVersion.Version, severity);
}

if (deprecationTitle == "" && vulnerabilitiesTitle == "")
{
<td class="package-icon-cell" aria-hidden="true" />
}
else
{
deprecationTitle += deprecationTitle != "" && vulnerabilitiesTitle != "" ? "; " : deprecationTitle != "" ? "." : "";
var iconTitle = deprecationTitle + vulnerabilitiesTitle;

<td class="package-icon-cell">
<i class="ms-Icon ms-Icon--Warning package-icon" title="@deprecationTitle"></i>
<i class="ms-Icon ms-Icon--Warning package-icon" title="@iconTitle"></i>
</td>
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@model DisplayPackageViewModel

<div class="vulnerabilities-container">
<div class="icon-text alert alert-warning">
<div id="show-vulnerabilities-content-container" class="vulnerabilities-expander" tabindex="0" data-toggle="collapse" data-target="#vulnerabilities-content-container" aria-expanded="false" aria-controls="vulnerabilities-content-container" aria-labelledby="vulnerabilities-container-label" role="button">
<div class="vulnerabilities-expander" role="button">
<div class="vulnerabilities-expander-container">
<i class="vulnerabilities-expander-icon ms-Icon ms-Icon--Warning" aria-hidden="true"></i>

<div id="vulnerabilities-container-label" class="vulnerabilities-expander-info-right">
@{
var maxSeverity = Enum.GetName(typeof(PackageVulnerabilitySeverity), Model.MaxVulnerabilitySeverity).ToLowerInvariant();
if (Model.CanDisplayPrivateMetadata)
{
@:This package version has at least one vulnerability with a <span class="vulnerabilities-severity-@maxSeverity">@maxSeverity</span> severity. Please unlist, deprecate, and release a new package with a fix.
}
else
{
@:This package has at least one <b>vulnerability</b> with <b>@maxSeverity</b> severity. It may lead to specific problems in your project. Try updating the package version.
}
}
</div>
</div>

<div class="vulnerabilities-expander-container">
<i id="vulnerabilities-expander-icon-right" class="vulnerabilities-expander-icon vulnerabilities-expander-info-right ms-Icon ms-Icon--ChevronDown" aria-hidden="true"></i>
</div>
</div>
</div>

<div class="vulnerabilities-content-container collapse" id="vulnerabilities-content-container">
<b>Details</b>
<table width="100%" class="vulnerabilities-list">
@{
foreach (var vulnerability in Model.Vulnerabilities)
{
var truncatedUrl = vulnerability.AdvisoryUrl;
if (truncatedUrl.Length > 50)
{
truncatedUrl = truncatedUrl.Substring(0, 47) + "...";
}

var vulnerabilitySeverity = Enum.GetName(typeof(PackageVulnerabilitySeverity), vulnerability.Severity).ToLowerInvariant();

<tr>
<td>Advisory: <a href="@vulnerability.AdvisoryUrl" target="_blank">@truncatedUrl</a></td>
<td>Severity: <span class="vulnerabilities-severity-@vulnerabilitySeverity">@vulnerabilitySeverity</span></td>
</tr>
}
}
</table>
</div>
</div>
</div>

0 comments on commit d31453f

Please sign in to comment.