Skip to content

Commit 62d038d

Browse files
authored
Configurable rejection of packages with <license> (#6562)
* Added the RejectPackagesWithLicense configuration item that enables rejecting any package that contains <license> metadata in it. * Addressed feedback. * empty line removed.
1 parent 7aa650f commit 62d038d

9 files changed

Lines changed: 125 additions & 8 deletions

File tree

src/NuGetGallery/Configuration/AppConfiguration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,5 +352,8 @@ public string ExternalBrandingMessage
352352
[DefaultValue(null)]
353353
[TypeConverter(typeof(StringArrayConverter))]
354354
public string[] RedirectedCuratedFeeds { get; set; }
355+
356+
[DefaultValue(false)]
357+
public bool RejectPackagesWithLicense { get; set; }
355358
}
356359
}

src/NuGetGallery/Configuration/IAppConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,5 +360,10 @@ public interface IAppConfiguration : IMessageServiceConfiguration
360360
/// The name of zero or more curated feeds that are redirected to the main feed.
361361
/// </summary>
362362
string[] RedirectedCuratedFeeds { get; set; }
363+
364+
/// <summary>
365+
/// Flag that indicates whether packages with `license` node in them should be rejected.
366+
/// </summary>
367+
bool RejectPackagesWithLicense { get; set; }
363368
}
364369
}

src/NuGetGallery/Services/PackageUploadService.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace NuGetGallery
1717
{
1818
public class PackageUploadService : IPackageUploadService
1919
{
20+
private const string LicenseNodeName = "license";
2021
private readonly IPackageService _packageService;
2122
private readonly IPackageFileService _packageFileService;
2223
private readonly IEntitiesContext _entitiesContext;
@@ -70,8 +71,51 @@ public async Task<PackageValidationResult> ValidateBeforeGeneratePackageAsync(Pa
7071
return result;
7172
}
7273

74+
result = CheckLicenseMetadata(nuGetPackage);
75+
76+
if (result != null)
77+
{
78+
return result;
79+
}
80+
7381
return PackageValidationResult.AcceptedWithWarnings(warnings);
7482
}
83+
84+
private class LicenseCheckingNuspecReader : NuspecReader
85+
{
86+
public LicenseCheckingNuspecReader(Stream stream)
87+
: base(stream)
88+
{
89+
90+
}
91+
92+
public bool HasLicenseMetadata()
93+
=> MetadataNode
94+
.Elements()
95+
.Any(e => LicenseNodeName == e.Name.LocalName);
96+
}
97+
98+
private PackageValidationResult CheckLicenseMetadata(PackageArchiveReader nuGetPackage)
99+
{
100+
if (!_config.RejectPackagesWithLicense)
101+
{
102+
return null;
103+
}
104+
105+
LicenseCheckingNuspecReader nuspecReader = null;
106+
107+
using (var nuspec = nuGetPackage.GetNuspec())
108+
{
109+
nuspecReader = new LicenseCheckingNuspecReader(nuspec);
110+
}
111+
112+
if (nuspecReader.HasLicenseMetadata())
113+
{
114+
return PackageValidationResult.Invalid(Strings.UploadPackage_NotAcceptingPackagesWithLicense);
115+
}
116+
117+
return null;
118+
}
75119

76120
private async Task<PackageValidationResult> CheckPackageEntryCountAsync(
77121
PackageArchiveReader nuGetPackage,

src/NuGetGallery/Strings.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/NuGetGallery/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,4 +996,7 @@ Note that NuGet.org password login is deprecated. Please use Microsoft account t
996996
Thanks,
997997
The {1} Team</value>
998998
</data>
999+
<data name="UploadPackage_NotAcceptingPackagesWithLicense" xml:space="preserve">
1000+
<value>This package contains a &lt;license&gt; metadata which is currently not supported.</value>
1001+
</data>
9991002
</root>

src/NuGetGallery/Web.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@
179179
<add key="PackageDelete.StatisticsUpdateFrequencyInHours" value=""/>
180180
<add key="PackageDelete.HourLimitWithMaximumDownloads" value=""/>
181181
<add key="PackageDelete.MaximumDownloadsForPackageVersion" value=""/>
182+
183+
<add key="Gallery.RejectPackagesWithLicense" value="true" />
182184
</appSettings>
183185
<connectionStrings>
184186
<add name="Gallery.SqlServer" connectionString="Data Source=(localdb)\mssqllocaldb; Initial Catalog=NuGetGallery; Integrated Security=True; MultipleActiveResultSets=True" providerName="System.Data.SqlClient"/>

tests/NuGetGallery.Facts/Services/PackageUploadServiceFacts.cs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using System.Collections.Generic;
66
using System.IO;
7-
using System.IO.Compression;
87
using System.Linq;
98
using System.Threading;
109
using System.Threading.Tasks;
@@ -441,6 +440,49 @@ public async Task WithNotTooManyPackageEntries_WhenRejectPackagesWithTooManyPack
441440
Assert.Empty(result.Warnings);
442441
}
443442

443+
private static string[] LicenseNodeVariants => new string[]
444+
{
445+
"<license/>",
446+
"<license></license>",
447+
"<license> </license>",
448+
"<license>ttt</license>",
449+
"<license type='file'>fff</license>",
450+
"<license type='expression'>ee</license>",
451+
"<license type='foobar'>ttt</license>",
452+
};
453+
454+
public static IEnumerable<object[]> RejectsLicensedPackagesWhenConfigured_Input =>
455+
from licenseNode in LicenseNodeVariants
456+
from rejectLicenseSetting in new[] { false, true }
457+
select new object[] { licenseNode, rejectLicenseSetting, !rejectLicenseSetting };
458+
459+
[Theory]
460+
[MemberData(nameof(RejectsLicensedPackagesWhenConfigured_Input))]
461+
public async Task RejectsLicensedPackagesWhenConfigured(string licenseNode, bool rejectPackagesWithLicense, bool expectedSuccess)
462+
{
463+
_config
464+
.SetupGet(x => x.RejectPackagesWithLicense)
465+
.Returns(rejectPackagesWithLicense);
466+
_nuGetPackage = GeneratePackage(getCustomNuspecNodes: () => licenseNode);
467+
468+
var result = await _target.ValidateBeforeGeneratePackageAsync(
469+
_nuGetPackage.Object,
470+
GetPackageMetadata(_nuGetPackage));
471+
472+
if (expectedSuccess)
473+
{
474+
Assert.Equal(PackageValidationResultType.Accepted, result.Type);
475+
Assert.Null(result.Message);
476+
Assert.Empty(result.Warnings);
477+
}
478+
else
479+
{
480+
Assert.Equal(PackageValidationResultType.Invalid, result.Type);
481+
Assert.Contains("license", result.Message);
482+
Assert.Empty(result.Warnings);
483+
}
484+
}
485+
444486
private PackageMetadata GetPackageMetadata(Mock<TestPackageReader> mockPackage)
445487
{
446488
return PackageMetadata.FromNuspecReader(mockPackage.Object.GetNuspecReader(), strict: true);
@@ -1100,14 +1142,16 @@ protected static Mock<TestPackageReader> GeneratePackage(
11001142
string version = "1.2.3-alpha.0",
11011143
RepositoryMetadata repositoryMetadata = null,
11021144
bool isSigned = true,
1103-
int? desiredTotalEntryCount = null)
1145+
int? desiredTotalEntryCount = null,
1146+
Func<string> getCustomNuspecNodes = null)
11041147
{
11051148
return PackageServiceUtility.CreateNuGetPackage(
11061149
id: "theId",
11071150
version: version,
11081151
repositoryMetadata: repositoryMetadata,
11091152
isSigned: isSigned,
1110-
desiredTotalEntryCount: desiredTotalEntryCount);
1153+
desiredTotalEntryCount: desiredTotalEntryCount,
1154+
getCustomNuspecNodes: getCustomNuspecNodes);
11111155
}
11121156
}
11131157
}

