dotnet / sdk

Core functionality needed to create .NET Core projects, that is shared between Visual Studio and CLI
https://dot.net/core
MIT License
2.68k stars 1.06k forks source link

CopyLocalLockFileAssemblies produces broken deps.json file if PrivateAssets is set to compile #22124

Open handerss-spotfire opened 2 years ago

handerss-spotfire commented 2 years ago

Setting CopyLocalLockFileAssemblies causes runtime exceptions for projects where PrivateAssets is set to compile. This is because the resulting .deps.json file will contain a ".Reference" entry which does not contain the necessary runtime dll.

Example

This project uses a facade library which is meant to wrap some API (in this case System.Security.Cryptography.ProtectedData). To protect consuming projects from the wrapped library the package reference has its private assets set to compile. This works well unless the Facade library adds the CopyLocalLockFileAssemblies and sets it true. This addition causes the consuming program's .deps.json to get a reference entry added, in this case it's System.Security.Cryptography.ProtectedData.Reference/5.0.0.0, which in turn results in the program crashing with a runtime error.

The example program below can be run with dotnet run --project ./Transitive/Transitive.csproj and results in the following output:

Unhandled exception. System.PlatformNotSupportedException: Windows Data Protection API (DPAPI) is not supported on this platform.
   at System.Security.Cryptography.ProtectedData.Protect(Byte[] userData, Byte[] optionalEntropy, DataProtectionScope scope)
   at Facade.Facade.Protect(Byte[] input) in C:\tibco\Source\transitive\Facade\Facade.cs:line 8
   at Program.<Main>$(String[] args) in C:\tibco\Source\transitive\Transitive\Program.cs:line 4

Facade/Facade.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.Security.Cryptography.ProtectedData" Version="5.0.0" PrivateAssets="compile" />
  </ItemGroup>
</Project>

Facade/Facade.cs

using System;
using System.Security.Cryptography;

namespace Facade
{
    public static class Facade
    {
        public static byte[] Protect(byte[] input) => ProtectedData.Protect(input, null, DataProtectionScope.CurrentUser);
    }
}

Transitive/Transitive.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\Facade\Facade.csproj" />
  </ItemGroup>
</Project>

Transitive/Program.cs

using System;
using Facade;

var foo = Facade.Facade.Protect(Array.Empty<byte>());
handerss-spotfire commented 2 years ago

The solution of setting PrivateAssets to compile to protect against transitive dependencies is suggested by @nkolev92 here: https://github.com/dotnet/sdk/issues/11803#issuecomment-744729221

For our use case this works well except for the fact that the resulting deps.json files sometimes contain ".Reference" entries which break at runtime. This issue is one such reproducible case, but unfortunately we get these additions even without CopyLocalLockFileAssemblies. Is there anyway to turn this type of behavior off in general?

Is PrivateAssets=compile + DisableTransitiveProjectReferences the best way to protect against compiling against transitive dependencies.

sfoslund commented 2 years ago

This issue is one such reproducible case, but unfortunately we get these additions even without CopyLocalLockFileAssemblies. Is there anyway to turn this type of behavior off in general?

Not sure, @dsplaisted do you have any guidance here?