Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signing: more SDK initialization and enable opt-in on Linux #4720

Merged
merged 1 commit into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using NuGet.Credentials;
using NuGet.LibraryModel;
using NuGet.Packaging;
using NuGet.Packaging.Signing;
using NuGet.Versioning;

namespace Microsoft.Build.NuGetSdkResolver
Expand Down Expand Up @@ -146,6 +147,10 @@ public static SdkResult GetSdkResult(SdkReference sdk, object nuGetVersion, SdkR
{
DefaultCredentialServiceUtility.SetupDefaultCredentialService(logger, nonInteractive: !context.Interactive);

#if !NETFRAMEWORK
X509TrustStore.InitializeForDotNetSdk(logger);
#endif

// Asynchronously run the restore without a commit which find the package on configured feeds, download, and unzip it without generating any other files
// This must be run in its own task because legacy project system evaluates projects on the UI thread which can cause RunWithoutCommit() to deadlock
// https://developercommunity.visualstudio.com/content/problem/311379/solution-load-never-completes-when-project-contain.html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using Microsoft.Extensions.CommandLineUtils;
using NuGet.Common;
using NuGet.Packaging.Signing;

namespace NuGet.CommandLine.XPlat
{
Expand Down Expand Up @@ -101,6 +102,9 @@ public static void Register(CommandLineApplication app, Func<ILogger> getLogger,
PackageId = id.Values[0]
};
var msBuild = new MSBuildAPIUtility(logger);

X509TrustStore.InitializeForDotNetSdk(logger);

var addPackageRefCommandRunner = getCommandRunner();
return addPackageRefCommandRunner.ExecuteCommand(packageRefArgs, msBuild);
});
Expand Down
2 changes: 1 addition & 1 deletion src/NuGet.Core/NuGet.Packaging/PackageArchiveReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ public override bool CanVerifySignedPackages(SignedPackageVerifierSettings verif
// Please note: Linux/MAC case sensitive for env var name.
string signVerifyEnvVariable = _environmentVariableReader.GetEnvironmentVariable("DOTNET_NUGET_SIGNATURE_VERIFICATION");

bool canVerify = RuntimeEnvironmentHelper.IsLinux;
bool canVerify = false;
dtivel marked this conversation as resolved.
Show resolved Hide resolved

if (!string.IsNullOrEmpty(signVerifyEnvVariable))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ internal static IX509ChainFactory CreateX509ChainFactoryForDotNetSdk(ILogger log
if (SystemCertificateBundleX509ChainFactory.TryCreate(
out SystemCertificateBundleX509ChainFactory systemBundleFactory))
{
logger.LogVerbose(
logger.LogInformation(
heng-liu marked this conversation as resolved.
Show resolved Hide resolved
string.Format(
CultureInfo.CurrentCulture,
Strings.ChainBuilding_UsingSystemCertificateBundle,
Expand All @@ -84,7 +84,7 @@ internal static IX509ChainFactory CreateX509ChainFactoryForDotNetSdk(ILogger log
out FallbackCertificateBundleX509ChainFactory fallbackBundleFactory,
fallbackCertificateBundleFile?.FullName))
{
logger.LogVerbose(
logger.LogInformation(
string.Format(
CultureInfo.CurrentCulture,
Strings.ChainBuilding_UsingFallbackCertificateBundle,
Expand All @@ -93,7 +93,7 @@ internal static IX509ChainFactory CreateX509ChainFactoryForDotNetSdk(ILogger log
return fallbackBundleFactory;
}

logger.LogVerbose(Strings.ChainBuilding_UsingNoCertificateBundle);
logger.LogInformation(Strings.ChainBuilding_UsingNoCertificateBundle);

return new NoCertificateBundleX509ChainFactory();
}
Expand All @@ -104,7 +104,7 @@ internal static IX509ChainFactory CreateX509ChainFactoryForDotNetSdk(ILogger log
out FallbackCertificateBundleX509ChainFactory fallbackBundleFactory,
fallbackCertificateBundleFile?.FullName))
{
logger.LogVerbose(
logger.LogInformation(
string.Format(
CultureInfo.CurrentCulture,
Strings.ChainBuilding_UsingFallbackCertificateBundle,
Expand All @@ -113,7 +113,7 @@ internal static IX509ChainFactory CreateX509ChainFactoryForDotNetSdk(ILogger log
return fallbackBundleFactory;
}

logger.LogVerbose(Strings.ChainBuilding_UsingNoCertificateBundle);
logger.LogInformation(Strings.ChainBuilding_UsingNoCertificateBundle);

return new NoCertificateBundleX509ChainFactory();
}
Expand All @@ -125,7 +125,7 @@ internal static IX509ChainFactory CreateX509ChainFactoryForDotNetSdk(ILogger log
// Non-private for testing purposes only
internal static IX509ChainFactory CreateX509ChainFactory(ILogger logger)
{
logger.LogVerbose(Strings.ChainBuilding_UsingDefaultTrustStore);
logger.LogInformation(Strings.ChainBuilding_UsingDefaultTrustStore);

return new DotNetDefaultTrustStoreX509ChainFactory();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using NuGet.Common;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.ProjectModel;
Expand Down Expand Up @@ -613,5 +614,75 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async(
Assert.Contains($"Installed {packageX} {version} from {packageSource2}", result.AllOutput);
Assert.Contains($"NU1100: Unable to resolve '{packageZ} (>= {version})' for 'net5.0'", result.AllOutput);
}

[Fact]
public void AddPkg_WhenSourceIsSignedPackageWithExpiredCertificatesAndWithTimestamps_Success()
{
using (SimpleTestPathContext pathContext = new())
{
var projectName = "project";
var targetFrameworks = "net6.0";
SimpleTestProjectContext projectA = XPlatTestUtils.CreateProject(projectName, pathContext, targetFrameworks);

// This package is important because:
// * it has no package dependencies and thus simplifies the test scenario
// * it is a signed package and thus verifies signed package verification, if enabled
// * the author- and repository-signing certificates have expired
// * the author and repository timestamps may be untrusted on Linux/macOS if a valid certificate bundle isn't found
PackageIdentity package = new("NuGet.Versioning", new NuGetVersion("5.0.0"));
DirectoryInfo packageSourceDirectory = new(Path.Combine(pathContext.WorkingDirectory, "PackageSource"));
var packageFileName = $"{package.Id.ToLowerInvariant()}.{package.Version}.nupkg";

CopyResourceToDirectory(packageFileName, packageSourceDirectory);

string projectDirectory = Path.Combine(pathContext.SolutionRoot, projectName);
string projectFilePath = Path.Combine(projectDirectory, $"{projectName}.csproj");

CommandRunnerResult result = _fixture.RunDotnet(
projectDirectory,
$"add {projectFilePath} package {package.Id} -s {packageSourceDirectory.FullName} -v {package.Version}",
ignoreExitCode: true);

result.Success.Should().BeTrue(because: result.AllOutput);

if (RuntimeEnvironmentHelper.IsWindows)
{
result.AllOutput.Should()
.Contain(
Strings.ChainBuilding_UsingDefaultTrustStore,
because: result.AllOutput);
}
else
{
result.AllOutput.Should()
.ContainAny(
new string[] {
"X.509 certificate chain validation will use the fallback certificate bundle at ",
"X.509 certificate chain validation will use the system certificate bundle at "
},
because: result.AllOutput);
}

LockFileTarget ridlessTarget = projectA.AssetsFile.Targets
.Where(e => string.IsNullOrEmpty(e.RuntimeIdentifier))
.Single();

ridlessTarget.Libraries.Should().Contain(e => e.Type == "package" && e.Name == package.Id);
ridlessTarget.Libraries.Should().Contain(e => e.Version.Equals(package.Version));
}
}

private void CopyResourceToDirectory(string resourceName, DirectoryInfo directory)
{
string fullResourceName = $"Dotnet.Integration.Test.compiler.resources.{resourceName}";
string destinationFilePath = Path.Combine(directory.FullName, resourceName);

directory.Create();

using (Stream stream = GetType().Assembly.GetManifestResourceStream(fullResourceName))
{
stream.CopyToFile(destinationFilePath);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public X509TrustStoreTests(MsbuildIntegrationTestFixture msbuildFixture, ITestOu
FallbackCertificateBundleX509ChainFactory.SubdirectoryName,
FallbackCertificateBundleX509ChainFactory.FileName));

_logger.LogInformation($"Expected fallback certificate bundle file path: {_fallbackCertificateBundle.FullName}");
_logger.LogInformation($"Fallback certificate bundle file exists: {_fallbackCertificateBundle.Exists}");
_logger.LogVerbose($"Expected fallback certificate bundle file path: {_fallbackCertificateBundle.FullName}");
_logger.LogVerbose($"Fallback certificate bundle file exists: {_fallbackCertificateBundle.Exists}");
}

[PlatformFact(Platform.Windows)]
Expand All @@ -44,8 +44,8 @@ public void CreateX509ChainFactoryForDotNetSdk_OnWindowsAlways_ReturnsInstance()

// 1 message from the API under test and 2 messages from this test class's constructor
Assert.Equal(3, _logger.Messages.Count);
Assert.Equal(1, _logger.VerboseMessages.Count);
Assert.True(_logger.VerboseMessages.TryDequeue(out string actualMessage));
Assert.Equal(1, _logger.InformationMessages.Count);
Assert.True(_logger.InformationMessages.TryPeek(out string actualMessage));
Assert.Equal(Strings.ChainBuilding_UsingDefaultTrustStore, actualMessage);
}

Expand Down Expand Up @@ -75,8 +75,8 @@ public void CreateX509ChainFactoryForDotNetSdk_OnLinuxAlways_ReturnsInstance()

// 1 message from the API under test and 2 messages from this test class's constructor
Assert.Equal(3, _logger.Messages.Count);
Assert.Equal(1, _logger.VerboseMessages.Count);
Assert.True(_logger.VerboseMessages.TryDequeue(out string actualMessage));
Assert.Equal(1, _logger.InformationMessages.Count);
Assert.True(_logger.InformationMessages.TryPeek(out string actualMessage));

string expectedMessage;

Expand Down Expand Up @@ -114,8 +114,8 @@ public void CreateX509ChainFactoryForDotNetSdk_OnMacOsAlways_ReturnsInstance()

// 1 message from the API under test and 2 messages from this test class's constructor
Assert.Equal(3, _logger.Messages.Count);
Assert.Equal(1, _logger.VerboseMessages.Count);
Assert.True(_logger.VerboseMessages.TryDequeue(out string actualMessage));
Assert.Equal(1, _logger.InformationMessages.Count);
Assert.True(_logger.InformationMessages.TryPeek(out string actualMessage));
Assert.Equal(expectedMessage, actualMessage);
}

Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ public void CreateX509ChainFactory_Always_ReturnsInstance()

Assert.IsType<DotNetDefaultTrustStoreX509ChainFactory>(factory);
Assert.Equal(1, _logger.Messages.Count);
Assert.Equal(1, _logger.VerboseMessages.Count);
Assert.True(_logger.VerboseMessages.TryDequeue(out string actualMessage));
Assert.Equal(1, _logger.InformationMessages.Count);
Assert.True(_logger.InformationMessages.TryDequeue(out string actualMessage));
Assert.Equal(Strings.ChainBuilding_UsingDefaultTrustStore, actualMessage);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.IO;
using FluentAssertions;
using Microsoft.Build.Framework;
using Moq;
using NuGet.Packaging;
using NuGet.Test.Utility;
using NuGet.Versioning;
Expand Down Expand Up @@ -77,6 +76,29 @@ public void Resolve_WhenPackageExists_ReturnsSucceededSdkResult()
result.Version.Should().Be(sdkReference.Version);
result.Errors.Should().BeEmpty();
result.Warnings.Should().BeEmpty();

bool wasMessageFound = false;

foreach ((string Message, MessageImportance _) in sdkResolverContext.MockSdkLogger.LoggedMessages)
{
// On Linux and macOS the message will be:
//
// X.509 certificate chain validation will not have any trusted roots.
// Chain building will fail with an untrusted status.
//
// This is because this test is not a .NET SDK test but a unit test.
if (Message.Contains("X.509 certificate chain validation will"))
{
wasMessageFound = true;
break;
}
}

#if NETFRAMEWORK
wasMessageFound.Should().BeFalse();
#else
wasMessageFound.Should().BeTrue();
#endif
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1976,23 +1976,19 @@ private static byte[] CreateNonEmptyZipArchive()
}
#endif

[PlatformFact(Platform.Windows, Platform.Linux)]
public void CanVerifySignedPackages_OnWindowsAndLinux_ReturnsValueBasedOnOperatingSystemAndFramework()
[Fact]
public void CanVerifySignedPackages_Always_ReturnsValueBasedOnOperatingSystemAndFramework()
{
// Arrange
using (var test = TestPackagesCore.GetPackageContentReaderTestPackage())
using (var packageArchiveReader = new PackageArchiveReader(test))
{
bool expectedResult = CanVerifySignedPackages();

// Act
var result = packageArchiveReader.CanVerifySignedPackages(null);
// Assert
#if IS_SIGNING_SUPPORTED
// Verify package signature when signing is supported
Assert.True(result);
#else
// Cannot verify package signature when signing is not supported
Assert.False(result);
#endif
bool actualResult = packageArchiveReader.CanVerifySignedPackages(null);

Assert.Equal(expectedResult, actualResult);
}
}

Expand All @@ -2011,23 +2007,6 @@ public void CanVerifySignedPackages_OnMacOs_ReturnsValueBasedOnOperatingSystemAn
}
}

[Fact]
public void CanVerifySignedPackages_ReturnsValueBasedOnOperatingSystemAndFramework_Fails()
{
// Arrange
using (var test = TestPackagesCore.GetPackageContentReaderTestPackage())
using (var packageArchiveReader = new PackageArchiveReader(test))
{
bool expectedResult = CanVerifySignedPackages();

// Act
bool actualResult = packageArchiveReader.CanVerifySignedPackages(null);

// Assert
Assert.Equal(expectedResult, actualResult);
}
}

[Theory]
[InlineData("TRUE")]
[InlineData("True")]
Expand Down Expand Up @@ -2123,7 +2102,7 @@ public void CanVerifySignedPackages_ReturnsValueBasedOnOperatingSystemAndFramewo

private static bool CanVerifySignedPackages()
{
return (RuntimeEnvironmentHelper.IsWindows || RuntimeEnvironmentHelper.IsLinux) &&
return RuntimeEnvironmentHelper.IsWindows &&
#if IS_SIGNING_SUPPORTED
true;
#else
Expand Down