@vibecheck-ai/mcp 24.6.9 → 24.6.12
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 +1 -1
- package/dist/APITruthEngine-IZRR3NT5-LPFUOMLD.js +9 -0
- package/dist/CredentialsEngine-B66ANCBB-HY5ZQTSX.js +9 -0
- package/dist/EnvVarEngine-ZFNW2XKP-6HRTZULP.js +9 -0
- package/dist/ErrorHandlingEngine-FG65SFRB-4NEANCMF.js +11 -0
- package/dist/FrameworkPackEngine-RRBJW4MC-KH7WRXXS.js +12 -0
- package/dist/GhostRouteEngine-UMYBCOCL-MSZOPVZY.js +9 -0
- package/dist/LogicGapEngine-OK5UKZQ5-YGXZDERB.js +11 -0
- package/dist/PhantomDepEngine-5O7Z7MDE-4A37GGL4.js +10 -0
- package/dist/SecurityEngine-MVMRPKLH-BNP7IC46.js +9 -0
- package/dist/VersionHallucinationEngine-673DJ26J-BD4SK6JX.js +9 -0
- package/dist/chokidar-CI5VJY5M.js +2414 -0
- package/dist/chunk-43XAAYST.js +863 -0
- package/dist/chunk-5DADZJ3D.js +650 -0
- package/dist/chunk-DDTUTWRY.js +605 -0
- package/dist/chunk-DGNNNAVK.js +304 -0
- package/dist/chunk-F34MHA6A.js +772 -0
- package/dist/chunk-FGMVY5QW.js +42 -0
- package/dist/chunk-FMRX5OVJ.js +1968 -0
- package/dist/chunk-FRK2XZX5.js +213309 -0
- package/dist/chunk-J52EUKKW.js +196 -0
- package/dist/chunk-JZSHXEYP.js +915 -0
- package/dist/chunk-LQSBUKYZ.js +551 -0
- package/dist/chunk-MUP4JXOF.js +219 -0
- package/dist/chunk-NR36RTVO.js +152 -0
- package/dist/chunk-QGPX6H6L.js +3044 -0
- package/dist/chunk-QYXENOVK.js +499 -0
- package/dist/chunk-RR5ETBSV.js +66 -0
- package/dist/chunk-WUHPSW7M.js +11130 -0
- package/dist/chunk-YWUMPN4Z.js +53 -0
- package/dist/dist-HFMJ3GIR.js +1091 -0
- package/dist/dist-JUOVMQEA.js +9 -0
- package/dist/dist-NXITTS32-O3XLWR6T.js +386 -0
- package/dist/dist-Y2Z46SBD.js +22 -0
- package/dist/fingerprint-NOJ7TDB6-K6SB7LCZ.js +9 -0
- package/dist/index.js +5462 -4676
- package/dist/semantic-WW6XVII4.js +8544 -0
- package/dist/transformers.node-K4WKH4PR.js +45809 -0
- package/dist/tree-sitter-AGICL65I.js +1412 -0
- package/dist/tree-sitter-H5E7LKR4-MKO3NNLJ.js +9 -0
- package/package.json +7 -6
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { dirname } from 'path';
|
|
5
|
+
|
|
6
|
+
createRequire(import.meta.url);
|
|
7
|
+
const __filename$1 = fileURLToPath(import.meta.url);
|
|
8
|
+
dirname(__filename$1);
|
|
9
|
+
function deterministicId(uri, line, ruleId, patternName) {
|
|
10
|
+
const input = `cred:${uri}::${line}::${ruleId}::${patternName}`;
|
|
11
|
+
let hash = 2166136261;
|
|
12
|
+
for (let i = 0; i < input.length; i++) {
|
|
13
|
+
hash ^= input.charCodeAt(i);
|
|
14
|
+
hash = hash * 16777619 >>> 0;
|
|
15
|
+
}
|
|
16
|
+
return `cred-${hash.toString(16).padStart(8, "0")}`;
|
|
17
|
+
}
|
|
18
|
+
function inferCredentialLang(delta) {
|
|
19
|
+
const lid = delta.documentLanguage.toLowerCase();
|
|
20
|
+
if (lid.includes("python")) return "python";
|
|
21
|
+
if (lid.includes("go") || lid === "gomod") return "go";
|
|
22
|
+
const uriPath = delta.documentUri.replace(/^file:\/\//, "");
|
|
23
|
+
const ext = path.extname(uriPath).toLowerCase();
|
|
24
|
+
if (ext === ".py" || ext === ".pyi") return "python";
|
|
25
|
+
if (ext === ".go") return "go";
|
|
26
|
+
if (ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs" || ext === ".mts" || ext === ".cts") {
|
|
27
|
+
return "javascript";
|
|
28
|
+
}
|
|
29
|
+
return "other";
|
|
30
|
+
}
|
|
31
|
+
function isTestFile(uri) {
|
|
32
|
+
const u = uri.replace(/^file:\/\//, "");
|
|
33
|
+
if (/(?:^|[\\/])test_[\w.-]+\.py$/i.test(u) || /(?:^|[\\/])[\w.-]+_test\.py$/i.test(u) || /(?:^|[\\/])conftest\.py$/i.test(u))
|
|
34
|
+
return true;
|
|
35
|
+
if (/[^\\/]+_test\.go$/i.test(u)) return true;
|
|
36
|
+
return /\.(test|spec)\.(ts|tsx|js|jsx)$/i.test(u) || /(?:^|[\\/])(?:__tests__|__mocks__|tests?|fixtures?|e2e|spec|cypress|playwright|__snapshots__|__fixtures__|stubs?|mocks?|test[-_]?data|test[-_]?helpers?|test[-_]?utils?)[\\/]/i.test(u) || /\.(?:mock|fixture|stub)\.[^\\/]*$/i.test(u);
|
|
37
|
+
}
|
|
38
|
+
function isExampleFile(uri) {
|
|
39
|
+
return /\.(?:example|sample|template)\b/.test(uri) || /\.env\.(?:example|sample)\b/.test(uri);
|
|
40
|
+
}
|
|
41
|
+
var FAKE_VALUE_PATTERNS = /(?:test|example|sample|dummy|fake|placeholder|xxx+|your[-_]|changeme|todo|mock|fixture|replace[-_]?me|insert[-_]?here|fill[-_]?in|update[-_]?me|secret|password|change[-_]?this|put[-_]?here|add[-_]?your|enter[-_]?your|sk[-_]test|pk[-_]test|demo|default|replace|temp|tmp|foobar|foo|bar|baz|qux|lorem|ipsum|abc|1234|0000)/i;
|
|
42
|
+
function hasLowEntropy(value) {
|
|
43
|
+
const unique = new Set(value.toLowerCase()).size;
|
|
44
|
+
return unique <= 4 && value.length >= 8;
|
|
45
|
+
}
|
|
46
|
+
function isFakeCredentialValue(value) {
|
|
47
|
+
if (value.length < 8) return true;
|
|
48
|
+
if (FAKE_VALUE_PATTERNS.test(value)) return true;
|
|
49
|
+
if (hasLowEntropy(value)) return true;
|
|
50
|
+
if (value.length < 16 && /^[a-z_-]+$/.test(value)) return true;
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
function isCriticalPath(uri) {
|
|
54
|
+
const u = uri.replace(/^file:\/\//, "");
|
|
55
|
+
return /[\\/]api[\\/]|[\\/]auth[\\/]|[\\/]payment|[\\/]billing|[\\/]admin|[\\/]checkout|[\\/]webhook|[\\/]security/.test(u) || /middleware\.(ts|js|go|py)$/.test(u) || /[\\/]lib[\\/]auth|[\\/]lib[\\/]db|[\\/]lib[\\/]stripe/.test(u) || /(?:^|[\\/])(?:settings|urls)\.py$/i.test(u) || /[\\/](?:internal[\\/]auth|internal[\\/]api|handlers)(?:[\\/]|$)/i.test(u);
|
|
56
|
+
}
|
|
57
|
+
function localizeCredentialSuggestion(suggestion, lang) {
|
|
58
|
+
if (lang === "javascript" || lang === "other") return suggestion;
|
|
59
|
+
if (lang === "python") {
|
|
60
|
+
return suggestion.replace(/\bprocess\.env\.([A-Z_][A-Z0-9_]*)\b/g, 'os.environ["$1"]').replace(/\bimport\.meta\.env\.([A-Z_][A-Z0-9_]*)\b/g, 'os.environ["$1"]');
|
|
61
|
+
}
|
|
62
|
+
if (lang === "go") {
|
|
63
|
+
return suggestion.replace(/\bprocess\.env\.([A-Z_][A-Z0-9_]*)\b/g, 'os.Getenv("$1")').replace(/\bimport\.meta\.env\.([A-Z_][A-Z0-9_]*)\b/g, 'os.Getenv("$1")');
|
|
64
|
+
}
|
|
65
|
+
return suggestion;
|
|
66
|
+
}
|
|
67
|
+
function escalate(severity, critical) {
|
|
68
|
+
if (!critical) return severity;
|
|
69
|
+
const up = {
|
|
70
|
+
low: "medium",
|
|
71
|
+
medium: "high",
|
|
72
|
+
high: "critical",
|
|
73
|
+
critical: "critical"
|
|
74
|
+
};
|
|
75
|
+
return up[severity] ?? severity;
|
|
76
|
+
}
|
|
77
|
+
function maskEvidence(evidence) {
|
|
78
|
+
return evidence.replace(/(sk_(?:live|test)_)[a-zA-Z0-9]{4,}/g, "$1****").replace(/(pk_(?:live|test)_)[a-zA-Z0-9]{4,}/g, "$1****").replace(/(AKIA)[0-9A-Z]{12,}/g, "$1****").replace(/(sk-(?:ant-)?)[a-zA-Z0-9-]{8,}/g, "$1****").replace(/(gh[po]_)[a-zA-Z0-9]{4,}/g, "$1****").replace(/(xox[bpoas]-)[0-9a-zA-Z-]{4,}/g, "$1****").replace(/(SG\.)[a-zA-Z0-9_-]{4,}/g, "$1****").replace(/(npm_)[a-zA-Z0-9]{4,}/g, "$1****").replace(/(hf_)[a-zA-Z0-9]{4,}/g, "$1****").replace(/(AIza)[0-9A-Za-z_-]{4,}/g, "$1****").replace(/(pscale_(?:tkn|pw)_)[a-zA-Z0-9_]{4,}/g, "$1****").replace(/(dapi)[a-f0-9]{4,}/g, "$1****").replace(/(key-)[a-f0-9]{4,}/g, "$1****").replace(/(phc_)[a-zA-Z0-9]{4,}/g, "$1****").replace(/(lin_api_)[a-zA-Z0-9]{4,}/g, "$1****").replace(/(re_)[a-zA-Z0-9]{4,}/g, "$1****").replace(/([MN][A-Za-z\d]{23,})\.[^'"` ]{6,}/g, "$1.****").replace(/:\/\/([^:]+):([^@]{4,})@/g, "://$1:****@").replace(/['"`]([a-zA-Z0-9+/=_-]{32,})['"`]/g, (m, val) => m.replace(val, val.slice(0, 4) + "****"));
|
|
79
|
+
}
|
|
80
|
+
var PATTERNS = [
|
|
81
|
+
// ── Stripe ──
|
|
82
|
+
{
|
|
83
|
+
name: "stripe-live-secret",
|
|
84
|
+
ruleId: "CRED001",
|
|
85
|
+
regex: /(?:['"`](sk_live_[a-zA-Z0-9]{20,})['"`]|(?:^|[\s=])(sk_live_[a-zA-Z0-9]{20,})(?:\s|$))/m,
|
|
86
|
+
severity: "critical",
|
|
87
|
+
message: "Stripe live secret key hardcoded",
|
|
88
|
+
suggestion: "Move to process.env.STRIPE_SECRET_KEY and add to .env (gitignored).",
|
|
89
|
+
confidence: 99,
|
|
90
|
+
flagInTests: true,
|
|
91
|
+
skipInExamples: false
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "stripe-live-publishable",
|
|
95
|
+
ruleId: "CRED001",
|
|
96
|
+
regex: /['"`](pk_live_[a-zA-Z0-9]{20,})['"`]/,
|
|
97
|
+
severity: "high",
|
|
98
|
+
message: "Stripe live publishable key hardcoded",
|
|
99
|
+
suggestion: "Use process.env.NEXT_PUBLIC_STRIPE_KEY",
|
|
100
|
+
confidence: 95,
|
|
101
|
+
flagInTests: false,
|
|
102
|
+
skipInExamples: true
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "stripe-test-secret",
|
|
106
|
+
ruleId: "CRED001",
|
|
107
|
+
regex: /['"`](sk_test_[a-zA-Z0-9]{20,})['"`]/,
|
|
108
|
+
severity: "high",
|
|
109
|
+
message: "Stripe test secret key hardcoded \u2014 use env var even for test keys",
|
|
110
|
+
suggestion: "Use process.env.STRIPE_TEST_KEY",
|
|
111
|
+
confidence: 90,
|
|
112
|
+
flagInTests: false,
|
|
113
|
+
skipInExamples: true
|
|
114
|
+
},
|
|
115
|
+
// ── AWS ──
|
|
116
|
+
{
|
|
117
|
+
name: "aws-access-key",
|
|
118
|
+
ruleId: "CRED001",
|
|
119
|
+
regex: /['"`](AKIA[0-9A-Z]{16})['"`]/,
|
|
120
|
+
severity: "critical",
|
|
121
|
+
message: "AWS Access Key ID hardcoded",
|
|
122
|
+
suggestion: "Use AWS SDK credential chain or AWS_ACCESS_KEY_ID env var.",
|
|
123
|
+
confidence: 99,
|
|
124
|
+
flagInTests: true,
|
|
125
|
+
skipInExamples: false
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "aws-secret-key",
|
|
129
|
+
ruleId: "CRED001",
|
|
130
|
+
regex: /aws_secret_access_key\s*[:=]\s*['"`]([^'"`]{20,})['"`]/i,
|
|
131
|
+
severity: "critical",
|
|
132
|
+
message: "AWS Secret Access Key hardcoded",
|
|
133
|
+
suggestion: "Use AWS_SECRET_ACCESS_KEY environment variable.",
|
|
134
|
+
confidence: 98,
|
|
135
|
+
flagInTests: true,
|
|
136
|
+
skipInExamples: false
|
|
137
|
+
},
|
|
138
|
+
// ── AI providers ──
|
|
139
|
+
{
|
|
140
|
+
name: "openai-key",
|
|
141
|
+
ruleId: "CRED001",
|
|
142
|
+
regex: /['"`](sk-[a-zA-Z0-9]{32,})['"`]/,
|
|
143
|
+
severity: "critical",
|
|
144
|
+
message: "OpenAI API key hardcoded",
|
|
145
|
+
suggestion: "Use process.env.OPENAI_API_KEY",
|
|
146
|
+
confidence: 92,
|
|
147
|
+
flagInTests: true,
|
|
148
|
+
skipInExamples: false
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: "anthropic-key",
|
|
152
|
+
ruleId: "CRED001",
|
|
153
|
+
regex: /['"`](sk-ant-[a-zA-Z0-9-]{20,})['"`]/,
|
|
154
|
+
severity: "critical",
|
|
155
|
+
message: "Anthropic API key hardcoded",
|
|
156
|
+
suggestion: "Use process.env.ANTHROPIC_API_KEY",
|
|
157
|
+
confidence: 98,
|
|
158
|
+
flagInTests: true,
|
|
159
|
+
skipInExamples: false
|
|
160
|
+
},
|
|
161
|
+
// ── GitHub ──
|
|
162
|
+
{
|
|
163
|
+
name: "github-pat",
|
|
164
|
+
ruleId: "CRED001",
|
|
165
|
+
regex: /['"`](ghp_[a-zA-Z0-9]{36,})['"`]/,
|
|
166
|
+
severity: "critical",
|
|
167
|
+
message: "GitHub personal access token hardcoded",
|
|
168
|
+
suggestion: "Use process.env.GITHUB_TOKEN",
|
|
169
|
+
confidence: 99,
|
|
170
|
+
flagInTests: true,
|
|
171
|
+
skipInExamples: false
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "github-oauth",
|
|
175
|
+
ruleId: "CRED001",
|
|
176
|
+
regex: /['"`](gho_[a-zA-Z0-9]{36,})['"`]/,
|
|
177
|
+
severity: "critical",
|
|
178
|
+
message: "GitHub OAuth token hardcoded",
|
|
179
|
+
suggestion: "Use process.env.GITHUB_OAUTH_TOKEN",
|
|
180
|
+
confidence: 99,
|
|
181
|
+
flagInTests: true,
|
|
182
|
+
skipInExamples: false
|
|
183
|
+
},
|
|
184
|
+
// ── Google ──
|
|
185
|
+
{
|
|
186
|
+
name: "google-api-key",
|
|
187
|
+
ruleId: "CRED001",
|
|
188
|
+
regex: /['"`](AIza[0-9A-Za-z_-]{35,})['"`]/,
|
|
189
|
+
severity: "high",
|
|
190
|
+
message: "Google API key hardcoded",
|
|
191
|
+
suggestion: "Use process.env.GOOGLE_API_KEY",
|
|
192
|
+
confidence: 95,
|
|
193
|
+
flagInTests: false,
|
|
194
|
+
skipInExamples: false
|
|
195
|
+
},
|
|
196
|
+
// ── Generic secrets ──
|
|
197
|
+
{
|
|
198
|
+
name: "jwt-secret",
|
|
199
|
+
ruleId: "CRED003",
|
|
200
|
+
regex: /(?:jwt[_-]?secret|JWT_SECRET)\s*[:=]\s*['"`]([^'"`]{8,})['"`]/i,
|
|
201
|
+
severity: "critical",
|
|
202
|
+
message: "JWT signing secret hardcoded \u2014 anyone with this can forge auth tokens",
|
|
203
|
+
suggestion: "Store in JWT_SECRET environment variable and rotate regularly.",
|
|
204
|
+
confidence: 90,
|
|
205
|
+
flagInTests: false,
|
|
206
|
+
skipInExamples: true
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "generic-password",
|
|
210
|
+
ruleId: "CRED002",
|
|
211
|
+
regex: /(?:password|passwd|pwd)\s*[:=]\s*['"`]([^'"`]{4,})['"`]/i,
|
|
212
|
+
severity: "high",
|
|
213
|
+
message: "Hardcoded password detected",
|
|
214
|
+
suggestion: "Use an environment variable or secrets manager (Vault, AWS Secrets Manager).",
|
|
215
|
+
confidence: 75,
|
|
216
|
+
flagInTests: false,
|
|
217
|
+
skipInExamples: true
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: "generic-secret",
|
|
221
|
+
ruleId: "CRED002",
|
|
222
|
+
regex: /(?:secret|api[_-]?key|auth[_-]?token)\s*[:=]\s*['"`]([^'"`]{8,})['"`]/i,
|
|
223
|
+
severity: "high",
|
|
224
|
+
message: "Hardcoded secret / token detected",
|
|
225
|
+
suggestion: "Use an environment variable.",
|
|
226
|
+
confidence: 70,
|
|
227
|
+
flagInTests: false,
|
|
228
|
+
skipInExamples: true
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: "private-key-pem",
|
|
232
|
+
ruleId: "CRED004",
|
|
233
|
+
regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
|
|
234
|
+
severity: "critical",
|
|
235
|
+
message: "Private key embedded in source code",
|
|
236
|
+
suggestion: "Store key in a file and reference its path via env var.",
|
|
237
|
+
confidence: 99,
|
|
238
|
+
flagInTests: true,
|
|
239
|
+
skipInExamples: false
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: "connection-string",
|
|
243
|
+
ruleId: "CRED005",
|
|
244
|
+
regex: /['"`](?:mongodb(?:\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\/\/[^'"`\s]{10,}['"`]/i,
|
|
245
|
+
severity: "critical",
|
|
246
|
+
message: "Database connection string with credentials in source",
|
|
247
|
+
suggestion: "Use DATABASE_URL environment variable.",
|
|
248
|
+
confidence: 92,
|
|
249
|
+
flagInTests: false,
|
|
250
|
+
skipInExamples: true
|
|
251
|
+
},
|
|
252
|
+
// ── SaaS tokens ──
|
|
253
|
+
{
|
|
254
|
+
name: "slack-token",
|
|
255
|
+
ruleId: "CRED001",
|
|
256
|
+
regex: /['"`](xox[bpoas]-[0-9a-zA-Z-]{10,})['"`]/,
|
|
257
|
+
severity: "critical",
|
|
258
|
+
message: "Slack token hardcoded",
|
|
259
|
+
suggestion: "Use process.env.SLACK_TOKEN",
|
|
260
|
+
confidence: 97,
|
|
261
|
+
flagInTests: true,
|
|
262
|
+
skipInExamples: false
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: "sendgrid-key",
|
|
266
|
+
ruleId: "CRED001",
|
|
267
|
+
regex: /['"`](SG\.[a-zA-Z0-9_-]{22,}\.[a-zA-Z0-9_-]{43,})['"`]/,
|
|
268
|
+
severity: "critical",
|
|
269
|
+
message: "SendGrid API key hardcoded",
|
|
270
|
+
suggestion: "Use process.env.SENDGRID_API_KEY",
|
|
271
|
+
confidence: 99,
|
|
272
|
+
flagInTests: true,
|
|
273
|
+
skipInExamples: false
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "twilio-key",
|
|
277
|
+
ruleId: "CRED001",
|
|
278
|
+
regex: /['"`](SK[a-f0-9]{32})['"`]/,
|
|
279
|
+
severity: "high",
|
|
280
|
+
message: "Twilio API key hardcoded",
|
|
281
|
+
suggestion: "Use process.env.TWILIO_API_KEY",
|
|
282
|
+
confidence: 85,
|
|
283
|
+
flagInTests: false,
|
|
284
|
+
skipInExamples: true
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: "npm-token",
|
|
288
|
+
ruleId: "CRED001",
|
|
289
|
+
regex: /['"`](npm_[a-zA-Z0-9]{36,})['"`]/,
|
|
290
|
+
severity: "critical",
|
|
291
|
+
message: "npm auth token hardcoded",
|
|
292
|
+
suggestion: "Use .npmrc with env var interpolation: //registry.npmjs.org/:_authToken=${NPM_TOKEN}",
|
|
293
|
+
confidence: 98,
|
|
294
|
+
flagInTests: true,
|
|
295
|
+
skipInExamples: false
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: "huggingface-token",
|
|
299
|
+
ruleId: "CRED001",
|
|
300
|
+
regex: /['"`](hf_[a-zA-Z0-9]{30,})['"`]/,
|
|
301
|
+
severity: "critical",
|
|
302
|
+
message: "HuggingFace API token hardcoded",
|
|
303
|
+
suggestion: "Use process.env.HUGGINGFACE_TOKEN",
|
|
304
|
+
confidence: 97,
|
|
305
|
+
flagInTests: true,
|
|
306
|
+
skipInExamples: false
|
|
307
|
+
},
|
|
308
|
+
// ── Additional SaaS tokens ──
|
|
309
|
+
{
|
|
310
|
+
name: "supabase-service-key",
|
|
311
|
+
ruleId: "CRED001",
|
|
312
|
+
regex: /['"`](eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[a-zA-Z0-9_-]{50,}\.[a-zA-Z0-9_-]{20,})['"`]/,
|
|
313
|
+
severity: "critical",
|
|
314
|
+
message: "Supabase service role key hardcoded \u2014 grants admin access to your database",
|
|
315
|
+
suggestion: "Use process.env.SUPABASE_SERVICE_ROLE_KEY. Never expose service role keys client-side.",
|
|
316
|
+
confidence: 88,
|
|
317
|
+
flagInTests: true,
|
|
318
|
+
skipInExamples: false
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: "clerk-secret-key",
|
|
322
|
+
ruleId: "CRED001",
|
|
323
|
+
regex: /['"`](sk_(?:live|test)_[a-zA-Z0-9]{20,})['"`]/,
|
|
324
|
+
severity: "critical",
|
|
325
|
+
message: "Clerk secret key hardcoded",
|
|
326
|
+
suggestion: "Use process.env.CLERK_SECRET_KEY",
|
|
327
|
+
confidence: 90,
|
|
328
|
+
flagInTests: true,
|
|
329
|
+
skipInExamples: false
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "vercel-token",
|
|
333
|
+
ruleId: "CRED001",
|
|
334
|
+
regex: /['"`]([a-zA-Z0-9]{24})['"`](?=.*(?:vercel|VERCEL))/i,
|
|
335
|
+
severity: "high",
|
|
336
|
+
message: "Vercel API token hardcoded",
|
|
337
|
+
suggestion: "Use process.env.VERCEL_TOKEN",
|
|
338
|
+
confidence: 80,
|
|
339
|
+
flagInTests: false,
|
|
340
|
+
skipInExamples: true
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "discord-bot-token",
|
|
344
|
+
ruleId: "CRED001",
|
|
345
|
+
regex: /['"`]([MN][A-Za-z\d]{23,}\.[\w-]{6}\.[\w-]{27,})['"`]/,
|
|
346
|
+
severity: "critical",
|
|
347
|
+
message: "Discord bot token hardcoded",
|
|
348
|
+
suggestion: "Use process.env.DISCORD_BOT_TOKEN",
|
|
349
|
+
confidence: 95,
|
|
350
|
+
flagInTests: true,
|
|
351
|
+
skipInExamples: false
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: "linear-api-key",
|
|
355
|
+
ruleId: "CRED001",
|
|
356
|
+
regex: /['"`](lin_api_[a-zA-Z0-9]{30,})['"`]/,
|
|
357
|
+
severity: "critical",
|
|
358
|
+
message: "Linear API key hardcoded",
|
|
359
|
+
suggestion: "Use process.env.LINEAR_API_KEY",
|
|
360
|
+
confidence: 98,
|
|
361
|
+
flagInTests: true,
|
|
362
|
+
skipInExamples: false
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
name: "resend-api-key",
|
|
366
|
+
ruleId: "CRED001",
|
|
367
|
+
regex: /['"`](re_[a-zA-Z0-9]{20,})['"`]/,
|
|
368
|
+
severity: "critical",
|
|
369
|
+
message: "Resend API key hardcoded",
|
|
370
|
+
suggestion: "Use process.env.RESEND_API_KEY",
|
|
371
|
+
confidence: 90,
|
|
372
|
+
flagInTests: true,
|
|
373
|
+
skipInExamples: false
|
|
374
|
+
},
|
|
375
|
+
// ── Cloud Providers ──
|
|
376
|
+
{
|
|
377
|
+
name: "gcp-service-account-key",
|
|
378
|
+
ruleId: "CRED001",
|
|
379
|
+
regex: /['"`](\{[^'"`]*"type"\s*:\s*"service_account"[^'"`]*\})['"`]/,
|
|
380
|
+
severity: "critical",
|
|
381
|
+
message: "GCP service account key JSON hardcoded \u2014 grants full cloud access",
|
|
382
|
+
suggestion: "Use GOOGLE_APPLICATION_CREDENTIALS env var pointing to a key file outside the repo.",
|
|
383
|
+
confidence: 98,
|
|
384
|
+
flagInTests: true,
|
|
385
|
+
skipInExamples: false
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
name: "gcp-api-key",
|
|
389
|
+
ruleId: "CRED001",
|
|
390
|
+
regex: /['"`](AIza[0-9A-Za-z_-]{35})['"`]/,
|
|
391
|
+
severity: "high",
|
|
392
|
+
message: "Google Cloud / Firebase API key hardcoded",
|
|
393
|
+
suggestion: "Use process.env.GOOGLE_API_KEY or restrict the key in GCP console.",
|
|
394
|
+
confidence: 95,
|
|
395
|
+
flagInTests: false,
|
|
396
|
+
skipInExamples: true
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: "azure-storage-key",
|
|
400
|
+
ruleId: "CRED001",
|
|
401
|
+
regex: /(?:AccountKey|azure_storage_key|AZURE_STORAGE_KEY)\s*[:=]\s*['"`]([A-Za-z0-9+/=]{44,})['"`]/i,
|
|
402
|
+
severity: "critical",
|
|
403
|
+
message: "Azure Storage account key hardcoded",
|
|
404
|
+
suggestion: "Use process.env.AZURE_STORAGE_KEY or Managed Identity for authentication.",
|
|
405
|
+
confidence: 90,
|
|
406
|
+
flagInTests: true,
|
|
407
|
+
skipInExamples: false
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: "azure-connection-string",
|
|
411
|
+
ruleId: "CRED005",
|
|
412
|
+
regex: /['"`]DefaultEndpointsProtocol=https?;AccountName=[^;]+;AccountKey=[^'"`]{20,}['"`]/,
|
|
413
|
+
severity: "critical",
|
|
414
|
+
message: "Azure connection string with credentials hardcoded",
|
|
415
|
+
suggestion: "Use process.env.AZURE_STORAGE_CONNECTION_STRING",
|
|
416
|
+
confidence: 98,
|
|
417
|
+
flagInTests: true,
|
|
418
|
+
skipInExamples: false
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: "cloudflare-api-token",
|
|
422
|
+
ruleId: "CRED001",
|
|
423
|
+
regex: /['"`]([A-Za-z0-9_-]{40})['"`](?=.*(?:cloudflare|CLOUDFLARE|CF_))/i,
|
|
424
|
+
severity: "critical",
|
|
425
|
+
message: "Cloudflare API token hardcoded",
|
|
426
|
+
suggestion: "Use process.env.CLOUDFLARE_API_TOKEN",
|
|
427
|
+
confidence: 82,
|
|
428
|
+
flagInTests: true,
|
|
429
|
+
skipInExamples: false
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: "planetscale-token",
|
|
433
|
+
ruleId: "CRED001",
|
|
434
|
+
regex: /['"`](pscale_tkn_[a-zA-Z0-9_]{20,})['"`]/,
|
|
435
|
+
severity: "critical",
|
|
436
|
+
message: "PlanetScale API token hardcoded",
|
|
437
|
+
suggestion: "Use process.env.PLANETSCALE_TOKEN",
|
|
438
|
+
confidence: 98,
|
|
439
|
+
flagInTests: true,
|
|
440
|
+
skipInExamples: false
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
name: "planetscale-password",
|
|
444
|
+
ruleId: "CRED001",
|
|
445
|
+
regex: /['"`](pscale_pw_[a-zA-Z0-9_]{20,})['"`]/,
|
|
446
|
+
severity: "critical",
|
|
447
|
+
message: "PlanetScale database password hardcoded",
|
|
448
|
+
suggestion: "Use process.env.DATABASE_PASSWORD or PlanetScale connection string from env.",
|
|
449
|
+
confidence: 98,
|
|
450
|
+
flagInTests: true,
|
|
451
|
+
skipInExamples: false
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
name: "neon-connection-string",
|
|
455
|
+
ruleId: "CRED005",
|
|
456
|
+
regex: /['"`]postgres(?:ql)?:\/\/[^'"`]*\.neon\.tech[^'"`]*['"`]/i,
|
|
457
|
+
severity: "critical",
|
|
458
|
+
message: "Neon database connection string hardcoded",
|
|
459
|
+
suggestion: "Use process.env.DATABASE_URL",
|
|
460
|
+
confidence: 95,
|
|
461
|
+
flagInTests: false,
|
|
462
|
+
skipInExamples: true
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "turso-auth-token",
|
|
466
|
+
ruleId: "CRED001",
|
|
467
|
+
regex: /(?:TURSO_AUTH_TOKEN|authToken)\s*[:=]\s*['"`](eyJ[a-zA-Z0-9_-]{20,}\.[a-zA-Z0-9_-]{20,})['"`]/i,
|
|
468
|
+
severity: "critical",
|
|
469
|
+
message: "Turso database auth token hardcoded",
|
|
470
|
+
suggestion: "Use process.env.TURSO_AUTH_TOKEN",
|
|
471
|
+
confidence: 92,
|
|
472
|
+
flagInTests: true,
|
|
473
|
+
skipInExamples: false
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: "upstash-redis-token",
|
|
477
|
+
ruleId: "CRED001",
|
|
478
|
+
regex: /(?:UPSTASH_REDIS_REST_TOKEN|upstash.*token)\s*[:=]\s*['"`]([A-Za-z0-9]{20,})['"`]/i,
|
|
479
|
+
severity: "critical",
|
|
480
|
+
message: "Upstash Redis REST token hardcoded",
|
|
481
|
+
suggestion: "Use process.env.UPSTASH_REDIS_REST_TOKEN",
|
|
482
|
+
confidence: 88,
|
|
483
|
+
flagInTests: true,
|
|
484
|
+
skipInExamples: false
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
name: "databricks-token",
|
|
488
|
+
ruleId: "CRED001",
|
|
489
|
+
regex: /['"`](dapi[a-f0-9]{32})['"`]/,
|
|
490
|
+
severity: "critical",
|
|
491
|
+
message: "Databricks API token hardcoded",
|
|
492
|
+
suggestion: "Use process.env.DATABRICKS_TOKEN",
|
|
493
|
+
confidence: 96,
|
|
494
|
+
flagInTests: true,
|
|
495
|
+
skipInExamples: false
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
name: "telegram-bot-token",
|
|
499
|
+
ruleId: "CRED001",
|
|
500
|
+
regex: /['"`](\d{8,10}:[A-Za-z0-9_-]{35})['"`]/,
|
|
501
|
+
severity: "critical",
|
|
502
|
+
message: "Telegram bot token hardcoded",
|
|
503
|
+
suggestion: "Use process.env.TELEGRAM_BOT_TOKEN",
|
|
504
|
+
confidence: 92,
|
|
505
|
+
flagInTests: true,
|
|
506
|
+
skipInExamples: false
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
name: "postmark-server-token",
|
|
510
|
+
ruleId: "CRED001",
|
|
511
|
+
regex: /['"`]([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})['"`](?=.*(?:postmark|POSTMARK))/i,
|
|
512
|
+
severity: "critical",
|
|
513
|
+
message: "Postmark server token hardcoded",
|
|
514
|
+
suggestion: "Use process.env.POSTMARK_SERVER_TOKEN",
|
|
515
|
+
confidence: 85,
|
|
516
|
+
flagInTests: true,
|
|
517
|
+
skipInExamples: false
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
name: "mailgun-api-key",
|
|
521
|
+
ruleId: "CRED001",
|
|
522
|
+
regex: /['"`](key-[a-f0-9]{32})['"`]/,
|
|
523
|
+
severity: "critical",
|
|
524
|
+
message: "Mailgun API key hardcoded",
|
|
525
|
+
suggestion: "Use process.env.MAILGUN_API_KEY",
|
|
526
|
+
confidence: 95,
|
|
527
|
+
flagInTests: true,
|
|
528
|
+
skipInExamples: false
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
name: "algolia-admin-key",
|
|
532
|
+
ruleId: "CRED001",
|
|
533
|
+
regex: /['"`]([a-f0-9]{32})['"`](?=.*(?:algolia|ALGOLIA).*(?:admin|ADMIN))/i,
|
|
534
|
+
severity: "critical",
|
|
535
|
+
message: "Algolia admin API key hardcoded",
|
|
536
|
+
suggestion: "Use process.env.ALGOLIA_ADMIN_API_KEY. Use search-only keys client-side.",
|
|
537
|
+
confidence: 82,
|
|
538
|
+
flagInTests: true,
|
|
539
|
+
skipInExamples: false
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
name: "sentry-dsn",
|
|
543
|
+
ruleId: "CRED001",
|
|
544
|
+
regex: /['"`](https:\/\/[a-f0-9]{32}@[a-z0-9]+\.ingest\.sentry\.io\/\d+)['"`]/,
|
|
545
|
+
severity: "medium",
|
|
546
|
+
message: "Sentry DSN hardcoded \u2014 consider using env var for environment-specific DSNs",
|
|
547
|
+
suggestion: "Use process.env.SENTRY_DSN (or NEXT_PUBLIC_SENTRY_DSN for client-side).",
|
|
548
|
+
confidence: 85,
|
|
549
|
+
flagInTests: false,
|
|
550
|
+
skipInExamples: true
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
name: "posthog-api-key",
|
|
554
|
+
ruleId: "CRED001",
|
|
555
|
+
regex: /['"`](phc_[a-zA-Z0-9]{20,})['"`]/,
|
|
556
|
+
severity: "medium",
|
|
557
|
+
message: "PostHog API key hardcoded \u2014 use env var for environment isolation",
|
|
558
|
+
suggestion: "Use process.env.NEXT_PUBLIC_POSTHOG_KEY",
|
|
559
|
+
confidence: 90,
|
|
560
|
+
flagInTests: false,
|
|
561
|
+
skipInExamples: true
|
|
562
|
+
},
|
|
563
|
+
// ── URL-embedded credentials ──
|
|
564
|
+
{
|
|
565
|
+
name: "url-embedded-credentials",
|
|
566
|
+
ruleId: "CRED006",
|
|
567
|
+
regex: /['"`]https?:\/\/[^'"`\s]*:[^'"`\s@]*@[^'"`\s]+['"`]/,
|
|
568
|
+
severity: "high",
|
|
569
|
+
message: "URL contains embedded credentials (user:password@host)",
|
|
570
|
+
suggestion: "Move credentials to environment variables. Use URL without auth: https://host/path",
|
|
571
|
+
confidence: 85,
|
|
572
|
+
flagInTests: false,
|
|
573
|
+
skipInExamples: true
|
|
574
|
+
}
|
|
575
|
+
];
|
|
576
|
+
var CredentialsEngine = class {
|
|
577
|
+
id = "credentials";
|
|
578
|
+
async scan(delta, signal) {
|
|
579
|
+
if (signal?.aborted) return [];
|
|
580
|
+
const uri = delta.documentUri;
|
|
581
|
+
const source = delta.fullText;
|
|
582
|
+
if (!source.includes("'") && !source.includes('"') && !source.includes("`") && !source.includes("-----BEGIN ") && !/(?:sk_live_|sk_test_|pk_live_|AKIA|ghp_|gho_|xox[bpoas]-|sk-ant-|npm_|hf_)/i.test(source)) return [];
|
|
583
|
+
const credLang = inferCredentialLang(delta);
|
|
584
|
+
const isTest = isTestFile(uri);
|
|
585
|
+
const isExample = isExampleFile(uri);
|
|
586
|
+
const critical = isCriticalPath(uri);
|
|
587
|
+
const findings = [];
|
|
588
|
+
const lines = delta.lines ?? source.split("\n");
|
|
589
|
+
for (let i = 0; i < lines.length; i++) {
|
|
590
|
+
if (signal?.aborted) break;
|
|
591
|
+
const line = lines[i];
|
|
592
|
+
const trimmed = line.trim();
|
|
593
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("#") || credLang === "python" && (trimmed.startsWith('"""') || trimmed.startsWith("'''"))) {
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
for (const pattern of PATTERNS) {
|
|
597
|
+
if (isTest && !pattern.flagInTests) continue;
|
|
598
|
+
if (isExample && pattern.skipInExamples) continue;
|
|
599
|
+
const match = pattern.regex.exec(line);
|
|
600
|
+
if (!match) continue;
|
|
601
|
+
if (/^\s*\w+\s*[?]?\s*:\s*(?:string|number|boolean|any|unknown|Record|Partial|Required)/.test(line)) continue;
|
|
602
|
+
if (/\(\s*\w+\s*:\s*(?:string|any)/.test(line)) continue;
|
|
603
|
+
if (pattern.ruleId !== "CRED001" && pattern.ruleId !== "CRED004") {
|
|
604
|
+
if (/process\.env\.|import\.meta\.env\.|config\.|getenv|dotenv/.test(line)) continue;
|
|
605
|
+
}
|
|
606
|
+
if (pattern.ruleId === "CRED002" || pattern.ruleId === "CRED003") {
|
|
607
|
+
const val = match[1] ?? "";
|
|
608
|
+
if (!val) continue;
|
|
609
|
+
if (isFakeCredentialValue(val)) continue;
|
|
610
|
+
if (/(?:type\s|interface\s|Schema|schema|zod\.|z\.|yup\.|joi\.|@param|@type|PropTypes)/.test(line)) continue;
|
|
611
|
+
if (/(?:expect|assert|should|toBe|toEqual|toMatch|describe|it\s*\()/.test(line)) continue;
|
|
612
|
+
}
|
|
613
|
+
if (pattern.name === "generic-password") {
|
|
614
|
+
const val = match[1] ?? "";
|
|
615
|
+
if (!val || val.length < 6) continue;
|
|
616
|
+
if (isFakeCredentialValue(val)) continue;
|
|
617
|
+
if (/password\s*:\s*(?:string|undefined|null|boolean)/.test(line)) continue;
|
|
618
|
+
}
|
|
619
|
+
if (pattern.name === "generic-secret") {
|
|
620
|
+
const val = match[1] ?? "";
|
|
621
|
+
if (!val || /^[a-z_]+$/i.test(val)) continue;
|
|
622
|
+
if (isFakeCredentialValue(val)) continue;
|
|
623
|
+
if (/\w+\(|\w+\.\w+/.test(val)) continue;
|
|
624
|
+
}
|
|
625
|
+
if (pattern.ruleId === "CRED005" || pattern.ruleId === "CRED006") {
|
|
626
|
+
if (/\$\{|<.*>|YOUR_|REPLACE|TODO|localhost/.test(line)) continue;
|
|
627
|
+
if (/example/i.test(line) && !/example\.(?:com|org|net|io)/i.test(line)) continue;
|
|
628
|
+
}
|
|
629
|
+
findings.push({
|
|
630
|
+
id: deterministicId(uri, i + 1, pattern.ruleId, pattern.name),
|
|
631
|
+
engine: "credentials",
|
|
632
|
+
severity: escalate(pattern.severity, critical),
|
|
633
|
+
category: "credentials",
|
|
634
|
+
file: uri,
|
|
635
|
+
line: i + 1,
|
|
636
|
+
column: match.index ?? 0,
|
|
637
|
+
message: pattern.message,
|
|
638
|
+
evidence: maskEvidence(trimmed),
|
|
639
|
+
suggestion: localizeCredentialSuggestion(pattern.suggestion, credLang),
|
|
640
|
+
confidence: pattern.confidence / 100,
|
|
641
|
+
autoFixable: false,
|
|
642
|
+
ruleId: pattern.ruleId
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return findings;
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
export { CredentialsEngine };
|