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.
- package/README.md +281 -23
- package/ai-defense/cost-protection.md +292 -0
- package/ai-defense/llm-security-checklist.md +324 -0
- package/ai-defense/prompt-injection-patterns.js +283 -0
- package/cli/bin/ship-safe.js +44 -2
- package/cli/commands/fix.js +216 -0
- package/cli/commands/guard.js +297 -0
- package/cli/commands/mcp.js +303 -0
- package/cli/commands/scan.js +231 -39
- package/cli/utils/entropy.js +126 -0
- package/cli/utils/output.js +10 -1
- package/cli/utils/patterns.js +376 -24
- package/configs/firebase/firestore-rules.txt +215 -0
- package/configs/firebase/security-checklist.md +236 -0
- package/configs/firebase/storage-rules.txt +206 -0
- package/configs/ship-safeignore-template +50 -0
- package/configs/supabase/rls-templates.sql +242 -0
- package/configs/supabase/secure-client.ts +225 -0
- package/configs/supabase/security-checklist.md +278 -0
- package/package.json +11 -2
- package/snippets/README.md +89 -25
- package/snippets/api-security/api-security-checklist.md +412 -0
- package/snippets/api-security/cors-config.ts +322 -0
- package/snippets/api-security/input-validation.ts +430 -0
- package/snippets/auth/jwt-checklist.md +322 -0
- package/snippets/rate-limiting/nextjs-middleware.ts +211 -0
- package/snippets/rate-limiting/upstash-ratelimit.ts +229 -0
|
@@ -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": "
|
|
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",
|
package/snippets/README.md
CHANGED
|
@@ -6,40 +6,73 @@ This folder contains drop-in code snippets for securing your application. Each s
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Available Snippets
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
- Express.js middleware
|
|
13
|
-
- Next.js API route wrapper
|
|
14
|
-
- Upstash Redis implementation
|
|
11
|
+
### Rate Limiting
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- XSS sanitization
|
|
18
|
+
**Quick Start:**
|
|
19
|
+
```typescript
|
|
20
|
+
import { apiRatelimit } from './rate-limiting/upstash-ratelimit';
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
30
|
+
### Authentication
|
|
34
31
|
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|