eslint-plugin-node-security 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/README.md +50 -0
- package/package.json +79 -0
- package/src/index.d.ts +10 -0
- package/src/index.js +118 -0
- package/src/index.js.map +1 -0
- package/src/rules/detect-child-process/index.d.ts +30 -0
- package/src/rules/detect-child-process/index.js +535 -0
- package/src/rules/detect-child-process/index.js.map +1 -0
- package/src/rules/detect-eval-with-expression/index.d.ts +28 -0
- package/src/rules/detect-eval-with-expression/index.js +398 -0
- package/src/rules/detect-eval-with-expression/index.js.map +1 -0
- package/src/rules/detect-non-literal-fs-filename/index.d.ts +26 -0
- package/src/rules/detect-non-literal-fs-filename/index.js +460 -0
- package/src/rules/detect-non-literal-fs-filename/index.js.map +1 -0
- package/src/rules/detect-suspicious-dependencies/index.d.ts +12 -0
- package/src/rules/detect-suspicious-dependencies/index.js +77 -0
- package/src/rules/detect-suspicious-dependencies/index.js.map +1 -0
- package/src/rules/lock-file/index.d.ts +13 -0
- package/src/rules/lock-file/index.js +94 -0
- package/src/rules/lock-file/index.js.map +1 -0
- package/src/rules/no-arbitrary-file-access/index.d.ts +12 -0
- package/src/rules/no-arbitrary-file-access/index.js +201 -0
- package/src/rules/no-arbitrary-file-access/index.js.map +1 -0
- package/src/rules/no-buffer-overread/index.d.ts +39 -0
- package/src/rules/no-buffer-overread/index.js +612 -0
- package/src/rules/no-buffer-overread/index.js.map +1 -0
- package/src/rules/no-cryptojs/index.d.ts +24 -0
- package/src/rules/no-cryptojs/index.js +104 -0
- package/src/rules/no-cryptojs/index.js.map +1 -0
- package/src/rules/no-cryptojs-weak-random/index.d.ts +24 -0
- package/src/rules/no-cryptojs-weak-random/index.js +112 -0
- package/src/rules/no-cryptojs-weak-random/index.js.map +1 -0
- package/src/rules/no-data-in-temp-storage/index.d.ts +14 -0
- package/src/rules/no-data-in-temp-storage/index.js +99 -0
- package/src/rules/no-data-in-temp-storage/index.js.map +1 -0
- package/src/rules/no-deprecated-cipher-method/index.d.ts +23 -0
- package/src/rules/no-deprecated-cipher-method/index.js +118 -0
- package/src/rules/no-deprecated-cipher-method/index.js.map +1 -0
- package/src/rules/no-dynamic-dependency-loading/index.d.ts +12 -0
- package/src/rules/no-dynamic-dependency-loading/index.js +55 -0
- package/src/rules/no-dynamic-dependency-loading/index.js.map +1 -0
- package/src/rules/no-dynamic-require/index.d.ts +21 -0
- package/src/rules/no-dynamic-require/index.js +122 -0
- package/src/rules/no-dynamic-require/index.js.map +1 -0
- package/src/rules/no-ecb-mode/index.d.ts +23 -0
- package/src/rules/no-ecb-mode/index.js +113 -0
- package/src/rules/no-ecb-mode/index.js.map +1 -0
- package/src/rules/no-insecure-key-derivation/index.d.ts +24 -0
- package/src/rules/no-insecure-key-derivation/index.js +116 -0
- package/src/rules/no-insecure-key-derivation/index.js.map +1 -0
- package/src/rules/no-insecure-rsa-padding/index.d.ts +24 -0
- package/src/rules/no-insecure-rsa-padding/index.js +110 -0
- package/src/rules/no-insecure-rsa-padding/index.js.map +1 -0
- package/src/rules/no-pii-in-logs/index.d.ts +12 -0
- package/src/rules/no-pii-in-logs/index.js +74 -0
- package/src/rules/no-pii-in-logs/index.js.map +1 -0
- package/src/rules/no-self-signed-certs/index.d.ts +23 -0
- package/src/rules/no-self-signed-certs/index.js +116 -0
- package/src/rules/no-self-signed-certs/index.js.map +1 -0
- package/src/rules/no-sha1-hash/index.d.ts +24 -0
- package/src/rules/no-sha1-hash/index.js +128 -0
- package/src/rules/no-sha1-hash/index.js.map +1 -0
- package/src/rules/no-static-iv/index.d.ts +23 -0
- package/src/rules/no-static-iv/index.js +147 -0
- package/src/rules/no-static-iv/index.js.map +1 -0
- package/src/rules/no-timing-unsafe-compare/index.d.ts +23 -0
- package/src/rules/no-timing-unsafe-compare/index.js +114 -0
- package/src/rules/no-timing-unsafe-compare/index.js.map +1 -0
- package/src/rules/no-toctou-vulnerability/index.d.ts +26 -0
- package/src/rules/no-toctou-vulnerability/index.js +214 -0
- package/src/rules/no-toctou-vulnerability/index.js.map +1 -0
- package/src/rules/no-unsafe-dynamic-require/index.d.ts +19 -0
- package/src/rules/no-unsafe-dynamic-require/index.js +112 -0
- package/src/rules/no-unsafe-dynamic-require/index.js.map +1 -0
- package/src/rules/no-weak-cipher-algorithm/index.d.ts +25 -0
- package/src/rules/no-weak-cipher-algorithm/index.js +190 -0
- package/src/rules/no-weak-cipher-algorithm/index.js.map +1 -0
- package/src/rules/no-weak-hash-algorithm/index.d.ts +25 -0
- package/src/rules/no-weak-hash-algorithm/index.js +218 -0
- package/src/rules/no-weak-hash-algorithm/index.js.map +1 -0
- package/src/rules/no-zip-slip/index.d.ts +35 -0
- package/src/rules/no-zip-slip/index.js +451 -0
- package/src/rules/no-zip-slip/index.js.map +1 -0
- package/src/rules/prefer-native-crypto/index.d.ts +23 -0
- package/src/rules/prefer-native-crypto/index.js +124 -0
- package/src/rules/prefer-native-crypto/index.js.map +1 -0
- package/src/rules/require-dependency-integrity/index.d.ts +12 -0
- package/src/rules/require-dependency-integrity/index.js +70 -0
- package/src/rules/require-dependency-integrity/index.js.map +1 -0
- package/src/rules/require-secure-credential-storage/index.d.ts +12 -0
- package/src/rules/require-secure-credential-storage/index.js +54 -0
- package/src/rules/require-secure-credential-storage/index.js.map +1 -0
- package/src/rules/require-secure-deletion/index.d.ts +12 -0
- package/src/rules/require-secure-deletion/index.js +46 -0
- package/src/rules/require-secure-deletion/index.js.map +1 -0
- package/src/rules/require-storage-encryption/index.d.ts +12 -0
- package/src/rules/require-storage-encryption/index.js +54 -0
- package/src/rules/require-storage-encryption/index.js.map +1 -0
- package/src/types/index.d.ts +24 -0
- package/src/types/index.js +8 -0
- package/src/types/index.js.map +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
3
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
4
|
+
* MIT license that can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* ESLint Rule: no-cryptojs
|
|
8
|
+
* Warns on usage of deprecated crypto-js library
|
|
9
|
+
* CWE-1104: Use of Unmaintained Third Party Components
|
|
10
|
+
*
|
|
11
|
+
* crypto-js is not maintained since 2022 and recommends using native crypto
|
|
12
|
+
* @see https://www.npmjs.com/package/crypto-js
|
|
13
|
+
*/
|
|
14
|
+
import type { TSESLint } from '@interlace/eslint-devkit';
|
|
15
|
+
type MessageIds = 'deprecatedCryptojs' | 'useNativeCrypto' | 'useWebCrypto';
|
|
16
|
+
export interface Options {
|
|
17
|
+
/** Severity level. Default: 'warn' */
|
|
18
|
+
severity?: 'error' | 'warn';
|
|
19
|
+
}
|
|
20
|
+
type RuleOptions = [Options?];
|
|
21
|
+
export declare const noCryptojs: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
|
|
22
|
+
name: string;
|
|
23
|
+
};
|
|
24
|
+
export type { Options as NoCryptojsOptions };
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.noCryptojs = void 0;
|
|
9
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
+
exports.noCryptojs = (0, eslint_devkit_1.createRule)({
|
|
11
|
+
name: 'no-cryptojs',
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'suggestion',
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Disallow deprecated crypto-js library (use native crypto instead)',
|
|
16
|
+
},
|
|
17
|
+
hasSuggestions: true,
|
|
18
|
+
messages: {
|
|
19
|
+
deprecatedCryptojs: (0, eslint_devkit_1.formatLLMMessage)({
|
|
20
|
+
icon: eslint_devkit_1.MessageIcons.WARNING,
|
|
21
|
+
issueName: 'Deprecated crypto-js library',
|
|
22
|
+
cwe: 'CWE-1104',
|
|
23
|
+
description: 'crypto-js is no longer maintained (last update: 2022). Future vulnerabilities will not be patched.',
|
|
24
|
+
severity: 'MEDIUM',
|
|
25
|
+
fix: 'Migrate to native Node.js crypto module or Web Crypto API',
|
|
26
|
+
documentationLink: 'https://nodejs.org/api/crypto.html',
|
|
27
|
+
}),
|
|
28
|
+
useNativeCrypto: (0, eslint_devkit_1.formatLLMMessage)({
|
|
29
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
30
|
+
issueName: 'Use Node.js crypto',
|
|
31
|
+
description: 'Node.js crypto module is maintained by the Node.js core team',
|
|
32
|
+
severity: 'LOW',
|
|
33
|
+
fix: 'import crypto from "node:crypto"',
|
|
34
|
+
documentationLink: 'https://nodejs.org/api/crypto.html',
|
|
35
|
+
}),
|
|
36
|
+
useWebCrypto: (0, eslint_devkit_1.formatLLMMessage)({
|
|
37
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
38
|
+
issueName: 'Use Web Crypto API',
|
|
39
|
+
description: 'Web Crypto API works in browsers and Node.js',
|
|
40
|
+
severity: 'LOW',
|
|
41
|
+
fix: 'globalThis.crypto.subtle',
|
|
42
|
+
documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API',
|
|
43
|
+
}),
|
|
44
|
+
},
|
|
45
|
+
schema: [
|
|
46
|
+
{
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
severity: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
enum: ['error', 'warn'],
|
|
52
|
+
default: 'warn',
|
|
53
|
+
description: 'Severity level for the rule',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
defaultOptions: [
|
|
61
|
+
{
|
|
62
|
+
severity: 'warn',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
create(context) {
|
|
66
|
+
function reportDeprecatedLibrary(node) {
|
|
67
|
+
context.report({
|
|
68
|
+
node,
|
|
69
|
+
messageId: 'deprecatedCryptojs',
|
|
70
|
+
suggest: [
|
|
71
|
+
{
|
|
72
|
+
messageId: 'useNativeCrypto',
|
|
73
|
+
fix: () => null, // Complex migration
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
messageId: 'useWebCrypto',
|
|
77
|
+
fix: () => null, // Complex migration
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
// import CryptoJS from 'crypto-js'
|
|
84
|
+
ImportDeclaration(node) {
|
|
85
|
+
if (typeof node.source.value === 'string' &&
|
|
86
|
+
(node.source.value === 'crypto-js' || node.source.value.startsWith('crypto-js/'))) {
|
|
87
|
+
reportDeprecatedLibrary(node);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
// const CryptoJS = require('crypto-js')
|
|
91
|
+
CallExpression(node) {
|
|
92
|
+
if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
93
|
+
node.callee.name === 'require' &&
|
|
94
|
+
node.arguments.length === 1 &&
|
|
95
|
+
node.arguments[0].type === eslint_devkit_1.AST_NODE_TYPES.Literal &&
|
|
96
|
+
typeof node.arguments[0].value === 'string' &&
|
|
97
|
+
(node.arguments[0].value === 'crypto-js' || node.arguments[0].value.startsWith('crypto-js/'))) {
|
|
98
|
+
reportDeprecatedLibrary(node);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-node-security/src/rules/no-cryptojs/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
3
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
4
|
+
* MIT license that can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* ESLint Rule: no-cryptojs-weak-random
|
|
8
|
+
* Detects crypto-js WordArray.random() which was insecure pre-3.2.1
|
|
9
|
+
* CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator
|
|
10
|
+
*
|
|
11
|
+
* CVE-2020-36732: crypto-js < 3.2.1 used Math.random() for crypto operations
|
|
12
|
+
* @see https://nvd.nist.gov/vuln/detail/CVE-2020-36732
|
|
13
|
+
*/
|
|
14
|
+
import type { TSESLint } from '@interlace/eslint-devkit';
|
|
15
|
+
type MessageIds = 'weakRandom' | 'useNativeRandom';
|
|
16
|
+
export interface Options {
|
|
17
|
+
/** Allow in test files. Default: false */
|
|
18
|
+
allowInTests?: boolean;
|
|
19
|
+
}
|
|
20
|
+
type RuleOptions = [Options?];
|
|
21
|
+
export declare const noCryptojsWeakRandom: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
|
|
22
|
+
name: string;
|
|
23
|
+
};
|
|
24
|
+
export type { Options as NoCryptojsWeakRandomOptions };
|
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.noCryptojsWeakRandom = void 0;
|
|
9
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
+
exports.noCryptojsWeakRandom = (0, eslint_devkit_1.createRule)({
|
|
11
|
+
name: 'no-cryptojs-weak-random',
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'problem',
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Disallow crypto-js WordArray.random() (CVE-2020-36732)',
|
|
16
|
+
},
|
|
17
|
+
hasSuggestions: true,
|
|
18
|
+
messages: {
|
|
19
|
+
weakRandom: (0, eslint_devkit_1.formatLLMMessage)({
|
|
20
|
+
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
21
|
+
issueName: 'Weak random in crypto-js',
|
|
22
|
+
cwe: 'CWE-338',
|
|
23
|
+
description: 'CryptoJS.lib.WordArray.random() was insecure in versions < 3.2.1 (CVE-2020-36732). Used Math.random() instead of CSPRNG.',
|
|
24
|
+
severity: 'CRITICAL',
|
|
25
|
+
fix: 'Use crypto.randomBytes() from Node.js or crypto.getRandomValues() in browsers',
|
|
26
|
+
documentationLink: 'https://nvd.nist.gov/vuln/detail/CVE-2020-36732',
|
|
27
|
+
}),
|
|
28
|
+
useNativeRandom: (0, eslint_devkit_1.formatLLMMessage)({
|
|
29
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
30
|
+
issueName: 'Use native randomBytes',
|
|
31
|
+
description: 'Use cryptographically secure random from native crypto',
|
|
32
|
+
severity: 'LOW',
|
|
33
|
+
fix: 'crypto.randomBytes(32)',
|
|
34
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptorandombytessize-callback',
|
|
35
|
+
}),
|
|
36
|
+
},
|
|
37
|
+
schema: [
|
|
38
|
+
{
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
allowInTests: {
|
|
42
|
+
type: 'boolean',
|
|
43
|
+
default: false,
|
|
44
|
+
description: 'Allow in test files',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
defaultOptions: [
|
|
52
|
+
{
|
|
53
|
+
allowInTests: false,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
create(context, [options = {}]) {
|
|
57
|
+
const { allowInTests = false } = options;
|
|
58
|
+
const filename = context.filename;
|
|
59
|
+
const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
|
|
60
|
+
function checkCallExpression(node) {
|
|
61
|
+
if (isTestFile)
|
|
62
|
+
return;
|
|
63
|
+
// Check for CryptoJS.lib.WordArray.random()
|
|
64
|
+
// or WordArray.random()
|
|
65
|
+
if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression) {
|
|
66
|
+
const callee = node.callee;
|
|
67
|
+
// Check for .random() method
|
|
68
|
+
if (callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
69
|
+
callee.property.name === 'random') {
|
|
70
|
+
// Check if it's on WordArray
|
|
71
|
+
if (callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier && callee.object.name === 'WordArray') {
|
|
72
|
+
reportWeakRandom(node);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Check for CryptoJS.lib.WordArray.random()
|
|
76
|
+
if (callee.object.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression) {
|
|
77
|
+
const innerObj = callee.object;
|
|
78
|
+
if (innerObj.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
79
|
+
innerObj.property.name === 'WordArray') {
|
|
80
|
+
reportWeakRandom(node);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Check for CryptoJS.random or similar patterns
|
|
87
|
+
if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
88
|
+
node.callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
89
|
+
node.callee.object.name === 'CryptoJS' &&
|
|
90
|
+
node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
91
|
+
node.callee.property.name === 'random') {
|
|
92
|
+
reportWeakRandom(node);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function reportWeakRandom(node) {
|
|
96
|
+
context.report({
|
|
97
|
+
node,
|
|
98
|
+
messageId: 'weakRandom',
|
|
99
|
+
suggest: [
|
|
100
|
+
{
|
|
101
|
+
messageId: 'useNativeRandom',
|
|
102
|
+
fix: () => null, // Complex refactoring
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
CallExpression: checkCallExpression,
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-node-security/src/rules/no-cryptojs-weak-random/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
3
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
4
|
+
* MIT license that can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
export interface Options {
|
|
7
|
+
tempPaths?: string[];
|
|
8
|
+
ignoreFiles?: string[];
|
|
9
|
+
}
|
|
10
|
+
type RuleOptions = [Options?];
|
|
11
|
+
export declare const noDataInTempStorage: import("@typescript-eslint/utils/ts-eslint").RuleModule<"violationDetected", RuleOptions, unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.noDataInTempStorage = void 0;
|
|
9
|
+
/**
|
|
10
|
+
* @fileoverview Prevent sensitive data in temp directories
|
|
11
|
+
*/
|
|
12
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
13
|
+
const DEFAULT_TEMP_PATHS = ['/tmp', '/var/tmp', 'temp/', '/temp'];
|
|
14
|
+
exports.noDataInTempStorage = (0, eslint_devkit_1.createRule)({
|
|
15
|
+
name: 'no-data-in-temp-storage',
|
|
16
|
+
meta: {
|
|
17
|
+
type: 'problem',
|
|
18
|
+
docs: {
|
|
19
|
+
description: 'Prevent sensitive data in temp directories',
|
|
20
|
+
},
|
|
21
|
+
messages: {
|
|
22
|
+
violationDetected: (0, eslint_devkit_1.formatLLMMessage)({
|
|
23
|
+
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
24
|
+
issueName: 'Temp Storage Data',
|
|
25
|
+
cwe: 'CWE-312',
|
|
26
|
+
description: 'Sensitive data written to temp directory - not secure',
|
|
27
|
+
severity: 'HIGH',
|
|
28
|
+
fix: 'Use secure storage location or encrypt data before writing',
|
|
29
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/312.html',
|
|
30
|
+
})
|
|
31
|
+
},
|
|
32
|
+
schema: [
|
|
33
|
+
{
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
tempPaths: {
|
|
37
|
+
type: 'array',
|
|
38
|
+
items: { type: 'string' },
|
|
39
|
+
description: 'Custom list of temporary paths to flag'
|
|
40
|
+
},
|
|
41
|
+
ignoreFiles: {
|
|
42
|
+
type: 'array',
|
|
43
|
+
items: { type: 'string' },
|
|
44
|
+
description: 'List of files or patterns to ignore'
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
additionalProperties: false
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
defaultOptions: [{}],
|
|
52
|
+
create(context) {
|
|
53
|
+
const options = context.options[0] || {};
|
|
54
|
+
const tempPaths = options.tempPaths || DEFAULT_TEMP_PATHS;
|
|
55
|
+
const ignoreFiles = options.ignoreFiles || [];
|
|
56
|
+
const filename = context.filename;
|
|
57
|
+
if (ignoreFiles.some(pattern => filename.includes(pattern))) {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
function report(node) {
|
|
61
|
+
context.report({ node, messageId: 'violationDetected' });
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
CallExpression(node) {
|
|
65
|
+
// Detect fs.writeFileSync or fs.writeFile with temp path
|
|
66
|
+
if (node.callee.type === 'MemberExpression' &&
|
|
67
|
+
node.callee.object.type === 'Identifier' &&
|
|
68
|
+
node.callee.object.name === 'fs' &&
|
|
69
|
+
node.callee.property.type === 'Identifier' &&
|
|
70
|
+
['writeFileSync', 'writeFile'].includes(node.callee.property.name)) {
|
|
71
|
+
const pathArg = node.arguments[0];
|
|
72
|
+
if (pathArg && pathArg.type === 'Literal' && typeof pathArg.value === 'string') {
|
|
73
|
+
if (tempPaths.some(tp => pathArg.value.includes(tp))) {
|
|
74
|
+
report(pathArg);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
Literal(node) {
|
|
80
|
+
// Detect temp path literals
|
|
81
|
+
if (typeof node.value === 'string') {
|
|
82
|
+
if (tempPaths.some(tp => node.value.includes(tp))) {
|
|
83
|
+
// Only flag if parent is assignment or variable declaration
|
|
84
|
+
const parent = node.parent;
|
|
85
|
+
if (parent?.type === 'VariableDeclarator' || parent?.type === 'AssignmentExpression') {
|
|
86
|
+
// Avoid double reporting if it's already handled by CallExpression
|
|
87
|
+
if (parent.type === 'VariableDeclarator' || parent.type === 'AssignmentExpression') {
|
|
88
|
+
// Check if it's the first argument of an fs call which is handled above
|
|
89
|
+
// Actually the Literal listener here catches it regardless of being in fs call or not if it's in a VariableDeclarator
|
|
90
|
+
}
|
|
91
|
+
report(node);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-node-security/src/rules/no-data-in-temp-storage/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
3
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
4
|
+
* MIT license that can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* ESLint Rule: no-deprecated-cipher-method
|
|
8
|
+
* Detects use of deprecated crypto.createCipher/createDecipher methods
|
|
9
|
+
* CWE-327: These methods don't use IV, making encryption deterministic
|
|
10
|
+
*
|
|
11
|
+
* @see https://nodejs.org/api/crypto.html#cryptocreatecipheralgorithm-password-options
|
|
12
|
+
*/
|
|
13
|
+
import type { TSESLint } from '@interlace/eslint-devkit';
|
|
14
|
+
type MessageIds = 'deprecatedCipherMethod' | 'useCipheriv';
|
|
15
|
+
export interface Options {
|
|
16
|
+
/** Allow deprecated methods in test files. Default: false */
|
|
17
|
+
allowInTests?: boolean;
|
|
18
|
+
}
|
|
19
|
+
type RuleOptions = [Options?];
|
|
20
|
+
export declare const noDeprecatedCipherMethod: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
|
|
21
|
+
name: string;
|
|
22
|
+
};
|
|
23
|
+
export type { Options as NoDeprecatedCipherMethodOptions };
|
|
@@ -0,0 +1,118 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.noDeprecatedCipherMethod = void 0;
|
|
9
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
+
const DEPRECATED_METHODS = new Set(['createCipher', 'createDecipher']);
|
|
11
|
+
exports.noDeprecatedCipherMethod = (0, eslint_devkit_1.createRule)({
|
|
12
|
+
name: 'no-deprecated-cipher-method',
|
|
13
|
+
meta: {
|
|
14
|
+
type: 'problem',
|
|
15
|
+
docs: {
|
|
16
|
+
description: 'Disallow deprecated crypto.createCipher/createDecipher methods (use createCipheriv/createDecipheriv instead)',
|
|
17
|
+
},
|
|
18
|
+
hasSuggestions: true,
|
|
19
|
+
messages: {
|
|
20
|
+
deprecatedCipherMethod: (0, eslint_devkit_1.formatLLMMessage)({
|
|
21
|
+
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
22
|
+
issueName: 'Deprecated cipher method',
|
|
23
|
+
cwe: 'CWE-327',
|
|
24
|
+
description: 'crypto.{{method}}() is deprecated. It derives key from password without salt and uses no IV, making encryption deterministic and vulnerable.',
|
|
25
|
+
severity: 'HIGH',
|
|
26
|
+
fix: 'Use crypto.{{replacement}}() with explicit key and random IV',
|
|
27
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptocreatecipherivalgorithm-key-iv-options',
|
|
28
|
+
}),
|
|
29
|
+
useCipheriv: (0, eslint_devkit_1.formatLLMMessage)({
|
|
30
|
+
icon: eslint_devkit_1.MessageIcons.INFO,
|
|
31
|
+
issueName: 'Use createCipheriv',
|
|
32
|
+
description: 'Use createCipheriv with explicit key derivation and random IV',
|
|
33
|
+
severity: 'LOW',
|
|
34
|
+
fix: 'const key = crypto.scryptSync(password, salt, 32);\nconst iv = crypto.randomBytes(16);\nconst cipher = crypto.createCipheriv(algorithm, key, iv);',
|
|
35
|
+
documentationLink: 'https://nodejs.org/api/crypto.html#cryptocreatecipherivalgorithm-key-iv-options',
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
schema: [
|
|
39
|
+
{
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
allowInTests: {
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
default: false,
|
|
45
|
+
description: 'Allow deprecated methods in test files',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
defaultOptions: [
|
|
53
|
+
{
|
|
54
|
+
allowInTests: false,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
create(context, [options = {}]) {
|
|
58
|
+
const { allowInTests = false } = options;
|
|
59
|
+
const filename = context.filename;
|
|
60
|
+
const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
|
|
61
|
+
function checkCallExpression(node) {
|
|
62
|
+
if (isTestFile)
|
|
63
|
+
return;
|
|
64
|
+
// Check for crypto.createCipher() or crypto.createDecipher()
|
|
65
|
+
if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
66
|
+
node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
67
|
+
DEPRECATED_METHODS.has(node.callee.property.name)) {
|
|
68
|
+
// Capture narrowed type before callback (TypeScript loses narrowing in closures)
|
|
69
|
+
const callee = node.callee;
|
|
70
|
+
const propertyNode = callee.property;
|
|
71
|
+
const methodName = propertyNode.name;
|
|
72
|
+
const replacementName = methodName === 'createCipher' ? 'createCipheriv' : 'createDecipheriv';
|
|
73
|
+
context.report({
|
|
74
|
+
node: propertyNode,
|
|
75
|
+
messageId: 'deprecatedCipherMethod',
|
|
76
|
+
data: {
|
|
77
|
+
method: methodName,
|
|
78
|
+
replacement: replacementName,
|
|
79
|
+
},
|
|
80
|
+
suggest: [
|
|
81
|
+
{
|
|
82
|
+
messageId: 'useCipheriv',
|
|
83
|
+
fix: (fixer) => {
|
|
84
|
+
return fixer.replaceText(propertyNode, replacementName);
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Check for standalone createCipher() / createDecipher()
|
|
91
|
+
if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
92
|
+
DEPRECATED_METHODS.has(node.callee.name)) {
|
|
93
|
+
const methodName = node.callee.name;
|
|
94
|
+
const replacementName = methodName === 'createCipher' ? 'createCipheriv' : 'createDecipheriv';
|
|
95
|
+
context.report({
|
|
96
|
+
node: node.callee,
|
|
97
|
+
messageId: 'deprecatedCipherMethod',
|
|
98
|
+
data: {
|
|
99
|
+
method: methodName,
|
|
100
|
+
replacement: replacementName,
|
|
101
|
+
},
|
|
102
|
+
suggest: [
|
|
103
|
+
{
|
|
104
|
+
messageId: 'useCipheriv',
|
|
105
|
+
fix: (fixer) => {
|
|
106
|
+
return fixer.replaceText(node.callee, replacementName);
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
CallExpression: checkCallExpression,
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-node-security/src/rules/no-deprecated-cipher-method/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
3
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
4
|
+
* MIT license that can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
export interface Options {
|
|
7
|
+
}
|
|
8
|
+
type RuleOptions = [Options?];
|
|
9
|
+
export declare const noDynamicDependencyLoading: import("@typescript-eslint/utils/ts-eslint").RuleModule<"violationDetected", RuleOptions, unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
10
|
+
name: string;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.noDynamicDependencyLoading = void 0;
|
|
9
|
+
/**
|
|
10
|
+
* @fileoverview Prevent dynamic dependency injection
|
|
11
|
+
* @see https://owasp.org/www-project-mobile-top-10/
|
|
12
|
+
* @see https://cwe.mitre.org/data/definitions/494.html
|
|
13
|
+
*/
|
|
14
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
15
|
+
exports.noDynamicDependencyLoading = (0, eslint_devkit_1.createRule)({
|
|
16
|
+
name: 'no-dynamic-dependency-loading',
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'problem',
|
|
19
|
+
docs: {
|
|
20
|
+
description: 'Prevent runtime dependency injection with dynamic paths',
|
|
21
|
+
},
|
|
22
|
+
messages: {
|
|
23
|
+
violationDetected: (0, eslint_devkit_1.formatLLMMessage)({
|
|
24
|
+
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
25
|
+
issueName: 'violation Detected',
|
|
26
|
+
cwe: 'CWE-1104',
|
|
27
|
+
description: 'Dynamic import/require detected - use static imports for security',
|
|
28
|
+
severity: 'HIGH',
|
|
29
|
+
fix: 'Review and apply secure practices',
|
|
30
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/1104.html',
|
|
31
|
+
})
|
|
32
|
+
},
|
|
33
|
+
schema: [],
|
|
34
|
+
},
|
|
35
|
+
defaultOptions: [],
|
|
36
|
+
create(context) {
|
|
37
|
+
return {
|
|
38
|
+
CallExpression(node) {
|
|
39
|
+
// Dynamic require
|
|
40
|
+
if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
41
|
+
node.callee.name === 'require' &&
|
|
42
|
+
node.arguments[0]?.type !== eslint_devkit_1.AST_NODE_TYPES.Literal) {
|
|
43
|
+
context.report({ node, messageId: 'violationDetected' });
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
ImportExpression(node) {
|
|
47
|
+
// Dynamic import()
|
|
48
|
+
if (node.source.type !== 'Literal') {
|
|
49
|
+
context.report({ node, messageId: 'violationDetected' });
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-node-security/src/rules/no-dynamic-dependency-loading/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH;;;;GAIG;AAEH,4DAAsG;AAUzF,QAAA,0BAA0B,GAAG,IAAA,0BAAU,EAA0B;IAC5E,IAAI,EAAE,+BAA+B;IACrC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,yDAAyD;SACvE;QACD,QAAQ,EAAE;YACR,iBAAiB,EAAE,IAAA,gCAAgB,EAAC;gBAClC,IAAI,EAAE,4BAAY,CAAC,QAAQ;gBAC3B,SAAS,EAAE,oBAAoB;gBAC/B,GAAG,EAAE,UAAU;gBACf,WAAW,EAAE,mEAAmE;gBAChF,QAAQ,EAAE,MAAM;gBAChB,GAAG,EAAE,mCAAmC;gBACxC,iBAAiB,EAAE,kDAAkD;aACtE,CAAC;SACH;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,cAAc,CAAC,IAA6B;gBAC1C,kBAAkB;gBAClB,IACE,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,8BAAc,CAAC,UAAU;oBAC9C,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS;oBAC9B,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,8BAAc,CAAC,OAAO,EAClD,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;YAED,gBAAgB,CAAC,IAA+B;gBAC9C,mBAAmB;gBACnB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBACnC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;SACH,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
3
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
4
|
+
* MIT license that can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* ESLint Rule: no-dynamic-require
|
|
8
|
+
* Forbid `require()` calls with expressions (eslint-plugin-import inspired)
|
|
9
|
+
*/
|
|
10
|
+
import type { TSESLint } from '@interlace/eslint-devkit';
|
|
11
|
+
export interface Options {
|
|
12
|
+
/** Allow dynamic requires in specific contexts */
|
|
13
|
+
allowContexts?: ('test' | 'config' | 'build' | 'runtime')[];
|
|
14
|
+
/** Allow specific patterns of dynamic requires */
|
|
15
|
+
allowPatterns?: string[];
|
|
16
|
+
}
|
|
17
|
+
type RuleOptions = [Options?];
|
|
18
|
+
export declare const noDynamicRequire: TSESLint.RuleModule<"dynamicRequire", RuleOptions, unknown, TSESLint.RuleListener> & {
|
|
19
|
+
name: string;
|
|
20
|
+
};
|
|
21
|
+
export {};
|