Skip to content

Commit

Permalink
Signing: enable NuGet signature verification by default on Linux (#31868
Browse files Browse the repository at this point in the history
  • Loading branch information
dtivel committed Apr 20, 2023
1 parent d9858bc commit 3eafa1f
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Cli/dotnet/NuGetForwardingApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public NuGetForwardingApp(IEnumerable<string> argsToForward)
_forwardingApp = new ForwardingApp(
GetNuGetExePath(),
argsToForward);

NuGetSignatureVerificationEnabler.ConditionallyEnable(_forwardingApp);
}

public int Execute()
Expand Down
61 changes: 61 additions & 0 deletions src/Cli/dotnet/NuGetSignatureVerificationEnabler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#nullable enable

using System;
using System.Runtime.InteropServices;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools.MSBuild;

namespace Microsoft.DotNet.Tools
{
public static class NuGetSignatureVerificationEnabler
{
private static readonly EnvironmentProvider s_environmentProvider = new();

internal static readonly string DotNetNuGetSignatureVerification = "DOTNET_NUGET_SIGNATURE_VERIFICATION";

public static void ConditionallyEnable(ForwardingApp forwardingApp, IEnvironmentProvider? environmentProvider = null)
{
ArgumentNullException.ThrowIfNull(forwardingApp, nameof(forwardingApp));

if (!IsLinux())
{
return;
}

string value = GetSignatureVerificationEnablementValue(environmentProvider);

forwardingApp.WithEnvironmentVariable(DotNetNuGetSignatureVerification, value);
}

public static void ConditionallyEnable(MSBuildForwardingApp forwardingApp, IEnvironmentProvider? environmentProvider = null)
{
ArgumentNullException.ThrowIfNull(forwardingApp, nameof(forwardingApp));

if (!IsLinux())
{
return;
}

string value = GetSignatureVerificationEnablementValue(environmentProvider);

forwardingApp.EnvironmentVariable(DotNetNuGetSignatureVerification, value);
}

private static string GetSignatureVerificationEnablementValue(IEnvironmentProvider? environmentProvider)
{
string? value = (environmentProvider ?? s_environmentProvider).GetEnvironmentVariable(DotNetNuGetSignatureVerification);

return string.Equals(bool.FalseString, value, StringComparison.OrdinalIgnoreCase)
? bool.FalseString : bool.TrueString;
}

private static bool IsLinux()
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
}
}
}
5 changes: 5 additions & 0 deletions src/Cli/dotnet/commands/RestoringCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public RestoringCommand(
Task.Run(() => WorkloadManifestUpdater.BackgroundUpdateAdvertisingManifestsAsync(userProfileDir));
SeparateRestoreCommand = GetSeparateRestoreCommand(msbuildArgs, noRestore, msbuildPath);
AdvertiseWorkloadUpdates = advertiseWorkloadUpdates;

if (!noRestore)
{
NuGetSignatureVerificationEnabler.ConditionallyEnable(this);
}
}

