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.
Files changed (102) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +50 -0
  3. package/package.json +79 -0
  4. package/src/index.d.ts +10 -0
  5. package/src/index.js +118 -0
  6. package/src/index.js.map +1 -0
  7. package/src/rules/detect-child-process/index.d.ts +30 -0
  8. package/src/rules/detect-child-process/index.js +535 -0
  9. package/src/rules/detect-child-process/index.js.map +1 -0
  10. package/src/rules/detect-eval-with-expression/index.d.ts +28 -0
  11. package/src/rules/detect-eval-with-expression/index.js +398 -0
  12. package/src/rules/detect-eval-with-expression/index.js.map +1 -0
  13. package/src/rules/detect-non-literal-fs-filename/index.d.ts +26 -0
  14. package/src/rules/detect-non-literal-fs-filename/index.js +460 -0
  15. package/src/rules/detect-non-literal-fs-filename/index.js.map +1 -0
  16. package/src/rules/detect-suspicious-dependencies/index.d.ts +12 -0
  17. package/src/rules/detect-suspicious-dependencies/index.js +77 -0
  18. package/src/rules/detect-suspicious-dependencies/index.js.map +1 -0
  19. package/src/rules/lock-file/index.d.ts +13 -0
  20. package/src/rules/lock-file/index.js +94 -0
  21. package/src/rules/lock-file/index.js.map +1 -0
  22. package/src/rules/no-arbitrary-file-access/index.d.ts +12 -0
  23. package/src/rules/no-arbitrary-file-access/index.js +201 -0
  24. package/src/rules/no-arbitrary-file-access/index.js.map +1 -0
  25. package/src/rules/no-buffer-overread/index.d.ts +39 -0
  26. package/src/rules/no-buffer-overread/index.js +612 -0
  27. package/src/rules/no-buffer-overread/index.js.map +1 -0
  28. package/src/rules/no-cryptojs/index.d.ts +24 -0
  29. package/src/rules/no-cryptojs/index.js +104 -0
  30. package/src/rules/no-cryptojs/index.js.map +1 -0
  31. package/src/rules/no-cryptojs-weak-random/index.d.ts +24 -0
  32. package/src/rules/no-cryptojs-weak-random/index.js +112 -0
  33. package/src/rules/no-cryptojs-weak-random/index.js.map +1 -0
  34. package/src/rules/no-data-in-temp-storage/index.d.ts +14 -0
  35. package/src/rules/no-data-in-temp-storage/index.js +99 -0
  36. package/src/rules/no-data-in-temp-storage/index.js.map +1 -0
  37. package/src/rules/no-deprecated-cipher-method/index.d.ts +23 -0
  38. package/src/rules/no-deprecated-cipher-method/index.js +118 -0
  39. package/src/rules/no-deprecated-cipher-method/index.js.map +1 -0
  40. package/src/rules/no-dynamic-dependency-loading/index.d.ts +12 -0
  41. package/src/rules/no-dynamic-dependency-loading/index.js +55 -0
  42. package/src/rules/no-dynamic-dependency-loading/index.js.map +1 -0
  43. package/src/rules/no-dynamic-require/index.d.ts +21 -0
  44. package/src/rules/no-dynamic-require/index.js +122 -0
  45. package/src/rules/no-dynamic-require/index.js.map +1 -0
  46. package/src/rules/no-ecb-mode/index.d.ts +23 -0
  47. package/src/rules/no-ecb-mode/index.js +113 -0
  48. package/src/rules/no-ecb-mode/index.js.map +1 -0
  49. package/src/rules/no-insecure-key-derivation/index.d.ts +24 -0
  50. package/src/rules/no-insecure-key-derivation/index.js +116 -0
  51. package/src/rules/no-insecure-key-derivation/index.js.map +1 -0
  52. package/src/rules/no-insecure-rsa-padding/index.d.ts +24 -0
  53. package/src/rules/no-insecure-rsa-padding/index.js +110 -0
  54. package/src/rules/no-insecure-rsa-padding/index.js.map +1 -0
  55. package/src/rules/no-pii-in-logs/index.d.ts +12 -0
  56. package/src/rules/no-pii-in-logs/index.js +74 -0
  57. package/src/rules/no-pii-in-logs/index.js.map +1 -0
  58. package/src/rules/no-self-signed-certs/index.d.ts +23 -0
  59. package/src/rules/no-self-signed-certs/index.js +116 -0
  60. package/src/rules/no-self-signed-certs/index.js.map +1 -0
  61. package/src/rules/no-sha1-hash/index.d.ts +24 -0
  62. package/src/rules/no-sha1-hash/index.js +128 -0
  63. package/src/rules/no-sha1-hash/index.js.map +1 -0
  64. package/src/rules/no-static-iv/index.d.ts +23 -0
  65. package/src/rules/no-static-iv/index.js +147 -0
  66. package/src/rules/no-static-iv/index.js.map +1 -0
  67. package/src/rules/no-timing-unsafe-compare/index.d.ts +23 -0
  68. package/src/rules/no-timing-unsafe-compare/index.js +114 -0
  69. package/src/rules/no-timing-unsafe-compare/index.js.map +1 -0
  70. package/src/rules/no-toctou-vulnerability/index.d.ts +26 -0
  71. package/src/rules/no-toctou-vulnerability/index.js +214 -0
  72. package/src/rules/no-toctou-vulnerability/index.js.map +1 -0
  73. package/src/rules/no-unsafe-dynamic-require/index.d.ts +19 -0
  74. package/src/rules/no-unsafe-dynamic-require/index.js +112 -0
  75. package/src/rules/no-unsafe-dynamic-require/index.js.map +1 -0
  76. package/src/rules/no-weak-cipher-algorithm/index.d.ts +25 -0
  77. package/src/rules/no-weak-cipher-algorithm/index.js +190 -0
  78. package/src/rules/no-weak-cipher-algorithm/index.js.map +1 -0
  79. package/src/rules/no-weak-hash-algorithm/index.d.ts +25 -0
  80. package/src/rules/no-weak-hash-algorithm/index.js +218 -0
  81. package/src/rules/no-weak-hash-algorithm/index.js.map +1 -0
  82. package/src/rules/no-zip-slip/index.d.ts +35 -0
  83. package/src/rules/no-zip-slip/index.js +451 -0
  84. package/src/rules/no-zip-slip/index.js.map +1 -0
  85. package/src/rules/prefer-native-crypto/index.d.ts +23 -0
  86. package/src/rules/prefer-native-crypto/index.js +124 -0
  87. package/src/rules/prefer-native-crypto/index.js.map +1 -0
  88. package/src/rules/require-dependency-integrity/index.d.ts +12 -0
  89. package/src/rules/require-dependency-integrity/index.js +70 -0
  90. package/src/rules/require-dependency-integrity/index.js.map +1 -0
  91. package/src/rules/require-secure-credential-storage/index.d.ts +12 -0
  92. package/src/rules/require-secure-credential-storage/index.js +54 -0
  93. package/src/rules/require-secure-credential-storage/index.js.map +1 -0
  94. package/src/rules/require-secure-deletion/index.d.ts +12 -0
  95. package/src/rules/require-secure-deletion/index.js +46 -0
  96. package/src/rules/require-secure-deletion/index.js.map +1 -0
  97. package/src/rules/require-storage-encryption/index.d.ts +12 -0
  98. package/src/rules/require-storage-encryption/index.js +54 -0
  99. package/src/rules/require-storage-encryption/index.js.map +1 -0
  100. package/src/types/index.d.ts +24 -0
  101. package/src/types/index.js +8 -0
  102. package/src/types/index.js.map +1 -0
@@ -0,0 +1,26 @@
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-toctou-vulnerability
8
+ * Detects Time-of-Check-Time-of-Use vulnerabilities
9
+ * CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition
10
+ *
11
+ * @see https://cwe.mitre.org/data/definitions/367.html
12
+ * @see https://owasp.org/www-community/vulnerabilities/TOCTOU_Race_Condition
13
+ */
14
+ import type { TSESLint } from '@interlace/eslint-devkit';
15
+ type MessageIds = 'toctouVulnerability' | 'useAtomicOperations' | 'useFsPromises' | 'addProperLocking';
16
+ export interface Options {
17
+ /** Ignore in test files. Default: true */
18
+ ignoreInTests?: boolean;
19
+ /** File system methods to check. Default: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'] */
20
+ fsMethods?: string[];
21
+ }
22
+ type RuleOptions = [Options?];
23
+ export declare const noToctouVulnerability: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
24
+ name: string;
25
+ };
26
+ export {};
@@ -0,0 +1,214 @@
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.noToctouVulnerability = void 0;
9
+ const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
+ const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
+ exports.noToctouVulnerability = (0, eslint_devkit_2.createRule)({
12
+ name: 'no-toctou-vulnerability',
13
+ meta: {
14
+ type: 'problem',
15
+ docs: {
16
+ description: 'Detects Time-of-Check-Time-of-Use vulnerabilities',
17
+ },
18
+ hasSuggestions: true,
19
+ messages: {
20
+ toctouVulnerability: (0, eslint_devkit_1.formatLLMMessage)({
21
+ icon: eslint_devkit_1.MessageIcons.SECURITY,
22
+ issueName: 'TOCTOU vulnerability',
23
+ cwe: 'CWE-367',
24
+ description: 'Time-of-check Time-of-use race condition detected',
25
+ severity: 'HIGH',
26
+ fix: 'Use atomic operations or fs.promises for file operations',
27
+ documentationLink: 'https://cwe.mitre.org/data/definitions/367.html',
28
+ }),
29
+ useAtomicOperations: (0, eslint_devkit_1.formatLLMMessage)({
30
+ icon: eslint_devkit_1.MessageIcons.INFO,
31
+ issueName: 'Use Atomic Operations',
32
+ description: 'Use atomic file operations',
33
+ severity: 'LOW',
34
+ fix: 'fs.promises.access() then fs.promises.readFile()',
35
+ documentationLink: 'https://nodejs.org/api/fs.html#fspromisesaccesspath-mode',
36
+ }),
37
+ useFsPromises: (0, eslint_devkit_1.formatLLMMessage)({
38
+ icon: eslint_devkit_1.MessageIcons.INFO,
39
+ issueName: 'Use fs.promises',
40
+ description: 'Use fs.promises API',
41
+ severity: 'LOW',
42
+ fix: 'await fs.promises.readFile() instead of sync operations',
43
+ documentationLink: 'https://nodejs.org/api/fs.html#promises-api',
44
+ }),
45
+ addProperLocking: (0, eslint_devkit_1.formatLLMMessage)({
46
+ icon: eslint_devkit_1.MessageIcons.INFO,
47
+ issueName: 'Add File Locking',
48
+ description: 'Add proper locking mechanism',
49
+ severity: 'LOW',
50
+ fix: 'Use proper-lockfile or similar for concurrent access',
51
+ documentationLink: 'https://github.com/moxystudio/node-proper-lockfile',
52
+ }),
53
+ },
54
+ schema: [
55
+ {
56
+ type: 'object',
57
+ properties: {
58
+ ignoreInTests: {
59
+ type: 'boolean',
60
+ default: true,
61
+ },
62
+ fsMethods: {
63
+ type: 'array',
64
+ items: { type: 'string' },
65
+ default: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'],
66
+ },
67
+ },
68
+ additionalProperties: false,
69
+ },
70
+ ],
71
+ },
72
+ defaultOptions: [
73
+ {
74
+ ignoreInTests: true,
75
+ fsMethods: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'],
76
+ },
77
+ ],
78
+ create(context, [options = {}]) {
79
+ const { ignoreInTests = true } = options || {};
80
+ const filename = context.getFilename();
81
+ const isTestFile = ignoreInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
82
+ if (isTestFile) {
83
+ return {};
84
+ }
85
+ const sourceCode = context.sourceCode || context.sourceCode;
86
+ /**
87
+ * Check for TOCTOU patterns
88
+ */
89
+ function checkCallExpression(node) {
90
+ // 1. Identify the file operation (Use)
91
+ let useMethodName = '';
92
+ if (node.callee.type === 'MemberExpression' && node.callee.property.type === 'Identifier') {
93
+ const objectName = node.callee.object.type === 'Identifier' ? node.callee.object.name : '';
94
+ if (objectName === 'fs' || objectName === 'fsPromises') {
95
+ useMethodName = node.callee.property.name;
96
+ }
97
+ }
98
+ else if (node.callee.type === 'Identifier') {
99
+ useMethodName = node.callee.name;
100
+ }
101
+ const riskyUseMethods = ['readFileSync', 'writeFileSync', 'readFile', 'writeFile', 'openSync', 'open', 'unlinkSync', 'unlink'];
102
+ if (!riskyUseMethods.includes(useMethodName)) {
103
+ return;
104
+ }
105
+ const useArg = node.arguments[0];
106
+ if (!useArg)
107
+ return;
108
+ // 2. Walk up to find the condition (Check)
109
+ let current = node.parent;
110
+ while (current) {
111
+ if (current.type === 'IfStatement') {
112
+ // Extract the condition node
113
+ let condition = current.test;
114
+ // Handle negated condition: if (!exists(path)) { create(path) } -> also TOCTOU but different logic?
115
+ // Actually TOCTOU is usually Check(exists) -> Use(read).
116
+ // If (!exists) -> create is Check -> Use.
117
+ // But strict TOCTOU is checking state then acting.
118
+ // If checking for negation
119
+ if (condition.type === 'UnaryExpression' && condition.operator === '!') {
120
+ condition = condition.argument;
121
+ }
122
+ if (condition.type === 'CallExpression') {
123
+ // Check if it's a file check method
124
+ let checkMethodName = '';
125
+ if (condition.callee.type === 'MemberExpression' && condition.callee.property.type === 'Identifier') {
126
+ checkMethodName = condition.callee.property.name;
127
+ }
128
+ else if (condition.callee.type === 'Identifier') {
129
+ checkMethodName = condition.callee.name;
130
+ }
131
+ const checkMethods = ['existsSync', 'statSync', 'accessSync', 'exists', 'stat', 'access'];
132
+ if (checkMethods.includes(checkMethodName)) {
133
+ // Compare arguments
134
+ const checkArg = condition.arguments[0];
135
+ if (checkArg) {
136
+ // Method 1: Identifier match (same variable)
137
+ if (checkArg.type === 'Identifier' && useArg.type === 'Identifier' && checkArg.name === useArg.name) {
138
+ reportToctou(node);
139
+ return;
140
+ }
141
+ // Method 2: Text match (fallback)
142
+ const checkArgText = sourceCode.getText(checkArg).replace(/\s/g, '');
143
+ const useArgText = sourceCode.getText(useArg).replace(/\s/g, '');
144
+ if (checkArgText === useArgText) {
145
+ reportToctou(node);
146
+ return;
147
+ }
148
+ }
149
+ }
150
+ // Handle stats.isFile() / stats.isDirectory() pattern
151
+ if (condition.callee.type === 'MemberExpression' &&
152
+ condition.callee.property.type === 'Identifier' &&
153
+ ['isFile', 'isDirectory'].includes(condition.callee.property.name) &&
154
+ condition.callee.object.type === 'Identifier') {
155
+ const statsVarName = condition.callee.object.name;
156
+ let currentScope = sourceCode.getScope(condition);
157
+ let variable = null;
158
+ while (currentScope) {
159
+ variable = currentScope.variables.find(v => v.name === statsVarName) || null;
160
+ if (variable)
161
+ break;
162
+ currentScope = currentScope.upper;
163
+ }
164
+ if (variable && variable.defs.length > 0) {
165
+ const def = variable.defs[0];
166
+ if (def.type === 'Variable' && def.node.init && def.node.init.type === 'CallExpression') {
167
+ const init = def.node.init;
168
+ if (init.callee.type === 'MemberExpression' &&
169
+ init.callee.property.type === 'Identifier' &&
170
+ ['statSync', 'lstatSync', 'stat', 'lstat'].includes(init.callee.property.name)) {
171
+ const statArg = init.arguments[0];
172
+ if (statArg) {
173
+ const checkArgText = sourceCode.getText(statArg).replace(/\s/g, '');
174
+ const useArgText = sourceCode.getText(useArg).replace(/\s/g, '');
175
+ if (checkArgText === useArgText) {
176
+ reportToctou(node);
177
+ return;
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ current = current.parent;
187
+ }
188
+ }
189
+ function reportToctou(node) {
190
+ context.report({
191
+ node,
192
+ messageId: 'toctouVulnerability',
193
+ suggest: [
194
+ {
195
+ messageId: 'useAtomicOperations',
196
+ fix: () => null,
197
+ },
198
+ {
199
+ messageId: 'useFsPromises',
200
+ fix: () => null,
201
+ },
202
+ {
203
+ messageId: 'addProperLocking',
204
+ fix: () => null,
205
+ },
206
+ ],
207
+ });
208
+ }
209
+ return {
210
+ CallExpression: checkCallExpression,
211
+ };
212
+ },
213
+ });
214
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-node-security/src/rules/no-toctou-vulnerability/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
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-unsafe-dynamic-require
8
+ * Detects dynamic require() calls that could lead to code injection
9
+ */
10
+ import type { TSESLint } from '@interlace/eslint-devkit';
11
+ export interface Options {
12
+ /** Allow dynamic import() expressions. Default: false (stricter) */
13
+ allowDynamicImport?: boolean;
14
+ }
15
+ type RuleOptions = [Options?];
16
+ export declare const noUnsafeDynamicRequire: TSESLint.RuleModule<"unsafeDynamicRequire", RuleOptions, unknown, TSESLint.RuleListener> & {
17
+ name: string;
18
+ };
19
+ export {};
@@ -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.noUnsafeDynamicRequire = void 0;
9
+ const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
+ const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
+ exports.noUnsafeDynamicRequire = (0, eslint_devkit_2.createRule)({
12
+ name: 'no-unsafe-dynamic-require',
13
+ meta: {
14
+ type: 'problem',
15
+ docs: {
16
+ description: 'Prevent unsafe dynamic require() calls that could enable code injection',
17
+ },
18
+ fixable: 'code',
19
+ hasSuggestions: false,
20
+ messages: {
21
+ unsafeDynamicRequire: (0, eslint_devkit_1.formatLLMMessage)({
22
+ icon: eslint_devkit_1.MessageIcons.SECURITY,
23
+ issueName: 'Dynamic require()',
24
+ cwe: 'CWE-95',
25
+ description: 'Dynamic require() detected',
26
+ severity: 'CRITICAL',
27
+ fix: 'Use allowlist: const ALLOWED = ["mod1", "mod2"]; if (!ALLOWED.includes(name)) throw Error("Not allowed")',
28
+ documentationLink: 'https://owasp.org/www-community/attacks/Code_Injection',
29
+ }),
30
+ },
31
+ schema: [
32
+ {
33
+ type: 'object',
34
+ properties: {
35
+ allowDynamicImport: {
36
+ type: 'boolean',
37
+ default: false,
38
+ },
39
+ },
40
+ additionalProperties: false,
41
+ },
42
+ ],
43
+ },
44
+ defaultOptions: [
45
+ {
46
+ allowDynamicImport: false,
47
+ },
48
+ ],
49
+ create(context) {
50
+ /**
51
+ * Track variables that reference require
52
+ * Maps variable name to the node where it was assigned
53
+ */
54
+ const requireVariables = new Set();
55
+ /**
56
+ * Check if a node is a reference to require
57
+ */
58
+ const isRequireReference = (node) => {
59
+ if (node.type === 'Identifier' && node.name === 'require') {
60
+ return true;
61
+ }
62
+ if (node.type === 'Identifier' && requireVariables.has(node.name)) {
63
+ return true;
64
+ }
65
+ return false;
66
+ };
67
+ /**
68
+ * Check if argument is dynamic (not a literal)
69
+ */
70
+ const isDynamicArgument = (arg) => {
71
+ if (arg.type === 'Literal')
72
+ return false;
73
+ if (arg.type === 'TemplateLiteral' && arg.expressions.length === 0)
74
+ return false;
75
+ return true;
76
+ };
77
+ return {
78
+ VariableDeclarator(node) {
79
+ // Track when require is assigned to a variable
80
+ if (node.id.type === 'Identifier' && node.init) {
81
+ if (node.init.type === 'Identifier' && node.init.name === 'require') {
82
+ requireVariables.add(node.id.name);
83
+ }
84
+ }
85
+ },
86
+ CallExpression(node) {
87
+ // Check for require() calls (direct or via variable)
88
+ if (node.callee.type !== 'Identifier') {
89
+ return;
90
+ }
91
+ // Check if callee is require or a variable that references require
92
+ if (!isRequireReference(node.callee)) {
93
+ return;
94
+ }
95
+ // Must have at least one argument
96
+ if (node.arguments.length === 0)
97
+ return;
98
+ const firstArg = node.arguments[0];
99
+ if (firstArg.type === 'SpreadElement')
100
+ return;
101
+ // Check if dynamic
102
+ if (!isDynamicArgument(firstArg))
103
+ return;
104
+ context.report({
105
+ node,
106
+ messageId: 'unsafeDynamicRequire',
107
+ });
108
+ },
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-unsafe-dynamic-require/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
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-weak-cipher-algorithm
8
+ * Detects use of weak cipher algorithms (DES, 3DES, RC4, Blowfish)
9
+ * CWE-327: Use of a Broken or Risky Cryptographic Algorithm
10
+ *
11
+ * @see https://cwe.mitre.org/data/definitions/327.html
12
+ */
13
+ import type { TSESLint } from '@interlace/eslint-devkit';
14
+ type MessageIds = 'weakCipherAlgorithm' | 'useAes256Gcm' | 'useChaCha20';
15
+ export interface Options {
16
+ /** Additional weak ciphers to detect. Default: [] */
17
+ additionalWeakCiphers?: string[];
18
+ /** Allow weak ciphers in test files. Default: false */
19
+ allowInTests?: boolean;
20
+ }
21
+ type RuleOptions = [Options?];
22
+ export declare const noWeakCipherAlgorithm: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
23
+ name: string;
24
+ };
25
+ export type { Options as NoWeakCipherAlgorithmOptions };
@@ -0,0 +1,190 @@
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.noWeakCipherAlgorithm = void 0;
9
+ const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
+ const WEAK_CIPHER_PATTERNS = [
11
+ {
12
+ pattern: /\bdes\b(?!-ede)/i,
13
+ name: 'DES',
14
+ alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
15
+ replacement: 'aes-256-gcm',
16
+ },
17
+ {
18
+ pattern: /\bdes-ede3?\b|\b3des\b|\btripledes\b/i,
19
+ name: '3DES',
20
+ alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
21
+ replacement: 'aes-256-gcm',
22
+ },
23
+ {
24
+ pattern: /\brc4\b|\barc4\b/i,
25
+ name: 'RC4',
26
+ alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
27
+ replacement: 'aes-256-gcm',
28
+ },
29
+ {
30
+ pattern: /\bblowfish\b|\bbf\b/i,
31
+ name: 'Blowfish',
32
+ alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
33
+ replacement: 'aes-256-gcm',
34
+ },
35
+ {
36
+ pattern: /\brc2\b/i,
37
+ name: 'RC2',
38
+ alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
39
+ replacement: 'aes-256-gcm',
40
+ },
41
+ {
42
+ pattern: /\bidea\b/i,
43
+ name: 'IDEA',
44
+ alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
45
+ replacement: 'aes-256-gcm',
46
+ },
47
+ ];
48
+ /**
49
+ * Check if a string contains a weak cipher algorithm
50
+ */
51
+ function findWeakCipher(value, additionalPatterns) {
52
+ // Check standard patterns
53
+ for (const pattern of WEAK_CIPHER_PATTERNS) {
54
+ if (pattern.pattern.test(value)) {
55
+ return pattern;
56
+ }
57
+ }
58
+ // Check additional patterns
59
+ for (const additionalPattern of additionalPatterns) {
60
+ const regex = new RegExp(`\\b${additionalPattern}\\b`, 'i');
61
+ if (regex.test(value)) {
62
+ return {
63
+ pattern: regex,
64
+ name: additionalPattern.toUpperCase(),
65
+ alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
66
+ replacement: 'aes-256-gcm',
67
+ };
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ exports.noWeakCipherAlgorithm = (0, eslint_devkit_1.createRule)({
73
+ name: 'no-weak-cipher-algorithm',
74
+ meta: {
75
+ type: 'problem',
76
+ docs: {
77
+ description: 'Disallow weak cipher algorithms (DES, 3DES, RC4, Blowfish)',
78
+ },
79
+ hasSuggestions: true,
80
+ messages: {
81
+ weakCipherAlgorithm: (0, eslint_devkit_1.formatLLMMessage)({
82
+ icon: eslint_devkit_1.MessageIcons.SECURITY,
83
+ issueName: 'Weak cipher algorithm',
84
+ cwe: 'CWE-327',
85
+ description: 'Use of weak cipher algorithm: {{algorithm}}. {{algorithm}} has known vulnerabilities and should not be used.',
86
+ severity: 'CRITICAL',
87
+ fix: 'Replace with {{replacement}}: crypto.createCipheriv("{{replacement}}", key, iv)',
88
+ documentationLink: 'https://owasp.org/www-community/vulnerabilities/Weak_Cryptography',
89
+ }),
90
+ useAes256Gcm: (0, eslint_devkit_1.formatLLMMessage)({
91
+ icon: eslint_devkit_1.MessageIcons.INFO,
92
+ issueName: 'Use AES-256-GCM',
93
+ description: 'Replace with AES-256-GCM for authenticated encryption',
94
+ severity: 'LOW',
95
+ fix: 'crypto.createCipheriv("aes-256-gcm", key, iv)',
96
+ documentationLink: 'https://nodejs.org/api/crypto.html#cryptocreatecipherivalgorithm-key-iv-options',
97
+ }),
98
+ useChaCha20: (0, eslint_devkit_1.formatLLMMessage)({
99
+ icon: eslint_devkit_1.MessageIcons.INFO,
100
+ issueName: 'Use ChaCha20-Poly1305',
101
+ description: 'Replace with ChaCha20-Poly1305 for modern encryption',
102
+ severity: 'LOW',
103
+ fix: 'crypto.createCipheriv("chacha20-poly1305", key, iv)',
104
+ documentationLink: 'https://nodejs.org/api/crypto.html#cryptocreatecipherivalgorithm-key-iv-options',
105
+ }),
106
+ },
107
+ schema: [
108
+ {
109
+ type: 'object',
110
+ properties: {
111
+ additionalWeakCiphers: {
112
+ type: 'array',
113
+ items: { type: 'string' },
114
+ default: [],
115
+ description: 'Additional weak ciphers to detect',
116
+ },
117
+ allowInTests: {
118
+ type: 'boolean',
119
+ default: false,
120
+ description: 'Allow weak ciphers in test files',
121
+ },
122
+ },
123
+ additionalProperties: false,
124
+ },
125
+ ],
126
+ },
127
+ defaultOptions: [
128
+ {
129
+ additionalWeakCiphers: [],
130
+ allowInTests: false,
131
+ },
132
+ ],
133
+ create(context, [options = {}]) {
134
+ const { additionalWeakCiphers = [], allowInTests = false, } = options;
135
+ const filename = context.filename;
136
+ const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
137
+ /**
138
+ * Check if a call expression uses a weak cipher
139
+ */
140
+ function checkCallExpression(node) {
141
+ if (isTestFile)
142
+ return;
143
+ const cipherMethods = new Set(['createCipher', 'createCipheriv', 'createDecipher', 'createDecipheriv']);
144
+ // Check for crypto.createCipher*() pattern
145
+ if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
146
+ node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
147
+ cipherMethods.has(node.callee.property.name)) {
148
+ checkCipherArgument(node);
149
+ }
150
+ // Check for standalone createCipher*() pattern
151
+ if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
152
+ cipherMethods.has(node.callee.name)) {
153
+ checkCipherArgument(node);
154
+ }
155
+ }
156
+ /**
157
+ * Check the algorithm argument passed to createCipher*
158
+ */
159
+ function checkCipherArgument(node) {
160
+ const firstArg = node.arguments[0];
161
+ if (firstArg?.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof firstArg.value === 'string') {
162
+ const weakPattern = findWeakCipher(firstArg.value, additionalWeakCiphers);
163
+ if (weakPattern) {
164
+ context.report({
165
+ node: firstArg,
166
+ messageId: 'weakCipherAlgorithm',
167
+ data: {
168
+ algorithm: weakPattern.name,
169
+ replacement: weakPattern.replacement,
170
+ },
171
+ suggest: [
172
+ {
173
+ messageId: 'useAes256Gcm',
174
+ fix: (fixer) => fixer.replaceText(firstArg, `"aes-256-gcm"`),
175
+ },
176
+ {
177
+ messageId: 'useChaCha20',
178
+ fix: (fixer) => fixer.replaceText(firstArg, `"chacha20-poly1305"`),
179
+ },
180
+ ],
181
+ });
182
+ }
183
+ }
184
+ }
185
+ return {
186
+ CallExpression: checkCallExpression,
187
+ };
188
+ },
189
+ });
190
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-node-security/src/rules/no-weak-cipher-algorithm/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
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-weak-hash-algorithm
8
+ * Detects use of weak hash algorithms (MD5, SHA1, MD4)
9
+ * CWE-327: Use of a Broken or Risky Cryptographic Algorithm
10
+ *
11
+ * @see https://cwe.mitre.org/data/definitions/327.html
12
+ */
13
+ import type { TSESLint } from '@interlace/eslint-devkit';
14
+ type MessageIds = 'weakHashAlgorithm' | 'useSha256' | 'useSha512' | 'useSha3';
15
+ export interface Options {
16
+ /** Additional weak algorithms to detect. Default: [] */
17
+ additionalWeakAlgorithms?: string[];
18
+ /** Allow weak hashes in test files. Default: false */
19
+ allowInTests?: boolean;
20
+ }
21
+ type RuleOptions = [Options?];
22
+ export declare const noWeakHashAlgorithm: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
23
+ name: string;
24
+ };
25
+ export type { Options as NoWeakHashAlgorithmOptions };