arcade icon indicating copy to clipboard operation
arcade copied to clipboard

Add DoNotUnpack flag to sign containers without extracting contents

Open Copilot opened this issue 1 month ago • 5 comments

Description

Adds DoNotUnpack metadata flag to FileSignInfo and FileExtensionSignInfo that signs containers at the top level without unpacking. Prevents signing checked-in binaries in source archives. The certificate name is optional when specifying DoNotUnpack, allowing flexible configuration where DoNotUnpack behavior can be set independently of certificate selection.

Changes

  • SignToolConstants: Added DoNotUnpack constant
  • SignInfo: Added DoNotUnpack property and WithDoNotUnpack() method
  • FileSignInfoEntry: New class to hold certificate name (optional) and DoNotUnpack flag for FileSignInfo entries
  • ExplicitSignInfoKey (renamed from ExplicitCertificateKey): Represents the entity being signed rather than just certificate correlation, supports setting properties like DoNotUnpack independently of certificates
  • SignToolTask:
    • Parse DoNotUnpack metadata from both FileSignInfo and FileExtensionSignInfo
    • Fixed validation: Certificate is only required when DoNotUnpack is NOT set (applies to both FileSignInfo and FileExtensionSignInfo)
  • Configuration:
    • Skip TryBuildZipData/TryBuildWixData when DoNotUnpack is set via SignInfo
    • Updated comments to clarify mappings include DoNotUnpack data
    • Logic allows setting DoNotUnpack without specifying a certificate
  • FileSignInfo:
    • ShouldRepack now uses SignInfo.DoNotUnpack
    • Updated ToString() to include DoNotUnpack info when file has signable parts and DoNotUnpack is true
  • Tests: Added comprehensive coverage for FileSignInfo and FileExtensionSignInfo scenarios:
    • ContainerSigningWithDoNotUnpackViaFileSignInfo - verifies DoNotUnpack on top-level container without certificate and on nested container with certificate
    • ContainerSigningWithDoNotUnpackViaFileExtensionSignInfo - verifies DoNotUnpack via extension for all matching files

Usage

Via FileSignInfo (per-file) with certificate:

<FileSignInfo Include="dotnet-sdk-source.tar.gz" CertificateName="LinuxSign" DoNotUnpack="true" />

Via FileSignInfo (per-file) without certificate (uses default from extension):

<FileSignInfo Include="dotnet-sdk-source.tar.gz" DoNotUnpack="true" />

Via FileExtensionSignInfo (per-extension) with certificate:

<FileExtensionSignInfo Include=".tar.gz" CertificateName="LinuxSign" DoNotUnpack="true" />

Via FileExtensionSignInfo (per-extension) without certificate:

<FileExtensionSignInfo Include=".tar.gz" DoNotUnpack="true" />

With DoNotUnpack=true, the container is signed as-is without extracting or signing nested binaries. When no certificate is specified with DoNotUnpack, the file uses the default certificate from FileExtensionSignInfo.

Precedence

FileSignInfo takes precedence over FileExtensionSignInfo for the DoNotUnpack value. This allows the same file to be signed without unpacking when it appears at the top level, but still be unpacked when nested in another container (unless explicitly overridden by FileSignInfo).

To double check:

  • [x] The right tests are in and the right validation has happened. Guidance: https://github.com/dotnet/arcade/blob/main/Documentation/Validation.md
Original prompt

This section details on the original issue you should resolve

<issue_title>Allow Signing of Containers Without Unpacking</issue_title> <issue_description>Currently, when we attempt to sign containers using SignTool, the process unpacks the container and signs artifacts inside them. For some containers, such as the source-built source archives, this leads to the risk of signing artifacts, like binaries, that were checked into the repo. We need to add a feature to SignTool that allows signing of the top-level container without unpacking.</issue_description>

<agent_instructions>SignTool is in https://github.com/dotnet/arcade/tree/main/src/Microsoft.DotNet.SignTool. As part of this work, also add some unit tests in https://github.com/dotnet/arcade/blob/main/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs for this feature.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

  • Fixes dotnet/arcade#16370

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot avatar Dec 03 '25 17:12 Copilot