tests/NuGetGallery.Facts/TestPackage.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public static void WriteNuspec(
3737
IEnumerable<PackageDependencyGroup> packageDependencyGroups = null,
3838
IEnumerable<ClientPackageType> packageTypes = null,
3939
bool isSymbolPackage = false,
40-
RepositoryMetadata repositoryMetadata = null)
40+
RepositoryMetadata repositoryMetadata = null,
41+
Func<string> getCustomNodes = null)
4142
{
4243
var fullNuspec = (@"<?xml version=""1.0""?>
4344
<package xmlns=""http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"">
@@ -60,6 +61,7 @@ public static void WriteNuspec(
6061
<packageTypes>" + WritePackageTypes(packageTypes) + @"</packageTypes>
6162
<dependencies>" + WriteDependencies(packageDependencyGroups) + @"</dependencies>
6263
" + WriteRepositoryMetadata(repositoryMetadata) + @"
64+
" + (getCustomNodes != null ? getCustomNodes() : "") + @"
6365
</metadata>
6466
</package>");
6567

@@ -172,7 +174,8 @@ public static Stream CreateTestPackageStream(
172174
RepositoryMetadata repositoryMetadata = null,
173175
Action<ZipArchive> populatePackage = null,
174176
bool isSymbolPackage = false,
175-
int? desiredTotalEntryCount = null)
177+
int? desiredTotalEntryCount = null,
178+
Func<string> getCustomNuspecNodes = null)
176179
{
177180
return CreateTestPackageStream(packageArchive =>
178181
{
@@ -181,7 +184,8 @@ public static Stream CreateTestPackageStream(
181184
{
182185
WriteNuspec(stream, true, id, version, title, summary, authors, owners, description, tags, language,
183186
copyright, releaseNotes, minClientVersion, licenseUrl, projectUrl, iconUrl,
184-
requireLicenseAcceptance, packageDependencyGroups, packageTypes, isSymbolPackage, repositoryMetadata);
187+
requireLicenseAcceptance, packageDependencyGroups, packageTypes, isSymbolPackage, repositoryMetadata,
188+
getCustomNuspecNodes);
185189
}
186190

187191
if (populatePackage != null)

tests/NuGetGallery.Facts/TestUtils/TestServiceUtility.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ public static Mock<TestPackageReader> CreateNuGetPackage(
8181
IEnumerable<NuGet.Packaging.Core.PackageType> packageTypes = null,
8282
RepositoryMetadata repositoryMetadata = null,
8383
bool isSigned = false,
84-
int? desiredTotalEntryCount = null)
84+
int? desiredTotalEntryCount = null,
85+
Func<string> getCustomNuspecNodes = null)
8586
{
8687
if (packageDependencyGroups == null)
8788
{
@@ -139,7 +140,9 @@ public static Mock<TestPackageReader> CreateNuGetPackage(
139140
writer.Write("Fake signature file.");
140141
}
141142
}
142-
}, desiredTotalEntryCount: desiredTotalEntryCount);
143+
},
144+
desiredTotalEntryCount: desiredTotalEntryCount,
145+
getCustomNuspecNodes: getCustomNuspecNodes);
143146

144147
var mock = new Mock<TestPackageReader>(testPackage);
145148
mock.CallBase = true;

0 commit comments

Comments
 (0)