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

Make verify command consider default nuget.config file and nuget.config option #4396

Merged
merged 15 commits into from
Jan 26, 2022

Conversation

erdembayar
Copy link
Contributor

@erdembayar erdembayar commented Jan 8, 2022

Bug

Fixes: NuGet/Home#10774
Fixes: NuGet/Home#10011

Regression? Last working version:

Description

Currently both nuget verify and dotnet nuget verify command ignore nuget.config file content. So, this makes trustedSigners section, specially allowUntrustedRoot value ignored for verify command for both default nuget.config and passed nuget.config option. Also dotnet nuget verify doesn't even has pass nuget.config option yet.
This PR add followings:

  • dotnet nuget verify command doesn't ignore nuget.config file, detect broken/missing tags.
  • Takes allowUntrustedRoot value into account for certificates with untrusted root from trustedSigners section in nuget.config file (It's default nuget.config or passed nuget.config option)
  • Add pass nuget.config option for dotnet nuget verify

Follow up issue: https://github.com/NuGet/docs.microsoft.com-nuget/issues/2632

PR Checklist

@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch 4 times, most recently from b73afcc to 4c17651 Compare January 8, 2022 01:23
@erdembayar erdembayar marked this pull request as ready for review January 8, 2022 01:36
@erdembayar erdembayar requested a review from a team as a code owner January 8, 2022 01:36
@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch 3 times, most recently from be350d2 to f555e35 Compare January 10, 2022 18:39
@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch from 6910e0f to 896a0a9 Compare January 10, 2022 20:41
@@ -58,10 +60,16 @@ public async Task<int> ExecuteCommandAsync(VerifyArgs verifyArgs)
_defaultFingerprintAlgorithm)).ToList();

var verifierSettings = SignedPackageVerifierSettings.GetVerifyCommandDefaultPolicy();
var trustedSignersSection = verifyArgs.Settings.GetSection(TrustedSignersSectionName);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not backwards compatible because the obsolete constructor you added will have Settings as null, and therefore this code will throw a NullReferenceException.

Perhaps have a re-read of https://github.com/NuGet/NuGet.Client/blob/dev/docs/nuget-sdk.md#types-of-compatibility as a refresher to different types of compatibility.

src/NuGet.Core/NuGet.Commands/VerifyCommand/VerifyArgs.cs Outdated Show resolved Hide resolved
@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch 2 times, most recently from 0d841d7 to 448dbb3 Compare January 10, 2022 23:34
Copy link
Member

@nkolev92 nkolev92 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's considered default nuget.config here?

wrt current working directory?

Comment on lines 63 to 68
SettingSection trustedSignersSection = verifyArgs.Settings?.GetSection(TrustedSignersSectionName);
List<TrustedSignerItem> trustedSigners = trustedSignersSection?.Items.Select(c => c as TrustedSignerItem).Where(c => c != null).ToList();
IEnumerable<KeyValuePair<string, HashAlgorithmName>> allowUntrustedRootList = trustedSigners?
.SelectMany(c => c.Certificates)
.Where(c => c.AllowUntrustedRoot)
.Select(c => new KeyValuePair<string, HashAlgorithmName>(c.Fingerprint, c.HashAlgorithm));
Copy link
Contributor

@kartheekp-ms kartheekp-ms Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nuget.exe verify command spec specifies that -ConfigFile option from NuGet.Config file is used for only repository signature verification. This makes me think that verify command should not apply all the settings from the NuGet.Config file while verifying package signature.

I posted this comment in another issue but here are the default settings used by Verify command. I would check with @dtivel and @heng-liu just to make sure required options are read from the NuGet.Config file.

Setting Verify Command default setting
AllowUnsigned
AllowIllegal
AllowUntrusted
AllowIgnoreTimestamp
AllowMultipleTimestamps ✔️
AllowNoTimestamp ✔️
AllowUnknownRevocation ✔️
ReportUnknownRevocation ✔️
VerificationTarget Targets unknown, Author and Repository signatures
SignaturePlacement Primary, Counter signatures
RepositoryCountersignatureVerificationBehavior IfExists (Verify a signature if it exists)
RevocationMode Online unless "NUGET_CERT_REVOCATION_MODE" environment variable is set

Copy link
Contributor Author

@erdembayar erdembayar Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, let's wait for Damon to clarify for us. Mean time I looked at dotnet nuget verify spec. It does have --config file section. Should we implement according to it? Somehow it doesn't say anything about repository signature verification.

--configfile The NuGet configuration file. If specified, only the settings from this file will be used. If not specified, the hierarchy of configuration files from the current directory will be used.

Copy link
Contributor Author

@erdembayar erdembayar Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JonDouglas
Hi. I know it happened very long time ago. Could you be able to check if discrepancy between 2 specs? I couldn't see any other spec contributors from spec file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aortiz-msft
Fyi: Should we ignore --configFile option? How about NuGet.config file current working directory?

Copy link
Contributor Author

@erdembayar erdembayar Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kartheekp-ms
Could you able to clarify your comment about #10011? I'm bit confused now. Shouldn't #10011 already be closed or at least P3 priority item based on your above comment since nuget.config file is ignored?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me think that verify command should not apply all the settings from the NuGet.Config file while verifying package signature.

What are the main concerns here? There should be a way to specify what configuration file is applied & if not, defaulting to the user location given it applies to all operations. If in a directory that contains a solution/project, they are overridden by solution level config settings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find anything in nuget verify command spec, but found a wiki (not sure if it's still up to date though): https://github.com/NuGet/Home/wiki/NuGet-Verify-Command#config-file-for-repository-signature-verification, saying that verify command will take the config file option.

Copy link
Contributor

@kartheekp-ms kartheekp-ms Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kartheekp-ms
Could you able to clarify your comment about #10011? I'm bit confused now. Shouldn't #10011 already be closed or at least P3 priority item based on your above comment since nuget.config file is ignored?

My message in that comment is, the behavior of nuget.exe verify and dotnet nuget verify command should match once we add an option to accept config file. The reason is we try to maintain parity between nuget.exe and dotnet commands. I agree with Nikolche's comment (next to mine in the same issue) because the changes would impact both the commands because the code base is shared.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is we try to maintain parity between nuget.exe and dotnet commands.

I see people in the NuGet team keep saying this, but the dotnet cli intentionally distanced itself from the older msbuild.exe, including changing defaults to make more sense given the decade(s) that passed since msbuild/nuget.exe was first created. Some examples are most commands changing default verbosity from normal to minimal, and changing from Microsoft/cmd.exe style /arg arguments to GNU style -a and --arg style. So, by design, the dotnet CLI is not 100% compatible with the older tools. A third example is that msbuild.exe does not do an implicit NuGet restore (the -restore argument), whereas the dotnet CLI does.

I agree with reading nuget.config files, and think it's a bug that nuget.exe verify didn't. But I don't like seeing "make dotnet nuget the same as nuget.exe" used as any justification since I think we can improve customer experience with the newer tool.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kartheekp-ms, the current version of that spec is misleading. NuGet 4.6.0 delivered author signing, while NuGet 4.7.0 delivered repository signing. I think that the engineer who worked on repository signing updated the verify spec, which already supported author signatures, with such single-feature focus that the changes read like the command has a behavioral difference between author- and repository-signed packages. A previous version of the spec doesn't have this problem. Based on this information, I don't see any concern around the verify command applying settings from the NuGet.Config file incorrectly while verifying package signatures. Do you? The verifier settings are not changing with this PR.

@erdembayar, as part of this work can you please update https://github.com/NuGet/Home/wiki/NuGet-Verify-Command/ to not sound like it works only for repository signatures? As an example,
Config file for repository signature verification should be "Config file for signature verification".

Copy link
Member

@nkolev92 nkolev92 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks fine to me.

Sounds like @kartheekp-ms has behavior concern.

I'll be happy to take a look again when that's figured out.

@jeffkl jeffkl added the Merge next release PRs that should not be merged until the dev branch targets the next release label Jan 11, 2022
Comment on lines 63 to 68
SettingSection trustedSignersSection = verifyArgs.Settings?.GetSection(TrustedSignersSectionName);
List<TrustedSignerItem> trustedSigners = trustedSignersSection?.Items.Select(c => c as TrustedSignerItem).Where(c => c != null).ToList();
IEnumerable<KeyValuePair<string, HashAlgorithmName>> allowUntrustedRootList = trustedSigners?
.SelectMany(c => c.Certificates)
.Where(c => c.AllowUntrustedRoot)
.Select(c => new KeyValuePair<string, HashAlgorithmName>(c.Fingerprint, c.HashAlgorithm));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be using TrustedSignersProvider.GetAllowListEntries(...) instead, which will do this for you and get the "owners" that you didn't.

Advice: try to find out how existing code does it before implementing something new.

Comment on lines 63 to 68
SettingSection trustedSignersSection = verifyArgs.Settings?.GetSection(TrustedSignersSectionName);
List<TrustedSignerItem> trustedSigners = trustedSignersSection?.Items.Select(c => c as TrustedSignerItem).Where(c => c != null).ToList();
IEnumerable<KeyValuePair<string, HashAlgorithmName>> allowUntrustedRootList = trustedSigners?
.SelectMany(c => c.Certificates)
.Where(c => c.AllowUntrustedRoot)
.Select(c => new KeyValuePair<string, HashAlgorithmName>(c.Fingerprint, c.HashAlgorithm));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kartheekp-ms, the current version of that spec is misleading. NuGet 4.6.0 delivered author signing, while NuGet 4.7.0 delivered repository signing. I think that the engineer who worked on repository signing updated the verify spec, which already supported author signatures, with such single-feature focus that the changes read like the command has a behavioral difference between author- and repository-signed packages. A previous version of the spec doesn't have this problem. Based on this information, I don't see any concern around the verify command applying settings from the NuGet.Config file incorrectly while verifying package signatures. Do you? The verifier settings are not changing with this PR.

@erdembayar, as part of this work can you please update https://github.com/NuGet/Home/wiki/NuGet-Verify-Command/ to not sound like it works only for repository signatures? As an example,
Config file for repository signature verification should be "Config file for signature verification".

@erdembayar erdembayar marked this pull request as draft January 13, 2022 18:03
@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch 5 times, most recently from 828a3da to 8ec6ad7 Compare January 15, 2022 01:18
@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch 3 times, most recently from dd6b6dd to 4baba17 Compare January 18, 2022 23:36
// trustedSigners section >> allowUntrustedRoot set true are considered here.
verificationProviders.Add(new SignatureTrustAndValidityVerificationProvider(trustedSignerAllowUntrustedRootList));

// List of values passed through --certificate-fingerprint option are considered here.
Copy link
Contributor Author

@erdembayar erdembayar Jan 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For verify action repository signing allow list not considered.

if (repositorySignatureInfo != null && repositorySignatureInfo.RepositoryCertificateInfos != null)
{
var repositoryAllowList = RepositorySignatureInfoUtility.GetRepositoryAllowList(repositorySignatureInfo.RepositoryCertificateInfos);
verificationProviders.Add(new AllowListVerificationProvider(
repositoryAllowList,
repositorySignatureInfo.AllRepositorySigned,
Strings.Error_NoRepoAllowList,
Strings.Error_NoMatchingRepositoryCertificate));
}

@erdembayar
Copy link
Contributor Author

I see a lot of tests that are ran on the dotnet nuget verify, nuget verify are duplicated.

Can we test the core functionality with the VerifyCommandRunner rather than tests that kick off new processes? And only then add some E2E tests.

I really appreciate how thorough your tests are, but we also need to make sure we're not repeatedly testing the same.

We expect dotnet nuget verify and nuget verify work exactly same, that is why I tried make tests look same for them. But they have different command syntax so testing them separately worth, besides I don't see VerifyCommandRunner specific tests, not sure time costs of setting up new one.

@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch from 4baba17 to 1ebb2df Compare January 19, 2022 00:21
@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch from 1ebb2df to 328ef60 Compare January 19, 2022 02:03
@@ -50,6 +50,9 @@ public async Task<int> ExecuteCommandAsync(VerifyArgs verifyArgs)
return packages;
});

var clientPolicyContext = ClientPolicyContext.GetClientPolicy(verifyArgs.Settings, verifyArgs.Logger);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use explicit type instead of var.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, addressed.

Comment on lines 65 to 67
// nuget.config >> trustedSigners section read
IReadOnlyList<TrustedSignerAllowListEntry> trustedSignerAllowList = TrustedSignersProvider.GetAllowListEntries(verifyArgs.Settings, verifyArgs.Logger);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, I recommended using TrustedSignersProvider.GetAllowListEntries(...) directly (instead of ClientPolicyContext.GetClientPolicy(...)). My reasoning was that ClientPolicyContext.GetClientPolicy(...) already does 2 things:

  1. Determine the signature validation mode.
  2. Fetch the allow list using TrustedSignersProvider.GetAllowListEntries(...).

...and that you only need # 2. However, with your latest changes, we're now setting signature validation mode --- which is fine, provided that we have good test coverage.

Make sure you delete this statement and replace trustedSignerAllowList with clientPolicyContext.AllowList.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

Comment on lines 75 to 79
new AllowListVerificationProvider(
trustedSignerAllowList,
requireNonEmptyAllowList: clientPolicyContext.Policy == SignatureValidationMode.Require,
emptyListErrorMessage: Strings.Error_NoClientAllowList,
noMatchErrorMessage: Strings.Error_NoMatchingClientCertificate));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 level of indentation is 4 spaces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed

List<string> configPaths = settings.GetConfigFilePaths().ToList();
Assert.True(configPaths.Count > 1);
// Assert user default nuget.config is loaded
Assert.True(configPaths.Contains(baseNugetConfigPath));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put that comment in the code.

@@ -191,5 +192,20 @@ public void AddNetStandardFeeds()

Save();
}

// Simply add any text as section into nuget.config file, adding large child node into nuget.config via api is tedious.
public static void AddSectionIntoNuGetConfig(string path, string content, string parentNode)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a utility method to simplify/deduplicate code is great. Trying to create a general-purpose utility method with only 1 scenario in use is risky.

Should this method guard against adding a section when a like-named section already exists?

Copy link
Contributor Author

@erdembayar erdembayar Jan 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your feedback.
I added guard preventing this scenario, please check now. But I didn't add unit test for asserting it, since we don't have specific file for adding test for test utility itself. Alternatively, I could add one DotnetVerifyTests.cs, but not sure if it's correct thing to do.

@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch from 5a7ee62 to fad5bb9 Compare January 19, 2022 21:32
@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch from 37a01aa to f5f0313 Compare January 19, 2022 21:51
@erdembayar
Copy link
Contributor Author

I see a lot of tests that are ran on the dotnet nuget verify, nuget verify are duplicated.
Can we test the core functionality with the VerifyCommandRunner rather than tests that kick off new processes? And only then add some E2E tests.
I really appreciate how thorough your tests are, but we also need to make sure we're not repeatedly testing the same.

We expect dotnet nuget verify and nuget verify work exactly same, that is why I tried make tests look same for them. But they have different command syntax so testing them separately worth, besides I don't see VerifyCommandRunner specific tests, not sure time costs of setting up new one.

@nkolev92 @dtivel
I created follow up issue https://github.com/NuGet/Client.Engineering/issues/1389 for creating unified testing for both nuget verify and dotnet nuget verify, after that we can cleanup duplicate tests.

@nkolev92
Copy link
Member

Sure, but that makes this PR harder to review as we're dealing double the code.

Comment on lines 6 to 9
NuGet.Commands.PackCollectorLogger.PackCollectorLogger(NuGet.Common.ILogger innerLogger, NuGet.ProjectModel.WarningProperties warningProperties, NuGet.Commands.PackCommand.PackageSpecificWarningProperties packageSpecificWarningProperties) -> void
NuGet.Commands.PackCommand.PackageSpecificWarningProperties
NuGet.Commands.PackCommand.PackageSpecificWarningProperties.PackageSpecificWarningProperties() -> void
static NuGet.Commands.PackCommand.PackageSpecificWarningProperties.CreatePackageSpecificWarningProperties(System.Collections.Generic.IDictionary<string, System.Collections.Generic.HashSet<(NuGet.Common.NuGetLogCode, NuGet.Frameworks.NuGetFramework)>> noWarnProperties) -> NuGet.Commands.PackCommand.PackageSpecificWarningProperties
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public API changes in your latest commit look accidental. Please confirm.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is ok, it happened because I resolved merge conflicts by merging to dev branch instead of rebasing the dev branch (with rebase if file has conflict on early commit, then it is very time consuming to resolve in all subsequent commits same), final compare you wouldn't see it.

Comment on lines +241 to +243
[InlineData("true", true)]
[InlineData("true", false)]
[InlineData("false", true)]
[InlineData("false", false)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this implementation https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Boolean.cs#L82, I think we can simply call allowUntrustedRoot.ToString() wherever we need "true/false" as strings without allocation additional strings.

@erdembayar erdembayar force-pushed the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch from 978f91a to c26dade Compare January 22, 2022 02:08
@erdembayar erdembayar merged commit 0e5d9bd into dev Jan 26, 2022
@erdembayar erdembayar deleted the dev-eryondon-10774-DotnetVerifyNotUsingNugetConfig branch January 26, 2022 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Merge next release PRs that should not be merged until the dev branch targets the next release
Projects
None yet
8 participants