-
Notifications
You must be signed in to change notification settings - Fork 2k
Claude/build website builder 7p1p y #3073
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 11 commits
050e3e4
1e24b04
4e016fe
0f6ddeb
74cab71
5b8c595
64911e2
60ddd81
eac7ba2
79386b2
17476be
12a8978
75fbb0c
e4ddef6
cfb0f58
137d534
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| -- PHASE 3: Add Audit Fields to Build Sessions | ||
| -- Created: 2025-12-19 | ||
| -- Purpose: Wire build sessions to real Cynthia audits | ||
|
|
||
| -- ============================================================ | ||
| -- ENUMS | ||
| -- ============================================================ | ||
|
|
||
| -- Build session audit status enum | ||
| CREATE TYPE build_session_audit_status AS ENUM ( | ||
| 'pending', | ||
| 'running', | ||
| 'completed', | ||
| 'failed' | ||
| ); | ||
|
|
||
| -- ============================================================ | ||
| -- ALTER TABLES | ||
| -- ============================================================ | ||
|
|
||
| -- Add audit fields to build_sessions | ||
| ALTER TABLE build_sessions | ||
| ADD COLUMN audit_id UUID REFERENCES cynthia_audits(id) ON DELETE SET NULL ON UPDATE CASCADE, | ||
| ADD COLUMN audit_status build_session_audit_status DEFAULT 'pending'; | ||
|
|
||
| -- ============================================================ | ||
| -- INDEXES | ||
| -- ============================================================ | ||
|
|
||
| -- Index on build_sessions.audit_id for audit lookups | ||
| CREATE INDEX idx_build_sessions_audit_id ON build_sessions(audit_id); | ||
|
|
||
| -- Index on build_sessions.audit_status for filtering by status | ||
| CREATE INDEX idx_build_sessions_audit_status ON build_sessions(audit_status); | ||
|
|
||
| -- ============================================================ | ||
| -- COMMENTS | ||
| -- ============================================================ | ||
|
|
||
| COMMENT ON COLUMN build_sessions.audit_id IS 'Phase 3: Links to real Cynthia audit (nullable until audit completes)'; | ||
| COMMENT ON COLUMN build_sessions.audit_status IS 'Phase 3: Tracks audit processing state (pending → running → completed/failed)'; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,194 @@ | ||||||||||||||||||||||||||||||
| -- PHASE 2: Build Sessions & Preview Links | ||||||||||||||||||||||||||||||
| -- Created: 2025-12-19 | ||||||||||||||||||||||||||||||
| -- Purpose: Add tables for "Build My Site" viral wedge functionality | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
| -- ENUMS | ||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Build session status enum | ||||||||||||||||||||||||||||||
| CREATE TYPE build_session_status AS ENUM ( | ||||||||||||||||||||||||||||||
| 'created', | ||||||||||||||||||||||||||||||
| 'previewed', | ||||||||||||||||||||||||||||||
| 'locked', | ||||||||||||||||||||||||||||||
| 'converted' | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Build session input type enum | ||||||||||||||||||||||||||||||
| CREATE TYPE build_session_input_type AS ENUM ( | ||||||||||||||||||||||||||||||
| 'idea', | ||||||||||||||||||||||||||||||
| 'url' | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
| -- TABLES | ||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Build sessions table | ||||||||||||||||||||||||||||||
| -- Stores each "Build My Site" session (anonymous or authenticated) | ||||||||||||||||||||||||||||||
| CREATE TABLE IF NOT EXISTS build_sessions ( | ||||||||||||||||||||||||||||||
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | ||||||||||||||||||||||||||||||
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | ||||||||||||||||||||||||||||||
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Session data | ||||||||||||||||||||||||||||||
| language TEXT NOT NULL DEFAULT 'en', -- 'en' | 'es' | ||||||||||||||||||||||||||||||
| input_type build_session_input_type NOT NULL, | ||||||||||||||||||||||||||||||
| input_value TEXT NOT NULL, | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Audit results (static for Phase 2, real in Phase 3) | ||||||||||||||||||||||||||||||
| teaser_score INTEGER, | ||||||||||||||||||||||||||||||
| teaser_summary JSONB, | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Status tracking | ||||||||||||||||||||||||||||||
| status build_session_status NOT NULL DEFAULT 'created', | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- User relationship (nullable - anonymous sessions allowed) | ||||||||||||||||||||||||||||||
| user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL ON UPDATE CASCADE | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Preview links table | ||||||||||||||||||||||||||||||
| -- Public shareable links for build sessions | ||||||||||||||||||||||||||||||
| CREATE TABLE IF NOT EXISTS preview_links ( | ||||||||||||||||||||||||||||||
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | ||||||||||||||||||||||||||||||
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Build session relationship | ||||||||||||||||||||||||||||||
| build_session_id UUID NOT NULL REFERENCES build_sessions(id) ON DELETE CASCADE ON UPDATE CASCADE, | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Public slug for sharing (unguessable) | ||||||||||||||||||||||||||||||
| slug TEXT NOT NULL UNIQUE, | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Optional expiration | ||||||||||||||||||||||||||||||
| expires_at TIMESTAMPTZ | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
| -- INDEXES | ||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Index on preview_links.slug for fast lookup | ||||||||||||||||||||||||||||||
| CREATE INDEX idx_preview_links_slug ON preview_links(slug); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Index on build_sessions.user_id for user session queries | ||||||||||||||||||||||||||||||
| CREATE INDEX idx_build_sessions_user_id ON build_sessions(user_id); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Index on build_sessions.status for status filtering | ||||||||||||||||||||||||||||||
| CREATE INDEX idx_build_sessions_status ON build_sessions(status); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
| -- ROW LEVEL SECURITY (RLS) POLICIES | ||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- Enable RLS on both tables | ||||||||||||||||||||||||||||||
| ALTER TABLE build_sessions ENABLE ROW LEVEL SECURITY; | ||||||||||||||||||||||||||||||
| ALTER TABLE preview_links ENABLE ROW LEVEL SECURITY; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
| -- BUILD_SESSIONS POLICIES | ||||||||||||||||||||||||||||||
| -- ============================================================ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- RLS Policy: Public insert allowed (anonymous users can create sessions) | ||||||||||||||||||||||||||||||
| -- Rationale: "No signup to start" - anyone can create a build session | ||||||||||||||||||||||||||||||
| DROP POLICY IF EXISTS "build_sessions_insert_policy" ON build_sessions; | ||||||||||||||||||||||||||||||
| CREATE POLICY "build_sessions_insert_policy" ON build_sessions | ||||||||||||||||||||||||||||||
| FOR INSERT | ||||||||||||||||||||||||||||||
| TO anon, authenticated | ||||||||||||||||||||||||||||||
| WITH CHECK (true); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- RLS Policy: Public select DENIED (no public listing of sessions) | ||||||||||||||||||||||||||||||
| -- Rationale: Sessions are private by default, only accessible via preview link | ||||||||||||||||||||||||||||||
| DROP POLICY IF EXISTS "build_sessions_select_anon_policy" ON build_sessions; | ||||||||||||||||||||||||||||||
| CREATE POLICY "build_sessions_select_anon_policy" ON build_sessions | ||||||||||||||||||||||||||||||
| FOR SELECT | ||||||||||||||||||||||||||||||
| TO anon | ||||||||||||||||||||||||||||||
| USING (false); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- RLS Policy: Owner select allowed (users can see their own sessions) | ||||||||||||||||||||||||||||||
| -- Rationale: Authenticated users can view sessions they created | ||||||||||||||||||||||||||||||
| DROP POLICY IF EXISTS "build_sessions_select_owner_policy" ON build_sessions; | ||||||||||||||||||||||||||||||
| CREATE POLICY "build_sessions_select_owner_policy" ON build_sessions | ||||||||||||||||||||||||||||||
| FOR SELECT | ||||||||||||||||||||||||||||||
| TO authenticated | ||||||||||||||||||||||||||||||
| USING (user_id = auth.uid()); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| -- RLS Policy: Owner update allowed (users can update their own sessions) | ||||||||||||||||||||||||||||||
| -- Rationale: Users can change status or claim anonymous sessions | ||||||||||||||||||||||||||||||
| DROP POLICY IF EXISTS "build_sessions_update_owner_policy" ON build_sessions; | ||||||||||||||||||||||||||||||
| CREATE POLICY "build_sessions_update_owner_policy" ON build_sessions | ||||||||||||||||||||||||||||||
| FOR UPDATE | ||||||||||||||||||||||||||||||
| TO authenticated | ||||||||||||||||||||||||||||||
| USING (user_id = auth.uid() OR user_id IS NULL) | ||||||||||||||||||||||||||||||
|
Comment on lines
+116
to
+121
|
||||||||||||||||||||||||||||||
| -- Rationale: Users can change status or claim anonymous sessions | |
| DROP POLICY IF EXISTS "build_sessions_update_owner_policy" ON build_sessions; | |
| CREATE POLICY "build_sessions_update_owner_policy" ON build_sessions | |
| FOR UPDATE | |
| TO authenticated | |
| USING (user_id = auth.uid() OR user_id IS NULL) | |
| -- Rationale: Authenticated users can only update sessions they own; associating | |
| -- anonymous sessions with a user must be done via trusted backend code | |
| DROP POLICY IF EXISTS "build_sessions_update_owner_policy" ON build_sessions; | |
| CREATE POLICY "build_sessions_update_owner_policy" ON build_sessions | |
| FOR UPDATE | |
| TO authenticated | |
| USING (user_id = auth.uid()) |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The preview_links_select_by_slug_policy grants SELECT on all preview_links rows to both anon and authenticated roles with USING (true), which lets anyone holding the public Supabase anon key enumerate every preview slug and associated build_session_id. This breaks the intended "unguessable slug, no enumeration" security model and allows an attacker to list all share links and then fetch each preview (and, in combination with the build session update policy, potentially take over those sessions). Restrict SELECT so that public roles can only read a single row by a provided slug through a tightly scoped backend endpoint or RPC (and not via unrestricted table SELECT), and ensure expired links and any sensitive fields are filtered server-side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
preview_links_insert_policy blocks anonymous session creation flow.
The policy only allows authenticated users to insert preview links, but the application flow (per build-session.ts) creates preview links for anonymous users. This will cause inserts to fail for anonymous sessions unless the insert bypasses RLS (e.g., using a service role).
🔎 Potential fixes
Option 1: Allow anon to insert (if acceptable):
CREATE POLICY "preview_links_insert_policy" ON preview_links
FOR INSERT
-TO authenticated
+TO anon, authenticated
WITH CHECK (true);Option 2: Ensure the tRPC router uses a service role connection that bypasses RLS for anonymous session creation. Verify this is the intended design.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| -- RLS Policy: No public insert (only server/authenticated can create) | |
| -- Rationale: Prevent spam, only app can generate preview links | |
| DROP POLICY IF EXISTS "preview_links_insert_policy" ON preview_links; | |
| CREATE POLICY "preview_links_insert_policy" ON preview_links | |
| FOR INSERT | |
| TO authenticated | |
| WITH CHECK (true); | |
| -- RLS Policy: No public insert (only server/authenticated can create) | |
| -- Rationale: Prevent spam, only app can generate preview links | |
| DROP POLICY IF EXISTS "preview_links_insert_policy" ON preview_links; | |
| CREATE POLICY "preview_links_insert_policy" ON preview_links | |
| FOR INSERT | |
| TO anon, authenticated | |
| WITH CHECK (true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This migration adds
audit_idwith a foreign key tocynthia_audits, but no migration creates that table (rg overapps/backend/supabase/migrationsfinds only this reference), so applying this script will fail with a missing relation error and block the migration stack. The table or its creation migration needs to be added before introducing the FK.Useful? React with 👍 / 👎.