public void GetTrustedSigner_WithEmptyTrustedSignersSection_ReturnsEmptyList() { // Arrange var config = @" <configuration> <trustedSigners> </trustedSigners> </configuration>"; var nugetConfigPath = "NuGet.Config"; using (var mockBaseDirectory = TestDirectory.Create()) { SettingsTestUtils.CreateConfigurationFile(nugetConfigPath, mockBaseDirectory, config); // Act and Assert var settings = new Settings(mockBaseDirectory); settings.Should().NotBeNull(); var trustedSignerProvider = new TrustedSignersProvider(settings); var trustedSigners = trustedSignerProvider.GetTrustedSigners(); trustedSigners.Should().NotBeNull(); trustedSigners.Should().BeEmpty(); } }
public void GetAllowListEntries_WithDuplicateEntries_ConflictingAllowUntrustedRoot_WarnsAndSetsToFalse() { // Arrange var config = @" <configuration> <trustedSigners> <repository name=""repository1"" serviceIndex=""api.v3ServiceIndex.test/json""> <certificate fingerprint=""abc"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <certificate fingerprint=""def"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <certificate fingerprint=""abc"" hashAlgorithm=""SHA512"" allowUntrustedRoot=""false"" /> <owners>nuget;randomOwner</owners> </repository> <author name=""author1""> <certificate fingerprint=""jkl"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <certificate fingerprint=""abc"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""true"" /> </author> </trustedSigners> </configuration>"; var nugetConfigPath = "NuGet.Config"; using (var mockBaseDirectory = TestDirectory.Create()) { SettingsTestUtils.CreateConfigurationFile(nugetConfigPath, mockBaseDirectory, config); // Act and Assert var settings = new Settings(mockBaseDirectory); settings.Should().NotBeNull(); var logger = new Mock <ILogger>(); var entries = TrustedSignersProvider.GetAllowListEntries(settings, logger.Object); entries.Should().NotBeNull(); entries.Count.Should().Be(4); logger.Verify(l => l.Log(It.Is <ILogMessage>(m => m.Level == LogLevel.Warning && m.Code == NuGetLogCode.NU3040))); var expectedEntries = new List <VerificationAllowListEntry>() { new TrustedSignerAllowListEntry(VerificationTarget.Repository, SignaturePlacement.Any, "abc", HashAlgorithmName.SHA512, owners: new List <string>() { "nuget", "randomOwner" }), new TrustedSignerAllowListEntry(VerificationTarget.Author | VerificationTarget.Repository, SignaturePlacement.Any, "abc", HashAlgorithmName.SHA256, owners: new List <string>() { "nuget", "randomOwner" }), new TrustedSignerAllowListEntry(VerificationTarget.Repository, SignaturePlacement.Any, "def", HashAlgorithmName.SHA256, owners: new List <string>() { "nuget", "randomOwner" }), new TrustedSignerAllowListEntry(VerificationTarget.Author, SignaturePlacement.PrimarySignature, "jkl", HashAlgorithmName.SHA256), }; entries.Should().BeEquivalentTo(expectedEntries); } }
public void GetAllowListEntries_WithNullLogger_Throws() { // Act and Assert var ex = Record.Exception(() => TrustedSignersProvider.GetAllowListEntries(settings: NullSettings.Instance, logger: null)); ex.Should().NotBeNull(); ex.Should().BeOfType <ArgumentNullException>(); }
public void AddOrUpdateTrustedSigner_WithNullItem_Throws() { var trustedSignersProvider = new TrustedSignersProvider(settings: NullSettings.Instance); // Act and Assert var ex = Record.Exception(() => trustedSignersProvider.AddOrUpdateTrustedSigner(trustedSigner: null)); ex.Should().NotBeNull(); ex.Should().BeOfType <ArgumentNullException>(); }
public void Remove_WithEmptyListOFSignersToRemove_Throws() { var trustedSignersProvider = new TrustedSignersProvider(settings: NullSettings.Instance); // Act and Assert var ex = Record.Exception(() => trustedSignersProvider.Remove(trustedSigners: new List <TrustedSignerItem>())); ex.Should().NotBeNull(); ex.Should().BeOfType <ArgumentException>(); }
public void GetAllowListEntries_WithDuplicateFingerprints_DifferentHashAlgorithm_TakesThemAsDifferentEntries() { // Arrange var config = @" <configuration> <trustedSigners> <repository name=""repository1"" serviceIndex=""api.v3ServiceIndex.test/json""> <certificate fingerprint=""abc"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <certificate fingerprint=""def"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <certificate fingerprint=""abc"" hashAlgorithm=""SHA512"" allowUntrustedRoot=""false"" /> <owners>nuget;randomOwner</owners> </repository> <author name=""author1""> <certificate fingerprint=""jkl"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <certificate fingerprint=""abc"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </author> </trustedSigners> </configuration>"; var nugetConfigPath = "NuGet.Config"; using (var mockBaseDirectory = TestDirectory.Create()) { SettingsTestUtils.CreateConfigurationFile(nugetConfigPath, mockBaseDirectory, config); // Act and Assert var settings = new Settings(mockBaseDirectory); settings.Should().NotBeNull(); var entries = TrustedSignersProvider.GetAllowListEntries(settings, NullLogger.Instance); entries.Should().NotBeNull(); entries.Count.Should().Be(4); var expectedEntries = new List <VerificationAllowListEntry>() { new TrustedSignerAllowListEntry(VerificationTarget.Repository, SignaturePlacement.Any, "abc", HashAlgorithmName.SHA512, owners: new List <string>() { "nuget", "randomOwner" }), new TrustedSignerAllowListEntry(VerificationTarget.Author | VerificationTarget.Repository, SignaturePlacement.Any, "abc", HashAlgorithmName.SHA256, owners: new List <string>() { "nuget", "randomOwner" }), new TrustedSignerAllowListEntry(VerificationTarget.Repository, SignaturePlacement.Any, "def", HashAlgorithmName.SHA256, owners: new List <string>() { "nuget", "randomOwner" }), new TrustedSignerAllowListEntry(VerificationTarget.Author, SignaturePlacement.PrimarySignature, "jkl", HashAlgorithmName.SHA256), }; entries.Should().BeEquivalentTo(expectedEntries); } }
public void AddOrUpdateTrustedSigner_WithNewTrustedSigner_AddsItSuccesfully() { // Arrange var config = @" <configuration> <trustedSigners> <author name=""author1""> <certificate fingerprint=""abc"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </author> <author name=""author2""> <certificate fingerprint=""def"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </author> <repository name=""repo1"" serviceIndex=""https://serviceIndex.test/v3/api.json""> <certificate fingerprint=""ghi"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </repository> </trustedSigners> </configuration>"; var nugetConfigPath = "NuGet.Config"; using (var mockBaseDirectory = TestDirectory.Create()) { SettingsTestUtils.CreateConfigurationFile(nugetConfigPath, mockBaseDirectory, config); var expectedTrustedSigners = new List <TrustedSignerItem>() { new AuthorItem("author1", new CertificateItem("abc", HashAlgorithmName.SHA256)), new AuthorItem("author2", new CertificateItem("def", HashAlgorithmName.SHA256)), new RepositoryItem("repo1", "https://serviceIndex.test/v3/api.json", new CertificateItem("ghi", HashAlgorithmName.SHA256)), new AuthorItem("author3", new CertificateItem("jkl", HashAlgorithmName.SHA256)), }; // Act and Assert var settings = new Settings(mockBaseDirectory); settings.Should().NotBeNull(); var trustedSignerProvider = new TrustedSignersProvider(settings); trustedSignerProvider.AddOrUpdateTrustedSigner(new AuthorItem("author3", new CertificateItem("jkl", HashAlgorithmName.SHA256))); var trustedSigners = trustedSignerProvider.GetTrustedSigners(); trustedSigners.Should().NotBeNull(); trustedSigners.Count.Should().Be(4); trustedSigners.Should().BeEquivalentTo( expectedTrustedSigners, options => options .Excluding(o => o.Path == "[0].Origin") .Excluding(o => o.Path == "[1].Origin") .Excluding(o => o.Path == "[2].Origin") .Excluding(o => o.Path == "[3].Origin")); } }
public override async Task ExecuteCommandAsync() { var actionString = Arguments.FirstOrDefault(); TrustedSignersAction action; if (string.IsNullOrEmpty(actionString)) { action = TrustedSignersAction.List; } else if (!Enum.TryParse(actionString, ignoreCase: true, result: out action)) { throw new CommandLineArgumentCombinationException(string.Format(CultureInfo.CurrentCulture, NuGetResources.Error_UnknownAction, actionString)); } string packagePath = null; if (Arguments.Count() > 1) { packagePath = Arguments[1]; } var trustedSignersProvider = new TrustedSignersProvider(Settings); var trustedSignersArgs = new TrustedSignersArgs() { Action = action, PackagePath = packagePath, Name = Name, ServiceIndex = ServiceIndex, CertificateFingerprint = CertificateFingerprint, FingerprintAlgorithm = FingerprintAlgorithm, AllowUntrustedRoot = AllowUntrustedRoot, Author = Author, Repository = Repository, Owners = Owners, Logger = Console }; if (TrustedSignersCommandRunner == null) { TrustedSignersCommandRunner = new TrustedSignersCommandRunner(trustedSignersProvider, SourceProvider); } var result = await TrustedSignersCommandRunner.ExecuteCommandAsync(trustedSignersArgs); if (result > 0) { throw new ExitCodeException(1); } }
public void GetTrustedSigner_WithItemsDifferentThanTrustedSigners_IgnoresThemAndOnlyReturnsTrustedSigners() { // Arrange var config = @" <configuration> <trustedSigners> <add key=""oneKey"" value=""val"" /> <author name=""author1""> <certificate fingerprint=""abc"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </author> <author name=""author2""> <certificate fingerprint=""def"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </author> <certificate fingerprint=""ghi"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <repository name=""repo1"" serviceIndex=""https://serviceIndex.test/v3/api.json""> <certificate fingerprint=""ghi"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </repository> <add key=""otherKey"" value=""val"" /> </trustedSigners> </configuration>"; var nugetConfigPath = "NuGet.Config"; using (var mockBaseDirectory = TestDirectory.Create()) { SettingsTestUtils.CreateConfigurationFile(nugetConfigPath, mockBaseDirectory, config); var expectedTrustedSigners = new List <TrustedSignerItem>() { new AuthorItem("author1", new CertificateItem("abc", HashAlgorithmName.SHA256)), new AuthorItem("author2", new CertificateItem("def", HashAlgorithmName.SHA256)), new RepositoryItem("repo1", "https://serviceIndex.test/v3/api.json", new CertificateItem("ghi", HashAlgorithmName.SHA256)) }; // Act and Assert var settings = new Settings(mockBaseDirectory); settings.Should().NotBeNull(); var trustedSignerProvider = new TrustedSignersProvider(settings); var trustedSigners = trustedSignerProvider.GetTrustedSigners(); trustedSigners.Should().NotBeNull(); trustedSigners.Count.Should().Be(3); trustedSigners.Should().BeEquivalentTo( expectedTrustedSigners, options => options .Excluding(o => o.SelectedMemberPath == "[0].Origin") .Excluding(o => o.SelectedMemberPath == "[1].Origin") .Excluding(o => o.SelectedMemberPath == "[2].Origin")); } }
public void Remove_IgnoresAnyUnexistantTrustedSigner() { // Arrange var config = @" <configuration> <trustedSigners> <author name=""author1""> <certificate fingerprint=""abc"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </author> <author name=""author2""> <certificate fingerprint=""def"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </author> <repository name=""repo1"" serviceIndex=""https://serviceIndex.test/v3/api.json""> <certificate fingerprint=""ghi"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </repository> </trustedSigners> </configuration>"; var nugetConfigPath = "NuGet.Config"; using (var mockBaseDirectory = TestDirectory.Create()) { SettingsTestUtils.CreateConfigurationFile(nugetConfigPath, mockBaseDirectory, config); var expectedTrustedSigners = new List <TrustedSignerItem>() { new AuthorItem("author2", new CertificateItem("def", HashAlgorithmName.SHA256)), new RepositoryItem("repo1", "https://serviceIndex.test/v3/api.json", new CertificateItem("ghi", HashAlgorithmName.SHA256)) }; // Act and Assert var settings = new Settings(mockBaseDirectory); settings.Should().NotBeNull(); var trustedSignerProvider = new TrustedSignersProvider(settings); var trustedSigners = trustedSignerProvider.GetTrustedSigners(); trustedSignerProvider.Remove(new[] { trustedSigners.First(), new AuthorItem("DontExist", new CertificateItem("abc", HashAlgorithmName.SHA256)) }); trustedSigners = trustedSignerProvider.GetTrustedSigners(); trustedSigners.Should().NotBeNull(); trustedSigners.Count.Should().Be(2); trustedSigners.Should().BeEquivalentTo(expectedTrustedSigners); } }
public void GetAllowListEntries_TrustedRepository_WithMultipleCertificates_ParsedCorrectly() { // Arrange var config = @" <configuration> <trustedSigners> <repository name=""repository1"" serviceIndex=""api.v3ServiceIndex.test/json""> <certificate fingerprint=""abc"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <certificate fingerprint=""def"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <certificate fingerprint=""ghi"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </repository> <repository name=""repository2"" serviceIndex=""api.v3ServiceIndex.test/json""> <certificate fingerprint=""jkl"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> <certificate fingerprint=""mno"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </repository> </trustedSigners> </configuration>"; var nugetConfigPath = "NuGet.Config"; using (var mockBaseDirectory = TestDirectory.Create()) { SettingsTestUtils.CreateConfigurationFile(nugetConfigPath, mockBaseDirectory, config); // Act and Assert var settings = new Settings(mockBaseDirectory); settings.Should().NotBeNull(); var entries = TrustedSignersProvider.GetAllowListEntries(settings, NullLogger.Instance); entries.Should().NotBeNull(); entries.Count.Should().Be(5); var expectedEntries = new List <VerificationAllowListEntry>() { new TrustedSignerAllowListEntry(VerificationTarget.Repository, SignaturePlacement.Any, "abc", HashAlgorithmName.SHA256), new TrustedSignerAllowListEntry(VerificationTarget.Repository, SignaturePlacement.Any, "def", HashAlgorithmName.SHA256), new TrustedSignerAllowListEntry(VerificationTarget.Repository, SignaturePlacement.Any, "ghi", HashAlgorithmName.SHA256), new TrustedSignerAllowListEntry(VerificationTarget.Repository, SignaturePlacement.Any, "jkl", HashAlgorithmName.SHA256), new TrustedSignerAllowListEntry(VerificationTarget.Repository, SignaturePlacement.Any, "mno", HashAlgorithmName.SHA256) }; entries.Should().BeEquivalentTo(expectedEntries); } }
public void GetAllowListEntries_WithoutTrustedSignersSection_ReturnsEmptyList() { // Arrange var config = @" <configuration> </configuration>"; var nugetConfigPath = "NuGet.Config"; using (var mockBaseDirectory = TestDirectory.Create()) { SettingsTestUtils.CreateConfigurationFile(nugetConfigPath, mockBaseDirectory, config); // Act and Assert var settings = new Settings(mockBaseDirectory); settings.Should().NotBeNull(); var entries = TrustedSignersProvider.GetAllowListEntries(settings, NullLogger.Instance); entries.Should().NotBeNull(); entries.Should().BeEmpty(); } }
public void GetAllowListEntries_TrustedAuthor_WithOneCertificate_ParsedCorrectly() { // Arrange var config = @" <configuration> <trustedSigners> <author name=""author1""> <certificate fingerprint=""abc"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </author> <author name=""author2""> <certificate fingerprint=""def"" hashAlgorithm=""SHA256"" allowUntrustedRoot=""false"" /> </author> </trustedSigners> </configuration>"; var nugetConfigPath = "NuGet.Config"; using (var mockBaseDirectory = TestDirectory.Create()) { SettingsTestUtils.CreateConfigurationFile(nugetConfigPath, mockBaseDirectory, config); // Act and Assert var settings = new Settings(mockBaseDirectory); settings.Should().NotBeNull(); var entries = TrustedSignersProvider.GetAllowListEntries(settings, NullLogger.Instance); entries.Should().NotBeNull(); entries.Count.Should().Be(2); var expectedEntries = new List <VerificationAllowListEntry>() { new TrustedSignerAllowListEntry(VerificationTarget.Author, SignaturePlacement.PrimarySignature, "abc", HashAlgorithmName.SHA256), new TrustedSignerAllowListEntry(VerificationTarget.Author, SignaturePlacement.PrimarySignature, "def", HashAlgorithmName.SHA256) }; entries.Should().BeEquivalentTo(expectedEntries); } }
private static async Task <int> ExecuteCommand(TrustCommand action, CommandOption algorithm, bool allowUntrustedRootOption, CommandOption owners, CommandOption verbosity, CommandOption configFile, Func <ILogger> getLogger, Action <LogLevel> setLogLevel, string name = null, string sourceUrl = null, string packagePath = null, string fingerprint = null) { ILogger logger = getLogger(); try { ISettings settings = XPlatUtility.ProcessConfigFile(configFile.Value()); var trustedSignersArgs = new TrustedSignersArgs() { Action = MapTrustEnumAction(action), PackagePath = packagePath, Name = name, ServiceIndex = sourceUrl, CertificateFingerprint = fingerprint, FingerprintAlgorithm = algorithm?.Value(), AllowUntrustedRoot = allowUntrustedRootOption, Author = action == TrustCommand.Author, Repository = action == TrustCommand.Repository, Owners = CommandLineUtility.SplitAndJoinAcrossMultipleValues(owners?.Values), Logger = logger }; setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value())); // Add is the only action which does certificate chain building. if (trustedSignersArgs.Action == TrustedSignersAction.Add) { X509TrustStore.InitializeForDotNetSdk(logger); } #pragma warning disable CS0618 // Type or member is obsolete var sourceProvider = new PackageSourceProvider(settings, enablePackageSourcesChangedEvent: false); #pragma warning restore CS0618 // Type or member is obsolete var trustedSignersProvider = new TrustedSignersProvider(settings); var runner = new TrustedSignersCommandRunner(trustedSignersProvider, sourceProvider); Task <int> trustedSignTask = runner.ExecuteCommandAsync(trustedSignersArgs); return(await trustedSignTask); } catch (InvalidOperationException e) { // nuget trust command handled exceptions. if (!string.IsNullOrWhiteSpace(name)) { var error_TrustedSignerAlreadyExistsMessage = string.Format(CultureInfo.CurrentCulture, Strings.Error_TrustedSignerAlreadyExists, name); if (e.Message == error_TrustedSignerAlreadyExistsMessage) { logger.LogError(error_TrustedSignerAlreadyExistsMessage); return(1); } } if (!string.IsNullOrWhiteSpace(sourceUrl)) { var error_TrustedRepoAlreadyExists = string.Format(CultureInfo.CurrentCulture, Strings.Error_TrustedRepoAlreadyExists, sourceUrl); if (e.Message == error_TrustedRepoAlreadyExists) { logger.LogError(error_TrustedRepoAlreadyExists); return(1); } } throw; } catch (ArgumentException e) { if (e.Data is System.Collections.IDictionary) { logger.LogError(string.Format(CultureInfo.CurrentCulture, Strings.Error_TrustFingerPrintAlreadyExist)); return(1); } throw; } }
internal static void Register(CommandLineApplication app, Func <ILogger> getLogger, Action <LogLevel> setLogLevel) { app.Command("trust", trustedSignersCmd => { CommandArgument command = trustedSignersCmd.Argument( "<command>", Strings.TrustCommandActionDescription, multipleValues: true); CommandOption algorithm = trustedSignersCmd.Option( "--algorithm", Strings.TrustCommandAlgorithm, CommandOptionType.SingleValue); CommandOption allowUntrustedRootOption = trustedSignersCmd.Option( "--allow-untrusted-root", Strings.TrustCommandAllowUntrustedRoot, CommandOptionType.NoValue); CommandOption owners = trustedSignersCmd.Option( "--owners", Strings.TrustCommandOwners, CommandOptionType.MultipleValue); CommandOption verbosity = trustedSignersCmd.Option( "-v|--verbosity", Strings.Verbosity_Description, CommandOptionType.SingleValue); CommandOption configFile = trustedSignersCmd.Option( "--configfile", Strings.Option_ConfigFile, CommandOptionType.SingleValue); trustedSignersCmd.HelpOption(XPlatUtility.HelpOption); trustedSignersCmd.Description = Strings.TrustCommandDescription; trustedSignersCmd.OnExecute(async() => { TrustCommand action; if (!command.Values.Any() || string.IsNullOrEmpty(command.Values[0])) { action = TrustCommand.List; } else if (!Enum.TryParse(command.Values[0], ignoreCase: true, result: out action)) { throw new CommandLineArgumentCombinationException(string.Format(CultureInfo.CurrentCulture, Strings.Error_UnknownAction, command.Values[0])); } string name = null; if (command.Values.Count > 1) { name = command.Values[1]; } string packagePath = null; string sourceUrl = null; string fingerprint = null; if (command.Values.Count() > 2) { if (action == TrustCommand.Author || action == TrustCommand.Repository) { packagePath = command.Values[2]; } else if (action == TrustCommand.Source) { sourceUrl = command.Values[2]; } else if (action == TrustCommand.Certificate) { fingerprint = command.Values[2]; } } ISettings settings = ProcessConfigFile(configFile.Value()); var trustedSignersArgs = new TrustedSignersArgs() { Action = MapTrustEnumAction(action), PackagePath = packagePath, Name = name, ServiceIndex = sourceUrl, CertificateFingerprint = fingerprint, FingerprintAlgorithm = algorithm.Value(), AllowUntrustedRoot = allowUntrustedRootOption.HasValue(), Author = action == TrustCommand.Author, Repository = action == TrustCommand.Repository, Owners = CommandLineUtility.SplitAndJoinAcrossMultipleValues(owners.Values), Logger = getLogger() }; setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value())); #pragma warning disable CS0618 // Type or member is obsolete var sourceProvider = new PackageSourceProvider(settings, enablePackageSourcesChangedEvent: false); #pragma warning restore CS0618 // Type or member is obsolete var trustedSignersProvider = new TrustedSignersProvider(settings); var runner = new TrustedSignersCommandRunner(trustedSignersProvider, sourceProvider); Task <int> trustedSignTask = runner.ExecuteCommandAsync(trustedSignersArgs); return(await trustedSignTask); }); }); }