Skip to content

Add catalog signing for unsigned apphost template files#127888

Open
jesuszarate wants to merge 3 commits into
dotnet:mainfrom
jesuszarate:dev/jezarat/catalog-sign-apphost-templates
Open

Add catalog signing for unsigned apphost template files#127888
jesuszarate wants to merge 3 commits into
dotnet:mainfrom
jesuszarate:dev/jezarat/catalog-sign-apphost-templates

Conversation

@jesuszarate
Copy link
Copy Markdown

Summary

The apphost template files (\�pphost.exe, \singlefilehost.exe, \comhost.dll) are intentionally unsigned because the .NET SDK modifies them at build time via \HostWriter.CreateAppHost(). However, the Visual Studio signing scan (SignVerify) flags them as non-compliant unsigned PE binaries.

This change adds a catalog file (.cat) containing SHA256 hashes of the template files. The catalog is signed with \MicrosoftDotNet500\ via Arcade signing and shipped alongside the templates in the AppHostPack NuGet package and MSI. This provides integrity verification for VS signing compliance without breaking the SDK workflow.

Changes

  1. *\�ng/Signing.props* — Added \FileSignInfo\ entry for \�pphost-templates.cat\ with \MicrosoftDotNet500\ certificate
  2. *\src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Host.sfxproj* — Added \GenerateAppHostTemplateCatalog\ target that:
    • Stages the three unsigned template files in an isolated directory
    • Generates a .cat\ file using \New-FileCatalog\ (Windows only)
    • Includes the .cat\ in \FilesToPackage\ so it ships in the NuGet pack and MSI

Context

Risk

Low — This is a purely additive change. The unsigned template files are not modified. The .cat\ file is a new artifact that provides integrity verification only. The target only runs on Windows builds.

The apphost template files (apphost.exe, singlefilehost.exe, comhost.dll)
are intentionally unsigned because the .NET SDK modifies them at build time
via HostWriter.CreateAppHost(). However, the Visual Studio signing scan
flags them as non-compliant.

This change adds a catalog file (.cat) containing SHA256 hashes of the
template files. The catalog is signed with MicrosoftDotNet500 via Arcade
signing and shipped alongside the templates in the AppHostPack NuGet
package and MSI. This provides integrity verification for VS signing
compliance without breaking the SDK workflow.

Fixes the VS signing scan finding for 54 apphost template PE files across
.NET 8/9/10 architectures.

Related: dotnet#3694
Copilot AI review requested due to automatic review settings May 6, 2026 21:28
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.

@jesuszarate jesuszarate marked this pull request as ready for review May 18, 2026 21:00
Copilot AI review requested due to automatic review settings May 18, 2026 21:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Condition="'$(TargetOS)' == 'windows'">
<PropertyGroup>
<_CatStagingDir>$(IntermediateOutputPath)apphost-catalog\</_CatStagingDir>
<_CatalogFilePath>$(DotNetHostBinDir)apphost-templates.cat</_CatalogFilePath>
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 3ef521e — moved _CatalogFilePath from to so it's per-project and won't collide across parallel TFM/arch builds.

Comment on lines +107 to +117
<!-- Stage the unsigned template files in an isolated directory for catalog generation -->
<RemoveDir Directories="$(_CatStagingDir)" />
<MakeDir Directories="$(_CatStagingDir)" />
<Copy SourceFiles="$(DotNetHostBinDir)apphost.exe" DestinationFolder="$(_CatStagingDir)" Condition="Exists('$(DotNetHostBinDir)apphost.exe')" />
<Copy SourceFiles="$(DotNetHostBinDir)singlefilehost.exe" DestinationFolder="$(_CatStagingDir)" Condition="Exists('$(DotNetHostBinDir)singlefilehost.exe')" />
<Copy SourceFiles="$(DotNetHostBinDir)comhost.dll" DestinationFolder="$(_CatStagingDir)" Condition="Exists('$(DotNetHostBinDir)comhost.dll')" />

<Exec Command="powershell.exe -NoProfile -NonInteractive -Command &quot;New-FileCatalog -Path '$(_CatStagingDir)' -CatalogFilePath '$(_CatalogFilePath)' -CatalogVersion 2&quot;" />

<ItemGroup>
<FilesToPackage Include="$(_CatalogFilePath)" />
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 3ef521e — added _StagedTemplateFiles ItemGroup that globs the staging directory. The Exec is now conditioned on @(_StagedTemplateFiles) != '' and FilesToPackage is conditioned on Exists('$(_CatalogFilePath)'), so neither runs if no template files were staged.

<Copy SourceFiles="$(DotNetHostBinDir)singlefilehost.exe" DestinationFolder="$(_CatStagingDir)" Condition="Exists('$(DotNetHostBinDir)singlefilehost.exe')" />
<Copy SourceFiles="$(DotNetHostBinDir)comhost.dll" DestinationFolder="$(_CatStagingDir)" Condition="Exists('$(DotNetHostBinDir)comhost.dll')" />

<Exec Command="powershell.exe -NoProfile -NonInteractive -Command &quot;New-FileCatalog -Path '$(_CatStagingDir)' -CatalogFilePath '$(_CatalogFilePath)' -CatalogVersion 2&quot;" />
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 3ef521e — added a _PowerShellCommand property that resolves to the full path via $(SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe when it exists, falling back to bare powershell.exe otherwise. This avoids PATH dependency on Windows build agents.

…ll path

- Move _CatalogFilePath from $(DotNetHostBinDir) to $(IntermediateOutputPath)
  to avoid cross-target/parallel-build collisions in the shared output directory
- Add _StagedTemplateFiles ItemGroup and condition the Exec on it being
  non-empty so the build is resilient to missing template inputs
- Condition FilesToPackage on Exists() for the catalog file
- Resolve powershell.exe via $(SystemRoot) full path to avoid PATH dependency,
  with fallback to bare powershell.exe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Host community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants