Skip to content

TypeScript SDK: makeDeserializer requires unsafe-eval CSP directive, blocking strict Content-Security-Policy #206

@rainmanjam

Description

@rainmanjam

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

  1. Set CSP: script-src 'self' 'nonce-{random}' (no unsafe-eval)
  2. Connect via DbConnection.builder().build()
  3. 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'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions