eslint-plugin-node-security 4.5.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +5 -8
- package/src/index.d.ts +17 -10
- package/src/index.js +8 -31
- package/src/oxlint.d.ts +17 -20
- package/src/oxlint.js +0 -19
- package/src/rules/detect-child-process/index.js +0 -107
- package/src/rules/detect-eval-with-expression/index.js +2 -48
- package/src/rules/detect-non-literal-fs-filename/index.js +0 -82
- package/src/rules/detect-suspicious-dependencies/index.js +0 -12
- package/src/rules/lock-file/index.js +0 -18
- package/src/rules/no-arbitrary-file-access/index.js +0 -52
- package/src/rules/no-buffer-overread/index.js +3 -96
- package/src/rules/no-cryptojs/index.js +2 -10
- package/src/rules/no-cryptojs-weak-random/index.js +1 -13
- package/src/rules/no-data-in-temp-storage/index.js +0 -12
- package/src/rules/no-deprecated-buffer/index.js +0 -15
- package/src/rules/no-deprecated-cipher-method/index.js +0 -9
- package/src/rules/no-dynamic-algorithm-selection/index.js +0 -13
- package/src/rules/no-dynamic-command-string/index.js +201 -0
- package/src/rules/no-dynamic-dependency-loading/index.js +0 -13
- package/src/rules/no-dynamic-require/index.js +0 -10
- package/src/rules/no-ecb-mode/index.js +0 -7
- package/src/rules/no-insecure-key-derivation/index.js +0 -10
- package/src/rules/no-insecure-rsa-padding/index.js +0 -9
- package/src/rules/no-math-random-crypto/index.js +1 -17
- package/src/rules/no-self-signed-certs/index.js +1 -9
- package/src/rules/no-sha1-hash/index.js +0 -9
- package/src/rules/no-shell-injection/index.js +0 -20
- package/src/rules/no-ssrf/index.js +5 -79
- package/src/rules/no-static-iv/index.js +2 -21
- package/src/rules/no-timing-unsafe-compare/index.js +1 -11
- package/src/rules/no-toctou-vulnerability/index.js +0 -22
- package/src/rules/no-unsafe-buffer-alloc/index.js +87 -0
- package/src/rules/no-unsafe-dynamic-require/index.js +0 -22
- package/src/rules/no-weak-cipher-algorithm/index.js +0 -19
- package/src/rules/no-weak-hash-algorithm/index.js +0 -22
- package/src/rules/no-zip-slip/index.js +3 -81
- package/src/rules/prefer-native-crypto/index.js +4 -11
- package/src/rules/require-dependency-integrity/index.js +0 -11
- package/src/rules/require-secure-credential-storage/index.js +0 -12
- package/src/rules/require-secure-deletion/index.js +0 -29
- package/src/rules/require-storage-encryption/index.js +0 -12
- package/src/types/index.js +0 -6
- package/CHANGELOG.md +0 -384
- package/src/index.js.map +0 -1
- package/src/oxlint.js.map +0 -1
- package/src/rules/detect-child-process/index.d.ts +0 -52
- package/src/rules/detect-child-process/index.js.map +0 -1
- package/src/rules/detect-eval-with-expression/index.d.ts +0 -46
- package/src/rules/detect-eval-with-expression/index.js.map +0 -1
- package/src/rules/detect-non-literal-fs-filename/index.d.ts +0 -52
- package/src/rules/detect-non-literal-fs-filename/index.js.map +0 -1
- package/src/rules/detect-suspicious-dependencies/index.d.ts +0 -12
- package/src/rules/detect-suspicious-dependencies/index.js.map +0 -1
- package/src/rules/lock-file/index.d.ts +0 -13
- package/src/rules/lock-file/index.js.map +0 -1
- package/src/rules/no-arbitrary-file-access/index.d.ts +0 -12
- package/src/rules/no-arbitrary-file-access/index.js.map +0 -1
- package/src/rules/no-buffer-overread/index.d.ts +0 -39
- package/src/rules/no-buffer-overread/index.js.map +0 -1
- package/src/rules/no-cryptojs/index.d.ts +0 -24
- package/src/rules/no-cryptojs/index.js.map +0 -1
- package/src/rules/no-cryptojs-weak-random/index.d.ts +0 -24
- package/src/rules/no-cryptojs-weak-random/index.js.map +0 -1
- package/src/rules/no-data-in-temp-storage/index.d.ts +0 -14
- package/src/rules/no-data-in-temp-storage/index.js.map +0 -1
- package/src/rules/no-deprecated-buffer/index.d.ts +0 -23
- package/src/rules/no-deprecated-buffer/index.js.map +0 -1
- package/src/rules/no-deprecated-cipher-method/index.d.ts +0 -23
- package/src/rules/no-deprecated-cipher-method/index.js.map +0 -1
- package/src/rules/no-dynamic-algorithm-selection/index.d.ts +0 -26
- package/src/rules/no-dynamic-algorithm-selection/index.js.map +0 -1
- package/src/rules/no-dynamic-dependency-loading/index.d.ts +0 -12
- package/src/rules/no-dynamic-dependency-loading/index.js.map +0 -1
- package/src/rules/no-dynamic-require/index.d.ts +0 -21
- package/src/rules/no-dynamic-require/index.js.map +0 -1
- package/src/rules/no-ecb-mode/index.d.ts +0 -23
- package/src/rules/no-ecb-mode/index.js.map +0 -1
- package/src/rules/no-insecure-key-derivation/index.d.ts +0 -24
- package/src/rules/no-insecure-key-derivation/index.js.map +0 -1
- package/src/rules/no-insecure-rsa-padding/index.d.ts +0 -24
- package/src/rules/no-insecure-rsa-padding/index.js.map +0 -1
- package/src/rules/no-math-random-crypto/index.d.ts +0 -26
- package/src/rules/no-math-random-crypto/index.js.map +0 -1
- package/src/rules/no-self-signed-certs/index.d.ts +0 -23
- package/src/rules/no-self-signed-certs/index.js.map +0 -1
- package/src/rules/no-sha1-hash/index.d.ts +0 -24
- package/src/rules/no-sha1-hash/index.js.map +0 -1
- package/src/rules/no-shell-injection/index.d.ts +0 -20
- package/src/rules/no-shell-injection/index.js.map +0 -1
- package/src/rules/no-ssrf/index.d.ts +0 -25
- package/src/rules/no-ssrf/index.js.map +0 -1
- package/src/rules/no-static-iv/index.d.ts +0 -23
- package/src/rules/no-static-iv/index.js.map +0 -1
- package/src/rules/no-timing-unsafe-compare/index.d.ts +0 -23
- package/src/rules/no-timing-unsafe-compare/index.js.map +0 -1
- package/src/rules/no-toctou-vulnerability/index.d.ts +0 -26
- package/src/rules/no-toctou-vulnerability/index.js.map +0 -1
- package/src/rules/no-unsafe-dynamic-require/index.d.ts +0 -19
- package/src/rules/no-unsafe-dynamic-require/index.js.map +0 -1
- package/src/rules/no-weak-cipher-algorithm/index.d.ts +0 -25
- package/src/rules/no-weak-cipher-algorithm/index.js.map +0 -1
- package/src/rules/no-weak-hash-algorithm/index.d.ts +0 -25
- package/src/rules/no-weak-hash-algorithm/index.js.map +0 -1
- package/src/rules/no-zip-slip/index.d.ts +0 -35
- package/src/rules/no-zip-slip/index.js.map +0 -1
- package/src/rules/prefer-native-crypto/index.d.ts +0 -23
- package/src/rules/prefer-native-crypto/index.js.map +0 -1
- package/src/rules/require-dependency-integrity/index.d.ts +0 -12
- package/src/rules/require-dependency-integrity/index.js.map +0 -1
- package/src/rules/require-secure-credential-storage/index.d.ts +0 -12
- package/src/rules/require-secure-credential-storage/index.js.map +0 -1
- package/src/rules/require-secure-deletion/index.d.ts +0 -17
- package/src/rules/require-secure-deletion/index.js.map +0 -1
- package/src/rules/require-storage-encryption/index.d.ts +0 -12
- package/src/rules/require-storage-encryption/index.js.map +0 -1
- 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;
|
|
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;
|
|
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
|
-
|
|
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,
|
|
71
|
+
fix: () => null,
|
|
77
72
|
},
|
|
78
73
|
{
|
|
79
74
|
messageId: 'useWebCrypto',
|
|
80
|
-
fix: () => null,
|
|
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,
|
|
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
|