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 17, 2020
1 parent 91e8d13 commit 7d3afb4
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 24 deletions.
41 changes: 40 additions & 1 deletion src/Bootstrap/dist/css/bootstrap-theme.css

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

40 changes: 40 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,46 @@
}
}

.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-expander-severity-rating {
margin-left: 5px;
}
}
}

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

p {
margin-top: 5px;
}

p:last-of-type {
margin-bottom: 0px;
}
}
}

.failed-validation-alert-list {
margin-top: 15px;
margin-bottom: 15px;
Expand Down
7 changes: 0 additions & 7 deletions src/NuGetGallery.Core/Entities/EntitiesContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -491,13 +491,6 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder)
.HasIndex(pv => new { pv.VulnerabilityKey, pv.PackageId, pv.PackageVersionRange })
.IsUnique();

modelBuilder.Entity<VulnerablePackageVersionRange>()
.HasMany<Package>(vpvr => vpvr.Packages)
.WithMany(p => p.VulnerablePackageRanges)
.Map(c => c.ToTable("VulnerablePackageVersionRangePackages")
.MapLeftKey("VulnerablePackageVersionRange_Key")
.MapRightKey("Package_Key"));

modelBuilder.Entity<PackageRename>()
.HasKey(r => r.Key)
.HasIndex(r => r.TransferPopularity);
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
22 changes: 18 additions & 4 deletions src/NuGetGallery/Scripts/gallery/page-display-package.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ $(function () {
window.nuget.configureExpander("rename-content-container", "ChevronDown", null, "ChevronUp");
configureExpanderWithEnterKeydown($('#show-rename-content-container'));

// Configure the vulnerability information container
var container = $('#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(container)
}
else {
// If the container does not have content, remove its expander attributes
expanderAttributes.forEach(attribute => container.removeAttr(attribute));

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

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

// Configure the deprecation information container
var container = $('#show-deprecation-content-container');
if ($('#deprecation-content-container').children().length) {
Expand All @@ -14,10 +31,7 @@ $(function () {
}
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 => container.removeAttr(attribute));

// The expander should not be clickable when it doesn't have content
container.find('.deprecation-expander').removeAttr('role');
Expand Down
67 changes: 55 additions & 12 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.Vulnerabilities!= null && Model.Vulnerabilities.Any())
{
@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,23 @@
{
<th aria-hidden="true" abbr="Signature Information"></th>
}
@if (Model.IsPackageDeprecationEnabled)
@if (Model.IsPackageDeprecationEnabled || Model.IsPackageVulnerabilitiesEnabled)
{
<th aria-hidden="true" abbr="Deprecation Information"></th>
var abbreviation = "";
if (Model.IsPackageDeprecationEnabled && Model.IsPackageVulnerabilitiesEnabled)
{
abbreviation = "Deprecation/Vulnerability Information";
}
else if (Model.IsPackageDeprecationEnabled)
{
abbreviation = "Deprecation Information";
}
else
{
abbreviation = "Vulnerability Information";
}

<th aria-hidden="true" abbr ="@abbreviation" />
}
</tr>
</thead>
Expand Down Expand Up @@ -753,15 +772,16 @@
</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 = "";
var useAlso = false;

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)
Expand All @@ -784,8 +804,31 @@
deprecationTitle += " is deprecated.";
}

useAlso = true;
}

if (Model.IsPackageVulnerabilitiesEnabled && packageVersion.Vulnerabilities != null && packageVersion.Vulnerabilities.Any())
{
vulnerabilitiesTitle = useAlso ? "Also, " : "";
var severity = Enum.GetName(typeof(PackageVulnerabilitySeverity), packageVersion.MaxVulnerabilitySeverity).ToLowerInvariant();
vulnerabilitiesTitle += packageVersion.Version + " has at least one vulnerability with " + severity + " severity.";
}

if (deprecationTitle == "" && vulnerabilitiesTitle == "")
{
<td class="package-icon-cell" aria-hidden="true" />
}
else
{
if (deprecationTitle != "")
{
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,45 @@
@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 severity = Enum.GetName(typeof(PackageVulnerabilitySeverity), Model.MaxVulnerabilitySeverity).ToLowerInvariant();
@:This package has at least one <b>vulnerability</b> with <b>@severity</b> severity.
}
</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="table borderless">
@{
foreach (var vulnerability in Model.Vulnerabilities)
{
var truncatedUrl = vulnerability.AdvisoryUrl;
if (truncatedUrl.Length > 50)
{
truncatedUrl = truncatedUrl.Substring(0, 47) + "...";
}

<tr>
<td>Advisory: <a href="@vulnerability.AdvisoryUrl" target="_blank">@truncatedUrl</a></td>
<td>Severity: <b>@Enum.GetName(typeof(PackageVulnerabilitySeverity), vulnerability.Severity)</b></td>
</tr>
}
}
</table>
</div>
</div>
</div>

0 comments on commit 7d3afb4

Please sign in to comment.