clockworklabs / clockworklabs/SpacetimeDB

TypeScript SDK 2.2.0 still requires 'unsafe-eval' — Function() codegen breaks strict CSP (recurrence of #4669)

Open
#4,966 2 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
25.2k
Forks
1.1k
Avg merge
2d 7h
Merged PRs (30d)
46

Description

Re-filing because #4669 was closed without a fix or comment, and the issue is still present in spacetimedb@2.2.0. This is a real production blocker for apps that ship a strict Content-Security-Policy.

Environment

  • SpacetimeDB SDK: 2.2.0 (npm spacetimedb)
  • Framework: Next.js 16.2.4 (App Router, Turbopack)
  • Host: Firebase App Hosting
  • Browsers: Chrome, Safari (mobile + desktop), Firefox

Repro

  1. Ship a Next.js app with this CSP header (no 'unsafe-eval'):
    script-src 'self' 'unsafe-inline'
    
  2. Build for production and connect via DbConnection.builder()...build().
  3. The first row deserialization throws a CSP violation; React's error boundary cannot recover; Next.js 16 falls back to its built-in DefaultGlobalError UI ("This page couldn't load. Reload to try again, or go back."). The reload button is a <form> POST to /, so the page reloads in a tight loop until the user navigates away.

Where the eval happens (SDK 2.2.0, dist/index.browser.mjs)

Five codegen sites all rely on the Function() constructor:

// line 1189 — product type serializer
serializer = Function("writer", "value", body2);
// line 1197 — product type serializer (bound)
serializer = Function("writer", "value", body).bind(...)
// line 1236 — deserializer
deserializer = Function("reader", body);
// line 1241 — deserializer
deserializer = Function(...)
// line 1327 — product type serializer (bound)
serializer = Function("writer", "value", body).bind(...)

These fire on every subscription update and every reducer arg encode/decode, so 'unsafe-eval' is required throughout the lifetime of the connection — not just at startup.

Why the previous "workaround" isn't a workaround

Adding 'unsafe-eval' undoes the most important hardening the CSP gives you. For projects subject to OWASP ASVS, SOC 2, or enterprise procurement reviews, that's not a trade we can make.

Suggested fix (same as #4669)

spacetime generate already produces fully-typed client bindings. Emit the serializer and deserializer bodies at codegen time into the generated module_bindings/ files, the same way Protobuf, FlatBuffers, and Cap'n Proto do. The SDK then imports concrete functions instead of building them at runtime.

Happy to test a beta SDK against a Next.js + strict-CSP app if it'd help.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing the five Function() sites in dist/index.browser.mjs and the spacetime generate path that emits module_bindings/. Compare the generated bindings with the runtime serializer and deserializer code, then verify that a production Next.js app using the stated strict CSP can connect and process rows without requiring unsafe-eval.

Written by the indexing model from the issue text.

Assessment

Tech stack
firebase, next.js, typescript
Domain
build-system, security, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.