eslint-plugin-node-security 4.7.3 → 4.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/package.json +2 -2
  2. package/src/index.js +1 -95
  3. package/src/oxlint.js +1 -3
  4. package/src/rules/detect-child-process/index.js +1 -625
  5. package/src/rules/detect-eval-with-expression/index.js +1 -364
  6. package/src/rules/detect-non-literal-fs-filename/index.js +1 -431
  7. package/src/rules/detect-suspicious-dependencies/index.js +1 -69
  8. package/src/rules/lock-file/index.js +1 -92
  9. package/src/rules/no-arbitrary-file-access/index.js +1 -152
  10. package/src/rules/no-buffer-overread/index.js +1 -543
  11. package/src/rules/no-cryptojs/index.js +1 -99
  12. package/src/rules/no-cryptojs-weak-random/index.js +1 -103
  13. package/src/rules/no-data-in-temp-storage/index.js +1 -85
  14. package/src/rules/no-deprecated-buffer/index.js +1 -84
  15. package/src/rules/no-deprecated-cipher-method/index.js +1 -112
  16. package/src/rules/no-dynamic-algorithm-selection/index.js +1 -72
  17. package/src/rules/no-dynamic-command-string/index.js +1 -201
  18. package/src/rules/no-dynamic-dependency-loading/index.js +1 -45
  19. package/src/rules/no-dynamic-require/index.js +1 -96
  20. package/src/rules/no-ecb-mode/index.js +1 -108
  21. package/src/rules/no-insecure-key-derivation/index.js +1 -109
  22. package/src/rules/no-insecure-rsa-padding/index.js +1 -104
  23. package/src/rules/no-math-random-crypto/index.js +1 -192
  24. package/src/rules/no-self-signed-certs/index.js +1 -110
  25. package/src/rules/no-sha1-hash/index.js +1 -121
  26. package/src/rules/no-shell-injection/index.js +1 -68
  27. package/src/rules/no-ssrf/index.js +1 -221
  28. package/src/rules/no-static-iv/index.js +1 -129
  29. package/src/rules/no-timing-unsafe-compare/index.js +1 -106
  30. package/src/rules/no-toctou-vulnerability/index.js +1 -195
  31. package/src/rules/no-unsafe-buffer-alloc/index.js +1 -87
  32. package/src/rules/no-unsafe-dynamic-require/index.js +1 -93
  33. package/src/rules/no-weak-cipher-algorithm/index.js +1 -174
  34. package/src/rules/no-weak-hash-algorithm/index.js +1 -199
  35. package/src/rules/no-zip-slip/index.js +1 -410
  36. package/src/rules/prefer-native-crypto/index.js +1 -119
  37. package/src/rules/require-dependency-integrity/index.js +1 -62
  38. package/src/rules/require-secure-credential-storage/index.js +1 -45
  39. package/src/rules/require-secure-deletion/index.js +1 -82
  40. package/src/rules/require-storage-encryption/index.js +1 -45
  41. package/src/types/index.js +1 -2
@@ -1,104 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noInsecureRsaPadding = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const PKCS1_PADDING_NAMES = new Set([
6
- 'RSA_PKCS1_PADDING',
7
- 'constants.RSA_PKCS1_PADDING',
8
- 'crypto.constants.RSA_PKCS1_PADDING',
9
- ]);
10
- exports.noInsecureRsaPadding = (0, eslint_devkit_1.createRule)({
11
- name: 'no-insecure-rsa-padding',
12
- meta: {
13
- type: 'problem',
14
- docs: {
15
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-insecure-rsa-padding.md',
16
- description: 'Disallow RSA PKCS#1 v1.5 padding (CVE-2023-46809 Marvin Attack)',
17
- cwe: 'CWE-327',
18
- cvss: 7.5,
19
- },
20
- hasSuggestions: true,
21
- messages: {
22
- insecureRsaPadding: (0, eslint_devkit_1.formatLLMMessage)({
23
- icon: eslint_devkit_1.MessageIcons.SECURITY,
24
- issueName: 'Insecure RSA padding',
25
- cwe: 'CWE-327',
26
- description: 'RSA PKCS#1 v1.5 padding is vulnerable to the Marvin Attack (CVE-2023-46809). Timing side-channels allow attackers to decrypt ciphertexts or forge signatures.',
27
- severity: 'HIGH',
28
- fix: 'Use RSA_PKCS1_OAEP_PADDING instead',
29
- documentationLink: 'https://people.redhat.com/~hkario/marvin/',
30
- }),
31
- useOaep: (0, eslint_devkit_1.formatLLMMessage)({
32
- icon: eslint_devkit_1.MessageIcons.INFO,
33
- issueName: 'Use OAEP padding',
34
- description: 'Use RSA-OAEP which is not vulnerable to padding oracle attacks',
35
- severity: 'LOW',
36
- fix: 'padding: crypto.constants.RSA_PKCS1_OAEP_PADDING',
37
- documentationLink: 'https://nodejs.org/api/crypto.html#cryptopublicdecryptkey-buffer',
38
- }),
39
- },
40
- schema: [
41
- {
42
- type: 'object',
43
- properties: {
44
- allowInTests: {
45
- type: 'boolean',
46
- default: false,
47
- description: 'Allow in test files',
48
- },
49
- },
50
- additionalProperties: false,
51
- },
52
- ],
53
- },
54
- defaultOptions: [
55
- {
56
- allowInTests: false,
57
- },
58
- ],
59
- create(context, [options = {}]) {
60
- const { allowInTests = false } = options;
61
- const filename = context.filename;
62
- const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
63
- const sourceCode = context.sourceCode;
64
- function checkCallExpression(node) {
65
- if (isTestFile)
66
- return;
67
- const rsaMethods = new Set(['privateDecrypt', 'publicDecrypt', 'privateEncrypt', 'publicEncrypt']);
68
- const isRsaCall = (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
69
- node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
70
- rsaMethods.has(node.callee.property.name)) ||
71
- (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
72
- rsaMethods.has(node.callee.name));
73
- if (isRsaCall && node.arguments.length >= 1) {
74
- const keyArg = node.arguments[0];
75
- if (keyArg.type === eslint_devkit_1.AST_NODE_TYPES.ObjectExpression) {
76
- for (const prop of keyArg.properties) {
77
- if (prop.type === eslint_devkit_1.AST_NODE_TYPES.Property &&
78
- prop.key.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
79
- prop.key.name === 'padding') {
80
- const paddingText = sourceCode.getText(prop.value);
81
- if (PKCS1_PADDING_NAMES.has(paddingText) || paddingText.includes('RSA_PKCS1_PADDING')) {
82
- context.report({
83
- node: prop,
84
- messageId: 'insecureRsaPadding',
85
- suggest: [
86
- {
87
- messageId: 'useOaep',
88
- fix: (fixer) => {
89
- return fixer.replaceText(prop.value, 'crypto.constants.RSA_PKCS1_OAEP_PADDING');
90
- },
91
- },
92
- ],
93
- });
94
- }
95
- }
96
- }
97
- }
98
- }
99
- }
100
- return {
101
- CallExpression: checkCallExpression,
102
- };
103
- },
104
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noInsecureRsaPadding=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const PKCS1_PADDING_NAMES=new Set(["RSA_PKCS1_PADDING","constants.RSA_PKCS1_PADDING","crypto.constants.RSA_PKCS1_PADDING"]);exports.noInsecureRsaPadding=(0,eslint_devkit_1.createRule)({name:"no-insecure-rsa-padding",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-insecure-rsa-padding.md",description:"Disallow RSA PKCS#1 v1.5 padding (CVE-2023-46809 Marvin Attack)",cwe:"CWE-327",cvss:7.5},hasSuggestions:true,messages:{insecureRsaPadding:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Insecure RSA padding",cwe:"CWE-327",description:"RSA PKCS#1 v1.5 padding is vulnerable to the Marvin Attack (CVE-2023-46809). Timing side-channels allow attackers to decrypt ciphertexts or forge signatures.",severity:"HIGH",fix:"Use RSA_PKCS1_OAEP_PADDING instead",documentationLink:"https://people.redhat.com/~hkario/marvin/"}),useOaep:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use OAEP padding",description:"Use RSA-OAEP which is not vulnerable to padding oracle attacks",severity:"LOW",fix:"padding: crypto.constants.RSA_PKCS1_OAEP_PADDING",documentationLink:"https://nodejs.org/api/crypto.html#cryptopublicdecryptkey-buffer"})},schema:[{type:"object",properties:{allowInTests:{type:"boolean",default:false,description:"Allow in test files"}},additionalProperties:false}]},defaultOptions:[{allowInTests:false}],create(context,[options={}]){const{allowInTests=false}=options;const filename=context.filename;const isTestFile=allowInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);const sourceCode=context.sourceCode;function checkCallExpression(node){if(isTestFile)return;const rsaMethods=new Set(["privateDecrypt","publicDecrypt","privateEncrypt","publicEncrypt"]);const isRsaCall=node.callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&node.callee.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&rsaMethods.has(node.callee.property.name)||node.callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&rsaMethods.has(node.callee.name);if(isRsaCall&&node.arguments.length>=1){const keyArg=node.arguments[0];if(keyArg.type===eslint_devkit_1.AST_NODE_TYPES.ObjectExpression){for(const prop of keyArg.properties){if(prop.type===eslint_devkit_1.AST_NODE_TYPES.Property&&prop.key.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&prop.key.name==="padding"){const paddingText=sourceCode.getText(prop.value);if(PKCS1_PADDING_NAMES.has(paddingText)||paddingText.includes("RSA_PKCS1_PADDING")){context.report({node:prop,messageId:"insecureRsaPadding",suggest:[{messageId:"useOaep",fix:fixer=>{return fixer.replaceText(prop.value,"crypto.constants.RSA_PKCS1_OAEP_PADDING")}}]})}}}}}}return{CallExpression:checkCallExpression}}});
@@ -1,192 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noMathRandomCrypto = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const CRYPTO_VARIABLE_PATTERNS = [
6
- /token/i,
7
- /key/i,
8
- /secret/i,
9
- /password/i,
10
- /salt/i,
11
- /iv/i,
12
- /nonce/i,
13
- /random/i,
14
- /seed/i,
15
- /hash/i,
16
- /cipher/i,
17
- /encrypt/i,
18
- /auth/i,
19
- /session/i,
20
- /csrf/i,
21
- /otp/i,
22
- /pin/i,
23
- /code/i,
24
- /verify/i,
25
- ];
26
- const CRYPTO_FUNCTION_PATTERNS = [
27
- /generate.*token/i,
28
- /generate.*key/i,
29
- /generate.*id/i,
30
- /create.*secret/i,
31
- /create.*token/i,
32
- /random.*string/i,
33
- /get.*random/i,
34
- /make.*salt/i,
35
- /gen.*password/i,
36
- ];
37
- exports.noMathRandomCrypto = (0, eslint_devkit_1.createRule)({
38
- name: 'no-math-random-crypto',
39
- meta: {
40
- type: 'problem',
41
- docs: {
42
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-math-random-crypto.md',
43
- description: 'Disallow Math.random() for cryptographic purposes',
44
- cwe: 'CWE-338',
45
- cvss: 5.3,
46
- confidence: 'medium',
47
- },
48
- hasSuggestions: true,
49
- messages: {
50
- mathRandomCrypto: (0, eslint_devkit_1.formatLLMMessage)({
51
- icon: eslint_devkit_1.MessageIcons.SECURITY,
52
- issueName: 'Math.random() used for crypto',
53
- cwe: 'CWE-338',
54
- description: 'Math.random() is not cryptographically secure. It uses a PRNG that can be predicted. Never use it for tokens, keys, passwords, or any security-sensitive values.',
55
- severity: 'CRITICAL',
56
- fix: 'Use crypto.randomBytes() or crypto.randomUUID() instead',
57
- documentationLink: 'https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#secure-random-number-generation',
58
- }),
59
- useRandomBytes: (0, eslint_devkit_1.formatLLMMessage)({
60
- icon: eslint_devkit_1.MessageIcons.INFO,
61
- issueName: 'Use randomBytes',
62
- description: 'Use crypto.randomBytes() for cryptographically secure random values',
63
- severity: 'LOW',
64
- fix: 'crypto.randomBytes(32).toString("hex")',
65
- documentationLink: 'https://nodejs.org/api/crypto.html#cryptorandombytessize-callback',
66
- }),
67
- useRandomUUID: (0, eslint_devkit_1.formatLLMMessage)({
68
- icon: eslint_devkit_1.MessageIcons.INFO,
69
- issueName: 'Use randomUUID',
70
- description: 'Use crypto.randomUUID() for UUID generation',
71
- severity: 'LOW',
72
- fix: 'crypto.randomUUID()',
73
- documentationLink: 'https://nodejs.org/api/crypto.html#cryptorandomuuidoptions',
74
- }),
75
- },
76
- schema: [
77
- {
78
- type: 'object',
79
- properties: {
80
- allowInTests: {
81
- type: 'boolean',
82
- default: false,
83
- description: 'Allow Math.random() in test files',
84
- },
85
- },
86
- additionalProperties: false,
87
- },
88
- ],
89
- },
90
- defaultOptions: [
91
- {
92
- allowInTests: false,
93
- },
94
- ],
95
- create(context, [options = {}]) {
96
- const { allowInTests = false } = options;
97
- const filename = context.filename;
98
- const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
99
- function isCryptoContext(node) {
100
- let current = node.parent;
101
- while (current) {
102
- if (current.type === eslint_devkit_1.AST_NODE_TYPES.VariableDeclarator) {
103
- if (current.id.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
104
- const varName = current.id.name;
105
- if (CRYPTO_VARIABLE_PATTERNS.some((p) => p.test(varName))) {
106
- return true;
107
- }
108
- }
109
- }
110
- if (current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration && current.id) {
111
- const funcName = current.id.name;
112
- if (CRYPTO_FUNCTION_PATTERNS.some((p) => p.test(funcName))) {
113
- return true;
114
- }
115
- }
116
- if (current.type === eslint_devkit_1.AST_NODE_TYPES.AssignmentExpression) {
117
- if (current.left.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
118
- current.left.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
119
- const propName = current.left.property.name;
120
- if (CRYPTO_VARIABLE_PATTERNS.some((p) => p.test(propName))) {
121
- return true;
122
- }
123
- }
124
- }
125
- if (current.type === eslint_devkit_1.AST_NODE_TYPES.Property) {
126
- if (current.key.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
127
- const propName = current.key.name;
128
- if (CRYPTO_VARIABLE_PATTERNS.some((p) => p.test(propName))) {
129
- return true;
130
- }
131
- }
132
- }
133
- if (current.type === eslint_devkit_1.AST_NODE_TYPES.ReturnStatement) {
134
- const func = findContainingFunction(current);
135
- if (func) {
136
- if ((func.type === eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration ||
137
- func.type === eslint_devkit_1.AST_NODE_TYPES.FunctionExpression) &&
138
- func.id?.name) {
139
- const funcName = func.id.name;
140
- if (CRYPTO_FUNCTION_PATTERNS.some((p) => p.test(funcName)) ||
141
- CRYPTO_VARIABLE_PATTERNS.some((p) => p.test(funcName))) {
142
- return true;
143
- }
144
- }
145
- }
146
- }
147
- current = current.parent;
148
- }
149
- return false;
150
- }
151
- function findContainingFunction(node) {
152
- let current = node.parent;
153
- while (current) {
154
- if (current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration ||
155
- current.type === eslint_devkit_1.AST_NODE_TYPES.FunctionExpression ||
156
- current.type === eslint_devkit_1.AST_NODE_TYPES.ArrowFunctionExpression) {
157
- return current;
158
- }
159
- current = current.parent;
160
- }
161
- return null;
162
- }
163
- return {
164
- CallExpression(node) {
165
- if (isTestFile)
166
- return;
167
- if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
168
- node.callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
169
- node.callee.object.name === 'Math' &&
170
- node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
171
- node.callee.property.name === 'random') {
172
- if (isCryptoContext(node)) {
173
- context.report({
174
- node,
175
- messageId: 'mathRandomCrypto',
176
- suggest: [
177
- {
178
- messageId: 'useRandomBytes',
179
- fix: () => null,
180
- },
181
- {
182
- messageId: 'useRandomUUID',
183
- fix: () => null,
184
- },
185
- ],
186
- });
187
- }
188
- }
189
- },
190
- };
191
- },
192
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMathRandomCrypto=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const CRYPTO_VARIABLE_PATTERNS=[/token/i,/key/i,/secret/i,/password/i,/salt/i,/iv/i,/nonce/i,/random/i,/seed/i,/hash/i,/cipher/i,/encrypt/i,/auth/i,/session/i,/csrf/i,/otp/i,/pin/i,/code/i,/verify/i];const CRYPTO_FUNCTION_PATTERNS=[/generate.*token/i,/generate.*key/i,/generate.*id/i,/create.*secret/i,/create.*token/i,/random.*string/i,/get.*random/i,/make.*salt/i,/gen.*password/i];exports.noMathRandomCrypto=(0,eslint_devkit_1.createRule)({name:"no-math-random-crypto",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-math-random-crypto.md",description:"Disallow Math.random() for cryptographic purposes",cwe:"CWE-338",cvss:5.3,confidence:"medium"},hasSuggestions:true,messages:{mathRandomCrypto:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Math.random() used for crypto",cwe:"CWE-338",description:"Math.random() is not cryptographically secure. It uses a PRNG that can be predicted. Never use it for tokens, keys, passwords, or any security-sensitive values.",severity:"CRITICAL",fix:"Use crypto.randomBytes() or crypto.randomUUID() instead",documentationLink:"https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#secure-random-number-generation"}),useRandomBytes:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use randomBytes",description:"Use crypto.randomBytes() for cryptographically secure random values",severity:"LOW",fix:'crypto.randomBytes(32).toString("hex")',documentationLink:"https://nodejs.org/api/crypto.html#cryptorandombytessize-callback"}),useRandomUUID:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use randomUUID",description:"Use crypto.randomUUID() for UUID generation",severity:"LOW",fix:"crypto.randomUUID()",documentationLink:"https://nodejs.org/api/crypto.html#cryptorandomuuidoptions"})},schema:[{type:"object",properties:{allowInTests:{type:"boolean",default:false,description:"Allow Math.random() in test files"}},additionalProperties:false}]},defaultOptions:[{allowInTests:false}],create(context,[options={}]){const{allowInTests=false}=options;const filename=context.filename;const isTestFile=allowInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);function isCryptoContext(node){let current=node.parent;while(current){if(current.type===eslint_devkit_1.AST_NODE_TYPES.VariableDeclarator){if(current.id.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){const varName=current.id.name;if(CRYPTO_VARIABLE_PATTERNS.some(p=>p.test(varName))){return true}}}if(current.type===eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration&&current.id){const funcName=current.id.name;if(CRYPTO_FUNCTION_PATTERNS.some(p=>p.test(funcName))){return true}}if(current.type===eslint_devkit_1.AST_NODE_TYPES.AssignmentExpression){if(current.left.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&current.left.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){const propName=current.left.property.name;if(CRYPTO_VARIABLE_PATTERNS.some(p=>p.test(propName))){return true}}}if(current.type===eslint_devkit_1.AST_NODE_TYPES.Property){if(current.key.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){const propName=current.key.name;if(CRYPTO_VARIABLE_PATTERNS.some(p=>p.test(propName))){return true}}}if(current.type===eslint_devkit_1.AST_NODE_TYPES.ReturnStatement){const func=findContainingFunction(current);if(func){if((func.type===eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration||func.type===eslint_devkit_1.AST_NODE_TYPES.FunctionExpression)&&func.id?.name){const funcName=func.id.name;if(CRYPTO_FUNCTION_PATTERNS.some(p=>p.test(funcName))||CRYPTO_VARIABLE_PATTERNS.some(p=>p.test(funcName))){return true}}}}current=current.parent}return false}function findContainingFunction(node){let current=node.parent;while(current){if(current.type===eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration||current.type===eslint_devkit_1.AST_NODE_TYPES.FunctionExpression||current.type===eslint_devkit_1.AST_NODE_TYPES.ArrowFunctionExpression){return current}current=current.parent}return null}return{CallExpression(node){if(isTestFile)return;if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&node.callee.object.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.callee.object.name==="Math"&&node.callee.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.callee.property.name==="random"){if(isCryptoContext(node)){context.report({node,messageId:"mathRandomCrypto",suggest:[{messageId:"useRandomBytes",fix:()=>null},{messageId:"useRandomUUID",fix:()=>null}]})}}}}}});
@@ -1,110 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noSelfSignedCerts = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- exports.noSelfSignedCerts = (0, eslint_devkit_1.createRule)({
6
- name: 'no-self-signed-certs',
7
- meta: {
8
- type: 'problem',
9
- docs: {
10
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-self-signed-certs.md',
11
- description: 'Disallow rejectUnauthorized: false in TLS options',
12
- cwe: 'CWE-295',
13
- cvss: 9.5,
14
- },
15
- hasSuggestions: true,
16
- messages: {
17
- insecureTls: (0, eslint_devkit_1.formatLLMMessage)({
18
- icon: eslint_devkit_1.MessageIcons.SECURITY,
19
- issueName: 'TLS certificate validation disabled',
20
- cwe: 'CWE-295',
21
- description: 'rejectUnauthorized: false disables TLS certificate validation, enabling man-in-the-middle attacks. Any certificate will be accepted, including self-signed and expired ones.',
22
- severity: 'CRITICAL',
23
- fix: 'Remove rejectUnauthorized: false or set it to true',
24
- documentationLink: 'https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html',
25
- }),
26
- enableValidation: (0, eslint_devkit_1.formatLLMMessage)({
27
- icon: eslint_devkit_1.MessageIcons.INFO,
28
- issueName: 'Enable certificate validation',
29
- description: 'Enable proper TLS certificate validation',
30
- severity: 'LOW',
31
- fix: 'rejectUnauthorized: true (or remove the property)',
32
- documentationLink: 'https://nodejs.org/api/tls.html#tlsconnectoptions-callback',
33
- }),
34
- },
35
- schema: [
36
- {
37
- type: 'object',
38
- properties: {
39
- allowInTests: {
40
- type: 'boolean',
41
- default: false,
42
- description: 'Allow in test files',
43
- },
44
- },
45
- additionalProperties: false,
46
- },
47
- ],
48
- },
49
- defaultOptions: [
50
- {
51
- allowInTests: false,
52
- },
53
- ],
54
- create(context, [options = {}]) {
55
- const { allowInTests = false } = options;
56
- const filename = context.filename;
57
- const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
58
- function checkProperty(node) {
59
- if (isTestFile)
60
- return;
61
- if (node.key.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
62
- node.key.name === 'rejectUnauthorized' &&
63
- node.value.type === eslint_devkit_1.AST_NODE_TYPES.Literal &&
64
- node.value.value === false) {
65
- context.report({
66
- node,
67
- messageId: 'insecureTls',
68
- suggest: [
69
- {
70
- messageId: 'enableValidation',
71
- fix: (fixer) => {
72
- return fixer.replaceText(node.value, 'true');
73
- },
74
- },
75
- ],
76
- });
77
- }
78
- }
79
- function checkAssignmentExpression(node) {
80
- if (isTestFile)
81
- return;
82
- if (node.left.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
83
- node.left.object.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
84
- node.left.object.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
85
- node.left.object.object.name === 'process' &&
86
- node.left.object.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
87
- node.left.object.property.name === 'env' &&
88
- node.left.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
89
- node.left.property.name === 'NODE_TLS_REJECT_UNAUTHORIZED') {
90
- if (node.right.type === eslint_devkit_1.AST_NODE_TYPES.Literal &&
91
- (node.right.value === '0' || node.right.value === 0)) {
92
- context.report({
93
- node,
94
- messageId: 'insecureTls',
95
- suggest: [
96
- {
97
- messageId: 'enableValidation',
98
- fix: () => null,
99
- },
100
- ],
101
- });
102
- }
103
- }
104
- }
105
- return {
106
- Property: checkProperty,
107
- AssignmentExpression: checkAssignmentExpression,
108
- };
109
- },
110
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noSelfSignedCerts=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.noSelfSignedCerts=(0,eslint_devkit_1.createRule)({name:"no-self-signed-certs",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-self-signed-certs.md",description:"Disallow rejectUnauthorized: false in TLS options",cwe:"CWE-295",cvss:9.5},hasSuggestions:true,messages:{insecureTls:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"TLS certificate validation disabled",cwe:"CWE-295",description:"rejectUnauthorized: false disables TLS certificate validation, enabling man-in-the-middle attacks. Any certificate will be accepted, including self-signed and expired ones.",severity:"CRITICAL",fix:"Remove rejectUnauthorized: false or set it to true",documentationLink:"https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html"}),enableValidation:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Enable certificate validation",description:"Enable proper TLS certificate validation",severity:"LOW",fix:"rejectUnauthorized: true (or remove the property)",documentationLink:"https://nodejs.org/api/tls.html#tlsconnectoptions-callback"})},schema:[{type:"object",properties:{allowInTests:{type:"boolean",default:false,description:"Allow in test files"}},additionalProperties:false}]},defaultOptions:[{allowInTests:false}],create(context,[options={}]){const{allowInTests=false}=options;const filename=context.filename;const isTestFile=allowInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);function checkProperty(node){if(isTestFile)return;if(node.key.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.key.name==="rejectUnauthorized"&&node.value.type===eslint_devkit_1.AST_NODE_TYPES.Literal&&node.value.value===false){context.report({node,messageId:"insecureTls",suggest:[{messageId:"enableValidation",fix:fixer=>{return fixer.replaceText(node.value,"true")}}]})}}function checkAssignmentExpression(node){if(isTestFile)return;if(node.left.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&node.left.object.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&node.left.object.object.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.left.object.object.name==="process"&&node.left.object.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.left.object.property.name==="env"&&node.left.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.left.property.name==="NODE_TLS_REJECT_UNAUTHORIZED"){if(node.right.type===eslint_devkit_1.AST_NODE_TYPES.Literal&&(node.right.value==="0"||node.right.value===0)){context.report({node,messageId:"insecureTls",suggest:[{messageId:"enableValidation",fix:()=>null}]})}}}return{Property:checkProperty,AssignmentExpression:checkAssignmentExpression}}});
@@ -1,121 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noSha1Hash = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- exports.noSha1Hash = (0, eslint_devkit_1.createRule)({
6
- name: 'no-sha1-hash',
7
- meta: {
8
- type: 'problem',
9
- docs: {
10
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-sha1-hash.md',
11
- description: 'Disallow sha1() from crypto-hash package (use sha256 or sha512)',
12
- cwe: 'CWE-327',
13
- cvss: 7.5,
14
- },
15
- hasSuggestions: true,
16
- messages: {
17
- sha1Deprecated: (0, eslint_devkit_1.formatLLMMessage)({
18
- icon: eslint_devkit_1.MessageIcons.SECURITY,
19
- issueName: 'SHA-1 hash detected',
20
- cwe: 'CWE-327',
21
- description: 'SHA-1 is cryptographically broken and should not be used. The crypto-hash package itself warns against using sha1().',
22
- severity: 'HIGH',
23
- fix: 'Use sha256() or sha512() instead',
24
- documentationLink: 'https://shattered.io/',
25
- }),
26
- useSha256: (0, eslint_devkit_1.formatLLMMessage)({
27
- icon: eslint_devkit_1.MessageIcons.INFO,
28
- issueName: 'Use sha256',
29
- description: 'SHA-256 provides adequate security for most use cases',
30
- severity: 'LOW',
31
- fix: "import { sha256 } from 'crypto-hash'; await sha256(data)",
32
- documentationLink: 'https://www.npmjs.com/package/crypto-hash',
33
- }),
34
- useSha512: (0, eslint_devkit_1.formatLLMMessage)({
35
- icon: eslint_devkit_1.MessageIcons.INFO,
36
- issueName: 'Use sha512',
37
- description: 'SHA-512 provides stronger security with longer hash',
38
- severity: 'LOW',
39
- fix: "import { sha512 } from 'crypto-hash'; await sha512(data)",
40
- documentationLink: 'https://www.npmjs.com/package/crypto-hash',
41
- }),
42
- },
43
- schema: [
44
- {
45
- type: 'object',
46
- properties: {
47
- allowInTests: {
48
- type: 'boolean',
49
- default: false,
50
- description: 'Allow SHA1 in test files',
51
- },
52
- },
53
- additionalProperties: false,
54
- },
55
- ],
56
- },
57
- defaultOptions: [
58
- {
59
- allowInTests: false,
60
- },
61
- ],
62
- create(context, [options = {}]) {
63
- const { allowInTests = false } = options;
64
- const filename = context.filename;
65
- const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
66
- let sha1ImportedFromCryptoHash = false;
67
- return {
68
- ImportDeclaration(node) {
69
- if (isTestFile)
70
- return;
71
- if (node.source.value === 'crypto-hash') {
72
- for (const specifier of node.specifiers) {
73
- if (specifier.type === eslint_devkit_1.AST_NODE_TYPES.ImportSpecifier &&
74
- specifier.imported.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
75
- specifier.imported.name === 'sha1') {
76
- sha1ImportedFromCryptoHash = true;
77
- context.report({
78
- node: specifier,
79
- messageId: 'sha1Deprecated',
80
- suggest: [
81
- {
82
- messageId: 'useSha256',
83
- fix: (fixer) => {
84
- return fixer.replaceText(specifier.imported, 'sha256');
85
- },
86
- },
87
- {
88
- messageId: 'useSha512',
89
- fix: (fixer) => {
90
- return fixer.replaceText(specifier.imported, 'sha512');
91
- },
92
- },
93
- ],
94
- });
95
- }
96
- }
97
- }
98
- },
99
- CallExpression(node) {
100
- if (isTestFile)
101
- return;
102
- if (sha1ImportedFromCryptoHash &&
103
- node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
104
- node.callee.name === 'sha1') {
105
- context.report({
106
- node,
107
- messageId: 'sha1Deprecated',
108
- suggest: [
109
- {
110
- messageId: 'useSha256',
111
- fix: (fixer) => {
112
- return fixer.replaceText(node.callee, 'sha256');
113
- },
114
- },
115
- ],
116
- });
117
- }
118
- },
119
- };
120
- },
121
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noSha1Hash=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.noSha1Hash=(0,eslint_devkit_1.createRule)({name:"no-sha1-hash",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-sha1-hash.md",description:"Disallow sha1() from crypto-hash package (use sha256 or sha512)",cwe:"CWE-327",cvss:7.5},hasSuggestions:true,messages:{sha1Deprecated:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"SHA-1 hash detected",cwe:"CWE-327",description:"SHA-1 is cryptographically broken and should not be used. The crypto-hash package itself warns against using sha1().",severity:"HIGH",fix:"Use sha256() or sha512() instead",documentationLink:"https://shattered.io/"}),useSha256:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use sha256",description:"SHA-256 provides adequate security for most use cases",severity:"LOW",fix:"import { sha256 } from 'crypto-hash'; await sha256(data)",documentationLink:"https://www.npmjs.com/package/crypto-hash"}),useSha512:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use sha512",description:"SHA-512 provides stronger security with longer hash",severity:"LOW",fix:"import { sha512 } from 'crypto-hash'; await sha512(data)",documentationLink:"https://www.npmjs.com/package/crypto-hash"})},schema:[{type:"object",properties:{allowInTests:{type:"boolean",default:false,description:"Allow SHA1 in test files"}},additionalProperties:false}]},defaultOptions:[{allowInTests:false}],create(context,[options={}]){const{allowInTests=false}=options;const filename=context.filename;const isTestFile=allowInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);let sha1ImportedFromCryptoHash=false;return{ImportDeclaration(node){if(isTestFile)return;if(node.source.value==="crypto-hash"){for(const specifier of node.specifiers){if(specifier.type===eslint_devkit_1.AST_NODE_TYPES.ImportSpecifier&&specifier.imported.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&specifier.imported.name==="sha1"){sha1ImportedFromCryptoHash=true;context.report({node:specifier,messageId:"sha1Deprecated",suggest:[{messageId:"useSha256",fix:fixer=>{return fixer.replaceText(specifier.imported,"sha256")}},{messageId:"useSha512",fix:fixer=>{return fixer.replaceText(specifier.imported,"sha512")}}]})}}}},CallExpression(node){if(isTestFile)return;if(sha1ImportedFromCryptoHash&&node.callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&node.callee.name==="sha1"){context.report({node,messageId:"sha1Deprecated",suggest:[{messageId:"useSha256",fix:fixer=>{return fixer.replaceText(node.callee,"sha256")}}]})}}}}});
@@ -1,68 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noShellInjection = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const SHELL_EXEC_FUNCTIONS = new Set([
6
- 'exec', 'execSync',
7
- ]);
8
- function isStringConcatOrTemplate(node) {
9
- if (node.type === eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral && node.expressions.length > 0)
10
- return true;
11
- if (node.type === eslint_devkit_1.AST_NODE_TYPES.BinaryExpression &&
12
- node.operator === '+' &&
13
- (node.left.type === eslint_devkit_1.AST_NODE_TYPES.Literal ||
14
- node.left.type === eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral ||
15
- node.left.type === eslint_devkit_1.AST_NODE_TYPES.BinaryExpression ||
16
- node.right.type === eslint_devkit_1.AST_NODE_TYPES.Literal ||
17
- node.right.type === eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral))
18
- return true;
19
- return false;
20
- }
21
- exports.noShellInjection = (0, eslint_devkit_1.createRule)({
22
- name: 'no-shell-injection',
23
- meta: {
24
- type: 'problem',
25
- docs: {
26
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-shell-injection.md',
27
- description: 'Disallow string concatenation or template expressions in shell command arguments (CWE-78)',
28
- cwe: 'CWE-78',
29
- cvss: 9.8,
30
- },
31
- messages: {
32
- shellInjection: (0, eslint_devkit_1.formatLLMMessage)({
33
- icon: eslint_devkit_1.MessageIcons.SECURITY,
34
- issueName: 'OS Command Injection (CWE-78)',
35
- cwe: 'CWE-78',
36
- description: 'Shell command built via string concatenation or template literal. An attacker who controls any interpolated value can execute arbitrary OS commands.',
37
- severity: 'CRITICAL',
38
- fix: 'Use spawn(cmd, [arg1, arg2]) with separate arguments instead of exec(cmd + args). Never build shell commands via string interpolation.',
39
- documentationLink: 'https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html',
40
- }),
41
- },
42
- schema: [],
43
- },
44
- defaultOptions: [],
45
- create(context) {
46
- return {
47
- CallExpression(node) {
48
- const callee = node.callee;
49
- let fnName = null;
50
- if (callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
51
- fnName = callee.name;
52
- }
53
- else if (callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
54
- callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
55
- fnName = callee.property.name;
56
- }
57
- if (!fnName || !SHELL_EXEC_FUNCTIONS.has(fnName))
58
- return;
59
- const firstArg = node.arguments[0];
60
- if (!firstArg || firstArg.type === eslint_devkit_1.AST_NODE_TYPES.SpreadElement)
61
- return;
62
- if (isStringConcatOrTemplate(firstArg)) {
63
- context.report({ node: firstArg, messageId: 'shellInjection' });
64
- }
65
- },
66
- };
67
- },
68
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noShellInjection=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const SHELL_EXEC_FUNCTIONS=new Set(["exec","execSync"]);function isStringConcatOrTemplate(node){if(node.type===eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral&&node.expressions.length>0)return true;if(node.type===eslint_devkit_1.AST_NODE_TYPES.BinaryExpression&&node.operator==="+"&&(node.left.type===eslint_devkit_1.AST_NODE_TYPES.Literal||node.left.type===eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral||node.left.type===eslint_devkit_1.AST_NODE_TYPES.BinaryExpression||node.right.type===eslint_devkit_1.AST_NODE_TYPES.Literal||node.right.type===eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral))return true;return false}exports.noShellInjection=(0,eslint_devkit_1.createRule)({name:"no-shell-injection",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-shell-injection.md",description:"Disallow string concatenation or template expressions in shell command arguments (CWE-78)",cwe:"CWE-78",cvss:9.8},messages:{shellInjection:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"OS Command Injection (CWE-78)",cwe:"CWE-78",description:"Shell command built via string concatenation or template literal. An attacker who controls any interpolated value can execute arbitrary OS commands.",severity:"CRITICAL",fix:"Use spawn(cmd, [arg1, arg2]) with separate arguments instead of exec(cmd + args). Never build shell commands via string interpolation.",documentationLink:"https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html"})},schema:[]},defaultOptions:[],create(context){return{CallExpression(node){const callee=node.callee;let fnName=null;if(callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){fnName=callee.name}else if(callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&callee.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier){fnName=callee.property.name}if(!fnName||!SHELL_EXEC_FUNCTIONS.has(fnName))return;const firstArg=node.arguments[0];if(!firstArg||firstArg.type===eslint_devkit_1.AST_NODE_TYPES.SpreadElement)return;if(isStringConcatOrTemplate(firstArg)){context.report({node:firstArg,messageId:"shellInjection"})}}}}});