eslint-plugin-secure-coding 1.0.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/AGENTS.md +196 -0
- package/CHANGELOG.md +105 -0
- package/LICENSE +23 -0
- package/README.md +377 -0
- package/package.json +80 -0
- package/src/index.d.ts +32 -0
- package/src/index.js +345 -0
- package/src/index.js.map +1 -0
- package/src/rules/security/database-injection.d.ts +13 -0
- package/src/rules/security/database-injection.js +407 -0
- package/src/rules/security/database-injection.js.map +1 -0
- package/src/rules/security/detect-child-process.d.ts +11 -0
- package/src/rules/security/detect-child-process.js +460 -0
- package/src/rules/security/detect-child-process.js.map +1 -0
- package/src/rules/security/detect-eval-with-expression.d.ts +9 -0
- package/src/rules/security/detect-eval-with-expression.js +393 -0
- package/src/rules/security/detect-eval-with-expression.js.map +1 -0
- package/src/rules/security/detect-non-literal-fs-filename.d.ts +7 -0
- package/src/rules/security/detect-non-literal-fs-filename.js +322 -0
- package/src/rules/security/detect-non-literal-fs-filename.js.map +1 -0
- package/src/rules/security/detect-non-literal-regexp.d.ts +9 -0
- package/src/rules/security/detect-non-literal-regexp.js +387 -0
- package/src/rules/security/detect-non-literal-regexp.js.map +1 -0
- package/src/rules/security/detect-object-injection.d.ts +11 -0
- package/src/rules/security/detect-object-injection.js +411 -0
- package/src/rules/security/detect-object-injection.js.map +1 -0
- package/src/rules/security/no-buffer-overread.d.ts +14 -0
- package/src/rules/security/no-buffer-overread.js +519 -0
- package/src/rules/security/no-buffer-overread.js.map +1 -0
- package/src/rules/security/no-clickjacking.d.ts +10 -0
- package/src/rules/security/no-clickjacking.js +381 -0
- package/src/rules/security/no-clickjacking.js.map +1 -0
- package/src/rules/security/no-directive-injection.d.ts +12 -0
- package/src/rules/security/no-directive-injection.js +446 -0
- package/src/rules/security/no-directive-injection.js.map +1 -0
- package/src/rules/security/no-document-cookie.d.ts +5 -0
- package/src/rules/security/no-document-cookie.js +90 -0
- package/src/rules/security/no-document-cookie.js.map +1 -0
- package/src/rules/security/no-electron-security-issues.d.ts +10 -0
- package/src/rules/security/no-electron-security-issues.js +421 -0
- package/src/rules/security/no-electron-security-issues.js.map +1 -0
- package/src/rules/security/no-exposed-sensitive-data.d.ts +11 -0
- package/src/rules/security/no-exposed-sensitive-data.js +341 -0
- package/src/rules/security/no-exposed-sensitive-data.js.map +1 -0
- package/src/rules/security/no-format-string-injection.d.ts +17 -0
- package/src/rules/security/no-format-string-injection.js +653 -0
- package/src/rules/security/no-format-string-injection.js.map +1 -0
- package/src/rules/security/no-graphql-injection.d.ts +12 -0
- package/src/rules/security/no-graphql-injection.js +410 -0
- package/src/rules/security/no-graphql-injection.js.map +1 -0
- package/src/rules/security/no-hardcoded-credentials.d.ts +26 -0
- package/src/rules/security/no-hardcoded-credentials.js +377 -0
- package/src/rules/security/no-hardcoded-credentials.js.map +1 -0
- package/src/rules/security/no-improper-sanitization.d.ts +12 -0
- package/src/rules/security/no-improper-sanitization.js +408 -0
- package/src/rules/security/no-improper-sanitization.js.map +1 -0
- package/src/rules/security/no-improper-type-validation.d.ts +10 -0
- package/src/rules/security/no-improper-type-validation.js +420 -0
- package/src/rules/security/no-improper-type-validation.js.map +1 -0
- package/src/rules/security/no-insecure-comparison.d.ts +7 -0
- package/src/rules/security/no-insecure-comparison.js +125 -0
- package/src/rules/security/no-insecure-comparison.js.map +1 -0
- package/src/rules/security/no-insecure-cookie-settings.d.ts +9 -0
- package/src/rules/security/no-insecure-cookie-settings.js +305 -0
- package/src/rules/security/no-insecure-cookie-settings.js.map +1 -0
- package/src/rules/security/no-insecure-jwt.d.ts +10 -0
- package/src/rules/security/no-insecure-jwt.js +338 -0
- package/src/rules/security/no-insecure-jwt.js.map +1 -0
- package/src/rules/security/no-insecure-redirects.d.ts +7 -0
- package/src/rules/security/no-insecure-redirects.js +215 -0
- package/src/rules/security/no-insecure-redirects.js.map +1 -0
- package/src/rules/security/no-insufficient-postmessage-validation.d.ts +14 -0
- package/src/rules/security/no-insufficient-postmessage-validation.js +390 -0
- package/src/rules/security/no-insufficient-postmessage-validation.js.map +1 -0
- package/src/rules/security/no-insufficient-random.d.ts +9 -0
- package/src/rules/security/no-insufficient-random.js +207 -0
- package/src/rules/security/no-insufficient-random.js.map +1 -0
- package/src/rules/security/no-ldap-injection.d.ts +10 -0
- package/src/rules/security/no-ldap-injection.js +449 -0
- package/src/rules/security/no-ldap-injection.js.map +1 -0
- package/src/rules/security/no-missing-authentication.d.ts +13 -0
- package/src/rules/security/no-missing-authentication.js +322 -0
- package/src/rules/security/no-missing-authentication.js.map +1 -0
- package/src/rules/security/no-missing-cors-check.d.ts +9 -0
- package/src/rules/security/no-missing-cors-check.js +449 -0
- package/src/rules/security/no-missing-cors-check.js.map +1 -0
- package/src/rules/security/no-missing-csrf-protection.d.ts +11 -0
- package/src/rules/security/no-missing-csrf-protection.js +183 -0
- package/src/rules/security/no-missing-csrf-protection.js.map +1 -0
- package/src/rules/security/no-missing-security-headers.d.ts +7 -0
- package/src/rules/security/no-missing-security-headers.js +217 -0
- package/src/rules/security/no-missing-security-headers.js.map +1 -0
- package/src/rules/security/no-privilege-escalation.d.ts +13 -0
- package/src/rules/security/no-privilege-escalation.js +321 -0
- package/src/rules/security/no-privilege-escalation.js.map +1 -0
- package/src/rules/security/no-redos-vulnerable-regex.d.ts +7 -0
- package/src/rules/security/no-redos-vulnerable-regex.js +307 -0
- package/src/rules/security/no-redos-vulnerable-regex.js.map +1 -0
- package/src/rules/security/no-sensitive-data-exposure.d.ts +11 -0
- package/src/rules/security/no-sensitive-data-exposure.js +251 -0
- package/src/rules/security/no-sensitive-data-exposure.js.map +1 -0
- package/src/rules/security/no-sql-injection.d.ts +10 -0
- package/src/rules/security/no-sql-injection.js +332 -0
- package/src/rules/security/no-sql-injection.js.map +1 -0
- package/src/rules/security/no-timing-attack.d.ts +10 -0
- package/src/rules/security/no-timing-attack.js +358 -0
- package/src/rules/security/no-timing-attack.js.map +1 -0
- package/src/rules/security/no-toctou-vulnerability.d.ts +7 -0
- package/src/rules/security/no-toctou-vulnerability.js +165 -0
- package/src/rules/security/no-toctou-vulnerability.js.map +1 -0
- package/src/rules/security/no-unchecked-loop-condition.d.ts +12 -0
- package/src/rules/security/no-unchecked-loop-condition.js +635 -0
- package/src/rules/security/no-unchecked-loop-condition.js.map +1 -0
- package/src/rules/security/no-unencrypted-transmission.d.ts +11 -0
- package/src/rules/security/no-unencrypted-transmission.js +237 -0
- package/src/rules/security/no-unencrypted-transmission.js.map +1 -0
- package/src/rules/security/no-unescaped-url-parameter.d.ts +9 -0
- package/src/rules/security/no-unescaped-url-parameter.js +266 -0
- package/src/rules/security/no-unescaped-url-parameter.js.map +1 -0
- package/src/rules/security/no-unlimited-resource-allocation.d.ts +12 -0
- package/src/rules/security/no-unlimited-resource-allocation.js +659 -0
- package/src/rules/security/no-unlimited-resource-allocation.js.map +1 -0
- package/src/rules/security/no-unsafe-deserialization.d.ts +10 -0
- package/src/rules/security/no-unsafe-deserialization.js +501 -0
- package/src/rules/security/no-unsafe-deserialization.js.map +1 -0
- package/src/rules/security/no-unsafe-dynamic-require.d.ts +5 -0
- package/src/rules/security/no-unsafe-dynamic-require.js +107 -0
- package/src/rules/security/no-unsafe-dynamic-require.js.map +1 -0
- package/src/rules/security/no-unsafe-regex-construction.d.ts +9 -0
- package/src/rules/security/no-unsafe-regex-construction.js +292 -0
- package/src/rules/security/no-unsafe-regex-construction.js.map +1 -0
- package/src/rules/security/no-unsanitized-html.d.ts +9 -0
- package/src/rules/security/no-unsanitized-html.js +347 -0
- package/src/rules/security/no-unsanitized-html.js.map +1 -0
- package/src/rules/security/no-unvalidated-user-input.d.ts +9 -0
- package/src/rules/security/no-unvalidated-user-input.js +418 -0
- package/src/rules/security/no-unvalidated-user-input.js.map +1 -0
- package/src/rules/security/no-weak-crypto.d.ts +11 -0
- package/src/rules/security/no-weak-crypto.js +350 -0
- package/src/rules/security/no-weak-crypto.js.map +1 -0
- package/src/rules/security/no-weak-password-recovery.d.ts +12 -0
- package/src/rules/security/no-weak-password-recovery.js +401 -0
- package/src/rules/security/no-weak-password-recovery.js.map +1 -0
- package/src/rules/security/no-xpath-injection.d.ts +10 -0
- package/src/rules/security/no-xpath-injection.js +487 -0
- package/src/rules/security/no-xpath-injection.js.map +1 -0
- package/src/rules/security/no-xxe-injection.d.ts +7 -0
- package/src/rules/security/no-xxe-injection.js +270 -0
- package/src/rules/security/no-xxe-injection.js.map +1 -0
- package/src/rules/security/no-zip-slip.d.ts +9 -0
- package/src/rules/security/no-zip-slip.js +446 -0
- package/src/rules/security/no-zip-slip.js.map +1 -0
- package/src/types/index.d.ts +131 -0
- package/src/types/index.js +18 -0
- package/src/types/index.js.map +1 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noPrivilegeEscalation = void 0;
|
|
4
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
5
|
+
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
6
|
+
/**
|
|
7
|
+
* Common role check patterns
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_ROLE_CHECK_PATTERNS = [
|
|
10
|
+
'hasRole',
|
|
11
|
+
'checkRole',
|
|
12
|
+
'isAdmin',
|
|
13
|
+
'isAuthorized',
|
|
14
|
+
'hasPermission',
|
|
15
|
+
'checkPermission',
|
|
16
|
+
'verifyRole',
|
|
17
|
+
'requireRole',
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Common user input patterns
|
|
21
|
+
*/
|
|
22
|
+
const DEFAULT_USER_INPUT_PATTERNS = [
|
|
23
|
+
/\breq\.(body|query|params)\b/,
|
|
24
|
+
/\brequest\.(body|query|params)\b/,
|
|
25
|
+
/\buserInput\b/,
|
|
26
|
+
/\binput\b/,
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Check if a string matches any ignore pattern
|
|
30
|
+
*/
|
|
31
|
+
function matchesIgnorePattern(text, patterns) {
|
|
32
|
+
return patterns.some(pattern => {
|
|
33
|
+
try {
|
|
34
|
+
const regex = new RegExp(pattern, 'i');
|
|
35
|
+
return regex.test(text);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Invalid regex - treat as literal string match
|
|
39
|
+
return text.toLowerCase().includes(pattern.toLowerCase());
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if a node contains user input patterns
|
|
45
|
+
*/
|
|
46
|
+
function containsUserInput(node, sourceCode, userInputPatterns) {
|
|
47
|
+
const text = sourceCode.getText(node);
|
|
48
|
+
return userInputPatterns.some(pattern => pattern.test(text));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if a node is inside a role check call
|
|
52
|
+
*/
|
|
53
|
+
function isInsideRoleCheck(node, sourceCode, roleCheckPatterns) {
|
|
54
|
+
let current = node;
|
|
55
|
+
while (current) {
|
|
56
|
+
// Check if current is inside an IfStatement with role check in condition
|
|
57
|
+
if (current.parent && current.parent.type === 'IfStatement') {
|
|
58
|
+
const ifStmt = current.parent;
|
|
59
|
+
const conditionText = sourceCode.getText(ifStmt.test);
|
|
60
|
+
// Check if condition contains role check patterns
|
|
61
|
+
if (roleCheckPatterns.some(pattern => conditionText.toLowerCase().includes(pattern.toLowerCase()))) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
/* c8 ignore start -- redundant check: conditionText pattern match above catches these cases first */
|
|
65
|
+
// Check if condition is a CallExpression with role check
|
|
66
|
+
if (ifStmt.test.type === 'CallExpression') {
|
|
67
|
+
const callExpr = ifStmt.test;
|
|
68
|
+
const callee = callExpr.callee;
|
|
69
|
+
if (callee.type === 'Identifier') {
|
|
70
|
+
const calleeName = callee.name.toLowerCase();
|
|
71
|
+
if (roleCheckPatterns.some(pattern => calleeName.includes(pattern.toLowerCase()))) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {
|
|
76
|
+
const propertyName = callee.property.name.toLowerCase();
|
|
77
|
+
if (roleCheckPatterns.some(pattern => propertyName.includes(pattern.toLowerCase()))) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/* c8 ignore stop */
|
|
83
|
+
}
|
|
84
|
+
// Check if current is inside a ConditionalExpression (ternary) with role check
|
|
85
|
+
if (current.parent && current.parent.type === 'ConditionalExpression') {
|
|
86
|
+
const condExpr = current.parent;
|
|
87
|
+
const testText = sourceCode.getText(condExpr.test);
|
|
88
|
+
// Check if test contains role check patterns
|
|
89
|
+
if (roleCheckPatterns.some(pattern => testText.toLowerCase().includes(pattern.toLowerCase()))) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
/* c8 ignore start -- redundant check: testText pattern match above catches these cases first */
|
|
93
|
+
// Check if test is a CallExpression with role check
|
|
94
|
+
if (condExpr.test.type === 'CallExpression') {
|
|
95
|
+
const callExpr = condExpr.test;
|
|
96
|
+
const callee = callExpr.callee;
|
|
97
|
+
if (callee.type === 'Identifier') {
|
|
98
|
+
const calleeName = callee.name.toLowerCase();
|
|
99
|
+
if (roleCheckPatterns.some(pattern => calleeName.includes(pattern.toLowerCase()))) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/* c8 ignore stop */
|
|
105
|
+
}
|
|
106
|
+
// Check if current is inside a CallExpression with role check
|
|
107
|
+
if (current.parent && current.parent.type === 'CallExpression') {
|
|
108
|
+
const callExpr = current.parent;
|
|
109
|
+
const callee = callExpr.callee;
|
|
110
|
+
if (callee.type === 'Identifier') {
|
|
111
|
+
const calleeName = callee.name.toLowerCase();
|
|
112
|
+
if (roleCheckPatterns.some(pattern => calleeName.includes(pattern.toLowerCase()))) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {
|
|
117
|
+
const propertyName = callee.property.name.toLowerCase();
|
|
118
|
+
if (roleCheckPatterns.some(pattern => propertyName.includes(pattern.toLowerCase()))) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Traverse up the AST
|
|
124
|
+
if ('parent' in current && current.parent) {
|
|
125
|
+
current = current.parent;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
exports.noPrivilegeEscalation = (0, eslint_devkit_2.createRule)({
|
|
134
|
+
name: 'no-privilege-escalation',
|
|
135
|
+
meta: {
|
|
136
|
+
type: 'problem',
|
|
137
|
+
docs: {
|
|
138
|
+
description: 'Detects potential privilege escalation vulnerabilities',
|
|
139
|
+
},
|
|
140
|
+
hasSuggestions: true,
|
|
141
|
+
messages: {
|
|
142
|
+
privilegeEscalation: (0, eslint_devkit_1.formatLLMMessage)({
|
|
143
|
+
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
144
|
+
issueName: 'Privilege Escalation',
|
|
145
|
+
cwe: 'CWE-269',
|
|
146
|
+
description: 'Potential privilege escalation: {{issue}} - user input used without role validation',
|
|
147
|
+
severity: 'HIGH',
|
|
148
|
+
fix: 'Add role check before using user input: if (!hasRole(user, requiredRole)) throw new Error("Unauthorized");',
|
|
149
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/269.html',
|
|
150
|
+
}),
|
|
151
|
+
addRoleCheck: (0, eslint_devkit_1.formatLLMMessage)({
|
|
152
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
153
|
+
issueName: 'Add Role Check',
|
|
154
|
+
description: 'Add role check before privilege operations',
|
|
155
|
+
severity: 'LOW',
|
|
156
|
+
fix: 'if (!hasRole(user, requiredRole)) throw new Error("Unauthorized")',
|
|
157
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/269.html',
|
|
158
|
+
}),
|
|
159
|
+
},
|
|
160
|
+
schema: [
|
|
161
|
+
{
|
|
162
|
+
type: 'object',
|
|
163
|
+
properties: {
|
|
164
|
+
allowInTests: {
|
|
165
|
+
type: 'boolean',
|
|
166
|
+
default: false,
|
|
167
|
+
description: 'Allow privilege escalation patterns in test files',
|
|
168
|
+
},
|
|
169
|
+
testFilePattern: {
|
|
170
|
+
type: 'string',
|
|
171
|
+
default: '\\.(test|spec)\\.(ts|tsx|js|jsx)$',
|
|
172
|
+
description: 'Test file pattern regex string',
|
|
173
|
+
},
|
|
174
|
+
roleCheckPatterns: {
|
|
175
|
+
type: 'array',
|
|
176
|
+
items: { type: 'string' },
|
|
177
|
+
default: DEFAULT_ROLE_CHECK_PATTERNS,
|
|
178
|
+
description: 'Role check patterns to recognize',
|
|
179
|
+
},
|
|
180
|
+
userInputPatterns: {
|
|
181
|
+
type: 'array',
|
|
182
|
+
items: { type: 'string' },
|
|
183
|
+
default: [],
|
|
184
|
+
description: 'Additional user input patterns to check (regex strings)',
|
|
185
|
+
},
|
|
186
|
+
ignorePatterns: {
|
|
187
|
+
type: 'array',
|
|
188
|
+
items: { type: 'string' },
|
|
189
|
+
default: [],
|
|
190
|
+
description: 'Additional patterns to ignore',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
additionalProperties: false,
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
defaultOptions: [
|
|
198
|
+
{
|
|
199
|
+
allowInTests: false,
|
|
200
|
+
testFilePattern: '\\.(test|spec)\\.(ts|tsx|js|jsx)$',
|
|
201
|
+
roleCheckPatterns: DEFAULT_ROLE_CHECK_PATTERNS,
|
|
202
|
+
userInputPatterns: [],
|
|
203
|
+
ignorePatterns: [],
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
create(context, [options = {}]) {
|
|
207
|
+
const { allowInTests = false, testFilePattern = '\\.(test|spec)\\.(ts|tsx|js|jsx)$', roleCheckPatterns = DEFAULT_ROLE_CHECK_PATTERNS, userInputPatterns: additionalUserInputPatterns = [], ignorePatterns = [], } = options;
|
|
208
|
+
const filename = context.getFilename();
|
|
209
|
+
const testFileRegex = new RegExp(testFilePattern);
|
|
210
|
+
const isTestFile = allowInTests && testFileRegex.test(filename);
|
|
211
|
+
const sourceCode = context.sourceCode || context.sourceCode;
|
|
212
|
+
// Combine default and additional user input patterns
|
|
213
|
+
const userInputPatterns = [
|
|
214
|
+
...DEFAULT_USER_INPUT_PATTERNS,
|
|
215
|
+
...additionalUserInputPatterns.map(pattern => new RegExp(pattern, 'i')),
|
|
216
|
+
];
|
|
217
|
+
/**
|
|
218
|
+
* Check AssignmentExpression for privilege escalation
|
|
219
|
+
*/
|
|
220
|
+
function checkAssignmentExpression(node) {
|
|
221
|
+
if (isTestFile) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
// Check for role assignment from user input
|
|
225
|
+
// Pattern: user.role = req.body.role
|
|
226
|
+
if (node.left.type === 'MemberExpression' &&
|
|
227
|
+
node.left.property.type === 'Identifier') {
|
|
228
|
+
const propertyName = node.left.property.name.toLowerCase();
|
|
229
|
+
// Check if it's a role/permission related property
|
|
230
|
+
if (['role', 'permission', 'privilege', 'access', 'level'].includes(propertyName)) {
|
|
231
|
+
const text = sourceCode.getText(node);
|
|
232
|
+
// Check if it matches any ignore pattern
|
|
233
|
+
if (matchesIgnorePattern(text, ignorePatterns)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// Check if right side contains user input
|
|
237
|
+
if (containsUserInput(node.right, sourceCode, userInputPatterns)) {
|
|
238
|
+
// Check if it's inside a role check
|
|
239
|
+
if (!isInsideRoleCheck(node, sourceCode, roleCheckPatterns)) {
|
|
240
|
+
context.report({
|
|
241
|
+
node: node,
|
|
242
|
+
messageId: 'privilegeEscalation',
|
|
243
|
+
data: {
|
|
244
|
+
issue: `Role assignment from user input: ${sourceCode.getText(node.left)} = ${sourceCode.getText(node.right)}`,
|
|
245
|
+
},
|
|
246
|
+
suggest: [
|
|
247
|
+
{
|
|
248
|
+
messageId: 'addRoleCheck',
|
|
249
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
250
|
+
fix: (_fixer) => null,
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Check CallExpression for privilege operations with user input
|
|
261
|
+
*/
|
|
262
|
+
function checkCallExpression(node) {
|
|
263
|
+
if (isTestFile) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
// Check for privilege-related function calls with user input
|
|
267
|
+
const callee = node.callee;
|
|
268
|
+
let isPrivilegeOperation = false;
|
|
269
|
+
let operationName = '';
|
|
270
|
+
if (callee.type === 'Identifier') {
|
|
271
|
+
const calleeName = callee.name.toLowerCase();
|
|
272
|
+
if (['setrole', 'grant', 'revoke', 'elevate', 'promote'].some(op => calleeName.includes(op))) {
|
|
273
|
+
isPrivilegeOperation = true;
|
|
274
|
+
operationName = callee.name;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {
|
|
278
|
+
const propertyName = callee.property.name.toLowerCase();
|
|
279
|
+
if (['setrole', 'grant', 'revoke', 'elevate', 'promote', 'updaterole'].some(op => propertyName.includes(op))) {
|
|
280
|
+
isPrivilegeOperation = true;
|
|
281
|
+
operationName = propertyName;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (isPrivilegeOperation) {
|
|
285
|
+
const text = sourceCode.getText(node);
|
|
286
|
+
// Check if it matches any ignore pattern
|
|
287
|
+
if (matchesIgnorePattern(text, ignorePatterns)) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// Check if any argument contains user input
|
|
291
|
+
for (const arg of node.arguments) {
|
|
292
|
+
if (containsUserInput(arg, sourceCode, userInputPatterns)) {
|
|
293
|
+
// Check if it's inside a role check
|
|
294
|
+
if (!isInsideRoleCheck(node, sourceCode, roleCheckPatterns)) {
|
|
295
|
+
context.report({
|
|
296
|
+
node: node,
|
|
297
|
+
messageId: 'privilegeEscalation',
|
|
298
|
+
data: {
|
|
299
|
+
issue: `Privilege operation (${operationName}) with user input without role validation`,
|
|
300
|
+
},
|
|
301
|
+
suggest: [
|
|
302
|
+
{
|
|
303
|
+
messageId: 'addRoleCheck',
|
|
304
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
305
|
+
fix: (_fixer) => null,
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
});
|
|
309
|
+
return; // Report once per call
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
AssignmentExpression: checkAssignmentExpression,
|
|
317
|
+
CallExpression: checkCallExpression,
|
|
318
|
+
};
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
//# sourceMappingURL=no-privilege-escalation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-privilege-escalation.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-secure-coding/src/rules/security/no-privilege-escalation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface Options {
|
|
2
|
+
/** Allow certain common patterns. Default: false */
|
|
3
|
+
allowCommonPatterns?: boolean;
|
|
4
|
+
/** Maximum pattern length to analyze. Default: 500 */
|
|
5
|
+
maxPatternLength?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const noRedosVulnerableRegex: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noRedosVulnerableRegex = void 0;
|
|
4
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
5
|
+
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
6
|
+
// Type guard for regex literal nodes
|
|
7
|
+
const isRegExpLiteral = (node) => {
|
|
8
|
+
return node.type === 'Literal' && Object.prototype.hasOwnProperty.call(node, 'regex');
|
|
9
|
+
};
|
|
10
|
+
const REDOS_PATTERNS = [
|
|
11
|
+
{
|
|
12
|
+
pattern: /\([^)]*\+\)\+|\([^)]*\*\)\*|\([^)]*\?\)\?/,
|
|
13
|
+
name: 'Nested Quantifiers',
|
|
14
|
+
description: 'Nested quantifiers like (a+)+, (a*)*, (a?)? cause exponential backtracking',
|
|
15
|
+
example: {
|
|
16
|
+
bad: '/(a+)+b/',
|
|
17
|
+
good: '/(?>a+)b/ or /a+b/'
|
|
18
|
+
},
|
|
19
|
+
fix: 'Use atomic groups (?>...) or restructure to avoid nesting',
|
|
20
|
+
severity: 'critical'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
pattern: /\([^)]*\+[^)]*\)\+|\([^)]*\*[^)]*\)\*/,
|
|
24
|
+
name: 'Nested Repetition',
|
|
25
|
+
description: 'Quantifiers nested within groups with quantifiers',
|
|
26
|
+
example: {
|
|
27
|
+
bad: '/(x+)+y/',
|
|
28
|
+
good: '/x+y/'
|
|
29
|
+
},
|
|
30
|
+
fix: 'Flatten nested quantifiers',
|
|
31
|
+
severity: 'critical'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
pattern: /\([^)]*\|[^)]*\)\+|\([^)]*\|[^)]*\)\*/,
|
|
35
|
+
name: 'Alternation with Quantifier',
|
|
36
|
+
description: 'Alternation groups with quantifiers can cause backtracking',
|
|
37
|
+
example: {
|
|
38
|
+
bad: '/(a|b)+c/',
|
|
39
|
+
good: '/[ab]+c/'
|
|
40
|
+
},
|
|
41
|
+
fix: 'Use character classes instead of alternation when possible',
|
|
42
|
+
severity: 'high'
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
pattern: /\.\*\.\*|\.\+\+\.\+/,
|
|
46
|
+
name: 'Nested Wildcards',
|
|
47
|
+
description: 'Nested wildcard quantifiers cause catastrophic backtracking',
|
|
48
|
+
example: {
|
|
49
|
+
bad: '/.*.*/',
|
|
50
|
+
good: '/.*/ or be more specific'
|
|
51
|
+
},
|
|
52
|
+
fix: 'Remove redundant wildcards or be more specific',
|
|
53
|
+
severity: 'critical'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
pattern: /\([^)]*\)\{[0-9]+,\}[^)]*\([^)]*\)\{[0-9]+,\}/,
|
|
57
|
+
name: 'Multiple Repetition Groups',
|
|
58
|
+
description: 'Multiple repetition groups can cause exponential backtracking',
|
|
59
|
+
example: {
|
|
60
|
+
bad: '/(a{2,})+(b{2,})+/',
|
|
61
|
+
good: 'Restructure to avoid nested repetitions'
|
|
62
|
+
},
|
|
63
|
+
fix: 'Restructure regex to avoid nested repetitions',
|
|
64
|
+
severity: 'high'
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* Check if a regex pattern contains ReDoS vulnerabilities
|
|
69
|
+
*/
|
|
70
|
+
function hasReDoSVulnerability(pattern) {
|
|
71
|
+
for (const redosPattern of REDOS_PATTERNS) {
|
|
72
|
+
if (redosPattern.pattern.test(pattern)) {
|
|
73
|
+
return redosPattern;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Additional checks for common ReDoS patterns
|
|
77
|
+
// Nested quantifiers: (a+)+, (a*)*, (a?)?
|
|
78
|
+
if (/(\([^)]*[+*?][^)]*\)[+*?])/.test(pattern)) {
|
|
79
|
+
return {
|
|
80
|
+
pattern: /\([^)]*[+*?][^)]*\)[+*?]/,
|
|
81
|
+
name: 'Nested Quantifier Pattern',
|
|
82
|
+
description: 'Pattern contains nested quantifiers that can cause exponential backtracking',
|
|
83
|
+
example: {
|
|
84
|
+
bad: pattern.substring(0, 30),
|
|
85
|
+
good: 'Restructure to avoid nested quantifiers'
|
|
86
|
+
},
|
|
87
|
+
fix: 'Use atomic groups or restructure regex',
|
|
88
|
+
severity: 'critical'
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Generate fix suggestions based on the vulnerability
|
|
95
|
+
*/
|
|
96
|
+
function generateFixSuggestions(vulnerability) {
|
|
97
|
+
const suggestions = [];
|
|
98
|
+
if (vulnerability.severity === 'critical' || vulnerability.name.includes('Nested')) {
|
|
99
|
+
suggestions.push({
|
|
100
|
+
messageId: 'useAtomicGroups',
|
|
101
|
+
description: vulnerability.fix
|
|
102
|
+
});
|
|
103
|
+
suggestions.push({
|
|
104
|
+
messageId: 'restructureRegex',
|
|
105
|
+
description: 'Restructure the regex to avoid nested quantifiers'
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (vulnerability.name.includes('Quantifier')) {
|
|
109
|
+
suggestions.push({
|
|
110
|
+
messageId: 'usePossessiveQuantifiers',
|
|
111
|
+
description: 'Use possessive quantifiers (*+, ++, ?+) if supported'
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
suggestions.push({
|
|
115
|
+
messageId: 'useSafeLibrary',
|
|
116
|
+
description: 'Consider using safe-regex library to validate patterns'
|
|
117
|
+
});
|
|
118
|
+
return suggestions;
|
|
119
|
+
}
|
|
120
|
+
exports.noRedosVulnerableRegex = (0, eslint_devkit_2.createRule)({
|
|
121
|
+
name: 'no-redos-vulnerable-regex',
|
|
122
|
+
meta: {
|
|
123
|
+
type: 'problem',
|
|
124
|
+
docs: {
|
|
125
|
+
description: 'Detects ReDoS-vulnerable regex patterns in literal regex patterns',
|
|
126
|
+
},
|
|
127
|
+
hasSuggestions: true,
|
|
128
|
+
messages: {
|
|
129
|
+
redosVulnerable: (0, eslint_devkit_1.formatLLMMessage)({
|
|
130
|
+
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
131
|
+
issueName: 'ReDoS vulnerable regex',
|
|
132
|
+
cwe: 'CWE-400',
|
|
133
|
+
description: '{{vulnerabilityName}}: {{description}}',
|
|
134
|
+
severity: '{{severity}}',
|
|
135
|
+
fix: '{{fix}}',
|
|
136
|
+
documentationLink: 'https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS',
|
|
137
|
+
}),
|
|
138
|
+
useAtomicGroups: (0, eslint_devkit_1.formatLLMMessage)({
|
|
139
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
140
|
+
issueName: 'Use Atomic Groups',
|
|
141
|
+
description: 'Use atomic groups to prevent backtracking',
|
|
142
|
+
severity: 'LOW',
|
|
143
|
+
fix: '(?>...) to prevent backtracking',
|
|
144
|
+
documentationLink: 'https://www.regular-expressions.info/atomic.html',
|
|
145
|
+
}),
|
|
146
|
+
usePossessiveQuantifiers: (0, eslint_devkit_1.formatLLMMessage)({
|
|
147
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
148
|
+
issueName: 'Use Possessive Quantifiers',
|
|
149
|
+
description: 'Use possessive quantifiers',
|
|
150
|
+
severity: 'LOW',
|
|
151
|
+
fix: '*+, ++, ?+ (if supported)',
|
|
152
|
+
documentationLink: 'https://www.regular-expressions.info/possessive.html',
|
|
153
|
+
}),
|
|
154
|
+
restructureRegex: (0, eslint_devkit_1.formatLLMMessage)({
|
|
155
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
156
|
+
issueName: 'Restructure Regex',
|
|
157
|
+
description: 'Restructure to avoid nested quantifiers',
|
|
158
|
+
severity: 'LOW',
|
|
159
|
+
fix: 'Avoid (a+)+ patterns',
|
|
160
|
+
documentationLink: 'https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS',
|
|
161
|
+
}),
|
|
162
|
+
useSafeLibrary: (0, eslint_devkit_1.formatLLMMessage)({
|
|
163
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
164
|
+
issueName: 'Use safe-regex',
|
|
165
|
+
description: 'Validate with safe-regex library',
|
|
166
|
+
severity: 'LOW',
|
|
167
|
+
fix: 'if (safeRegex(pattern)) { new RegExp(pattern) }',
|
|
168
|
+
documentationLink: 'https://github.com/substack/safe-regex',
|
|
169
|
+
}),
|
|
170
|
+
},
|
|
171
|
+
schema: [
|
|
172
|
+
{
|
|
173
|
+
type: 'object',
|
|
174
|
+
properties: {
|
|
175
|
+
allowCommonPatterns: {
|
|
176
|
+
type: 'boolean',
|
|
177
|
+
default: false,
|
|
178
|
+
description: 'Allow certain common patterns',
|
|
179
|
+
},
|
|
180
|
+
maxPatternLength: {
|
|
181
|
+
type: 'number',
|
|
182
|
+
default: 500,
|
|
183
|
+
minimum: 1,
|
|
184
|
+
description: 'Maximum pattern length to analyze',
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
additionalProperties: false,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
defaultOptions: [
|
|
192
|
+
{
|
|
193
|
+
allowCommonPatterns: false,
|
|
194
|
+
maxPatternLength: 500,
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
create(context, [options = {}]) {
|
|
198
|
+
const { allowCommonPatterns = false, maxPatternLength = 500 } = options || {};
|
|
199
|
+
/**
|
|
200
|
+
* Check literal regex patterns for ReDoS vulnerabilities
|
|
201
|
+
*/
|
|
202
|
+
function checkLiteralRegExp(node) {
|
|
203
|
+
if (!isRegExpLiteral(node)) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const pattern = node.regex.pattern;
|
|
207
|
+
// Skip if pattern is too long (performance)
|
|
208
|
+
if (pattern.length > maxPatternLength) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const vulnerability = hasReDoSVulnerability(pattern);
|
|
212
|
+
if (!vulnerability) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// Allow common patterns if configured
|
|
216
|
+
if (allowCommonPatterns && (vulnerability.severity === 'medium' || vulnerability.name === 'Alternation with Quantifier')) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const suggestions = generateFixSuggestions(vulnerability);
|
|
220
|
+
const severity = vulnerability.severity.toUpperCase();
|
|
221
|
+
context.report({
|
|
222
|
+
node,
|
|
223
|
+
messageId: 'redosVulnerable',
|
|
224
|
+
data: {
|
|
225
|
+
vulnerabilityName: vulnerability.name,
|
|
226
|
+
description: vulnerability.description,
|
|
227
|
+
severity,
|
|
228
|
+
fix: vulnerability.fix,
|
|
229
|
+
},
|
|
230
|
+
suggest: suggestions.map(suggestion => ({
|
|
231
|
+
messageId: suggestion.messageId,
|
|
232
|
+
fix: () => null, // Complex refactoring, cannot auto-fix
|
|
233
|
+
})),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Check new RegExp() calls for ReDoS vulnerabilities
|
|
238
|
+
*/
|
|
239
|
+
function checkNewRegExp(node) {
|
|
240
|
+
// Check for new RegExp(pattern) or RegExp(pattern)
|
|
241
|
+
let callee;
|
|
242
|
+
if (node.type === 'NewExpression') {
|
|
243
|
+
callee = node.callee;
|
|
244
|
+
}
|
|
245
|
+
else if (node.type === 'CallExpression') {
|
|
246
|
+
callee = node.callee;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
/* c8 ignore next */
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const isRegExp = callee.type === 'Identifier' && callee.name === 'RegExp';
|
|
253
|
+
if (!isRegExp) {
|
|
254
|
+
/* c8 ignore next */
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
// Check if first argument is a string literal
|
|
258
|
+
if (node.arguments.length === 0) {
|
|
259
|
+
/* c8 ignore next */
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const firstArg = node.arguments[0];
|
|
263
|
+
if (firstArg.type !== 'Literal' || typeof firstArg.value !== 'string') {
|
|
264
|
+
/* c8 ignore next */
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const pattern = firstArg.value;
|
|
268
|
+
// Skip if pattern is too long (performance)
|
|
269
|
+
if (pattern.length > maxPatternLength) {
|
|
270
|
+
/* c8 ignore next */
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const vulnerability = hasReDoSVulnerability(pattern);
|
|
274
|
+
if (!vulnerability) {
|
|
275
|
+
/* c8 ignore next */
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
// Allow common patterns if configured
|
|
279
|
+
if (allowCommonPatterns && (vulnerability.severity === 'medium' || vulnerability.name === 'Alternation with Quantifier')) {
|
|
280
|
+
/* c8 ignore next */
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const suggestions = generateFixSuggestions(vulnerability);
|
|
284
|
+
const severity = vulnerability.severity.toUpperCase();
|
|
285
|
+
context.report({
|
|
286
|
+
node,
|
|
287
|
+
messageId: 'redosVulnerable',
|
|
288
|
+
data: {
|
|
289
|
+
vulnerabilityName: vulnerability.name,
|
|
290
|
+
description: vulnerability.description,
|
|
291
|
+
severity,
|
|
292
|
+
fix: vulnerability.fix,
|
|
293
|
+
},
|
|
294
|
+
suggest: suggestions.map(suggestion => ({
|
|
295
|
+
messageId: suggestion.messageId,
|
|
296
|
+
fix: () => null, // Complex refactoring, cannot auto-fix
|
|
297
|
+
})),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
Literal: checkLiteralRegExp,
|
|
302
|
+
CallExpression: checkNewRegExp,
|
|
303
|
+
NewExpression: checkNewRegExp,
|
|
304
|
+
};
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
//# sourceMappingURL=no-redos-vulnerable-regex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-redos-vulnerable-regex.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-secure-coding/src/rules/security/no-redos-vulnerable-regex.ts"],"names":[],"mappings":"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"}
|