ship-safe 1.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,278 @@
1
+ # Supabase Security Checklist
2
+
3
+ **Complete this checklist before launching your Supabase-powered app.**
4
+
5
+ Based on [CVE-2025-48757](https://byteiota.com/supabase-security-flaw-170-apps-exposed-by-missing-rls/) and common pentesting findings.
6
+
7
+ ---
8
+
9
+ ## Critical: Row Level Security (RLS)
10
+
11
+ ### 1. [ ] RLS is ENABLED on ALL tables
12
+
13
+ ```sql
14
+ -- Check which tables DON'T have RLS
15
+ SELECT schemaname, tablename
16
+ FROM pg_tables
17
+ WHERE schemaname = 'public'
18
+ AND tablename NOT IN (
19
+ SELECT tablename::text FROM pg_class
20
+ WHERE relrowsecurity = true
21
+ );
22
+ ```
23
+
24
+ **If any tables appear, enable RLS immediately:**
25
+ ```sql
26
+ ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
27
+ ```
28
+
29
+ ### 2. [ ] Every table has at least one policy
30
+
31
+ ```sql
32
+ -- Tables with RLS enabled but NO policies (locked to everyone!)
33
+ SELECT tablename FROM pg_tables
34
+ WHERE schemaname = 'public'
35
+ AND tablename IN (
36
+ SELECT tablename::text FROM pg_class WHERE relrowsecurity = true
37
+ )
38
+ AND tablename NOT IN (
39
+ SELECT tablename FROM pg_policies
40
+ );
41
+ ```
42
+
43
+ ### 3. [ ] Policies use `auth.uid()` not hardcoded values
44
+
45
+ **Good:**
46
+ ```sql
47
+ USING (auth.uid() = user_id)
48
+ ```
49
+
50
+ **Bad:**
51
+ ```sql
52
+ USING (user_id = 'some-uuid') -- Hardcoded!
53
+ ```
54
+
55
+ ### 4. [ ] INSERT policies have `WITH CHECK`
56
+
57
+ ```sql
58
+ -- Without WITH CHECK, users can insert data for other users!
59
+ CREATE POLICY "Users insert own data"
60
+ ON your_table FOR INSERT
61
+ WITH CHECK (auth.uid() = user_id); -- This is required!
62
+ ```
63
+
64
+ ### 5. [ ] UPDATE policies have both `USING` and `WITH CHECK`
65
+
66
+ ```sql
67
+ -- USING: which rows can be updated
68
+ -- WITH CHECK: what the new values must satisfy
69
+ CREATE POLICY "Users update own data"
70
+ ON your_table FOR UPDATE
71
+ USING (auth.uid() = user_id) -- Can only update own rows
72
+ WITH CHECK (auth.uid() = user_id); -- Can't change user_id to someone else
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Critical: API Keys
78
+
79
+ ### 6. [ ] `service_role` key is NEVER in frontend code
80
+
81
+ ```bash
82
+ # Scan your codebase
83
+ npx ship-safe scan .
84
+
85
+ # Or manually grep
86
+ grep -r "service_role" ./src
87
+ grep -r "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" ./src
88
+ ```
89
+
90
+ **The `service_role` key bypasses ALL RLS. It should only exist in:**
91
+ - Server-side code (API routes, edge functions)
92
+ - Environment variables on your server
93
+ - Never in client bundles
94
+
95
+ ### 7. [ ] `anon` key is only used where RLS protects data
96
+
97
+ The `anon` key is designed to be public, but only if RLS is properly configured.
98
+
99
+ ```typescript
100
+ // Frontend: Use anon key (safe if RLS is set up)
101
+ const supabase = createClient(url, anonKey);
102
+
103
+ // Server: Use service_role key (for admin operations)
104
+ const supabaseAdmin = createClient(url, serviceRoleKey);
105
+ ```
106
+
107
+ ---
108
+
109
+ ## High: Authentication Settings
110
+
111
+ ### 8. [ ] Email confirmations enabled (if using email auth)
112
+
113
+ Dashboard > Authentication > Providers > Email
114
+ - [ ] Confirm email = ON
115
+ - [ ] Secure email change = ON
116
+
117
+ ### 9. [ ] Rate limiting on auth endpoints
118
+
119
+ Supabase has built-in rate limiting, but verify:
120
+ - Dashboard > Authentication > Rate Limits
121
+ - Default: 30 requests per hour for signup/signin
122
+
123
+ ### 10. [ ] Leaked password protection enabled
124
+
125
+ Dashboard > Authentication > Providers > Email
126
+ - [ ] Check for leaked passwords = ON
127
+
128
+ ---
129
+
130
+ ## High: Database Security
131
+
132
+ ### 11. [ ] No sensitive tables exposed to PostgREST
133
+
134
+ By default, all tables in `public` schema are exposed via REST API.
135
+
136
+ ```sql
137
+ -- Move sensitive tables to a private schema
138
+ ALTER TABLE sensitive_table SET SCHEMA private;
139
+
140
+ -- Or revoke access from anon/authenticated roles
141
+ REVOKE ALL ON sensitive_table FROM anon, authenticated;
142
+ ```
143
+
144
+ ### 12. [ ] Functions with `SECURITY DEFINER` are reviewed
145
+
146
+ ```sql
147
+ -- List all SECURITY DEFINER functions
148
+ SELECT proname, prosecdef
149
+ FROM pg_proc
150
+ WHERE prosecdef = true;
151
+ ```
152
+
153
+ `SECURITY DEFINER` functions run with the privileges of the creator, not the caller. Review each one for:
154
+ - SQL injection vulnerabilities
155
+ - Proper input validation
156
+ - Necessary privilege escalation
157
+
158
+ ### 13. [ ] HTTP extension not exposed to anon users
159
+
160
+ ```sql
161
+ -- Check if http extension functions are callable by anon
162
+ SELECT routine_name
163
+ FROM information_schema.routine_privileges
164
+ WHERE grantee = 'anon'
165
+ AND routine_schema = 'extensions';
166
+ ```
167
+
168
+ If `http_get`, `http_post` appear, attackers can make SSRF requests.
169
+
170
+ ---
171
+
172
+ ## Medium: Storage Security
173
+
174
+ ### 14. [ ] Storage buckets have proper policies
175
+
176
+ Dashboard > Storage > Policies
177
+
178
+ Each bucket should have:
179
+ - SELECT policy (who can download)
180
+ - INSERT policy (who can upload)
181
+ - UPDATE policy (who can replace)
182
+ - DELETE policy (who can remove)
183
+
184
+ ### 15. [ ] File type validation on uploads
185
+
186
+ ```typescript
187
+ const { error } = await supabase.storage
188
+ .from('avatars')
189
+ .upload(path, file, {
190
+ contentType: 'image/png', // Explicit content type
191
+ upsert: false // Prevent overwrites
192
+ });
193
+ ```
194
+
195
+ ### 16. [ ] File size limits configured
196
+
197
+ Dashboard > Storage > Settings
198
+ - Set max file size per bucket
199
+ - Consider implementing client-side validation too
200
+
201
+ ---
202
+
203
+ ## Medium: Realtime Security
204
+
205
+ ### 17. [ ] Realtime only enabled on necessary tables
206
+
207
+ Dashboard > Database > Replication
208
+
209
+ Only enable realtime for tables that need it. Each enabled table:
210
+ - Increases server load
211
+ - Broadcasts changes to subscribed clients
212
+ - Must have RLS to protect data
213
+
214
+ ### 18. [ ] Broadcast/Presence channels authenticated
215
+
216
+ ```typescript
217
+ // Require authentication for realtime channels
218
+ const channel = supabase.channel('room', {
219
+ config: {
220
+ broadcast: { self: true },
221
+ presence: { key: user.id }
222
+ }
223
+ });
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Bonus: Monitoring & Alerts
229
+
230
+ ### 19. [ ] Database logs enabled
231
+
232
+ Dashboard > Database > Logs
233
+ - Enable query logging for debugging
234
+ - Set up alerts for failed auth attempts
235
+
236
+ ### 20. [ ] Supabase email alerts configured
237
+
238
+ Dashboard > Settings > Alerts
239
+ - Database approaching limits
240
+ - High error rates
241
+ - Unusual activity
242
+
243
+ ---
244
+
245
+ ## Quick Commands
246
+
247
+ ```sql
248
+ -- Enable RLS on all public tables at once (CAREFUL!)
249
+ DO $$
250
+ DECLARE
251
+ t text;
252
+ BEGIN
253
+ FOR t IN
254
+ SELECT tablename FROM pg_tables WHERE schemaname = 'public'
255
+ LOOP
256
+ EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t);
257
+ END LOOP;
258
+ END
259
+ $$;
260
+
261
+ -- List all policies
262
+ SELECT * FROM pg_policies;
263
+
264
+ -- Check RLS status for all tables
265
+ SELECT
266
+ schemaname,
267
+ tablename,
268
+ rowsecurity
269
+ FROM pg_tables
270
+ JOIN pg_class ON tablename = relname
271
+ WHERE schemaname = 'public';
272
+ ```
273
+
274
+ ---
275
+
276
+ **Remember: Supabase makes it easy to build fast, but security is still your responsibility.**
277
+
278
+ Run `npx ship-safe scan .` to check for leaked keys before every deploy.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ship-safe",
3
- "version": "1.0.1",
3
+ "version": "3.0.0",
4
4
  "description": "Security toolkit for vibe coders and indie hackers. Secure your MVP in 5 minutes.",
5
5
  "main": "cli/index.js",
6
6
  "bin": {
@@ -23,7 +23,16 @@
23
23
  "indie-hacker",
24
24
  "vibe-coding",
25
25
  "mvp",
26
- "cli"
26
+ "cli",
27
+ "supabase",
28
+ "firebase",
29
+ "llm-security",
30
+ "prompt-injection",
31
+ "rate-limiting",
32
+ "owasp",
33
+ "jwt",
34
+ "cors",
35
+ "rls"
27
36
  ],
28
37
  "author": "ship-safe contributors",
29
38
  "license": "MIT",
@@ -6,40 +6,73 @@ This folder contains drop-in code snippets for securing your application. Each s
6
6
 
7
7
  ---
8
8
 
9
- ## Coming Soon
9
+ ## Available Snippets
10
10
 
11
- - **Rate Limiting**
12
- - Express.js middleware
13
- - Next.js API route wrapper
14
- - Upstash Redis implementation
11
+ ### Rate Limiting
15
12
 
16
- - **Authentication**
17
- - Secure session configuration
18
- - JWT validation middleware
19
- - OAuth state parameter handling
13
+ | File | Description |
14
+ |------|-------------|
15
+ | [upstash-ratelimit.ts](./rate-limiting/upstash-ratelimit.ts) | Production-ready rate limiting with Upstash Redis. Includes different limiters for API, auth, and AI endpoints. |
16
+ | [nextjs-middleware.ts](./rate-limiting/nextjs-middleware.ts) | In-memory rate limiting at the Next.js middleware level. Good for development or simple deployments. |
20
17
 
21
- - **Input Validation**
22
- - Zod schemas for common patterns
23
- - SQL injection prevention
24
- - XSS sanitization
18
+ **Quick Start:**
19
+ ```typescript
20
+ import { apiRatelimit } from './rate-limiting/upstash-ratelimit';
25
21
 
26
- - **API Security**
27
- - CORS configuration
28
- - API key validation
29
- - Webhook signature verification (Stripe, GitHub)
22
+ const { success } = await apiRatelimit.limit(userId);
23
+ if (!success) {
24
+ return new Response('Too Many Requests', { status: 429 });
25
+ }
26
+ ```
30
27
 
31
28
  ---
32
29
 
33
- ## Contributing
30
+ ### Authentication
34
31
 
35
- Have a security snippet that saved your app? Add it here!
32
+ | File | Description |
33
+ |------|-------------|
34
+ | [jwt-checklist.md](./auth/jwt-checklist.md) | Complete JWT security checklist. Covers algorithms, token lifetime, storage, validation, and revocation. |
36
35
 
37
- 1. Create a new file: `snippets/your-snippet-name.{js,ts,py}`
38
- 2. Add extensive comments explaining:
39
- - What attack this prevents
40
- - How to integrate it
41
- - Common gotchas
42
- 3. Open a PR
36
+ **Key Points:**
37
+ - Use RS256/ES256, not HS256 with weak secrets
38
+ - Access tokens: 15-60 minutes max
39
+ - Store in httpOnly cookies, not localStorage
40
+ - Always validate issuer and audience claims
41
+
42
+ ---
43
+
44
+ ### API Security
45
+
46
+ | File | Description |
47
+ |------|-------------|
48
+ | [cors-config.ts](./api-security/cors-config.ts) | CORS configurations for Next.js, Express, Fastify, Hono, and Vercel Edge. |
49
+ | [input-validation.ts](./api-security/input-validation.ts) | Zod schemas and validation patterns for API endpoints. Includes file upload validation. |
50
+ | [api-security-checklist.md](./api-security/api-security-checklist.md) | Comprehensive API security checklist based on OWASP API Security Top 10. |
51
+
52
+ **Quick Start (CORS):**
53
+ ```typescript
54
+ const ALLOWED_ORIGINS = ['https://yourapp.com'];
55
+
56
+ // Only allow specific origins
57
+ if (origin && ALLOWED_ORIGINS.includes(origin)) {
58
+ headers['Access-Control-Allow-Origin'] = origin;
59
+ }
60
+ ```
61
+
62
+ **Quick Start (Validation):**
63
+ ```typescript
64
+ import { z } from 'zod';
65
+
66
+ const schema = z.object({
67
+ email: z.string().email().max(255),
68
+ password: z.string().min(8).max(128),
69
+ });
70
+
71
+ const result = schema.safeParse(body);
72
+ if (!result.success) {
73
+ return Response.json({ error: result.error.issues }, { status: 400 });
74
+ }
75
+ ```
43
76
 
44
77
  ---
45
78
 
@@ -56,3 +89,34 @@ Each snippet follows this format:
56
89
 
57
90
  [actual code with inline comments]
58
91
  ```
92
+
93
+ ---
94
+
95
+ ## Related Resources
96
+
97
+ - **[/configs](../configs/)** - Framework configs (Next.js headers, Supabase RLS, Firebase rules)
98
+ - **[/ai-defense](../ai-defense/)** - AI/LLM security (prompt injection, cost protection)
99
+ - **[/checklists](../checklists/)** - Security checklists (launch day)
100
+
101
+ ---
102
+
103
+ ## Contributing
104
+
105
+ Have a security snippet that saved your app? Add it here!
106
+
107
+ 1. Create a new file in the appropriate subfolder
108
+ 2. Add extensive comments explaining:
109
+ - What attack this prevents
110
+ - How to integrate it
111
+ - Common gotchas
112
+ 3. Open a PR
113
+
114
+ ---
115
+
116
+ ## What's Next
117
+
118
+ Future additions planned:
119
+ - Webhook signature verification (Stripe, GitHub)
120
+ - OAuth state parameter handling
121
+ - CSRF protection patterns
122
+ - Content Security Policy builder