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

Adding support for NuGet.exe sign command with fake signing #1755

Merged
merged 15 commits into from
Oct 20, 2017

Conversation

mishra14
Copy link
Contributor

Bug

Link: Contributes to NuGet/Home#5904
Regression: No

Fix

Details: Adds a top level command to NuGet.exe to allow authors to sign packages with fake signing. The behavior is documented in the spec.

Testing/Validation

Tests Added: Yes
Validation done: Manually tested NuGet.exe

public ISignCommandRunner SignCommandRunner { get; set; }

// List of possible values - https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/security/cryptography/HashAlgorithmName.cs
private static string[] _acceptedHashAlgorithms = { "SHA256", "SHA384", "SHA512" };
Copy link
Member

Choose a reason for hiding this comment

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

note: this will probably need to come from the helper that verifies this later based on the package signing version, that isn't in yet though

{
throw new ArgumentException(NuGetCommand.SignCommandNoCertificateException);
}
else if (!string.IsNullOrEmpty(CertificatePath) &&
Copy link
Member

Choose a reason for hiding this comment

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

I think this would more clear if we had some static helper methods for these checks.

  • Verify required option exists
  • Verify no invalid option combinations
  • Enum parsing + verify valid result
  • With a way to throw the right message for all the above

Example: https://github.com/emgarten/NuGet.CatalogReader/blob/bcda3b86fa55a914aa0b7d3af7a3c366c0aee96e/src/NuGetMirror/Common/CmdUtils.cs#L17-L70

Copy link
Member

Choose a reason for hiding this comment

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

Using an else if for all of these also implies that these things are related, but they aren't. It would be the same logic if they were all separate ifs.

{
}

public ISignCommandRunner SignCommandRunner { get; set; }
Copy link
Member

Choose a reason for hiding this comment

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

It seems like it would be easier if we had a static helper that created SignArgs and passed it back instead of needing to catch the SignArgs result by passing a mock SignCommandRunner in to get that result.

<EmbeddedResource Update="NuGetResources.resx">
<!-- Strings are shared by other projects, use public strings. -->
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>NuGetResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Update="NuGetCommand.Designer.cs">
Copy link
Member

Choose a reason for hiding this comment

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

nit: why did this move?

<value>Signs a NuGet package.</value>
</data>
<data name="SignCommandTimestampHashAlgorithmDescription" xml:space="preserve">
<value>Hash algorithm to be used by the RFC 3161 time stamp server. Defaults to SHA256.</value>
Copy link
Member

Choose a reason for hiding this comment

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

Should time stamp be timestamp here?

@@ -33,6 +33,9 @@
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Security">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Security.dll</HintPath>
Copy link
Member

Choose a reason for hiding this comment

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

Remove hint path

/// </summary>
internal class CertificateSourceOptions
{

Copy link
Member

Choose a reason for hiding this comment

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

nit: remove extra space

{
var package = new SignedPackageArchive(zip);
var signer = new Signer(package);
signer.SignAsync(request, logger, CancellationToken.None).Wait();
Copy link
Member

Choose a reason for hiding this comment

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

🥇

@@ -221,7 +221,7 @@ private static string GetSourceDisplayName(string source)
return "'" + source + "'";
}

private static IEnumerable<string> GetPackagesToPush(string packagePath)
public static IEnumerable<string> ResolvePackageFromPath(string packagePath)
Copy link
Member

Choose a reason for hiding this comment

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

Move these wildcard methods to a new static helper class. The Sign command shouldn't have a dependency on Push.

LocalFolderUtility might be a good place to add them.


// Act & Assert
var ex = Assert.Throws<ArgumentException>(() => signCommand.Execute());
Assert.Equal("No package was provided. For a list of accepted ways to provide a package, please visit http://docs.nuget.org/docs/reference/command-line-reference", ex.Message);
Copy link
Member

Choose a reason for hiding this comment

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

any reason this doesn't use the const above?

Copy link
Member

@emgarten emgarten left a comment

Choose a reason for hiding this comment

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

Address minor feedback then :shipit:

};
}

private void ValidateAndParseHashAlgorithm(string value, string name, out HashAlgorithmName hashAlgorithm)
Copy link
Member

Choose a reason for hiding this comment

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

these methods can just return the value, it doesn't look like the out is needed since there is only 1 return

@@ -949,6 +949,47 @@ public static IEnumerable<LocalPackageInfo> GetPackagesV3(string root, string id
yield break;
}


