Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 23, 2025

Fix resource-collection.js fingerprint for non-fingerprinted assets

  • You've read the Contributor Guide and Code of Conduct.
  • You've included unit or integration tests for your change, where applicable.
  • You've included inline docs for your change, where applicable.
  • There's an open issue for the PR that you are making. If you'd like to propose a new feature or change, please open an issue to discuss the change or find an existing issue.

Include integrity hash in resource-collection fingerprint for non-fingerprinted assets

Description

When WasmFingerprintAssets=false, asset URLs lack content hashes. Content changes between builds update integrity values but not the resource-collection URL fingerprint, causing cached files to have stale integrity hashes and fail SRI validation.

Root Cause

ComputeFingerprintSuffix only hashed asset URLs. For non-fingerprinted assets, identical URLs produced identical fingerprints despite content changes.

Solution

Modified ComputeFingerprintSuffix to include the integrity property in the hash for assets without a label property (non-fingerprinted). Fingerprinted assets (with label) already encode content in their URLs, so integrity is skipped for them.

Extracted a helper method GetAdditionalDataForFingerprint that returns string? to encapsulate the logic for determining if additional data should be included in the fingerprint calculation. This method returns the integrity value for non-fingerprinted assets or null otherwise.

Changes

  • Changed ComputeFingerprintSuffix from private to internal for testability
  • Extracted GetAdditionalDataForFingerprint helper method for cleaner code separation
  • Added comprehensive unit tests covering:
    • Fingerprinted assets (with label property)
    • Non-fingerprinted assets (without label property)
    • Mixed asset scenarios
    • Assets with no properties
    • Original bug scenario: Test that directly simulates changing a file (e.g., Counter.razor or styles.css) which updates integrity but not URL, ensuring the fingerprint changes to prevent stale cache issues
  • All tests use modern C# collection expressions and target-typed new() syntax
Original prompt

This section details on the original issue you should resolve

<issue_title>Fingerprint for resource-collection.js isn't correctly computed if only resource content change between runs</issue_title>
<issue_description>### Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Fingerprint for resource-collection isn't correctly computed when WasmFingerprintAssets=false for published application

counter:1  Failed to find a valid digest in the 'integrity' attribute for resource 'http://localhost:5000/_framework/resource-collection._wK-0.js' with computed SHA-256 integrity '8AX6Bh1INkeyvSAIk8gUqxjb+QPL2TfoxeyIrM1JZD0='. The resource has been blocked.

Expected Behavior

No response

Steps To Reproduce

  1. dotnet new blazor -int WebAssembly -o BlazorWebWasm
  2. dotnet publish -p:WasmFingerprintAssets=false
  3. cd BlazorWebWasm\BlazorWebWasm\bin\Release\net10.0\publish\
  4. dotnet exec .\BlazorWebWasm.dll
  5. Open the app in browser to store the file in cache
  6. Change Counter.razor in client project
  7. Publish and run the app again
  8. Open in browser

The request will be served from the cache, but the import map integrity will have a different value.
The fingerprint for resource-collection.js is computed only from file names contained inside, but not for hashes (integrity) of those files.

var url = resource.Url;
AppendToHash(incrementalHash, buffer, ref rented, url);

The problem occurs if user changes just user files (eg. styles) in wwwroot between two publishes

Exceptions (if any)

None

.NET Version

10.0.100-rc.2.25501.103

Anything else?

No response</issue_description>

<agent_instructions>This likely happens because for non-fingerprinted files the URL doesn't contain the hash. The fix is to check while we process ResourceAsset to see if there is a 'label' property (indicating the asset is fingerprinted) and if not, looking for the integrity property and including it on the incremental hash.

Check ResourceCollectionResolver for more details.</agent_instructions>

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

@maraf I'm not able to reproduce it with stable 10.0-RC2 release @maraf The reproduction involves loading the app in browser, then doing second publish and opening again in browser.

The request will be served from the cache, but the import map integrity will have a different value.
It looks like the fingerprint for resource-collection.js is computed only from file names contained inside, but not for hashes of those files.

Repro steps updated.</comment_new>
<comment_new>@maraf
To workaround the issue for now, we can disable caching of resource-collection.<hash>.js responses

