Skip to content
Open
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
2 changes: 1 addition & 1 deletion NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<clear />
<!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
<!-- Begin: Package sources from dotnet-dotnet -->
<add key="darc-pub-dotnet-dotnet-4fdcea9" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-dotnet-4fdcea96/nuget/v3/index.json" />
<add key="darc-pub-dotnet-dotnet-4bc7a57" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-dotnet-4bc7a57e/nuget/v3/index.json" />
<!-- End: Package sources from dotnet-dotnet -->
<!-- Begin: Package sources from dotnet-runtime -->
<!-- End: Package sources from dotnet-runtime -->
Expand Down
24 changes: 15 additions & 9 deletions docs/api/Inside-the-Template-Engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ designed to separate gathering, instantiating and processing templates.

# Overview of the subsystems

## [IEngineEnvironmentSettings](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Abstractions/IEngineEnvironmentSettings.cs)
## [IEngineEnvironmentSettings](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Abstractions/IEngineEnvironmentSettings.cs)

This is responsible for holding all properties of the environment.

Expand Down Expand Up @@ -69,7 +69,7 @@ and

`ComponentManager` is responsible for loading [components](#components) provided by the host from
`ITemplateEngineHost.BuiltIns`. Additional components can be added via
`IComponentManager.AddComponent`. Similar to templates, components are also
`IComponentManager.AddComponent`. Similar to templates, components are also
dynamically loaded from template packages during scanning. `ComponentManager`
implementation is not publicly accessible; however, it is used when default
implementation of `IEngineEnvironmentSettings` is used.
Expand Down Expand Up @@ -120,9 +120,9 @@ instantiate the class when needed.

### Supplying parameters conditions results

It is possible to supply evaluation results of parameters conditions when instantiating template via Edge API [`TemplateCreator.InstantiateAsync`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/TemplateCreator.cs#L89). Example use case is instantiation from Visual Studio host, that will leverage condition evaluator integrated within the New Project Dialog.
It is possible to supply evaluation results of parameters conditions when instantiating template via Edge API [`TemplateCreator.InstantiateAsync`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/TemplateCreator.cs#L89). Example use case is instantiation from Visual Studio host, that will leverage condition evaluator integrated within the New Project Dialog.

This can be achieved by passing the structured [`InputDataSet`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/InputDataSet.cs) argument that is populated with [`EvaluatedInputParameterData`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/EvaluatedInputParameterData.cs) objects for evaluated parameters.
This can be achieved by passing the structured [`InputDataSet`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/InputDataSet.cs) argument that is populated with [`EvaluatedInputParameterData`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/EvaluatedInputParameterData.cs) objects for evaluated parameters.

It is currently not possible to provide just partial external evaluation - meaning that the template engine evaluates either all the parameter conditions or none. If the [`InputDataSet`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/InputDataSet.cs) collection contains at least one [`EvaluatedInputParameterData`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/EvaluatedInputParameterData.cs) element, results of all parameter conditions are expected to be passed.

Expand Down Expand Up @@ -178,7 +178,7 @@ be split into two main categories:

- mount points

## Template package providers
## Template package providers

Template package providers responsible for providing template packages the
template engine needs to manage. There are two kinds of providers:
Expand Down Expand Up @@ -243,6 +243,12 @@ Template engine provides two installer implementations:
You can use them when implementing your own providers, they are available from
IComponentManager.

When package verification is enabled, downloaded signed NuGet packages are
verified using `dotnet nuget verify --all` before installation. Unsigned packages
are accepted only when NuGet's `signatureValidationMode` is `accept` and the
source does not declare `allRepositorySigned=true`. A client policy of `require`
or a repository signing requirement rejects unsigned packages.

## [Mount point](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Abstractions/Mount/IMountPoint.cs)

Template package location is represented by mount point URI. Mount point that
Expand All @@ -268,14 +274,14 @@ Template engine host works with constraint using `TemplateConstraintManager` cla

The template may define `bind` symbols. Those symbols might be bound to values provided externally using bind symbol source components.
Default sources are:
- host provided parameters - uses prefix `host:`. Binds symbol to parameters provided by the host.
- host provided parameters - uses prefix `host:`. Binds symbol to parameters provided by the host.
- environment variables - uses prefix `env:`. Binds symbol to environment variables.

These components are defined in `Microsoft.TemplateEngine.Edge` and are part of mandatory components collection to be loaded.

If the `binding` does not have prefix, or prefixed value cannot be evaluated the evaluation without prefix is performed as the fallback.
If the `binding` does not have prefix, or prefixed value cannot be evaluated the evaluation without prefix is performed as the fallback.
In case there are more than one component which returns the value, the component priority will be used to define the value to be bounded. In case of same priorities, the evaluation results in error.
The template author needs to explicitly specify the prefix in this case. It is possible to restrict usage of prefix with setting `IBindSymbolSource.RequiresPrefixMatch` to `true`.
The template author needs to explicitly specify the prefix in this case. It is possible to restrict usage of prefix with setting `IBindSymbolSource.RequiresPrefixMatch` to `true`.


## Registering the components
Expand Down Expand Up @@ -325,7 +331,7 @@ It is possible to register additional components to in the following way:
Note that components are not persisted, so they should be added each time
EngineEnvironmentSettings instance is created.

# Microsoft.TemplateEngine.IDE
# Microsoft.TemplateEngine.IDE

The package provides
[Bootstrapper](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.IDE/Bootstrapper.cs)
Expand Down
2 changes: 1 addition & 1 deletion eng/Version.Details.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file should be imported by eng/Versions.props
<Project>
<PropertyGroup>
<!-- dotnet-dotnet dependencies -->
<MicrosoftDotNetArcadeSdkPackageVersion>10.0.0-beta.26561.117</MicrosoftDotNetArcadeSdkPackageVersion>
<MicrosoftDotNetArcadeSdkPackageVersion>10.0.0-beta.26562.116</MicrosoftDotNetArcadeSdkPackageVersion>
<SystemCommandLinePackageVersion>2.0.16</SystemCommandLinePackageVersion>
<!-- dotnet-runtime dependencies -->
<MicrosoftBclAsyncInterfacesPackageVersion>10.0.1</MicrosoftBclAsyncInterfacesPackageVersion>
Expand Down
8 changes: 4 additions & 4 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Dependencies>
<Source Uri="https://github.com/dotnet/dotnet" Mapping="templating" Sha="4fdcea96a759959466691f692a50fc4abf22c83a" BarId="341128" />
<Source Uri="https://github.com/dotnet/dotnet" Mapping="templating" Sha="4bc7a57e2c2686797289688cbdc396791eedaa02" BarId="341305" />
<ProductDependencies>
<Dependency Name="System.CommandLine" Version="2.0.16">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>4fdcea96a759959466691f692a50fc4abf22c83a</Sha>
<Sha>4bc7a57e2c2686797289688cbdc396791eedaa02</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="10.0.0-beta.26561.117">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="10.0.0-beta.26562.116">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>4fdcea96a759959466691f692a50fc4abf22c83a</Sha>
<Sha>4bc7a57e2c2686797289688cbdc396791eedaa02</Sha>
</Dependency>
<!-- Dependencies required for source build. We'll still update manually -->
<Dependency Name="System.Formats.Asn1" Version="10.0.1">
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"dotnet": "10.0.115"
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26561.117"
"Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26562.116"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using Microsoft.TemplateEngine.CommandUtils;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Packaging;
using NuGet.Packaging.Signing;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;

namespace Microsoft.TemplateEngine.Edge.Installers.NuGet
{
internal interface INuGetPackageVerifier
{
bool IsEnabled { get; }

Task VerifyAsync(string packagePath, SourceRepository repository, ISettings settings, ILogger logger, CancellationToken cancellationToken);
}

internal sealed class DotnetNuGetPackageVerifier : INuGetPackageVerifier
{
private const string SignatureVerificationEnvironmentVariable = "DOTNET_NUGET_SIGNATURE_VERIFICATION";

public DotnetNuGetPackageVerifier()
{
bool? configuredValue = bool.TryParse(
Environment.GetEnvironmentVariable(SignatureVerificationEnvironmentVariable),
out bool shouldVerify)
? shouldVerify
: null;

IsEnabled = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|| configuredValue == true
|| (configuredValue == null && RuntimeInformation.IsOSPlatform(OSPlatform.Linux));
}

public bool IsEnabled { get; }

public async Task VerifyAsync(string packagePath, SourceRepository repository, ISettings settings, ILogger logger, CancellationToken cancellationToken)
{
_ = packagePath ?? throw new ArgumentNullException(nameof(packagePath));
_ = repository ?? throw new ArgumentNullException(nameof(repository));
_ = settings ?? throw new ArgumentNullException(nameof(settings));
_ = logger ?? throw new ArgumentNullException(nameof(logger));
cancellationToken.ThrowIfCancellationRequested();

bool isSigned;
using (var reader = new PackageArchiveReader(packagePath))
{
isSigned = await reader.IsSignedAsync(cancellationToken).ConfigureAwait(false);
}

if (!isSigned)
{
ClientPolicyContext clientPolicy = ClientPolicyContext.GetClientPolicy(settings, logger);
RepositorySignatureResource? repositorySignature =
await repository.GetResourceAsync<RepositorySignatureResource>(cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
if (clientPolicy.Policy == SignatureValidationMode.Require || repositorySignature?.AllRepositorySigned == true)
{
const string message = "The package is unsigned, but client or repository policy requires signing.";
logger.LogDebug(message);
throw new InvalidDataException(message);
}

logger.LogDebug("Accepting unsigned package under accept policy.");
return;
}

await VerifySignedPackageAsync(packagePath, logger, cancellationToken).ConfigureAwait(false);
}

private static async Task VerifySignedPackageAsync(string packagePath, ILogger logger, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "dotnet",
Arguments = $"nuget verify --all {ArgumentEscaper.EscapeSingleArg(packagePath)}",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true
}
};
if (!process.Start())
{
throw new InvalidOperationException("Failed to start 'dotnet nuget verify'.");
}

#if NET
Task<string> standardOutput = process.StandardOutput.ReadToEndAsync(CancellationToken.None);
Task<string> standardError = process.StandardError.ReadToEndAsync(CancellationToken.None);
#else
Task<string> standardOutput = process.StandardOutput.ReadToEndAsync();
Task<string> standardError = process.StandardError.ReadToEndAsync();
#endif
await WaitForExitAsync(process, logger, cancellationToken).ConfigureAwait(false);

string output = await standardOutput.ConfigureAwait(false);
string error = await standardError.ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();

LogOutput(logger, output, LogLevel.Debug);
LogOutput(logger, error, process.ExitCode == 0 ? LogLevel.Debug : LogLevel.Error);

if (process.ExitCode != 0)
{
string command = $"{process.StartInfo.FileName} {process.StartInfo.Arguments}";
throw new SecurityException($"'{command}' failed with exit code {process.ExitCode}.");
}

return;

static void LogOutput(ILogger logger, string output, LogLevel level)
{
if (!string.IsNullOrWhiteSpace(output))
{
logger.Log(level, output.Trim());
}
}
}

private static async Task WaitForExitAsync(Process process, ILogger logger, CancellationToken cancellationToken)
{
using (cancellationToken.Register(() => TerminateProcess(process, logger)))
{
// Process.WaitForExitAsync is unavailable on netstandard2.0 and net472.
await Task.Run(process.WaitForExit, CancellationToken.None).ConfigureAwait(false);
}

return;

static void TerminateProcess(Process process, ILogger logger)
{
try
{
if (!process.HasExited)
{
process.Kill();
}
}
catch (InvalidOperationException) when (process.HasExited)
{
// The process exited between checking HasExited and calling Kill.
}
catch (Win32Exception ex)
{
if (!process.HasExited)
{
logger.LogDebug($"Failed to terminate 'dotnet nuget verify': {ex}");
}
}
}
}
}
}
Loading
Loading