Skip to content

Commit a042aee

Browse files
JL03-YuemarcpopMSFT
authored andcommitted
add signature VerifySigning for dotnet tools (dotnet#39268)
Fixes dotnet#37469 Add signature verifications for NuGet Tools
1 parent 6e87400 commit a042aee

37 files changed

+413
-186
lines changed

src/Cli/dotnet/CommandFactory/CommandFactoryUsingResolver.cs

+12-6
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ public static Command CreateDotNet(
1515
string commandName,
1616
IEnumerable<string> args,
1717
NuGetFramework framework = null,
18-
string configuration = Constants.DefaultConfiguration)
18+
string configuration = Constants.DefaultConfiguration,
19+
string currentWorkingDirectory = null)
1920
{
2021
return Create("dotnet",
2122
new[] { commandName }.Concat(args),
2223
framework,
23-
configuration: configuration);
24+
configuration: configuration,
25+
currentWorkingDirectory);
2426
}
2527

2628
/// <summary>
@@ -35,7 +37,8 @@ public static Command Create(
3537
NuGetFramework framework = null,
3638
string configuration = Constants.DefaultConfiguration,
3739
string outputPath = null,
38-
string applicationName = null)
40+
string applicationName = null,
41+
string currentWorkingDirectory = null)
3942
{
4043
return Create(
4144
new DefaultCommandResolverPolicy(),
@@ -44,7 +47,8 @@ public static Command Create(
4447
framework,
4548
configuration,
4649
outputPath,
47-
applicationName);
50+
applicationName,
51+
currentWorkingDirectory);
4852
}
4953

5054
public static Command Create(
@@ -54,7 +58,8 @@ public static Command Create(
5458
NuGetFramework framework = null,
5559
string configuration = Constants.DefaultConfiguration,
5660
string outputPath = null,
57-
string applicationName = null)
61+
string applicationName = null,
62+
string currentWorkingDirectory = null)
5863
{
5964
var commandSpec = CommandResolver.TryResolveCommandSpec(
6065
commandResolverPolicy,
@@ -63,7 +68,8 @@ public static Command Create(
6368
framework,
6469
configuration: configuration,
6570
outputPath: outputPath,
66-
applicationName: applicationName);
71+
applicationName: applicationName,
72+
currentWorkingDirectory: currentWorkingDirectory);
6773

6874
if (commandSpec == null)
6975
{

src/Cli/dotnet/CommandFactory/CommandResolution/DefaultCommandResolverPolicy.cs

+8-6
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ namespace Microsoft.DotNet.CommandFactory
77
{
88
public class DefaultCommandResolverPolicy : ICommandResolverPolicy
99
{
10-
public CompositeCommandResolver CreateCommandResolver()
10+
public CompositeCommandResolver CreateCommandResolver(string currentWorkingDirectory = null)
1111
{
12-
return Create();
12+
return Create(currentWorkingDirectory);
1313
}
1414

15-
public static CompositeCommandResolver Create()
15+
public static CompositeCommandResolver Create(string currentWorkingDirectory = null)
1616
{
1717
var environment = new EnvironmentProvider();
1818
var packagedCommandSpecFactory = new PackagedCommandSpecFactoryWithCliRuntime();
@@ -32,20 +32,22 @@ public static CompositeCommandResolver Create()
3232
environment,
3333
packagedCommandSpecFactory,
3434
platformCommandSpecFactory,
35-
publishedPathCommandSpecFactory);
35+
publishedPathCommandSpecFactory,
36+
currentWorkingDirectory);
3637
}
3738

3839
public static CompositeCommandResolver CreateDefaultCommandResolver(
3940
IEnvironmentProvider environment,
4041
IPackagedCommandSpecFactory packagedCommandSpecFactory,
4142
IPlatformCommandSpecFactory platformCommandSpecFactory,
42-
IPublishedPathCommandSpecFactory publishedPathCommandSpecFactory)
43+
IPublishedPathCommandSpecFactory publishedPathCommandSpecFactory,
44+
string currentWorkingDirectory = null)
4345
{
4446
var compositeCommandResolver = new CompositeCommandResolver();
4547

4648
compositeCommandResolver.AddCommandResolver(new MuxerCommandResolver());
4749
compositeCommandResolver.AddCommandResolver(new DotnetToolsCommandResolver());
48-
compositeCommandResolver.AddCommandResolver(new LocalToolsCommandResolver());
50+
compositeCommandResolver.AddCommandResolver(new LocalToolsCommandResolver(currentWorkingDirectory: currentWorkingDirectory));
4951
compositeCommandResolver.AddCommandResolver(new RootedCommandResolver());
5052
compositeCommandResolver.AddCommandResolver(
5153
new ProjectToolsCommandResolver(packagedCommandSpecFactory, environment));

src/Cli/dotnet/CommandFactory/CommandResolution/ICommandResolverPolicy.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ namespace Microsoft.DotNet.CommandFactory
55
{
66
public interface ICommandResolverPolicy
77
{
8-
CompositeCommandResolver CreateCommandResolver();
8+
CompositeCommandResolver CreateCommandResolver(string currentWorkingDirectory = null);
99
}
1010
}

src/Cli/dotnet/CommandFactory/CommandResolution/LocalToolsCommandResolver.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ internal class LocalToolsCommandResolver : ICommandResolver
2121
public LocalToolsCommandResolver(
2222
ToolManifestFinder toolManifest = null,
2323
ILocalToolsResolverCache localToolsResolverCache = null,
24-
IFileSystem fileSystem = null)
24+
IFileSystem fileSystem = null,
25+
string currentWorkingDirectory = null)
2526
{
26-
_toolManifest = toolManifest ?? new ToolManifestFinder(new DirectoryPath(Directory.GetCurrentDirectory()));
27+
_toolManifest = toolManifest ?? new ToolManifestFinder(new DirectoryPath(currentWorkingDirectory ?? Directory.GetCurrentDirectory()));
2728
_localToolsResolverCache = localToolsResolverCache ?? new LocalToolsResolverCache();
2829
_fileSystem = fileSystem ?? new FileSystemWrapper();
2930
}

src/Cli/dotnet/CommandFactory/CommandResolution/PathCommandResolverPolicy.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Microsoft.DotNet.CommandFactory
77
{
88
public class PathCommandResolverPolicy : ICommandResolverPolicy
99
{
10-
public CompositeCommandResolver CreateCommandResolver()
10+
public CompositeCommandResolver CreateCommandResolver(string CurrentWorkingDirectory = null)
1111
{
1212
return Create();
1313
}

src/Cli/dotnet/CommandFactory/CommandResolver.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,21 @@ public static CommandSpec TryResolveCommandSpec(
3333
NuGetFramework framework = null,
3434
string configuration = Constants.DefaultConfiguration,
3535
string outputPath = null,
36-
string applicationName = null)
36+
string applicationName = null,
37+
string currentWorkingDirectory = null)
3738
{
3839
var commandResolverArgs = new CommandResolverArguments
3940
{
4041
CommandName = commandName,
4142
CommandArguments = args,
4243
Framework = framework,
43-
ProjectDirectory = Directory.GetCurrentDirectory(),
44+
ProjectDirectory = currentWorkingDirectory ?? Directory.GetCurrentDirectory(),
4445
Configuration = configuration,
4546
OutputPath = outputPath,
4647
ApplicationName = applicationName
4748
};
4849

49-
var defaultCommandResolver = commandResolverPolicy.CreateCommandResolver();
50+
var defaultCommandResolver = commandResolverPolicy.CreateCommandResolver(currentWorkingDirectory);
5051

5152
return defaultCommandResolver.Resolve(commandResolverArgs);
5253
}

src/Cli/dotnet/DotNetCommandFactory.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ namespace Microsoft.DotNet.Cli
1212
public class DotNetCommandFactory : ICommandFactory
1313
{
1414
private bool _alwaysRunOutOfProc;
15+
private readonly string _currentWorkingDirectory;
1516

16-
public DotNetCommandFactory(bool alwaysRunOutOfProc = false)
17+
public DotNetCommandFactory(bool alwaysRunOutOfProc = false, string currentWorkingDirectory = null)
1718
{
1819
_alwaysRunOutOfProc = alwaysRunOutOfProc;
20+
_currentWorkingDirectory = currentWorkingDirectory;
1921
}
2022

2123
public ICommand Create(
@@ -32,7 +34,7 @@ public ICommand Create(
3234
return new BuiltInCommand(commandName, args, builtInCommand);
3335
}
3436

35-
return CommandFactoryUsingResolver.CreateDotNet(commandName, args, framework, configuration);
37+
return CommandFactoryUsingResolver.CreateDotNet(commandName, args, framework, configuration, _currentWorkingDirectory);
3638
}
3739

3840
private bool TryGetBuiltInCommand(string commandName, out Func<string[], int> commandFunc)

src/Cli/dotnet/NugetPackageDownloader/FirstPartyNuGetPackageSigningVerifier.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,10 @@ internal bool IsFirstParty(FilePath nupkgToVerify)
7474
}
7575
}
7676

77-
private static bool NuGetVerify(FilePath nupkgToVerify, out string commandOutput)
77+
public static bool NuGetVerify(FilePath nupkgToVerify, out string commandOutput, string currentWorkingDirectory = null)
7878
{
7979
var args = new[] { "verify", "--all", nupkgToVerify.Value };
80-
var command = new DotNetCommandFactory(alwaysRunOutOfProc: true)
80+
var command = new DotNetCommandFactory(alwaysRunOutOfProc: true, currentWorkingDirectory)
8181
.Create("nuget", args);
8282

8383
var commandResult = command.CaptureStdOut().Execute();

src/Cli/dotnet/NugetPackageDownloader/LocalizableStrings.resx

+8-1
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,18 @@
132132
<data name="NuGetPackageSignatureVerificationSkipped" xml:space="preserve">
133133
<value>Skipping NuGet package signature verification.</value>
134134
</data>
135+
<data name="VerifyingNuGetPackageSignature" xml:space="preserve">
136+
<value>Verified that the NuGet package "{0}" has a valid signature.</value>
137+
</data>
138+
<data name="NuGetPackageShouldNotBeSigned" xml:space="preserve">
139+
<value>Skipping signature verification for NuGet package "{0}" because it comes from a source that does not require signature validation.</value>
140+
</data>
135141
<data name="SkipNuGetpackageSigningValidationmacOSLinux" xml:space="preserve">
136142
<value>Skip NuGet package signing validation. NuGet signing validation is not available on Linux or macOS https://aka.ms/workloadskippackagevalidation .</value>
137143
</data>
138144
<data name="FailedToValidatePackageSigning" xml:space="preserve">
139-
<value>Failed to validate package signing.</value>
145+
<value>Failed to validate package signing.
146+
{0}</value>
140147
</data>
141148
<data name="FailedToFindSourceUnderPackageSourceMapping" xml:space="preserve">
142149
<value>Package Source Mapping is enabled, but no source found under the specified package ID: {0}. See the documentation for Package Source Mapping at https://aka.ms/nuget-package-source-mapping for more details.</value>

src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs

+33-17
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ internal class NuGetPackageDownloader : INuGetPackageDownloader
4040

4141
private readonly bool _verifySignatures;
4242
private readonly VerbosityOptions _verbosityOptions;
43+
private readonly string _currentWorkingDirectory;
4344

4445
public NuGetPackageDownloader(
4546
DirectoryPath packageInstallDir,
@@ -51,8 +52,10 @@ public NuGetPackageDownloader(
5152
Func<IEnumerable<Task>> timer = null,
5253
bool verifySignatures = false,
5354
bool shouldUsePackageSourceMapping = false,
54-
VerbosityOptions verbosityOptions = VerbosityOptions.normal)
55+
VerbosityOptions verbosityOptions = VerbosityOptions.normal,
56+
string currentWorkingDirectory = null)
5557
{
58+
_currentWorkingDirectory = currentWorkingDirectory;
5659
_packageInstallDir = packageInstallDir;
5760
_reporter = reporter ?? Reporter.Output;
5861
_verboseLogger = verboseLogger ?? new NuGetConsoleLogger();
@@ -127,22 +130,22 @@ public async Task<string> DownloadPackageAsync(PackageId packageId,
127130
packageVersion.ToNormalizedString()));
128131
}
129132

130-
VerifySigning(nupkgPath);
131-
133+
await VerifySigning(nupkgPath, repository);
134+
132135
return nupkgPath;
133136
}
134137

135-
private bool verbosityGreaterThanMinimal()
136-
{
137-
return _verbosityOptions != VerbosityOptions.quiet && _verbosityOptions != VerbosityOptions.q
138-
&& _verbosityOptions != VerbosityOptions.minimal && _verbosityOptions != VerbosityOptions.m;
139-
}
138+
private bool VerbosityGreaterThanMinimal() =>
139+
_verbosityOptions != VerbosityOptions.quiet && _verbosityOptions != VerbosityOptions.q &&
140+
_verbosityOptions != VerbosityOptions.minimal && _verbosityOptions != VerbosityOptions.m;
140141

141-
private void VerifySigning(string nupkgPath)
142+
private bool DiagnosticVerbosity() => _verbosityOptions == VerbosityOptions.diag || _verbosityOptions == VerbosityOptions.diagnostic;
143+
144+
private async Task VerifySigning(string nupkgPath, SourceRepository repository)
142145
{
143146
if (!_verifySignatures && !_validationMessagesDisplayed)
144147
{
145-
if (verbosityGreaterThanMinimal())
148+
if (VerbosityGreaterThanMinimal())
146149
{
147150
_reporter.WriteLine(LocalizableStrings.NuGetPackageSignatureVerificationSkipped);
148151
}
@@ -154,15 +157,28 @@ private void VerifySigning(string nupkgPath)
154157
return;
155158
}
156159

157-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
160+
if (repository is not null &&
161+
await repository.GetResourceAsync<RepositorySignatureResource>().ConfigureAwait(false) is RepositorySignatureResource resource &&
162+
resource.AllRepositorySigned)
158163
{
159-
if (!_firstPartyNuGetPackageSigningVerifier.Verify(new FilePath(nupkgPath),
160-
out string commandOutput))
164+
string commandOutput;
165+
// The difference between _firstPartyNuGetPackageSigningVerifier.Verify and FirstPartyNuGetPackageSigningVerifier.NuGetVerify is that while NuGetVerify
166+
// just ensures that the package is signed properly, Verify additionally requires that the package be from Microsoft. NuGetVerify does not require that
167+
// the package be from Microsoft.
168+
if ((!_shouldUsePackageSourceMapping && !_firstPartyNuGetPackageSigningVerifier.Verify(new FilePath(nupkgPath), out commandOutput)) ||
169+
(_shouldUsePackageSourceMapping && !FirstPartyNuGetPackageSigningVerifier.NuGetVerify(new FilePath(nupkgPath), out commandOutput, _currentWorkingDirectory)))
161170
{
162-
throw new NuGetPackageInstallerException(LocalizableStrings.FailedToValidatePackageSigning +
163-
Environment.NewLine +
164-
commandOutput);
171+
throw new NuGetPackageInstallerException(string.Format(LocalizableStrings.FailedToValidatePackageSigning, commandOutput));
165172
}
173+
174+
if (DiagnosticVerbosity())
175+
{
176+
_reporter.WriteLine(LocalizableStrings.VerifyingNuGetPackageSignature, Path.GetFileNameWithoutExtension(nupkgPath));
177+
}
178+
}
179+
else if (DiagnosticVerbosity())
180+
{
181+
_reporter.WriteLine(LocalizableStrings.NuGetPackageShouldNotBeSigned, Path.GetFileNameWithoutExtension(nupkgPath));
166182
}
167183
}
168184

@@ -334,7 +350,7 @@ private IEnumerable<PackageSource> LoadOverrideSources(PackageSourceLocation pac
334350
private List<PackageSource> LoadDefaultSources(PackageId packageId, PackageSourceLocation packageSourceLocation = null, PackageSourceMapping packageSourceMapping = null)
335351
{
336352
List<PackageSource> defaultSources = new();
337-
string currentDirectory = Directory.GetCurrentDirectory();
353+
string currentDirectory = _currentWorkingDirectory ?? Directory.GetCurrentDirectory();
338354
ISettings settings;
339355
if (packageSourceLocation?.NugetConfig != null)
340356
{

src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.cs.xlf

+14-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cli/dotnet/NugetPackageDownloader/xlf/LocalizableStrings.de.xlf

+14-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)