Standalone Blazor WebAssembly's import map cannot satisfy a strict Content-Security-Policy without 'unsafe-inline'
- Dominant language
- C#
- Stars
- 3.2k
- Forks
- 1.3k
- PR merge metrics
- PR metrics pending
Description
## Describe the bug
With `OverrideHtmlAssetPlaceholders` set to `true` in a standalone Blazor WebAssembly project, the SDK writes a `` tag into `wwwroot/index.html`. This tag is an inline script. A Content-Security-Policy that restricts `script-src` and does not allow `'unsafe-inline'` blocks this tag, so the app never starts.
Blazor Web App has an `<ImportMap />` component. That component accepts a `nonce` attribute, or an app can read the import map from `HttpContext.GetEndpoint()` and compute its hash on each request. See dotnet/aspnetcore#57277 and dotnet/aspnetcore#59486. Both approaches work because a Blazor Web App renders `index.html` on every request.
A standalone Blazor WebAssembly app has no such component and no such request pipeline. `wwwroot/index.html` is a static file, written once at build time. A nonce needs a new random value on every request, so it does not fit a static file. A `script-src 'sha256-...'` hash could work in theory, but the SDK does not compute or write that hash anywhere. The import map's content also changes whenever any fingerprinted asset changes, so a developer cannot type the hash in by hand and keep it correct.
I could not find any supported way to lock down `script-src` for the import map in a standalone Blazor WebAssembly app. The only working option is `'unsafe-inline'`, which removes most of the protection a strict policy is meant to give.
## Steps to reproduce
1. `dotnet new blazorwasm -o Repro`
2. Add this to `Repro.csproj`.
```xml
<PropertyGroup>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>
```
3. Add a collocated JS module for a component, for example `Pages/Home.razor.js`, and import it from `Home.razor` with `IJSObjectReference`.
4. Add a strict CSP to `wwwroot/index.html`.
```html
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; object-src 'none';" />
```
5. Run `dotnet build`, then serve `wwwroot` (or `dotnet watch run`) and open the app in a browser.
## Expected behavior
The app starts, and the browser accepts the import map under a `script-src` policy that has no `'unsafe-inline'`.
## Actual behavior
The browser blocks the inline `<script type="importmap">` tag and reports a Content-Security-Policy violation. The app never starts.
## Is this a regression?
No. As far as I know, there was never a supported way to do this for a standalone Blazor WebAssembly app.
## Are there any workarounds?
Add `'unsafe-inline'` to `script-src`. That works, but it defeats much of the purpose of a strict policy.
## dotnet --info output
```
.NET SDK:
Version: 10.0.401
Commit: e34a38d2ae
Workload version: 10.0.400-manifests.a3b2712e
MSBuild version: 18.9.11+e34a38d2a
Runtime Environment:
OS Name: Windows
OS Version: 10.0.26200
OS Platform: Windows
RID: win-x64
```
## IDE version
Not applicable. I reproduced this from the command line.
## Other details
This is a different problem from dotnet/sdk#56148, which is about the import map's integrity digest going stale during the dev inner loop after a JS edit. This issue is about the import map having no supported way to satisfy a strict `script-src` policy at all, in the dev server output and in the published app alike.
This is also different from dotnet/aspnetcore#57277 and dotnet/aspnetcore#59486. Those issues, and their fix, apply to the `<ImportMap />` component in a Blazor Web App, which renders on every request. Standalone Blazor WebAssembly renders `index.html` once, at build time, so neither a nonce nor a per-request hash applies to it.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.