Description
The TypeScript SDK uses the Function() constructor to dynamically build serializer and deserializer functions at runtime in algebraic_type.ts. This requires 'unsafe-eval' in the browser's Content-Security-Policy script-src directive, preventing applications from using strict CSP.
Location in Source
packages/sdk/src/algebraic_type.ts — three call sites in the compiled browser bundle (dist/index.browser.mjs):
- Line ~1182:
serializer = Function("writer", "value", body2) (ProductType)
- Line ~1190:
serializer = Function("writer", "value", body).bind(...) (SumType)
- Line ~1228:
deserializer = Function("reader", body) (makeDeserializer)
These construct functions from string bodies at runtime, which CSP treats identically to eval().
Environment
- SpacetimeDB SDK Version: latest (npm
spacetimedb)
- Runtime: Browser (Chrome, Firefox, Safari)
- Framework: Next.js 16 on Cloudflare Pages
Steps to Reproduce
- Set CSP:
script-src 'self' 'nonce-{random}' (no unsafe-eval)
- Connect via
DbConnection.builder().build()
- Browser blocks with:
EvalError: Evaluating a string as JavaScript violates the following
Content Security Policy directive.
at Object.makeDeserializer
Impact
Applications cannot achieve strict CSP without 'unsafe-eval'. This blocks:
- OWASP ASVS Level 2
- Enterprise procurement requirements
Suggested Fix
The Function() constructor builds serializers/deserializers from string bodies like:
Function("reader", "return [reader.readU32(), reader.readString()]")
This could be replaced with build-time codegen during spacetime generate. The generated module_bindings/ already has full type information — serializer/deserializer functions could be emitted as static code alongside the type definitions, eliminating the runtime Function() calls entirely.
Alternatively, the string bodies could be converted to closure-based builders that compose reader/writer calls without string concatenation:
// Instead of: Function("reader", "return [reader.readU32(), reader.readString()]")
// Use: (reader) => [reader.readU32(), reader.readString()]
Workaround
Add 'unsafe-eval' to CSP and document as accepted risk:
script-src 'self' 'nonce-{random}' 'unsafe-eval'
Description
The TypeScript SDK uses the
Function()constructor to dynamically build serializer and deserializer functions at runtime inalgebraic_type.ts. This requires'unsafe-eval'in the browser's Content-Security-Policyscript-srcdirective, preventing applications from using strict CSP.Location in Source
packages/sdk/src/algebraic_type.ts— three call sites in the compiled browser bundle (dist/index.browser.mjs):serializer = Function("writer", "value", body2)(ProductType)serializer = Function("writer", "value", body).bind(...)(SumType)deserializer = Function("reader", body)(makeDeserializer)These construct functions from string bodies at runtime, which CSP treats identically to
eval().Environment
spacetimedb)Steps to Reproduce
script-src 'self' 'nonce-{random}'(nounsafe-eval)DbConnection.builder().build()Impact
Applications cannot achieve strict CSP without
'unsafe-eval'. This blocks:Suggested Fix
The
Function()constructor builds serializers/deserializers from string bodies like:This could be replaced with build-time codegen during
spacetime generate. The generatedmodule_bindings/already has full type information — serializer/deserializer functions could be emitted as static code alongside the type definitions, eliminating the runtimeFunction()calls entirely.Alternatively, the string bodies could be converted to closure-based builders that compose reader/writer calls without string concatenation:
Workaround
Add
'unsafe-eval'to CSP and document as accepted risk: