Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,38 @@ Out of scope:
- Social engineering attacks
- Rate limiting / denial of service on free-tier Vercel/Supabase

## Row Level Security (RLS)

DevTrack uses Supabase with Row Level Security enabled on all tables to ensure users can only access their own data.

### Protected Tables

| Table | RLS Enabled | Policies |
|-------|-------------|----------|
| `users` | ✅ | SELECT, UPDATE own row only |
| `goals` | ✅ | SELECT, INSERT, UPDATE, DELETE own rows only |
| `metric_snapshots` | ✅ | SELECT, INSERT, DELETE own rows only |

### How It Works

- All RLS policies use `auth.uid()` to match against the `id` or `user_id` column
- Users can only read, write, or delete their **own** rows
- `supabaseAdmin` (service role key) bypasses RLS automatically for trusted server-side operations — it is **never** exposed to the client
- The anon key has no access to any table by default

### Migration

RLS policies are defined in:

## Disclosure Policy

Once a fix is released, we will publish a summary in the [GitHub Security Advisories](https://github.com/Priyanshu-byte-coder/devtrack/security/advisories) page. Credit will be given to the reporter unless they prefer to remain anonymous.

To apply locally:
```bash
supabase db push
```

### Security Principle

All client-facing queries use the anon key with RLS enforcement. Server-side API routes use `supabaseAdmin` only when elevated privileges are required (e.g. creating a user on first login).
64 changes: 64 additions & 0 deletions supabase/migrations/20260517000000_enable_rls.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
-- Migration: Enable Row Level Security on all tables
-- Created: 2026-05-17
-- Description: Enables RLS and adds policies so users can only access their own data.
-- supabaseAdmin (service role key) bypasses RLS automatically for server-side ops.

-- ============================================================
-- USERS TABLE
-- ============================================================
alter table users enable row level security;

-- Users can only read their own row
create policy "users_select_own"
on users for select
using (id = auth.uid()::text);

-- Users can only update their own row
create policy "users_update_own"
on users for update
using (id = auth.uid()::text);

-- ============================================================
-- GOALS TABLE
-- ============================================================
alter table goals enable row level security;

-- Users can only read their own goals
create policy "goals_select_own"
on goals for select
using (user_id = auth.uid()::text);

-- Users can only insert goals for themselves
create policy "goals_insert_own"
on goals for insert
with check (user_id = auth.uid()::text);

-- Users can only update their own goals
create policy "goals_update_own"
on goals for update
using (user_id = auth.uid()::text);

-- Users can only delete their own goals
create policy "goals_delete_own"
on goals for delete
using (user_id = auth.uid()::text);

-- ============================================================
-- METRIC_SNAPSHOTS TABLE
-- ============================================================
alter table metric_snapshots enable row level security;

-- Users can only read their own snapshots
create policy "metric_snapshots_select_own"
on metric_snapshots for select
using (user_id = auth.uid()::text);

-- Users can only insert their own snapshots
create policy "metric_snapshots_insert_own"
on metric_snapshots for insert
with check (user_id = auth.uid()::text);

-- Users can only delete their own snapshots
create policy "metric_snapshots_delete_own"
on metric_snapshots for delete
using (user_id = auth.uid()::text);
Loading