Repository navigation
Pre-ship security pass: 25-item audit and fixes - #148
Conversation
Uses the CLI's experimental stack engine with the native runtime so several agents can each run migrations, pgTAP and RLS queries at once without the shared db-gate lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Lock a user out of promo redemption after 5 failures in 15 minutes and answer unknown, expired and exhausted codes alike (next-15 #2). - Promo grants never lower a tier, end date or daily limit. - Revoke TRUNCATE/TRIGGER/REFERENCES/MAINTAIN from client roles, make global_prompts and global rate limits server-only, make the admin reporting RPCs service-role only, and close default privileges. - Account deletion removes the user's demo code and scrubs the IPs and fingerprints recorded against it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stack does not find functions behind a symlink, and its workers read supabase/functions/.env rather than --env-file, so copy both into the overlay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A prompt-injected source could make the model emit an image whose URL carries the student's work; the browser fetched it without a click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop provider, Jina, Google API and font hosts the client no longer calls; replace *.supabase.co with the two project hosts; stop allowing images from any HTTPS host; add form-action; make X-Frame-Options agree with frame-ancestors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Fail the build if dist/ holds source maps, server-secret patterns or a non-anon JWT. - Log auth events only in development builds. - Redact share codes from Sentry events and transactions. - Move tailwindcss-animate to devDependencies and update dompurify. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tate Co-Authored-By: Codex <noreply@openai.com>
…nput Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
…uota Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Claiming an event before its handlers means a worker that dies mid-run would leave a claim that blocks every Stripe retry. Mark claims complete, and let a retry take over a claim left incomplete for 10 minutes. payment_succeeded now copies the subscription status from Stripe instead of writing active, so a late delivery cannot revive a canceled plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
esm.sh/@supabase/supabase-js@2 resolved whatever 2.x esm.sh served on deploy day, inside functions that hold the service-role key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
ℹ️ Recent review info
📝 Walkthrough
Note 🎁 Summarized by CodeRabbit Free
Comment |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The webhook now reads subscription state from Stripe, so it honours STRIPE_API_URL like the account function, and the fake serves GET /v1/subscriptions/:id from state the test sets. The billing journey also checks that a late active update cannot revive a canceled plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The form gained visible Title and Content labels, so the placeholder is no longer the accessible name. This was already failing on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review round 1: - A retry that found an unfinished claim got 200 duplicate, which ends Stripe's retries; it now gets 409 so a dead delivery is retried. - The abandon cutoff came from the Edge clock but was compared with a Postgres timestamp. claim_webhook_event and complete_webhook_event now decide on the database clock. - The new-object grant test checked pg_default_acl rows; it now checks the effective privileges of a newly created table and sequence. A global function revoke would also strip extension functions (pgTAP broke), so functions keep their explicit per-migration revokes, which the anon EXECUTE test enforces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review round 2: if a failed delivery released its claim between a concurrent delivery's insert and its read, the second delivery found no row and answered duplicate (HTTP 200) for an event nobody completed. Claim again when the row is gone, and fall back to in_progress. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # e2e/full-stack/case-generation.spec.ts # e2e/full-stack/current-debate.spec.ts # e2e/full-stack/debate-context.spec.ts # e2e/full-stack/stack-core.ts # supabase/functions/ai/index.ts
Production and staging carry rate_limits policies that no migration created (rate_limits_select_own_or_global, "Users can read own limits"), both allowing global rows. Dropping by the migration's own name would have left them. Drop every policy on the two tables, and revoke client writes on rate_limits. Rehearsed on read-only dumps of both projects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 25-item pre-ship security pass, plus fixes for every finding with a clear fix. Three Opus auditors each covered one slice in their own worktree: the database, the Edge Functions, and the frontend with hosting. Each auditor ran its own Docker-free local Supabase. Nothing touched staging or production. The full report is in plans/2026-10-16-security-preship-pass.md, including what still needs Adam.
Do not merge yet. Adam reviews first. Merging ships the frontend, and the migration and Edge Functions need their own authorized deploys. See the deploy order below.
Highs fixed
subscription.updatedevent restored Pro after cancellation. The webhook now claims each event before running handlers and reads subscription state from Stripe. A claim left by a worker that died is taken over after 10 minutes.Mediums and lows fixed
export-gdocaccepted LAN origins;supabase-jsimport had no exact version.*.supabase.co;npm audit --omit=devwas a build-only dependency (now cleared).dist/contains source maps, a server-secret pattern, or a JWT whose role isn't anon.Tooling
scripts/agent-stack.shgives each agent its own Docker-free local Supabase. It uses the CLI's experimental stack engine on the native runtime, which needs Supabase CLI 2.121 or newer. Each stack starts in about 20 seconds, needs no lock, and runs pgTAP, the concurrency scripts andfunctions serve. The auditors used it to run RLS probes as two real users and to call every function with eight kinds of bad token.docs/development.mdexplains how to use it. Declarative Schemas 2.0 worked as an export (122 files, clean strict coverage). This PR keepssupabase/migrations/as the source of truth; whether to switch is in the report.Verification
All results are local, because GitHub Actions can't run on this account right now: every job stops after 2 seconds with zero steps. A local pass is not a CI pass.
npm run verifypassed at the merge with main (633317ce):./.superset/db-gate.sh) passed at0dae75ee: 441 pgTAP tests (23 files), the three concurrency scripts, and all 71 full-stack desktop and mobile journeys, including billing.activeevent can't revive a canceled plan.connect-src;script-srcblockingeval, and production already shows the same violation from its current bundle.af01c879; the global function revoke was dismissed because it strips extension functions.4323a681.4323a681. The only commit after it,0dae75ee, changes the report and nothing else.Previews (the staging one uses
VERCEL_STAGING_BYPASSor your Vercel login):The staging preview runs this branch's frontend against staging's current backend. The migration and Edge Functions only take effect there after a staging sync.
Also in this PR:
GET /v1/subscriptions/:id, andstripe-webhookhonoursSTRIPE_API_URLthe wayaccountalready did.maingained Stream every AI answer live, token by token #149 (live streaming) after review. It is merged in; its new streaming error paths return the same fixed messages, andstream.test.tsnow asserts the provider's text stays out. Codex round 4 atebb3f404reviewed the merge and the policy change: no findings.rate_limitspolicies that no migration created, and both allow global rows. The migration now drops every policy onrate_limitsandglobal_promptsby catalog. It was rehearsed in one transaction on read-only dumps of both projects and applies cleanly.Not verified: Stripe Checkout itself, hosted Auth settings, hosted preflight, staging or production backends with this migration, the race in
claim_webhook_eventunder real concurrency (reviewed, not reproduced).Deploy order (needs authorization)
20261016120000_security_preship_hardening.sqlto staging, then production.stripe-webhookneeds itswebhook_events.completed_atcolumn.stripe-webhookafter the migration._shared/cors.tsand_shared/auth.tschanged, and every function imports them; 12 functions also changed directly.Needs Adam
Hosted settings to check:
X-Forwarded-Forcontains.Decisions:
The report lists each one with its evidence.
🤖 Generated with Claude Code
Summary by CodeRabbit