eslint-plugin-node-security 4.6.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 +1 -0
- package/package.json +5 -8
- package/src/index.d.ts +17 -10
- package/src/index.js +5 -36
- 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 +4 -80
- 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 +0 -13
- 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 -413
- 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-buffer-alloc/index.d.ts +0 -47
- package/src/rules/no-unsafe-buffer-alloc/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
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noDynamicCommandString = void 0;
|
|
4
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
5
|
+
const ARGV_FUNCTIONS = new Set([
|
|
6
|
+
'spawn',
|
|
7
|
+
'spawnSync',
|
|
8
|
+
'execFile',
|
|
9
|
+
'execFileSync',
|
|
10
|
+
'fork',
|
|
11
|
+
]);
|
|
12
|
+
const POSIX_COMMAND_FLAGS = new Set(['-c']);
|
|
13
|
+
const CMD_COMMAND_FLAGS = new Set(['/c', '/C', '/k', '/K']);
|
|
14
|
+
const POWERSHELL_COMMAND_FLAGS = new Set([
|
|
15
|
+
'-Command',
|
|
16
|
+
'-command',
|
|
17
|
+
'-c',
|
|
18
|
+
'-EncodedCommand',
|
|
19
|
+
'-encodedcommand',
|
|
20
|
+
'-e',
|
|
21
|
+
'-ec',
|
|
22
|
+
]);
|
|
23
|
+
const SHELL_COMMAND_FLAGS = {
|
|
24
|
+
sh: POSIX_COMMAND_FLAGS,
|
|
25
|
+
bash: POSIX_COMMAND_FLAGS,
|
|
26
|
+
zsh: POSIX_COMMAND_FLAGS,
|
|
27
|
+
dash: POSIX_COMMAND_FLAGS,
|
|
28
|
+
ksh: POSIX_COMMAND_FLAGS,
|
|
29
|
+
busybox: POSIX_COMMAND_FLAGS,
|
|
30
|
+
cmd: CMD_COMMAND_FLAGS,
|
|
31
|
+
'cmd.exe': CMD_COMMAND_FLAGS,
|
|
32
|
+
powershell: POWERSHELL_COMMAND_FLAGS,
|
|
33
|
+
'powershell.exe': POWERSHELL_COMMAND_FLAGS,
|
|
34
|
+
pwsh: POWERSHELL_COMMAND_FLAGS,
|
|
35
|
+
};
|
|
36
|
+
const COMMAND_RUNNERS = new Set([
|
|
37
|
+
'execaCommand',
|
|
38
|
+
'execaCommandSync',
|
|
39
|
+
'$.raw',
|
|
40
|
+
]);
|
|
41
|
+
function basename(command) {
|
|
42
|
+
const segments = command.split(/[\\/]/);
|
|
43
|
+
return segments[segments.length - 1];
|
|
44
|
+
}
|
|
45
|
+
function isAssembledString(node) {
|
|
46
|
+
if (node.type === eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral) {
|
|
47
|
+
return node.expressions.length > 0;
|
|
48
|
+
}
|
|
49
|
+
if (node.type === eslint_devkit_1.AST_NODE_TYPES.BinaryExpression) {
|
|
50
|
+
return node.operator === '+';
|
|
51
|
+
}
|
|
52
|
+
return (node.type === eslint_devkit_1.AST_NODE_TYPES.Identifier ||
|
|
53
|
+
node.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression ||
|
|
54
|
+
node.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression);
|
|
55
|
+
}
|
|
56
|
+
function calleeName(callee) {
|
|
57
|
+
if (callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier)
|
|
58
|
+
return callee.name;
|
|
59
|
+
if (callee.type !== eslint_devkit_1.AST_NODE_TYPES.MemberExpression)
|
|
60
|
+
return null;
|
|
61
|
+
if (callee.property.type !== eslint_devkit_1.AST_NODE_TYPES.Identifier)
|
|
62
|
+
return null;
|
|
63
|
+
if (callee.computed)
|
|
64
|
+
return null;
|
|
65
|
+
if (callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
66
|
+
callee.object.name === '$') {
|
|
67
|
+
return `$.${callee.property.name}`;
|
|
68
|
+
}
|
|
69
|
+
return callee.property.name;
|
|
70
|
+
}
|
|
71
|
+
exports.noDynamicCommandString = (0, eslint_devkit_1.createRule)({
|
|
72
|
+
name: 'no-dynamic-command-string',
|
|
73
|
+
meta: {
|
|
74
|
+
type: 'problem',
|
|
75
|
+
docs: {
|
|
76
|
+
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-dynamic-command-string.md',
|
|
77
|
+
description: 'Disallow dynamically assembled command strings passed to a shell flag or to a command-runner that does not escape (CWE-77)',
|
|
78
|
+
cwe: 'CWE-77',
|
|
79
|
+
cvss: 9.8,
|
|
80
|
+
confidence: 'high',
|
|
81
|
+
},
|
|
82
|
+
messages: {
|
|
83
|
+
shellFlagInjection: (0, eslint_devkit_1.formatLLMMessage)({
|
|
84
|
+
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
85
|
+
issueName: 'Command Injection Through Shell Flag (CWE-77)',
|
|
86
|
+
cwe: 'CWE-77',
|
|
87
|
+
cvss: 9.8,
|
|
88
|
+
description: '{{fn}}("{{shell}}", ["{{flag}}", …]) hands a dynamically built string to {{shell}}, which parses it as a command line. The argument array looks parameterized but everything after {{flag}} is re-parsed — `;`, `&&`, backticks and `$()` all execute.',
|
|
89
|
+
severity: 'CRITICAL',
|
|
90
|
+
fix: 'Invoke the target program directly with its own argument array — spawn("kill", [String(pid)]) — instead of routing it through a shell.',
|
|
91
|
+
documentationLink: 'https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html',
|
|
92
|
+
}),
|
|
93
|
+
commandStringInterpolation: (0, eslint_devkit_1.formatLLMMessage)({
|
|
94
|
+
icon: eslint_devkit_1.MessageIcons.SECURITY,
|
|
95
|
+
issueName: 'Interpolated Command Line (CWE-77)',
|
|
96
|
+
cwe: 'CWE-77',
|
|
97
|
+
cvss: 9.8,
|
|
98
|
+
description: '{{fn}}() takes a whole command line and does NOT escape interpolated values (unlike the execa/zx tagged-template forms). Any special character in the interpolated value changes which command runs.',
|
|
99
|
+
severity: 'CRITICAL',
|
|
100
|
+
fix: 'Use the tagged-template or array form that escapes for you: execa("git", ["clone", url]) or await $`git clone ${url}`.',
|
|
101
|
+
documentationLink: 'https://cwe.mitre.org/data/definitions/77.html',
|
|
102
|
+
}),
|
|
103
|
+
},
|
|
104
|
+
schema: [
|
|
105
|
+
{
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
extraCommandRunners: {
|
|
109
|
+
type: 'array',
|
|
110
|
+
items: { type: 'string' },
|
|
111
|
+
description: 'Extra functions that accept a full command line without escaping',
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
additionalProperties: false,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
defaultOptions: [{}],
|
|
119
|
+
create(context, [options]) {
|
|
120
|
+
const { extraCommandRunners } = options;
|
|
121
|
+
const runners = new Set([
|
|
122
|
+
...COMMAND_RUNNERS,
|
|
123
|
+
...(extraCommandRunners ?? []),
|
|
124
|
+
]);
|
|
125
|
+
function checkShellFlag(node, fn) {
|
|
126
|
+
const command = node.arguments[0];
|
|
127
|
+
if (!command ||
|
|
128
|
+
command.type !== eslint_devkit_1.AST_NODE_TYPES.Literal ||
|
|
129
|
+
typeof command.value !== 'string') {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const shell = basename(command.value);
|
|
133
|
+
const commandFlags = SHELL_COMMAND_FLAGS[shell.toLowerCase()];
|
|
134
|
+
if (!commandFlags)
|
|
135
|
+
return;
|
|
136
|
+
const argv = node.arguments[1];
|
|
137
|
+
if (!argv || argv.type !== eslint_devkit_1.AST_NODE_TYPES.ArrayExpression)
|
|
138
|
+
return;
|
|
139
|
+
for (let i = 0; i < argv.elements.length - 1; i += 1) {
|
|
140
|
+
const flag = argv.elements[i];
|
|
141
|
+
if (!flag || flag.type !== eslint_devkit_1.AST_NODE_TYPES.Literal)
|
|
142
|
+
continue;
|
|
143
|
+
if (typeof flag.value !== 'string')
|
|
144
|
+
continue;
|
|
145
|
+
if (!commandFlags.has(flag.value))
|
|
146
|
+
continue;
|
|
147
|
+
const commandString = argv.elements[i + 1];
|
|
148
|
+
if (!commandString)
|
|
149
|
+
continue;
|
|
150
|
+
if (!isAssembledString(commandString))
|
|
151
|
+
continue;
|
|
152
|
+
context.report({
|
|
153
|
+
node: commandString,
|
|
154
|
+
messageId: 'shellFlagInjection',
|
|
155
|
+
data: { fn, shell, flag: flag.value },
|
|
156
|
+
});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function checkCommandRunner(node, fn) {
|
|
161
|
+
const commandLine = node.arguments[0];
|
|
162
|
+
if (!commandLine)
|
|
163
|
+
return;
|
|
164
|
+
if (commandLine.type === eslint_devkit_1.AST_NODE_TYPES.Literal)
|
|
165
|
+
return;
|
|
166
|
+
if (!isAssembledString(commandLine))
|
|
167
|
+
return;
|
|
168
|
+
context.report({
|
|
169
|
+
node: commandLine,
|
|
170
|
+
messageId: 'commandStringInterpolation',
|
|
171
|
+
data: { fn },
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
CallExpression(node) {
|
|
176
|
+
const fn = calleeName(node.callee);
|
|
177
|
+
if (!fn)
|
|
178
|
+
return;
|
|
179
|
+
if (ARGV_FUNCTIONS.has(fn)) {
|
|
180
|
+
checkShellFlag(node, fn);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (runners.has(fn)) {
|
|
184
|
+
checkCommandRunner(node, fn);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
TaggedTemplateExpression(node) {
|
|
188
|
+
const fn = calleeName(node.tag);
|
|
189
|
+
if (!fn || !runners.has(fn))
|
|
190
|
+
return;
|
|
191
|
+
if (node.quasi.expressions.length === 0)
|
|
192
|
+
return;
|
|
193
|
+
context.report({
|
|
194
|
+
node: node.quasi,
|
|
195
|
+
messageId: 'commandStringInterpolation',
|
|
196
|
+
data: { fn },
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
});
|
|
@@ -1,16 +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.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
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
15
5
|
exports.noDynamicDependencyLoading = (0, eslint_devkit_1.createRule)({
|
|
16
6
|
name: 'no-dynamic-dependency-loading',
|
|
@@ -39,7 +29,6 @@ exports.noDynamicDependencyLoading = (0, eslint_devkit_1.createRule)({
|
|
|
39
29
|
create(context) {
|
|
40
30
|
return {
|
|
41
31
|
CallExpression(node) {
|
|
42
|
-
// Dynamic require
|
|
43
32
|
if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
44
33
|
node.callee.name === 'require' &&
|
|
45
34
|
node.arguments[0]?.type !== eslint_devkit_1.AST_NODE_TYPES.Literal) {
|
|
@@ -47,7 +36,6 @@ exports.noDynamicDependencyLoading = (0, eslint_devkit_1.createRule)({
|
|
|
47
36
|
}
|
|
48
37
|
},
|
|
49
38
|
ImportExpression(node) {
|
|
50
|
-
// Dynamic import()
|
|
51
39
|
if (node.source.type !== 'Literal') {
|
|
52
40
|
context.report({ node, messageId: 'violationDetected' });
|
|
53
41
|
}
|
|
@@ -55,4 +43,3 @@ exports.noDynamicDependencyLoading = (0, eslint_devkit_1.createRule)({
|
|
|
55
43
|
};
|
|
56
44
|
},
|
|
57
45
|
});
|
|
58
|
-
//# 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.noDynamicRequire = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -75,7 +70,6 @@ exports.noDynamicRequire = (0, eslint_devkit_1.createRule)({
|
|
|
75
70
|
}
|
|
76
71
|
return false;
|
|
77
72
|
}
|
|
78
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
79
73
|
function isStaticLiteral(node) {
|
|
80
74
|
return node.type === 'Literal' && typeof node.value === 'string';
|
|
81
75
|
}
|
|
@@ -88,12 +82,9 @@ exports.noDynamicRequire = (0, eslint_devkit_1.createRule)({
|
|
|
88
82
|
if (isInAllowedContext()) {
|
|
89
83
|
return;
|
|
90
84
|
}
|
|
91
|
-
// Check if it's a static literal
|
|
92
85
|
if (isStaticLiteral(requireArg)) {
|
|
93
|
-
// Static requires are allowed
|
|
94
86
|
return;
|
|
95
87
|
}
|
|
96
|
-
// Report dynamic require
|
|
97
88
|
context.report({
|
|
98
89
|
node: requireArg,
|
|
99
90
|
messageId: 'dynamicRequire',
|
|
@@ -103,4 +94,3 @@ exports.noDynamicRequire = (0, eslint_devkit_1.createRule)({
|
|
|
103
94
|
};
|
|
104
95
|
},
|
|
105
96
|
});
|
|
106
|
-
//# 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.noEcbMode = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -72,7 +67,6 @@ exports.noEcbMode = (0, eslint_devkit_1.createRule)({
|
|
|
72
67
|
if (isTestFile)
|
|
73
68
|
return;
|
|
74
69
|
const cipherMethods = new Set(['createCipher', 'createCipheriv', 'createDecipher', 'createDecipheriv']);
|
|
75
|
-
// Check for crypto.createCipher*() pattern
|
|
76
70
|
const isCipherCall = (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
77
71
|
node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
78
72
|
cipherMethods.has(node.callee.property.name)) ||
|
|
@@ -112,4 +106,3 @@ exports.noEcbMode = (0, eslint_devkit_1.createRule)({
|
|
|
112
106
|
};
|
|
113
107
|
},
|
|
114
108
|
});
|
|
115
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,13 +1,7 @@
|
|
|
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.noInsecureKeyDerivation = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
-
// OWASP 2023 recommendations
|
|
11
5
|
const DEFAULT_MIN_ITERATIONS = 100000;
|
|
12
6
|
exports.noInsecureKeyDerivation = (0, eslint_devkit_1.createRule)({
|
|
13
7
|
name: 'no-insecure-key-derivation',
|
|
@@ -77,15 +71,12 @@ exports.noInsecureKeyDerivation = (0, eslint_devkit_1.createRule)({
|
|
|
77
71
|
create(context, [options = {}]) {
|
|
78
72
|
const { minIterations = DEFAULT_MIN_ITERATIONS } = options;
|
|
79
73
|
function checkCallExpression(node) {
|
|
80
|
-
// Check for crypto.pbkdf2() or crypto.pbkdf2Sync()
|
|
81
74
|
const isPbkdf2Call = (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
82
75
|
node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
83
76
|
(node.callee.property.name === 'pbkdf2' || node.callee.property.name === 'pbkdf2Sync')) ||
|
|
84
77
|
(node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
85
78
|
(node.callee.name === 'pbkdf2' || node.callee.name === 'pbkdf2Sync'));
|
|
86
79
|
if (isPbkdf2Call) {
|
|
87
|
-
// pbkdf2(password, salt, iterations, keylen, digest, callback)
|
|
88
|
-
// iterations is the 3rd argument (index 2)
|
|
89
80
|
const iterationsArg = node.arguments[2];
|
|
90
81
|
if (iterationsArg?.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof iterationsArg.value === 'number') {
|
|
91
82
|
const iterations = iterationsArg.value;
|
|
@@ -116,4 +107,3 @@ exports.noInsecureKeyDerivation = (0, eslint_devkit_1.createRule)({
|
|
|
116
107
|
};
|
|
117
108
|
},
|
|
118
109
|
});
|
|
119
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,13 +1,7 @@
|
|
|
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.noInsecureRsaPadding = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
-
// Constants for PKCS#1 v1.5 padding (insecure)
|
|
11
5
|
const PKCS1_PADDING_NAMES = new Set([
|
|
12
6
|
'RSA_PKCS1_PADDING',
|
|
13
7
|
'constants.RSA_PKCS1_PADDING',
|
|
@@ -70,7 +64,6 @@ exports.noInsecureRsaPadding = (0, eslint_devkit_1.createRule)({
|
|
|
70
64
|
function checkCallExpression(node) {
|
|
71
65
|
if (isTestFile)
|
|
72
66
|
return;
|
|
73
|
-
// Check for privateDecrypt, publicDecrypt, privateEncrypt, publicEncrypt
|
|
74
67
|
const rsaMethods = new Set(['privateDecrypt', 'publicDecrypt', 'privateEncrypt', 'publicEncrypt']);
|
|
75
68
|
const isRsaCall = (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
76
69
|
node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
@@ -79,7 +72,6 @@ exports.noInsecureRsaPadding = (0, eslint_devkit_1.createRule)({
|
|
|
79
72
|
rsaMethods.has(node.callee.name));
|
|
80
73
|
if (isRsaCall && node.arguments.length >= 1) {
|
|
81
74
|
const keyArg = node.arguments[0];
|
|
82
|
-
// Check if first argument is an object with padding property
|
|
83
75
|
if (keyArg.type === eslint_devkit_1.AST_NODE_TYPES.ObjectExpression) {
|
|
84
76
|
for (const prop of keyArg.properties) {
|
|
85
77
|
if (prop.type === eslint_devkit_1.AST_NODE_TYPES.Property &&
|
|
@@ -110,4 +102,3 @@ exports.noInsecureRsaPadding = (0, eslint_devkit_1.createRule)({
|
|
|
110
102
|
};
|
|
111
103
|
},
|
|
112
104
|
});
|
|
113
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,13 +1,7 @@
|
|
|
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.noMathRandomCrypto = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
-
// Variable names that suggest cryptographic usage
|
|
11
5
|
const CRYPTO_VARIABLE_PATTERNS = [
|
|
12
6
|
/token/i,
|
|
13
7
|
/key/i,
|
|
@@ -29,7 +23,6 @@ const CRYPTO_VARIABLE_PATTERNS = [
|
|
|
29
23
|
/code/i,
|
|
30
24
|
/verify/i,
|
|
31
25
|
];
|
|
32
|
-
// Function names that suggest cryptographic usage
|
|
33
26
|
const CRYPTO_FUNCTION_PATTERNS = [
|
|
34
27
|
/generate.*token/i,
|
|
35
28
|
/generate.*key/i,
|
|
@@ -104,10 +97,8 @@ exports.noMathRandomCrypto = (0, eslint_devkit_1.createRule)({
|
|
|
104
97
|
const filename = context.filename;
|
|
105
98
|
const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
|
|
106
99
|
function isCryptoContext(node) {
|
|
107
|
-
// Check variable declaration context
|
|
108
100
|
let current = node.parent;
|
|
109
101
|
while (current) {
|
|
110
|
-
// Check variable names
|
|
111
102
|
if (current.type === eslint_devkit_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
112
103
|
if (current.id.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
|
|
113
104
|
const varName = current.id.name;
|
|
@@ -116,14 +107,12 @@ exports.noMathRandomCrypto = (0, eslint_devkit_1.createRule)({
|
|
|
116
107
|
}
|
|
117
108
|
}
|
|
118
109
|
}
|
|
119
|
-
// Check function names
|
|
120
110
|
if (current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration && current.id) {
|
|
121
111
|
const funcName = current.id.name;
|
|
122
112
|
if (CRYPTO_FUNCTION_PATTERNS.some((p) => p.test(funcName))) {
|
|
123
113
|
return true;
|
|
124
114
|
}
|
|
125
115
|
}
|
|
126
|
-
// Check assignment to crypto-named property
|
|
127
116
|
if (current.type === eslint_devkit_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
128
117
|
if (current.left.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
129
118
|
current.left.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
|
|
@@ -133,7 +122,6 @@ exports.noMathRandomCrypto = (0, eslint_devkit_1.createRule)({
|
|
|
133
122
|
}
|
|
134
123
|
}
|
|
135
124
|
}
|
|
136
|
-
// Check object property
|
|
137
125
|
if (current.type === eslint_devkit_1.AST_NODE_TYPES.Property) {
|
|
138
126
|
if (current.key.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
|
|
139
127
|
const propName = current.key.name;
|
|
@@ -142,7 +130,6 @@ exports.noMathRandomCrypto = (0, eslint_devkit_1.createRule)({
|
|
|
142
130
|
}
|
|
143
131
|
}
|
|
144
132
|
}
|
|
145
|
-
// Check return in crypto-named function
|
|
146
133
|
if (current.type === eslint_devkit_1.AST_NODE_TYPES.ReturnStatement) {
|
|
147
134
|
const func = findContainingFunction(current);
|
|
148
135
|
if (func) {
|
|
@@ -177,13 +164,11 @@ exports.noMathRandomCrypto = (0, eslint_devkit_1.createRule)({
|
|
|
177
164
|
CallExpression(node) {
|
|
178
165
|
if (isTestFile)
|
|
179
166
|
return;
|
|
180
|
-
// Check for Math.random()
|
|
181
167
|
if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
182
168
|
node.callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
183
169
|
node.callee.object.name === 'Math' &&
|
|
184
170
|
node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
185
171
|
node.callee.property.name === 'random') {
|
|
186
|
-
// Check if used in cryptographic context
|
|
187
172
|
if (isCryptoContext(node)) {
|
|
188
173
|
context.report({
|
|
189
174
|
node,
|
|
@@ -191,7 +176,7 @@ exports.noMathRandomCrypto = (0, eslint_devkit_1.createRule)({
|
|
|
191
176
|
suggest: [
|
|
192
177
|
{
|
|
193
178
|
messageId: 'useRandomBytes',
|
|
194
|
-
fix: () => null,
|
|
179
|
+
fix: () => null,
|
|
195
180
|
},
|
|
196
181
|
{
|
|
197
182
|
messageId: 'useRandomUUID',
|
|
@@ -205,4 +190,3 @@ exports.noMathRandomCrypto = (0, eslint_devkit_1.createRule)({
|
|
|
205
190
|
};
|
|
206
191
|
},
|
|
207
192
|
});
|
|
208
|
-
//# 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.noSelfSignedCerts = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -63,7 +58,6 @@ exports.noSelfSignedCerts = (0, eslint_devkit_1.createRule)({
|
|
|
63
58
|
function checkProperty(node) {
|
|
64
59
|
if (isTestFile)
|
|
65
60
|
return;
|
|
66
|
-
// Check for rejectUnauthorized: false
|
|
67
61
|
if (node.key.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
68
62
|
node.key.name === 'rejectUnauthorized' &&
|
|
69
63
|
node.value.type === eslint_devkit_1.AST_NODE_TYPES.Literal &&
|
|
@@ -85,7 +79,6 @@ exports.noSelfSignedCerts = (0, eslint_devkit_1.createRule)({
|
|
|
85
79
|
function checkAssignmentExpression(node) {
|
|
86
80
|
if (isTestFile)
|
|
87
81
|
return;
|
|
88
|
-
// Check for process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
|
89
82
|
if (node.left.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
90
83
|
node.left.object.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
91
84
|
node.left.object.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
@@ -102,7 +95,7 @@ exports.noSelfSignedCerts = (0, eslint_devkit_1.createRule)({
|
|
|
102
95
|
suggest: [
|
|
103
96
|
{
|
|
104
97
|
messageId: 'enableValidation',
|
|
105
|
-
fix: () => null,
|
|
98
|
+
fix: () => null,
|
|
106
99
|
},
|
|
107
100
|
],
|
|
108
101
|
});
|
|
@@ -115,4 +108,3 @@ exports.noSelfSignedCerts = (0, eslint_devkit_1.createRule)({
|
|
|
115
108
|
};
|
|
116
109
|
},
|
|
117
110
|
});
|
|
118
|
-
//# 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.noSha1Hash = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -68,13 +63,11 @@ exports.noSha1Hash = (0, eslint_devkit_1.createRule)({
|
|
|
68
63
|
const { allowInTests = false } = options;
|
|
69
64
|
const filename = context.filename;
|
|
70
65
|
const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
|
|
71
|
-
// Track if sha1 was imported from crypto-hash
|
|
72
66
|
let sha1ImportedFromCryptoHash = false;
|
|
73
67
|
return {
|
|
74
68
|
ImportDeclaration(node) {
|
|
75
69
|
if (isTestFile)
|
|
76
70
|
return;
|
|
77
|
-
// Check for: import { sha1 } from 'crypto-hash'
|
|
78
71
|
if (node.source.value === 'crypto-hash') {
|
|
79
72
|
for (const specifier of node.specifiers) {
|
|
80
73
|
if (specifier.type === eslint_devkit_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
@@ -106,7 +99,6 @@ exports.noSha1Hash = (0, eslint_devkit_1.createRule)({
|
|
|
106
99
|
CallExpression(node) {
|
|
107
100
|
if (isTestFile)
|
|
108
101
|
return;
|
|
109
|
-
// Check for sha1() call if imported from crypto-hash
|
|
110
102
|
if (sha1ImportedFromCryptoHash &&
|
|
111
103
|
node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
|
|
112
104
|
node.callee.name === 'sha1') {
|
|
@@ -127,4 +119,3 @@ exports.noSha1Hash = (0, eslint_devkit_1.createRule)({
|
|
|
127
119
|
};
|
|
128
120
|
},
|
|
129
121
|
});
|
|
130
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,24 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* ESLint Rule: no-shell-injection
|
|
4
|
-
* CWE-78: OS Command Injection
|
|
5
|
-
*
|
|
6
|
-
* Fires when child_process shell-execution functions receive a first
|
|
7
|
-
* argument built via string concatenation or template literal expressions.
|
|
8
|
-
*
|
|
9
|
-
* Detection: structural-api. The rule checks the SHAPE of the first argument,
|
|
10
|
-
* not what value flows into it. exec(`git ${cmd}`) fires regardless of what
|
|
11
|
-
* `cmd` contains — the concatenation itself is the signal.
|
|
12
|
-
*
|
|
13
|
-
* Does NOT fire on:
|
|
14
|
-
* - exec('literal command') — static string, no injection surface
|
|
15
|
-
* - spawn('cmd', [args]) — args array is the safe parameterization form
|
|
16
|
-
* - exec(variable) — indirect; data-flow analysis required, out of scope
|
|
17
|
-
*/
|
|
18
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
3
|
exports.noShellInjection = void 0;
|
|
20
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
21
|
-
/** Shell-execution functions from child_process that run the first arg as a shell command. */
|
|
22
5
|
const SHELL_EXEC_FUNCTIONS = new Set([
|
|
23
6
|
'exec', 'execSync',
|
|
24
7
|
]);
|
|
@@ -64,11 +47,9 @@ exports.noShellInjection = (0, eslint_devkit_1.createRule)({
|
|
|
64
47
|
CallExpression(node) {
|
|
65
48
|
const callee = node.callee;
|
|
66
49
|
let fnName = null;
|
|
67
|
-
// exec('cmd') / execSync('cmd') — bare call
|
|
68
50
|
if (callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
|
|
69
51
|
fnName = callee.name;
|
|
70
52
|
}
|
|
71
|
-
// require('child_process').exec('cmd') — member access
|
|
72
53
|
else if (callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
|
|
73
54
|
callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
|
|
74
55
|
fnName = callee.property.name;
|
|
@@ -85,4 +66,3 @@ exports.noShellInjection = (0, eslint_devkit_1.createRule)({
|
|
|
85
66
|
};
|
|
86
67
|
},
|
|
87
68
|
});
|
|
88
|
-
//# sourceMappingURL=index.js.map
|