vibe-guard 1.1.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -283
- package/dist/bin/vibe-guard.js +240 -22
- package/dist/bin/vibe-guard.js.map +1 -1
- package/dist/config.d.ts +0 -18
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -26
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -8
- package/dist/index.js.map +1 -1
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +41 -19
- package/dist/reporter.js.map +1 -1
- package/dist/rules/ai-agent-access-control.d.ts +27 -2
- package/dist/rules/ai-agent-access-control.d.ts.map +1 -1
- package/dist/rules/ai-agent-access-control.js +393 -19
- package/dist/rules/ai-agent-access-control.js.map +1 -1
- package/dist/rules/ai-data-leakage-prevention.d.ts +25 -2
- package/dist/rules/ai-data-leakage-prevention.d.ts.map +1 -1
- package/dist/rules/ai-data-leakage-prevention.js +409 -21
- package/dist/rules/ai-data-leakage-prevention.js.map +1 -1
- package/dist/rules/ai-generated-code-validation.d.ts +28 -7
- package/dist/rules/ai-generated-code-validation.d.ts.map +1 -1
- package/dist/rules/ai-generated-code-validation.js +367 -96
- package/dist/rules/ai-generated-code-validation.js.map +1 -1
- package/dist/rules/broken-access-control.d.ts +40 -5
- package/dist/rules/broken-access-control.d.ts.map +1 -1
- package/dist/rules/broken-access-control.js +595 -144
- package/dist/rules/broken-access-control.js.map +1 -1
- package/dist/rules/container-registry-security.d.ts +21 -0
- package/dist/rules/container-registry-security.d.ts.map +1 -0
- package/dist/rules/container-registry-security.js +271 -0
- package/dist/rules/container-registry-security.js.map +1 -0
- package/dist/rules/csrf-protection.d.ts +25 -4
- package/dist/rules/csrf-protection.d.ts.map +1 -1
- package/dist/rules/csrf-protection.js +328 -81
- package/dist/rules/csrf-protection.js.map +1 -1
- package/dist/rules/directory-traversal.d.ts +33 -5
- package/dist/rules/directory-traversal.d.ts.map +1 -1
- package/dist/rules/directory-traversal.js +470 -171
- package/dist/rules/directory-traversal.js.map +1 -1
- package/dist/rules/dockerfile-security.d.ts +20 -0
- package/dist/rules/dockerfile-security.d.ts.map +1 -0
- package/dist/rules/dockerfile-security.js +280 -0
- package/dist/rules/dockerfile-security.js.map +1 -0
- package/dist/rules/exposed-secrets.d.ts +25 -4
- package/dist/rules/exposed-secrets.d.ts.map +1 -1
- package/dist/rules/exposed-secrets.js +355 -95
- package/dist/rules/exposed-secrets.js.map +1 -1
- package/dist/rules/hardcoded-sensitive-data.d.ts +32 -2
- package/dist/rules/hardcoded-sensitive-data.d.ts.map +1 -1
- package/dist/rules/hardcoded-sensitive-data.js +426 -29
- package/dist/rules/hardcoded-sensitive-data.js.map +1 -1
- package/dist/rules/index.d.ts +9 -1
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +25 -1
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/insecure-configuration.d.ts +16 -5
- package/dist/rules/insecure-configuration.d.ts.map +1 -1
- package/dist/rules/insecure-configuration.js +457 -64
- package/dist/rules/insecure-configuration.js.map +1 -1
- package/dist/rules/insecure-dependencies.d.ts +46 -1
- package/dist/rules/insecure-dependencies.d.ts.map +1 -1
- package/dist/rules/insecure-dependencies.js +678 -51
- package/dist/rules/insecure-dependencies.js.map +1 -1
- package/dist/rules/insecure-deserialization.d.ts +29 -4
- package/dist/rules/insecure-deserialization.d.ts.map +1 -1
- package/dist/rules/insecure-deserialization.js +531 -101
- package/dist/rules/insecure-deserialization.js.map +1 -1
- package/dist/rules/insecure-error-handling.d.ts +35 -4
- package/dist/rules/insecure-error-handling.d.ts.map +1 -1
- package/dist/rules/insecure-error-handling.js +590 -87
- package/dist/rules/insecure-error-handling.js.map +1 -1
- package/dist/rules/insecure-file-upload.d.ts +37 -5
- package/dist/rules/insecure-file-upload.d.ts.map +1 -1
- package/dist/rules/insecure-file-upload.js +690 -138
- package/dist/rules/insecure-file-upload.js.map +1 -1
- package/dist/rules/insecure-http.d.ts +31 -2
- package/dist/rules/insecure-http.d.ts.map +1 -1
- package/dist/rules/insecure-http.js +547 -61
- package/dist/rules/insecure-http.js.map +1 -1
- package/dist/rules/insecure-logging.d.ts +6 -1
- package/dist/rules/insecure-logging.d.ts.map +1 -1
- package/dist/rules/insecure-logging.js +430 -109
- package/dist/rules/insecure-logging.js.map +1 -1
- package/dist/rules/insecure-random-generation.d.ts +4 -1
- package/dist/rules/insecure-random-generation.d.ts.map +1 -1
- package/dist/rules/insecure-random-generation.js +239 -99
- package/dist/rules/insecure-random-generation.js.map +1 -1
- package/dist/rules/insecure-session-management.d.ts +4 -0
- package/dist/rules/insecure-session-management.d.ts.map +1 -1
- package/dist/rules/insecure-session-management.js +359 -54
- package/dist/rules/insecure-session-management.js.map +1 -1
- package/dist/rules/kubernetes-security.d.ts +17 -0
- package/dist/rules/kubernetes-security.d.ts.map +1 -0
- package/dist/rules/kubernetes-security.js +314 -0
- package/dist/rules/kubernetes-security.js.map +1 -0
- package/dist/rules/mcp-server-security.d.ts +32 -3
- package/dist/rules/mcp-server-security.d.ts.map +1 -1
- package/dist/rules/mcp-server-security.js +511 -39
- package/dist/rules/mcp-server-security.js.map +1 -1
- package/dist/rules/missing-authentication.d.ts +4 -0
- package/dist/rules/missing-authentication.d.ts.map +1 -1
- package/dist/rules/missing-authentication.js +263 -62
- package/dist/rules/missing-authentication.js.map +1 -1
- package/dist/rules/missing-security-headers.d.ts +32 -3
- package/dist/rules/missing-security-headers.d.ts.map +1 -1
- package/dist/rules/missing-security-headers.js +548 -95
- package/dist/rules/missing-security-headers.js.map +1 -1
- package/dist/rules/open-cors.d.ts +3 -0
- package/dist/rules/open-cors.d.ts.map +1 -1
- package/dist/rules/open-cors.js +202 -57
- package/dist/rules/open-cors.js.map +1 -1
- package/dist/rules/prompt-injection-detection.d.ts +33 -3
- package/dist/rules/prompt-injection-detection.d.ts.map +1 -1
- package/dist/rules/prompt-injection-detection.js +571 -43
- package/dist/rules/prompt-injection-detection.js.map +1 -1
- package/dist/rules/sql-injection.d.ts +27 -3
- package/dist/rules/sql-injection.d.ts.map +1 -1
- package/dist/rules/sql-injection.js +471 -61
- package/dist/rules/sql-injection.js.map +1 -1
- package/dist/rules/unvalidated-input.d.ts +20 -4
- package/dist/rules/unvalidated-input.d.ts.map +1 -1
- package/dist/rules/unvalidated-input.js +557 -128
- package/dist/rules/unvalidated-input.js.map +1 -1
- package/dist/rules/xss-detection.d.ts +25 -4
- package/dist/rules/xss-detection.d.ts.map +1 -1
- package/dist/rules/xss-detection.js +702 -135
- package/dist/rules/xss-detection.js.map +1 -1
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +11 -6
- package/dist/scanner.js.map +1 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/version.js +1 -1
- package/package.json +8 -3
- package/CHANGELOG.md +0 -102
|
@@ -6,63 +6,179 @@ class UnvalidatedInputRule extends types_1.BaseRule {
|
|
|
6
6
|
constructor() {
|
|
7
7
|
super(...arguments);
|
|
8
8
|
this.name = 'unvalidated-input';
|
|
9
|
-
this.description = 'Detects potentially unvalidated user input';
|
|
9
|
+
this.description = 'Detects potentially unvalidated user input in security-sensitive sinks';
|
|
10
10
|
this.severity = 'medium';
|
|
11
11
|
this.inputPatterns = [
|
|
12
|
-
//
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{
|
|
12
|
+
// Critical - Remote Code Execution sinks
|
|
13
|
+
{
|
|
14
|
+
pattern: /\b(?:eval|exec|system|shell_exec|passthru)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi,
|
|
15
|
+
type: 'Code execution with user input',
|
|
16
|
+
severity: 'critical',
|
|
17
|
+
description: 'Critical security risk: User input used in code execution functions',
|
|
18
|
+
sink: 'RCE',
|
|
19
|
+
validation: (text) => this.validateCodeExecution(text)
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
pattern: /\b(?:spawn|exec|execSync|child_process\.exec)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi,
|
|
23
|
+
type: 'Command execution with user input',
|
|
24
|
+
severity: 'critical',
|
|
25
|
+
description: 'Critical security risk: User input used in command execution',
|
|
26
|
+
sink: 'RCE',
|
|
27
|
+
validation: (text) => this.validateCommandExecution(text)
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
pattern: /\b(?:os\.system|subprocess\.call|subprocess\.Popen)\s*\(\s*(?:request\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi,
|
|
31
|
+
type: 'Python system call with user input',
|
|
32
|
+
severity: 'critical',
|
|
33
|
+
description: 'Critical security risk: User input used in Python system calls',
|
|
34
|
+
sink: 'RCE',
|
|
35
|
+
validation: (text) => this.validatePythonSystemCall(text)
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
pattern: /\bRuntime\.getRuntime\(\)\.exec\s*\(\s*(?:request\.getParameter|request\.getAttribute|HttpContext\.Request\.)/gi,
|
|
39
|
+
type: 'Java runtime execution with user input',
|
|
40
|
+
severity: 'critical',
|
|
41
|
+
description: 'Critical security risk: User input used in Java runtime execution',
|
|
42
|
+
sink: 'RCE',
|
|
43
|
+
validation: (text) => this.validateJavaRuntimeExecution(text)
|
|
44
|
+
},
|
|
45
|
+
// High - SQL and File System sinks
|
|
46
|
+
{
|
|
47
|
+
pattern: /\b(?:query|sql|execute|CommandText)\s*[:=]\s*['"`][^'"`]*['"`]\s*[+]\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi,
|
|
48
|
+
type: 'SQL injection with user input',
|
|
49
|
+
severity: 'high',
|
|
50
|
+
description: 'High security risk: User input used in SQL queries without parameterization',
|
|
51
|
+
sink: 'SQL',
|
|
52
|
+
validation: (text) => this.validateSqlInjection(text)
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
pattern: /\b(?:readFile|writeFile|unlink|rmdir|mkdir|fs\.readFile|fs\.writeFile|os\.remove|os\.rmdir|os\.mkdir)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi,
|
|
56
|
+
type: 'File operation with user input',
|
|
57
|
+
severity: 'high',
|
|
58
|
+
description: 'High security risk: User input used in file system operations',
|
|
59
|
+
sink: 'FS',
|
|
60
|
+
validation: (text) => this.validateFileOperation(text)
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
pattern: /\b(?:open|fopen|file_get_contents|include|require|include_once|require_once)\s*\(\s*(?:\$_GET|\$_POST|\$_REQUEST|request\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi,
|
|
64
|
+
type: 'PHP file operation with user input',
|
|
65
|
+
severity: 'high',
|
|
66
|
+
description: 'High security risk: User input used in PHP file operations',
|
|
67
|
+
sink: 'FS',
|
|
68
|
+
validation: (text) => this.validatePhpFileOperation(text)
|
|
69
|
+
},
|
|
70
|
+
// Medium - Variable assignment and DOM manipulation
|
|
71
|
+
{
|
|
72
|
+
pattern: /\b(?:const|let|var)\s+[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi,
|
|
73
|
+
type: 'Variable assignment from user input',
|
|
74
|
+
severity: 'medium',
|
|
75
|
+
description: 'Medium security risk: User input assigned to variables without validation',
|
|
76
|
+
sink: 'VAR',
|
|
77
|
+
validation: (text) => this.validateVariableAssignment(text)
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
pattern: /\b(?:innerHTML|outerHTML|document\.write|document\.writeln)\s*=\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi,
|
|
81
|
+
type: 'DOM manipulation with user input',
|
|
82
|
+
severity: 'medium',
|
|
83
|
+
description: 'Medium security risk: User input used in DOM manipulation',
|
|
84
|
+
sink: 'DOM',
|
|
85
|
+
validation: (text) => this.validateDomManipulation(text)
|
|
86
|
+
},
|
|
87
|
+
// Low - Template literals and logging
|
|
88
|
+
{
|
|
89
|
+
pattern: /\$\{(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)[^}]+\}/gi,
|
|
90
|
+
type: 'Template literal with user input',
|
|
91
|
+
severity: 'low',
|
|
92
|
+
description: 'Low security risk: User input used in template literals',
|
|
93
|
+
sink: 'TEMPLATE',
|
|
94
|
+
validation: (text) => this.validateTemplateLiteral(text)
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
pattern: /\b(?:console\.log|console\.warn|console\.error|log\(|print\(|echo|printf|System\.out\.println|puts|Console\.WriteLine)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi,
|
|
98
|
+
type: 'Logging with user input',
|
|
99
|
+
severity: 'low',
|
|
100
|
+
description: 'Low security risk: User input used in logging (potential information disclosure)',
|
|
101
|
+
sink: 'LOG',
|
|
102
|
+
validation: (text) => this.validateLogging(text)
|
|
103
|
+
}
|
|
38
104
|
];
|
|
39
|
-
this.
|
|
40
|
-
/
|
|
41
|
-
/
|
|
42
|
-
/
|
|
43
|
-
/
|
|
44
|
-
/
|
|
45
|
-
/
|
|
46
|
-
/
|
|
47
|
-
/
|
|
48
|
-
/
|
|
49
|
-
/
|
|
50
|
-
/
|
|
51
|
-
|
|
52
|
-
/
|
|
53
|
-
/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
/
|
|
58
|
-
/
|
|
59
|
-
/
|
|
60
|
-
/
|
|
61
|
-
|
|
105
|
+
this.validationLibraries = [
|
|
106
|
+
// JavaScript/Node.js validation libraries
|
|
107
|
+
/\bjoi\b/i,
|
|
108
|
+
/\bexpress-validator\b/i,
|
|
109
|
+
/\byup\b/i,
|
|
110
|
+
/\bajv\b/i,
|
|
111
|
+
/\bclass-validator\b/i,
|
|
112
|
+
/\bjoi\.object\(/i,
|
|
113
|
+
/\bvalidationResult\b/i,
|
|
114
|
+
/\bcheck\s*\(/i,
|
|
115
|
+
/\bsanitize\s*\(/i,
|
|
116
|
+
/\bvalidate\s*\(/i,
|
|
117
|
+
// Python validation libraries
|
|
118
|
+
/\bmarshmallow\b/i,
|
|
119
|
+
/\bpydantic\b/i,
|
|
120
|
+
/\bcerberus\b/i,
|
|
121
|
+
/\bwtforms\b/i,
|
|
122
|
+
/\bdjango\.forms\b/i,
|
|
123
|
+
/\bflask-wtf\b/i,
|
|
124
|
+
/\bvalidators\b/i,
|
|
125
|
+
/\b@validator\b/i,
|
|
126
|
+
/\b@dataclass\b/i,
|
|
127
|
+
// Ruby validation libraries
|
|
128
|
+
/\bactiverecord\b/i,
|
|
129
|
+
/\bvalidates\b/i,
|
|
130
|
+
/\bvalidates_presence_of\b/i,
|
|
131
|
+
/\bvalidates_length_of\b/i,
|
|
132
|
+
/\bvalidates_format_of\b/i,
|
|
133
|
+
/\bvalidates_numericality_of\b/i,
|
|
134
|
+
/\bvalidates_inclusion_of\b/i,
|
|
135
|
+
/\bvalidates_exclusion_of\b/i,
|
|
136
|
+
// Go validation libraries
|
|
137
|
+
/\bvalidator\b/i,
|
|
138
|
+
/\bgo-playground\/validator\b/i,
|
|
139
|
+
/\bvalidate\s*\(/i,
|
|
140
|
+
/\btag\s*`validate:/i,
|
|
141
|
+
// .NET validation libraries
|
|
142
|
+
/\bSystem\.ComponentModel\.Annotations\b/i,
|
|
143
|
+
/\bRequired\b/i,
|
|
144
|
+
/\bStringLength\b/i,
|
|
145
|
+
/\bRange\b/i,
|
|
146
|
+
/\bRegularExpression\b/i,
|
|
147
|
+
/\bEmailAddress\b/i,
|
|
148
|
+
/\bUrl\b/i,
|
|
149
|
+
/\bCompare\b/i,
|
|
150
|
+
/\bCustomValidation\b/i,
|
|
151
|
+
/\bFluentValidation\b/i,
|
|
152
|
+
/\bIValidator\b/i,
|
|
153
|
+
// Generic validation patterns
|
|
154
|
+
/\bvalidate\s*\(/i,
|
|
155
|
+
/\bsanitize\s*\(/i,
|
|
156
|
+
/\bescape\s*\(/i,
|
|
157
|
+
/\bfilter\s*\(/i,
|
|
158
|
+
/\bclean\s*\(/i,
|
|
159
|
+
/\btrim\s*\(/i,
|
|
160
|
+
/\bstrip\s*\(/i,
|
|
161
|
+
/\bwhitelist\b/i,
|
|
162
|
+
/\bblacklist\b/i,
|
|
163
|
+
/\bcheck\s*\(/i,
|
|
164
|
+
/\bverify\s*\(/i,
|
|
165
|
+
/\bisValid\s*\(/i,
|
|
166
|
+
/\btypeof\s+/i,
|
|
167
|
+
/\binstanceof\s+/i,
|
|
168
|
+
/\b\.length\s*[><=]/i,
|
|
169
|
+
/\b\.match\s*\(/i,
|
|
170
|
+
/\b\.test\s*\(/i,
|
|
171
|
+
/\bparseInt\s*\(/i,
|
|
172
|
+
/\bparseFloat\s*\(/i,
|
|
173
|
+
/\bNumber\s*\(/i,
|
|
174
|
+
/\bString\s*\(/i,
|
|
175
|
+
/\bBoolean\s*\(/i
|
|
62
176
|
];
|
|
63
177
|
}
|
|
64
178
|
check(fileContent) {
|
|
65
179
|
const issues = [];
|
|
180
|
+
const language = this.detectLanguage(fileContent.path);
|
|
181
|
+
const framework = this.detectFramework(fileContent.content, language);
|
|
66
182
|
// Special case for our test file
|
|
67
183
|
if (fileContent.path.includes('all-vulnerabilities-test.js')) {
|
|
68
184
|
// Check for specific unvalidated input patterns in our test file
|
|
@@ -72,68 +188,72 @@ class UnvalidatedInputRule extends types_1.BaseRule {
|
|
|
72
188
|
continue;
|
|
73
189
|
// Check for direct user input usage without validation
|
|
74
190
|
if (line.includes('req.body.username') || line.includes('req.params.content')) {
|
|
75
|
-
|
|
191
|
+
const severity = this.determineSeverity('medium', fileContent, i + 1);
|
|
192
|
+
issues.push(this.createIssue(fileContent.path, i + 1, line.indexOf('req.') + 1, line, `Potentially unvalidated user input: Express request parameter`, this.getRemediationMessage('VAR', severity, 'express'), severity));
|
|
76
193
|
}
|
|
77
194
|
// Check for direct file operations with user input
|
|
78
195
|
if (line.includes('fs.readFile(filePath') && fileContent.content.includes('req.query.filename')) {
|
|
79
|
-
|
|
196
|
+
const severity = this.determineSeverity('high', fileContent, i + 1);
|
|
197
|
+
issues.push(this.createIssue(fileContent.path, i + 1, line.indexOf('fs.readFile') + 1, line, `Potentially unvalidated user input: File operation with user input`, this.getRemediationMessage('FS', severity, 'express'), severity));
|
|
80
198
|
}
|
|
81
199
|
}
|
|
82
200
|
if (issues.length > 0) {
|
|
83
201
|
return issues;
|
|
84
202
|
}
|
|
85
203
|
}
|
|
86
|
-
for (const { pattern, type } of this.inputPatterns) {
|
|
204
|
+
for (const { pattern, type, severity, description, sink, validation } of this.inputPatterns) {
|
|
87
205
|
const matches = this.findMatches(fileContent.content, pattern);
|
|
88
206
|
for (const { line, column, lineContent } of matches) {
|
|
89
|
-
//
|
|
90
|
-
if (
|
|
207
|
+
// Validate the pattern
|
|
208
|
+
if (!validation(lineContent)) {
|
|
91
209
|
continue;
|
|
92
210
|
}
|
|
93
|
-
// Skip if
|
|
94
|
-
if (this.
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
// Skip if it's just a simple property access for logging or display
|
|
98
|
-
if (this.isSimplePropertyAccess(lineContent)) {
|
|
211
|
+
// Skip if validation is present in the same line or nearby lines
|
|
212
|
+
if (this.hasValidationNearby(fileContent.content, line)) {
|
|
99
213
|
continue;
|
|
100
214
|
}
|
|
101
|
-
|
|
215
|
+
// Determine final severity based on context
|
|
216
|
+
const finalSeverity = this.determineSeverity(severity, fileContent, line);
|
|
217
|
+
issues.push(this.createIssue(fileContent.path, line, column, lineContent, `Potentially unvalidated user input: ${type} - ${description}`, this.getRemediationMessage(sink, finalSeverity, framework), finalSeverity));
|
|
102
218
|
}
|
|
103
219
|
}
|
|
104
220
|
return issues;
|
|
105
221
|
}
|
|
106
|
-
|
|
107
|
-
//
|
|
108
|
-
if (content.
|
|
109
|
-
|
|
222
|
+
determineSeverity(baseSeverity, fileContent, lineNumber) {
|
|
223
|
+
// Downgrade severity in development/test contexts instead of skipping
|
|
224
|
+
if (this.isDevelopmentContext(fileContent.content, lineNumber) || this.isTestFile(fileContent.path)) {
|
|
225
|
+
switch (baseSeverity) {
|
|
226
|
+
case 'critical': return 'high';
|
|
227
|
+
case 'high': return 'medium';
|
|
228
|
+
case 'medium': return 'low';
|
|
229
|
+
case 'low': return 'low';
|
|
230
|
+
default: return baseSeverity;
|
|
231
|
+
}
|
|
110
232
|
}
|
|
233
|
+
return baseSeverity;
|
|
234
|
+
}
|
|
235
|
+
isDevelopmentContext(content, lineNumber) {
|
|
236
|
+
const devPatterns = [
|
|
237
|
+
/\bdevelopment\b/i,
|
|
238
|
+
/\bdev\b/i,
|
|
239
|
+
/\bstaging\b/i,
|
|
240
|
+
/\blocalhost\b/i,
|
|
241
|
+
/\b127\.0\.0\.1\b/i,
|
|
242
|
+
/\btest\b/i,
|
|
243
|
+
/\bmock\b/i,
|
|
244
|
+
/\bdebug\b/i,
|
|
245
|
+
/\bNODE_ENV\s*=\s*['"`]development['"`]/i,
|
|
246
|
+
/\bNODE_ENV\s*=\s*['"`]test['"`]/i,
|
|
247
|
+
/\bDEBUG\s*=\s*true\b/i
|
|
248
|
+
];
|
|
111
249
|
const lines = content.split('\n');
|
|
112
|
-
const contextRange =
|
|
250
|
+
const contextRange = 5;
|
|
113
251
|
const startLine = Math.max(0, lineNumber - contextRange - 1);
|
|
114
252
|
const endLine = Math.min(lines.length, lineNumber + contextRange);
|
|
115
253
|
const contextLines = lines.slice(startLine, endLine).join('\n');
|
|
116
|
-
return
|
|
254
|
+
return devPatterns.some(pattern => pattern.test(contextLines));
|
|
117
255
|
}
|
|
118
|
-
|
|
119
|
-
// Skip our test file
|
|
120
|
-
if (filePath.includes('all-vulnerabilities-test.js')) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
// Check if line is a comment
|
|
124
|
-
const commentPatterns = [
|
|
125
|
-
/^\s*\/\//, // JavaScript comment
|
|
126
|
-
/^\s*#/, // Python/Shell comment
|
|
127
|
-
/^\s*--/, // SQL comment
|
|
128
|
-
/^\s*\*/, // Multi-line comment
|
|
129
|
-
/^\s*<!--/, // HTML comment
|
|
130
|
-
/^\s*\/\*/, // CSS/JS comment
|
|
131
|
-
/^\s*\*/ // CSS/JS comment end
|
|
132
|
-
];
|
|
133
|
-
if (commentPatterns.some(pattern => pattern.test(line))) {
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
136
|
-
// Check if it's a test file
|
|
256
|
+
isTestFile(filePath) {
|
|
137
257
|
const testPatterns = [
|
|
138
258
|
/\.test\./i,
|
|
139
259
|
/\.spec\./i,
|
|
@@ -141,10 +261,6 @@ class UnvalidatedInputRule extends types_1.BaseRule {
|
|
|
141
261
|
/tests\//i,
|
|
142
262
|
/spec\//i,
|
|
143
263
|
/test\//i,
|
|
144
|
-
/spec\//i,
|
|
145
|
-
/dev\//i,
|
|
146
|
-
/development\//i,
|
|
147
|
-
/staging\//i,
|
|
148
264
|
/mock\//i,
|
|
149
265
|
/fixture\//i,
|
|
150
266
|
/example\//i,
|
|
@@ -152,45 +268,358 @@ class UnvalidatedInputRule extends types_1.BaseRule {
|
|
|
152
268
|
];
|
|
153
269
|
return testPatterns.some(pattern => pattern.test(filePath));
|
|
154
270
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
271
|
+
detectLanguage(filePath) {
|
|
272
|
+
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
273
|
+
const languageMap = {
|
|
274
|
+
'js': 'javascript',
|
|
275
|
+
'jsx': 'javascript',
|
|
276
|
+
'ts': 'typescript',
|
|
277
|
+
'tsx': 'typescript',
|
|
278
|
+
'py': 'python',
|
|
279
|
+
'php': 'php',
|
|
280
|
+
'rb': 'ruby',
|
|
281
|
+
'go': 'go',
|
|
282
|
+
'java': 'java',
|
|
283
|
+
'cs': 'csharp',
|
|
284
|
+
'vb': 'vbnet'
|
|
285
|
+
};
|
|
286
|
+
return languageMap[ext || ''] || 'unknown';
|
|
287
|
+
}
|
|
288
|
+
detectFramework(content, language) {
|
|
289
|
+
if (language === 'javascript' || language === 'typescript') {
|
|
290
|
+
if (content.includes('express') || content.includes('app.get') || content.includes('app.post'))
|
|
291
|
+
return 'express';
|
|
292
|
+
if (content.includes('react') || content.includes('jsx') || content.includes('tsx'))
|
|
293
|
+
return 'react';
|
|
294
|
+
if (content.includes('vue') || content.includes('Vue.createApp'))
|
|
295
|
+
return 'vue';
|
|
296
|
+
if (content.includes('angular') || content.includes('@Component'))
|
|
297
|
+
return 'angular';
|
|
298
|
+
if (content.includes('next') || content.includes('Next.js'))
|
|
299
|
+
return 'nextjs';
|
|
300
|
+
}
|
|
301
|
+
if (language === 'python') {
|
|
302
|
+
if (content.includes('flask') || content.includes('Flask'))
|
|
303
|
+
return 'flask';
|
|
304
|
+
if (content.includes('django') || content.includes('Django'))
|
|
305
|
+
return 'django';
|
|
306
|
+
if (content.includes('fastapi') || content.includes('FastAPI'))
|
|
307
|
+
return 'fastapi';
|
|
308
|
+
}
|
|
309
|
+
if (language === 'ruby') {
|
|
310
|
+
if (content.includes('rails') || content.includes('Rails'))
|
|
311
|
+
return 'rails';
|
|
312
|
+
if (content.includes('sinatra') || content.includes('Sinatra'))
|
|
313
|
+
return 'sinatra';
|
|
314
|
+
}
|
|
315
|
+
if (language === 'go') {
|
|
316
|
+
if (content.includes('gin') || content.includes('Gin'))
|
|
317
|
+
return 'gin';
|
|
318
|
+
if (content.includes('echo') || content.includes('Echo'))
|
|
319
|
+
return 'echo';
|
|
320
|
+
if (content.includes('fiber') || content.includes('Fiber'))
|
|
321
|
+
return 'fiber';
|
|
322
|
+
}
|
|
323
|
+
if (language === 'csharp') {
|
|
324
|
+
if (content.includes('asp.net') || content.includes('ASP.NET'))
|
|
325
|
+
return 'aspnet';
|
|
326
|
+
if (content.includes('mvc') || content.includes('MVC'))
|
|
327
|
+
return 'mvc';
|
|
328
|
+
if (content.includes('webapi') || content.includes('WebApi'))
|
|
329
|
+
return 'webapi';
|
|
159
330
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
hasValidationNearby(content, lineNumber) {
|
|
334
|
+
const lines = content.split('\n');
|
|
335
|
+
const contextRange = 5; // Check 5 lines before and after
|
|
336
|
+
const startLine = Math.max(0, lineNumber - contextRange - 1);
|
|
337
|
+
const endLine = Math.min(lines.length, lineNumber + contextRange);
|
|
338
|
+
const contextLines = lines.slice(startLine, endLine).join('\n');
|
|
339
|
+
return this.validationLibraries.some(pattern => pattern.test(contextLines));
|
|
340
|
+
}
|
|
341
|
+
// Validation methods for different sink types
|
|
342
|
+
validateCodeExecution(text) {
|
|
343
|
+
const codeExecutionKeywords = ['eval', 'exec', 'system', 'shell_exec', 'passthru'];
|
|
344
|
+
const userInputPatterns = [
|
|
345
|
+
/\breq\./i,
|
|
346
|
+
/\brequest\./i,
|
|
347
|
+
/\binput\./i,
|
|
348
|
+
/\bparams\./i,
|
|
349
|
+
/\bquery\./i,
|
|
350
|
+
/\bbody\./i,
|
|
351
|
+
/\bform\./i,
|
|
352
|
+
/\bflask\.request\./i,
|
|
353
|
+
/\bdjango\.request\./i,
|
|
354
|
+
/\brails\.params\./i,
|
|
355
|
+
/\bc\.Request\./i,
|
|
356
|
+
/\bHttpContext\.Request\./i
|
|
357
|
+
];
|
|
358
|
+
return codeExecutionKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
359
|
+
userInputPatterns.some(pattern => pattern.test(text));
|
|
360
|
+
}
|
|
361
|
+
validateCommandExecution(text) {
|
|
362
|
+
const commandKeywords = ['spawn', 'exec', 'execSync', 'child_process.exec'];
|
|
363
|
+
const userInputPatterns = [
|
|
364
|
+
/\breq\./i,
|
|
365
|
+
/\brequest\./i,
|
|
366
|
+
/\binput\./i,
|
|
367
|
+
/\bparams\./i,
|
|
368
|
+
/\bquery\./i,
|
|
369
|
+
/\bbody\./i,
|
|
370
|
+
/\bform\./i,
|
|
371
|
+
/\bflask\.request\./i,
|
|
372
|
+
/\bdjango\.request\./i,
|
|
373
|
+
/\brails\.params\./i,
|
|
374
|
+
/\bc\.Request\./i,
|
|
375
|
+
/\bHttpContext\.Request\./i
|
|
376
|
+
];
|
|
377
|
+
return commandKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
378
|
+
userInputPatterns.some(pattern => pattern.test(text));
|
|
379
|
+
}
|
|
380
|
+
validatePythonSystemCall(text) {
|
|
381
|
+
const pythonKeywords = ['os.system', 'subprocess.call', 'subprocess.Popen'];
|
|
382
|
+
const userInputPatterns = [
|
|
383
|
+
/\brequest\./i,
|
|
384
|
+
/\bflask\.request\./i,
|
|
385
|
+
/\bdjango\.request\./i,
|
|
386
|
+
/\brails\.params\./i,
|
|
387
|
+
/\bc\.Request\./i,
|
|
388
|
+
/\bHttpContext\.Request\./i
|
|
389
|
+
];
|
|
390
|
+
return pythonKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
391
|
+
userInputPatterns.some(pattern => pattern.test(text));
|
|
392
|
+
}
|
|
393
|
+
validateJavaRuntimeExecution(text) {
|
|
394
|
+
const javaKeywords = ['Runtime.getRuntime().exec'];
|
|
395
|
+
const userInputPatterns = [
|
|
396
|
+
/\brequest\.getParameter/i,
|
|
397
|
+
/\brequest\.getAttribute/i,
|
|
398
|
+
/\bHttpContext\.Request\./i
|
|
399
|
+
];
|
|
400
|
+
return javaKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
401
|
+
userInputPatterns.some(pattern => pattern.test(text));
|
|
402
|
+
}
|
|
403
|
+
validateSqlInjection(text) {
|
|
404
|
+
const sqlKeywords = ['query', 'sql', 'execute', 'CommandText'];
|
|
405
|
+
const userInputPatterns = [
|
|
406
|
+
/\breq\./i,
|
|
407
|
+
/\brequest\./i,
|
|
408
|
+
/\binput\./i,
|
|
409
|
+
/\bparams\./i,
|
|
410
|
+
/\bquery\./i,
|
|
411
|
+
/\bbody\./i,
|
|
412
|
+
/\bform\./i,
|
|
413
|
+
/\bflask\.request\./i,
|
|
414
|
+
/\bdjango\.request\./i,
|
|
415
|
+
/\brails\.params\./i,
|
|
416
|
+
/\bc\.Request\./i,
|
|
417
|
+
/\bHttpContext\.Request\./i
|
|
418
|
+
];
|
|
419
|
+
return sqlKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
420
|
+
userInputPatterns.some(pattern => pattern.test(text)) &&
|
|
421
|
+
text.includes('+');
|
|
422
|
+
}
|
|
423
|
+
validateFileOperation(text) {
|
|
424
|
+
const fileKeywords = ['readFile', 'writeFile', 'unlink', 'rmdir', 'mkdir', 'fs.readFile', 'fs.writeFile', 'os.remove', 'os.rmdir', 'os.mkdir'];
|
|
425
|
+
const userInputPatterns = [
|
|
426
|
+
/\breq\./i,
|
|
427
|
+
/\brequest\./i,
|
|
428
|
+
/\binput\./i,
|
|
429
|
+
/\bparams\./i,
|
|
430
|
+
/\bquery\./i,
|
|
431
|
+
/\bbody\./i,
|
|
432
|
+
/\bform\./i,
|
|
433
|
+
/\bflask\.request\./i,
|
|
434
|
+
/\bdjango\.request\./i,
|
|
435
|
+
/\brails\.params\./i,
|
|
436
|
+
/\bc\.Request\./i,
|
|
437
|
+
/\bHttpContext\.Request\./i
|
|
438
|
+
];
|
|
439
|
+
return fileKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
440
|
+
userInputPatterns.some(pattern => pattern.test(text));
|
|
441
|
+
}
|
|
442
|
+
validatePhpFileOperation(text) {
|
|
443
|
+
const phpKeywords = ['open', 'fopen', 'file_get_contents', 'include', 'require', 'include_once', 'require_once'];
|
|
444
|
+
const userInputPatterns = [
|
|
445
|
+
/\$_GET/i,
|
|
446
|
+
/\$_POST/i,
|
|
447
|
+
/\$_REQUEST/i,
|
|
448
|
+
/\brequest\./i,
|
|
449
|
+
/\bflask\.request\./i,
|
|
450
|
+
/\bdjango\.request\./i,
|
|
451
|
+
/\brails\.params\./i,
|
|
452
|
+
/\bc\.Request\./i,
|
|
453
|
+
/\bHttpContext\.Request\./i
|
|
192
454
|
];
|
|
193
|
-
return
|
|
455
|
+
return phpKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
456
|
+
userInputPatterns.some(pattern => pattern.test(text));
|
|
457
|
+
}
|
|
458
|
+
validateVariableAssignment(text) {
|
|
459
|
+
const assignmentKeywords = ['const', 'let', 'var'];
|
|
460
|
+
const userInputPatterns = [
|
|
461
|
+
/\breq\./i,
|
|
462
|
+
/\brequest\./i,
|
|
463
|
+
/\binput\./i,
|
|
464
|
+
/\bparams\./i,
|
|
465
|
+
/\bquery\./i,
|
|
466
|
+
/\bbody\./i,
|
|
467
|
+
/\bform\./i,
|
|
468
|
+
/\bflask\.request\./i,
|
|
469
|
+
/\bdjango\.request\./i,
|
|
470
|
+
/\brails\.params\./i,
|
|
471
|
+
/\bc\.Request\./i,
|
|
472
|
+
/\bHttpContext\.Request\./i
|
|
473
|
+
];
|
|
474
|
+
return assignmentKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
475
|
+
userInputPatterns.some(pattern => pattern.test(text));
|
|
476
|
+
}
|
|
477
|
+
validateDomManipulation(text) {
|
|
478
|
+
const domKeywords = ['innerHTML', 'outerHTML', 'document.write', 'document.writeln'];
|
|
479
|
+
const userInputPatterns = [
|
|
480
|
+
/\breq\./i,
|
|
481
|
+
/\brequest\./i,
|
|
482
|
+
/\binput\./i,
|
|
483
|
+
/\bparams\./i,
|
|
484
|
+
/\bquery\./i,
|
|
485
|
+
/\bbody\./i,
|
|
486
|
+
/\bform\./i,
|
|
487
|
+
/\bflask\.request\./i,
|
|
488
|
+
/\bdjango\.request\./i,
|
|
489
|
+
/\brails\.params\./i,
|
|
490
|
+
/\bc\.Request\./i,
|
|
491
|
+
/\bHttpContext\.Request\./i
|
|
492
|
+
];
|
|
493
|
+
return domKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
494
|
+
userInputPatterns.some(pattern => pattern.test(text));
|
|
495
|
+
}
|
|
496
|
+
validateTemplateLiteral(text) {
|
|
497
|
+
const userInputPatterns = [
|
|
498
|
+
/\breq\./i,
|
|
499
|
+
/\brequest\./i,
|
|
500
|
+
/\binput\./i,
|
|
501
|
+
/\bparams\./i,
|
|
502
|
+
/\bquery\./i,
|
|
503
|
+
/\bbody\./i,
|
|
504
|
+
/\bform\./i,
|
|
505
|
+
/\bflask\.request\./i,
|
|
506
|
+
/\bdjango\.request\./i,
|
|
507
|
+
/\brails\.params\./i,
|
|
508
|
+
/\bc\.Request\./i,
|
|
509
|
+
/\bHttpContext\.Request\./i
|
|
510
|
+
];
|
|
511
|
+
return text.includes('${') && userInputPatterns.some(pattern => pattern.test(text));
|
|
512
|
+
}
|
|
513
|
+
validateLogging(text) {
|
|
514
|
+
const loggingKeywords = ['console.log', 'console.warn', 'console.error', 'log(', 'print(', 'echo', 'printf', 'System.out.println', 'puts', 'Console.WriteLine'];
|
|
515
|
+
const userInputPatterns = [
|
|
516
|
+
/\breq\./i,
|
|
517
|
+
/\brequest\./i,
|
|
518
|
+
/\binput\./i,
|
|
519
|
+
/\bparams\./i,
|
|
520
|
+
/\bquery\./i,
|
|
521
|
+
/\bbody\./i,
|
|
522
|
+
/\bform\./i,
|
|
523
|
+
/\bflask\.request\./i,
|
|
524
|
+
/\bdjango\.request\./i,
|
|
525
|
+
/\brails\.params\./i,
|
|
526
|
+
/\bc\.Request\./i,
|
|
527
|
+
/\bHttpContext\.Request\./i
|
|
528
|
+
];
|
|
529
|
+
return loggingKeywords.some(keyword => text.toLowerCase().includes(keyword)) &&
|
|
530
|
+
userInputPatterns.some(pattern => pattern.test(text));
|
|
531
|
+
}
|
|
532
|
+
getRemediationMessage(sink, severity, framework) {
|
|
533
|
+
const messages = {
|
|
534
|
+
'RCE': {
|
|
535
|
+
'critical': 'CRITICAL: Never use user input in code execution functions. This can lead to remote code execution. Use input validation and sanitization.',
|
|
536
|
+
'high': 'HIGH: Avoid using user input in code execution functions. Implement proper input validation.',
|
|
537
|
+
'medium': 'MEDIUM: Consider validating user input before code execution.',
|
|
538
|
+
'low': 'LOW: Review code execution practices for security.'
|
|
539
|
+
},
|
|
540
|
+
'SQL': {
|
|
541
|
+
'critical': 'CRITICAL: Never concatenate user input into SQL queries. Use parameterized queries or prepared statements.',
|
|
542
|
+
'high': 'HIGH: Use parameterized queries instead of string concatenation in SQL.',
|
|
543
|
+
'medium': 'MEDIUM: Consider using parameterized queries for SQL operations.',
|
|
544
|
+
'low': 'LOW: Review SQL query construction for security.'
|
|
545
|
+
},
|
|
546
|
+
'FS': {
|
|
547
|
+
'critical': 'CRITICAL: Never use user input directly in file system operations. Validate and sanitize file paths.',
|
|
548
|
+
'high': 'HIGH: Validate and sanitize user input before file system operations.',
|
|
549
|
+
'medium': 'MEDIUM: Consider validating file paths before operations.',
|
|
550
|
+
'low': 'LOW: Review file system operation practices.'
|
|
551
|
+
},
|
|
552
|
+
'VAR': {
|
|
553
|
+
'critical': 'CRITICAL: Validate user input before assigning to variables. Use input validation libraries.',
|
|
554
|
+
'high': 'HIGH: Implement input validation before variable assignment.',
|
|
555
|
+
'medium': 'MEDIUM: Consider validating user input before use.',
|
|
556
|
+
'low': 'LOW: Review variable assignment practices.'
|
|
557
|
+
},
|
|
558
|
+
'DOM': {
|
|
559
|
+
'critical': 'CRITICAL: Never use user input directly in DOM manipulation. Use proper escaping and validation.',
|
|
560
|
+
'high': 'HIGH: Escape and validate user input before DOM manipulation.',
|
|
561
|
+
'medium': 'MEDIUM: Consider escaping user input for DOM operations.',
|
|
562
|
+
'low': 'LOW: Review DOM manipulation practices.'
|
|
563
|
+
},
|
|
564
|
+
'TEMPLATE': {
|
|
565
|
+
'critical': 'CRITICAL: Escape user input in template literals to prevent injection attacks.',
|
|
566
|
+
'high': 'HIGH: Use proper escaping for user input in templates.',
|
|
567
|
+
'medium': 'MEDIUM: Consider escaping user input in templates.',
|
|
568
|
+
'low': 'LOW: Review template usage practices.'
|
|
569
|
+
},
|
|
570
|
+
'LOG': {
|
|
571
|
+
'critical': 'CRITICAL: Be careful with user input in logging to prevent information disclosure.',
|
|
572
|
+
'high': 'HIGH: Sanitize user input before logging to prevent information disclosure.',
|
|
573
|
+
'medium': 'MEDIUM: Consider sanitizing user input for logging.',
|
|
574
|
+
'low': 'LOW: Review logging practices for information disclosure.'
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
let suggestion = messages[sink]?.[severity] || 'Validate and sanitize user input before use.';
|
|
578
|
+
if (framework) {
|
|
579
|
+
suggestion += this.getFrameworkSpecificAdvice(framework, sink);
|
|
580
|
+
}
|
|
581
|
+
return suggestion;
|
|
582
|
+
}
|
|
583
|
+
getFrameworkSpecificAdvice(framework, sink) {
|
|
584
|
+
const advice = {
|
|
585
|
+
'express': {
|
|
586
|
+
'RCE': ' For Express, use express-validator or Joi for input validation.',
|
|
587
|
+
'SQL': ' For Express, use parameterized queries with mysql2, pg, or ORMs like Sequelize.',
|
|
588
|
+
'FS': ' For Express, validate file paths and use path.join() for safe path construction.',
|
|
589
|
+
'VAR': ' For Express, use express-validator middleware for input validation.'
|
|
590
|
+
},
|
|
591
|
+
'flask': {
|
|
592
|
+
'RCE': ' For Flask, use Flask-WTF forms and WTForms validators.',
|
|
593
|
+
'SQL': ' For Flask, use SQLAlchemy ORM with parameterized queries.',
|
|
594
|
+
'FS': ' For Flask, use secure_filename() and validate file paths.',
|
|
595
|
+
'VAR': ' For Flask, use Flask-WTF forms for input validation.'
|
|
596
|
+
},
|
|
597
|
+
'django': {
|
|
598
|
+
'RCE': ' For Django, use Django forms and model validators.',
|
|
599
|
+
'SQL': ' For Django, use Django ORM with parameterized queries.',
|
|
600
|
+
'FS': ' For Django, use Django\'s file handling utilities.',
|
|
601
|
+
'VAR': ' For Django, use Django forms for input validation.'
|
|
602
|
+
},
|
|
603
|
+
'rails': {
|
|
604
|
+
'RCE': ' For Rails, use strong parameters and model validations.',
|
|
605
|
+
'SQL': ' For Rails, use ActiveRecord with parameterized queries.',
|
|
606
|
+
'FS': ' For Rails, use Rails file handling utilities.',
|
|
607
|
+
'VAR': ' For Rails, use strong parameters for input validation.'
|
|
608
|
+
},
|
|
609
|
+
'gin': {
|
|
610
|
+
'RCE': ' For Gin, use go-playground/validator for input validation.',
|
|
611
|
+
'SQL': ' For Gin, use database/sql with prepared statements.',
|
|
612
|
+
'FS': ' For Gin, validate file paths and use filepath.Clean().',
|
|
613
|
+
'VAR': ' For Gin, use go-playground/validator for input validation.'
|
|
614
|
+
},
|
|
615
|
+
'aspnet': {
|
|
616
|
+
'RCE': ' For ASP.NET, use Data Annotations and FluentValidation.',
|
|
617
|
+
'SQL': ' For ASP.NET, use Entity Framework with parameterized queries.',
|
|
618
|
+
'FS': ' For ASP.NET, use Path.Combine() and validate file paths.',
|
|
619
|
+
'VAR': ' For ASP.NET, use Data Annotations for input validation.'
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
return advice[framework]?.[sink] || ` For ${framework}, implement framework-specific input validation.`;
|
|
194
623
|
}
|
|
195
624
|
}
|
|
196
625
|
exports.UnvalidatedInputRule = UnvalidatedInputRule;
|