ship-safe 1.0.1 → 2.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 CHANGED
@@ -6,6 +6,9 @@ You're shipping fast. You're using AI to write code. You're one `git push` away
6
6
 
7
7
  **Ship Safe** is a security toolkit for indie hackers and vibe coders who want to secure their MVP in 5 minutes, not 5 days.
8
8
 
9
+ [![npm version](https://badge.fury.io/js/ship-safe.svg)](https://www.npmjs.com/package/ship-safe)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
+
9
12
  ---
10
13
 
11
14
  ## Quick Start
@@ -23,6 +26,18 @@ npx ship-safe init
23
26
 
24
27
  That's it. Three commands to secure your MVP.
25
28
 
29
+ ### Let AI Do It For You
30
+
31
+ Copy this prompt to your AI coding assistant:
32
+
33
+ ```
34
+ Run "npx ship-safe scan ." on my project and fix any secrets you find.
35
+ Then run "npx ship-safe init" to add security configs.
36
+ Explain what you're doing as you go.
37
+ ```
38
+
39
+ [More AI prompts for specific frameworks](./AI_SECURITY_PROMPT.md)
40
+
26
41
  ---
27
42
 
28
43
  ## Why This Exists
@@ -59,7 +74,14 @@ npx ship-safe scan . -v
59
74
 
60
75
  **Exit codes:** Returns `1` if secrets found (useful for CI), `0` if clean.
61
76
 
62
- **Detects:** OpenAI keys, AWS credentials, GitHub tokens, Stripe keys, Supabase service keys, database URLs, private keys, and 20+ more patterns.
77
+ **Detects 50+ secret patterns:**
78
+ - **AI/ML:** OpenAI, Anthropic, Google AI, Cohere, Replicate, Hugging Face
79
+ - **Auth:** Clerk, Auth0, Supabase Auth
80
+ - **Cloud:** AWS, Google Cloud, Azure
81
+ - **Database:** Supabase, PlanetScale, Neon, MongoDB, PostgreSQL, MySQL
82
+ - **Payment:** Stripe, PayPal
83
+ - **Messaging:** Twilio, SendGrid, Resend
84
+ - **And more:** GitHub tokens, private keys, JWTs, generic secrets
63
85
 
64
86
  ---
65
87
 
@@ -111,19 +133,149 @@ npx ship-safe init -f
111
133
 
112
134
  ### [`/configs`](./configs)
113
135
  **Secure defaults for popular stacks. Drop-in ready.**
114
- - [Next.js Security Headers](./configs/nextjs-security-headers.js) - CSP, X-Frame-Options, and more
115
136
 
116
- ### [`/scripts`](./scripts)
117
- **Automated scanning tools. Run them in CI or locally.**
118
- - [Secret Scanner](./scripts/scan_secrets.py) - Python version of the secret scanner
137
+ | Stack | Files |
138
+ |-------|-------|
139
+ | **Next.js** | [Security Headers](./configs/nextjs-security-headers.js) - CSP, X-Frame-Options, HSTS |
140
+ | **Supabase** | [RLS Templates](./configs/supabase/rls-templates.sql) \| [Security Checklist](./configs/supabase/security-checklist.md) \| [Secure Client](./configs/supabase/secure-client.ts) |
141
+ | **Firebase** | [Firestore Rules](./configs/firebase/firestore-rules.txt) \| [Storage Rules](./configs/firebase/storage-rules.txt) \| [Security Checklist](./configs/firebase/security-checklist.md) |
119
142
 
120
143
  ### [`/snippets`](./snippets)
121
144
  **Copy-paste code blocks for common security patterns.**
122
- - Rate limiting, auth middleware, input validation (coming soon)
145
+
146
+ | Category | Files |
147
+ |----------|-------|
148
+ | **Rate Limiting** | [Upstash Redis](./snippets/rate-limiting/upstash-ratelimit.ts) \| [Next.js Middleware](./snippets/rate-limiting/nextjs-middleware.ts) |
149
+ | **Authentication** | [JWT Security Checklist](./snippets/auth/jwt-checklist.md) |
150
+ | **API Security** | [CORS Config](./snippets/api-security/cors-config.ts) \| [Input Validation](./snippets/api-security/input-validation.ts) \| [API Checklist](./snippets/api-security/api-security-checklist.md) |
123
151
 
124
152
  ### [`/ai-defense`](./ai-defense)
125
- **Protect your AI features from abuse.**
126
- - [System Prompt Armor](./ai-defense/system-prompt-armor.md) - Prevent prompt injection attacks
153
+ **Protect your AI features from abuse and cost explosions.**
154
+
155
+ | File | Description |
156
+ |------|-------------|
157
+ | [LLM Security Checklist](./ai-defense/llm-security-checklist.md) | Based on OWASP LLM Top 10 - prompt injection, data protection, scope control |
158
+ | [Prompt Injection Patterns](./ai-defense/prompt-injection-patterns.js) | Regex patterns to detect 25+ injection attempts |
159
+ | [Cost Protection Guide](./ai-defense/cost-protection.md) | Prevent $50k surprise bills - rate limits, budget caps, circuit breakers |
160
+ | [System Prompt Armor](./ai-defense/system-prompt-armor.md) | Template for hardened system prompts |
161
+
162
+ ### [`/scripts`](./scripts)
163
+ **Automated scanning tools. Run them in CI or locally.**
164
+ - [Secret Scanner](./scripts/scan_secrets.py) - Python version of the secret scanner
165
+
166
+ ---
167
+
168
+ ## AI/LLM Security
169
+
170
+ Building with AI? Don't let it bankrupt you or get hijacked.
171
+
172
+ ### Quick Setup
173
+
174
+ ```typescript
175
+ import { containsInjectionAttempt } from './ai-defense/prompt-injection-patterns';
176
+
177
+ async function handleChat(userInput: string) {
178
+ // 1. Check for injection attempts
179
+ const { detected } = containsInjectionAttempt(userInput);
180
+ if (detected) {
181
+ return "I can't process that request.";
182
+ }
183
+
184
+ // 2. Rate limit per user
185
+ const { success } = await ratelimit.limit(userId);
186
+ if (!success) {
187
+ return "Too many requests. Please slow down.";
188
+ }
189
+
190
+ // 3. Check budget before calling
191
+ await checkUserBudget(userId, estimatedCost);
192
+
193
+ // 4. Make the API call with token limits
194
+ const response = await openai.chat.completions.create({
195
+ model: 'gpt-4',
196
+ messages,
197
+ max_tokens: 500, // Hard cap
198
+ });
199
+
200
+ return response;
201
+ }
202
+ ```
203
+
204
+ ### Cost Protection Layers
205
+
206
+ 1. **Token limits** - Cap input/output per request
207
+ 2. **Rate limits** - Cap requests per user (10/min)
208
+ 3. **Budget caps** - Daily ($1) and monthly ($10) per user
209
+ 4. **Circuit breaker** - Disable AI when global budget hit
210
+ 5. **Provider limits** - Set hard limits in OpenAI/Anthropic dashboard
211
+
212
+ [Full cost protection guide →](./ai-defense/cost-protection.md)
213
+
214
+ ---
215
+
216
+ ## Database Security
217
+
218
+ ### Supabase RLS Templates
219
+
220
+ ```sql
221
+ -- Users can only see their own data
222
+ CREATE POLICY "Users own their data" ON items
223
+ FOR ALL USING (auth.uid() = user_id);
224
+
225
+ -- Read-only public data
226
+ CREATE POLICY "Public read access" ON public_items
227
+ FOR SELECT USING (true);
228
+ ```
229
+
230
+ [6 more RLS patterns →](./configs/supabase/rls-templates.sql)
231
+
232
+ ### Firebase Security Rules
233
+
234
+ ```javascript
235
+ // Users can only access their own documents
236
+ match /users/{userId} {
237
+ allow read, write: if request.auth != null
238
+ && request.auth.uid == userId;
239
+ }
240
+ ```
241
+
242
+ [Full Firestore rules template →](./configs/firebase/firestore-rules.txt)
243
+
244
+ ---
245
+
246
+ ## API Security
247
+
248
+ ### CORS (Don't use `*` in production)
249
+
250
+ ```typescript
251
+ const ALLOWED_ORIGINS = [
252
+ 'https://yourapp.com',
253
+ 'https://www.yourapp.com',
254
+ ];
255
+
256
+ // Only allow specific origins
257
+ if (origin && ALLOWED_ORIGINS.includes(origin)) {
258
+ headers['Access-Control-Allow-Origin'] = origin;
259
+ }
260
+ ```
261
+
262
+ [CORS configs for Next.js, Express, Fastify, Hono →](./snippets/api-security/cors-config.ts)
263
+
264
+ ### Input Validation (Zod)
265
+
266
+ ```typescript
267
+ const createUserSchema = z.object({
268
+ email: z.string().email().max(255),
269
+ password: z.string().min(8).max(128),
270
+ });
271
+
272
+ const result = createUserSchema.safeParse(body);
273
+ if (!result.success) {
274
+ return Response.json({ error: result.error.issues }, { status: 400 });
275
+ }
276
+ ```
277
+
278
+ [Full validation patterns →](./snippets/api-security/input-validation.ts)
127
279
 
128
280
  ---
129
281
 
@@ -152,11 +304,13 @@ The scan exits with code `1` if secrets are found, failing your build.
152
304
 
153
305
  ## The 5-Minute Security Checklist
154
306
 
155
- 1. Run `npx ship-safe scan .` on your project
156
- 2. Run `npx ship-safe init` to add security configs
157
- 3. Add security headers to your Next.js config
158
- 4. Run `npx ship-safe checklist` before launching
159
- 5. If using AI features, add the [System Prompt Armor](./ai-defense/system-prompt-armor.md)
307
+ 1. Run `npx ship-safe scan .` on your project
308
+ 2. Run `npx ship-safe init` to add security configs
309
+ 3. Add security headers to your Next.js config
310
+ 4. Run `npx ship-safe checklist` before launching
311
+ 5. If using AI features, implement [cost protection](./ai-defense/cost-protection.md)
312
+ 6. ✅ If using Supabase, check the [RLS checklist](./configs/supabase/security-checklist.md)
313
+ 7. ✅ If using Firebase, check the [Firebase checklist](./configs/firebase/security-checklist.md)
160
314
 
161
315
  ---
162
316
 
@@ -178,15 +332,17 @@ Found a security pattern that saved your app? Share it!
178
332
  3. Include educational comments explaining *why* it matters
179
333
  4. Open a PR
180
334
 
335
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
336
+
181
337
  ---
182
338
 
183
- ## Stack-Specific Guides (Coming Soon)
339
+ ## Security Standards Reference
184
340
 
185
- - [ ] Supabase Security Defaults
186
- - [ ] Firebase Rules Templates
187
- - [ ] Vercel Environment Variables
188
- - [ ] Stripe Webhook Validation
189
- - [ ] Clerk/Auth.js Hardening
341
+ This toolkit is based on:
342
+ - [OWASP Top 10 Web 2025](https://owasp.org/Top10/)
343
+ - [OWASP Top 10 Mobile 2024](https://owasp.org/www-project-mobile-top-10/)
344
+ - [OWASP LLM Top 10 2025](https://genai.owasp.org/llm-top-10/)
345
+ - [OWASP API Security Top 10 2023](https://owasp.org/API-Security/)
190
346
 
191
347
  ---
192
348
 
@@ -0,0 +1,292 @@
1
+ # AI Cost Protection Guide
2
+
3
+ **Prevent your AI features from bankrupting you.**
4
+
5
+ Real incidents: $50k+ bills from runaway AI usage, abuse, or misconfiguration.
6
+
7
+ ---
8
+
9
+ ## Why Cost Protection Matters
10
+
11
+ | Scenario | Risk |
12
+ |----------|------|
13
+ | Leaked API key | Anyone can rack up charges on your account |
14
+ | No rate limits | Single user sends 10,000 requests |
15
+ | Long responses | GPT-4 response = $0.03-0.12 per request |
16
+ | Infinite loops | Code bug calls AI repeatedly |
17
+ | Viral launch | 10x traffic = 10x costs |
18
+
19
+ ---
20
+
21
+ ## Layer 1: API Key Security
22
+
23
+ ### Keep keys server-side only
24
+
25
+ ```typescript
26
+ // BAD: Key in frontend code
27
+ const openai = new OpenAI({ apiKey: 'sk-...' });
28
+
29
+ // GOOD: Key in server environment variable
30
+ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
31
+ ```
32
+
33
+ ### Scan for leaked keys
34
+
35
+ ```bash
36
+ npx ship-safe scan .
37
+ ```
38
+
39
+ ### Rotate keys periodically
40
+
41
+ - OpenAI: Dashboard > API Keys > Create new > Delete old
42
+ - Anthropic: Console > API Keys > Rotate
43
+
44
+ ---
45
+
46
+ ## Layer 2: Request Limits
47
+
48
+ ### Token limits per request
49
+
50
+ ```typescript
51
+ // Limit input
52
+ const MAX_INPUT_CHARS = 2000;
53
+ if (userInput.length > MAX_INPUT_CHARS) {
54
+ return "Message too long";
55
+ }
56
+
57
+ // Limit output
58
+ const response = await openai.chat.completions.create({
59
+ model: 'gpt-4',
60
+ messages: messages,
61
+ max_tokens: 500, // Hard cap on response length
62
+ });
63
+ ```
64
+
65
+ ### Rate limiting per user
66
+
67
+ ```typescript
68
+ import { Ratelimit } from '@upstash/ratelimit';
69
+
70
+ const aiRatelimit = new Ratelimit({
71
+ redis,
72
+ limiter: Ratelimit.slidingWindow(10, '1 m'), // 10 requests/minute
73
+ });
74
+
75
+ async function aiHandler(request, userId) {
76
+ const { success } = await aiRatelimit.limit(userId);
77
+ if (!success) {
78
+ return new Response('Too many requests', { status: 429 });
79
+ }
80
+ // Process request
81
+ }
82
+ ```
83
+
84
+ ### Global rate limiting
85
+
86
+ ```typescript
87
+ const globalRatelimit = new Ratelimit({
88
+ redis,
89
+ limiter: Ratelimit.slidingWindow(1000, '1 h'), // 1000 requests/hour total
90
+ prefix: 'ratelimit:global:ai',
91
+ });
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Layer 3: Budget Caps
97
+
98
+ ### Track usage in database
99
+
100
+ ```typescript
101
+ interface AIUsageRecord {
102
+ userId: string;
103
+ model: string;
104
+ inputTokens: number;
105
+ outputTokens: number;
106
+ cost: number;
107
+ timestamp: Date;
108
+ }
109
+
110
+ async function logUsage(usage: AIUsageRecord) {
111
+ await db.aiUsage.create({ data: usage });
112
+ }
113
+ ```
114
+
115
+ ### Calculate cost before request
116
+
117
+ ```typescript
118
+ // Approximate cost calculation
119
+ const COSTS = {
120
+ 'gpt-4': { input: 0.03 / 1000, output: 0.06 / 1000 },
121
+ 'gpt-4-turbo': { input: 0.01 / 1000, output: 0.03 / 1000 },
122
+ 'gpt-3.5-turbo': { input: 0.0005 / 1000, output: 0.0015 / 1000 },
123
+ 'claude-3-opus': { input: 0.015 / 1000, output: 0.075 / 1000 },
124
+ 'claude-3-sonnet': { input: 0.003 / 1000, output: 0.015 / 1000 },
125
+ };
126
+
127
+ function estimateCost(model: string, inputTokens: number, maxOutputTokens: number) {
128
+ const rates = COSTS[model] || COSTS['gpt-4'];
129
+ return (inputTokens * rates.input) + (maxOutputTokens * rates.output);
130
+ }
131
+ ```
132
+
133
+ ### Enforce user budget
134
+
135
+ ```typescript
136
+ async function checkUserBudget(userId: string, estimatedCost: number) {
137
+ const dailyLimit = 1.00; // $1/day per user
138
+ const monthlyLimit = 10.00; // $10/month per user
139
+
140
+ const today = new Date();
141
+ today.setHours(0, 0, 0, 0);
142
+
143
+ const dailyUsage = await db.aiUsage.aggregate({
144
+ where: { userId, timestamp: { gte: today } },
145
+ _sum: { cost: true },
146
+ });
147
+
148
+ if ((dailyUsage._sum.cost || 0) + estimatedCost > dailyLimit) {
149
+ throw new Error('Daily AI budget exceeded');
150
+ }
151
+
152
+ // Similar check for monthly
153
+ }
154
+ ```
155
+
156
+ ### Global budget circuit breaker
157
+
158
+ ```typescript
159
+ async function checkGlobalBudget(estimatedCost: number) {
160
+ const monthlyBudget = 500.00; // $500/month total
161
+
162
+ const monthStart = new Date();
163
+ monthStart.setDate(1);
164
+ monthStart.setHours(0, 0, 0, 0);
165
+
166
+ const monthlyUsage = await db.aiUsage.aggregate({
167
+ where: { timestamp: { gte: monthStart } },
168
+ _sum: { cost: true },
169
+ });
170
+
171
+ if ((monthlyUsage._sum.cost || 0) + estimatedCost > monthlyBudget) {
172
+ // CIRCUIT BREAKER: Disable AI features
173
+ await disableAIFeatures();
174
+ await alertAdmins('AI budget exceeded - features disabled');
175
+ throw new Error('Service temporarily unavailable');
176
+ }
177
+ }
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Layer 4: Provider-Side Limits
183
+
184
+ ### OpenAI usage limits
185
+
186
+ 1. Go to platform.openai.com
187
+ 2. Settings > Limits
188
+ 3. Set monthly hard limit
189
+
190
+ ### Anthropic usage limits
191
+
192
+ 1. Go to console.anthropic.com
193
+ 2. Settings > Usage Limits
194
+ 3. Set spend limits
195
+
196
+ ### Set up billing alerts
197
+
198
+ Most providers support alerts at:
199
+ - 50% of budget
200
+ - 80% of budget
201
+ - 100% of budget
202
+
203
+ ---
204
+
205
+ ## Layer 5: Monitoring & Alerts
206
+
207
+ ### Real-time usage dashboard
208
+
209
+ ```typescript
210
+ // Track key metrics
211
+ const metrics = {
212
+ requestsPerMinute: await getRequestsPerMinute(),
213
+ costToday: await getCostToday(),
214
+ costThisMonth: await getCostThisMonth(),
215
+ topUsers: await getTopUsersByUsage(),
216
+ errorRate: await getErrorRate(),
217
+ };
218
+ ```
219
+
220
+ ### Alert on anomalies
221
+
222
+ ```typescript
223
+ async function checkForAnomalies() {
224
+ // Alert if hourly cost exceeds normal
225
+ const hourlyCost = await getHourlyCost();
226
+ const avgHourlyCost = await getAvgHourlyCost();
227
+
228
+ if (hourlyCost > avgHourlyCost * 3) {
229
+ await sendAlert({
230
+ type: 'anomaly',
231
+ message: `Hourly AI cost spike: $${hourlyCost} (avg: $${avgHourlyCost})`,
232
+ severity: 'high',
233
+ });
234
+ }
235
+
236
+ // Alert if single user is abusing
237
+ const topUser = await getTopUserThisHour();
238
+ if (topUser.requests > 100) {
239
+ await sendAlert({
240
+ type: 'abuse',
241
+ message: `User ${topUser.id} made ${topUser.requests} AI requests this hour`,
242
+ severity: 'medium',
243
+ });
244
+ }
245
+ }
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Cost Comparison: Models
251
+
252
+ | Model | Input ($/1M tokens) | Output ($/1M tokens) | Best For |
253
+ |-------|---------------------|----------------------|----------|
254
+ | GPT-4 | $30 | $60 | Complex tasks |
255
+ | GPT-4 Turbo | $10 | $30 | Long context |
256
+ | GPT-3.5 Turbo | $0.50 | $1.50 | Simple tasks |
257
+ | Claude 3 Opus | $15 | $75 | Highest quality |
258
+ | Claude 3 Sonnet | $3 | $15 | Balanced |
259
+ | Claude 3 Haiku | $0.25 | $1.25 | Speed/cost |
260
+
261
+ **Tip:** Use cheaper models for simple tasks, reserve expensive models for complex ones.
262
+
263
+ ```typescript
264
+ function selectModel(task: string) {
265
+ const simpleTasks = ['summarize', 'classify', 'extract'];
266
+ const complexTasks = ['code', 'analyze', 'create'];
267
+
268
+ if (simpleTasks.some(t => task.includes(t))) {
269
+ return 'gpt-3.5-turbo'; // Cheap and fast
270
+ }
271
+ return 'gpt-4-turbo'; // Better but pricier
272
+ }
273
+ ```
274
+
275
+ ---
276
+
277
+ ## Quick Implementation Checklist
278
+
279
+ 1. [ ] API keys in server-side environment variables only
280
+ 2. [ ] Input length limits (e.g., 2000 chars)
281
+ 3. [ ] Output token limits (e.g., 500 tokens)
282
+ 4. [ ] Rate limiting per user (e.g., 10 requests/minute)
283
+ 5. [ ] Daily budget per user (e.g., $1/day)
284
+ 6. [ ] Global monthly budget with circuit breaker
285
+ 7. [ ] Provider-side hard limits configured
286
+ 8. [ ] Billing alerts at 50%, 80%, 100%
287
+ 9. [ ] Usage tracking in database
288
+ 10. [ ] Anomaly detection and alerting
289
+
290
+ ---
291
+
292
+ **Remember: A $50,000 surprise bill is a real risk. Implement these layers before launch.**