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
package/cli/utils/patterns.js
CHANGED
|
@@ -11,8 +11,11 @@
|
|
|
11
11
|
*
|
|
12
12
|
* MAINTENANCE NOTES:
|
|
13
13
|
* - Patterns should have low false-positive rates
|
|
14
|
+
* - Only include patterns with SPECIFIC PREFIXES to avoid noise
|
|
14
15
|
* - Test new patterns against real codebases before adding
|
|
15
16
|
* - Order doesn't matter (all patterns are checked)
|
|
17
|
+
*
|
|
18
|
+
* v1.2.0 - Added 40+ new patterns for 2025-2026 services
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
21
|
export const SECRET_PATTERNS = [
|
|
@@ -49,27 +52,69 @@ export const SECRET_PATTERNS = [
|
|
|
49
52
|
severity: 'critical',
|
|
50
53
|
description: 'GitHub App tokens have installation-level access to repositories.'
|
|
51
54
|
},
|
|
55
|
+
{
|
|
56
|
+
name: 'GitHub Fine-Grained PAT',
|
|
57
|
+
pattern: /github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}/g,
|
|
58
|
+
severity: 'critical',
|
|
59
|
+
description: 'GitHub fine-grained PATs can access repositories with scoped permissions.'
|
|
60
|
+
},
|
|
52
61
|
{
|
|
53
62
|
name: 'Stripe Live Secret Key',
|
|
54
63
|
pattern: /sk_live_[a-zA-Z0-9]{24,}/g,
|
|
55
64
|
severity: 'critical',
|
|
56
65
|
description: 'Stripe live keys can process real payments and access customer data.'
|
|
57
66
|
},
|
|
58
|
-
{
|
|
59
|
-
name: 'Stripe Live Publishable Key',
|
|
60
|
-
pattern: /pk_live_[a-zA-Z0-9]{24,}/g,
|
|
61
|
-
severity: 'high',
|
|
62
|
-
description: 'Stripe publishable keys are less sensitive but should not be in server code.'
|
|
63
|
-
},
|
|
64
67
|
{
|
|
65
68
|
name: 'Private Key Block',
|
|
66
69
|
pattern: /-----BEGIN\s+(RSA\s+|EC\s+|DSA\s+|OPENSSH\s+)?PRIVATE\s+KEY-----/g,
|
|
67
70
|
severity: 'critical',
|
|
68
71
|
description: 'Private keys enable impersonation and decryption. Never commit these.'
|
|
69
72
|
},
|
|
73
|
+
{
|
|
74
|
+
name: 'PlanetScale Password',
|
|
75
|
+
pattern: /pscale_pw_[a-zA-Z0-9_-]{32,}/g,
|
|
76
|
+
severity: 'critical',
|
|
77
|
+
description: 'PlanetScale passwords grant database access. Keep in environment variables.'
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'PlanetScale OAuth Token',
|
|
81
|
+
pattern: /pscale_oauth_[a-zA-Z0-9_-]{32,}/g,
|
|
82
|
+
severity: 'critical',
|
|
83
|
+
description: 'PlanetScale OAuth tokens can manage your database branches and schema.'
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'Clerk Secret Key',
|
|
87
|
+
pattern: /sk_live_[a-zA-Z0-9]{27,}/g,
|
|
88
|
+
severity: 'critical',
|
|
89
|
+
description: 'Clerk secret keys grant full access to your auth system. Never expose in frontend.'
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'Doppler Service Token',
|
|
93
|
+
pattern: /dp\.st\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9]{32,}/g,
|
|
94
|
+
severity: 'critical',
|
|
95
|
+
description: 'Doppler service tokens grant access to your secrets. Ironic if leaked!'
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'HashiCorp Vault Token',
|
|
99
|
+
pattern: /hvs\.[a-zA-Z0-9_-]{24,}/g,
|
|
100
|
+
severity: 'critical',
|
|
101
|
+
description: 'HashiCorp Vault tokens grant access to your secrets.'
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'Neon Database Connection String',
|
|
105
|
+
pattern: /postgres(ql)?:\/\/[^:]+:[^@]+@[^.]+\.neon\.tech/g,
|
|
106
|
+
severity: 'critical',
|
|
107
|
+
description: 'Neon Postgres connection strings contain database credentials.'
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'MongoDB Atlas Connection String',
|
|
111
|
+
pattern: /mongodb(\+srv)?:\/\/[^:]+:[^@]+@[^.]+\.mongodb\.net/g,
|
|
112
|
+
severity: 'critical',
|
|
113
|
+
description: 'MongoDB Atlas connection strings contain database credentials.'
|
|
114
|
+
},
|
|
70
115
|
|
|
71
116
|
// =========================================================================
|
|
72
|
-
// HIGH:
|
|
117
|
+
// HIGH: AI/ML Provider Keys (2025-2026)
|
|
73
118
|
// =========================================================================
|
|
74
119
|
{
|
|
75
120
|
name: 'OpenAI API Key',
|
|
@@ -77,12 +122,70 @@ export const SECRET_PATTERNS = [
|
|
|
77
122
|
severity: 'high',
|
|
78
123
|
description: 'OpenAI keys can rack up API charges and access your usage history.'
|
|
79
124
|
},
|
|
125
|
+
{
|
|
126
|
+
name: 'OpenAI Project Key',
|
|
127
|
+
pattern: /sk-proj-[a-zA-Z0-9_-]{48,}/g,
|
|
128
|
+
severity: 'high',
|
|
129
|
+
description: 'OpenAI project keys grant access to specific project resources.'
|
|
130
|
+
},
|
|
80
131
|
{
|
|
81
132
|
name: 'Anthropic API Key',
|
|
82
|
-
pattern: /sk-ant-[a-zA-Z0-
|
|
133
|
+
pattern: /sk-ant-[a-zA-Z0-9_-]{32,}/g,
|
|
83
134
|
severity: 'high',
|
|
84
135
|
description: 'Anthropic API keys grant access to Claude and your usage quota.'
|
|
85
136
|
},
|
|
137
|
+
{
|
|
138
|
+
name: 'Google AI (Gemini) API Key',
|
|
139
|
+
pattern: /AIzaSy[a-zA-Z0-9_-]{33}/g,
|
|
140
|
+
severity: 'high',
|
|
141
|
+
description: 'Google AI API keys grant access to Gemini and other Google AI services.'
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'Replicate API Token',
|
|
145
|
+
pattern: /r8_[a-zA-Z0-9]{37}/g,
|
|
146
|
+
severity: 'high',
|
|
147
|
+
description: 'Replicate tokens can run AI models and incur charges on your account.'
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'Hugging Face Token',
|
|
151
|
+
pattern: /hf_[a-zA-Z0-9]{34}/g,
|
|
152
|
+
severity: 'high',
|
|
153
|
+
description: 'Hugging Face tokens grant access to models, datasets, and Inference API.'
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'Perplexity API Key',
|
|
157
|
+
pattern: /pplx-[a-f0-9]{48}/g,
|
|
158
|
+
severity: 'high',
|
|
159
|
+
description: 'Perplexity API keys can access their search-augmented AI models.'
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'Groq API Key',
|
|
163
|
+
pattern: /gsk_[a-zA-Z0-9]{52}/g,
|
|
164
|
+
severity: 'high',
|
|
165
|
+
description: 'Groq API keys provide access to fast LLM inference.'
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'Cohere API Key',
|
|
169
|
+
pattern: /(?:cohere|COHERE)[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?([a-zA-Z0-9]{40})["']?/gi,
|
|
170
|
+
severity: 'high',
|
|
171
|
+
description: 'Cohere API keys grant access to their NLP models.'
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'Mistral API Key',
|
|
175
|
+
pattern: /(?:mistral|MISTRAL)[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?([a-zA-Z0-9]{32})["']?/gi,
|
|
176
|
+
severity: 'high',
|
|
177
|
+
description: 'Mistral AI API keys can access their language models.'
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'Together AI API Key',
|
|
181
|
+
pattern: /(?:together|TOGETHER)[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?([a-f0-9]{64})["']?/gi,
|
|
182
|
+
severity: 'high',
|
|
183
|
+
description: 'Together AI keys grant access to open-source model hosting.'
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
// =========================================================================
|
|
187
|
+
// HIGH: Communication & Messaging
|
|
188
|
+
// =========================================================================
|
|
86
189
|
{
|
|
87
190
|
name: 'Slack Token',
|
|
88
191
|
pattern: /xox[baprs]-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*/g,
|
|
@@ -108,11 +211,15 @@ export const SECRET_PATTERNS = [
|
|
|
108
211
|
description: 'Discord bot tokens grant full control over your bot.'
|
|
109
212
|
},
|
|
110
213
|
{
|
|
111
|
-
name: '
|
|
112
|
-
pattern: /
|
|
214
|
+
name: 'Telegram Bot Token',
|
|
215
|
+
pattern: /[0-9]{8,10}:[a-zA-Z0-9_-]{35}/g,
|
|
113
216
|
severity: 'high',
|
|
114
|
-
description: '
|
|
217
|
+
description: 'Telegram bot tokens grant full control over your bot.'
|
|
115
218
|
},
|
|
219
|
+
|
|
220
|
+
// =========================================================================
|
|
221
|
+
// HIGH: Email Services
|
|
222
|
+
// =========================================================================
|
|
116
223
|
{
|
|
117
224
|
name: 'SendGrid API Key',
|
|
118
225
|
pattern: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/g,
|
|
@@ -125,6 +232,44 @@ export const SECRET_PATTERNS = [
|
|
|
125
232
|
severity: 'high',
|
|
126
233
|
description: 'Mailgun keys can send emails and access logs.'
|
|
127
234
|
},
|
|
235
|
+
{
|
|
236
|
+
name: 'Resend API Key',
|
|
237
|
+
pattern: /re_[a-zA-Z0-9]{32,}/g,
|
|
238
|
+
severity: 'high',
|
|
239
|
+
description: 'Resend API keys can send emails from your account and access logs.'
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: 'Postmark Server Token',
|
|
243
|
+
pattern: /(?:postmark|POSTMARK)[_-]?(?:server[_-]?)?token["']?\s*[:=]\s*["']?([a-f0-9-]{36})["']?/gi,
|
|
244
|
+
severity: 'high',
|
|
245
|
+
description: 'Postmark tokens can send emails from your account.'
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'Mailchimp API Key',
|
|
249
|
+
pattern: /[a-f0-9]{32}-us[0-9]{1,2}/g,
|
|
250
|
+
severity: 'high',
|
|
251
|
+
description: 'Mailchimp API keys can access your audience and send campaigns.'
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
// =========================================================================
|
|
255
|
+
// HIGH: SMS & Phone
|
|
256
|
+
// =========================================================================
|
|
257
|
+
{
|
|
258
|
+
name: 'Twilio API Key',
|
|
259
|
+
pattern: /SK[a-f0-9]{32}/g,
|
|
260
|
+
severity: 'high',
|
|
261
|
+
description: 'Twilio keys can send SMS/calls and access account data.'
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
name: 'Twilio Account SID',
|
|
265
|
+
pattern: /AC[a-f0-9]{32}/g,
|
|
266
|
+
severity: 'medium',
|
|
267
|
+
description: 'Twilio Account SIDs identify your account. Usually paired with auth token.'
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
// =========================================================================
|
|
271
|
+
// HIGH: Databases & Backend Services
|
|
272
|
+
// =========================================================================
|
|
128
273
|
{
|
|
129
274
|
name: 'Firebase/Google Service Account',
|
|
130
275
|
pattern: /"type":\s*"service_account"/g,
|
|
@@ -137,6 +282,34 @@ export const SECRET_PATTERNS = [
|
|
|
137
282
|
severity: 'high',
|
|
138
283
|
description: 'Supabase service role keys bypass Row Level Security. Keep server-side only.'
|
|
139
284
|
},
|
|
285
|
+
{
|
|
286
|
+
name: 'Upstash Redis REST Token',
|
|
287
|
+
pattern: /AX[a-zA-Z0-9]{34,}/g,
|
|
288
|
+
severity: 'high',
|
|
289
|
+
description: 'Upstash Redis tokens grant access to your serverless Redis database.'
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'Upstash QStash Token',
|
|
293
|
+
pattern: /qstash_[a-zA-Z0-9]{32,}/g,
|
|
294
|
+
severity: 'high',
|
|
295
|
+
description: 'Upstash QStash tokens can schedule and manage message queues.'
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: 'Turso Database URL',
|
|
299
|
+
pattern: /libsql:\/\/[^.]+\.turso\.io/g,
|
|
300
|
+
severity: 'high',
|
|
301
|
+
description: 'Turso database URLs. Check for embedded auth tokens in full connection string.'
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: 'Convex Deployment URL',
|
|
305
|
+
pattern: /https:\/\/[a-z]+-[a-z]+-[0-9]+\.convex\.cloud/g,
|
|
306
|
+
severity: 'medium',
|
|
307
|
+
description: 'Convex deployment URLs identify your backend. Check for paired secrets.'
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
// =========================================================================
|
|
311
|
+
// HIGH: Hosting & Deployment
|
|
312
|
+
// =========================================================================
|
|
140
313
|
{
|
|
141
314
|
name: 'Vercel Token',
|
|
142
315
|
pattern: /vercel_[a-zA-Z0-9]{24}/gi,
|
|
@@ -161,51 +334,207 @@ export const SECRET_PATTERNS = [
|
|
|
161
334
|
severity: 'high',
|
|
162
335
|
description: 'DigitalOcean tokens can manage droplets and resources.'
|
|
163
336
|
},
|
|
337
|
+
{
|
|
338
|
+
name: 'Render API Key',
|
|
339
|
+
pattern: /rnd_[a-zA-Z0-9]{32,}/g,
|
|
340
|
+
severity: 'high',
|
|
341
|
+
description: 'Render API keys can manage your services and deployments.'
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: 'Fly.io Token',
|
|
345
|
+
pattern: /FlyV1\s+[a-zA-Z0-9_-]{43}/g,
|
|
346
|
+
severity: 'high',
|
|
347
|
+
description: 'Fly.io tokens can deploy and manage your applications.'
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
name: 'Railway Token',
|
|
351
|
+
pattern: /(?:railway|RAILWAY)[_-]?token["']?\s*[:=]\s*["']?([a-f0-9-]{36})["']?/gi,
|
|
352
|
+
severity: 'high',
|
|
353
|
+
description: 'Railway API tokens can manage your services.'
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: 'Netlify Personal Access Token',
|
|
357
|
+
pattern: /nfp_[a-zA-Z0-9]{40}/g,
|
|
358
|
+
severity: 'high',
|
|
359
|
+
description: 'Netlify PATs can manage sites and deploys.'
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: 'Cloudflare API Token',
|
|
363
|
+
pattern: /(?:cloudflare|CF)[_-]?(?:api[_-]?)?token["']?\s*[:=]\s*["']?([a-zA-Z0-9_-]{40})["']?/gi,
|
|
364
|
+
severity: 'high',
|
|
365
|
+
description: 'Cloudflare API tokens can manage DNS, workers, and other services.'
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
// =========================================================================
|
|
369
|
+
// HIGH: Auth Providers
|
|
370
|
+
// =========================================================================
|
|
371
|
+
{
|
|
372
|
+
name: 'Clerk Publishable Key (Live)',
|
|
373
|
+
pattern: /pk_live_[a-zA-Z0-9]{27,}/g,
|
|
374
|
+
severity: 'medium',
|
|
375
|
+
description: 'Clerk publishable keys are meant for frontend but verify it\'s intentional.'
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: 'Clerk Test Secret Key',
|
|
379
|
+
pattern: /sk_test_[a-zA-Z0-9]{27,}/g,
|
|
380
|
+
severity: 'medium',
|
|
381
|
+
description: 'Clerk test keys are lower risk but should still be in environment variables.'
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
name: 'Auth0 Domain with Credentials',
|
|
385
|
+
pattern: /https:\/\/[^.]+\.auth0\.com.*client_secret/gi,
|
|
386
|
+
severity: 'critical',
|
|
387
|
+
description: 'Auth0 URLs with embedded client secrets should never be in code.'
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: 'Supabase Anon Key in Code',
|
|
391
|
+
pattern: /(?:supabase|SUPABASE)[_-]?(?:anon[_-]?)?key["']?\s*[:=]\s*["']?(eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+)["']?/gi,
|
|
392
|
+
severity: 'medium',
|
|
393
|
+
description: 'Supabase anon keys. Safe for frontend but verify RLS is enabled.'
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
// =========================================================================
|
|
397
|
+
// HIGH: Productivity & SaaS
|
|
398
|
+
// =========================================================================
|
|
399
|
+
{
|
|
400
|
+
name: 'Linear API Key',
|
|
401
|
+
pattern: /lin_api_[a-zA-Z0-9]{40}/g,
|
|
402
|
+
severity: 'high',
|
|
403
|
+
description: 'Linear API keys can access your project management data.'
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: 'Notion API Key',
|
|
407
|
+
pattern: /secret_[a-zA-Z0-9]{43}/g,
|
|
408
|
+
severity: 'high',
|
|
409
|
+
description: 'Notion API keys can access and modify your workspace content.'
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: 'Airtable API Key',
|
|
413
|
+
pattern: /pat[a-zA-Z0-9]{14}\.[a-f0-9]{64}/g,
|
|
414
|
+
severity: 'high',
|
|
415
|
+
description: 'Airtable personal access tokens grant access to your bases.'
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
name: 'Figma Personal Access Token',
|
|
419
|
+
pattern: /figd_[a-zA-Z0-9_-]{40,}/g,
|
|
420
|
+
severity: 'high',
|
|
421
|
+
description: 'Figma PATs can access your design files and projects.'
|
|
422
|
+
},
|
|
164
423
|
|
|
165
424
|
// =========================================================================
|
|
166
|
-
//
|
|
425
|
+
// HIGH: Payments (Additional)
|
|
167
426
|
// =========================================================================
|
|
168
427
|
{
|
|
169
|
-
name: '
|
|
170
|
-
pattern: /[
|
|
428
|
+
name: 'Stripe Test Secret Key',
|
|
429
|
+
pattern: /sk_test_[a-zA-Z0-9]{24,}/g,
|
|
171
430
|
severity: 'medium',
|
|
431
|
+
description: 'Stripe test keys are lower risk but should still be in environment variables.'
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
name: 'Stripe Live Publishable Key',
|
|
435
|
+
pattern: /pk_live_[a-zA-Z0-9]{24,}/g,
|
|
436
|
+
severity: 'medium',
|
|
437
|
+
description: 'Stripe publishable keys are meant for frontend but verify it\'s intentional.'
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'Stripe Webhook Secret',
|
|
441
|
+
pattern: /whsec_[a-zA-Z0-9]{32,}/g,
|
|
442
|
+
severity: 'high',
|
|
443
|
+
description: 'Stripe webhook secrets validate incoming webhooks. Keep server-side only.'
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
name: 'Lemon Squeezy API Key',
|
|
447
|
+
pattern: /(?:lemon|LEMON)[_-]?(?:squeezy|SQUEEZY)?[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?([a-f0-9-]{36})["']?/gi,
|
|
448
|
+
severity: 'high',
|
|
449
|
+
description: 'Lemon Squeezy API keys can manage your store and orders.'
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
name: 'Paddle API Key',
|
|
453
|
+
pattern: /(?:paddle|PADDLE)[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?([a-f0-9]{64})["']?/gi,
|
|
454
|
+
severity: 'high',
|
|
455
|
+
description: 'Paddle API keys can manage your subscriptions and payments.'
|
|
456
|
+
},
|
|
457
|
+
|
|
458
|
+
// =========================================================================
|
|
459
|
+
// HIGH: Analytics & Monitoring
|
|
460
|
+
// =========================================================================
|
|
461
|
+
{
|
|
462
|
+
name: 'Sentry DSN',
|
|
463
|
+
pattern: /https:\/\/[a-f0-9]{32}@[a-z0-9]+\.ingest\.sentry\.io\/[0-9]+/g,
|
|
464
|
+
severity: 'medium',
|
|
465
|
+
description: 'Sentry DSNs are semi-public but contain project identifiers.'
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
name: 'PostHog API Key',
|
|
469
|
+
pattern: /phc_[a-zA-Z0-9]{32,}/g,
|
|
470
|
+
severity: 'medium',
|
|
471
|
+
description: 'PostHog project API keys. Usually safe in frontend but verify.'
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: 'New Relic API Key',
|
|
475
|
+
pattern: /NRAK-[A-Z0-9]{27}/g,
|
|
476
|
+
severity: 'high',
|
|
477
|
+
description: 'New Relic API keys can access your monitoring data and configurations.'
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: 'Datadog API Key',
|
|
481
|
+
pattern: /(?:datadog|DD)[_-]?(?:api[_-]?)?key["']?\s*[:=]\s*["']?([a-f0-9]{32})["']?/gi,
|
|
482
|
+
severity: 'high',
|
|
483
|
+
description: 'Datadog API keys can access and send monitoring data.'
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
// =========================================================================
|
|
487
|
+
// MEDIUM: Generic patterns (entropy-checked to reduce false positives)
|
|
488
|
+
// requiresEntropyCheck: true → value is scored before reporting
|
|
489
|
+
// =========================================================================
|
|
490
|
+
{
|
|
491
|
+
name: 'Generic API Key Assignment',
|
|
492
|
+
pattern: /["']?(?:api[_-]?key|apikey)["']?\s*[:=]\s*["']([a-zA-Z0-9_\-]{20,})["']/gi,
|
|
493
|
+
severity: 'medium',
|
|
494
|
+
requiresEntropyCheck: true,
|
|
172
495
|
description: 'Hardcoded API keys should be moved to environment variables.'
|
|
173
496
|
},
|
|
174
497
|
{
|
|
175
|
-
name: 'Generic Secret',
|
|
176
|
-
pattern: /["']?(?:secret|secret[_-]?key)["']?\s*[:=]\s*["']([a-zA-Z0-9_\-]{
|
|
498
|
+
name: 'Generic Secret Assignment',
|
|
499
|
+
pattern: /["']?(?:secret|secret[_-]?key)["']?\s*[:=]\s*["']([a-zA-Z0-9_\-]{20,})["']/gi,
|
|
177
500
|
severity: 'medium',
|
|
501
|
+
requiresEntropyCheck: true,
|
|
178
502
|
description: 'Hardcoded secrets should be moved to environment variables.'
|
|
179
503
|
},
|
|
180
504
|
{
|
|
181
505
|
name: 'Password Assignment',
|
|
182
506
|
pattern: /["']?password["']?\s*[:=]\s*["']([^"']{8,})["']/gi,
|
|
183
507
|
severity: 'medium',
|
|
508
|
+
requiresEntropyCheck: true,
|
|
184
509
|
description: 'Hardcoded passwords are a critical vulnerability.'
|
|
185
510
|
},
|
|
186
511
|
{
|
|
187
512
|
name: 'Database URL with Credentials',
|
|
188
513
|
pattern: /(mongodb|postgres|postgresql|mysql|redis):\/\/[^:]+:[^@]+@[^\s"']+/gi,
|
|
189
514
|
severity: 'medium',
|
|
515
|
+
requiresEntropyCheck: true,
|
|
190
516
|
description: 'Database URLs with embedded passwords expose your database.'
|
|
191
517
|
},
|
|
192
518
|
{
|
|
193
|
-
name: 'Bearer Token',
|
|
519
|
+
name: 'Bearer Token in Code',
|
|
194
520
|
pattern: /["']Bearer\s+[a-zA-Z0-9_\-\.=]{20,}["']/gi,
|
|
195
521
|
severity: 'medium',
|
|
522
|
+
requiresEntropyCheck: true,
|
|
196
523
|
description: 'Hardcoded bearer tokens should not be in source code.'
|
|
197
524
|
},
|
|
198
|
-
{
|
|
199
|
-
name: 'JWT Token',
|
|
200
|
-
pattern: /["']?jwt["']?\s*[:=]\s*["']?(eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*)["']?/gi,
|
|
201
|
-
severity: 'medium',
|
|
202
|
-
description: 'JWTs in source code may be test tokens, but verify they\'re not production.'
|
|
203
|
-
},
|
|
204
525
|
{
|
|
205
526
|
name: 'Basic Auth Header',
|
|
206
|
-
pattern: /["']Basic\s+[A-Za-z0-9+/=]{
|
|
527
|
+
pattern: /["']Basic\s+[A-Za-z0-9+/=]{20,}["']/gi,
|
|
207
528
|
severity: 'medium',
|
|
529
|
+
requiresEntropyCheck: true,
|
|
208
530
|
description: 'Basic auth headers contain base64-encoded credentials.'
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
name: 'Private Key in Environment Variable',
|
|
534
|
+
pattern: /PRIVATE[_-]?KEY["']?\s*[:=]\s*["']([^"']+)["']/gi,
|
|
535
|
+
severity: 'high',
|
|
536
|
+
requiresEntropyCheck: true,
|
|
537
|
+
description: 'Private keys should be loaded from files, not hardcoded.'
|
|
209
538
|
}
|
|
210
539
|
];
|
|
211
540
|
|
|
@@ -263,3 +592,26 @@ export const SKIP_EXTENSIONS = new Set([
|
|
|
263
592
|
|
|
264
593
|
// Maximum file size to scan (1MB)
|
|
265
594
|
export const MAX_FILE_SIZE = 1_000_000;
|
|
595
|
+
|
|
596
|
+
// =============================================================================
|
|
597
|
+
// TEST FILE PATTERNS (skipped by default, override with --include-tests)
|
|
598
|
+
// =============================================================================
|
|
599
|
+
// Test fixtures are the #1 source of false positives. They contain fake
|
|
600
|
+
// credentials, mock data, and example values that look like real secrets.
|
|
601
|
+
|
|
602
|
+
export const TEST_FILE_PATTERNS = [
|
|
603
|
+
/\.test\.[jt]sx?$/,
|
|
604
|
+
/\.spec\.[jt]sx?$/,
|
|
605
|
+
/\.test\.py$/,
|
|
606
|
+
/test_[^/]+\.py$/,
|
|
607
|
+
/__tests__[/\\]/,
|
|
608
|
+
/[/\\]tests?[/\\]/,
|
|
609
|
+
/[/\\]test[/\\]/,
|
|
610
|
+
/[/\\]fixtures?[/\\]/,
|
|
611
|
+
/[/\\]mocks?[/\\]/,
|
|
612
|
+
/[/\\]__mocks__[/\\]/,
|
|
613
|
+
/[/\\]stubs?[/\\]/,
|
|
614
|
+
/[/\\]fakes?[/\\]/,
|
|
615
|
+
/\.stories\.[jt]sx?$/, // Storybook story files
|
|
616
|
+
/\.mock\.[jt]sx?$/,
|
|
617
|
+
];
|