eslint-plugin-secure-coding 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +1 -1
  3. package/README.md +90 -422
  4. package/package.json +6 -5
  5. package/src/index.d.ts +5 -14
  6. package/src/index.js +36 -265
  7. package/src/rules/detect-non-literal-regexp/index.d.ts +20 -1
  8. package/src/rules/detect-non-literal-regexp/index.js +5 -0
  9. package/src/rules/detect-object-injection/index.d.ts +25 -1
  10. package/src/rules/detect-object-injection/index.js +5 -0
  11. package/src/rules/detect-weak-password-validation/index.d.ts +8 -2
  12. package/src/rules/detect-weak-password-validation/index.js +6 -1
  13. package/src/rules/no-directive-injection/index.d.ts +27 -1
  14. package/src/rules/no-directive-injection/index.js +5 -0
  15. package/src/rules/no-electron-security-issues/index.d.ts +27 -1
  16. package/src/rules/no-electron-security-issues/index.js +5 -0
  17. package/src/rules/no-format-string-injection/index.d.ts +28 -1
  18. package/src/rules/no-format-string-injection/index.js +5 -0
  19. package/src/rules/no-graphql-injection/index.d.ts +29 -1
  20. package/src/rules/no-graphql-injection/index.js +5 -0
  21. package/src/rules/no-hardcoded-credentials/index.d.ts +19 -1
  22. package/src/rules/no-hardcoded-credentials/index.js +5 -0
  23. package/src/rules/no-hardcoded-session-tokens/index.d.ts +8 -2
  24. package/src/rules/no-hardcoded-session-tokens/index.js +6 -1
  25. package/src/rules/no-improper-sanitization/index.d.ts +27 -1
  26. package/src/rules/no-improper-sanitization/index.js +5 -0
  27. package/src/rules/no-improper-type-validation/index.d.ts +27 -1
  28. package/src/rules/no-improper-type-validation/index.js +5 -0
  29. package/src/rules/no-insecure-comparison/index.d.ts +20 -1
  30. package/src/rules/no-insecure-comparison/index.js +5 -0
  31. package/src/rules/no-ldap-injection/index.d.ts +30 -1
  32. package/src/rules/no-ldap-injection/index.js +5 -0
  33. package/src/rules/no-missing-authentication/index.d.ts +20 -1
  34. package/src/rules/no-missing-authentication/index.js +5 -1
  35. package/src/rules/no-pii-in-logs/index.d.ts +8 -4
  36. package/src/rules/no-pii-in-logs/index.js +15 -12
  37. package/src/rules/no-privilege-escalation/index.d.ts +20 -1
  38. package/src/rules/no-privilege-escalation/index.js +5 -0
  39. package/src/rules/no-redos-vulnerable-regex/index.d.ts +22 -1
  40. package/src/rules/no-redos-vulnerable-regex/index.js +5 -0
  41. package/src/rules/no-sensitive-data-exposure/index.d.ts +20 -1
  42. package/src/rules/no-sensitive-data-exposure/index.js +5 -0
  43. package/src/rules/no-unchecked-loop-condition/index.d.ts +27 -1
  44. package/src/rules/no-unchecked-loop-condition/index.js +5 -0
  45. package/src/rules/no-unlimited-resource-allocation/index.d.ts +27 -1
  46. package/src/rules/no-unlimited-resource-allocation/index.js +5 -0
  47. package/src/rules/no-unsafe-deserialization/index.d.ts +31 -1
  48. package/src/rules/no-unsafe-deserialization/index.js +5 -0
  49. package/src/rules/no-unsafe-regex-construction/index.d.ts +22 -1
  50. package/src/rules/no-unsafe-regex-construction/index.js +5 -0
  51. package/src/rules/no-weak-password-recovery/index.d.ts +27 -1
  52. package/src/rules/no-weak-password-recovery/index.js +5 -0
  53. package/src/rules/no-xpath-injection/index.d.ts +30 -1
  54. package/src/rules/no-xpath-injection/index.js +5 -0
  55. package/src/rules/no-xxe-injection/index.d.ts +30 -1
  56. package/src/rules/no-xxe-injection/index.js +5 -0
  57. package/src/rules/require-backend-authorization/index.d.ts +8 -2
  58. package/src/rules/require-backend-authorization/index.js +6 -1
  59. package/src/rules/require-secure-defaults/index.d.ts +8 -4
  60. package/src/rules/require-secure-defaults/index.js +7 -6
  61. package/src/types/index.d.ts +10 -52
  62. package/src/types/index.js +3 -12
  63. package/src/rules/detect-child-process/index.d.ts +0 -11
  64. package/src/rules/detect-child-process/index.js +0 -529
  65. package/src/rules/detect-eval-with-expression/index.d.ts +0 -9
  66. package/src/rules/detect-eval-with-expression/index.js +0 -392
  67. package/src/rules/detect-mixed-content/index.d.ts +0 -8
  68. package/src/rules/detect-mixed-content/index.js +0 -44
  69. package/src/rules/detect-non-literal-fs-filename/index.d.ts +0 -7
  70. package/src/rules/detect-non-literal-fs-filename/index.js +0 -454
  71. package/src/rules/detect-suspicious-dependencies/index.d.ts +0 -8
  72. package/src/rules/detect-suspicious-dependencies/index.js +0 -71
  73. package/src/rules/no-allow-arbitrary-loads/index.d.ts +0 -8
  74. package/src/rules/no-allow-arbitrary-loads/index.js +0 -47
  75. package/src/rules/no-arbitrary-file-access/index.d.ts +0 -13
  76. package/src/rules/no-arbitrary-file-access/index.js +0 -195
  77. package/src/rules/no-buffer-overread/index.d.ts +0 -29
  78. package/src/rules/no-buffer-overread/index.js +0 -606
  79. package/src/rules/no-clickjacking/index.d.ts +0 -10
  80. package/src/rules/no-clickjacking/index.js +0 -396
  81. package/src/rules/no-client-side-auth-logic/index.d.ts +0 -6
  82. package/src/rules/no-client-side-auth-logic/index.js +0 -69
  83. package/src/rules/no-credentials-in-query-params/index.d.ts +0 -8
  84. package/src/rules/no-credentials-in-query-params/index.js +0 -57
  85. package/src/rules/no-data-in-temp-storage/index.d.ts +0 -6
  86. package/src/rules/no-data-in-temp-storage/index.js +0 -64
  87. package/src/rules/no-debug-code-in-production/index.d.ts +0 -8
  88. package/src/rules/no-debug-code-in-production/index.js +0 -51
  89. package/src/rules/no-disabled-certificate-validation/index.d.ts +0 -6
  90. package/src/rules/no-disabled-certificate-validation/index.js +0 -61
  91. package/src/rules/no-dynamic-dependency-loading/index.d.ts +0 -8
  92. package/src/rules/no-dynamic-dependency-loading/index.js +0 -51
  93. package/src/rules/no-exposed-debug-endpoints/index.d.ts +0 -6
  94. package/src/rules/no-exposed-debug-endpoints/index.js +0 -62
  95. package/src/rules/no-exposed-sensitive-data/index.d.ts +0 -11
  96. package/src/rules/no-exposed-sensitive-data/index.js +0 -340
  97. package/src/rules/no-http-urls/index.d.ts +0 -12
  98. package/src/rules/no-http-urls/index.js +0 -114
  99. package/src/rules/no-insecure-redirects/index.d.ts +0 -7
  100. package/src/rules/no-insecure-redirects/index.js +0 -216
  101. package/src/rules/no-insecure-websocket/index.d.ts +0 -6
  102. package/src/rules/no-insecure-websocket/index.js +0 -61
  103. package/src/rules/no-missing-cors-check/index.d.ts +0 -9
  104. package/src/rules/no-missing-cors-check/index.js +0 -399
  105. package/src/rules/no-missing-csrf-protection/index.d.ts +0 -11
  106. package/src/rules/no-missing-csrf-protection/index.js +0 -180
  107. package/src/rules/no-missing-security-headers/index.d.ts +0 -7
  108. package/src/rules/no-missing-security-headers/index.js +0 -218
  109. package/src/rules/no-password-in-url/index.d.ts +0 -8
  110. package/src/rules/no-password-in-url/index.js +0 -54
  111. package/src/rules/no-permissive-cors/index.d.ts +0 -8
  112. package/src/rules/no-permissive-cors/index.js +0 -65
  113. package/src/rules/no-sensitive-data-in-analytics/index.d.ts +0 -8
  114. package/src/rules/no-sensitive-data-in-analytics/index.js +0 -62
  115. package/src/rules/no-sensitive-data-in-cache/index.d.ts +0 -8
  116. package/src/rules/no-sensitive-data-in-cache/index.js +0 -52
  117. package/src/rules/no-toctou-vulnerability/index.d.ts +0 -7
  118. package/src/rules/no-toctou-vulnerability/index.js +0 -208
  119. package/src/rules/no-tracking-without-consent/index.d.ts +0 -6
  120. package/src/rules/no-tracking-without-consent/index.js +0 -67
  121. package/src/rules/no-unencrypted-transmission/index.d.ts +0 -11
  122. package/src/rules/no-unencrypted-transmission/index.js +0 -236
  123. package/src/rules/no-unescaped-url-parameter/index.d.ts +0 -9
  124. package/src/rules/no-unescaped-url-parameter/index.js +0 -355
  125. package/src/rules/no-unsafe-dynamic-require/index.d.ts +0 -5
  126. package/src/rules/no-unsafe-dynamic-require/index.js +0 -106
  127. package/src/rules/no-unvalidated-deeplinks/index.d.ts +0 -6
  128. package/src/rules/no-unvalidated-deeplinks/index.js +0 -62
  129. package/src/rules/no-unvalidated-user-input/index.d.ts +0 -9
  130. package/src/rules/no-unvalidated-user-input/index.js +0 -420
  131. package/src/rules/no-verbose-error-messages/index.d.ts +0 -8
  132. package/src/rules/no-verbose-error-messages/index.js +0 -68
  133. package/src/rules/no-zip-slip/index.d.ts +0 -9
  134. package/src/rules/no-zip-slip/index.js +0 -445
  135. package/src/rules/require-code-minification/index.d.ts +0 -8
  136. package/src/rules/require-code-minification/index.js +0 -47
  137. package/src/rules/require-csp-headers/index.d.ts +0 -6
  138. package/src/rules/require-csp-headers/index.js +0 -64
  139. package/src/rules/require-data-minimization/index.d.ts +0 -8
  140. package/src/rules/require-data-minimization/index.js +0 -53
  141. package/src/rules/require-dependency-integrity/index.d.ts +0 -6
  142. package/src/rules/require-dependency-integrity/index.js +0 -64
  143. package/src/rules/require-https-only/index.d.ts +0 -8
  144. package/src/rules/require-https-only/index.js +0 -62
  145. package/src/rules/require-mime-type-validation/index.d.ts +0 -6
  146. package/src/rules/require-mime-type-validation/index.js +0 -66
  147. package/src/rules/require-network-timeout/index.d.ts +0 -8
  148. package/src/rules/require-network-timeout/index.js +0 -50
  149. package/src/rules/require-package-lock/index.d.ts +0 -8
  150. package/src/rules/require-package-lock/index.js +0 -63
  151. package/src/rules/require-secure-credential-storage/index.d.ts +0 -8
  152. package/src/rules/require-secure-credential-storage/index.js +0 -50
  153. package/src/rules/require-secure-deletion/index.d.ts +0 -8
  154. package/src/rules/require-secure-deletion/index.js +0 -44
  155. package/src/rules/require-storage-encryption/index.d.ts +0 -8
  156. package/src/rules/require-storage-encryption/index.js +0 -50
  157. package/src/rules/require-url-validation/index.d.ts +0 -6
  158. package/src/rules/require-url-validation/index.js +0 -72