private static IEnumerable<string> GetCommandArguments(
Expand Down
1 change: 1 addition & 0 deletions src/Cli/dotnet/commands/dotnet-restore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class RestoreCommand : MSBuildForwardingApp
public RestoreCommand(IEnumerable<string> msbuildArgs, string msbuildPath = null)
: base(msbuildArgs, msbuildPath)
{
NuGetSignatureVerificationEnabler.ConditionallyEnable(this);
}

public static RestoreCommand FromArgs(string[] args, string msbuildPath = null, bool noLogo = true)
Expand Down
167 changes: 167 additions & 0 deletions src/Tests/dotnet.Tests/NuGetSignatureVerificationEnablerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#nullable enable

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Xunit;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.MSBuild;
using Moq;

namespace Microsoft.DotNet.Tests
{
public class NuGetSignatureVerificationEnablerTests
{
private static readonly string FakeFilePath = Path.Combine(Path.GetTempPath(), "file.fake");

public static IEnumerable<object[]> GetNonFalseValues()
{
yield return new object[] { null! };
yield return new object[] { string.Empty };
yield return new object[] { "0" };
yield return new object[] { "1" };
yield return new object[] { "no" };
yield return new object[] { "yes" };
yield return new object[] { "true" };
yield return new object[] { "TRUE" };
}

public static IEnumerable<object[]> GetFalseValues()
{
yield return new object[] { "false" };
yield return new object[] { "FALSE" };
}

[Fact]
public void GivenANullForwardingAppThrows()
{
ForwardingApp forwardingApp = null!;

ArgumentNullException exception = Assert.Throws<ArgumentNullException>(
() => NuGetSignatureVerificationEnabler.ConditionallyEnable(forwardingApp));

Assert.Equal("forwardingApp", exception.ParamName);
}

[Fact]
public void GivenANullMSBuildForwardingAppThrows()
{
MSBuildForwardingApp forwardingApp = null!;

ArgumentNullException exception = Assert.Throws<ArgumentNullException>(
() => NuGetSignatureVerificationEnabler.ConditionallyEnable(forwardingApp));

Assert.Equal("forwardingApp", exception.ParamName);
}

[LinuxOnlyTheory]
[MemberData(nameof(GetNonFalseValues))]
public void GivenAForwardingAppAndAnEnvironmentVariableValueThatIsNotFalseSetsTrueOnLinux(string? value)
{
Mock<IEnvironmentProvider> environmentProvider = CreateEnvironmentProvider(value);
ForwardingApp forwardingApp = new(FakeFilePath, Array.Empty<string>());

NuGetSignatureVerificationEnabler.ConditionallyEnable(forwardingApp, environmentProvider.Object);

environmentProvider.VerifyAll();

VerifyEnvironmentVariable(forwardingApp.GetProcessStartInfo(), bool.TrueString);
}

[LinuxOnlyTheory]
[MemberData(nameof(GetFalseValues))]
public void GivenAForwardingAppAndAnEnvironmentVariableValueThatIsFalseSetsFalseOnLinux(string value)
{
Mock<IEnvironmentProvider> environmentProvider = CreateEnvironmentProvider(value);
ForwardingApp forwardingApp = new(FakeFilePath, Array.Empty<string>());

NuGetSignatureVerificationEnabler.ConditionallyEnable(forwardingApp, environmentProvider.Object);

environmentProvider.VerifyAll();

VerifyEnvironmentVariable(forwardingApp.GetProcessStartInfo(), bool.FalseString);
}

[LinuxOnlyTheory]
[MemberData(nameof(GetNonFalseValues))]
public void GivenAnMSBuildForwardingAppAndAnEnvironmentVariableValueThatIsNotFalseSetsTrueOnLinux(string? value)
{
Mock<IEnvironmentProvider> environmentProvider = CreateEnvironmentProvider(value);
MSBuildForwardingApp forwardingApp = new(Array.Empty<string>(), FakeFilePath);

NuGetSignatureVerificationEnabler.ConditionallyEnable(forwardingApp, environmentProvider.Object);

environmentProvider.VerifyAll();

VerifyEnvironmentVariable(forwardingApp.GetProcessStartInfo(), bool.TrueString);
}

[LinuxOnlyTheory]
[MemberData(nameof(GetFalseValues))]
public void GivenAnMSBuildForwardingAppAndAnEnvironmentVariableValueThatIsFalseSetsFalseOnLinux(string value)
{
Mock<IEnvironmentProvider> environmentProvider = CreateEnvironmentProvider(value);
MSBuildForwardingApp forwardingApp = new(Array.Empty<string>(), FakeFilePath);

NuGetSignatureVerificationEnabler.ConditionallyEnable(forwardingApp, environmentProvider.Object);

environmentProvider.VerifyAll();

VerifyEnvironmentVariable(forwardingApp.GetProcessStartInfo(), bool.FalseString);
}

[MacOSOnlyFact]
public void GivenAForwardingAppDoesNothingOnMacOs()
{
var environmentProvider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
ForwardingApp forwardingApp = new(FakeFilePath, Array.Empty<string>());

NuGetSignatureVerificationEnabler.ConditionallyEnable(forwardingApp, environmentProvider.Object);

environmentProvider.VerifyAll();

VerifyNoEnvironmentVariable(forwardingApp.GetProcessStartInfo());
}

[MacOSOnlyFact]
public void GivenAnMSBuildForwardingAppDoesNothingOnMacOs()
{
var environmentProvider = new Mock<IEnvironmentProvider>(MockBehavior.Strict);
MSBuildForwardingApp forwardingApp = new(Array.Empty<string>(), FakeFilePath);

NuGetSignatureVerificationEnabler.ConditionallyEnable(forwardingApp, environmentProvider.Object);

environmentProvider.VerifyAll();

VerifyNoEnvironmentVariable(forwardingApp.GetProcessStartInfo());
}

private static Mock<IEnvironmentProvider> CreateEnvironmentProvider(string? value)
{
Mock<IEnvironmentProvider> provider = new(MockBehavior.Strict);

provider
.Setup(p => p.GetEnvironmentVariable(NuGetSignatureVerificationEnabler.DotNetNuGetSignatureVerification))
.Returns(value!);

return provider;
}

private static void VerifyEnvironmentVariable(ProcessStartInfo startInfo, string expectedValue)
{
Assert.True(startInfo.EnvironmentVariables.ContainsKey(NuGetSignatureVerificationEnabler.DotNetNuGetSignatureVerification));
Assert.Equal(expectedValue, startInfo.EnvironmentVariables[NuGetSignatureVerificationEnabler.DotNetNuGetSignatureVerification]);
}

private static void VerifyNoEnvironmentVariable(ProcessStartInfo startInfo)
{
Assert.False(startInfo.EnvironmentVariables.ContainsKey(NuGetSignatureVerificationEnabler.DotNetNuGetSignatureVerification));
}
}
}

0 comments on commit 3eafa1f

Please sign in to comment.