eslint-plugin-node-security 4.6.0 → 4.7.1

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 (120) hide show
  1. package/README.md +1 -0
  2. package/package.json +5 -18
  3. package/src/index.d.ts +17 -10
  4. package/src/index.js +5 -36
  5. package/src/oxlint.d.ts +17 -20
  6. package/src/oxlint.js +0 -19
  7. package/src/rules/detect-child-process/index.js +0 -107
  8. package/src/rules/detect-eval-with-expression/index.js +2 -48
  9. package/src/rules/detect-non-literal-fs-filename/index.js +0 -82
  10. package/src/rules/detect-suspicious-dependencies/index.js +0 -12
  11. package/src/rules/lock-file/index.js +0 -18
  12. package/src/rules/no-arbitrary-file-access/index.js +0 -52
  13. package/src/rules/no-buffer-overread/index.js +3 -96
  14. package/src/rules/no-cryptojs/index.js +2 -10
  15. package/src/rules/no-cryptojs-weak-random/index.js +1 -13
  16. package/src/rules/no-data-in-temp-storage/index.js +0 -12
  17. package/src/rules/no-deprecated-buffer/index.js +0 -15
  18. package/src/rules/no-deprecated-cipher-method/index.js +0 -9
  19. package/src/rules/no-dynamic-algorithm-selection/index.js +0 -13
  20. package/src/rules/no-dynamic-command-string/index.js +201 -0
  21. package/src/rules/no-dynamic-dependency-loading/index.js +0 -13
  22. package/src/rules/no-dynamic-require/index.js +0 -10
  23. package/src/rules/no-ecb-mode/index.js +0 -7
  24. package/src/rules/no-insecure-key-derivation/index.js +0 -10
  25. package/src/rules/no-insecure-rsa-padding/index.js +0 -9
  26. package/src/rules/no-math-random-crypto/index.js +1 -17
  27. package/src/rules/no-self-signed-certs/index.js +1 -9
  28. package/src/rules/no-sha1-hash/index.js +0 -9
  29. package/src/rules/no-shell-injection/index.js +0 -20
  30. package/src/rules/no-ssrf/index.js +4 -80
  31. package/src/rules/no-static-iv/index.js +2 -21
  32. package/src/rules/no-timing-unsafe-compare/index.js +1 -11
  33. package/src/rules/no-toctou-vulnerability/index.js +0 -22
  34. package/src/rules/no-unsafe-buffer-alloc/index.js +0 -13
  35. package/src/rules/no-unsafe-dynamic-require/index.js +0 -22
  36. package/src/rules/no-weak-cipher-algorithm/index.js +0 -19
  37. package/src/rules/no-weak-hash-algorithm/index.js +0 -22
  38. package/src/rules/no-zip-slip/index.js +3 -81
  39. package/src/rules/prefer-native-crypto/index.js +4 -11
  40. package/src/rules/require-dependency-integrity/index.js +0 -11
  41. package/src/rules/require-secure-credential-storage/index.js +0 -12
  42. package/src/rules/require-secure-deletion/index.js +0 -29
  43. package/src/rules/require-storage-encryption/index.js +0 -12
  44. package/src/types/index.js +0 -6
  45. package/CHANGELOG.md +0 -413
  46. package/src/index.js.map +0 -1
  47. package/src/oxlint.js.map +0 -1
  48. package/src/rules/detect-child-process/index.d.ts +0 -52
  49. package/src/rules/detect-child-process/index.js.map +0 -1
  50. package/src/rules/detect-eval-with-expression/index.d.ts +0 -46
  51. package/src/rules/detect-eval-with-expression/index.js.map +0 -1
  52. package/src/rules/detect-non-literal-fs-filename/index.d.ts +0 -52
  53. package/src/rules/detect-non-literal-fs-filename/index.js.map +0 -1
  54. package/src/rules/detect-suspicious-dependencies/index.d.ts +0 -12
  55. package/src/rules/detect-suspicious-dependencies/index.js.map +0 -1
  56. package/src/rules/lock-file/index.d.ts +0 -13
  57. package/src/rules/lock-file/index.js.map +0 -1
  58. package/src/rules/no-arbitrary-file-access/index.d.ts +0 -12
  59. package/src/rules/no-arbitrary-file-access/index.js.map +0 -1
  60. package/src/rules/no-buffer-overread/index.d.ts +0 -39
  61. package/src/rules/no-buffer-overread/index.js.map +0 -1
  62. package/src/rules/no-cryptojs/index.d.ts +0 -24
  63. package/src/rules/no-cryptojs/index.js.map +0 -1
  64. package/src/rules/no-cryptojs-weak-random/index.d.ts +0 -24
  65. package/src/rules/no-cryptojs-weak-random/index.js.map +0 -1
  66. package/src/rules/no-data-in-temp-storage/index.d.ts +0 -14
  67. package/src/rules/no-data-in-temp-storage/index.js.map +0 -1
  68. package/src/rules/no-deprecated-buffer/index.d.ts +0 -23
  69. package/src/rules/no-deprecated-buffer/index.js.map +0 -1
  70. package/src/rules/no-deprecated-cipher-method/index.d.ts +0 -23
  71. package/src/rules/no-deprecated-cipher-method/index.js.map +0 -1
  72. package/src/rules/no-dynamic-algorithm-selection/index.d.ts +0 -26
  73. package/src/rules/no-dynamic-algorithm-selection/index.js.map +0 -1
  74. package/src/rules/no-dynamic-dependency-loading/index.d.ts +0 -12
  75. package/src/rules/no-dynamic-dependency-loading/index.js.map +0 -1
  76. package/src/rules/no-dynamic-require/index.d.ts +0 -21
  77. package/src/rules/no-dynamic-require/index.js.map +0 -1
  78. package/src/rules/no-ecb-mode/index.d.ts +0 -23
  79. package/src/rules/no-ecb-mode/index.js.map +0 -1
  80. package/src/rules/no-insecure-key-derivation/index.d.ts +0 -24
  81. package/src/rules/no-insecure-key-derivation/index.js.map +0 -1
  82. package/src/rules/no-insecure-rsa-padding/index.d.ts +0 -24
  83. package/src/rules/no-insecure-rsa-padding/index.js.map +0 -1
  84. package/src/rules/no-math-random-crypto/index.d.ts +0 -26
  85. package/src/rules/no-math-random-crypto/index.js.map +0 -1
  86. package/src/rules/no-self-signed-certs/index.d.ts +0 -23
  87. package/src/rules/no-self-signed-certs/index.js.map +0 -1
  88. package/src/rules/no-sha1-hash/index.d.ts +0 -24
  89. package/src/rules/no-sha1-hash/index.js.map +0 -1
  90. package/src/rules/no-shell-injection/index.d.ts +0 -20
  91. package/src/rules/no-shell-injection/index.js.map +0 -1
  92. package/src/rules/no-ssrf/index.d.ts +0 -25
  93. package/src/rules/no-ssrf/index.js.map +0 -1
  94. package/src/rules/no-static-iv/index.d.ts +0 -23
  95. package/src/rules/no-static-iv/index.js.map +0 -1
  96. package/src/rules/no-timing-unsafe-compare/index.d.ts +0 -23
  97. package/src/rules/no-timing-unsafe-compare/index.js.map +0 -1
  98. package/src/rules/no-toctou-vulnerability/index.d.ts +0 -26
  99. package/src/rules/no-toctou-vulnerability/index.js.map +0 -1
  100. package/src/rules/no-unsafe-buffer-alloc/index.d.ts +0 -47
  101. package/src/rules/no-unsafe-buffer-alloc/index.js.map +0 -1
  102. package/src/rules/no-unsafe-dynamic-require/index.d.ts +0 -19
  103. package/src/rules/no-unsafe-dynamic-require/index.js.map +0 -1
  104. package/src/rules/no-weak-cipher-algorithm/index.d.ts +0 -25
  105. package/src/rules/no-weak-cipher-algorithm/index.js.map +0 -1
  106. package/src/rules/no-weak-hash-algorithm/index.d.ts +0 -25
  107. package/src/rules/no-weak-hash-algorithm/index.js.map +0 -1
  108. package/src/rules/no-zip-slip/index.d.ts +0 -35
  109. package/src/rules/no-zip-slip/index.js.map +0 -1
  110. package/src/rules/prefer-native-crypto/index.d.ts +0 -23
  111. package/src/rules/prefer-native-crypto/index.js.map +0 -1
  112. package/src/rules/require-dependency-integrity/index.d.ts +0 -12
  113. package/src/rules/require-dependency-integrity/index.js.map +0 -1
  114. package/src/rules/require-secure-credential-storage/index.d.ts +0 -12
  115. package/src/rules/require-secure-credential-storage/index.js.map +0 -1
  116. package/src/rules/require-secure-deletion/index.d.ts +0 -17
  117. package/src/rules/require-secure-deletion/index.js.map +0 -1
  118. package/src/rules/require-storage-encryption/index.d.ts +0 -12
  119. package/src/rules/require-storage-encryption/index.js.map +0 -1
  120. package/src/types/index.js.map +0 -1
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noBufferOverread = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -186,22 +181,15 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
186
181
  const { bufferMethods = ['readUInt8', 'readUInt16LE', 'readUInt32LE', 'readInt8', 'readInt16LE', 'readInt32LE', 'writeUInt8', 'writeUInt16LE', 'writeUInt32LE', 'slice', 'copy'], boundsCheckFunctions = ['validateIndex', 'checkBounds', 'safeIndex', 'validateBufferIndex'], bufferTypes = ['Buffer', 'Uint8Array', 'ArrayBuffer', 'DataView'], trustedSanitizers = [], trustedAnnotations = [], strictMode = false, } = options;