@copilot Because a top-level file could be duplicated within other archives (e.g. an MSI at the top level as well as within a VS.* nupkg), I think a better way to implement this is via a new bit of metadata on FileSignInfo and other certificate specification metadata.

mmitche avatar Dec 04 '25 17:12 mmitche

@copilot Because a top-level file could be duplicated within other archives (e.g. an MSI at the top level as well as within a VS.* nupkg), I think a better way to implement this is via a new bit of me...

I understand the concern about duplicated files. To clarify the intended design: should the DoNotUnpack behavior be specified via FileSignInfo (per-file name) or FileExtensionSignInfo (per-extension like .tar.gz)? For example, would the configuration look like:

<FileSignInfo Include="dotnet-sdk-source.tar.gz" CertificateName="LinuxSign" DoNotUnpack="true" />

Or via extension:

<FileExtensionSignInfo Include=".tar.gz" CertificateName="LinuxSign" DoNotUnpack="true" />

This would allow the same file to be signed without unpacking when it appears at the top level, but still be unpacked when nested in another container. Please confirm and I'll refactor the implementation accordingly.

Copilot avatar Dec 04 '25 17:12 Copilot

I understand the concern about duplicated files. To clarify the intended design: should the DoNotUnpack behavior be specified via FileSignInfo (per-file name) or FileExtensionSignInfo (per-extension like .tar.gz)? For example, would the configuration look like:

<FileSignInfo Include="dotnet-sdk-source.tar.gz" CertificateName="LinuxSign" DoNotUnpack="true" />

Or via extension:

<FileExtensionSignInfo Include=".tar.gz" CertificateName="LinuxSign" DoNotUnpack="true" />

This would allow the same file to be signed without unpacking when it appears at the top level, but still be unpacked when nested in another container. Please confirm and I'll refactor the implementation accordingly.

@copilot - please implement this for both file name (FileSignInfo) and file extension (FileExtensionSignInfo).

ellahathaway avatar Dec 04 '25 20:12 ellahathaway

I understand the concern about duplicated files. To clarify the intended design: should the DoNotUnpack behavior be specified via FileSignInfo (per-file name) or FileExtensionSignInfo (per-ext...

Implemented DoNotUnpack for both FileSignInfo (per-file name) and FileExtensionSignInfo (per-extension). The implementation adds a new FileSignInfoEntry class to hold both certificate name and DoNotUnpack flag, and extends SignInfo to carry the DoNotUnpack state from extension-based configuration.

Usage examples:

<!-- Per-file via FileSignInfo -->
<FileSignInfo Include="dotnet-sdk-source.tar.gz" CertificateName="LinuxSign" DoNotUnpack="true" />

<!-- Per-extension via FileExtensionSignInfo -->
<FileExtensionSignInfo Include=".tar.gz" CertificateName="LinuxSign" DoNotUnpack="true" />

Added tests for both approaches. Commit: e7a45b0

[!WARNING]

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • securitytools.pkgs.visualstudio.com
    • Triggering command: /opt/hostedtoolcache/CodeQL/2.23.6/x64/codeql/csharp/tools/linux64/Semmle.Autobuild.CSharp /opt/hostedtoolcache/CodeQL/2.23.6/x64/codeql/csharp/tools/linux64/Semmle.Autobuild.CSharp (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot avatar Dec 04 '25 20:12 Copilot

@copilot

Please refactor ExplicitCertificateKey to a name that more clearly describes its actual use, such as ExplicitSignInfoKey (or something similar). The current name implies a direct relat...

Refactored ExplicitCertificateKey to ExplicitSignInfoKey to better reflect its purpose. The key now represents the entity being signed (file name, attributes) rather than just certificate correlation. Added documentation clarifying it can specify signing properties like DoNotUnpack independently of certificates. Commit: dfa9bcc

Copilot avatar Dec 12 '25 18:12 Copilot