From 908155d1e6bbb6c1b30c9fa8a7b16e7247dbcc02 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Fri, 6 Feb 2026 20:03:31 -0600 Subject: [PATCH 1/6] Fix WASM boot config ContentRoot to use IntermediateOutputPath Change the boot config's DefineStaticWebAssets ContentRoot from wwwroot to so the asset Identity points to the actual file location on disk rather than a stale copy in the output wwwroot folder. This fixes SRI integrity failures during incremental Blazor WASM builds where the compressed boot config used a stale fingerprint from the wwwroot copy instead of the fresh file in obj/. Fixes aspnetcore#65271 --- .../build/Microsoft.NET.Sdk.WebAssembly.Browser.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index 707ce66956448e..c477d21015540d 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -518,7 +518,7 @@ Copyright (c) .NET Foundation. All rights reserved. AssetTraitValue="manifest" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Never" - ContentRoot="$(OutDir)wwwroot" + ContentRoot="$(IntermediateOutputPath)" BasePath="$(StaticWebAssetBasePath)" > From 62fa4f2359f4a3cf4b79ce0257ac643a0cb3268e Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Fri, 6 Feb 2026 23:28:43 -0600 Subject: [PATCH 2/6] Use boot config output items for preload matching Replace the fragile FileName-based scanning of all StaticWebAsset items with direct references to _WasmBuildBootConfigStaticWebAsset and _WasmPublishBootConfigStaticWebAsset. These items are already produced by the DefineStaticWebAssets task in _GenerateBuildWasmBootJson and _AddPublishWasmBootJsonToStaticWebAssets respectively. The previous approach relied on FileName containing the fingerprint (e.g. dotnet.FINGERPRINT.js), which is an implementation detail of how DefineStaticWebAssets computes Identity. Using the pipeline's own output items is correct regardless of ContentRoot or Identity format. --- .../Microsoft.NET.Sdk.WebAssembly.Browser.targets | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index c477d21015540d..eb37d56533f747 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -584,18 +584,10 @@ Copyright (c) .NET Foundation. All rights reserved. - - <_WasmBootConfigFileNameWithoutExtension>$([System.IO.Path]::GetFileNameWithoutExtension('$(_WasmBootConfigFileName)')) - <_WasmBootConfigFileExtension>$([System.IO.Path]::GetExtension('$(_WasmBootConfigFileName)')) - - - <_WasmPreloadBuildScriptAsset Include="@(StaticWebAsset)" Condition="'$(_WasmFingerprintAssets)' == 'true' and '$(_WasmFingerprintBootConfig)' == 'true' and '%(AssetKind)' != 'Publish' and '%(FileName)%(Extension)' == '$(_WasmBootConfigFileNameWithoutExtension).%(Fingerprint)$(_WasmBootConfigFileExtension)'" /> - <_WasmPreloadBuildScriptAsset Include="@(StaticWebAsset)" Condition="('$(_WasmFingerprintAssets)' != 'true' or '$(_WasmFingerprintBootConfig)' != 'true') and '%(AssetKind)' != 'Publish' and '%(FileName)%(Extension)' == '$(_WasmBootConfigFileName)'" /> - @@ -614,13 +606,10 @@ Copyright (c) .NET Foundation. All rights reserved. - - <_WasmPreloadPublishScriptAsset Include="@(StaticWebAsset)" Condition="'%(AssetKind)' != 'Build' and '%(FileName)%(Extension)' == '$(_WasmPublishBootConfigFileName)'" /> - From 2e74845dad6e14737c1e313eb289809342d2f5a7 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 9 Feb 2026 09:07:21 -0600 Subject: [PATCH 3/6] Use IntermediateOutputPath as ContentRoot for WebCil assets WebCil candidates come from multiple source directories (obj/webcil/ for converted DLLs, runtime pack for native files, etc.) but were all defined with ContentRoot pointing to OutputPath/wwwroot. Use IntermediateOutputPath as ContentRoot instead. Files outside obj/ (e.g., runtime pack native files) get synthesized Identities with CopyCandidates to materialize the fingerprinted copies. Files already under obj/ (webcil-converted DLLs) use their physical path directly. This avoids the stale wwwroot Identity problem that caused SRI integrity failures during incremental builds, while maintaining correct publish behavior through CopyCandidate materialization. --- .../build/Microsoft.NET.Sdk.WebAssembly.Browser.targets | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index eb37d56533f747..463a77a59c858e 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -349,7 +349,6 @@ Copyright (c) .NET Foundation. All rights reserved. <_WasmBuildWebCilPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'webcil')) <_WasmBuildTmpWebCilPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'tmp-webcil')) - <_WasmBuildOuputPath>$([MSBuild]::NormalizeDirectory('$(OutputPath)', 'wwwroot')) @@ -372,7 +371,7 @@ Copyright (c) .NET Foundation. All rights reserved. AssetRole="Primary" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Never" - ContentRoot="$(_WasmBuildOuputPath)" + ContentRoot="$(IntermediateOutputPath)" FingerprintCandidates="$(_WasmFingerprintAssets)" FingerprintPatterns="@(FingerprintPatterns);@(_WasmFingerprintPatterns)" BasePath="$(StaticWebAssetBasePath)" From 2721a86c7168e7e21b9827545c538d6a346bd623 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 12 Feb 2026 07:58:38 -0600 Subject: [PATCH 4/6] Use per-item ContentRoot for both build and publish WebCil assets Replace task-level ContentRoot= with per-item ContentRoot=%(RootDir)%(Directory) on build-time WebCil candidates, and add per-item ContentRoot on publish-time candidates. Per-item ContentRoot ensures DefineStaticWebAssets resolves each asset's Identity to its actual file path on disk, preventing synthesized paths for runtime pack files that live outside IntermediateOutputPath. At publish time, promoted assets carry AssetKind=Build which means contentRoot is not nulled (IsPublish('Build')=false), so the per-item ContentRoot is used by ComputeCandidateIdentity to produce Identity= real FullPath instead of a fingerprinted filename that doesn't exist. --- .../build/Microsoft.NET.Sdk.WebAssembly.Browser.targets | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index 463a77a59c858e..3e4e50824c2670 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -357,6 +357,8 @@ Copyright (c) .NET Foundation. All rights reserved. + + <_WebCilAssetsCandidates Update="@(_WebCilAssetsCandidates)" ContentRoot="%(RootDir)%(Directory)" /> <_WasmFingerprintPatterns Include="WasmFiles" Pattern="*.wasm" Expression="#[.{fingerprint}]!" /> <_WasmFingerprintPatterns Include="DllFiles" Pattern="*.dll" Expression="#[.{fingerprint}]!" /> <_WasmFingerprintPatterns Include="DatFiles" Pattern="*.dat" Expression="#[.{fingerprint}]!" /> @@ -371,7 +373,6 @@ Copyright (c) .NET Foundation. All rights reserved. AssetRole="Primary" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Never" - ContentRoot="$(IntermediateOutputPath)" FingerprintCandidates="$(_WasmFingerprintAssets)" FingerprintPatterns="@(FingerprintPatterns);@(_WasmFingerprintPatterns)" BasePath="$(StaticWebAssetBasePath)" @@ -717,6 +718,9 @@ Copyright (c) .NET Foundation. All rights reserved. <_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata Include="@(_NewWebCilPublishStaticWebAssetsCandidates)" RemoveMetadata="Integrity;Fingerprint" /> + + <_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata Update="@(_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata)" ContentRoot="%(RootDir)%(Directory)" /> + <_PromotedWasmPublishStaticWebAssets Update="@(_PromotedWasmPublishStaticWebAssets)" ContentRoot="%(RootDir)%(Directory)" /> From 17ab6d2507ba9e83f968c4870ff87aa60d9565c8 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Fri, 13 Feb 2026 21:49:41 -0600 Subject: [PATCH 5/6] Fix CreatePromotedAsset to preserve real file paths Stop baking fingerprints into ItemSpec in CreatePromotedAsset. The old code constructed synthetic paths like dotnet.native.HASH.wasm that don't exist on disk, causing MSB3030 errors at publish time. Now ItemSpec always points to the real file, and DefineStaticWebAssets computes Identity from ContentRoot + RelativePath (which already contains fingerprint placeholders). This follows the invariant that ContentRoot + RelativePath must reference a real file. Also clear stale Fingerprint/Integrity metadata on promoted assets in the targets, so DefineStaticWebAssets recomputes them from the actual file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...rosoft.NET.Sdk.WebAssembly.Browser.targets | 5 +++-- .../ComputeWasmPublishAssets.cs | 22 +++++-------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index 3e4e50824c2670..f8d4d4c046fa95 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -718,9 +718,10 @@ Copyright (c) .NET Foundation. All rights reserved. <_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata Include="@(_NewWebCilPublishStaticWebAssetsCandidates)" RemoveMetadata="Integrity;Fingerprint" /> - + <_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata Update="@(_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata)" ContentRoot="%(RootDir)%(Directory)" /> - <_PromotedWasmPublishStaticWebAssets Update="@(_PromotedWasmPublishStaticWebAssets)" ContentRoot="%(RootDir)%(Directory)" /> + <_PromotedWasmPublishStaticWebAssets Update="@(_PromotedWasmPublishStaticWebAssets)" ContentRoot="%(RootDir)%(Directory)" Fingerprint="" Integrity="" /> diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs index 69205e6f3b982f..678624040dc401 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs @@ -332,23 +332,11 @@ static bool IsDotNetWasm(string key) private TaskItem CreatePromotedAsset(ITaskItem asset) { - string newAssetItemSpec = asset.ItemSpec; - string newAssetRelativePath = asset.GetMetadata("RelativePath"); - - if (FingerprintAssets) - { - string assetDirectory = Path.GetDirectoryName(asset.ItemSpec); - string assetFileNameToFingerprint = Path.GetFileName(newAssetRelativePath); - string fingerprint = asset.GetMetadata("Fingerprint"); - string newAssetFingerprintedFileName = assetFileNameToFingerprint.Replace("#[.{fingerprint}]!", $".{fingerprint}"); - if (newAssetFingerprintedFileName != assetFileNameToFingerprint) - { - newAssetItemSpec = $"{assetDirectory}/{newAssetFingerprintedFileName}"; - } - } - - var newAsset = new TaskItem(newAssetItemSpec, asset.CloneCustomMetadata()); - newAsset.SetMetadata("RelativePath", newAssetRelativePath); + // Keep ItemSpec pointing to the actual file on disk. + // DefineStaticWebAssets will resolve fingerprint placeholders in RelativePath + // and compute Fingerprint/Integrity from the real file. + var newAsset = new TaskItem(asset.ItemSpec, asset.CloneCustomMetadata()); + newAsset.SetMetadata("RelativePath", asset.GetMetadata("RelativePath")); ApplyPublishProperties(newAsset); return newAsset; From 8aa672c00d727db00ed7867b25eff915f5692a8d Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Sat, 14 Feb 2026 01:27:36 -0600 Subject: [PATCH 6/6] Fix satellite assembly publish by promoting Culture assets to Primary Satellite (Culture) promoted assets have RelatedAsset metadata pointing to build-time paths that don't match any publish-time candidate. DefineStaticWebAssets silently drops Related assets without a matching parent. Fix by promoting Culture assets to Primary and clearing their stale RelatedAsset so DefineStaticWebAssets processes them as standalone assets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../build/Microsoft.NET.Sdk.WebAssembly.Browser.targets | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index f8d4d4c046fa95..01d02adbc23fc8 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -722,6 +722,14 @@ Copyright (c) .NET Foundation. All rights reserved. Clear stale Fingerprint/Integrity so DefineStaticWebAssets recomputes from the actual file. --> <_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata Update="@(_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata)" ContentRoot="%(RootDir)%(Directory)" /> <_PromotedWasmPublishStaticWebAssets Update="@(_PromotedWasmPublishStaticWebAssets)" ContentRoot="%(RootDir)%(Directory)" Fingerprint="" Integrity="" /> + + <_PromotedWasmPublishStaticWebAssets + Update="@(_PromotedWasmPublishStaticWebAssets)" + Condition="'%(_PromotedWasmPublishStaticWebAssets.AssetTraitName)' == 'Culture'" + AssetRole="Primary" + RelatedAsset="" />