public static IEnumerable<string> ResolvePackageFromPath(string packagePath)
Copy link
Member

Choose a reason for hiding this comment

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

Add a /// summary here to make it clear to anyone else using this that the method is applying wild cards

@mishra14 mishra14 merged commit fed8530 into dev-feature-signing Oct 20, 2017
@mishra14 mishra14 deleted the dev-anmishr-signcommand-minimal branch October 20, 2017 00:03
}
}

private void ValidateCertificateInputs()
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it an error to specify one but not both of CryptographicServiceProvider and KeyContainer?

@@ -5345,4 +5345,92 @@ nuget locals global-packages -list</value>
<data name="InstallCommandFrameworkDescription" xml:space="preserve">
<value>Target framework used for selecting dependencies. Defaults to 'Any' if not specified.</value>
</data>
<data name="SignCommandCertificatePasswordDescription" xml:space="preserve">
<value>Password for the certificate, if needed.
This option can be used to specify the password for the certificate. If no password is provided, the user may be prompted for a password at run time, unless the -NonInteractive option is passed.</value>
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the extra space between NonInteractive and option.

@@ -5345,4 +5345,92 @@ nuget locals global-packages -list</value>
<data name="InstallCommandFrameworkDescription" xml:space="preserve">
<value>Target framework used for selecting dependencies. Defaults to 'Any' if not specified.</value>
</data>
<data name="SignCommandCertificatePasswordDescription" xml:space="preserve">
<value>Password for the certificate, if needed.
This option can be used to specify the password for the certificate. If no password is provided, the user may be prompted for a password at run time, unless the -NonInteractive option is passed.</value>
Copy link
Contributor

Choose a reason for hiding this comment

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

Nowhere else do we say "the user". The user is reading this message. Just say, "If no password is provided, the command will prompt for a password at runtime, unless the -NonInteractive option is passed."

The certificate store can be specified by -CertificateStoreName and -CertificateStoreLocation options.</value>
</data>
<data name="SignCommandCertificateSubjectNameDescription" xml:space="preserve">
<value>Subject name of the certificate used to search a local certificate store for the certificate.
Copy link
Contributor

Choose a reason for hiding this comment

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

Some of these option descriptions are quite long. Does this print nicely?

<value>Signs a NuGet package with the specified certificate.</value>
</data>
<data name="SignCommandExamples" xml:space="preserve">
<value>nuget sign MyPackage.nupkg -CertificatePath /path/to/certificate
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like all our other examples use Windows-style paths (\ not /).

{
// Arrange
var packagePath = @"foo\bar.nupkg";
var timestamper = "http://foo.bar";
Copy link
Contributor

Choose a reason for hiding this comment

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

https://unit.test. Applies throughout this file.

{
Console = mockConsole.Object,
Timestamper = timestamper,
CertificatePath =certificatePath,
Copy link
Contributor

Choose a reason for hiding this comment

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

While you're in this document, format it.

[InlineData("Root")]
[InlineData("TrustedPeople")]
[InlineData("TrustedPublisher")]
[InlineData("AddreSSBook")]
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you need to test the case-sensitivity of every StoreName member. Having one case-insensitivity test is completely sufficient.

[InlineData("CurrentUser")]
[InlineData("LocalMachine")]
[InlineData("currentuser")]
[InlineData("localmaChiNe")]
Copy link
Contributor

Choose a reason for hiding this comment

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

One case-insensitive test is sufficient.

// Act & Assert
var signArgs = signCommand.GetSignArgs();

Assert.True(
Copy link
Contributor

Choose a reason for hiding this comment

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

In this test and subsequent tests, would it be better to have multiple assertions? If this assertion fails, you don't know the reason without debugging. Bad!

@mishra14
Copy link
Contributor Author

@dtivel Thanks for the feedback, I will fix these in a separate PR.

@mishra14 mishra14 restored the dev-anmishr-signcommand-minimal branch October 21, 2017 01:30
mishra14 pushed a commit that referenced this pull request Oct 23, 2017
## Bug
Fixes: NA
Regression: No

## Fix
Details: This PR addresses the some PR feedback that came in after merging #1755 along with some additional cleanup.

## Testing/Validation
Tests Added: Yes
Validation done:  manual validation + tests
@mishra14 mishra14 deleted the dev-anmishr-signcommand-minimal branch November 2, 2017 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants