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.
Files changed (155) hide show
  1. package/AGENTS.md +196 -0
  2. package/CHANGELOG.md +105 -0
  3. package/LICENSE +23 -0
  4. package/README.md +377 -0
  5. package/package.json +80 -0
  6. package/src/index.d.ts +32 -0
  7. package/src/index.js +345 -0
  8. package/src/index.js.map +1 -0
  9. package/src/rules/security/database-injection.d.ts +13 -0
  10. package/src/rules/security/database-injection.js +407 -0
  11. package/src/rules/security/database-injection.js.map +1 -0
  12. package/src/rules/security/detect-child-process.d.ts +11 -0
  13. package/src/rules/security/detect-child-process.js +460 -0
  14. package/src/rules/security/detect-child-process.js.map +1 -0
  15. package/src/rules/security/detect-eval-with-expression.d.ts +9 -0
  16. package/src/rules/security/detect-eval-with-expression.js +393 -0
  17. package/src/rules/security/detect-eval-with-expression.js.map +1 -0
  18. package/src/rules/security/detect-non-literal-fs-filename.d.ts +7 -0
  19. package/src/rules/security/detect-non-literal-fs-filename.js +322 -0
  20. package/src/rules/security/detect-non-literal-fs-filename.js.map +1 -0
  21. package/src/rules/security/detect-non-literal-regexp.d.ts +9 -0
  22. package/src/rules/security/detect-non-literal-regexp.js +387 -0
  23. package/src/rules/security/detect-non-literal-regexp.js.map +1 -0
  24. package/src/rules/security/detect-object-injection.d.ts +11 -0
  25. package/src/rules/security/detect-object-injection.js +411 -0
  26. package/src/rules/security/detect-object-injection.js.map +1 -0
  27. package/src/rules/security/no-buffer-overread.d.ts +14 -0
  28. package/src/rules/security/no-buffer-overread.js +519 -0
  29. package/src/rules/security/no-buffer-overread.js.map +1 -0
  30. package/src/rules/security/no-clickjacking.d.ts +10 -0
  31. package/src/rules/security/no-clickjacking.js +381 -0
  32. package/src/rules/security/no-clickjacking.js.map +1 -0
  33. package/src/rules/security/no-directive-injection.d.ts +12 -0
  34. package/src/rules/security/no-directive-injection.js +446 -0
  35. package/src/rules/security/no-directive-injection.js.map +1 -0
  36. package/src/rules/security/no-document-cookie.d.ts +5 -0
  37. package/src/rules/security/no-document-cookie.js +90 -0
  38. package/src/rules/security/no-document-cookie.js.map +1 -0
  39. package/src/rules/security/no-electron-security-issues.d.ts +10 -0
  40. package/src/rules/security/no-electron-security-issues.js +421 -0
  41. package/src/rules/security/no-electron-security-issues.js.map +1 -0
  42. package/src/rules/security/no-exposed-sensitive-data.d.ts +11 -0
  43. package/src/rules/security/no-exposed-sensitive-data.js +341 -0
  44. package/src/rules/security/no-exposed-sensitive-data.js.map +1 -0
  45. package/src/rules/security/no-format-string-injection.d.ts +17 -0
  46. package/src/rules/security/no-format-string-injection.js +653 -0
  47. package/src/rules/security/no-format-string-injection.js.map +1 -0
  48. package/src/rules/security/no-graphql-injection.d.ts +12 -0
  49. package/src/rules/security/no-graphql-injection.js +410 -0
  50. package/src/rules/security/no-graphql-injection.js.map +1 -0
  51. package/src/rules/security/no-hardcoded-credentials.d.ts +26 -0
  52. package/src/rules/security/no-hardcoded-credentials.js +377 -0
  53. package/src/rules/security/no-hardcoded-credentials.js.map +1 -0
  54. package/src/rules/security/no-improper-sanitization.d.ts +12 -0
  55. package/src/rules/security/no-improper-sanitization.js +408 -0
  56. package/src/rules/security/no-improper-sanitization.js.map +1 -0
  57. package/src/rules/security/no-improper-type-validation.d.ts +10 -0
  58. package/src/rules/security/no-improper-type-validation.js +420 -0
  59. package/src/rules/security/no-improper-type-validation.js.map +1 -0
  60. package/src/rules/security/no-insecure-comparison.d.ts +7 -0
  61. package/src/rules/security/no-insecure-comparison.js +125 -0
  62. package/src/rules/security/no-insecure-comparison.js.map +1 -0
  63. package/src/rules/security/no-insecure-cookie-settings.d.ts +9 -0
  64. package/src/rules/security/no-insecure-cookie-settings.js +305 -0
  65. package/src/rules/security/no-insecure-cookie-settings.js.map +1 -0
  66. package/src/rules/security/no-insecure-jwt.d.ts +10 -0
  67. package/src/rules/security/no-insecure-jwt.js +338 -0
  68. package/src/rules/security/no-insecure-jwt.js.map +1 -0
  69. package/src/rules/security/no-insecure-redirects.d.ts +7 -0
  70. package/src/rules/security/no-insecure-redirects.js +215 -0
  71. package/src/rules/security/no-insecure-redirects.js.map +1 -0
  72. package/src/rules/security/no-insufficient-postmessage-validation.d.ts +14 -0
  73. package/src/rules/security/no-insufficient-postmessage-validation.js +390 -0
  74. package/src/rules/security/no-insufficient-postmessage-validation.js.map +1 -0
  75. package/src/rules/security/no-insufficient-random.d.ts +9 -0
  76. package/src/rules/security/no-insufficient-random.js +207 -0
  77. package/src/rules/security/no-insufficient-random.js.map +1 -0
  78. package/src/rules/security/no-ldap-injection.d.ts +10 -0
  79. package/src/rules/security/no-ldap-injection.js +449 -0
  80. package/src/rules/security/no-ldap-injection.js.map +1 -0
  81. package/src/rules/security/no-missing-authentication.d.ts +13 -0
  82. package/src/rules/security/no-missing-authentication.js +322 -0
  83. package/src/rules/security/no-missing-authentication.js.map +1 -0
  84. package/src/rules/security/no-missing-cors-check.d.ts +9 -0
  85. package/src/rules/security/no-missing-cors-check.js +449 -0
  86. package/src/rules/security/no-missing-cors-check.js.map +1 -0
  87. package/src/rules/security/no-missing-csrf-protection.d.ts +11 -0
  88. package/src/rules/security/no-missing-csrf-protection.js +183 -0
  89. package/src/rules/security/no-missing-csrf-protection.js.map +1 -0
  90. package/src/rules/security/no-missing-security-headers.d.ts +7 -0
  91. package/src/rules/security/no-missing-security-headers.js +217 -0
  92. package/src/rules/security/no-missing-security-headers.js.map +1 -0
  93. package/src/rules/security/no-privilege-escalation.d.ts +13 -0
  94. package/src/rules/security/no-privilege-escalation.js +321 -0
  95. package/src/rules/security/no-privilege-escalation.js.map +1 -0
  96. package/src/rules/security/no-redos-vulnerable-regex.d.ts +7 -0
  97. package/src/rules/security/no-redos-vulnerable-regex.js +307 -0
  98. package/src/rules/security/no-redos-vulnerable-regex.js.map +1 -0
  99. package/src/rules/security/no-sensitive-data-exposure.d.ts +11 -0
  100. package/src/rules/security/no-sensitive-data-exposure.js +251 -0
  101. package/src/rules/security/no-sensitive-data-exposure.js.map +1 -0
  102. package/src/rules/security/no-sql-injection.d.ts +10 -0
  103. package/src/rules/security/no-sql-injection.js +332 -0
  104. package/src/rules/security/no-sql-injection.js.map +1 -0
  105. package/src/rules/security/no-timing-attack.d.ts +10 -0
  106. package/src/rules/security/no-timing-attack.js +358 -0
  107. package/src/rules/security/no-timing-attack.js.map +1 -0
  108. package/src/rules/security/no-toctou-vulnerability.d.ts +7 -0
  109. package/src/rules/security/no-toctou-vulnerability.js +165 -0
  110. package/src/rules/security/no-toctou-vulnerability.js.map +1 -0
  111. package/src/rules/security/no-unchecked-loop-condition.d.ts +12 -0
  112. package/src/rules/security/no-unchecked-loop-condition.js +635 -0
  113. package/src/rules/security/no-unchecked-loop-condition.js.map +1 -0
  114. package/src/rules/security/no-unencrypted-transmission.d.ts +11 -0
  115. package/src/rules/security/no-unencrypted-transmission.js +237 -0
  116. package/src/rules/security/no-unencrypted-transmission.js.map +1 -0
  117. package/src/rules/security/no-unescaped-url-parameter.d.ts +9 -0
  118. package/src/rules/security/no-unescaped-url-parameter.js +266 -0
  119. package/src/rules/security/no-unescaped-url-parameter.js.map +1 -0
  120. package/src/rules/security/no-unlimited-resource-allocation.d.ts +12 -0
  121. package/src/rules/security/no-unlimited-resource-allocation.js +659 -0
  122. package/src/rules/security/no-unlimited-resource-allocation.js.map +1 -0
  123. package/src/rules/security/no-unsafe-deserialization.d.ts +10 -0
  124. package/src/rules/security/no-unsafe-deserialization.js +501 -0
  125. package/src/rules/security/no-unsafe-deserialization.js.map +1 -0
  126. package/src/rules/security/no-unsafe-dynamic-require.d.ts +5 -0
  127. package/src/rules/security/no-unsafe-dynamic-require.js +107 -0
  128. package/src/rules/security/no-unsafe-dynamic-require.js.map +1 -0
  129. package/src/rules/security/no-unsafe-regex-construction.d.ts +9 -0
  130. package/src/rules/security/no-unsafe-regex-construction.js +292 -0
  131. package/src/rules/security/no-unsafe-regex-construction.js.map +1 -0
  132. package/src/rules/security/no-unsanitized-html.d.ts +9 -0
  133. package/src/rules/security/no-unsanitized-html.js +347 -0
  134. package/src/rules/security/no-unsanitized-html.js.map +1 -0
  135. package/src/rules/security/no-unvalidated-user-input.d.ts +9 -0
  136. package/src/rules/security/no-unvalidated-user-input.js +418 -0
  137. package/src/rules/security/no-unvalidated-user-input.js.map +1 -0
  138. package/src/rules/security/no-weak-crypto.d.ts +11 -0
  139. package/src/rules/security/no-weak-crypto.js +350 -0
  140. package/src/rules/security/no-weak-crypto.js.map +1 -0
  141. package/src/rules/security/no-weak-password-recovery.d.ts +12 -0
  142. package/src/rules/security/no-weak-password-recovery.js +401 -0
  143. package/src/rules/security/no-weak-password-recovery.js.map +1 -0
  144. package/src/rules/security/no-xpath-injection.d.ts +10 -0
  145. package/src/rules/security/no-xpath-injection.js +487 -0
  146. package/src/rules/security/no-xpath-injection.js.map +1 -0
  147. package/src/rules/security/no-xxe-injection.d.ts +7 -0
  148. package/src/rules/security/no-xxe-injection.js +270 -0
  149. package/src/rules/security/no-xxe-injection.js.map +1 -0
  150. package/src/rules/security/no-zip-slip.d.ts +9 -0
  151. package/src/rules/security/no-zip-slip.js +446 -0
  152. package/src/rules/security/no-zip-slip.js.map +1 -0
  153. package/src/types/index.d.ts +131 -0
  154. package/src/types/index.js +18 -0
  155. package/src/types/index.js.map +1 -0
