@vibecheckai/cli 3.2.2 → 3.2.4
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/bin/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
- package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/analyzers.js +606 -325
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/backup.js +62 -62
- package/bin/runners/lib/billing.js +107 -107
- package/bin/runners/lib/claims.js +118 -118
- package/bin/runners/lib/cli-ui.js +540 -540
- package/bin/runners/lib/contracts/auth-contract.js +202 -202
- package/bin/runners/lib/contracts/env-contract.js +181 -181
- package/bin/runners/lib/contracts/external-contract.js +206 -206
- package/bin/runners/lib/contracts/guard.js +168 -168
- package/bin/runners/lib/contracts/index.js +89 -89
- package/bin/runners/lib/contracts/plan-validator.js +311 -311
- package/bin/runners/lib/contracts/route-contract.js +199 -199
- package/bin/runners/lib/contracts.js +804 -804
- package/bin/runners/lib/detect.js +89 -89
- package/bin/runners/lib/doctor/autofix.js +254 -254
- package/bin/runners/lib/doctor/index.js +37 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
- package/bin/runners/lib/doctor/modules/index.js +46 -46
- package/bin/runners/lib/doctor/modules/network.js +250 -250
- package/bin/runners/lib/doctor/modules/project.js +312 -312
- package/bin/runners/lib/doctor/modules/runtime.js +224 -224
- package/bin/runners/lib/doctor/modules/security.js +348 -348
- package/bin/runners/lib/doctor/modules/system.js +213 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
- package/bin/runners/lib/doctor/reporter.js +262 -262
- package/bin/runners/lib/doctor/service.js +262 -262
- package/bin/runners/lib/doctor/types.js +113 -113
- package/bin/runners/lib/doctor/ui.js +263 -263
- package/bin/runners/lib/doctor-v2.js +608 -608
- package/bin/runners/lib/drift.js +425 -425
- package/bin/runners/lib/enforcement.js +72 -72
- package/bin/runners/lib/engines/accessibility-engine.js +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- package/bin/runners/lib/env-resolver.js +417 -417
- package/bin/runners/lib/env-template.js +66 -66
- package/bin/runners/lib/env.js +189 -189
- package/bin/runners/lib/extractors/client-calls.js +990 -990
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
- package/bin/runners/lib/extractors/fastify-routes.js +426 -426
- package/bin/runners/lib/extractors/index.js +363 -363
- package/bin/runners/lib/extractors/next-routes.js +524 -524
- package/bin/runners/lib/extractors/proof-graph.js +431 -431
- package/bin/runners/lib/extractors/route-matcher.js +451 -451
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
- package/bin/runners/lib/extractors/ui-bindings.js +547 -547
- package/bin/runners/lib/findings-schema.js +281 -281
- package/bin/runners/lib/firewall-prompt.js +50 -50
- package/bin/runners/lib/global-flags.js +213 -213
- package/bin/runners/lib/graph/graph-builder.js +265 -265
- package/bin/runners/lib/graph/html-renderer.js +413 -413
- package/bin/runners/lib/graph/index.js +32 -32
- package/bin/runners/lib/graph/runtime-collector.js +215 -215
- package/bin/runners/lib/graph/static-extractor.js +518 -518
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/interactive-menu.js +1496 -1496
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/meter.js +61 -61
- package/bin/runners/lib/missions/evidence.js +126 -126
- package/bin/runners/lib/patch.js +40 -40
- package/bin/runners/lib/permissions/auth-model.js +213 -213
- package/bin/runners/lib/permissions/idor-prover.js +205 -205
- package/bin/runners/lib/permissions/index.js +45 -45
- package/bin/runners/lib/permissions/matrix-builder.js +198 -198
- package/bin/runners/lib/pkgjson.js +28 -28
- package/bin/runners/lib/policy.js +295 -295
- package/bin/runners/lib/preflight.js +142 -142
- package/bin/runners/lib/reality/correlation-detectors.js +359 -359
- package/bin/runners/lib/reality/index.js +318 -318
- package/bin/runners/lib/reality/request-hashing.js +416 -416
- package/bin/runners/lib/reality/request-mapper.js +453 -453
- package/bin/runners/lib/reality/safety-rails.js +463 -463
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
- package/bin/runners/lib/reality/toast-detector.js +393 -393
- package/bin/runners/lib/reality-findings.js +84 -84
- package/bin/runners/lib/receipts.js +179 -179
- package/bin/runners/lib/redact.js +29 -29
- package/bin/runners/lib/replay/capsule-manager.js +154 -154
- package/bin/runners/lib/replay/index.js +263 -263
- package/bin/runners/lib/replay/player.js +348 -348
- package/bin/runners/lib/replay/recorder.js +331 -331
- package/bin/runners/lib/report-output.js +187 -187
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- package/bin/runners/lib/sandbox/index.js +59 -59
- package/bin/runners/lib/sandbox/proof-chain.js +399 -399
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
- package/bin/runners/lib/sandbox/worktree.js +174 -174
- package/bin/runners/lib/scan-output.js +525 -190
- package/bin/runners/lib/schema-validator.js +350 -350
- package/bin/runners/lib/schemas/contracts.schema.json +160 -160
- package/bin/runners/lib/schemas/finding.schema.json +100 -100
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
- package/bin/runners/lib/schemas/validator.js +438 -438
- package/bin/runners/lib/score-history.js +282 -282
- package/bin/runners/lib/share-pack.js +239 -239
- package/bin/runners/lib/snippets.js +67 -67
- package/bin/runners/lib/status-output.js +253 -253
- package/bin/runners/lib/terminal-ui.js +351 -271
- package/bin/runners/lib/upsell.js +510 -510
- package/bin/runners/lib/usage.js +153 -153
- package/bin/runners/lib/validate-patch.js +156 -156
- package/bin/runners/lib/verdict-engine.js +628 -628
- package/bin/runners/reality/engine.js +917 -917
- package/bin/runners/reality/flows.js +122 -122
- package/bin/runners/reality/report.js +378 -378
- package/bin/runners/reality/session.js +193 -193
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +8 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runScan.js +17 -1
- package/bin/runners/runTruth.js +15 -3
- package/mcp-server/tier-auth.js +4 -4
- package/mcp-server/tools/index.js +72 -72
- package/package.json +1 -1
|
@@ -1,417 +1,417 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Env Resolver v2
|
|
3
|
-
*
|
|
4
|
-
* Resolves environment variables for client calls, handles baseURL,
|
|
5
|
-
* and generates .env.template from usage.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
"use strict";
|
|
9
|
-
|
|
10
|
-
const fs = require("fs");
|
|
11
|
-
const path = require("path");
|
|
12
|
-
|
|
13
|
-
// =============================================================================
|
|
14
|
-
// ENV VAR PATTERNS
|
|
15
|
-
// =============================================================================
|
|
16
|
-
|
|
17
|
-
const ENV_PATTERNS = {
|
|
18
|
-
// Standard Node.js
|
|
19
|
-
processEnv: /process\.env\.(\w+)/g,
|
|
20
|
-
processEnvBracket: /process\.env\[['"](\w+)['"]\]/g,
|
|
21
|
-
|
|
22
|
-
// Vite
|
|
23
|
-
importMetaEnv: /import\.meta\.env\.(\w+)/g,
|
|
24
|
-
|
|
25
|
-
// Template literals
|
|
26
|
-
templateEnv: /\$\{process\.env\.(\w+)\}/g,
|
|
27
|
-
|
|
28
|
-
// Next.js public
|
|
29
|
-
nextPublic: /NEXT_PUBLIC_(\w+)/g,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const BASE_URL_VARS = [
|
|
33
|
-
"NEXT_PUBLIC_API_URL",
|
|
34
|
-
"NEXT_PUBLIC_BASE_URL",
|
|
35
|
-
"NEXT_PUBLIC_APP_URL",
|
|
36
|
-
"VITE_API_URL",
|
|
37
|
-
"VITE_BASE_URL",
|
|
38
|
-
"API_URL",
|
|
39
|
-
"API_BASE_URL",
|
|
40
|
-
"BASE_URL",
|
|
41
|
-
"APP_URL",
|
|
42
|
-
"BACKEND_URL",
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
const SENSITIVE_PATTERNS = [
|
|
46
|
-
/SECRET/i,
|
|
47
|
-
/KEY/i,
|
|
48
|
-
/TOKEN/i,
|
|
49
|
-
/PASSWORD/i,
|
|
50
|
-
/PRIVATE/i,
|
|
51
|
-
/CREDENTIAL/i,
|
|
52
|
-
/AUTH/i,
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
// =============================================================================
|
|
56
|
-
// BASE URL RESOLUTION
|
|
57
|
-
// =============================================================================
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Resolve base URL from env vars
|
|
61
|
-
*/
|
|
62
|
-
function resolveBaseUrl(options = {}) {
|
|
63
|
-
const { envVars = {}, defaultUrl = "" } = options;
|
|
64
|
-
|
|
65
|
-
// Try each known base URL var
|
|
66
|
-
for (const varName of BASE_URL_VARS) {
|
|
67
|
-
if (envVars[varName]) {
|
|
68
|
-
return normalizeBaseUrl(envVars[varName]);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Try to get from process.env (at runtime)
|
|
73
|
-
for (const varName of BASE_URL_VARS) {
|
|
74
|
-
if (process.env[varName]) {
|
|
75
|
-
return normalizeBaseUrl(process.env[varName]);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return defaultUrl;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Normalize base URL (remove trailing slash)
|
|
84
|
-
*/
|
|
85
|
-
function normalizeBaseUrl(url) {
|
|
86
|
-
if (!url) return "";
|
|
87
|
-
return url.replace(/\/+$/, "");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Resolve URL with base
|
|
92
|
-
*/
|
|
93
|
-
function resolveUrlWithBase(urlTemplate, baseUrl) {
|
|
94
|
-
if (!urlTemplate) return urlTemplate;
|
|
95
|
-
|
|
96
|
-
// Already absolute
|
|
97
|
-
if (urlTemplate.startsWith("http://") || urlTemplate.startsWith("https://")) {
|
|
98
|
-
return urlTemplate;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Ensure path starts with /
|
|
102
|
-
const path = urlTemplate.startsWith("/") ? urlTemplate : `/${urlTemplate}`;
|
|
103
|
-
|
|
104
|
-
if (baseUrl) {
|
|
105
|
-
return `${normalizeBaseUrl(baseUrl)}${path}`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return path;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Extract env var references from URL template
|
|
113
|
-
*/
|
|
114
|
-
function extractEnvVarsFromUrl(urlTemplate) {
|
|
115
|
-
const vars = [];
|
|
116
|
-
|
|
117
|
-
if (!urlTemplate) return vars;
|
|
118
|
-
|
|
119
|
-
// Check for env var patterns
|
|
120
|
-
let match;
|
|
121
|
-
|
|
122
|
-
for (const [name, pattern] of Object.entries(ENV_PATTERNS)) {
|
|
123
|
-
const regex = new RegExp(pattern.source, "g");
|
|
124
|
-
while ((match = regex.exec(urlTemplate)) !== null) {
|
|
125
|
-
vars.push({
|
|
126
|
-
name: match[1],
|
|
127
|
-
pattern: name,
|
|
128
|
-
fullMatch: match[0],
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return vars;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Substitute env vars in URL template
|
|
138
|
-
*/
|
|
139
|
-
function substituteEnvVars(urlTemplate, envVars = {}) {
|
|
140
|
-
if (!urlTemplate) return urlTemplate;
|
|
141
|
-
|
|
142
|
-
let result = urlTemplate;
|
|
143
|
-
|
|
144
|
-
// Substitute process.env.VAR
|
|
145
|
-
result = result.replace(/process\.env\.(\w+)/g, (_, name) => {
|
|
146
|
-
return envVars[name] || process.env[name] || `\${${name}}`;
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Substitute process.env["VAR"]
|
|
150
|
-
result = result.replace(/process\.env\[['"](\w+)['"]\]/g, (_, name) => {
|
|
151
|
-
return envVars[name] || process.env[name] || `\${${name}}`;
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Substitute import.meta.env.VAR
|
|
155
|
-
result = result.replace(/import\.meta\.env\.(\w+)/g, (_, name) => {
|
|
156
|
-
return envVars[name] || process.env[name] || `\${${name}}`;
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Substitute ${VAR}
|
|
160
|
-
result = result.replace(/\$\{(\w+)\}/g, (_, name) => {
|
|
161
|
-
return envVars[name] || process.env[name] || `\${${name}}`;
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
return result;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// =============================================================================
|
|
168
|
-
// CLIENT CALL BASE URL RESOLUTION
|
|
169
|
-
// =============================================================================
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Resolve base URL for client calls
|
|
173
|
-
*/
|
|
174
|
-
function resolveClientCallUrls(clientCalls, options = {}) {
|
|
175
|
-
const { baseUrl = "", envVars = {} } = options;
|
|
176
|
-
|
|
177
|
-
const resolvedBaseUrl = baseUrl || resolveBaseUrl({ envVars });
|
|
178
|
-
|
|
179
|
-
return clientCalls.map(call => {
|
|
180
|
-
// Extract env vars from URL
|
|
181
|
-
const envRefs = extractEnvVarsFromUrl(call.urlTemplate);
|
|
182
|
-
|
|
183
|
-
// Substitute env vars
|
|
184
|
-
const substituted = substituteEnvVars(call.urlTemplate, envVars);
|
|
185
|
-
|
|
186
|
-
// Resolve with base URL
|
|
187
|
-
const resolved = resolveUrlWithBase(substituted, resolvedBaseUrl);
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
...call,
|
|
191
|
-
urlResolved: resolved,
|
|
192
|
-
envRefs,
|
|
193
|
-
baseUrlUsed: resolvedBaseUrl || null,
|
|
194
|
-
};
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// =============================================================================
|
|
199
|
-
// ENV TEMPLATE GENERATION
|
|
200
|
-
// =============================================================================
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Generate .env.template from truthpack
|
|
204
|
-
*/
|
|
205
|
-
function generateEnvTemplate(truthpack, options = {}) {
|
|
206
|
-
const { includeExamples = true, groupByPrefix = true } = options;
|
|
207
|
-
|
|
208
|
-
const env = truthpack?.env || {};
|
|
209
|
-
const vars = env.vars || [];
|
|
210
|
-
const declared = new Set(env.declared || []);
|
|
211
|
-
|
|
212
|
-
// Group vars
|
|
213
|
-
const groups = {
|
|
214
|
-
next_public: [],
|
|
215
|
-
vite: [],
|
|
216
|
-
api: [],
|
|
217
|
-
auth: [],
|
|
218
|
-
database: [],
|
|
219
|
-
stripe: [],
|
|
220
|
-
other: [],
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
for (const v of vars) {
|
|
224
|
-
const name = v.name;
|
|
225
|
-
|
|
226
|
-
if (name.startsWith("NEXT_PUBLIC_")) {
|
|
227
|
-
groups.next_public.push(v);
|
|
228
|
-
} else if (name.startsWith("VITE_")) {
|
|
229
|
-
groups.vite.push(v);
|
|
230
|
-
} else if (name.includes("API") || name.includes("URL")) {
|
|
231
|
-
groups.api.push(v);
|
|
232
|
-
} else if (name.includes("AUTH") || name.includes("SESSION") || name.includes("JWT")) {
|
|
233
|
-
groups.auth.push(v);
|
|
234
|
-
} else if (name.includes("DATABASE") || name.includes("DB_") || name.includes("POSTGRES") || name.includes("MYSQL")) {
|
|
235
|
-
groups.database.push(v);
|
|
236
|
-
} else if (name.includes("STRIPE")) {
|
|
237
|
-
groups.stripe.push(v);
|
|
238
|
-
} else {
|
|
239
|
-
groups.other.push(v);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Generate template
|
|
244
|
-
const lines = [];
|
|
245
|
-
lines.push("# Environment Variables Template");
|
|
246
|
-
lines.push("# Generated by Vibecheck - https://github.com/guardiavault-oss/Vibecheck");
|
|
247
|
-
lines.push(`# Generated at: ${new Date().toISOString()}`);
|
|
248
|
-
lines.push("");
|
|
249
|
-
|
|
250
|
-
const groupLabels = {
|
|
251
|
-
next_public: "# Next.js Public Variables (exposed to browser)",
|
|
252
|
-
vite: "# Vite Public Variables",
|
|
253
|
-
api: "# API Configuration",
|
|
254
|
-
auth: "# Authentication",
|
|
255
|
-
database: "# Database",
|
|
256
|
-
stripe: "# Stripe",
|
|
257
|
-
other: "# Other",
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
for (const [group, groupVars] of Object.entries(groups)) {
|
|
261
|
-
if (groupVars.length === 0) continue;
|
|
262
|
-
|
|
263
|
-
if (groupByPrefix) {
|
|
264
|
-
lines.push(groupLabels[group] || `# ${group}`);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
for (const v of groupVars) {
|
|
268
|
-
const isSensitive = SENSITIVE_PATTERNS.some(p => p.test(v.name));
|
|
269
|
-
const example = includeExamples ? getExampleValue(v.name, isSensitive) : "";
|
|
270
|
-
const required = v.required || v.usageCount > 1 ? " # required" : "";
|
|
271
|
-
|
|
272
|
-
lines.push(`${v.name}=${example}${required}`);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
lines.push("");
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return lines.join("\n");
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Get example value for env var
|
|
283
|
-
*/
|
|
284
|
-
function getExampleValue(name, isSensitive = false) {
|
|
285
|
-
if (isSensitive) {
|
|
286
|
-
return "your-secret-here";
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Common patterns
|
|
290
|
-
if (name.includes("URL") || name.includes("HOST")) {
|
|
291
|
-
if (name.includes("API")) return "http://localhost:3001";
|
|
292
|
-
if (name.includes("DATABASE") || name.includes("DB")) return "postgresql://user:pass@localhost:5432/db";
|
|
293
|
-
return "http://localhost:3000";
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (name.includes("PORT")) return "3000";
|
|
297
|
-
if (name.includes("NODE_ENV")) return "development";
|
|
298
|
-
if (name.includes("DEBUG")) return "false";
|
|
299
|
-
if (name.includes("LOG_LEVEL")) return "info";
|
|
300
|
-
|
|
301
|
-
return "";
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Write .env.template to disk
|
|
306
|
-
*/
|
|
307
|
-
function writeEnvTemplate(repoRoot, template) {
|
|
308
|
-
const templatePath = path.join(repoRoot, ".env.template");
|
|
309
|
-
fs.writeFileSync(templatePath, template);
|
|
310
|
-
return templatePath;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Compare .env.template with current usage
|
|
315
|
-
*/
|
|
316
|
-
function compareEnvTemplate(repoRoot, truthpack) {
|
|
317
|
-
const templatePath = path.join(repoRoot, ".env.template");
|
|
318
|
-
const examplePath = path.join(repoRoot, ".env.example");
|
|
319
|
-
|
|
320
|
-
// Try both common names
|
|
321
|
-
let existingPath = null;
|
|
322
|
-
if (fs.existsSync(templatePath)) {
|
|
323
|
-
existingPath = templatePath;
|
|
324
|
-
} else if (fs.existsSync(examplePath)) {
|
|
325
|
-
existingPath = examplePath;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (!existingPath) {
|
|
329
|
-
return { exists: false, missing: [], extra: [] };
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const existingContent = fs.readFileSync(existingPath, "utf8");
|
|
333
|
-
const existingVars = new Set();
|
|
334
|
-
|
|
335
|
-
// Parse existing template
|
|
336
|
-
for (const line of existingContent.split("\n")) {
|
|
337
|
-
const match = line.match(/^([A-Z][A-Z0-9_]*)=/);
|
|
338
|
-
if (match) {
|
|
339
|
-
existingVars.add(match[1]);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Compare with truthpack
|
|
344
|
-
const usedVars = new Set((truthpack?.env?.vars || []).map(v => v.name));
|
|
345
|
-
|
|
346
|
-
const missing = [...usedVars].filter(v => !existingVars.has(v));
|
|
347
|
-
const extra = [...existingVars].filter(v => !usedVars.has(v));
|
|
348
|
-
|
|
349
|
-
return {
|
|
350
|
-
exists: true,
|
|
351
|
-
path: existingPath,
|
|
352
|
-
missing,
|
|
353
|
-
extra,
|
|
354
|
-
upToDate: missing.length === 0,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// =============================================================================
|
|
359
|
-
// ENV GAP DETECTION
|
|
360
|
-
// =============================================================================
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Find env gaps - required vars not declared
|
|
364
|
-
*/
|
|
365
|
-
function findEnvGaps(truthpack) {
|
|
366
|
-
const env = truthpack?.env || {};
|
|
367
|
-
const vars = env.vars || [];
|
|
368
|
-
const declared = new Set(env.declared || []);
|
|
369
|
-
|
|
370
|
-
const gaps = [];
|
|
371
|
-
|
|
372
|
-
for (const v of vars) {
|
|
373
|
-
const isRequired = v.required || v.usageCount > 1;
|
|
374
|
-
|
|
375
|
-
if (isRequired && !declared.has(v.name)) {
|
|
376
|
-
gaps.push({
|
|
377
|
-
name: v.name,
|
|
378
|
-
usageCount: v.usageCount || 1,
|
|
379
|
-
files: v.files || [],
|
|
380
|
-
severity: SENSITIVE_PATTERNS.some(p => p.test(v.name)) ? "BLOCK" : "WARN",
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return gaps;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// =============================================================================
|
|
389
|
-
// EXPORTS
|
|
390
|
-
// =============================================================================
|
|
391
|
-
|
|
392
|
-
module.exports = {
|
|
393
|
-
// Patterns
|
|
394
|
-
ENV_PATTERNS,
|
|
395
|
-
BASE_URL_VARS,
|
|
396
|
-
SENSITIVE_PATTERNS,
|
|
397
|
-
|
|
398
|
-
// Base URL
|
|
399
|
-
resolveBaseUrl,
|
|
400
|
-
normalizeBaseUrl,
|
|
401
|
-
resolveUrlWithBase,
|
|
402
|
-
|
|
403
|
-
// Env var extraction
|
|
404
|
-
extractEnvVarsFromUrl,
|
|
405
|
-
substituteEnvVars,
|
|
406
|
-
|
|
407
|
-
// Client call resolution
|
|
408
|
-
resolveClientCallUrls,
|
|
409
|
-
|
|
410
|
-
// Template generation
|
|
411
|
-
generateEnvTemplate,
|
|
412
|
-
writeEnvTemplate,
|
|
413
|
-
compareEnvTemplate,
|
|
414
|
-
|
|
415
|
-
// Gap detection
|
|
416
|
-
findEnvGaps,
|
|
417
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Env Resolver v2
|
|
3
|
+
*
|
|
4
|
+
* Resolves environment variables for client calls, handles baseURL,
|
|
5
|
+
* and generates .env.template from usage.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// ENV VAR PATTERNS
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
const ENV_PATTERNS = {
|
|
18
|
+
// Standard Node.js
|
|
19
|
+
processEnv: /process\.env\.(\w+)/g,
|
|
20
|
+
processEnvBracket: /process\.env\[['"](\w+)['"]\]/g,
|
|
21
|
+
|
|
22
|
+
// Vite
|
|
23
|
+
importMetaEnv: /import\.meta\.env\.(\w+)/g,
|
|
24
|
+
|
|
25
|
+
// Template literals
|
|
26
|
+
templateEnv: /\$\{process\.env\.(\w+)\}/g,
|
|
27
|
+
|
|
28
|
+
// Next.js public
|
|
29
|
+
nextPublic: /NEXT_PUBLIC_(\w+)/g,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const BASE_URL_VARS = [
|
|
33
|
+
"NEXT_PUBLIC_API_URL",
|
|
34
|
+
"NEXT_PUBLIC_BASE_URL",
|
|
35
|
+
"NEXT_PUBLIC_APP_URL",
|
|
36
|
+
"VITE_API_URL",
|
|
37
|
+
"VITE_BASE_URL",
|
|
38
|
+
"API_URL",
|
|
39
|
+
"API_BASE_URL",
|
|
40
|
+
"BASE_URL",
|
|
41
|
+
"APP_URL",
|
|
42
|
+
"BACKEND_URL",
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const SENSITIVE_PATTERNS = [
|
|
46
|
+
/SECRET/i,
|
|
47
|
+
/KEY/i,
|
|
48
|
+
/TOKEN/i,
|
|
49
|
+
/PASSWORD/i,
|
|
50
|
+
/PRIVATE/i,
|
|
51
|
+
/CREDENTIAL/i,
|
|
52
|
+
/AUTH/i,
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// =============================================================================
|
|
56
|
+
// BASE URL RESOLUTION
|
|
57
|
+
// =============================================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve base URL from env vars
|
|
61
|
+
*/
|
|
62
|
+
function resolveBaseUrl(options = {}) {
|
|
63
|
+
const { envVars = {}, defaultUrl = "" } = options;
|
|
64
|
+
|
|
65
|
+
// Try each known base URL var
|
|
66
|
+
for (const varName of BASE_URL_VARS) {
|
|
67
|
+
if (envVars[varName]) {
|
|
68
|
+
return normalizeBaseUrl(envVars[varName]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Try to get from process.env (at runtime)
|
|
73
|
+
for (const varName of BASE_URL_VARS) {
|
|
74
|
+
if (process.env[varName]) {
|
|
75
|
+
return normalizeBaseUrl(process.env[varName]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return defaultUrl;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Normalize base URL (remove trailing slash)
|
|
84
|
+
*/
|
|
85
|
+
function normalizeBaseUrl(url) {
|
|
86
|
+
if (!url) return "";
|
|
87
|
+
return url.replace(/\/+$/, "");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Resolve URL with base
|
|
92
|
+
*/
|
|
93
|
+
function resolveUrlWithBase(urlTemplate, baseUrl) {
|
|
94
|
+
if (!urlTemplate) return urlTemplate;
|
|
95
|
+
|
|
96
|
+
// Already absolute
|
|
97
|
+
if (urlTemplate.startsWith("http://") || urlTemplate.startsWith("https://")) {
|
|
98
|
+
return urlTemplate;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Ensure path starts with /
|
|
102
|
+
const path = urlTemplate.startsWith("/") ? urlTemplate : `/${urlTemplate}`;
|
|
103
|
+
|
|
104
|
+
if (baseUrl) {
|
|
105
|
+
return `${normalizeBaseUrl(baseUrl)}${path}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return path;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Extract env var references from URL template
|
|
113
|
+
*/
|
|
114
|
+
function extractEnvVarsFromUrl(urlTemplate) {
|
|
115
|
+
const vars = [];
|
|
116
|
+
|
|
117
|
+
if (!urlTemplate) return vars;
|
|
118
|
+
|
|
119
|
+
// Check for env var patterns
|
|
120
|
+
let match;
|
|
121
|
+
|
|
122
|
+
for (const [name, pattern] of Object.entries(ENV_PATTERNS)) {
|
|
123
|
+
const regex = new RegExp(pattern.source, "g");
|
|
124
|
+
while ((match = regex.exec(urlTemplate)) !== null) {
|
|
125
|
+
vars.push({
|
|
126
|
+
name: match[1],
|
|
127
|
+
pattern: name,
|
|
128
|
+
fullMatch: match[0],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return vars;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Substitute env vars in URL template
|
|
138
|
+
*/
|
|
139
|
+
function substituteEnvVars(urlTemplate, envVars = {}) {
|
|
140
|
+
if (!urlTemplate) return urlTemplate;
|
|
141
|
+
|
|
142
|
+
let result = urlTemplate;
|
|
143
|
+
|
|
144
|
+
// Substitute process.env.VAR
|
|
145
|
+
result = result.replace(/process\.env\.(\w+)/g, (_, name) => {
|
|
146
|
+
return envVars[name] || process.env[name] || `\${${name}}`;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Substitute process.env["VAR"]
|
|
150
|
+
result = result.replace(/process\.env\[['"](\w+)['"]\]/g, (_, name) => {
|
|
151
|
+
return envVars[name] || process.env[name] || `\${${name}}`;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Substitute import.meta.env.VAR
|
|
155
|
+
result = result.replace(/import\.meta\.env\.(\w+)/g, (_, name) => {
|
|
156
|
+
return envVars[name] || process.env[name] || `\${${name}}`;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Substitute ${VAR}
|
|
160
|
+
result = result.replace(/\$\{(\w+)\}/g, (_, name) => {
|
|
161
|
+
return envVars[name] || process.env[name] || `\${${name}}`;
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// =============================================================================
|
|
168
|
+
// CLIENT CALL BASE URL RESOLUTION
|
|
169
|
+
// =============================================================================
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Resolve base URL for client calls
|
|
173
|
+
*/
|
|
174
|
+
function resolveClientCallUrls(clientCalls, options = {}) {
|
|
175
|
+
const { baseUrl = "", envVars = {} } = options;
|
|
176
|
+
|
|
177
|
+
const resolvedBaseUrl = baseUrl || resolveBaseUrl({ envVars });
|
|
178
|
+
|
|
179
|
+
return clientCalls.map(call => {
|
|
180
|
+
// Extract env vars from URL
|
|
181
|
+
const envRefs = extractEnvVarsFromUrl(call.urlTemplate);
|
|
182
|
+
|
|
183
|
+
// Substitute env vars
|
|
184
|
+
const substituted = substituteEnvVars(call.urlTemplate, envVars);
|
|
185
|
+
|
|
186
|
+
// Resolve with base URL
|
|
187
|
+
const resolved = resolveUrlWithBase(substituted, resolvedBaseUrl);
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
...call,
|
|
191
|
+
urlResolved: resolved,
|
|
192
|
+
envRefs,
|
|
193
|
+
baseUrlUsed: resolvedBaseUrl || null,
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// =============================================================================
|
|
199
|
+
// ENV TEMPLATE GENERATION
|
|
200
|
+
// =============================================================================
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Generate .env.template from truthpack
|
|
204
|
+
*/
|
|
205
|
+
function generateEnvTemplate(truthpack, options = {}) {
|
|
206
|
+
const { includeExamples = true, groupByPrefix = true } = options;
|
|
207
|
+
|
|
208
|
+
const env = truthpack?.env || {};
|
|
209
|
+
const vars = env.vars || [];
|
|
210
|
+
const declared = new Set(env.declared || []);
|
|
211
|
+
|
|
212
|
+
// Group vars
|
|
213
|
+
const groups = {
|
|
214
|
+
next_public: [],
|
|
215
|
+
vite: [],
|
|
216
|
+
api: [],
|
|
217
|
+
auth: [],
|
|
218
|
+
database: [],
|
|
219
|
+
stripe: [],
|
|
220
|
+
other: [],
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
for (const v of vars) {
|
|
224
|
+
const name = v.name;
|
|
225
|
+
|
|
226
|
+
if (name.startsWith("NEXT_PUBLIC_")) {
|
|
227
|
+
groups.next_public.push(v);
|
|
228
|
+
} else if (name.startsWith("VITE_")) {
|
|
229
|
+
groups.vite.push(v);
|
|
230
|
+
} else if (name.includes("API") || name.includes("URL")) {
|
|
231
|
+
groups.api.push(v);
|
|
232
|
+
} else if (name.includes("AUTH") || name.includes("SESSION") || name.includes("JWT")) {
|
|
233
|
+
groups.auth.push(v);
|
|
234
|
+
} else if (name.includes("DATABASE") || name.includes("DB_") || name.includes("POSTGRES") || name.includes("MYSQL")) {
|
|
235
|
+
groups.database.push(v);
|
|
236
|
+
} else if (name.includes("STRIPE")) {
|
|
237
|
+
groups.stripe.push(v);
|
|
238
|
+
} else {
|
|
239
|
+
groups.other.push(v);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Generate template
|
|
244
|
+
const lines = [];
|
|
245
|
+
lines.push("# Environment Variables Template");
|
|
246
|
+
lines.push("# Generated by Vibecheck - https://github.com/guardiavault-oss/Vibecheck");
|
|
247
|
+
lines.push(`# Generated at: ${new Date().toISOString()}`);
|
|
248
|
+
lines.push("");
|
|
249
|
+
|
|
250
|
+
const groupLabels = {
|
|
251
|
+
next_public: "# Next.js Public Variables (exposed to browser)",
|
|
252
|
+
vite: "# Vite Public Variables",
|
|
253
|
+
api: "# API Configuration",
|
|
254
|
+
auth: "# Authentication",
|
|
255
|
+
database: "# Database",
|
|
256
|
+
stripe: "# Stripe",
|
|
257
|
+
other: "# Other",
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
for (const [group, groupVars] of Object.entries(groups)) {
|
|
261
|
+
if (groupVars.length === 0) continue;
|
|
262
|
+
|
|
263
|
+
if (groupByPrefix) {
|
|
264
|
+
lines.push(groupLabels[group] || `# ${group}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
for (const v of groupVars) {
|
|
268
|
+
const isSensitive = SENSITIVE_PATTERNS.some(p => p.test(v.name));
|
|
269
|
+
const example = includeExamples ? getExampleValue(v.name, isSensitive) : "";
|
|
270
|
+
const required = v.required || v.usageCount > 1 ? " # required" : "";
|
|
271
|
+
|
|
272
|
+
lines.push(`${v.name}=${example}${required}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
lines.push("");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return lines.join("\n");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get example value for env var
|
|
283
|
+
*/
|
|
284
|
+
function getExampleValue(name, isSensitive = false) {
|
|
285
|
+
if (isSensitive) {
|
|
286
|
+
return "your-secret-here";
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Common patterns
|
|
290
|
+
if (name.includes("URL") || name.includes("HOST")) {
|
|
291
|
+
if (name.includes("API")) return "http://localhost:3001";
|
|
292
|
+
if (name.includes("DATABASE") || name.includes("DB")) return "postgresql://user:pass@localhost:5432/db";
|
|
293
|
+
return "http://localhost:3000";
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (name.includes("PORT")) return "3000";
|
|
297
|
+
if (name.includes("NODE_ENV")) return "development";
|
|
298
|
+
if (name.includes("DEBUG")) return "false";
|
|
299
|
+
if (name.includes("LOG_LEVEL")) return "info";
|
|
300
|
+
|
|
301
|
+
return "";
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Write .env.template to disk
|
|
306
|
+
*/
|
|
307
|
+
function writeEnvTemplate(repoRoot, template) {
|
|
308
|
+
const templatePath = path.join(repoRoot, ".env.template");
|
|
309
|
+
fs.writeFileSync(templatePath, template);
|
|
310
|
+
return templatePath;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Compare .env.template with current usage
|
|
315
|
+
*/
|
|
316
|
+
function compareEnvTemplate(repoRoot, truthpack) {
|
|
317
|
+
const templatePath = path.join(repoRoot, ".env.template");
|
|
318
|
+
const examplePath = path.join(repoRoot, ".env.example");
|
|
319
|
+
|
|
320
|
+
// Try both common names
|
|
321
|
+
let existingPath = null;
|
|
322
|
+
if (fs.existsSync(templatePath)) {
|
|
323
|
+
existingPath = templatePath;
|
|
324
|
+
} else if (fs.existsSync(examplePath)) {
|
|
325
|
+
existingPath = examplePath;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (!existingPath) {
|
|
329
|
+
return { exists: false, missing: [], extra: [] };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const existingContent = fs.readFileSync(existingPath, "utf8");
|
|
333
|
+
const existingVars = new Set();
|
|
334
|
+
|
|
335
|
+
// Parse existing template
|
|
336
|
+
for (const line of existingContent.split("\n")) {
|
|
337
|
+
const match = line.match(/^([A-Z][A-Z0-9_]*)=/);
|
|
338
|
+
if (match) {
|
|
339
|
+
existingVars.add(match[1]);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Compare with truthpack
|
|
344
|
+
const usedVars = new Set((truthpack?.env?.vars || []).map(v => v.name));
|
|
345
|
+
|
|
346
|
+
const missing = [...usedVars].filter(v => !existingVars.has(v));
|
|
347
|
+
const extra = [...existingVars].filter(v => !usedVars.has(v));
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
exists: true,
|
|
351
|
+
path: existingPath,
|
|
352
|
+
missing,
|
|
353
|
+
extra,
|
|
354
|
+
upToDate: missing.length === 0,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// =============================================================================
|
|
359
|
+
// ENV GAP DETECTION
|
|
360
|
+
// =============================================================================
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Find env gaps - required vars not declared
|
|
364
|
+
*/
|
|
365
|
+
function findEnvGaps(truthpack) {
|
|
366
|
+
const env = truthpack?.env || {};
|
|
367
|
+
const vars = env.vars || [];
|
|
368
|
+
const declared = new Set(env.declared || []);
|
|
369
|
+
|
|
370
|
+
const gaps = [];
|
|
371
|
+
|
|
372
|
+
for (const v of vars) {
|
|
373
|
+
const isRequired = v.required || v.usageCount > 1;
|
|
374
|
+
|
|
375
|
+
if (isRequired && !declared.has(v.name)) {
|
|
376
|
+
gaps.push({
|
|
377
|
+
name: v.name,
|
|
378
|
+
usageCount: v.usageCount || 1,
|
|
379
|
+
files: v.files || [],
|
|
380
|
+
severity: SENSITIVE_PATTERNS.some(p => p.test(v.name)) ? "BLOCK" : "WARN",
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return gaps;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// =============================================================================
|
|
389
|
+
// EXPORTS
|
|
390
|
+
// =============================================================================
|
|
391
|
+
|
|
392
|
+
module.exports = {
|
|
393
|
+
// Patterns
|
|
394
|
+
ENV_PATTERNS,
|
|
395
|
+
BASE_URL_VARS,
|
|
396
|
+
SENSITIVE_PATTERNS,
|
|
397
|
+
|
|
398
|
+
// Base URL
|
|
399
|
+
resolveBaseUrl,
|
|
400
|
+
normalizeBaseUrl,
|
|
401
|
+
resolveUrlWithBase,
|
|
402
|
+
|
|
403
|
+
// Env var extraction
|
|
404
|
+
extractEnvVarsFromUrl,
|
|
405
|
+
substituteEnvVars,
|
|
406
|
+
|
|
407
|
+
// Client call resolution
|
|
408
|
+
resolveClientCallUrls,
|
|
409
|
+
|
|
410
|
+
// Template generation
|
|
411
|
+
generateEnvTemplate,
|
|
412
|
+
writeEnvTemplate,
|
|
413
|
+
compareEnvTemplate,
|
|
414
|
+
|
|
415
|
+
// Gap detection
|
|
416
|
+
findEnvGaps,
|
|
417
|
+
};
|