Skip to content

security(rls): enable RLS on du_shared_reports + tighten du_feedback policy#63

Open
bleedmode wants to merge 1 commit intomainfrom
security/rls-shared-reports-feedback
Open

security(rls): enable RLS on du_shared_reports + tighten du_feedback policy#63
bleedmode wants to merge 1 commit intomainfrom
security/rls-shared-reports-feedback

Conversation

@bleedmode
Copy link
Copy Markdown
Owner

Summary

  • Enable RLS on du_shared_reports (was off — PostgREST exposed all share tokens to anon)
  • Tighten du_feedback policy from WITH CHECK (true) to bounded length checks + 100/hr rate-limit trigger
  • Switch feedback.ts to Prefer: return=minimal (anon no longer has implicit SELECT, so representation broke)

Why

Supabase advisor flagged on the public Dear User project:

  • ERROR rls_disabled_in_public + sensitive_columns_exposed on du_shared_reports
  • WARN rls_policy_always_true on du_feedback

The original 006 migration argued tokens-as-capabilities was sufficient, but PostgREST happily lists the whole table for anon, returning every token. RLS now blocks that — service role still bypasses, so loadSharedReport (web) and insertSharedReport (mcp) keep working without code changes.

Verified post-apply

  • anon GET /rest/v1/du_shared_reports?select=*[]
  • anon GET /rest/v1/du_feedback?select=*[]
  • anon POST /rest/v1/du_feedback with return=minimal → 201
  • Supabase advisor re-run: 0 ERROR/WARN on these tables (one INFO rls_enabled_no_policy is intentional — service-role only)
  • feedback.test.ts: 6/6 pass

Test plan

  • Smoke-test share flow on staging: create share via MCP, view at /r/<token> — should still work via service-role read
  • Send feedback via MCP dearuser feedback "test" — should still get "Tak — din feedback er modtaget"
  • Try to scrape du_shared_reports from any anon client — should return empty

🤖 Generated with Claude Code

…policy

Supabase advisor flagged two findings on the public Dear User project:

- ERROR rls_disabled_in_public + sensitive_columns_exposed on
  du_shared_reports: PostgREST exposed the whole table (including share
  tokens) to anon. The original 006 migration argued tokens act as
  capabilities, but that only holds if you can't list rows without one.
  Migration 008 enables RLS with no anon/authenticated policies — service
  role still bypasses, so loadSharedReport (web) and insertSharedReport
  (mcp) keep working.

- WARN rls_policy_always_true on du_feedback: WITH CHECK (true) let
  anon insert anything in any column. Migration 009 replaces the policy
  with bounded length checks across message/context/email/user_agent
  and adds a 100/hour global rate-limit trigger. The existing tabular
  CHECKs on message length, rating range, and source enum stay.

Side-effect: anon INSERT with `Prefer: return=representation` requires a
SELECT permission we don't grant, so feedback.ts switches to
return=minimal and drops the unused `id` round-trip. Tests updated.

Verified post-apply: anon SELECT on both tables returns [], anon INSERT
on du_feedback succeeds with return=minimal, advisor re-run is clear of
ERROR/WARN findings on these tables.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant