-
Notifications
You must be signed in to change notification settings - Fork 692
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
Add an ENV Variable for Package Signing Verification on .NET 5+ Linux/MAC #3986
Conversation
return false; | ||
// Conditionally enable back package sign verification temporary disabled due to Mozilla drop Symantec as CA on Linux/MAC. | ||
// Please note: Linux/MAC case sensitive for env var. | ||
string signVerifyEnvVariable = Environment.GetEnvironmentVariable("DOTNET_OPT_IN_SECURE_PACKAGE_VERIFICATION"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Common/EnvironmentVariableWrapper.cs so you can add a unit test.
This is an API that's easily unit testable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I already checked that one. I don't see any actual benefit.
Does it prevent from cross test env var
value set contamination? If yes then we're going to have more flaky unit tests?
Specially it's problem if parallel tests are running same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it prevents issues with concurrent unit test as you can provide a mock to each test with values you control. For example:
NuGet.Client/test/NuGet.Core.Tests/NuGet.Configuration.Test/ProxyCacheTests.cs
Lines 96 to 107 in f24bad0
// Arrange | |
var settings = Mock.Of<ISettings>(); | |
var environment = new Mock<IEnvironmentVariableReader>(MockBehavior.Strict); | |
environment.Setup(s => s.GetEnvironmentVariable("http_proxy")).Returns(proxyValue); | |
var proxyCache = new ProxyCache(settings, environment.Object); | |
// Act | |
var proxy = proxyCache.GetUserConfiguredProxy(); | |
// Assert | |
Assert.Null(proxy); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it prevent from cross test env var value set contamination? If yes then we're going to have more flaky unit tests?
Yes, because as @loic-sharma showed, you can mock the specific instance of the wrapper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized problem with this approach. I have to make change to existing public api PackageExtractor.ExtractPackageAsync in order to pass IEnvironmentVariableReader
as parameter.
Is it really good justification to make change to public api, just sake of additional unit testing working?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can add an internal constructor.
We set internalsvisibleto to test projects.
[assembly: InternalsVisibleTo("NuGet.Packaging.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nkolev92
PackageExtractor
is static class, so it doesn't have constructor.
If we introduce new field into static class then it'll cause problem in concurrent unit test context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PackageExtractor is static class, so it doesn't have constructor.
The change is in PackageArchiveReader not PackageExtractor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I create new internal constructors for PackageArchiveReader
and created new tests in most places by creating duplicate of existing test with environmental variable change.
But there're 2 places I can't use it and I don't think it it's really important for now.
- RestoreCommandTests.cs >> RestoreCommand_InvalidSignedPackageAsync_SuccessAsync()
- PackageExtractorTests.cs>>InstallFromSourceAsyncByStream_InvalidSignPackageWithoutUnzipAsync
Please review again.
93f5e0e
to
dfc6c98
Compare
result.Success.Should().BeFalse(); | ||
result.ExitCode.Should().Be(1, because: "error text should be displayed as restore failed"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to https://github.com/NuGet/NuGet.Client/blob/dev/test/TestUtilities/Test.Utility/CommandRunnerResult.cs#L37-L39, my understanding is, the two assertions are the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, technically they're same. I just copied from another already existing integration test.
2nd one actually tells you what went wrong in text.
NuGet.Client/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRestoreTests.cs
Lines 191 to 192 in 7e3b0e4
result.Success.Should().BeFalse(); | |
result.ExitCode.Should().Be(1, because: "error text should be displayed as restore failed"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result.Success.Should().BeFalse(because: "error text should be displayed as restore failed");
The following works too.
Existing code is not always perfect.
@NuGet/nuget-client |
string signVerifyEnvVariable = _environmentVariableReader == null ? | ||
EnvironmentVariableWrapper.Instance.GetEnvironmentVariable(envVarName) : | ||
_environmentVariableReader.GetEnvironmentVariable(envVarName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you made the old constructors call the new constructor, passing in EnvironmentVariableWrapper.Instance
to the new parameter, then _environmentVariableReader
will never be null. (well, assuming you implement null checking and throw ArgumentNullException).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My intention was not to change existing code. Also there're 6 public constructors vs 2 internal constructors for unit tests.
Currently _environmentVariableReader
is used only for prevent from flaky unit tests when many concurrent tests run same time and I'm expecting whole Linux/MAC specific changes are temporary (including this environmental var checking + all new unit tests) will be removed once we get long term solution resistant any 3rd party changes(like Mozilla did), so checking it and throwing ArgumentNullException
is not really important. Since I'm expecting this changes are temporary I prefer to make less changes to existing code, so it would be easy to revert in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can have the public constructors chain into a private constructor.
I couldn't find any blog posts or stack overflow answers suggesting best practices, but a pattern I've often seen, especially in the NuGet repo, is that only a single constructor has a non-empty body and all other constructor overloads chain into this "one true constructor". This makes it easy to validate that all fields/properties that must not be null are checked and assigned a value. Otherwise it's very difficult reading the code to ensure that fields/properties that should be set are correctly assigned a value in all different constructors.
This would have the side-effect of _environmentVariableHelper
never being null, and having slightly less complex code in the methods that use the field.
Currently _environmentVariableReader is used only for prevent from flaky unit tests
If it's "only for tests", then we need to consider whether Arcade need an opt-in mechanism to keep doing signature validation on Linux and Mac, and do API design, including a new public API if necessary, to allow them to keep doing so.
This is the problem with NuGet being both a tool and an API. Given that the .NET platform is built with the arcade SDK, if we disable package signature validation, we might risk breaking every github.com/dotnet/ repo since they're using these APIs (maybe we will "already have" when Arcade upgrades to the version of NuGet that includes the PR we merged a week or two ago).
Maybe we need to talk to DncEng to find out what exactly they're using our package validation APIs for, and test that it will still do what they need with all these changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My intention was not to change existing code. Also there're 6 public constructors vs 2 internal constructors for unit tests.
What's motivation behind not changing existing code?
Is there a risk inherent with that?
Having a single root constructor helps avoid many of the argument checking pitfalls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My intention was not to change existing code. Also there're 6 public constructors vs 2 internal constructors for unit tests.
What's motivation behind not changing existing code?
Is there a risk inherent with that?
Having a single root constructor helps avoid many of the argument checking pitfalls.
Since I'm expecting this changes are temporary I prefer to make less changes to existing code, so it would be easy to revert in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have git for that.
I think we should strive for quality code, regardless whether we think the change is temp or not temp.
There's no public API changes here, so no future risk there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we need to talk to DncEng to find out what exactly they're using our package validation APIs for, and test that it will still do what they need with all these changes.
I don't know if DncEng/Arcade would be only users of this env var
, intent for now only for testing. There're many cases temporary things become permanent thing so I'll address this for better code quality.
97a9715
to
fb609f6
Compare
result.Success.Should().BeFalse(); | ||
result.ExitCode.Should().Be(1, because: "error text should be displayed as restore failed"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result.Success.Should().BeFalse(because: "error text should be displayed as restore failed");
The following works too.
Existing code is not always perfect.
test/NuGet.Core.Tests/NuGet.Packaging.Test/PackageExtractorTests.cs
Outdated
Show resolved
Hide resolved
@JonDouglas @nkolev92 @aortiz-msft @dtivel |
Bug
Fixes: NuGet/Home#10742 https://github.com/NuGet/Client.Engineering/issues/882
Regression? Last working version:
Description
Add an environment variable to opt-in to the package signing verification on .NET 5.
This is to provide an opt-in / troubleshooting experience for Mac/Linux based on the reversion of:
#3979
We intend this ENV for testing and don't guarantee compatibility.
PR Checklist
PR has a meaningful title
PR has a linked issue.
Described changes
Tests
Documentation