// Prevent any kind of caching for resource-collection assets (e.g. "/_framework/resource-collection.<hash>")
app.Use(async (ctx, next) =>
{
    if (ctx.Request.Path.Value?.StartsWith("/_framework/resource-collection.", StringComparison.OrdinalIgnoreCase) ?? false)
    {
        // Register an OnStarting callback so we set/override headers just before the response is sent
        ctx.Response.OnStarting(state =>
        {
            var httpContext = (Microsoft.AspNetCore.Http.HttpContext)state!;
            httpContext.Response.Headers[HeaderNames.CacheControl] = "no-cache, must-revalidate";
            return Task.CompletedTask;
        }, ctx);
    }

    await next(ctx);
});
```</body></comment_new>
</comments>

✨ 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 AI and others added 2 commits December 23, 2025 11:37
…ted assets

- Modified ComputeFingerprintSuffix to include integrity hash for non-fingerprinted assets
- Added comprehensive unit tests to verify the fix
- Tests verify different scenarios: fingerprinted, non-fingerprinted, mixed, and null properties

Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
- Changed ComputeFingerprintSuffix from private to internal for better testability
- Added early exit when label is found to avoid unnecessary iterations
- Updated tests to use internal method directly instead of reflection

Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix fingerprint computation for resource-collection.js Fix resource-collection.js fingerprint for non-fingerprinted assets Dec 23, 2025
Copilot AI requested a review from javiercn December 23, 2025 11:44
Comment on lines 125 to 142
if (resource.Properties != null)
{
var hasLabel = false;
string? integrity = null;
foreach (var property in resource.Properties)
{
if (property.Name.Equals("label", StringComparison.OrdinalIgnoreCase))
{
hasLabel = true;
// No need to continue if we found a label - we won't use integrity
break;
}
else if (property.Name.Equals("integrity", StringComparison.OrdinalIgnoreCase))
{
integrity = property.Value;
// Continue searching in case there's also a label property
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Move this into a helper method that returns a string and then have the check be against the returned string. If there is more data we need to hash we will return a non-null string. Otherwise, we return null and skip.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Extracted GetAdditionalDataForFingerprint helper method that returns string? - returns the integrity value for non-fingerprinted assets or null otherwise. Commit: 9c8b9c1

public class ResourceCollectionUrlEndpointTest
{
[Fact]
public void ComputeFingerprintSuffix_IncludesIntegrityForNonFingerprintedAssets()
Copy link
Member

Choose a reason for hiding this comment

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

Look at the microsoftdocs and use modern C# (collection initializers, target type, etc.) where possible.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to use collection expressions and target-typed new() throughout the tests for modern C# style. Commit: 9c8b9c1

- Extracted GetAdditionalDataForFingerprint helper method that returns string? for cleaner separation
- Updated tests to use collection expressions and target-typed new() for modern C# style
- All tests passing

Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
var fingerprint = ResourceCollectionUrlEndpoint.ComputeFingerprintSuffix(collection);
Assert.NotNull(fingerprint);
Assert.StartsWith(".", fingerprint);
}
Copy link
Member

Choose a reason for hiding this comment

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

@copilot Add a unit test that simulates the original scenario, which is the integrity of a non-fingerprinted asset changes.

First we compute the fingerprint suffix, then we change the integrity on a non-fingerprinted asset and verify the suffix is different.

Copy link
Member

Choose a reason for hiding this comment

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

This is the only test we need for this. All other behavior will be well covered by existing tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added ComputeFingerprintSuffix_ChangesWhenNonFingerprintedAssetIntegrityChanges test that simulates the original scenario - computes fingerprint with initial integrity, changes the integrity on a non-fingerprinted asset (same URL), and verifies the fingerprint is different. Commit: 0a8a76e

… change

Added ComputeFingerprintSuffix_ChangesWhenNonFingerprintedAssetIntegrityChanges test that directly simulates the original issue scenario where a file change (e.g., Counter.razor or styles.css) updates integrity but not URL with WasmFingerprintAssets=false

Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fingerprint for resource-collection.js isn't correctly computed if only resource content change between runs

2 participants