Skip to content
Merged
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
8 changes: 5 additions & 3 deletions eng/Signing.props
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,14 @@

<!--
npm packages are tarballs, but only the Aspire CLI npm tarballs should get
detached signatures. Scope both the .tgz rule and the nested JavaScript
launcher override to the npm package ItemsToSign collision id so the global
.tgz/.js defaults remain unchanged for unrelated artifacts.
detached signatures. Scope these rules to the npm package ItemsToSign
collision id so the global .tgz/.js/native-executable defaults remain
unchanged for unrelated artifacts.
-->
<FileExtensionSignInfo Include=".tgz" CertificateName="LinuxSign500180PGP" CollisionPriorityId="AspireCliNpmPackage" />
<FileSignInfo Include="aspire.js" CertificateName="MicrosoftDotNet500" CollisionPriorityId="AspireCliNpmPackage" />
<FileSignInfo Include="aspire.exe" CertificateName="None" CollisionPriorityId="AspireCliNpmPackage" />
<FileSignInfo Include="aspire" CertificateName="None" CollisionPriorityId="AspireCliNpmPackage" />
</ItemGroup>

<ItemGroup>
Expand Down
33 changes: 33 additions & 0 deletions tests/Infrastructure.Tests/Pipelines/NpmCliPackageTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Xml.Linq;
using Xunit;

namespace Infrastructure.Tests;
Expand Down Expand Up @@ -200,6 +201,23 @@ public async Task NpmInstallValidationJobsUseExplicitJobsSharedStepsTemplateAndC
Assert.Contains("$(NPM_VALIDATION_SUMMARY_OSX_ARTIFACT)", releasePipeline);
}

[Fact]
public async Task NpmSigningScopeCoversNestedTarballPayloads()
{
var signingProps = XDocument.Parse(await ReadRepoFileAsync("eng/Signing.props"));

AssertScopedSigningRule(signingProps, "FileExtensionSignInfo", ".tgz", "LinuxSign500180PGP");
AssertScopedSigningRule(signingProps, "FileSignInfo", "aspire.js", "MicrosoftDotNet500");

// The native npm packages are built from already-signed native archives.
// The main Windows build should only produce the detached npm tarball
// signature; it must still provide scoped rules for nested native
// executables because Arcade resolves nested file certificates inside
// the ItemsToSign collision scope.
AssertScopedSigningRule(signingProps, "FileSignInfo", "aspire.exe", "None");
AssertScopedSigningRule(signingProps, "FileSignInfo", "aspire", "None");
}

[Fact]
public async Task ReleasePipelinePreflightsScheduledNpmPackagesBeforePublishing()
{
Expand Down Expand Up @@ -265,6 +283,21 @@ private static int CountOccurrences(string value, string substring)
return count;
}

private static void AssertScopedSigningRule(XDocument document, string elementName, string include, string certificateName)
{
var matchingRules = document
.Descendants(elementName)
.Where(element =>
(string?)element.Attribute("CollisionPriorityId") == "AspireCliNpmPackage" &&
((string?)element.Attribute("Include") == include || (string?)element.Attribute("Update") == include) &&
(string?)element.Attribute("CertificateName") == certificateName)
.ToArray();

Assert.True(
matchingRules.Length == 1,
$"Expected exactly one {elementName} for '{include}' using '{certificateName}' in the AspireCliNpmPackage signing scope, but found {matchingRules.Length}.");
}

private static string FindRepoRoot()
{
string? current = AppContext.BaseDirectory;
Expand Down
Loading