dotnet / templating

This repo contains the Template Engine which is used by dotnet new
https://docs.microsoft.com/dotnet/
MIT License
1.63k stars 369 forks source link

The conditions of the Target tag in the csproj are removed if a template's condition is true #7325

Open maximeozenne opened 10 months ago

maximeozenne commented 10 months ago

Product

dotnet CLI & Visual Studio

Describe The Bug

I'm creating a template which modify a .csproj file.

In my template.json, I added a boolean like following :

"symbols": {
    "GenerateClient": {
      "type": "parameter",
      "description": "Automatically adds the Client generation",
      "datatype": "bool",
      "defaultValue": "true"
    },
  },

In the code, it's as easy as following to modify it if the GenerateClient is true :

#if (GenerateClient)
    Console.WriteLine("Client generated.");
#endif

But in the .csproj it's different. I can't (or at least, I didn't find how to) add a bunch of lines in a GenerateClient condition.

So instead of that, I'm adding a condition to each line of my .csproj, like following :

<PackageReference Condition="'$(GenerateClient)' == 'True'" Include="NSwag.AspNetCore" Version="13.20.0" />

If the condition is false, the line is removed, and if it's true, the Condition parameter is removed. It's exactly what I want.

<PackageReference Include="NSwag.AspNetCore" Version="13.20.0" />

But unfortunately, if your tag already has a Condition parameter and you add the '$(GenerateClient)' == 'True' in it, the whole Condition is getting removed.

For example, I have a Target tag with the following Condition parameter :

<Target Name="NSwag" BeforeTargets="AfterBuild" Condition="'$(TF_BUILD)'!='True' And '$(Configuration)' != 'Debug'">

I want that the Target tag is getting added only if the GenerateClient bool is true. So I modified the Condition parameter as following :

<Target Name="NSwag" BeforeTargets="AfterBuild" Condition="'$(GenerateClient)' == 'True' And '$(TF_BUILD)'!='True' And '$(Configuration)' != 'Debug'">

If the GenerateClient bool is false, it works. The whole Target tag is getting removed. But if the GenerateClient is true, the Target tag is being added, but the Condition parameter is totally removed, like the following :

<Target Name="NSwag" BeforeTargets="AfterBuild">

I tried some workarounds, for example I added a Label : Label="The label will be replaced in order to readd Conditions", then I added a symbol to the template.json :

"TargetCondition": {
      "type": "parameter",
      "defaultValue": "Condition=\"'$(TF_BUILD)'!='True' And '$(Configuration)' != 'Debug'\"",
      "replaces":"Label=\"The label will be replaced in order to readd Conditions\""
    }

But the problem is now any user of my template can modify the TargetCondition string, and possibly abuse this vulnerability.

I also tried to surround the Target tag with an ItemGroup which has the GenerateClient condition like that :

<ItemGroup Condition="'$(GenerateClient)' == 'True'">
  <Target Name="NSwag" BeforeTargets="AfterBuild" Condition="'$(TF_BUILD)'!='True' And '$(Configuration)' != 'Debug'">
    ...
  </Target>
</ItemGroup>

But the Target tag should be at the root of the Project tag, and cannot be surrounded by any tag. So if I do this, the .csproj isn't valid anymore.

Would it be possible for the Condition parameter to not being totally removed ? Maybe I'm missing a point.

Thanks for your help and your work !

To Reproduce

Steps:

  1. Create a template with a template.json.
  2. Add a boolean symbol, for example "GenerateClient".
  3. Add a Target tag to any .csproj, with a Condition parameter.
  4. Add the boolean symbol previously created to the Condition parameter (Condition="'$(GenerateClient)' == 'True'").
  5. Generate a new project using this project, with the "GenerateClient" condition to true.
  6. The newly created .csprojshould have a Target tag without any Condition parameter.

dotnet Info

output SDK .NET : Version: 7.0.403 Commit: 142776d834 Environnement d'exécution : OS Name: Windows OS Version: 10.0.22621 OS Platform: Windows RID: win10-arm64 Base Path: C:\Program Files\dotnet\sdk\7.0.403\ Host: Version: 7.0.13 Architecture: arm64 Commit: 3f73a2f186 .NET SDKs installed: 7.0.403 [C:\Program Files\dotnet\sdk] .NET runtimes installed: Microsoft.AspNetCore.App 6.0.24 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 6.0.24 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 6.0.24 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Other architectures found: x64 [C:\Program Files\dotnet\x64] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x64\InstallLocation] x86 [C:\Program Files (x86)\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation] Environment variables: Not set global.json file: Not found Learn more: https://aka.ms/dotnet/info Download .NET: https://aka.ms/dotnet/download

Visual Studio Version

2022

Additional context

No response

maximeozenne commented 10 months ago

Hello,

So I updated my workaround using the constantgenerator I found during my researches :

"TargetCondition": {
      "type": "generated",
      "generator": "constant",
      "parameters": {
        "value":"Condition=\"'$(TF_BUILD)'!='True' And '$(Configuration)' != 'Debug'\""
      },
      "replaces":"Label=\"The label will be replaced in order to readd Conditions\""
    }

Now it works and there isn't a vulnerability in the template.

Unfortunately, I didn't find any cleaner solution to my problem for the moment.

marcselman commented 7 months ago

This is documented and can easily be fixed in two ways. See: https://github.com/dotnet/templating/blob/v7.0.308/docs/Conditional-processing-and-comment-syntax.md#ignore-conditions-expressions-in-msbuild-files