@@ -1,355 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noUnescapedUrlParameter = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
6
- /**
7
- * Check if a node is inside a URL encoding function call
8
- */
9
- function isInsideEncodingCall(node, sourceCode, trustedLibraries) {
10
- let current = node;
11
- while (current) {
12
- if (current.type === 'CallExpression') {
13
- const callee = current.callee;
14
- // Check for encodeURIComponent, encodeURI
15
- if (callee.type === 'Identifier') {
16
- const calleeName = callee.name;
17
- if (['encodeURIComponent', 'encodeURI', 'escape'].includes(calleeName)) {
18
- return true;
19
- }
20
- }
21
- // Check if it's a trusted library call
22
- if (callee.type === 'MemberExpression') {
23
- const object = callee.object;
24
- if (object.type === 'Identifier') {
25
- const objectName = object.name.toLowerCase();
26
- if (trustedLibraries.some(lib => objectName.includes(lib.toLowerCase()))) {
27
- return true;
28
- }
29
- }
30
- }
31
- }
32
- // Traverse up the AST
33
- if ('parent' in current && current.parent) {
34
- current = current.parent;
35
- }
36
- else {
37
- break;
38
- }
39
- }
40
- return false;
41
- }
42
- /**
43
- * Check if a string matches any ignore pattern
44
- */
45
- function matchesIgnorePattern(text, ignorePatterns) {
46
- return ignorePatterns.some(pattern => {
47
- try {
48
- const regex = new RegExp(pattern, 'i');
49
- return regex.test(text);
50
- }
51
- catch {
52
- return false;
53
- }
54
- });
55
- }
56
- /**
57
- * Check if a node is a URL construction pattern
58
- */
59
- function isUrlConstruction(node, sourceCode) {
60
- let text = sourceCode.getText(node);
61
- // For template literals, combine raw strings to improve pattern detection
62
- if (node.type === 'TemplateLiteral') {
63
- text = node.quasis.map(q => q.value.raw).join('');
64
- }
65
- // Check for URL construction patterns
66
- const urlPatterns = [
67
- /\bhttps?:\/\//, // HTTP/HTTPS URLs
68
- /\bnew\s+URL\s*\(/,
69
- /\burl\s*[=:]\s*/, // url = or url:
70
- /\burl\s*\+/, // url +
71
- /\bwindow\.location/,
72
- /\blocation\.href/,
73
- /\bwindow\.open\s*\(/,
74
- /\?[^=]+=/, // Query parameters
75
- ];
76
- return urlPatterns.some(pattern => pattern.test(text));
77
- }
78
- exports.noUnescapedUrlParameter = (0, eslint_devkit_2.createRule)({
79
- name: 'no-unescaped-url-parameter',
80
- meta: {
81
- type: 'problem',
82
- docs: {
83
- description: 'Detects unescaped URL parameters',
84
- },
85
- hasSuggestions: true,
86
- messages: {
87
- unescapedUrlParameter: (0, eslint_devkit_1.formatLLMMessage)({
88
- icon: eslint_devkit_1.MessageIcons.SECURITY,
89
- issueName: 'Unescaped URL Parameter',
90
- cwe: 'CWE-79',
91
- description: 'Unescaped URL parameter detected: {{parameter}}',
92
- severity: 'HIGH',
93
- fix: '{{safeAlternative}}',
94
- documentationLink: 'https://cwe.mitre.org/data/definitions/79.html',
95
- }),
96
- useEncodeURIComponent: (0, eslint_devkit_1.formatLLMMessage)({
97
- icon: eslint_devkit_1.MessageIcons.INFO,
98
- issueName: 'Use encodeURIComponent',
99
- description: 'Use encodeURIComponent for URL params',
100
- severity: 'LOW',
101
- fix: '`https://example.com?q=${encodeURIComponent(param)}`',
102
- documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent',
103
- }),
104
- useURLSearchParams: (0, eslint_devkit_1.formatLLMMessage)({
105
- icon: eslint_devkit_1.MessageIcons.INFO,
106
- issueName: 'Use URLSearchParams',
107
- description: 'Use URLSearchParams for safe URL construction',
108
- severity: 'LOW',
109
- fix: 'new URLSearchParams({ q: param }).toString()',
110
- documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams',
111
- }),
112
- },
113
- schema: [
114
- {
115
- type: 'object',
116
- properties: {
117
- allowInTests: {
118
- type: 'boolean',
119
- default: false,
120
- description: 'Allow unescaped URL parameters in test files',
121
- },
122
- trustedLibraries: {
123
- type: 'array',
124
- items: { type: 'string' },
125
- default: ['url', 'querystring'],
126
- description: 'Trusted URL construction libraries',
127
- },
128
- ignorePatterns: {
129
- type: 'array',
130
- items: { type: 'string' },
131
- default: [],
132
- description: 'Additional safe patterns to ignore',
133
- },
134
- },
135
- additionalProperties: false,
136
- },
137
- ],
138
- },
139
- defaultOptions: [
140
- {
141
- allowInTests: false,
142
- trustedLibraries: ['url', 'querystring'],
143
- ignorePatterns: [],
144
- },
145
- ],
146
- create(context, [options = {}]) {
147
- const { allowInTests = false, trustedLibraries = ['url', 'querystring'], ignorePatterns = [], } = options;
148
- const filename = context.getFilename();
149
- const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
150
- const sourceCode = context.sourceCode || context.sourceCode;
151
- function checkTemplateLiteral(node) {
152
- if (isTestFile) {
153
- return;
154
- }
155
- // Check if this is a URL construction
156
- if (!isUrlConstruction(node, sourceCode)) {
157
- return;
158
- }
159
- // Check each expression in the template
160
- for (const expression of node.expressions) {
161
- const text = sourceCode.getText(expression);
162
- // Check if it matches any ignore pattern
163
- if (matchesIgnorePattern(text, ignorePatterns)) {
164
- continue;
165
- }
166
- // Check if it's already encoded
167
- if (isInsideEncodingCall(expression, sourceCode, trustedLibraries)) {
168
- continue;
169
- }
170
- // Check if it's a user input pattern
171
- const userInputPatterns = [
172
- /\breq\.(query|params|body|headers|cookies)/,
173
- /\brequest\.(query|params|body)/,
174
- /\buserInput\b/i,
175
- /\binput\b/i,
176
- /\bsearchParams\b/,
177
- /\bparam\b/i, // Generic param variable
178
- ];
179
- // Check both the expression text and the full template literal
180
- // This catches nested patterns like req.query.id
181
- const fullText = sourceCode.getText(node);
182
- const exprText = sourceCode.getText(expression);
183
- const isUserInput = userInputPatterns.some(pattern => pattern.test(text)) ||
184
- userInputPatterns.some(pattern => pattern.test(fullText)) ||
185
- userInputPatterns.some(pattern => pattern.test(exprText)) ||
186
- // Also check for nested member expressions like req.query.id
187
- (expression.type === 'MemberExpression' &&
188
- userInputPatterns.some(pattern => {
189
- // Check the full expression including nested properties
190
- return pattern.test(exprText);
191
- }));
192
- if (isUserInput) {
193
- context.report({
194
- node: expression,
195
- messageId: 'unescapedUrlParameter',
196
- data: {
197
- parameter: text,
198
- safeAlternative: `Use encodeURIComponent() or URLSearchParams: const url = \`https://example.com?q=\${encodeURIComponent(${text})}\`;`,
199
- },
200
- suggest: [
201
- {
202
- messageId: 'useEncodeURIComponent',
203
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
204
- fix: (_fixer) => null,
205
- },
206
- {
207
- messageId: 'useURLSearchParams',
208
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
209
- fix: (_fixer) => null,
210
- },
211
- ],
212
- });
213
- }
214
- }
215
- }
216
- function checkBinaryExpression(node) {
217
- if (isTestFile) {
218
- return;
219
- }
220
- // Check for string concatenation in URL construction
221
- if (node.operator === '+') {
222
- if (!isUrlConstruction(node, sourceCode)) {
223
- return;
224
- }
225
- // Check right side (usually the parameter)
226
- if (node.right.type !== 'Literal') {
227
- const rightText = sourceCode.getText(node.right);
228
- // Check if it matches any ignore pattern
229
- if (matchesIgnorePattern(rightText, ignorePatterns)) {
230
- return;
231
- }
232
- // Check if it's already encoded
233
- if (isInsideEncodingCall(node.right, sourceCode, trustedLibraries)) {
234
- return;
235
- }
236
- // Check if it's a user input pattern
237
- const userInputPatterns = [
238
- /\breq\.(query|params|body)/,
239
- /\brequest\.(query|params|body)/,
240
- /\buserInput\b/,
241
- /\binput\b/,
242
- ];
243
- const isUserInput = userInputPatterns.some(pattern => pattern.test(rightText));
244
- if (isUserInput) {
245
- context.report({
246
- node: node.right,
247
- messageId: 'unescapedUrlParameter',
248
- data: {
249
- parameter: rightText,
250
- safeAlternative: `Use encodeURIComponent(): ${sourceCode.getText(node.left)} + encodeURIComponent(${rightText})`,
251
- },
252
- suggest: [
253
- {
254
- messageId: 'useEncodeURIComponent',
255
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
256
- fix: (_fixer) => null,
257
- },
258
- ],
259
- });
260
- }
261
- }
262
- }
263
- }
264
- function isUserControlled(node, visited = new Set()) {
265
- const text = sourceCode.getText(node);
266
- const patterns = [
267
- /\breq\.(query|params|body|headers|cookies)/,
268
- /\brequest\.(query|params|body)/,
269
- /\buserInput\b/i,
270
- /\binput\b/i,
271
- /\bsearchParams\b/,
272
- /\bparam\b/i,
273
- /\breturnUrl\b/i,
274
- /\burl\b/i,
275
- /\bredirect\b/i,
276
- /\bnext\b/i,
277
- ];
278
- if (patterns.some(p => p.test(text)))
279
- return true;
280
- // Trace identifiers
281
- if (node.type === 'Identifier') {
282
- if (visited.has(node.name))
283
- return false;
284
- visited.add(node.name);
285
- const scope = sourceCode.getScope(node);
286
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
287
- const variable = scope.variables.find((v) => v.name === node.name);
288
- if (variable && variable.defs.length > 0) {
289
- const def = variable.defs[0];
290
- if (def.type === 'Variable' && def.node.init) {
291
- const init = def.node.init;
292
- // Check if init is constructed from other user inputs
293
- if (isUserControlled(init, visited))
294
- return true;
295
- // Check if init is a TemplateLiteral containing user inputs in expressions
296
- if (init.type === 'TemplateLiteral') {
297
- return init.expressions.some(expr => isUserControlled(expr, visited));
298
- }
299
- // Check if init is BinaryExpression (concatenation)
300
- if (init.type === 'BinaryExpression') {
301
- return isUserControlled(init.left, visited) || isUserControlled(init.right, visited);
302
- }
303
- }
304
- }
305
- }
306
- return false;
307
- }
308
- return {
309
- TemplateLiteral: checkTemplateLiteral,
310
- BinaryExpression: checkBinaryExpression,
311
- AssignmentExpression(node) {
312
- if (isTestFile)
313
- return;
314
- // Check for window.location = ... or window.location.href = ...
315
- const left = node.left;
316
- let isLocationAssignment = false;
317
- if (left.type === 'MemberExpression') {
318
- const objectName = left.object.type === 'Identifier' ? left.object.name :
319
- (left.object.type === 'MemberExpression' ? sourceCode.getText(left.object) : '');
320
- const propName = left.property.type === 'Identifier' ? left.property.name : '';
321
- if ((objectName === 'window' && propName === 'location') ||
322
- (propName === 'href' && objectName.includes('location'))) {
323
- isLocationAssignment = true;
324
- }
325
- }
326
- else if (left.type === 'Identifier' && left.name === 'location') {
327
- // In browser location = ... is valid
328
- isLocationAssignment = true;
329
- }
330
- if (isLocationAssignment) {
331
- const right = node.right;
332
- const rightText = sourceCode.getText(right);
333
- // Skip TemplateLiteral and BinaryExpression as they are covered by their own visitors
334
- if (right.type === 'TemplateLiteral' || right.type === 'BinaryExpression') {
335
- return;
336
- }
337
- if (matchesIgnorePattern(rightText, ignorePatterns))
338
- return;
339
- if (isInsideEncodingCall(right, sourceCode, trustedLibraries))
340
- return;
341
- if (isUserControlled(right)) {
342
- context.report({
343
- node: right,
344
- messageId: 'unescapedUrlParameter',
345
- data: {
346
- parameter: rightText,
347
- safeAlternative: 'Validate and encode URL before redirecting',
348
- }
349
- });
350
- }
351
- }
352
- }
353
- };
354
- },
355
- });
@@ -1,5 +0,0 @@
1
- export interface Options {
2
- /** Allow dynamic import() expressions. Default: false (stricter) */
3
- allowDynamicImport?: boolean;
4
- }
5
- export declare const noUnsafeDynamicRequire: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
@@ -1,106 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noUnsafeDynamicRequire = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
6
- exports.noUnsafeDynamicRequire = (0, eslint_devkit_2.createRule)({
7
- name: 'no-unsafe-dynamic-require',
8
- meta: {
9
- type: 'problem',
10
- docs: {
11
- description: 'Prevent unsafe dynamic require() calls that could enable code injection',
12
- },
13
- fixable: 'code',
14
- hasSuggestions: false,
15
- messages: {
16
- unsafeDynamicRequire: (0, eslint_devkit_1.formatLLMMessage)({
17
- icon: eslint_devkit_1.MessageIcons.SECURITY,
18
- issueName: 'Dynamic require()',
19
- cwe: 'CWE-95',
20
- description: 'Dynamic require() detected',
21
- severity: 'CRITICAL',
22
- fix: 'Use allowlist: const ALLOWED = ["mod1", "mod2"]; if (!ALLOWED.includes(name)) throw Error("Not allowed")',
23
- documentationLink: 'https://owasp.org/www-community/attacks/Code_Injection',
24
- }),
25
- },
26
- schema: [
27
- {
28
- type: 'object',
29
- properties: {
30
- allowDynamicImport: {
31
- type: 'boolean',
32
- default: false,
33
- },
34
- },
35
- additionalProperties: false,
36
- },
37
- ],
38
- },
39
- defaultOptions: [
40
- {
41
- allowDynamicImport: false,
42
- },
43
- ],
44
- create(context) {
45
- /**
46
- * Track variables that reference require
47
- * Maps variable name to the node where it was assigned
48
- */
49
- const requireVariables = new Set();
50
- /**
51
- * Check if a node is a reference to require
52
- */
53
- const isRequireReference = (node) => {
54
- if (node.type === 'Identifier' && node.name === 'require') {
55
- return true;
56
- }
57
- if (node.type === 'Identifier' && requireVariables.has(node.name)) {
58
- return true;
59
- }
60
- return false;
61
- };
62
- /**
63
- * Check if argument is dynamic (not a literal)
64
- */
65
- const isDynamicArgument = (arg) => {
66
- if (arg.type === 'Literal')
67
- return false;
68
- if (arg.type === 'TemplateLiteral' && arg.expressions.length === 0)
69
- return false;
70
- return true;
71
- };
72
- return {
73
- VariableDeclarator(node) {
74
- // Track when require is assigned to a variable
75
- if (node.id.type === 'Identifier' && node.init) {
76
- if (node.init.type === 'Identifier' && node.init.name === 'require') {
77
- requireVariables.add(node.id.name);
78
- }
79
- }
80
- },
81
- CallExpression(node) {
82
- // Check for require() calls (direct or via variable)
83
- if (node.callee.type !== 'Identifier') {
84
- return;
85
- }
86
- // Check if callee is require or a variable that references require
87
- if (!isRequireReference(node.callee)) {
88
- return;
89
- }
90
- // Must have at least one argument
91
- if (node.arguments.length === 0)
92
- return;
93
- const firstArg = node.arguments[0];
94
- if (firstArg.type === 'SpreadElement')
95
- return;
96
- // Check if dynamic
97
- if (!isDynamicArgument(firstArg))
98
- return;
99
- context.report({
100
- node,
101
- messageId: 'unsafeDynamicRequire',
102
- });
103
- },
104
- };
105
- },
106
- });
@@ -1,6 +0,0 @@
1
- /**
2
- * @fileoverview Require validation of deep link URLs
3
- */
4
- export interface Options {
5
- }
6
- export declare const noUnvalidatedDeeplinks: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
@@ -1,62 +0,0 @@
1
- "use strict";
2
- /**
3
- * @fileoverview Require validation of deep link URLs
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.noUnvalidatedDeeplinks = void 0;
7
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
8
- exports.noUnvalidatedDeeplinks = (0, eslint_devkit_1.createRule)({
9
- name: 'no-unvalidated-deeplinks',
10
- meta: {
11
- type: 'problem',
12
- docs: {
13
- description: 'Require validation of deep link URLs',
14
- },
15
- messages: {
16
- violationDetected: (0, eslint_devkit_1.formatLLMMessage)({
17
- icon: eslint_devkit_1.MessageIcons.SECURITY,
18
- issueName: 'Unvalidated Deeplink',
19
- cwe: 'CWE-939',
20
- description: 'Deep link URL used without validation - potential open redirect',
21
- severity: 'HIGH',
22
- fix: 'Validate deep link URLs against a whitelist before navigation',
23
- documentationLink: 'https://cwe.mitre.org/data/definitions/939.html',
24
- })
25
- },
26
- schema: [],
27
- },
28
- defaultOptions: [],
29
- create(context) {
30
- function report(node) {
31
- context.report({ node, messageId: 'violationDetected' });
32
- }
33
- return {
34
- CallExpression(node) {
35
- // Detect Linking.openURL() with variable argument (React Native)
36
- if (node.callee.type === 'MemberExpression' &&
37
- node.callee.object.type === 'Identifier' &&
38
- node.callee.object.name === 'Linking' &&
39
- node.callee.property.type === 'Identifier' &&
40
- node.callee.property.name === 'openURL') {
41
- const urlArg = node.arguments[0];
42
- // Flag if URL is a variable/expression, not a literal
43
- if (urlArg && urlArg.type === 'Identifier') {
44
- report(node);
45
- }
46
- if (urlArg && urlArg.type === 'MemberExpression') {
47
- report(node);
48
- }
49
- }
50
- // Detect navigation.navigate with external URLs
51
- if (node.callee.type === 'MemberExpression' &&
52
- node.callee.property.type === 'Identifier' &&
53
- node.callee.property.name === 'navigate') {
54
- const urlArg = node.arguments[0];
55
- if (urlArg && urlArg.type === 'Identifier') {
56
- report(node);
57
- }
58
- }
59
- },
60
- };
61
- },
62
- });
@@ -1,9 +0,0 @@
1
- export interface Options {
2
- /** Allow unvalidated input in test files. Default: false */
3
- allowInTests?: boolean;
4
- /** Trusted validation libraries. Default: ['zod', 'joi', 'yup', 'class-validator'] */
5
- trustedLibraries?: string[];
6
- /** Additional safe patterns to ignore. Default: ['^safe', '^sanitized', '^validated', '^clean'] (prefix patterns) */
7
- ignorePatterns?: string[];
8
- }
9
- export declare const noUnvalidatedUserInput: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;