@vibecheckai/cli 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/registry.js +389 -269
- package/bin/runners/cli-utils.js +2 -33
- package/bin/runners/context/generators/cursor.js +49 -2
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +849 -0
- package/bin/runners/lib/analyzers.js +599 -142
- package/bin/runners/lib/audit-logger.js +532 -0
- package/bin/runners/lib/authority/authorities/architecture.js +364 -0
- package/bin/runners/lib/authority/authorities/compliance.js +341 -0
- package/bin/runners/lib/authority/authorities/human.js +343 -0
- package/bin/runners/lib/authority/authorities/quality.js +420 -0
- package/bin/runners/lib/authority/authorities/security.js +228 -0
- package/bin/runners/lib/authority/index.js +293 -0
- package/bin/runners/lib/authority-badge.js +425 -425
- package/bin/runners/lib/bundle/bundle-intelligence.js +846 -0
- package/bin/runners/lib/cli-charts.js +368 -0
- package/bin/runners/lib/cli-config-display.js +405 -0
- package/bin/runners/lib/cli-demo.js +275 -0
- package/bin/runners/lib/cli-errors.js +438 -0
- package/bin/runners/lib/cli-help-formatter.js +439 -0
- package/bin/runners/lib/cli-interactive-menu.js +509 -0
- package/bin/runners/lib/cli-prompts.js +441 -0
- package/bin/runners/lib/cli-scan-cards.js +362 -0
- package/bin/runners/lib/compliance-reporter.js +710 -0
- package/bin/runners/lib/conductor/index.js +671 -0
- package/bin/runners/lib/easy/README.md +123 -0
- package/bin/runners/lib/easy/index.js +140 -0
- package/bin/runners/lib/easy/interactive-wizard.js +788 -0
- package/bin/runners/lib/easy/one-click-firewall.js +564 -0
- package/bin/runners/lib/easy/zero-config-reality.js +714 -0
- package/bin/runners/lib/engines/accessibility-engine.js +218 -18
- package/bin/runners/lib/engines/api-consistency-engine.js +335 -30
- package/bin/runners/lib/engines/async-patterns-engine.js +444 -0
- package/bin/runners/lib/engines/bundle-size-engine.js +433 -0
- package/bin/runners/lib/engines/confidence-scoring.js +276 -0
- package/bin/runners/lib/engines/context-detection.js +264 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +292 -27
- package/bin/runners/lib/engines/database-patterns-engine.js +429 -0
- package/bin/runners/lib/engines/duplicate-code-engine.js +354 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +127 -17
- package/bin/runners/lib/engines/env-variables-engine.js +458 -0
- package/bin/runners/lib/engines/error-handling-engine.js +437 -0
- package/bin/runners/lib/engines/false-positive-prevention.js +630 -0
- package/bin/runners/lib/engines/framework-adapters/index.js +607 -0
- package/bin/runners/lib/engines/framework-detection.js +508 -0
- package/bin/runners/lib/engines/import-order-engine.js +429 -0
- package/bin/runners/lib/engines/mock-data-engine.js +53 -10
- package/bin/runners/lib/engines/naming-conventions-engine.js +544 -0
- package/bin/runners/lib/engines/noise-reduction-engine.js +452 -0
- package/bin/runners/lib/engines/orchestrator.js +334 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +176 -36
- package/bin/runners/lib/engines/react-patterns-engine.js +457 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +382 -54
- package/bin/runners/lib/engines/type-aware-engine.js +263 -39
- package/bin/runners/lib/engines/vibecheck-engines/index.js +122 -13
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +806 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +373 -73
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +577 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +543 -0
- package/bin/runners/lib/engines/vibecheck-engines.js +514 -0
- package/bin/runners/lib/enhanced-features/index.js +305 -0
- package/bin/runners/lib/enhanced-output.js +631 -0
- package/bin/runners/lib/enterprise.js +300 -0
- package/bin/runners/lib/entitlements-v2.js +161 -478
- package/bin/runners/lib/firewall/command-validator.js +351 -0
- package/bin/runners/lib/firewall/config.js +341 -0
- package/bin/runners/lib/firewall/content-validator.js +519 -0
- package/bin/runners/lib/firewall/index.js +101 -0
- package/bin/runners/lib/firewall/path-validator.js +256 -0
- package/bin/runners/lib/html-proof-report.js +350 -700
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +817 -0
- package/bin/runners/lib/mcp-utils.js +425 -0
- package/bin/runners/lib/missions/plan.js +46 -6
- package/bin/runners/lib/missions/templates.js +232 -0
- package/bin/runners/lib/output/index.js +1022 -0
- package/bin/runners/lib/policy-engine.js +652 -0
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +333 -0
- package/bin/runners/lib/polish/autofix/async-handlers.js +273 -0
- package/bin/runners/lib/polish/autofix/dead-code.js +280 -0
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +344 -0
- package/bin/runners/lib/polish/autofix/index.js +200 -0
- package/bin/runners/lib/polish/autofix/remove-consoles.js +209 -0
- package/bin/runners/lib/polish/autofix/strengthen-types.js +245 -0
- package/bin/runners/lib/polish/backend-checks.js +148 -0
- package/bin/runners/lib/polish/documentation-checks.js +111 -0
- package/bin/runners/lib/polish/frontend-checks.js +168 -0
- package/bin/runners/lib/polish/index.js +71 -0
- package/bin/runners/lib/polish/infrastructure-checks.js +131 -0
- package/bin/runners/lib/polish/library-detection.js +175 -0
- package/bin/runners/lib/polish/performance-checks.js +100 -0
- package/bin/runners/lib/polish/security-checks.js +148 -0
- package/bin/runners/lib/polish/utils.js +203 -0
- package/bin/runners/lib/prompt-builder.js +540 -0
- package/bin/runners/lib/proof-certificate.js +634 -0
- package/bin/runners/lib/reality/accessibility-audit.js +946 -0
- package/bin/runners/lib/reality/api-contract-validator.js +1012 -0
- package/bin/runners/lib/reality/chaos-engineering.js +1084 -0
- package/bin/runners/lib/reality/performance-tracker.js +1077 -0
- package/bin/runners/lib/reality/scenario-generator.js +1404 -0
- package/bin/runners/lib/reality/visual-regression.js +852 -0
- package/bin/runners/lib/reality-profiler.js +717 -0
- package/bin/runners/lib/replay/flight-recorder-viewer.js +1160 -0
- package/bin/runners/lib/review/ai-code-review.js +832 -0
- package/bin/runners/lib/rules/custom-rule-engine.js +985 -0
- package/bin/runners/lib/sbom-generator.js +641 -0
- package/bin/runners/lib/scan-output-enhanced.js +512 -0
- package/bin/runners/lib/scan-output.js +65 -19
- package/bin/runners/lib/security/owasp-scanner.js +939 -0
- package/bin/runners/lib/ship-output.js +18 -25
- package/bin/runners/lib/terminal-ui.js +113 -1
- package/bin/runners/lib/unified-cli-output.js +603 -430
- package/bin/runners/lib/upsell.js +90 -338
- package/bin/runners/lib/validators/contract-validator.js +283 -0
- package/bin/runners/lib/validators/dead-export-detector.js +279 -0
- package/bin/runners/lib/validators/dep-audit.js +245 -0
- package/bin/runners/lib/validators/env-validator.js +319 -0
- package/bin/runners/lib/validators/index.js +120 -0
- package/bin/runners/lib/validators/license-checker.js +252 -0
- package/bin/runners/lib/validators/route-validator.js +290 -0
- package/bin/runners/runAIAgent.js +5 -10
- package/bin/runners/runAgent.js +3 -0
- package/bin/runners/runApprove.js +1233 -1200
- package/bin/runners/runAuth.js +22 -1
- package/bin/runners/runAuthority.js +528 -0
- package/bin/runners/runCheckpoint.js +4 -24
- package/bin/runners/runClassify.js +862 -859
- package/bin/runners/runConductor.js +772 -0
- package/bin/runners/runContainer.js +366 -0
- package/bin/runners/runContext.js +3 -0
- package/bin/runners/runDoctor.js +28 -41
- package/bin/runners/runEasy.js +410 -0
- package/bin/runners/runFirewall.js +3 -0
- package/bin/runners/runFirewallHook.js +3 -0
- package/bin/runners/runFix.js +76 -66
- package/bin/runners/runGuard.js +411 -18
- package/bin/runners/runIaC.js +372 -0
- package/bin/runners/runInit.js +10 -60
- package/bin/runners/runMcp.js +11 -12
- package/bin/runners/runPolish.js +240 -64
- package/bin/runners/runPromptFirewall.js +5 -12
- package/bin/runners/runProve.js +20 -55
- package/bin/runners/runReality.js +68 -59
- package/bin/runners/runReport.js +31 -5
- package/bin/runners/runRuntime.js +5 -8
- package/bin/runners/runScan.js +194 -1273
- package/bin/runners/runShip.js +695 -47
- package/bin/runners/runTruth.js +3 -0
- package/bin/runners/runValidate.js +7 -11
- package/bin/runners/runVibe.js +791 -0
- package/bin/runners/runWatch.js +14 -23
- package/bin/vibecheck.js +179 -65
- package/mcp-server/index.js +202 -636
- package/mcp-server/lib/api-client.cjs +7 -299
- package/mcp-server/package.json +1 -1
- package/mcp-server/tier-auth.js +175 -574
- package/mcp-server/tools-v3.js +800 -505
- package/mcp-server/tools.js +495 -0
- package/package.json +1 -1
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +0 -291
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +0 -83
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +0 -198
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +0 -275
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +0 -167
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +0 -140
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +0 -234
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +0 -78
- package/mcp-server/index-v1.js +0 -698
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Security Vulnerabilities Detection Engine
|
|
3
3
|
* Detects SQL injection, XSS, command injection, path traversal, and other security issues
|
|
4
|
+
* Enhanced with smart detection to reduce false positives:
|
|
5
|
+
* - Detects parameterized queries (prepared statements)
|
|
6
|
+
* - Detects sanitization libraries (DOMPurify, xss, sanitize-html)
|
|
7
|
+
* - Detects path validation (path.resolve, path.normalize)
|
|
8
|
+
* - Framework-specific patterns (Prisma, Drizzle, Knex, TypeORM)
|
|
4
9
|
*/
|
|
5
10
|
|
|
6
11
|
const { getAST } = require("./ast-cache");
|
|
@@ -8,6 +13,138 @@ const traverse = require("@babel/traverse").default;
|
|
|
8
13
|
const t = require("@babel/types");
|
|
9
14
|
const { shouldExcludeFile } = require("./file-filter");
|
|
10
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Safe ORM/Query Builder patterns that use parameterized queries by default
|
|
18
|
+
*/
|
|
19
|
+
const SAFE_ORM_PATTERNS = [
|
|
20
|
+
// Prisma
|
|
21
|
+
/prisma\.\w+\.(?:findUnique|findFirst|findMany|create|update|delete|upsert)/i,
|
|
22
|
+
/prisma\.\$queryRaw`/i, // Tagged template = safe
|
|
23
|
+
/prisma\.\$executeRaw`/i,
|
|
24
|
+
// Drizzle
|
|
25
|
+
/db\.select\(\)|db\.insert\(\)|db\.update\(\)|db\.delete\(\)/i,
|
|
26
|
+
// TypeORM
|
|
27
|
+
/\.createQueryBuilder\(\)/i,
|
|
28
|
+
/getRepository\(\)\.find/i,
|
|
29
|
+
// Knex
|
|
30
|
+
/knex\(\s*['"]?\w+['"]?\s*\)\.where/i,
|
|
31
|
+
/knex\.raw\(\s*['"][^'"]+['"]\s*,\s*\[/i, // knex.raw with params array
|
|
32
|
+
// Sequelize
|
|
33
|
+
/\.findAll\(|\.findOne\(|\.create\(|\.update\(/i,
|
|
34
|
+
// MongoDB/Mongoose
|
|
35
|
+
/\.find\(\{|\.findOne\(\{|\.aggregate\(\[/i,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if a line uses a safe ORM pattern
|
|
40
|
+
*/
|
|
41
|
+
function usesSafeORM(line, surroundingLines) {
|
|
42
|
+
const context = surroundingLines ? [...surroundingLines, line].join('\n') : line;
|
|
43
|
+
return SAFE_ORM_PATTERNS.some(pattern => pattern.test(context));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Sanitization library patterns
|
|
48
|
+
*/
|
|
49
|
+
const SANITIZATION_PATTERNS = [
|
|
50
|
+
// DOMPurify
|
|
51
|
+
/DOMPurify\.sanitize\(/i,
|
|
52
|
+
/purify\.sanitize\(/i,
|
|
53
|
+
// xss library
|
|
54
|
+
/\bxss\s*\(/i,
|
|
55
|
+
/filterXSS\(/i,
|
|
56
|
+
// sanitize-html
|
|
57
|
+
/sanitizeHtml\(/i,
|
|
58
|
+
/sanitize\(/i,
|
|
59
|
+
// escape-html
|
|
60
|
+
/escapeHtml\(/i,
|
|
61
|
+
/escape\(/i,
|
|
62
|
+
// React's built-in escaping (JSX)
|
|
63
|
+
/dangerouslySetInnerHTML/i, // When this is NOT present, React escapes by default
|
|
64
|
+
// encode-html-entities
|
|
65
|
+
/htmlEncode\(/i,
|
|
66
|
+
/encodeHTML\(/i,
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check if content is sanitized before use
|
|
71
|
+
*/
|
|
72
|
+
function isSanitized(line, surroundingLines) {
|
|
73
|
+
const context = surroundingLines ? [...surroundingLines, line].join('\n') : line;
|
|
74
|
+
return SANITIZATION_PATTERNS.some(pattern => pattern.test(context));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Path validation patterns
|
|
79
|
+
*/
|
|
80
|
+
const PATH_VALIDATION_PATTERNS = [
|
|
81
|
+
// path.resolve normalizes and makes absolute
|
|
82
|
+
/path\.resolve\(/i,
|
|
83
|
+
// path.normalize removes .. traversals
|
|
84
|
+
/path\.normalize\(/i,
|
|
85
|
+
// path.join with validation
|
|
86
|
+
/path\.join\(\s*__dirname/i,
|
|
87
|
+
/path\.join\(\s*process\.cwd\(\)/i,
|
|
88
|
+
// Common validation patterns
|
|
89
|
+
/\.startsWith\(\s*(?:baseDir|rootDir|allowedPath|safeDir)/i,
|
|
90
|
+
/\.includes\(\s*['"]\.\.["']\s*\)/i, // Manual check for ..
|
|
91
|
+
/!.*\.includes\(\s*['"]\.\.["']\s*\)/i, // Negated check
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if path is validated/sanitized
|
|
96
|
+
*/
|
|
97
|
+
function isPathValidated(line, surroundingLines) {
|
|
98
|
+
const context = surroundingLines ? [...surroundingLines, line].join('\n') : line;
|
|
99
|
+
return PATH_VALIDATION_PATTERNS.some(pattern => pattern.test(context));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check if SQL query uses parameterized placeholders
|
|
104
|
+
*/
|
|
105
|
+
function usesParameterizedQuery(args, code) {
|
|
106
|
+
if (!args || args.length === 0) return false;
|
|
107
|
+
|
|
108
|
+
const firstArg = args[0];
|
|
109
|
+
|
|
110
|
+
// Check for placeholder patterns in the query string
|
|
111
|
+
if (t.isStringLiteral(firstArg)) {
|
|
112
|
+
const query = firstArg.value;
|
|
113
|
+
// PostgreSQL: $1, $2, etc.
|
|
114
|
+
if (/\$\d+/.test(query)) return true;
|
|
115
|
+
// MySQL: ?, ?? placeholders
|
|
116
|
+
if (/\?/.test(query)) return true;
|
|
117
|
+
// Named parameters: :name, @name
|
|
118
|
+
if (/[:@]\w+/.test(query)) return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check for tagged template literals (safe by default in most ORMs)
|
|
122
|
+
if (t.isTaggedTemplateExpression(args[0])) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check if there's a second argument that's an array (params)
|
|
127
|
+
if (args.length >= 2 && t.isArrayExpression(args[1])) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check if second argument is an object (named params)
|
|
132
|
+
if (args.length >= 2 && t.isObjectExpression(args[1])) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get surrounding lines for context analysis
|
|
141
|
+
*/
|
|
142
|
+
function getSurroundingLines(lines, lineIndex, range = 3) {
|
|
143
|
+
const start = Math.max(0, lineIndex - range);
|
|
144
|
+
const end = Math.min(lines.length, lineIndex + range + 1);
|
|
145
|
+
return lines.slice(start, end);
|
|
146
|
+
}
|
|
147
|
+
|
|
11
148
|
/**
|
|
12
149
|
* Analyze a file for security vulnerabilities
|
|
13
150
|
*/
|
|
@@ -22,7 +159,7 @@ function analyzeSecurityVulnerabilities(code, filePath) {
|
|
|
22
159
|
|
|
23
160
|
const lines = code.split("\n");
|
|
24
161
|
|
|
25
|
-
// SQL Injection patterns
|
|
162
|
+
// SQL Injection patterns - Enhanced with ORM/parameterization detection
|
|
26
163
|
traverse(ast, {
|
|
27
164
|
CallExpression(path) {
|
|
28
165
|
const node = path.node;
|
|
@@ -34,24 +171,44 @@ function analyzeSecurityVulnerabilities(code, filePath) {
|
|
|
34
171
|
|
|
35
172
|
// Database query methods
|
|
36
173
|
if (t.isIdentifier(prop) &&
|
|
37
|
-
["query", "execute", "exec", "run"].includes(prop.name)) {
|
|
174
|
+
["query", "execute", "exec", "run", "raw"].includes(prop.name)) {
|
|
175
|
+
|
|
176
|
+
const lineNum = node.loc.start.line;
|
|
177
|
+
const lineContent = lines[lineNum - 1] || "";
|
|
178
|
+
const surroundingLines = getSurroundingLines(lines, lineNum - 1);
|
|
179
|
+
|
|
180
|
+
// Skip if using safe ORM patterns
|
|
181
|
+
if (usesSafeORM(lineContent, surroundingLines)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Skip if using parameterized queries
|
|
186
|
+
if (usesParameterizedQuery(node.arguments, code)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
38
189
|
|
|
39
190
|
// Check if arguments contain template literals or string concatenation
|
|
40
191
|
for (const arg of node.arguments) {
|
|
41
192
|
if (t.isTemplateLiteral(arg) ||
|
|
42
193
|
(t.isBinaryExpression(arg) && arg.operator === "+")) {
|
|
43
|
-
|
|
194
|
+
|
|
195
|
+
// Double-check it's not a tagged template (safe)
|
|
196
|
+
if (t.isTaggedTemplateExpression(arg)) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
44
200
|
findings.push({
|
|
45
201
|
type: "sql_injection",
|
|
46
202
|
severity: "BLOCK",
|
|
47
203
|
category: "Security",
|
|
48
204
|
file: filePath,
|
|
49
|
-
line,
|
|
205
|
+
line: lineNum,
|
|
50
206
|
column: node.loc.start.column,
|
|
51
207
|
title: "Potential SQL injection vulnerability",
|
|
52
|
-
message: "SQL query constructed with
|
|
53
|
-
codeSnippet:
|
|
208
|
+
message: "SQL query constructed with string interpolation. Use parameterized queries instead.",
|
|
209
|
+
codeSnippet: lineContent.trim(),
|
|
54
210
|
confidence: "high",
|
|
211
|
+
fixHint: "Use parameterized queries: query('SELECT * FROM users WHERE id = $1', [userId])",
|
|
55
212
|
});
|
|
56
213
|
break;
|
|
57
214
|
}
|
|
@@ -61,7 +218,7 @@ function analyzeSecurityVulnerabilities(code, filePath) {
|
|
|
61
218
|
},
|
|
62
219
|
});
|
|
63
220
|
|
|
64
|
-
// XSS vulnerabilities
|
|
221
|
+
// XSS vulnerabilities - Enhanced with sanitization detection
|
|
65
222
|
traverse(ast, {
|
|
66
223
|
CallExpression(path) {
|
|
67
224
|
const node = path.node;
|
|
@@ -74,21 +231,38 @@ function analyzeSecurityVulnerabilities(code, filePath) {
|
|
|
74
231
|
if (t.isIdentifier(prop) &&
|
|
75
232
|
["innerHTML", "outerHTML", "insertAdjacentHTML"].includes(prop.name)) {
|
|
76
233
|
|
|
234
|
+
const lineNum = node.loc.start.line;
|
|
235
|
+
const lineContent = lines[lineNum - 1] || "";
|
|
236
|
+
const surroundingLines = getSurroundingLines(lines, lineNum - 1, 5);
|
|
237
|
+
|
|
238
|
+
// Skip if content is sanitized
|
|
239
|
+
if (isSanitized(lineContent, surroundingLines)) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
77
243
|
// Check if argument comes from user input or URL params
|
|
78
244
|
for (const arg of node.arguments) {
|
|
79
245
|
if (t.isIdentifier(arg) || t.isMemberExpression(arg)) {
|
|
80
|
-
|
|
246
|
+
// Check if the argument itself is a sanitize call
|
|
247
|
+
if (t.isCallExpression(arg)) {
|
|
248
|
+
const argCode = code.substring(arg.start, arg.end);
|
|
249
|
+
if (SANITIZATION_PATTERNS.some(p => p.test(argCode))) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
81
254
|
findings.push({
|
|
82
255
|
type: "xss",
|
|
83
256
|
severity: "BLOCK",
|
|
84
257
|
category: "Security",
|
|
85
258
|
file: filePath,
|
|
86
|
-
line,
|
|
259
|
+
line: lineNum,
|
|
87
260
|
column: node.loc.start.column,
|
|
88
261
|
title: "Potential XSS vulnerability",
|
|
89
|
-
message: `Using ${prop.name} with potentially unsafe content
|
|
90
|
-
codeSnippet:
|
|
262
|
+
message: `Using ${prop.name} with potentially unsafe content. Sanitize before rendering.`,
|
|
263
|
+
codeSnippet: lineContent.trim(),
|
|
91
264
|
confidence: "med",
|
|
265
|
+
fixHint: "Use DOMPurify.sanitize(content) or sanitize-html before setting innerHTML",
|
|
92
266
|
});
|
|
93
267
|
break;
|
|
94
268
|
}
|
|
@@ -97,75 +271,229 @@ function analyzeSecurityVulnerabilities(code, filePath) {
|
|
|
97
271
|
}
|
|
98
272
|
},
|
|
99
273
|
});
|
|
274
|
+
|
|
275
|
+
// Also check for dangerouslySetInnerHTML in React
|
|
276
|
+
traverse(ast, {
|
|
277
|
+
JSXAttribute(path) {
|
|
278
|
+
const node = path.node;
|
|
279
|
+
|
|
280
|
+
if (t.isJSXIdentifier(node.name, { name: "dangerouslySetInnerHTML" })) {
|
|
281
|
+
const lineNum = node.loc.start.line;
|
|
282
|
+
const lineContent = lines[lineNum - 1] || "";
|
|
283
|
+
const surroundingLines = getSurroundingLines(lines, lineNum - 1, 5);
|
|
284
|
+
|
|
285
|
+
// Skip if content is sanitized
|
|
286
|
+
if (isSanitized(lineContent, surroundingLines)) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
findings.push({
|
|
291
|
+
type: "xss",
|
|
292
|
+
severity: "WARN",
|
|
293
|
+
category: "Security",
|
|
294
|
+
file: filePath,
|
|
295
|
+
line: lineNum,
|
|
296
|
+
column: node.loc.start.column,
|
|
297
|
+
title: "Potential XSS via dangerouslySetInnerHTML",
|
|
298
|
+
message: "Using dangerouslySetInnerHTML without visible sanitization",
|
|
299
|
+
codeSnippet: lineContent.trim(),
|
|
300
|
+
confidence: "med",
|
|
301
|
+
fixHint: "Sanitize content with DOMPurify.sanitize() before rendering",
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
});
|
|
100
306
|
|
|
101
|
-
// Command injection
|
|
307
|
+
// Command injection - Enhanced with shell escape detection
|
|
308
|
+
const SHELL_ESCAPE_PATTERNS = [
|
|
309
|
+
/shellEscape\(/i,
|
|
310
|
+
/escapeShell\(/i,
|
|
311
|
+
/shell-escape/i,
|
|
312
|
+
/shellescape/i,
|
|
313
|
+
/quote\(/i, // shell-quote
|
|
314
|
+
];
|
|
315
|
+
|
|
316
|
+
function isShellEscaped(line, surroundingLines) {
|
|
317
|
+
const context = surroundingLines ? [...surroundingLines, line].join('\n') : line;
|
|
318
|
+
return SHELL_ESCAPE_PATTERNS.some(pattern => pattern.test(context));
|
|
319
|
+
}
|
|
320
|
+
|
|
102
321
|
traverse(ast, {
|
|
103
322
|
CallExpression(path) {
|
|
104
323
|
const node = path.node;
|
|
105
324
|
|
|
106
325
|
// Dangerous command execution methods
|
|
326
|
+
let methodName = null;
|
|
327
|
+
|
|
107
328
|
if (t.isIdentifier(node.callee)) {
|
|
108
|
-
|
|
329
|
+
methodName = node.callee.name;
|
|
330
|
+
} else if (t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.property)) {
|
|
331
|
+
methodName = node.callee.property.name;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const dangerousMethods = ["exec", "spawn", "execSync", "spawnSync", "execFile", "execFileSync"];
|
|
335
|
+
|
|
336
|
+
if (methodName && dangerousMethods.includes(methodName)) {
|
|
337
|
+
const lineNum = node.loc.start.line;
|
|
338
|
+
const lineContent = lines[lineNum - 1] || "";
|
|
339
|
+
const surroundingLines = getSurroundingLines(lines, lineNum - 1, 5);
|
|
109
340
|
|
|
110
|
-
if
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
341
|
+
// Skip if shell-escaped
|
|
342
|
+
if (isShellEscaped(lineContent, surroundingLines)) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Check if arguments contain user input
|
|
347
|
+
for (const arg of node.arguments) {
|
|
348
|
+
if (t.isTemplateLiteral(arg) ||
|
|
349
|
+
(t.isBinaryExpression(arg) && arg.operator === "+")) {
|
|
350
|
+
findings.push({
|
|
351
|
+
type: "command_injection",
|
|
352
|
+
severity: "BLOCK",
|
|
353
|
+
category: "Security",
|
|
354
|
+
file: filePath,
|
|
355
|
+
line: lineNum,
|
|
356
|
+
column: node.loc.start.column,
|
|
357
|
+
title: "Potential command injection vulnerability",
|
|
358
|
+
message: "Command execution with string interpolation. Use array args or shell-escape.",
|
|
359
|
+
codeSnippet: lineContent.trim(),
|
|
360
|
+
confidence: "high",
|
|
361
|
+
fixHint: "Use spawn with array arguments: spawn('cmd', [arg1, arg2]) instead of shell strings",
|
|
362
|
+
});
|
|
363
|
+
break;
|
|
130
364
|
}
|
|
131
365
|
}
|
|
132
366
|
}
|
|
133
367
|
},
|
|
134
368
|
});
|
|
135
369
|
|
|
136
|
-
//
|
|
370
|
+
// SSRF (Server-Side Request Forgery) detection
|
|
371
|
+
traverse(ast, {
|
|
372
|
+
CallExpression(path) {
|
|
373
|
+
const node = path.node;
|
|
374
|
+
|
|
375
|
+
// Check for fetch, axios, http.request with dynamic URLs
|
|
376
|
+
let isHttpCall = false;
|
|
377
|
+
let methodName = "";
|
|
378
|
+
|
|
379
|
+
if (t.isIdentifier(node.callee)) {
|
|
380
|
+
isHttpCall = ["fetch", "axios", "request", "got", "superagent"].includes(node.callee.name);
|
|
381
|
+
methodName = node.callee.name;
|
|
382
|
+
} else if (t.isMemberExpression(node.callee)) {
|
|
383
|
+
const prop = node.callee.property;
|
|
384
|
+
if (t.isIdentifier(prop)) {
|
|
385
|
+
isHttpCall = ["get", "post", "put", "delete", "request", "fetch"].includes(prop.name);
|
|
386
|
+
methodName = prop.name;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (isHttpCall && node.arguments.length > 0) {
|
|
391
|
+
const urlArg = node.arguments[0];
|
|
392
|
+
|
|
393
|
+
// Check if URL contains user input
|
|
394
|
+
if (t.isTemplateLiteral(urlArg) && urlArg.expressions && urlArg.expressions.length > 0) {
|
|
395
|
+
const lineNum = node.loc.start.line;
|
|
396
|
+
const lineContent = lines[lineNum - 1] || "";
|
|
397
|
+
const surroundingLines = getSurroundingLines(lines, lineNum - 1, 5);
|
|
398
|
+
|
|
399
|
+
// Check for URL validation patterns
|
|
400
|
+
const urlValidationPatterns = [
|
|
401
|
+
/new URL\(/i,
|
|
402
|
+
/url\.parse\(/i,
|
|
403
|
+
/isValidUrl\(/i,
|
|
404
|
+
/validateUrl\(/i,
|
|
405
|
+
/allowedHosts/i,
|
|
406
|
+
/whitelist/i,
|
|
407
|
+
];
|
|
408
|
+
|
|
409
|
+
const hasUrlValidation = urlValidationPatterns.some(p =>
|
|
410
|
+
p.test(lineContent) || p.test(surroundingLines.join('\n'))
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
if (!hasUrlValidation) {
|
|
414
|
+
findings.push({
|
|
415
|
+
type: "ssrf",
|
|
416
|
+
severity: "WARN",
|
|
417
|
+
category: "Security",
|
|
418
|
+
file: filePath,
|
|
419
|
+
line: lineNum,
|
|
420
|
+
column: node.loc.start.column,
|
|
421
|
+
title: "Potential SSRF vulnerability",
|
|
422
|
+
message: "HTTP request with user-controlled URL without visible validation",
|
|
423
|
+
codeSnippet: lineContent.trim(),
|
|
424
|
+
confidence: "med",
|
|
425
|
+
fixHint: "Validate URLs against an allowlist of hosts before making requests",
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// Path traversal - Enhanced with path validation detection
|
|
137
434
|
traverse(ast, {
|
|
138
435
|
CallExpression(path) {
|
|
139
436
|
const node = path.node;
|
|
140
437
|
|
|
141
438
|
// File system operations
|
|
142
|
-
if (t.isMemberExpression(node.callee)
|
|
143
|
-
|
|
439
|
+
if (t.isMemberExpression(node.callee)) {
|
|
440
|
+
const obj = node.callee.object;
|
|
144
441
|
const prop = node.callee.property;
|
|
145
442
|
|
|
146
|
-
|
|
147
|
-
|
|
443
|
+
// Check for fs.* or fs/promises methods
|
|
444
|
+
const isFsCall = t.isIdentifier(obj, { name: "fs" }) ||
|
|
445
|
+
t.isIdentifier(obj, { name: "fsp" }) ||
|
|
446
|
+
t.isIdentifier(obj, { name: "fsPromises" });
|
|
447
|
+
|
|
448
|
+
if (isFsCall && t.isIdentifier(prop) &&
|
|
449
|
+
["readFile", "writeFile", "readFileSync", "writeFileSync", "unlink",
|
|
450
|
+
"unlinkSync", "access", "accessSync", "stat", "statSync",
|
|
451
|
+
"readdir", "readdirSync", "mkdir", "mkdirSync"].includes(prop.name)) {
|
|
452
|
+
|
|
453
|
+
const lineNum = node.loc.start.line;
|
|
454
|
+
const lineContent = lines[lineNum - 1] || "";
|
|
455
|
+
const surroundingLines = getSurroundingLines(lines, lineNum - 1, 5);
|
|
456
|
+
|
|
457
|
+
// Skip if path is validated/sanitized
|
|
458
|
+
if (isPathValidated(lineContent, surroundingLines)) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
148
461
|
|
|
149
462
|
// Check if path contains user input or "../"
|
|
150
463
|
for (const arg of node.arguments) {
|
|
464
|
+
let hasTraversal = false;
|
|
465
|
+
let hasUserInput = false;
|
|
466
|
+
|
|
151
467
|
if (t.isTemplateLiteral(arg)) {
|
|
152
468
|
const template = code.substring(arg.start, arg.end);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
469
|
+
hasTraversal = template.includes("../") || template.includes("..\\");
|
|
470
|
+
hasUserInput = arg.expressions && arg.expressions.length > 0;
|
|
471
|
+
} else if (t.isBinaryExpression(arg) && arg.operator === "+") {
|
|
472
|
+
// String concatenation with user input
|
|
473
|
+
hasUserInput = true;
|
|
474
|
+
} else if (t.isIdentifier(arg)) {
|
|
475
|
+
// Variable path - check if it looks like user input
|
|
476
|
+
const varName = arg.name.toLowerCase();
|
|
477
|
+
hasUserInput = /path|file|dir|folder|name|input|param|query|body|req/i.test(varName);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (hasTraversal || hasUserInput) {
|
|
481
|
+
findings.push({
|
|
482
|
+
type: "path_traversal",
|
|
483
|
+
severity: hasTraversal ? "BLOCK" : "WARN",
|
|
484
|
+
category: "Security",
|
|
485
|
+
file: filePath,
|
|
486
|
+
line: lineNum,
|
|
487
|
+
column: node.loc.start.column,
|
|
488
|
+
title: "Potential path traversal vulnerability",
|
|
489
|
+
message: hasTraversal
|
|
490
|
+
? "File operation with path containing '..' - validate and sanitize paths"
|
|
491
|
+
: "File operation with potentially user-controlled path - validate before use",
|
|
492
|
+
codeSnippet: lineContent.trim(),
|
|
493
|
+
confidence: hasTraversal ? "high" : "med",
|
|
494
|
+
fixHint: "Use path.resolve() with a base directory and validate the result starts with the expected prefix",
|
|
495
|
+
});
|
|
496
|
+
break;
|
|
169
497
|
}
|
|
170
498
|
}
|
|
171
499
|
}
|