vestige/vercel.json
Sam Valladares b8de5c9bf7 fix(launch): close waitlist RLS bypass + open email relay + add security headers
Launch-critical fixes for the public July-14 signup surface (swarm-audited by
MiniMax M3, Claude-verified against the frontend RPC call path):
- migration: DROP the "anon can insert with check(true)" RLS policy. It let
  anon write arbitrary rows straight to the table, bypassing all join_waitlist
  validation. The frontend already calls the join_waitlist RPC (rest/v1/rpc/),
  never a direct insert, so signup is unaffected. RLS stays enabled with no
  permissive INSERT policy = direct anon inserts denied by default.
- join_waitlist: cap email <=254, reject control chars, truncate referrer <=2048.
- waitlist-welcome edge fn: FAIL CLOSED — if WAITLIST_WEBHOOK_SECRET is unset,
  return 401 instead of serving (was an open Resend relay). Guard the referral
  code against ^[a-z2-9]{7}$ and cap email length before sending.
- vercel.json: add baseline security headers (nosniff, Referrer-Policy,
  X-Frame-Options: DENY, HSTS, Permissions-Policy). CSP deferred (would break
  the WebGPU hero's inline bootstrap without nonces) — documented.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 14:23:58 -05:00

73 lines
1.9 KiB
JSON

{
"$schema": "https://openapi.vercel.sh/vercel.json",
"framework": null,
"installCommand": "pnpm install --frozen-lockfile",
"buildCommand": "VESTIGE_BASE_PATH= VITE_ROOT_REDIRECT=/launch pnpm --filter @vestige/dashboard build",
"outputDirectory": "apps/dashboard/build",
"cleanUrls": true,
"trailingSlash": false,
"headers": [
{
"source": "/_app/immutable/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "/((?!_app/immutable/).*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=0, must-revalidate"
}
]
},
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "Referrer-Policy",
"value": "strict-origin-when-cross-origin"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "Strict-Transport-Security",
"value": "max-age=63072000; includeSubDomains"
},
{
"key": "Permissions-Policy",
"value": "camera=(), microphone=(), geolocation=()"
}
]
}
],
"_csp_note": "CSP TODO: the WebGPU launch page uses SvelteKit inline bootstrap scripts (and likely inline styles); a strict Content-Security-Policy needs nonces/hashes wired through the SvelteKit adapter or it will break the page. Omitting CSP intentionally rather than shipping one that blocks the launch hero. Add later via svelte.config kit.csp once script/style sources are enumerated.",
"redirects": [
{
"source": "/dashboard/launch",
"destination": "/launch",
"permanent": false
},
{
"source": "/dashboard/launch/:path*",
"destination": "/launch",
"permanent": false
}
],
"rewrites": [
{
"source": "/((?!_app/|favicon\\.svg|manifest\\.json|robots\\.txt|.*\\..*).*)",
"destination": "/200.html"
}
]
}