Package: dymaptic.GeoBlazor.Core (the file below is in Core; we also reference
dymaptic.GeoBlazor.Pro, both pinned at the same version)
Version: 4.6.1 (Core and Pro)
Hosting model: Blazor WebAssembly, .NET 10
Environment: Chromium on Linux
What happens
With a Content-Security-Policy in enforcing mode that grants script-src 'self' 'wasm-unsafe-eval' and a per-request nonce, but NOT 'unsafe-eval', the map fails to
authenticate. The browser raises an EvalError originating in AuthenticationManager
during MapView's after-render, and the application falls back to a token-free basemap.
The relevant CSP violation is a blocked new Function(...) call.
Why 'wasm-unsafe-eval' does not help
We already grant 'wasm-unsafe-eval', because the ArcGIS JS SDK needs it for its
WebAssembly modules (pe-wasm.wasm, libtess.wasm). That directive permits WebAssembly
compilation only. It does not permit eval() or the Function constructor, which is
what new Function(...) needs. They are separate permissions and only the second one
re-opens the string-to-code path that CSP exists to close.
Why we will not just grant 'unsafe-eval'
Adding 'unsafe-eval' would leave the policy enforcing in name only. Our application
renders user-authored HTML on the same origin (sanitized, but the sanitizer is
load-bearing), so 'unsafe-eval' is precisely the directive we cannot afford. We would
rather stay in report-only mode until this is resolved than ship a policy that no longer
constrains the thing it exists to constrain.
The exact call (read from your repository at tag v4.6.2, commit 04cd44a)
src/dymaptic.GeoBlazor.Core/Scripts/authenticationManager.ts:
let interceptorName = 'authenticationManagerInterceptor';
let currentInterceptor = esriConfig.log.interceptors.find(i => i.name === interceptorName);
if (!currentInterceptor) {
let newInterceptor = new Function('logUncaughtError',
`return function ${interceptorName}(level, module, ...args) {
return logUncaughtError(level, module, 'global', ...args);
}`);
esriConfig.log.interceptors.push((newInterceptor(logUncaughtError) as __esri.LogInterceptor));
}
As we read it, the Function constructor is used only to give the interceptor a specific
.name, so the find(i => i.name === interceptorName) guard above it can deduplicate on a
later call. It is not compiling anything supplied by Esri or by the host application.
If that reading is right, a named closure would be equivalent and needs no unsafe-eval:
if (!currentInterceptor) {
const newInterceptor = (level, module, ...args) =>
logUncaughtError(level, module, 'global', ...args);
Object.defineProperty(newInterceptor, 'name', { value: interceptorName });
esriConfig.log.interceptors.push(newInterceptor as __esri.LogInterceptor);
}
Two further observations, offered so you do not have to re-derive them:
- It is unconditional.
MapView.razor.cs calls AuthenticationManager.Initialize() from
OnAfterRenderAsync on first render, with no flag to skip it, so every MapView reaches this
code path.
- It is long-standing rather than recent. The file's last substantive commit is 2025-11-15,
and the v4.6.1 to v4.6.2 diff touches only LICENSE, so 4.6.2 does not change this.
Why this looks fixable on your side rather than inherited from Esri
Esri's published FAQ for the ArcGIS Maps SDK for JavaScript answers
"Does the ArcGIS Maps SDK for JavaScript support all Content Security Policy (CSP)
directives?" with, verbatim:
"Most CSP directives are supported and certified within the JavaScript Maps SDK. The
JavaScript Maps SDK makes use of WebAssembly (Wasm), which requires the inclusion of
wasm-unsafe-eval in the script-src directive. If your CSP does not allow all styles by
setting either the default-src or style-src directives, then the unsafe-inline keyword
needs to be added to the style-src-attr directive. A worker-src directive should be set
to allow blob:."
That lists three requirements and 'unsafe-eval' is not among them. Our policy already
grants all three. So the requirement appears to originate in GeoBlazor's wrapper layer
rather than in the underlying SDK.
(We note some community reports of unsafe-eval still being needed for init.js in
certain 4.x setups, so this is not perfectly settled upstream either. But our failure is
specifically attributed to AuthenticationManager, not to init.js.)
What we are asking
- Can
AuthenticationManager's new Function(...) be replaced with the named-closure
form above, or something equivalent? We are happy to open a PR if that is easier for you.
- If it cannot be removed, could it be made opt-out, so applications that do not use
the affected authentication path can enforce CSP without it?
- Is there a supported configuration today that avoids this call which we have missed?
What we can provide
A minimal reproduction on request, plus the exact CSP header and the console trace.
Package:
dymaptic.GeoBlazor.Core(the file below is in Core; we also referencedymaptic.GeoBlazor.Pro, both pinned at the same version)Version: 4.6.1 (Core and Pro)
Hosting model: Blazor WebAssembly, .NET 10
Environment: Chromium on Linux
What happens
With a Content-Security-Policy in enforcing mode that grants
script-src 'self' 'wasm-unsafe-eval'and a per-request nonce, but NOT'unsafe-eval', the map fails toauthenticate. The browser raises an
EvalErrororiginating inAuthenticationManagerduring
MapView's after-render, and the application falls back to a token-free basemap.The relevant CSP violation is a blocked
new Function(...)call.Why
'wasm-unsafe-eval'does not helpWe already grant
'wasm-unsafe-eval', because the ArcGIS JS SDK needs it for itsWebAssembly modules (
pe-wasm.wasm,libtess.wasm). That directive permits WebAssemblycompilation only. It does not permit
eval()or theFunctionconstructor, which iswhat
new Function(...)needs. They are separate permissions and only the second onere-opens the string-to-code path that CSP exists to close.
Why we will not just grant
'unsafe-eval'Adding
'unsafe-eval'would leave the policy enforcing in name only. Our applicationrenders user-authored HTML on the same origin (sanitized, but the sanitizer is
load-bearing), so
'unsafe-eval'is precisely the directive we cannot afford. We wouldrather stay in report-only mode until this is resolved than ship a policy that no longer
constrains the thing it exists to constrain.
The exact call (read from your repository at tag v4.6.2, commit 04cd44a)
src/dymaptic.GeoBlazor.Core/Scripts/authenticationManager.ts:As we read it, the
Functionconstructor is used only to give the interceptor a specific.name, so thefind(i => i.name === interceptorName)guard above it can deduplicate on alater call. It is not compiling anything supplied by Esri or by the host application.
If that reading is right, a named closure would be equivalent and needs no
unsafe-eval:Two further observations, offered so you do not have to re-derive them:
MapView.razor.cscallsAuthenticationManager.Initialize()fromOnAfterRenderAsyncon first render, with no flag to skip it, so everyMapViewreaches thiscode path.
and the v4.6.1 to v4.6.2 diff touches only
LICENSE, so 4.6.2 does not change this.Why this looks fixable on your side rather than inherited from Esri
Esri's published FAQ for the ArcGIS Maps SDK for JavaScript answers
"Does the ArcGIS Maps SDK for JavaScript support all Content Security Policy (CSP)
directives?" with, verbatim:
That lists three requirements and
'unsafe-eval'is not among them. Our policy alreadygrants all three. So the requirement appears to originate in GeoBlazor's wrapper layer
rather than in the underlying SDK.
(We note some community reports of
unsafe-evalstill being needed forinit.jsincertain 4.x setups, so this is not perfectly settled upstream either. But our failure is
specifically attributed to
AuthenticationManager, not toinit.js.)What we are asking
AuthenticationManager'snew Function(...)be replaced with the named-closureform above, or something equivalent? We are happy to open a PR if that is easier for you.
the affected authentication path can enforce CSP without it?
What we can provide
A minimal reproduction on request, plus the exact CSP header and the console trace.