@vibecheckai/cli 3.5.0 → 3.5.2
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/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- 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 +73 -373
- 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/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Environment Variables Engine
|
|
3
|
-
* Detects:
|
|
4
|
-
* - Missing environment variable validation
|
|
5
|
-
* - Insecure default values
|
|
6
|
-
* - Exposed secrets in .env files committed to git
|
|
7
|
-
* - Missing required env vars
|
|
8
|
-
* - Type coercion issues
|
|
9
|
-
* - Environment-specific configuration problems
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const { getAST } = require("./ast-cache");
|
|
13
|
-
const traverse = require("@babel/traverse").default;
|
|
14
|
-
const t = require("@babel/types");
|
|
15
|
-
const fs = require("fs");
|
|
16
|
-
const path = require("path");
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Required environment variables for common frameworks
|
|
20
|
-
*/
|
|
21
|
-
const COMMON_REQUIRED_VARS = {
|
|
22
|
-
nextjs: ["NEXTAUTH_SECRET", "NEXTAUTH_URL"],
|
|
23
|
-
database: ["DATABASE_URL"],
|
|
24
|
-
stripe: ["STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET"],
|
|
25
|
-
auth: ["JWT_SECRET", "SESSION_SECRET"],
|
|
26
|
-
aws: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"],
|
|
27
|
-
email: ["SMTP_HOST", "SMTP_USER", "SMTP_PASSWORD"],
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Patterns that suggest sensitive env vars
|
|
32
|
-
*/
|
|
33
|
-
const SENSITIVE_VAR_PATTERNS = [
|
|
34
|
-
/SECRET/i,
|
|
35
|
-
/PASSWORD/i,
|
|
36
|
-
/TOKEN/i,
|
|
37
|
-
/API_KEY/i,
|
|
38
|
-
/PRIVATE/i,
|
|
39
|
-
/CREDENTIAL/i,
|
|
40
|
-
/AUTH/i,
|
|
41
|
-
/KEY$/i,
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Insecure default values
|
|
46
|
-
*/
|
|
47
|
-
const INSECURE_DEFAULTS = [
|
|
48
|
-
{ pattern: /^(password|secret|test|demo|admin|123|abc)$/i, message: "Insecure default value" },
|
|
49
|
-
{ pattern: /^(localhost|127\.0\.0\.1|0\.0\.0\.0)$/, message: "Development-only default" },
|
|
50
|
-
{ pattern: /^(true|false)$/, message: "Boolean default may hide missing config" },
|
|
51
|
-
{ pattern: /^your[-_]?/i, message: "Placeholder default value" },
|
|
52
|
-
{ pattern: /^change[-_]?me/i, message: "Placeholder default value" },
|
|
53
|
-
{ pattern: /^xxx+$/i, message: "Placeholder default value" },
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Check if a variable name looks sensitive
|
|
58
|
-
*/
|
|
59
|
-
function isSensitiveVar(varName) {
|
|
60
|
-
return SENSITIVE_VAR_PATTERNS.some(p => p.test(varName));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Check if a default value is insecure
|
|
65
|
-
*/
|
|
66
|
-
function checkInsecureDefault(value) {
|
|
67
|
-
for (const { pattern, message } of INSECURE_DEFAULTS) {
|
|
68
|
-
if (pattern.test(value)) {
|
|
69
|
-
return message;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Parse .env file content
|
|
77
|
-
*/
|
|
78
|
-
function parseEnvFile(content) {
|
|
79
|
-
const vars = new Map();
|
|
80
|
-
const lines = content.split("\n");
|
|
81
|
-
|
|
82
|
-
for (let i = 0; i < lines.length; i++) {
|
|
83
|
-
const line = lines[i].trim();
|
|
84
|
-
|
|
85
|
-
// Skip comments and empty lines
|
|
86
|
-
if (!line || line.startsWith("#")) continue;
|
|
87
|
-
|
|
88
|
-
// Parse key=value
|
|
89
|
-
const match = line.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/i);
|
|
90
|
-
if (match) {
|
|
91
|
-
const [, key, rawValue] = match;
|
|
92
|
-
// Remove quotes
|
|
93
|
-
let value = rawValue.trim();
|
|
94
|
-
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
95
|
-
(value.startsWith("'") && value.endsWith("'"))) {
|
|
96
|
-
value = value.slice(1, -1);
|
|
97
|
-
}
|
|
98
|
-
vars.set(key, { value, line: i + 1 });
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return vars;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Analyze .env file for issues
|
|
107
|
-
*/
|
|
108
|
-
function analyzeEnvFile(content, filePath) {
|
|
109
|
-
const findings = [];
|
|
110
|
-
const vars = parseEnvFile(content);
|
|
111
|
-
const lines = content.split("\n");
|
|
112
|
-
|
|
113
|
-
for (const [key, { value, line }] of vars) {
|
|
114
|
-
// Check for sensitive vars with actual values (not placeholders)
|
|
115
|
-
if (isSensitiveVar(key) && value && value.length > 5) {
|
|
116
|
-
// Check if it looks like a real secret (not a placeholder)
|
|
117
|
-
const isPlaceholder = /^(your|change|xxx|placeholder|example|sample)/i.test(value);
|
|
118
|
-
|
|
119
|
-
if (!isPlaceholder) {
|
|
120
|
-
findings.push({
|
|
121
|
-
type: "exposed_secret",
|
|
122
|
-
severity: "BLOCK",
|
|
123
|
-
category: "Security",
|
|
124
|
-
file: filePath,
|
|
125
|
-
line,
|
|
126
|
-
column: 0,
|
|
127
|
-
title: `Potential secret in env file: ${key}`,
|
|
128
|
-
message: `The variable '${key}' appears to contain a real secret. Ensure this file is in .gitignore.`,
|
|
129
|
-
codeSnippet: `${key}=***`,
|
|
130
|
-
confidence: "high",
|
|
131
|
-
fixHint: "Add .env to .gitignore and use .env.example with placeholder values",
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Check for insecure defaults
|
|
137
|
-
const insecureMessage = checkInsecureDefault(value);
|
|
138
|
-
if (insecureMessage && isSensitiveVar(key)) {
|
|
139
|
-
findings.push({
|
|
140
|
-
type: "insecure_default",
|
|
141
|
-
severity: "WARN",
|
|
142
|
-
category: "Security",
|
|
143
|
-
file: filePath,
|
|
144
|
-
line,
|
|
145
|
-
column: 0,
|
|
146
|
-
title: `${insecureMessage}: ${key}`,
|
|
147
|
-
message: `The variable '${key}' has an insecure or placeholder default value.`,
|
|
148
|
-
codeSnippet: lines[line - 1]?.trim(),
|
|
149
|
-
confidence: "med",
|
|
150
|
-
fixHint: "Use a secure, randomly generated value",
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Check for empty values on required-looking vars
|
|
155
|
-
if (!value && isSensitiveVar(key)) {
|
|
156
|
-
findings.push({
|
|
157
|
-
type: "empty_env_var",
|
|
158
|
-
severity: "INFO",
|
|
159
|
-
category: "Configuration",
|
|
160
|
-
file: filePath,
|
|
161
|
-
line,
|
|
162
|
-
column: 0,
|
|
163
|
-
title: `Empty value for: ${key}`,
|
|
164
|
-
message: `The variable '${key}' is defined but has no value.`,
|
|
165
|
-
codeSnippet: lines[line - 1]?.trim(),
|
|
166
|
-
confidence: "low",
|
|
167
|
-
fixHint: "Set a value or remove if not needed",
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return findings;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Analyze code for environment variable usage issues
|
|
177
|
-
*/
|
|
178
|
-
function analyzeEnvUsage(code, filePath) {
|
|
179
|
-
const findings = [];
|
|
180
|
-
const ast = getAST(code, filePath);
|
|
181
|
-
if (!ast) return findings;
|
|
182
|
-
|
|
183
|
-
const lines = code.split("\n");
|
|
184
|
-
const envVarsUsed = new Map(); // varName -> locations
|
|
185
|
-
const envVarsValidated = new Set();
|
|
186
|
-
|
|
187
|
-
traverse(ast, {
|
|
188
|
-
MemberExpression(path) {
|
|
189
|
-
const node = path.node;
|
|
190
|
-
const loc = node.loc?.start;
|
|
191
|
-
if (!loc) return;
|
|
192
|
-
|
|
193
|
-
// Check for process.env.VAR_NAME
|
|
194
|
-
if (t.isMemberExpression(node.object) &&
|
|
195
|
-
t.isIdentifier(node.object.object, { name: "process" }) &&
|
|
196
|
-
t.isIdentifier(node.object.property, { name: "env" })) {
|
|
197
|
-
|
|
198
|
-
let varName = null;
|
|
199
|
-
if (t.isIdentifier(node.property)) {
|
|
200
|
-
varName = node.property.name;
|
|
201
|
-
} else if (t.isStringLiteral(node.property)) {
|
|
202
|
-
varName = node.property.value;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (varName) {
|
|
206
|
-
if (!envVarsUsed.has(varName)) {
|
|
207
|
-
envVarsUsed.set(varName, []);
|
|
208
|
-
}
|
|
209
|
-
envVarsUsed.get(varName).push({ line: loc.line, path });
|
|
210
|
-
|
|
211
|
-
// Check for default value usage
|
|
212
|
-
const parent = path.parentPath;
|
|
213
|
-
|
|
214
|
-
// Check for || or ?? with default
|
|
215
|
-
if (parent.isLogicalExpression() &&
|
|
216
|
-
(parent.node.operator === "||" || parent.node.operator === "??")) {
|
|
217
|
-
const right = parent.node.right;
|
|
218
|
-
|
|
219
|
-
// Check if default is insecure
|
|
220
|
-
if (t.isStringLiteral(right)) {
|
|
221
|
-
const defaultValue = right.value;
|
|
222
|
-
const insecureMessage = checkInsecureDefault(defaultValue);
|
|
223
|
-
|
|
224
|
-
if (insecureMessage && isSensitiveVar(varName)) {
|
|
225
|
-
findings.push({
|
|
226
|
-
type: "insecure_env_default",
|
|
227
|
-
severity: "WARN",
|
|
228
|
-
category: "Security",
|
|
229
|
-
file: filePath,
|
|
230
|
-
line: loc.line,
|
|
231
|
-
column: loc.column,
|
|
232
|
-
title: `Insecure default for ${varName}`,
|
|
233
|
-
message: `${insecureMessage} for sensitive variable.`,
|
|
234
|
-
codeSnippet: lines[loc.line - 1]?.trim(),
|
|
235
|
-
confidence: "med",
|
|
236
|
-
fixHint: "Throw error if required env var is missing instead of using default",
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Check for validation patterns
|
|
243
|
-
if (parent.isIfStatement() ||
|
|
244
|
-
parent.isConditionalExpression() ||
|
|
245
|
-
parent.isLogicalExpression()) {
|
|
246
|
-
envVarsValidated.add(varName);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Check for import.meta.env.VAR_NAME (Vite)
|
|
252
|
-
if (t.isMemberExpression(node.object) &&
|
|
253
|
-
t.isMetaProperty(node.object.object) &&
|
|
254
|
-
t.isIdentifier(node.object.property, { name: "env" })) {
|
|
255
|
-
|
|
256
|
-
let varName = null;
|
|
257
|
-
if (t.isIdentifier(node.property)) {
|
|
258
|
-
varName = node.property.name;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (varName) {
|
|
262
|
-
if (!envVarsUsed.has(varName)) {
|
|
263
|
-
envVarsUsed.set(varName, []);
|
|
264
|
-
}
|
|
265
|
-
envVarsUsed.get(varName).push({ line: loc.line, path });
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
},
|
|
269
|
-
|
|
270
|
-
// Check for zod/validation schemas
|
|
271
|
-
CallExpression(path) {
|
|
272
|
-
const callee = path.node.callee;
|
|
273
|
-
|
|
274
|
-
// z.object({ VAR: z.string() })
|
|
275
|
-
if (t.isMemberExpression(callee) &&
|
|
276
|
-
t.isIdentifier(callee.property, { name: "object" })) {
|
|
277
|
-
const args = path.node.arguments;
|
|
278
|
-
if (args[0] && t.isObjectExpression(args[0])) {
|
|
279
|
-
for (const prop of args[0].properties) {
|
|
280
|
-
if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
|
|
281
|
-
envVarsValidated.add(prop.key.name);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
},
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
// Check for unvalidated sensitive env vars
|
|
290
|
-
for (const [varName, usages] of envVarsUsed) {
|
|
291
|
-
if (isSensitiveVar(varName) && !envVarsValidated.has(varName)) {
|
|
292
|
-
const firstUsage = usages[0];
|
|
293
|
-
|
|
294
|
-
findings.push({
|
|
295
|
-
type: "unvalidated_env_var",
|
|
296
|
-
severity: "INFO",
|
|
297
|
-
category: "Configuration",
|
|
298
|
-
file: filePath,
|
|
299
|
-
line: firstUsage.line,
|
|
300
|
-
column: 0,
|
|
301
|
-
title: `Unvalidated env var: ${varName}`,
|
|
302
|
-
message: `Sensitive variable '${varName}' is used without validation. Consider using zod or a similar validation library.`,
|
|
303
|
-
codeSnippet: lines[firstUsage.line - 1]?.trim(),
|
|
304
|
-
confidence: "low",
|
|
305
|
-
fixHint: "Add runtime validation: if (!process.env.VAR) throw new Error('Missing VAR')",
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Check for type coercion issues
|
|
311
|
-
traverse(ast, {
|
|
312
|
-
BinaryExpression(path) {
|
|
313
|
-
const node = path.node;
|
|
314
|
-
const loc = node.loc?.start;
|
|
315
|
-
if (!loc) return;
|
|
316
|
-
|
|
317
|
-
// Check for process.env.VAR === true/false (always false)
|
|
318
|
-
if ((node.operator === "===" || node.operator === "==") &&
|
|
319
|
-
(t.isBooleanLiteral(node.right) || t.isBooleanLiteral(node.left))) {
|
|
320
|
-
|
|
321
|
-
const envSide = t.isBooleanLiteral(node.right) ? node.left : node.right;
|
|
322
|
-
|
|
323
|
-
if (t.isMemberExpression(envSide) &&
|
|
324
|
-
t.isMemberExpression(envSide.object) &&
|
|
325
|
-
t.isIdentifier(envSide.object.object, { name: "process" }) &&
|
|
326
|
-
t.isIdentifier(envSide.object.property, { name: "env" })) {
|
|
327
|
-
|
|
328
|
-
findings.push({
|
|
329
|
-
type: "env_type_coercion",
|
|
330
|
-
severity: "WARN",
|
|
331
|
-
category: "CodeQuality",
|
|
332
|
-
file: filePath,
|
|
333
|
-
line: loc.line,
|
|
334
|
-
column: loc.column,
|
|
335
|
-
title: "Env var compared to boolean",
|
|
336
|
-
message: "Environment variables are always strings. This comparison may not work as expected.",
|
|
337
|
-
codeSnippet: lines[loc.line - 1]?.trim(),
|
|
338
|
-
confidence: "high",
|
|
339
|
-
fixHint: "Use process.env.VAR === 'true' instead",
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Check for process.env.VAR === number (always false)
|
|
345
|
-
if ((node.operator === "===" || node.operator === "==") &&
|
|
346
|
-
(t.isNumericLiteral(node.right) || t.isNumericLiteral(node.left))) {
|
|
347
|
-
|
|
348
|
-
const envSide = t.isNumericLiteral(node.right) ? node.left : node.right;
|
|
349
|
-
|
|
350
|
-
if (t.isMemberExpression(envSide) &&
|
|
351
|
-
t.isMemberExpression(envSide.object) &&
|
|
352
|
-
t.isIdentifier(envSide.object.object, { name: "process" }) &&
|
|
353
|
-
t.isIdentifier(envSide.object.property, { name: "env" })) {
|
|
354
|
-
|
|
355
|
-
findings.push({
|
|
356
|
-
type: "env_type_coercion",
|
|
357
|
-
severity: "WARN",
|
|
358
|
-
category: "CodeQuality",
|
|
359
|
-
file: filePath,
|
|
360
|
-
line: loc.line,
|
|
361
|
-
column: loc.column,
|
|
362
|
-
title: "Env var compared to number",
|
|
363
|
-
message: "Environment variables are always strings. Use Number() or parseInt() first.",
|
|
364
|
-
codeSnippet: lines[loc.line - 1]?.trim(),
|
|
365
|
-
confidence: "high",
|
|
366
|
-
fixHint: "Use Number(process.env.VAR) === 123 instead",
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
},
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
return findings;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Check for missing env files or misconfigurations
|
|
378
|
-
*/
|
|
379
|
-
function analyzeEnvSetup(repoRoot) {
|
|
380
|
-
const findings = [];
|
|
381
|
-
|
|
382
|
-
// Check if .env is in .gitignore
|
|
383
|
-
const gitignorePath = path.join(repoRoot, ".gitignore");
|
|
384
|
-
if (fs.existsSync(gitignorePath)) {
|
|
385
|
-
const gitignore = fs.readFileSync(gitignorePath, "utf8");
|
|
386
|
-
const hasEnvIgnore = /^\.env$/m.test(gitignore) ||
|
|
387
|
-
/^\*\.env$/m.test(gitignore) ||
|
|
388
|
-
/^\.env\.\*$/m.test(gitignore);
|
|
389
|
-
|
|
390
|
-
if (!hasEnvIgnore) {
|
|
391
|
-
// Check if .env exists
|
|
392
|
-
const envPath = path.join(repoRoot, ".env");
|
|
393
|
-
if (fs.existsSync(envPath)) {
|
|
394
|
-
findings.push({
|
|
395
|
-
type: "env_not_gitignored",
|
|
396
|
-
severity: "BLOCK",
|
|
397
|
-
category: "Security",
|
|
398
|
-
file: ".gitignore",
|
|
399
|
-
line: 1,
|
|
400
|
-
column: 0,
|
|
401
|
-
title: ".env file may be committed to git",
|
|
402
|
-
message: ".env is not in .gitignore but exists. Secrets may be exposed.",
|
|
403
|
-
codeSnippet: "",
|
|
404
|
-
confidence: "high",
|
|
405
|
-
fixHint: "Add '.env' and '.env.*' to .gitignore",
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Check for .env.example or .env.template
|
|
412
|
-
const hasExample = fs.existsSync(path.join(repoRoot, ".env.example")) ||
|
|
413
|
-
fs.existsSync(path.join(repoRoot, ".env.template")) ||
|
|
414
|
-
fs.existsSync(path.join(repoRoot, ".env.sample"));
|
|
415
|
-
|
|
416
|
-
if (!hasExample && fs.existsSync(path.join(repoRoot, ".env"))) {
|
|
417
|
-
findings.push({
|
|
418
|
-
type: "missing_env_example",
|
|
419
|
-
severity: "INFO",
|
|
420
|
-
category: "Configuration",
|
|
421
|
-
file: ".env",
|
|
422
|
-
line: 1,
|
|
423
|
-
column: 0,
|
|
424
|
-
title: "Missing .env.example file",
|
|
425
|
-
message: "Consider creating .env.example with placeholder values for other developers.",
|
|
426
|
-
codeSnippet: "",
|
|
427
|
-
confidence: "low",
|
|
428
|
-
fixHint: "Create .env.example with YOUR_VAR=your_value_here format",
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return findings;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Main analysis function
|
|
437
|
-
*/
|
|
438
|
-
function analyzeEnvVariables(code, filePath) {
|
|
439
|
-
// Check if it's an env file
|
|
440
|
-
if (filePath.includes(".env")) {
|
|
441
|
-
return analyzeEnvFile(code, filePath);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Otherwise analyze code for env usage
|
|
445
|
-
return analyzeEnvUsage(code, filePath);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
module.exports = {
|
|
449
|
-
analyzeEnvVariables,
|
|
450
|
-
analyzeEnvFile,
|
|
451
|
-
analyzeEnvUsage,
|
|
452
|
-
analyzeEnvSetup,
|
|
453
|
-
parseEnvFile,
|
|
454
|
-
isSensitiveVar,
|
|
455
|
-
checkInsecureDefault,
|
|
456
|
-
COMMON_REQUIRED_VARS,
|
|
457
|
-
SENSITIVE_VAR_PATTERNS,
|
|
458
|
-
};
|