187
182
  const sourceCode = context.sourceCode;
188
183
  const filename = context.filename;
189
- // Create safety checker for false positive detection
190
184
  const safetyChecker = (0, eslint_devkit_1.createSafetyChecker)({
191
185
  trustedSanitizers,
192
186
  trustedAnnotations,
193
187
  trustedOrmPatterns: [],
194
188
  strictMode,
195
189
  });
196
- // Pre-compute Sets for O(1) lookups (performance optimization)
197
190
  const bufferTypesSet = new Set(bufferTypes.map(t => t.toLowerCase()));
198
191
  const userControlledKeywords = new Set(['req', 'request', 'query', 'params', 'input', 'user', 'offset', 'index', 'body']);
199
- // Track buffer variables
200
192
  const bufferVars = new Set();
201
- /**
202
- * Check if variable is a buffer type
203
- * Uses Set-based lookup for O(1) performance
204
- */
205
193
  const isBufferType = (varName) => {
206
194
  if (bufferVars.has(varName))
207
195
  return true;
@@ -210,26 +198,11 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
210
198
  if (lowerName.includes(type))
211
199
  return true;
212
200
  }
213
- // Conventional Buffer parameter names. `buf` and `bytes` are strong
214
- // signals for Node Buffer parameters (the original FN target). `b`
215
- // and `chunk` are intentionally excluded — single-char names and
216
- // stream-chunk array variables produce too many FPs in real code.
217
201
  if (lowerName === 'buf' || lowerName === 'bytes')