@@ -0,0 +1,659 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noUnlimitedResourceAllocation = void 0;
4
+ const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
+ const eslint_devkit_2 = require("@interlace/eslint-devkit");
6
+ const eslint_devkit_3 = require("@interlace/eslint-devkit");
7
+ exports.noUnlimitedResourceAllocation = (0, eslint_devkit_1.createRule)({
8
+ name: 'no-unlimited-resource-allocation',
9
+ meta: {
10
+ type: 'problem',
11
+ docs: {
12
+ description: 'Detects unlimited resource allocation that could cause DoS',
13
+ },
14
+ fixable: 'code',
15
+ hasSuggestions: true,
16
+ messages: {
17
+ unlimitedResourceAllocation: (0, eslint_devkit_2.formatLLMMessage)({
18
+ icon: eslint_devkit_2.MessageIcons.SECURITY,
19
+ issueName: 'Unlimited Resource Allocation',
20
+ cwe: 'CWE-770',
21
+ description: 'Resource allocation without limits',
22
+ severity: '{{severity}}',
23
+ fix: '{{safeAlternative}}',
24
+ documentationLink: 'https://cwe.mitre.org/data/definitions/770.html',
25
+ }),
26
+ unlimitedBufferAllocation: (0, eslint_devkit_2.formatLLMMessage)({
27
+ icon: eslint_devkit_2.MessageIcons.SECURITY,
28
+ issueName: 'Unlimited Buffer Allocation',
29
+ cwe: 'CWE-770',
30
+ description: 'Buffer allocated without size limits',
31
+ severity: 'HIGH',
32
+ fix: 'Set maximum buffer size limits',
33
+ documentationLink: 'https://nodejs.org/api/buffer.html',
34
+ }),
35
+ unlimitedFileOperations: (0, eslint_devkit_2.formatLLMMessage)({
36
+ icon: eslint_devkit_2.MessageIcons.SECURITY,
37
+ issueName: 'Unlimited File Operations',
38
+ cwe: 'CWE-770',
39
+ description: 'File operations without size limits',
40
+ severity: 'MEDIUM',
41
+ fix: 'Validate file size before operations',
42
+ documentationLink: 'https://nodejs.org/api/fs.html',
43
+ }),
44
+ unlimitedNetworkConnections: (0, eslint_devkit_2.formatLLMMessage)({
45
+ icon: eslint_devkit_2.MessageIcons.SECURITY,
46
+ issueName: 'Unlimited Network Connections',
47
+ cwe: 'CWE-770',
48
+ description: 'Network connections without limits',
49
+ severity: 'MEDIUM',
50
+ fix: 'Limit concurrent connections',
51
+ documentationLink: 'https://nodejs.org/api/http.html',
52
+ }),
53
+ unlimitedMemoryAllocation: (0, eslint_devkit_2.formatLLMMessage)({
54
+ icon: eslint_devkit_2.MessageIcons.SECURITY,
55
+ issueName: 'Unlimited Memory Allocation',
56
+ cwe: 'CWE-770',
57
+ description: 'Memory allocated without limits',
58
+ severity: 'MEDIUM',
59
+ fix: 'Set memory allocation limits',
60
+ documentationLink: 'https://nodejs.org/api/buffer.html',
61
+ }),
62
+ userControlledResourceSize: (0, eslint_devkit_2.formatLLMMessage)({
63
+ icon: eslint_devkit_2.MessageIcons.SECURITY,
64
+ issueName: 'User Controlled Resource Size',
65
+ cwe: 'CWE-770',
66
+ description: 'Resource size controlled by user input',
67
+ severity: 'HIGH',
68
+ fix: 'Validate and limit user-controlled resource sizes',
69
+ documentationLink: 'https://cwe.mitre.org/data/definitions/770.html',
70
+ }),
71
+ missingResourceLimits: (0, eslint_devkit_2.formatLLMMessage)({
72
+ icon: eslint_devkit_2.MessageIcons.SECURITY,
73
+ issueName: 'Missing Resource Limits',
74
+ cwe: 'CWE-770',
75
+ description: 'Resource allocation lacks proper limits',
76
+ severity: 'MEDIUM',
77
+ fix: 'Implement resource size validation',
78
+ documentationLink: 'https://cwe.mitre.org/data/definitions/770.html',
79
+ }),
80
+ resourceAllocationInLoop: (0, eslint_devkit_2.formatLLMMessage)({
81
+ icon: eslint_devkit_2.MessageIcons.SECURITY,
82
+ issueName: 'Resource Allocation in Loop',
83
+ cwe: 'CWE-770',
84
+ description: 'Resource allocation inside loop without limits',
85
+ severity: 'HIGH',
86
+ fix: 'Move resource allocation outside loop or add iteration limits',
87
+ documentationLink: 'https://cwe.mitre.org/data/definitions/770.html',
88
+ }),
89
+ implementResourceLimits: (0, eslint_devkit_2.formatLLMMessage)({
90
+ icon: eslint_devkit_2.MessageIcons.INFO,
91
+ issueName: 'Implement Resource Limits',
92
+ description: 'Add limits to resource allocation',
93
+ severity: 'LOW',
94
+ fix: 'const limitedSize = Math.min(userSize, MAX_SIZE);',
95
+ documentationLink: 'https://cwe.mitre.org/data/definitions/770.html',
96
+ }),
97
+ validateResourceSize: (0, eslint_devkit_2.formatLLMMessage)({
98
+ icon: eslint_devkit_2.MessageIcons.INFO,
99
+ issueName: 'Validate Resource Size',
100
+ description: 'Validate resource size before allocation',
101
+ severity: 'LOW',
102
+ fix: 'if (size > MAX_SIZE) throw new Error("Size too large");',
103
+ documentationLink: 'https://cwe.mitre.org/data/definitions/770.html',
104
+ }),
105
+ useResourcePools: (0, eslint_devkit_2.formatLLMMessage)({
106
+ icon: eslint_devkit_2.MessageIcons.INFO,
107
+ issueName: 'Use Resource Pools',
108
+ description: 'Use resource pools for better control',
109
+ severity: 'LOW',
110
+ fix: 'Implement connection pooling and resource reuse',
111
+ documentationLink: 'https://en.wikipedia.org/wiki/Object_pool_pattern',
112
+ }),
113
+ strategyResourceManagement: (0, eslint_devkit_2.formatLLMMessage)({
114
+ icon: eslint_devkit_2.MessageIcons.STRATEGY,
115
+ issueName: 'Resource Management Strategy',
116
+ description: 'Implement comprehensive resource management',
117
+ severity: 'LOW',
118
+ fix: 'Use resource pools, limits, and cleanup mechanisms',
119
+ documentationLink: 'https://cwe.mitre.org/data/definitions/770.html',
120
+ }),
121
+ strategyRateLimiting: (0, eslint_devkit_2.formatLLMMessage)({
122
+ icon: eslint_devkit_2.MessageIcons.STRATEGY,
123
+ issueName: 'Rate Limiting Strategy',
124
+ description: 'Implement rate limiting for resource allocation',
125
+ severity: 'LOW',
126
+ fix: 'Use rate limiters to prevent resource exhaustion',
127
+ documentationLink: 'https://en.wikipedia.org/wiki/Rate_limiting',
128
+ }),
129
+ strategyResourceCleanup: (0, eslint_devkit_2.formatLLMMessage)({
130
+ icon: eslint_devkit_2.MessageIcons.STRATEGY,
131
+ issueName: 'Resource Cleanup Strategy',
132
+ description: 'Ensure proper resource cleanup',
133
+ severity: 'LOW',
134
+ fix: 'Implement try-finally blocks and resource disposal',
135
+ documentationLink: 'https://en.wikipedia.org/wiki/Resource_management_(computing)',
136
+ })
137
+ },
138
+ schema: [
139
+ {
140
+ type: 'object',
141
+ properties: {
142
+ maxResourceSize: {
143
+ type: 'number',
144
+ minimum: 1024,
145
+ default: 1048576, // 1MB
146
+ },
147
+ userInputVariables: {
148
+ type: 'array',
149
+ items: { type: 'string' },
150
+ default: ['req', 'request', 'body', 'query', 'params', 'input', 'data'],
151
+ },
152
+ safeResourceFunctions: {
153
+ type: 'array',
154
+ items: { type: 'string' },
155
+ default: ['validateSize', 'checkLimits', 'limitResource', 'safeAlloc'],
156
+ },
157
+ requireResourceValidation: {
158
+ type: 'boolean',
159
+ default: true,
160
+ },
161
+ trustedSanitizers: {
162
+ type: 'array',
163
+ items: { type: 'string' },
164
+ default: [],
165
+ description: 'Additional function names to consider as resource validators',
166
+ },
167
+ trustedAnnotations: {
168
+ type: 'array',
169
+ items: { type: 'string' },
170
+ default: [],
171
+ description: 'Additional JSDoc annotations to consider as safe markers',
172
+ },
173
+ strictMode: {
174
+ type: 'boolean',
175
+ default: false,
176
+ description: 'Disable all false positive detection (strict mode)',
177
+ },
178
+ },
179
+ additionalProperties: false,
180
+ },
181
+ ],
182
+ },
183
+ defaultOptions: [
184
+ {
185
+ maxResourceSize: 1048576, // 1MB
186
+ userInputVariables: ['req', 'request', 'body', 'query', 'params', 'input', 'data'],
187
+ safeResourceFunctions: ['validateSize', 'checkLimits', 'limitResource', 'safeAlloc'],
188
+ requireResourceValidation: true,
189
+ trustedSanitizers: [],
190
+ trustedAnnotations: [],
191
+ strictMode: false,
192
+ },
193
+ ],
194
+ create(context) {
195
+ const options = context.options[0] || {};
196
+ const { maxResourceSize = 1048576, userInputVariables = ['req', 'request', 'body', 'query', 'params', 'input', 'data'], safeResourceFunctions = ['validateSize', 'checkLimits', 'limitResource', 'safeAlloc'], requireResourceValidation = true, trustedSanitizers = [], trustedAnnotations = [], strictMode = false, } = options;
197
+ const sourceCode = context.sourceCode || context.sourceCode;
198
+ const filename = context.filename || context.getFilename();
199
+ // Create safety checker for false positive detection
200
+ const safetyChecker = (0, eslint_devkit_3.createSafetyChecker)({
201
+ trustedSanitizers,
202
+ trustedAnnotations,
203
+ trustedOrmPatterns: [],
204
+ strictMode,
205
+ });
206
+ /**
207
+ * Check if an expression contains user input
208
+ */
209
+ const isUserInput = (expression) => {
210
+ const exprText = sourceCode.getText(expression);
211
+ return userInputVariables.some(input => exprText.includes(input));
212
+ };
213
+ /**
214
+ * Check if resource allocation has size validation
215
+ */
216
+ const hasSizeValidation = (node) => {
217
+ const args = node.arguments;
218
+ if (args.length === 0) {
219
+ return false;
220
+ }
221
+ // Check if size argument is a validated expression
222
+ const sizeArg = args[0];
223
+ const sizeText = sourceCode.getText(sizeArg);
224
+ // Look for validation patterns
225
+ return sizeText.includes('Math.min(') ||
226
+ sizeText.includes('Math.max(') ||
227
+ sizeText.includes('Math.clamp(') ||
228
+ safeResourceFunctions.some(func => sizeText.includes(func));
229
+ };
230
+ /**
231
+ * Check if we're inside a loop
232
+ */
233
+ const isInsideLoop = (node) => {
234
+ let current = node;
235
+ while (current) {
236
+ if (current.type === 'ForStatement' ||
237
+ current.type === 'WhileStatement' ||
238
+ current.type === 'DoWhileStatement' ||
239
+ current.type === 'ForInStatement' ||
240
+ current.type === 'ForOfStatement') {
241
+ return true;
242
+ }
243
+ current = current.parent;
244
+ }
245
+ return false;
246
+ };
247
+ /**
248
+ * Estimate resource size from static analysis
249
+ */
250
+ const estimateResourceSize = (sizeExpression) => {
251
+ if (sizeExpression.type === 'Literal' && typeof sizeExpression.value === 'number') {
252
+ return sizeExpression.value;
253
+ }
254
+ // Handle binary expressions like 1024 * 1024 * 100
255
+ if (sizeExpression.type === 'BinaryExpression') {
256
+ const left = estimateResourceSize(sizeExpression.left);
257
+ const right = estimateResourceSize(sizeExpression.right);
258
+ if (left !== null && right !== null) {
259
+ switch (sizeExpression.operator) {
260
+ case '*':
261
+ return left * right;
262
+ case '+':
263
+ return left + right;
264
+ case '-':
265
+ return left - right;
266
+ case '/':
267
+ return right !== 0 ? left / right : null;
268
+ default:
269
+ return null;
270
+ }
271
+ }
272
+ }
273
+ return null;
274
+ };
275
+ return {
276
+ // Check Buffer allocation
277
+ CallExpression(node) {
278
+ const callee = node.callee;
279
+ const calleeText = sourceCode.getText(callee);
280
+ // Check for Buffer.alloc(), Buffer.allocUnsafe() or new Buffer()
281
+ const isBufferAlloc = callee.type === 'MemberExpression' &&
282
+ callee.object.type === 'Identifier' &&
283
+ callee.object.name === 'Buffer' &&
284
+ callee.property.type === 'Identifier' &&
285
+ (callee.property.name === 'alloc' || callee.property.name === 'allocUnsafe');
286
+ const isNewBuffer = callee.type === 'NewExpression' &&
287
+ callee.callee.type === 'Identifier' &&
288
+ callee.callee.name === 'Buffer';
289
+ if (isBufferAlloc || isNewBuffer) {
290
+ const args = node.arguments;
291
+ if (args.length > 0) {
292
+ const sizeArg = args[0];
293
+ // Check if size comes from user input (but skip if validated)
294
+ if (sizeArg.type !== 'SpreadElement' && isUserInput(sizeArg) && !hasSizeValidation(node)) {
295
+ // FALSE POSITIVE REDUCTION
296
+ if (safetyChecker.isSafe(node, context)) {
297
+ return;
298
+ }
299
+ context.report({
300
+ node: sizeArg,
301
+ messageId: 'userControlledResourceSize',
302
+ data: {
303
+ filePath: filename,
304
+ line: String(node.loc?.start.line ?? 0),
305
+ },
306
+ });
307
+ return;
308
+ }
309
+ // Check if size exceeds limits
310
+ const estimatedSize = sizeArg.type === 'SpreadElement' ? null : estimateResourceSize(sizeArg);
311
+ if (estimatedSize && estimatedSize > maxResourceSize) {
312
+ // FALSE POSITIVE REDUCTION
313
+ if (safetyChecker.isSafe(node, context)) {
314
+ return;
315
+ }
316
+ context.report({
317
+ node: sizeArg,
318
+ messageId: 'unlimitedBufferAllocation',
319
+ data: {
320
+ filePath: filename,
321
+ line: String(node.loc?.start.line ?? 0),
322
+ },
323
+ });
324
+ return;
325
+ }
326
+ // Check if no size validation present (only for non-literal sizes from user input)
327
+ const isLiteralSize = sizeArg.type === 'Literal' && typeof sizeArg.value === 'number';
328
+ const comesFromUserInput = sizeArg.type !== 'SpreadElement' && isUserInput(sizeArg);
329
+ if (requireResourceValidation && !hasSizeValidation(node) && !isLiteralSize && comesFromUserInput) {
330
+ // FALSE POSITIVE REDUCTION
331
+ if (safetyChecker.isSafe(node, context)) {
332
+ return;
333
+ }
334
+ context.report({
335
+ node,
336
+ messageId: 'missingResourceLimits',
337
+ data: {
338
+ filePath: filename,
339
+ line: String(node.loc?.start.line ?? 0),
340
+ },
341
+ });
342
+ }
343
+ }
344
+ }
345
+ // Check for multer configuration without limits
346
+ if (callee.type === 'Identifier' && callee.name === 'multer') {
347
+ const args = node.arguments;
348
+ if (args.length > 0 && args[0].type === 'ObjectExpression') {
349
+ const props = args[0].properties;
350
+ // Check for valid limits definition
351
+ const hasValidLimits = props.some((prop) => {
352
+ if (prop.type !== 'Property' || prop.key.type !== 'Identifier') {
353
+ return false;
354
+ }
355
+ // Direct fileSize (not standard but maybe used?)
356
+ if (prop.key.name === 'fileSize')
357
+ return true;
358
+ // Limits object
359
+ if (prop.key.name === 'limits' && prop.value.type === 'ObjectExpression') {
360
+ return prop.value.properties.some((limitProp) => limitProp.type === 'Property' &&
361
+ limitProp.key.type === 'Identifier' &&
362
+ limitProp.key.name === 'fileSize');
363
+ }
364
+ return false;
365
+ });
366
+ if (!hasValidLimits) {
367
+ // FALSE POSITIVE REDUCTION
368
+ if (safetyChecker.isSafe(node, context)) {
369
+ return;
370
+ }
371
+ context.report({
372
+ node,
373
+ messageId: 'unlimitedFileOperations',
374
+ data: {
375
+ filePath: filename,
376
+ line: String(node.loc?.start.line ?? 0),
377
+ },
378
+ });
379
+ }
380
+ }
381
+ return;
382
+ }
383
+ // Check for fs operations
384
+ if (callee.type === 'MemberExpression' &&
385
+ callee.object.type === 'Identifier' &&
386
+ callee.object.name === 'fs' &&
387
+ callee.property.type === 'Identifier' &&
388
+ ['readFile', 'writeFile', 'readFileSync', 'writeFileSync'].includes(callee.property.name)) {
389
+ const args = node.arguments;
390
+ if (args.length > 0) {
391
+ // Check if file path comes from user input (potential for large files)
392
+ const pathArg = args[0];
393
+ if (pathArg.type !== 'SpreadElement' && isUserInput(pathArg)) {
394
+ // FALSE POSITIVE REDUCTION
395
+ if (safetyChecker.isSafe(node, context)) {
396
+ return;
397
+ }
398
+ context.report({
399
+ node: pathArg,
400
+ messageId: 'unlimitedFileOperations',
401
+ data: {
402
+ filePath: filename,
403
+ line: String(node.loc?.start.line ?? 0),
404
+ },
405
+ });
406
+ }
407
+ }
408
+ }
409
+ // Check for Array constructor with user input
410
+ if (callee.type === 'Identifier' && callee.name === 'Array') {
411
+ const args = node.arguments;
412
+ if (args.length === 1) {
413
+ const sizeArg = args[0];
414
+ if (sizeArg.type !== 'SpreadElement' && isUserInput(sizeArg)) {
415
+ // FALSE POSITIVE REDUCTION
416
+ if (safetyChecker.isSafe(node, context)) {
417
+ return;
418
+ }
419
+ context.report({
420
+ node: sizeArg,
421
+ messageId: 'unlimitedMemoryAllocation',
422
+ data: {
423
+ filePath: filename,
424
+ line: String(node.loc?.start.line ?? 0),
425
+ },
426
+ });
427
+ }
428
+ }
429
+ }
430
+ // Check for new Array() constructor
431
+ /* c8 ignore start -- unreachable inside CallExpression visitor */
432
+ if (callee.type === 'NewExpression' &&
433
+ callee.callee.type === 'Identifier' &&
434
+ callee.callee.name === 'Array') {
435
+ const args = callee.arguments;
436
+ if (args.length === 1) {
437
+ const sizeArg = args[0];
438
+ if (sizeArg.type !== 'SpreadElement' && isUserInput(sizeArg)) {
439
+ // FALSE POSITIVE REDUCTION
440
+ if (safetyChecker.isSafe(node, context)) {
441
+ return;
442
+ }
443
+ context.report({
444
+ node: sizeArg,
445
+ messageId: 'unlimitedMemoryAllocation',
446
+ data: {
447
+ filePath: filename,
448
+ line: String(node.loc?.start.line ?? 0),
449
+ },
450
+ });
451
+ }
452
+ }
453
+ }
454
+ /* c8 ignore stop */
455
+ // Check for complex resource exhaustion patterns
456
+ /* c8 ignore start -- defensive detectors for rare patterns */
457
+ // ZIP bomb detection - unlimited decompression
458
+ if (calleeText.includes('unzipper') || calleeText.includes('Extract')) {
459
+ // Check for unlimited ZIP extraction
460
+ context.report({
461
+ node,
462
+ messageId: 'unlimitedFileOperations',
463
+ data: {
464
+ filePath: filename,
465
+ line: String(node.loc?.start.line ?? 0),
466
+ },
467
+ });
468
+ }
469
+ // XML expansion attack detection
470
+ if (calleeText.includes('xml2js') || calleeText.includes('parseString')) {
471
+ context.report({
472
+ node,
473
+ messageId: 'unlimitedMemoryAllocation',
474
+ data: {
475
+ filePath: filename,
476
+ line: String(node.loc?.start.line ?? 0),
477
+ },
478
+ });
479
+ }
480
+ // Check for cache with unlimited growth
481
+ if (calleeText.includes('set') && sourceCode.getText(node).includes('Buffer.alloc')) {
482
+ // Detect cache patterns that allocate buffers without limits
483
+ const args = node.arguments;
484
+ if (args.length >= 2) {
485
+ const valueArg = args[1];
486
+ const valueText = sourceCode.getText(valueArg);
487
+ if (valueText.includes('Buffer.alloc') && valueText.includes('length')) {
488
+ context.report({
489
+ node,
490
+ messageId: 'unlimitedMemoryAllocation',
491
+ data: {
492
+ filePath: filename,
493
+ line: String(node.loc?.start.line ?? 0),
494
+ },
495
+ });
496
+ }
497
+ }
498
+ }
499
+ // Check for recursive data structure processing
500
+ if (calleeText.includes('map') || calleeText.includes('forEach')) {
501
+ const args = node.arguments;
502
+ if (args.length > 0) {
503
+ const callbackArg = args[0];
504
+ const callbackText = sourceCode.getText(callbackArg);
505
+ // Detect patterns that create arrays from nested object properties
506
+ if (callbackText.includes('Object.keys') && callbackText.includes('map')) {
507
+ context.report({
508
+ node,
509
+ messageId: 'unlimitedMemoryAllocation',
510
+ data: {
511
+ filePath: filename,
512
+ line: String(node.loc?.start.line ?? 0),
513
+ },
514
+ });
515
+ }
516
+ }
517
+ }
518
+ /* c8 ignore stop */
519
+ // Check for resource allocation inside loops
520
+ if (isInsideLoop(node)) {
521
+ const calleeText = sourceCode.getText(callee);
522
+ // Check if this allocates resources
523
+ if (calleeText.includes('alloc') ||
524
+ calleeText.includes('Array') ||
525
+ calleeText.includes('Buffer') ||
526
+ calleeText.includes('readFile') ||
527
+ calleeText.includes('writeFile')) {
528
+ // FALSE POSITIVE REDUCTION
529
+ if (safetyChecker.isSafe(node, context)) {
530
+ return;
531
+ }
532
+ // Skip if this is an assignment to an array element (pre-allocated pattern)
533
+ const parent = node.parent;
534
+ if (parent && parent.type === 'AssignmentExpression' &&
535
+ parent.left.type === 'MemberExpression' &&
536
+ parent.left.object.type === 'Identifier') {
537
+ // This is assigning to an array element, likely pre-allocated
538
+ return;
539
+ }
540
+ // Report resourceAllocationInLoop - this can be in addition to user input errors
541
+ context.report({
542
+ node,
543
+ messageId: 'resourceAllocationInLoop',
544
+ data: {
545
+ filePath: filename,
546
+ line: String(node.loc?.start.line ?? 0),
547
+ },
548
+ });
549
+ }
550
+ }
551
+ },
552
+ // Check new expressions for resource allocation
553
+ NewExpression(node) {
554
+ const callee = node.callee;
555
+ // Check for new Buffer() with user input
556
+ if (callee.type === 'Identifier' && callee.name === 'Buffer') {
557
+ const args = node.arguments;
558
+ if (args.length > 0) {
559
+ const sizeArg = args[0];
560
+ // Check if size comes from user input (but skip if validated)
561
+ if (sizeArg.type !== 'SpreadElement' && isUserInput(sizeArg) && !hasSizeValidation(node)) {
562
+ // FALSE POSITIVE REDUCTION
563
+ if (safetyChecker.isSafe(node, context)) {
564
+ return;
565
+ }
566
+ context.report({
567
+ node: sizeArg,
568
+ messageId: 'userControlledResourceSize',
569
+ data: {
570
+ filePath: filename,
571
+ line: String(node.loc?.start.line ?? 0),
572
+ },
573
+ });
574
+ return;
575
+ }
576
+ // Check if size exceeds limits
577
+ const estimatedSize = sizeArg.type === 'SpreadElement' ? null : estimateResourceSize(sizeArg);
578
+ if (estimatedSize && estimatedSize > maxResourceSize) {
579
+ // FALSE POSITIVE REDUCTION
580
+ if (safetyChecker.isSafe(node, context)) {
581
+ return;
582
+ }
583
+ context.report({
584
+ node: sizeArg,
585
+ messageId: 'unlimitedBufferAllocation',
586
+ data: {
587
+ filePath: filename,
588
+ line: String(node.loc?.start.line ?? 0),
589
+ },
590
+ });
591
+ return;
592
+ }
593
+ // Check if no size validation present (only for non-literal sizes from user input)
594
+ const isLiteralSize = sizeArg.type === 'Literal' && typeof sizeArg.value === 'number';
595
+ const comesFromUserInput = sizeArg.type !== 'SpreadElement' && isUserInput(sizeArg);
596
+ if (requireResourceValidation && !hasSizeValidation(node) && !isLiteralSize && comesFromUserInput) {
597
+ // FALSE POSITIVE REDUCTION
598
+ if (safetyChecker.isSafe(node, context)) {
599
+ return;
600
+ }
601
+ context.report({
602
+ node,
603
+ messageId: 'missingResourceLimits',
604
+ data: {
605
+ filePath: filename,
606
+ line: String(node.loc?.start.line ?? 0),
607
+ },
608
+ });
609
+ }
610
+ }
611
+ }
612
+ // Check for new Array() with user input
613
+ if (callee.type === 'Identifier' && callee.name === 'Array') {
614
+ const args = node.arguments;
615
+ if (args.length === 1) {
616
+ const sizeArg = args[0];
617
+ if (sizeArg.type !== 'SpreadElement' && isUserInput(sizeArg)) {
618
+ // FALSE POSITIVE REDUCTION
619
+ if (safetyChecker.isSafe(node, context)) {
620
+ return;
621
+ }
622
+ context.report({
623
+ node: sizeArg,
624
+ messageId: 'unlimitedMemoryAllocation',
625
+ data: {
626
+ filePath: filename,
627
+ line: String(node.loc?.start.line ?? 0),
628
+ },
629
+ });
630
+ }
631
+ }
632
+ }
633
+ // Check for resource allocation inside loops
634
+ if (isInsideLoop(node)) {
635
+ const calleeText = sourceCode.getText(callee);
636
+ // Check if this allocates resources
637
+ if (calleeText.includes('Buffer') ||
638
+ calleeText.includes('Array') ||
639
+ calleeText.includes('Map') ||
640
+ calleeText.includes('Set')) {
641
+ // FALSE POSITIVE REDUCTION
642
+ if (safetyChecker.isSafe(node, context)) {
643
+ return;
644
+ }
645
+ context.report({
646
+ node,
647
+ messageId: 'resourceAllocationInLoop',
648
+ data: {
649
+ filePath: filename,
650
+ line: String(node.loc?.start.line ?? 0),
651
+ },
652
+ });
653
+ }
654
+ }
655
+ }
656
+ };
657
+ },
658
+ });
659
+ //# sourceMappingURL=no-unlimited-resource-allocation.js.map