Auth hardening: RLS migration, server-side security middleware, and HTML sanitization

nwhitehouse ran a broad security pass across 36 files, shipping a 451-line RLS migration, server-side HTTP security middleware, download-token tightening, and frontend HTML sanitization for assistant message rendering. This is the kind of sweep you do once you've decided the fork is hosting real user data.

securitymulti-tenant

The core of this commit (325ff20c) is migration 002_enable_rls_tenant_tables.sql (451 LOC), which enables row-level security across all tenant-owned tables: projects, folders, documents, versions, edits, chats, workflows, workflow shares, tabular reviews, cells, and tabular chat messages. The backend still runs with a service-role key - RLS here is defense-in-depth for direct Supabase client access and any app-layer check that gets missed.

backend/src/lib/httpSecurity.ts (55 LOC, new) adds server-side middleware covering stricter CORS, helmet, JSON body limits, upload concurrency caps, and required DOWNLOAD_SIGNING_SECRET. The commit also tightens project and document ID authorization checks and removes raw-stream logging of model content.

On the frontend, safeMarkdown.ts and sanitizeHtml.ts are added to sanitize assistant message rendering. The storage client consolidation deletes frontend/src/lib/storage.ts (-136 LOC) and removes some dead Anthropic/Gemini-specific code paths from chatTools.ts and olava.ts. A 315-line security regression test suite (backend/tests/security.test.ts) accompanies the migration.

One dependency note: this commit adds canvas@^3.2.3 to backend/package.json but only updates bun.lock, not package-lock.json. Railway uses npm ci, which then fails with a lockfile mismatch. A separate follow-up commit regenerates the npm lockfile. Worth knowing if you're picking this apart.

The migration is Supabase-specific: RLS policies reference auth.uid() and auth.users. If your fork uses a different auth backend, treat the migration as a checklist of what to enforce rather than a drop-in.

So what Worth importing if your fork shares the same Supabase auth shape - the migration and test pair are reusable, and the markdown/HTML sanitizers transfer directly. If you're on a different auth backend, the RLS migration is a useful reference but not portable. Either way, do a side-by-side diff before applying: the authorization tightening touches enough routes that a partial port can introduce new gaps rather than close them.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

1 commit from nwhitehouse/mike, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
325ff20c Harden auth RLS and rendering paths Nick Whitehouse 2026-05-03 ↗ GitHub

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-110.md from inside the repo you want the changes in.

⬇ Download capture-thread-110.md