218
202
  return true;
219
203
  return false;
220
204
  };
221
- /**
222
- * Check if index is user-controlled
223
- * Uses Set-based keyword matching for O(1) lookups
224
- */
225
205
  const isUserControlledIndex = (indexNode) => {
226
- // Direct MemberExpression: `req.query.length`, `req.body.size`,
227
- // `event.queryStringParameters.limit`, etc. — walk the chain to
228
- // check if the root identifier is a known taint source. Closes the
229
- // audit FN where `buf.slice(0, req.query.length)` was bypassing
230
- // detection because the arg was a MemberExpression, not an
231
- // Identifier. See benchmarks/AUDIT_PATTERNS.md §3.6 ("taint-source
232
- // breadth").
233
206
  if (indexNode.type === 'MemberExpression') {
234
207
  let walker = indexNode;
235
208
  while (walker.type === 'MemberExpression') {
@@ -237,11 +210,9 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
237
210
  }
238
211
  if (walker.type === 'Identifier') {
239
212
  const root = walker.name.toLowerCase();
240
- // Express / Lambda / Koa / Hono taint-source roots.
241
213
  if (['req', 'request', 'event', 'ctx', 'context'].includes(root)) {
242
214
  return true;
243
215
  }
244
- // Generic user-controlled keyword check on root name.
245
216
  for (const keyword of userControlledKeywords) {
246
217
  if (root.includes(keyword))
247
218
  return true;
@@ -250,12 +221,10 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
250
221
  }
251
222
  if (indexNode.type === 'Identifier') {
252
223
  const varName = indexNode.name.toLowerCase();
253
- // Check each part of the variable name against keywords Set
254
224
  for (const keyword of userControlledKeywords) {
255
225
  if (varName.includes(keyword))
256
226
  return true;
257
227
  }
258
- // Trace variable definition
259
228
  let currentScope = sourceCode.getScope(indexNode);
260
229
  let variable = null;
261
230
  while (currentScope) {
@@ -268,7 +237,6 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
268
237
  const def = variable.defs[0];
269
238
  if (def.type === 'Variable' && def.node.init) {
270
239
  const init = def.node.init;
271
- // Check MemberExpression involving user keywords (e.g. req.body.index)
272
240
  if (init.type === 'MemberExpression') {
273
241
  const objectText = sourceCode.getText(init.object).toLowerCase();
274
242
  const propertyText = sourceCode.getText(init.property).toLowerCase();
@@ -277,32 +245,26 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
277
245
  return true;
278
246
  }
279
247
  }
280
- // Check CallExpression with user-controlled arguments (Number(req.query.index), parseInt(), etc.)
281
248
  if (init.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression) {
282
- // Check if callee is a type conversion function
283
249
  const typeConversionFunctions = ['number', 'parseint', 'parsefloat', 'string', 'boolean'];
284
250
  let isTypeConversion = false;
285
251
  if (init.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
286
252
  isTypeConversion = typeConversionFunctions.includes(init.callee.name.toLowerCase());
287
253
  }
288
- // If it's a type conversion, check if the argument is user-controlled
289
254
  if (isTypeConversion && init.arguments.length > 0) {
290
255
  return isUserControlledIndex(init.arguments[0]);
291
256
  }
292
257
  }
293
- // Recursive check for Identifier assignment
294
258
  if (init.type === 'Identifier' && init.name !== indexNode.name) {
295
259
  return isUserControlledIndex(init);
296
260
  }
297
261
  }
298
262
  }
299
263
  }
300
- // Check CallExpression arguments (Number(req.query.index))
301
264
  if (indexNode.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression) {
302
265
  const typeConversionFunctions = ['Number', 'parseInt', 'parseFloat', 'String', 'Boolean'];
303
266
  if (indexNode.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
304
267
  typeConversionFunctions.includes(indexNode.callee.name)) {
305
- // Check if arguments are user-controlled
306
268
  for (const arg of indexNode.arguments) {
307
269
  if (isUserControlledIndex(arg)) {
308
270
  return true;
@@ -310,7 +272,6 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
310
272
  }
311
273
  }
312
274
  }
313
- // Check MemberExpression (req.query.index)
314
275
  if (indexNode.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression) {
315
276
  const text = sourceCode.getText(indexNode).toLowerCase();
316
277
  const keywords = ['req.', 'request.', 'query.', 'params.', 'body.', 'input.', 'user.'];
@@ -320,32 +281,23 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
320
281
  }
321
282
  return false;
322
283
  };
323
- /**
324
- * Check if index has been validated
325
- */
326
284
  const isIndexValidated = (indexNode) => {
327
- // If it's a literal number, check if it's non-negative
328
285
  if (indexNode.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof indexNode.value === 'number') {
329
286
  return indexNode.value >= 0;
330
287
  }
331
- // If it's an identifier, check if it comes from a bounds check function
332
288
  if (indexNode.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
333
289
  let current = indexNode;
334
- // Walk up the AST to find where this variable was assigned
335
290
  while (current) {
336
- // Check if we're in a variable declaration
337
291
  if (current.type === eslint_devkit_1.AST_NODE_TYPES.VariableDeclarator &&
338
292
  current.id.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
339
293
  current.id.name === indexNode.name &&
340
294
  current.init) {
341
295
  const init = current.init;
342
- // Check if assigned from a bounds check function
343
296
  if (init.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
344
297
  init.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
345
298
  boundsCheckFunctions.includes(init.callee.name)) {
346
299
  return true;
347
300
  }
348
- // Check if assigned from Math.min/max with buffer.length
349
301
  if (init.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
350
302
  init.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
351
303
  init.callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
@@ -356,21 +308,19 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
356
308
  }
357
309
  break;
358
310
  }
359
- // Check if it's a parameter in a function - assume validated if it's a function param
360
311
  if (current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration ||
361
312
  current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionExpression ||
362
313
  current.type === eslint_devkit_1.AST_NODE_TYPES.ArrowFunctionExpression) {
363
314
  const params = current.params;
364
315
  for (const param of params) {
365
316
  if (param.type === eslint_devkit_1.AST_NODE_TYPES.Identifier && param.name === indexNode.name) {
366
- return true; // Function parameters are assumed validated
317
+ return true;
367
318
  }
368
319
  }
369
320
  }
370
321
  current = current.parent;
371
322
  }
372
323
  }
373
- // Check if it's a call to a bounds check function directly
374
324
  if (indexNode.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
375
325
  indexNode.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
376
326
  boundsCheckFunctions.includes(indexNode.callee.name)) {
@@ -378,24 +328,17 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
378
328
  }
379
329
  return false;
380
330
  };
381
- /**
382
- * Check if there's a bounds check in the current scope
383
- */
384
331
  const hasBoundsCheck = (bufferName, indexNode) => {
385
- // Look for bounds checks in the current function scope
386
332
  let current = indexNode;
387
333
  while (current) {
388
- // Check if we're in a function
389
334
  if (current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration ||
390
335
  current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionExpression ||
391
336
  current.type === eslint_devkit_1.AST_NODE_TYPES.ArrowFunctionExpression) {
392
337
  break;
393
338
  }
394
- // Look for if statements that check bounds
395
339
  if (current.type === eslint_devkit_1.AST_NODE_TYPES.IfStatement) {
396
340
  const condition = current.test;
397
341
  const conditionText = sourceCode.getText(condition).toLowerCase();
398
- // Check for bounds checking patterns
399
342
  if (conditionText.includes(`${bufferName}.length`) &&
400
343
  (conditionText.includes('<') || conditionText.includes('<=') ||
401
344
  conditionText.includes('>') || conditionText.includes('>=') ||
@@ -403,7 +346,6 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
403
346
  return true;
404
347
  }
405
348
  }
406
- // Look for variable declarations that might be bounds checks
407
349
  if (current.type === eslint_devkit_1.AST_NODE_TYPES.VariableDeclaration) {
408
350
  for (const declarator of current.declarations) {
409
351
  if (declarator.init) {
@@ -416,7 +358,6 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
416
358
  }
417
359
  }
418
360
  }
419
- // Look for return statements or early returns that might indicate bounds checking
420
361
  if (current.type === eslint_devkit_1.AST_NODE_TYPES.ReturnStatement && current.argument) {
421
362
  const returnText = sourceCode.getText(current.argument).toLowerCase();
422
363
  if (returnText.includes(`${bufferName}.length`)) {
@@ -427,31 +368,20 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
427
368
  }
428
369
  return false;
429
370
  };
430
- /**
431
- * Check if index could be negative
432
- */
433
371
  const couldBeNegative = (indexNode) => {
434
- // Check for literal negative numbers
435
- // Check for literal negative numbers
436
372
  if (indexNode.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof indexNode.value === 'number') {
437
373
  return indexNode.value < 0;
438
374
  }
439
- // Check for unary minus expressions like -1, -10, etc.
440
375
  if (indexNode.type === eslint_devkit_1.AST_NODE_TYPES.UnaryExpression &&
441
376
  indexNode.operator === '-' &&
442
377
  indexNode.argument.type === eslint_devkit_1.AST_NODE_TYPES.Literal &&
443
378
  typeof indexNode.argument.value === 'number') {
444
- return true; // -N is always negative for positive N
379
+ return true;
445
380
  }
446
- // Check for binary expressions that could be negative like userInput - 10
447
381
  if (indexNode.type === eslint_devkit_1.AST_NODE_TYPES.BinaryExpression && indexNode.operator === '-') {
448
- // userInput - 10 could be negative, we can't be sure statically
449
- return true; // Conservative: assume it could be negative
382
+ return true;
450
383
  }
451
- // For variables, we can't be sure, but we can check for obvious patterns
452
384
  if (indexNode.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
453
- // Check if this variable is assigned a negative value somewhere
454
- // This is a simplified check - in practice we'd need more sophisticated analysis
455
385
  let current = indexNode;
456
386
  while (current) {
457
387
  if (current.type === eslint_devkit_1.AST_NODE_TYPES.VariableDeclarator && current.init) {
@@ -460,7 +390,6 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
460
390
  current.init.value < 0) {
461
391
  return true;
462
392
  }
463
- // Check for unary minus assignments
464
393
  if (current.init.type === eslint_devkit_1.AST_NODE_TYPES.UnaryExpression &&
465
394
  current.init.operator === '-' &&
466
395
  current.init.argument.type === eslint_devkit_1.AST_NODE_TYPES.Literal &&
@@ -474,17 +403,14 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
474
403
  return false;
475
404
  };
476
405
  return {
477
- // Track buffer variable declarations
478
406
  VariableDeclarator(node) {
479
407
  if (node.id.type === eslint_devkit_1.AST_NODE_TYPES.Identifier && node.init) {
480
408
  const varName = node.id.name;
481
- // Check if assigned a buffer type
482
409
  if (node.init.type === eslint_devkit_1.AST_NODE_TYPES.NewExpression &&
483
410
  node.init.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
484
411
  bufferTypes.includes(node.init.callee.name)) {
485
412
  bufferVars.add(varName);
486
413
  }
487
- // Check if assigned from Buffer.from() or Buffer.alloc()
488
414
  if (node.init.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
489
415
  node.init.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
490
416
  node.init.callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
@@ -493,7 +419,6 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
493
419
  ['from', 'alloc', 'allocUnsafe'].includes(node.init.callee.property.name)) {
494
420
  bufferVars.add(varName);
495
421
  }
496
- // Check if assigned a buffer method result
497
422
  if (node.init.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression) {
498
423
  const callee = node.init.callee;
499
424
  if (callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
@@ -502,20 +427,16 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
502
427
  bufferVars.add(varName);
503
428
  }
504
429
  }
505
- // Check variable name patterns
506
430
  if (bufferTypes.some(type => varName.toLowerCase().includes(type.toLowerCase()))) {
507
431
  bufferVars.add(varName);
508
432
  }
509
433
  }
510
434
  },
511
- // Check member expressions (buffer[index], buffer.method())
512
435
  MemberExpression(node) {
513
- // Check for buffer[index] access
514
436
  if (node.computed && node.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
515
437
  const bufferName = node.object.name;
516
438
  const indexNode = node.property;
517
439
  if (isBufferType(bufferName)) {
518
- // Check for negative indices
519
440
  if (couldBeNegative(indexNode)) {
520
441
  context.report({
521
442
  node,
@@ -527,9 +448,7 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
527
448
  });
528
449
  return;
529
450
  }
530
- // Check for user-controlled indices without validation
531
451
  if (isUserControlledIndex(indexNode) && !isIndexValidated(indexNode)) {
532
- // Check if there's a bounds check in scope
533
452
  if (!hasBoundsCheck(bufferName, indexNode)) {
534
453
  if (safetyChecker.isSafe(node, context)) {
535
454
  return;
@@ -545,7 +464,6 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
545
464
  return;
546
465
  }
547
466
  }
548
- // Check if there's any bounds validation
549
467
  if (!hasBoundsCheck(bufferName, indexNode) && !isIndexValidated(indexNode)) {
550
468
  if (safetyChecker.isSafe(node, context)) {
551
469
  return;
@@ -561,25 +479,20 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
561
479
  }
562
480
  }
563
481
  }
564
- // Check for buffer method calls that need bounds checking
565
482
  if (node.property.type === 'Identifier' &&
566
483
  bufferMethods.includes(node.property.name) &&
567
484
  node.object.type === 'Identifier' &&
568
485
  isBufferType(node.object.name)) {
569
- // This is a parent of a CallExpression, we'll check it there
570
486
  }
571
487
  },
572
- // Check buffer method calls
573
488
  CallExpression(node) {
574
489
  const callee = node.callee;
575
- // Check for buffer.slice() calls
576
490
  if (callee.type === 'MemberExpression' &&
577
491
  callee.property.type === 'Identifier' &&
578
492
  callee.property.name === 'slice' &&
579
493
  callee.object.type === 'Identifier' &&
580
494
  isBufferType(callee.object.name)) {
581
495
  const args = node.arguments;
582
- // Check slice arguments
583
496
  for (const arg of args) {
584
497
  if (isUserControlledIndex(arg) && !isIndexValidated(arg)) {
585
498
  if (safetyChecker.isSafe(node, context)) {
@@ -596,14 +509,12 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
596
509
  }
597
510
  }
598
511
  }
599
- // Check for buffer read/write methods
600
512
  if (callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
601
513
  callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
602
514
  bufferMethods.includes(callee.property.name) &&
603
515
  callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
604
516
  isBufferType(callee.object.name)) {
605
517
  const args = node.arguments;
606
- // Check offset/length arguments
607
518
  for (const arg of args) {
608
519
  if (isUserControlledIndex(arg) && !isIndexValidated(arg)) {
609
520
  if (safetyChecker.isSafe(node, context)) {
@@ -621,16 +532,12 @@ exports.noBufferOverread = (0, eslint_devkit_1.createRule)({
621
532
  }
622
533
  }
623
534
  },
624
- // Check binary expressions that might involve buffer operations
625
535
  BinaryExpression(node) {
626
- // Look for patterns like buffer.length - something that might indicate bounds checking
627
536
  const leftText = sourceCode.getText(node.left);
628
537
  const rightText = sourceCode.getText(node.right);
629
538
  if (leftText.includes('.length') || rightText.includes('.length')) {
630
- // This might be a bounds check - we could analyze this further
631
539
  }
632
540
  }
633
541
  };
634
542
  },
635
543
  });
636
- //# sourceMappingURL=index.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noCryptojs = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -73,24 +68,22 @@ exports.noCryptojs = (0, eslint_devkit_1.createRule)({
73
68
  suggest: [
74
69
  {
75
70
  messageId: 'useNativeCrypto',
76
- fix: () => null, // Complex migration
71
+ fix: () => null,
77
72
  },
78
73
  {
79
74
  messageId: 'useWebCrypto',
80
- fix: () => null, // Complex migration
75
+ fix: () => null,
81
76
  },
82
77
  ],
83
78
  });
84
79
  }
85
80
  return {
86
- // import CryptoJS from 'crypto-js'
87
81
  ImportDeclaration(node) {
88
82
  if (typeof node.source.value === 'string' &&
89
83
  (node.source.value === 'crypto-js' || node.source.value.startsWith('crypto-js/'))) {
90
84
  reportDeprecatedLibrary(node);
91
85
  }
92
86
  },
93
- // const CryptoJS = require('crypto-js')
94
87
  CallExpression(node) {
95
88
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
96
89
  node.callee.name === 'require' &&
@@ -104,4 +97,3 @@ exports.noCryptojs = (0, eslint_devkit_1.createRule)({
104
97
  };
105
98
  },
106
99
  });
107
- //# sourceMappingURL=index.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noCryptojsWeakRandom = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -63,19 +58,14 @@ exports.noCryptojsWeakRandom = (0, eslint_devkit_1.createRule)({
63
58
  function checkCallExpression(node) {
64
59
  if (isTestFile)
65
60
  return;
66
- // Check for CryptoJS.lib.WordArray.random()
67
- // or WordArray.random()
68
61
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression) {
69
62
  const callee = node.callee;
70
- // Check for .random() method
71
63
  if (callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
72
64
  callee.property.name === 'random') {
73
- // Check if it's on WordArray
74
65
  if (callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier && callee.object.name === 'WordArray') {
75
66
  reportWeakRandom(node);
76
67
  return;
77
68
  }
78
- // Check for CryptoJS.lib.WordArray.random()
79
69
  if (callee.object.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression) {
80
70
  const innerObj = callee.object;
81
71
  if (innerObj.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
@@ -86,7 +76,6 @@ exports.noCryptojsWeakRandom = (0, eslint_devkit_1.createRule)({
86
76
  }
87
77
  }
88
78
  }
89
- // Check for CryptoJS.random or similar patterns
90
79
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
91
80
  node.callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
92
81
  node.callee.object.name === 'CryptoJS' &&
@@ -102,7 +91,7 @@ exports.noCryptojsWeakRandom = (0, eslint_devkit_1.createRule)({
102
91
  suggest: [
103
92
  {
104
93
  messageId: 'useNativeRandom',
105
- fix: () => null, // Complex refactoring
94
+ fix: () => null,
106
95
  },
107
96
  ],
108
97
  });
@@ -112,4 +101,3 @@ exports.noCryptojsWeakRandom = (0, eslint_devkit_1.createRule)({
112
101
  };
113
102
  },
114
103
  });
115
- //# sourceMappingURL=index.js.map
@@ -1,14 +1,6 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noDataInTempStorage = void 0;
9
- /**
10
- * @fileoverview Prevent sensitive data in temp directories
11
- */
12
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
13
5
  const DEFAULT_TEMP_PATHS = ['/tmp', '/var/tmp', 'temp/', '/temp'];
14
6
  exports.noDataInTempStorage = (0, eslint_devkit_1.createRule)({
@@ -65,7 +57,6 @@ exports.noDataInTempStorage = (0, eslint_devkit_1.createRule)({
65
57
  }
66
58
  return {
67
59
  CallExpression(node) {
68
- // Detect fs.writeFileSync or fs.writeFile with temp path
69
60
  if (node.callee.type === 'MemberExpression' &&
70
61
  node.callee.object.type === 'Identifier' &&
71
62
  node.callee.object.name === 'fs' &&
@@ -80,10 +71,8 @@ exports.noDataInTempStorage = (0, eslint_devkit_1.createRule)({
80
71
  }
81
72
  },
82
73
  Literal(node) {
83
- // Detect temp path literals
84
74
  if (typeof node.value === 'string') {
85
75
  if (tempPaths.some(tp => node.value.includes(tp))) {
86
- // Only flag if parent is assignment or variable declaration
87
76
  const parent = node.parent;
88
77
  if (parent?.type === 'VariableDeclarator' || parent?.type === 'AssignmentExpression') {
89
78
  report(node);
@@ -94,4 +83,3 @@ exports.noDataInTempStorage = (0, eslint_devkit_1.createRule)({
94
83
  };
95
84
  },
96
85
  });
97
- //# sourceMappingURL=index.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noDeprecatedBuffer = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -48,11 +43,6 @@ exports.noDeprecatedBuffer = (0, eslint_devkit_1.createRule)({
48
43
  return (node.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
49
44
  node.name === 'Buffer');
50
45
  }
51
- /**
52
- * Returns the safe replacement for new Buffer(args):
53
- * new Buffer(number) → Buffer.alloc(number)
54
- * new Buffer(...) → Buffer.from(...)
55
- */
56
46
  function getBufferReplacement(args) {
57
47
  if (args.length === 1 &&
58
48
  args[0].type === eslint_devkit_1.AST_NODE_TYPES.Literal &&
@@ -62,7 +52,6 @@ exports.noDeprecatedBuffer = (0, eslint_devkit_1.createRule)({
62
52
  return 'Buffer.from';
63
53
  }
64
54
  return {
65
- // Catches `new Buffer(size)` / `new Buffer(arr)` / `new Buffer('str')`
66
55
  NewExpression(node) {
67
56
  if (!isBufferIdentifier(node.callee))
68
57
  return;
@@ -71,8 +60,6 @@ exports.noDeprecatedBuffer = (0, eslint_devkit_1.createRule)({
71
60
  node,
72
61
  messageId: 'deprecatedBufferConstructor',
73
62
  fix(fixer) {
74
- // Replace `new Buffer` with `Buffer.from` or `Buffer.alloc`,
75
- // removing the `new` keyword in the process.
76
63
  const sourceCode = context.sourceCode;
77
64
  const newToken = sourceCode.getFirstToken(node);
78
65
  const calleeEnd = node.callee.range[1];
@@ -80,7 +67,6 @@ exports.noDeprecatedBuffer = (0, eslint_devkit_1.createRule)({
80
67
  },
81
68
  });
82
69
  },
83
- // Catches `Buffer(size)` (factory call without `new`)
84
70
  CallExpression(node) {
85
71
  if (!isBufferIdentifier(node.callee))
86
72
  return;
@@ -96,4 +82,3 @@ exports.noDeprecatedBuffer = (0, eslint_devkit_1.createRule)({
96
82
  };
97
83
  },
98
84
  });
99
- //# sourceMappingURL=index.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noDeprecatedCipherMethod = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -64,11 +59,9 @@ exports.noDeprecatedCipherMethod = (0, eslint_devkit_1.createRule)({
64
59
  function checkCallExpression(node) {
65
60
  if (isTestFile)
66
61
  return;
67
- // Check for crypto.createCipher() or crypto.createDecipher()
68
62
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
69
63
  node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
70
64
  DEPRECATED_METHODS.has(node.callee.property.name)) {
71
- // Capture narrowed type before callback (TypeScript loses narrowing in closures)
72
65
  const callee = node.callee;
73
66
  const propertyNode = callee.property;
74
67
  const methodName = propertyNode.name;
@@ -90,7 +83,6 @@ exports.noDeprecatedCipherMethod = (0, eslint_devkit_1.createRule)({
90
83
  ],
91
84
  });
92
85
  }
93
- // Check for standalone createCipher() / createDecipher()
94
86
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
95
87
  DEPRECATED_METHODS.has(node.callee.name)) {
96
88
  const methodName = node.callee.name;
@@ -118,4 +110,3 @@ exports.noDeprecatedCipherMethod = (0, eslint_devkit_1.createRule)({
118
110
  };
119
111
  },
120
112
  });
121
- //# sourceMappingURL=index.js.map
@@ -1,12 +1,7 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License.
5
- */
6
2
  Object.defineProperty(exports, "__esModule", { value: true });
7
3
  exports.noDynamicAlgorithmSelection = void 0;
8
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
9
- /** Node crypto functions whose first argument is the algorithm name. */
10
5
  const CRYPTO_ALGORITHM_FUNCTIONS = new Set([
11
6
  'createHash',
12
7
  'createHmac',
@@ -25,9 +20,6 @@ exports.noDynamicAlgorithmSelection = (0, eslint_devkit_1.createRule)({
25
20
  url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-dynamic-algorithm-selection.md',
26
21
  description: 'Disallow dynamic algorithm names in Node.js crypto functions (CWE-327)',
27
22
  cwe: 'CWE-327',
28
- // Match the CVSS the finding emits: the message sets no per-message
29
- // cvss, so it inherits CWE_MAPPING['CWE-327'] (7.5) via enrichFromCWE.
30
- // Locked by security-cvss-docs-consistency.lock.test.ts.
31
23
  cvss: 7.5,
32
24
  },
33
25
  messages: {
@@ -48,7 +40,6 @@ exports.noDynamicAlgorithmSelection = (0, eslint_devkit_1.createRule)({
48
40
  return {
49
41
  CallExpression(node) {
50
42
  const { callee, arguments: args } = node;
51
- // Match crypto.method(algo) — MemberExpression where object is 'crypto'
52
43
  if (callee.type !== eslint_devkit_1.AST_NODE_TYPES.MemberExpression)
53
44
  return;
54
45
  if (callee.object.type !== eslint_devkit_1.AST_NODE_TYPES.Identifier)
@@ -64,12 +55,9 @@ exports.noDynamicAlgorithmSelection = (0, eslint_devkit_1.createRule)({
64
55
  const firstArg = args[0];
65
56
  if (!firstArg)
66
57
  return;
67
- // String literal → already handled by no-weak-hash-algorithm for weak algos;
68
- // a literal is always an intentional, auditable choice.
69
58
  if (firstArg.type === eslint_devkit_1.AST_NODE_TYPES.Literal &&
70
59
  typeof firstArg.value === 'string')
71
60
  return;
72
- // Template literal without expressions → equivalent to literal, safe.
73
61
  if (firstArg.type === eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral &&
74
62
  firstArg.expressions.length === 0)
75
63
  return;
@@ -82,4 +70,3 @@ exports.noDynamicAlgorithmSelection = (0, eslint_devkit_1.createRule)({
82
70
  };
83
71
  },
84
72
  });
85
- //# sourceMappingURL=index.js.map