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,195 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noToctouVulnerability = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
6
- exports.noToctouVulnerability = (0, eslint_devkit_2.createRule)({
7
- name: 'no-toctou-vulnerability',
8
- meta: {
9
- type: 'problem',
10
- docs: {
11
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-toctou-vulnerability.md',
12
- description: 'Detects Time-of-Check-Time-of-Use vulnerabilities',
13
- cwe: 'CWE-367',
14
- cvss: 7.5,
15
- },
16
- hasSuggestions: true,
17
- messages: {
18
- toctouVulnerability: (0, eslint_devkit_1.formatLLMMessage)({
19
- icon: eslint_devkit_1.MessageIcons.SECURITY,
20
- issueName: 'TOCTOU vulnerability',
21
- cwe: 'CWE-367',
22
- description: 'Time-of-check Time-of-use race condition detected',
23
- severity: 'HIGH',
24
- fix: 'Use atomic operations or fs.promises for file operations',
25
- documentationLink: 'https://cwe.mitre.org/data/definitions/367.html',
26
- }),
27
- useAtomicOperations: (0, eslint_devkit_1.formatLLMMessage)({
28
- icon: eslint_devkit_1.MessageIcons.INFO,
29
- issueName: 'Use Atomic Operations',
30
- description: 'Use atomic file operations',
31
- severity: 'LOW',
32
- fix: 'fs.promises.access() then fs.promises.readFile()',
33
- documentationLink: 'https://nodejs.org/api/fs.html#fspromisesaccesspath-mode',
34
- }),
35
- useFsPromises: (0, eslint_devkit_1.formatLLMMessage)({
36
- icon: eslint_devkit_1.MessageIcons.INFO,
37
- issueName: 'Use fs.promises',
38
- description: 'Use fs.promises API',
39
- severity: 'LOW',
40
- fix: 'await fs.promises.readFile() instead of sync operations',
41
- documentationLink: 'https://nodejs.org/api/fs.html#promises-api',
42
- }),
43
- addProperLocking: (0, eslint_devkit_1.formatLLMMessage)({
44
- icon: eslint_devkit_1.MessageIcons.INFO,
45
- issueName: 'Add File Locking',
46
- description: 'Add proper locking mechanism',
47
- severity: 'LOW',
48
- fix: 'Use proper-lockfile or similar for concurrent access',
49
- documentationLink: 'https://github.com/moxystudio/node-proper-lockfile',
50
- }),
51
- },
52
- schema: [
53
- {
54
- type: 'object',
55
- properties: {
56
- ignoreInTests: {
57
- type: 'boolean',
58
- default: true,
59
- },
60
- fsMethods: {
61
- type: 'array',
62
- items: { type: 'string' },
63
- default: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'], description: 'Filesystem check calls that create a time-of-check window'
64
- },
65
- },
66
- additionalProperties: false,
67
- },
68
- ],
69
- },
70
- defaultOptions: [
71
- {
72
- ignoreInTests: true,
73
- fsMethods: ['fs.existsSync', 'fs.statSync', 'fs.accessSync'],
74
- },
75
- ],
76
- create(context, [options = {}]) {
77
- const { ignoreInTests = true } = options || {};
78
- const filename = context.filename;
79
- const isTestFile = ignoreInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
80
- if (isTestFile) {
81
- return {};
82
- }
83
- const sourceCode = context.sourceCode;
84
- function checkCallExpression(node) {
85
- let useMethodName = '';
86
- if (node.callee.type === 'MemberExpression' && node.callee.property.type === 'Identifier') {
87
- const objectName = node.callee.object.type === 'Identifier' ? node.callee.object.name : '';
88
- if (objectName === 'fs' || objectName === 'fsPromises') {
89
- useMethodName = node.callee.property.name;
90
- }
91
- }
92
- else if (node.callee.type === 'Identifier') {
93
- useMethodName = node.callee.name;
94
- }
95
- const riskyUseMethods = ['readFileSync', 'writeFileSync', 'readFile', 'writeFile', 'openSync', 'open', 'unlinkSync', 'unlink'];
96
- if (!riskyUseMethods.includes(useMethodName)) {
97
- return;
98
- }
99
- const useArg = node.arguments[0];
100
- if (!useArg)
101
- return;
102
- let current = node.parent;
103
- while (current) {
104
- if (current.type === 'IfStatement') {
105
- let condition = current.test;
106
- if (condition.type === 'UnaryExpression' && condition.operator === '!') {
107
- condition = condition.argument;
108
- }
109
- if (condition.type === 'CallExpression') {
110
- let checkMethodName = '';
111
- if (condition.callee.type === 'MemberExpression' && condition.callee.property.type === 'Identifier') {
112
- checkMethodName = condition.callee.property.name;
113
- }
114
- else if (condition.callee.type === 'Identifier') {
115
- checkMethodName = condition.callee.name;
116
- }
117
- const checkMethods = ['existsSync', 'statSync', 'accessSync', 'exists', 'stat', 'access'];
118
- if (checkMethods.includes(checkMethodName)) {
119
- const checkArg = condition.arguments[0];
120
- if (checkArg) {
121
- if (checkArg.type === 'Identifier' && useArg.type === 'Identifier' && checkArg.name === useArg.name) {
122
- reportToctou(node);
123
- return;
124
- }
125
- const checkArgText = sourceCode.getText(checkArg).replace(/\s/g, '');
126
- const useArgText = sourceCode.getText(useArg).replace(/\s/g, '');
127
- if (checkArgText === useArgText) {
128
- reportToctou(node);
129
- return;
130
- }
131
- }
132
- }
133
- if (condition.callee.type === 'MemberExpression' &&
134
- condition.callee.property.type === 'Identifier' &&
135
- ['isFile', 'isDirectory'].includes(condition.callee.property.name) &&
136
- condition.callee.object.type === 'Identifier') {
137
- const statsVarName = condition.callee.object.name;
138
- let currentScope = sourceCode.getScope(condition);
139
- let variable = null;
140
- while (currentScope) {
141
- variable = currentScope.variables.find(v => v.name === statsVarName) || null;
142
- if (variable)
143
- break;
144
- currentScope = currentScope.upper;
145
- }
146
- if (variable && variable.defs.length > 0) {
147
- const def = variable.defs[0];
148
- if (def.type === 'Variable' && def.node.init && def.node.init.type === 'CallExpression') {
149
- const init = def.node.init;
150
- if (init.callee.type === 'MemberExpression' &&
151
- init.callee.property.type === 'Identifier' &&
152
- ['statSync', 'lstatSync', 'stat', 'lstat'].includes(init.callee.property.name)) {
153
- const statArg = init.arguments[0];
154
- if (statArg) {
155
- const checkArgText = sourceCode.getText(statArg).replace(/\s/g, '');
156
- const useArgText = sourceCode.getText(useArg).replace(/\s/g, '');
157
- if (checkArgText === useArgText) {
158
- reportToctou(node);
159
- return;
160
- }
161
- }
162
- }
163
- }
164
- }
165
- }
166
- }
167
- }
168
- current = current.parent;
169
- }
170
- }
171
- function reportToctou(node) {
172
- context.report({
173
- node,
174
- messageId: 'toctouVulnerability',
175
- suggest: [
176
- {
177
- messageId: 'useAtomicOperations',
178
- fix: () => null,
179
- },
180
- {
181
- messageId: 'useFsPromises',
182
- fix: () => null,
183
- },
184
- {
185
- messageId: 'addProperLocking',
186
- fix: () => null,
187
- },
188
- ],
189
- });
190
- }
191
- return {
192
- CallExpression: checkCallExpression,
193
- };
194
- },
195
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noToctouVulnerability=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noToctouVulnerability=(0,eslint_devkit_2.createRule)({name:"no-toctou-vulnerability",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-toctou-vulnerability.md",description:"Detects Time-of-Check-Time-of-Use vulnerabilities",cwe:"CWE-367",cvss:7.5},hasSuggestions:true,messages:{toctouVulnerability:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"TOCTOU vulnerability",cwe:"CWE-367",description:"Time-of-check Time-of-use race condition detected",severity:"HIGH",fix:"Use atomic operations or fs.promises for file operations",documentationLink:"https://cwe.mitre.org/data/definitions/367.html"}),useAtomicOperations:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Atomic Operations",description:"Use atomic file operations",severity:"LOW",fix:"fs.promises.access() then fs.promises.readFile()",documentationLink:"https://nodejs.org/api/fs.html#fspromisesaccesspath-mode"}),useFsPromises:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use fs.promises",description:"Use fs.promises API",severity:"LOW",fix:"await fs.promises.readFile() instead of sync operations",documentationLink:"https://nodejs.org/api/fs.html#promises-api"}),addProperLocking:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add File Locking",description:"Add proper locking mechanism",severity:"LOW",fix:"Use proper-lockfile or similar for concurrent access",documentationLink:"https://github.com/moxystudio/node-proper-lockfile"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true},fsMethods:{type:"array",items:{type:"string"},default:["fs.existsSync","fs.statSync","fs.accessSync"],description:"Filesystem check calls that create a time-of-check window"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,fsMethods:["fs.existsSync","fs.statSync","fs.accessSync"]}],create(context,[options={}]){const{ignoreInTests=true}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);if(isTestFile){return{}}const sourceCode=context.sourceCode;function checkCallExpression(node){let useMethodName="";if(node.callee.type==="MemberExpression"&&node.callee.property.type==="Identifier"){const objectName=node.callee.object.type==="Identifier"?node.callee.object.name:"";if(objectName==="fs"||objectName==="fsPromises"){useMethodName=node.callee.property.name}}else if(node.callee.type==="Identifier"){useMethodName=node.callee.name}const riskyUseMethods=["readFileSync","writeFileSync","readFile","writeFile","openSync","open","unlinkSync","unlink"];if(!riskyUseMethods.includes(useMethodName)){return}const useArg=node.arguments[0];if(!useArg)return;let current=node.parent;while(current){if(current.type==="IfStatement"){let condition=current.test;if(condition.type==="UnaryExpression"&&condition.operator==="!"){condition=condition.argument}if(condition.type==="CallExpression"){let checkMethodName="";if(condition.callee.type==="MemberExpression"&&condition.callee.property.type==="Identifier"){checkMethodName=condition.callee.property.name}else if(condition.callee.type==="Identifier"){checkMethodName=condition.callee.name}const checkMethods=["existsSync","statSync","accessSync","exists","stat","access"];if(checkMethods.includes(checkMethodName)){const checkArg=condition.arguments[0];if(checkArg){if(checkArg.type==="Identifier"&&useArg.type==="Identifier"&&checkArg.name===useArg.name){reportToctou(node);return}const checkArgText=sourceCode.getText(checkArg).replace(/\s/g,"");const useArgText=sourceCode.getText(useArg).replace(/\s/g,"");if(checkArgText===useArgText){reportToctou(node);return}}}if(condition.callee.type==="MemberExpression"&&condition.callee.property.type==="Identifier"&&["isFile","isDirectory"].includes(condition.callee.property.name)&&condition.callee.object.type==="Identifier"){const statsVarName=condition.callee.object.name;let currentScope=sourceCode.getScope(condition);let variable=null;while(currentScope){variable=currentScope.variables.find(v=>v.name===statsVarName)||null;if(variable)break;currentScope=currentScope.upper}if(variable&&variable.defs.length>0){const def=variable.defs[0];if(def.type==="Variable"&&def.node.init&&def.node.init.type==="CallExpression"){const init=def.node.init;if(init.callee.type==="MemberExpression"&&init.callee.property.type==="Identifier"&&["statSync","lstatSync","stat","lstat"].includes(init.callee.property.name)){const statArg=init.arguments[0];if(statArg){const checkArgText=sourceCode.getText(statArg).replace(/\s/g,"");const useArgText=sourceCode.getText(useArg).replace(/\s/g,"");if(checkArgText===useArgText){reportToctou(node);return}}}}}}}}current=current.parent}}function reportToctou(node){context.report({node,messageId:"toctouVulnerability",suggest:[{messageId:"useAtomicOperations",fix:()=>null},{messageId:"useFsPromises",fix:()=>null},{messageId:"addProperLocking",fix:()=>null}]})}return{CallExpression:checkCallExpression}}});
@@ -1,87 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noUnsafeBufferAlloc = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const UNSAFE_ALLOCATORS = new Set(['allocUnsafe', 'allocUnsafeSlow']);
6
- exports.noUnsafeBufferAlloc = (0, eslint_devkit_1.createRule)({
7
- name: 'no-unsafe-buffer-alloc',
8
- meta: {
9
- type: 'problem',
10
- hasSuggestions: true,
11
- docs: {
12
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-unsafe-buffer-alloc.md',
13
- description: 'Disallow `Buffer.allocUnsafe()` and `Buffer.allocUnsafeSlow()`, which return uninitialized memory',
14
- cwe: 'CWE-908',
15
- cweJustification: 'CWE-908 (Use of Uninitialized Resource) — allocUnsafe returns a view over non-zeroed heap memory; any byte not overwritten before the buffer is read or transmitted discloses prior process memory.',
16
- cvss: 7.5,
17
- confidence: 'high',
18
- },
19
- messages: {
20
- unsafeAlloc: (0, eslint_devkit_1.formatLLMMessage)({
21
- icon: eslint_devkit_1.MessageIcons.SECURITY,
22
- issueName: 'Uninitialized Buffer Allocation',
23
- cwe: 'CWE-908',
24
- cvss: 7.5,
25
- description: '`Buffer.allocUnsafe(size)` returns memory that has not been zeroed. Every byte not overwritten before the buffer is read or sent leaks whatever the allocator previously stored there.',
26
- severity: 'HIGH',
27
- fix: 'Use `Buffer.alloc(size)` (zero-filled), or keep `allocUnsafe` only where the very next statement overwrites the whole buffer — `Buffer.allocUnsafe(size).fill(0)` is accepted by this rule.',
28
- documentationLink: 'https://nodejs.org/api/buffer.html#static-method-bufferallocunsafesize',
29
- }),
30
- unsafeAllocSlow: (0, eslint_devkit_1.formatLLMMessage)({
31
- icon: eslint_devkit_1.MessageIcons.SECURITY,
32
- issueName: 'Uninitialized Buffer Allocation (allocUnsafeSlow)',
33
- cwe: 'CWE-908',
34
- cvss: 7.5,
35
- description: '`Buffer.allocUnsafeSlow(size)` allocates outside the shared pool but is equally uninitialized — the returned bytes are whatever was last in that memory.',
36
- severity: 'HIGH',
37
- fix: 'Use `Buffer.alloc(size)`, or append `.fill(0)` to zero the allocation at the call site.',
38
- documentationLink: 'https://nodejs.org/api/buffer.html#static-method-bufferallocunsafeslowsize',
39
- }),
40
- useSafeAlloc: 'Replace with `Buffer.alloc()` (zero-filled).',
41
- },
42
- schema: [],
43
- },
44
- defaultOptions: [],
45
- create(context) {
46
- function isFilledInPlace(node) {
47
- const parent = node.parent;
48
- if (parent?.type !== eslint_devkit_1.AST_NODE_TYPES.MemberExpression ||
49
- parent.object !== node ||
50
- parent.computed ||
51
- parent.property.type !== eslint_devkit_1.AST_NODE_TYPES.Identifier ||
52
- parent.property.name !== 'fill') {
53
- return false;
54
- }
55
- const grandparent = parent.parent;
56
- return (grandparent?.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
57
- grandparent.callee === parent);
58
- }
59
- return {
60
- CallExpression(node) {
61
- const callee = node.callee;
62
- if (callee.type !== eslint_devkit_1.AST_NODE_TYPES.MemberExpression ||
63
- callee.computed ||
64
- callee.object.type !== eslint_devkit_1.AST_NODE_TYPES.Identifier ||
65
- callee.object.name !== 'Buffer' ||
66
- callee.property.type !== eslint_devkit_1.AST_NODE_TYPES.Identifier ||
67
- !UNSAFE_ALLOCATORS.has(callee.property.name)) {
68
- return;
69
- }
70
- if (isFilledInPlace(node))
71
- return;
72
- context.report({
73
- node,
74
- messageId: callee.property.name === 'allocUnsafe'
75
- ? 'unsafeAlloc'
76
- : 'unsafeAllocSlow',
77
- suggest: [
78
- {
79
- messageId: 'useSafeAlloc',
80
- fix: (fixer) => fixer.replaceText(callee.property, 'alloc'),
81
- },
82
- ],
83
- });
84
- },
85
- };
86
- },
87
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUnsafeBufferAlloc=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const UNSAFE_ALLOCATORS=new Set(["allocUnsafe","allocUnsafeSlow"]);exports.noUnsafeBufferAlloc=(0,eslint_devkit_1.createRule)({name:"no-unsafe-buffer-alloc",meta:{type:"problem",hasSuggestions:true,docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-unsafe-buffer-alloc.md",description:"Disallow `Buffer.allocUnsafe()` and `Buffer.allocUnsafeSlow()`, which return uninitialized memory",cwe:"CWE-908",cweJustification:"CWE-908 (Use of Uninitialized Resource) \u2014 allocUnsafe returns a view over non-zeroed heap memory; any byte not overwritten before the buffer is read or transmitted discloses prior process memory.",cvss:7.5,confidence:"high"},messages:{unsafeAlloc:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Uninitialized Buffer Allocation",cwe:"CWE-908",cvss:7.5,description:"`Buffer.allocUnsafe(size)` returns memory that has not been zeroed. Every byte not overwritten before the buffer is read or sent leaks whatever the allocator previously stored there.",severity:"HIGH",fix:"Use `Buffer.alloc(size)` (zero-filled), or keep `allocUnsafe` only where the very next statement overwrites the whole buffer \u2014 `Buffer.allocUnsafe(size).fill(0)` is accepted by this rule.",documentationLink:"https://nodejs.org/api/buffer.html#static-method-bufferallocunsafesize"}),unsafeAllocSlow:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Uninitialized Buffer Allocation (allocUnsafeSlow)",cwe:"CWE-908",cvss:7.5,description:"`Buffer.allocUnsafeSlow(size)` allocates outside the shared pool but is equally uninitialized \u2014 the returned bytes are whatever was last in that memory.",severity:"HIGH",fix:"Use `Buffer.alloc(size)`, or append `.fill(0)` to zero the allocation at the call site.",documentationLink:"https://nodejs.org/api/buffer.html#static-method-bufferallocunsafeslowsize"}),useSafeAlloc:"Replace with `Buffer.alloc()` (zero-filled)."},schema:[]},defaultOptions:[],create(context){function isFilledInPlace(node){const parent=node.parent;if(parent?.type!==eslint_devkit_1.AST_NODE_TYPES.MemberExpression||parent.object!==node||parent.computed||parent.property.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier||parent.property.name!=="fill"){return false}const grandparent=parent.parent;return grandparent?.type===eslint_devkit_1.AST_NODE_TYPES.CallExpression&&grandparent.callee===parent}return{CallExpression(node){const callee=node.callee;if(callee.type!==eslint_devkit_1.AST_NODE_TYPES.MemberExpression||callee.computed||callee.object.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier||callee.object.name!=="Buffer"||callee.property.type!==eslint_devkit_1.AST_NODE_TYPES.Identifier||!UNSAFE_ALLOCATORS.has(callee.property.name)){return}if(isFilledInPlace(node))return;context.report({node,messageId:callee.property.name==="allocUnsafe"?"unsafeAlloc":"unsafeAllocSlow",suggest:[{messageId:"useSafeAlloc",fix:fixer=>fixer.replaceText(callee.property,"alloc")}]})}}}});
@@ -1,93 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noUnsafeDynamicRequire = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
6
- exports.noUnsafeDynamicRequire = (0, eslint_devkit_2.createRule)({
7
- name: 'no-unsafe-dynamic-require',
8
- meta: {
9
- type: 'problem',
10
- docs: {
11
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-unsafe-dynamic-require.md',
12
- description: 'Prevent unsafe dynamic require() calls that could enable code injection',
13
- cwe: 'CWE-95',
14
- cvss: 9.8,
15
- },
16
- hasSuggestions: false,
17
- messages: {
18
- unsafeDynamicRequire: (0, eslint_devkit_1.formatLLMMessage)({
19
- icon: eslint_devkit_1.MessageIcons.SECURITY,
20
- issueName: 'Dynamic require()',
21
- cwe: 'CWE-95',
22
- description: 'Dynamic require() detected',
23
- severity: 'CRITICAL',
24
- fix: 'Use allowlist: const ALLOWED = ["mod1", "mod2"]; if (!ALLOWED.includes(name)) throw Error("Not allowed")',
25
- documentationLink: 'https://owasp.org/www-community/attacks/Code_Injection',
26
- }),
27
- },
28
- schema: [
29
- {
30
- type: 'object',
31
- properties: {
32
- allowDynamicImport: {
33
- type: 'boolean',
34
- default: false,
35
- },
36
- },
37
- additionalProperties: false,
38
- },
39
- ],
40
- },
41
- defaultOptions: [
42
- {
43
- allowDynamicImport: false,
44
- },
45
- ],
46
- create(context) {
47
- const requireVariables = new Set();
48
- const isRequireReference = (node) => {
49
- if (node.type === 'Identifier' && node.name === 'require') {
50
- return true;
51
- }
52
- if (node.type === 'Identifier' && requireVariables.has(node.name)) {
53
- return true;
54
- }
55
- return false;
56
- };
57
- const isDynamicArgument = (arg) => {
58
- if (arg.type === 'Literal')
59
- return false;
60
- if (arg.type === 'TemplateLiteral' && arg.expressions.length === 0)
61
- return false;
62
- return true;
63
- };
64
- return {
65
- VariableDeclarator(node) {
66
- if (node.id.type === 'Identifier' && node.init) {
67
- if (node.init.type === 'Identifier' && node.init.name === 'require') {
68
- requireVariables.add(node.id.name);
69
- }
70
- }
71
- },
72
- CallExpression(node) {
73
- if (node.callee.type !== 'Identifier') {
74
- return;
75
- }
76
- if (!isRequireReference(node.callee)) {
77
- return;
78
- }
79
- if (node.arguments.length === 0)
80
- return;
81
- const firstArg = node.arguments[0];
82
- if (firstArg.type === 'SpreadElement')
83
- return;
84
- if (!isDynamicArgument(firstArg))
85
- return;
86
- context.report({
87
- node,
88
- messageId: 'unsafeDynamicRequire',
89
- });
90
- },
91
- };
92
- },
93
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUnsafeDynamicRequire=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noUnsafeDynamicRequire=(0,eslint_devkit_2.createRule)({name:"no-unsafe-dynamic-require",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-unsafe-dynamic-require.md",description:"Prevent unsafe dynamic require() calls that could enable code injection",cwe:"CWE-95",cvss:9.8},hasSuggestions:false,messages:{unsafeDynamicRequire:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Dynamic require()",cwe:"CWE-95",description:"Dynamic require() detected",severity:"CRITICAL",fix:'Use allowlist: const ALLOWED = ["mod1", "mod2"]; if (!ALLOWED.includes(name)) throw Error("Not allowed")',documentationLink:"https://owasp.org/www-community/attacks/Code_Injection"})},schema:[{type:"object",properties:{allowDynamicImport:{type:"boolean",default:false}},additionalProperties:false}]},defaultOptions:[{allowDynamicImport:false}],create(context){const requireVariables=new Set;const isRequireReference=node=>{if(node.type==="Identifier"&&node.name==="require"){return true}if(node.type==="Identifier"&&requireVariables.has(node.name)){return true}return false};const isDynamicArgument=arg=>{if(arg.type==="Literal")return false;if(arg.type==="TemplateLiteral"&&arg.expressions.length===0)return false;return true};return{VariableDeclarator(node){if(node.id.type==="Identifier"&&node.init){if(node.init.type==="Identifier"&&node.init.name==="require"){requireVariables.add(node.id.name)}}},CallExpression(node){if(node.callee.type!=="Identifier"){return}if(!isRequireReference(node.callee)){return}if(node.arguments.length===0)return;const firstArg=node.arguments[0];if(firstArg.type==="SpreadElement")return;if(!isDynamicArgument(firstArg))return;context.report({node,messageId:"unsafeDynamicRequire"})}}}});
@@ -1,174 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noWeakCipherAlgorithm = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const WEAK_CIPHER_PATTERNS = [
6
- {
7
- pattern: /\bdes\b(?!-ede)/i,
8
- name: 'DES',
9
- alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
10
- replacement: 'aes-256-gcm',
11
- },
12
- {
13
- pattern: /\bdes-ede3?\b|\b3des\b|\btripledes\b/i,
14
- name: '3DES',
15
- alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
16
- replacement: 'aes-256-gcm',
17
- },
18
- {
19
- pattern: /\brc4\b|\barc4\b/i,
20
- name: 'RC4',
21
- alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
22
- replacement: 'aes-256-gcm',
23
- },
24
- {
25
- pattern: /\bblowfish\b|\bbf\b/i,
26
- name: 'Blowfish',
27
- alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
28
- replacement: 'aes-256-gcm',
29
- },
30
- {
31
- pattern: /\brc2\b/i,
32
- name: 'RC2',
33
- alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
34
- replacement: 'aes-256-gcm',
35
- },
36
- {
37
- pattern: /\bidea\b/i,
38
- name: 'IDEA',
39
- alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
40
- replacement: 'aes-256-gcm',
41
- },
42
- ];
43
- function findWeakCipher(value, additionalPatterns) {
44
- for (const pattern of WEAK_CIPHER_PATTERNS) {
45
- if (pattern.pattern.test(value)) {
46
- return pattern;
47
- }
48
- }
49
- for (const additionalPattern of additionalPatterns) {
50
- const regex = new RegExp(`\\b${additionalPattern}\\b`, 'i');
51
- if (regex.test(value)) {
52
- return {
53
- pattern: regex,
54
- name: additionalPattern.toUpperCase(),
55
- alternatives: ['AES-256-GCM', 'ChaCha20-Poly1305'],
56
- replacement: 'aes-256-gcm',
57
- };
58
- }
59
- }
60
- return null;
61
- }
62
- exports.noWeakCipherAlgorithm = (0, eslint_devkit_1.createRule)({
63
- name: 'no-weak-cipher-algorithm',
64
- meta: {
65
- type: 'problem',
66
- docs: {
67
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-weak-cipher-algorithm.md',
68
- description: 'Disallow weak cipher algorithms (DES, 3DES, RC4, Blowfish)',
69
- cwe: 'CWE-327',
70
- cvss: 7.5,
71
- },
72
- hasSuggestions: true,
73
- messages: {
74
- weakCipherAlgorithm: (0, eslint_devkit_1.formatLLMMessage)({
75
- icon: eslint_devkit_1.MessageIcons.SECURITY,
76
- issueName: 'Weak cipher algorithm',
77
- cwe: 'CWE-327',
78
- description: 'Use of weak cipher algorithm: {{algorithm}}. {{algorithm}} has known vulnerabilities and should not be used.',
79
- severity: 'CRITICAL',
80
- fix: 'Replace with {{replacement}}: crypto.createCipheriv("{{replacement}}", key, iv)',
81
- documentationLink: 'https://owasp.org/www-community/vulnerabilities/Weak_Cryptography',
82
- }),
83
- useAes256Gcm: (0, eslint_devkit_1.formatLLMMessage)({
84
- icon: eslint_devkit_1.MessageIcons.INFO,
85
- issueName: 'Use AES-256-GCM',
86
- description: 'Replace with AES-256-GCM for authenticated encryption',
87
- severity: 'LOW',
88
- fix: 'crypto.createCipheriv("aes-256-gcm", key, iv)',
89
- documentationLink: 'https://nodejs.org/api/crypto.html#cryptocreatecipherivalgorithm-key-iv-options',
90
- }),
91
- useChaCha20: (0, eslint_devkit_1.formatLLMMessage)({
92
- icon: eslint_devkit_1.MessageIcons.INFO,
93
- issueName: 'Use ChaCha20-Poly1305',
94
- description: 'Replace with ChaCha20-Poly1305 for modern encryption',
95
- severity: 'LOW',
96
- fix: 'crypto.createCipheriv("chacha20-poly1305", key, iv)',
97
- documentationLink: 'https://nodejs.org/api/crypto.html#cryptocreatecipherivalgorithm-key-iv-options',
98
- }),
99
- },
100
- schema: [
101
- {
102
- type: 'object',
103
- properties: {
104
- additionalWeakCiphers: {
105
- type: 'array',
106
- items: { type: 'string' },
107
- default: [],
108
- description: 'Additional weak ciphers to detect',
109
- },
110
- allowInTests: {
111
- type: 'boolean',
112
- default: false,
113
- description: 'Allow weak ciphers in test files',
114
- },
115
- },
116
- additionalProperties: false,
117
- },
118
- ],
119
- },
120
- defaultOptions: [
121
- {
122
- additionalWeakCiphers: [],
123
- allowInTests: false,
124
- },
125
- ],
126
- create(context, [options = {}]) {
127
- const { additionalWeakCiphers = [], allowInTests = false, } = options;
128
- const filename = context.filename;
129
- const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
130
- function checkCallExpression(node) {
131
- if (isTestFile)
132
- return;
133
- const cipherMethods = new Set(['createCipher', 'createCipheriv', 'createDecipher', 'createDecipheriv']);
134
- if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
135
- node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
136
- cipherMethods.has(node.callee.property.name)) {
137
- checkCipherArgument(node);
138
- }
139
- if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
140
- cipherMethods.has(node.callee.name)) {
141
- checkCipherArgument(node);
142
- }
143
- }
144
- function checkCipherArgument(node) {
145
- const firstArg = node.arguments[0];
146
- if (firstArg?.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof firstArg.value === 'string') {
147
- const weakPattern = findWeakCipher(firstArg.value, additionalWeakCiphers);
148
- if (weakPattern) {
149
- context.report({
150
- node: firstArg,
151
- messageId: 'weakCipherAlgorithm',
152
- data: {
153
- algorithm: weakPattern.name,
154
- replacement: weakPattern.replacement,
155
- },
156
- suggest: [
157
- {
158
- messageId: 'useAes256Gcm',
159
- fix: (fixer) => fixer.replaceText(firstArg, `"aes-256-gcm"`),
160
- },
161
- {
162
- messageId: 'useChaCha20',
163
- fix: (fixer) => fixer.replaceText(firstArg, `"chacha20-poly1305"`),
164
- },
165
- ],
166
- });
167
- }
168
- }
169
- }
170
- return {
171
- CallExpression: checkCallExpression,
172
- };
173
- },
174
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noWeakCipherAlgorithm=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const WEAK_CIPHER_PATTERNS=[{pattern:/\bdes\b(?!-ede)/i,name:"DES",alternatives:["AES-256-GCM","ChaCha20-Poly1305"],replacement:"aes-256-gcm"},{pattern:/\bdes-ede3?\b|\b3des\b|\btripledes\b/i,name:"3DES",alternatives:["AES-256-GCM","ChaCha20-Poly1305"],replacement:"aes-256-gcm"},{pattern:/\brc4\b|\barc4\b/i,name:"RC4",alternatives:["AES-256-GCM","ChaCha20-Poly1305"],replacement:"aes-256-gcm"},{pattern:/\bblowfish\b|\bbf\b/i,name:"Blowfish",alternatives:["AES-256-GCM","ChaCha20-Poly1305"],replacement:"aes-256-gcm"},{pattern:/\brc2\b/i,name:"RC2",alternatives:["AES-256-GCM","ChaCha20-Poly1305"],replacement:"aes-256-gcm"},{pattern:/\bidea\b/i,name:"IDEA",alternatives:["AES-256-GCM","ChaCha20-Poly1305"],replacement:"aes-256-gcm"}];function findWeakCipher(value,additionalPatterns){for(const pattern of WEAK_CIPHER_PATTERNS){if(pattern.pattern.test(value)){return pattern}}for(const additionalPattern of additionalPatterns){const regex=new RegExp(`\\b${additionalPattern}\\b`,"i");if(regex.test(value)){return{pattern:regex,name:additionalPattern.toUpperCase(),alternatives:["AES-256-GCM","ChaCha20-Poly1305"],replacement:"aes-256-gcm"}}}return null}exports.noWeakCipherAlgorithm=(0,eslint_devkit_1.createRule)({name:"no-weak-cipher-algorithm",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-node-security/docs/rules/no-weak-cipher-algorithm.md",description:"Disallow weak cipher algorithms (DES, 3DES, RC4, Blowfish)",cwe:"CWE-327",cvss:7.5},hasSuggestions:true,messages:{weakCipherAlgorithm:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.SECURITY,issueName:"Weak cipher algorithm",cwe:"CWE-327",description:"Use of weak cipher algorithm: {{algorithm}}. {{algorithm}} has known vulnerabilities and should not be used.",severity:"CRITICAL",fix:'Replace with {{replacement}}: crypto.createCipheriv("{{replacement}}", key, iv)',documentationLink:"https://owasp.org/www-community/vulnerabilities/Weak_Cryptography"}),useAes256Gcm:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use AES-256-GCM",description:"Replace with AES-256-GCM for authenticated encryption",severity:"LOW",fix:'crypto.createCipheriv("aes-256-gcm", key, iv)',documentationLink:"https://nodejs.org/api/crypto.html#cryptocreatecipherivalgorithm-key-iv-options"}),useChaCha20:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use ChaCha20-Poly1305",description:"Replace with ChaCha20-Poly1305 for modern encryption",severity:"LOW",fix:'crypto.createCipheriv("chacha20-poly1305", key, iv)',documentationLink:"https://nodejs.org/api/crypto.html#cryptocreatecipherivalgorithm-key-iv-options"})},schema:[{type:"object",properties:{additionalWeakCiphers:{type:"array",items:{type:"string"},default:[],description:"Additional weak ciphers to detect"},allowInTests:{type:"boolean",default:false,description:"Allow weak ciphers in test files"}},additionalProperties:false}]},defaultOptions:[{additionalWeakCiphers:[],allowInTests:false}],create(context,[options={}]){const{additionalWeakCiphers=[],allowInTests=false}=options;const filename=context.filename;const isTestFile=allowInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);function checkCallExpression(node){if(isTestFile)return;const cipherMethods=new Set(["createCipher","createCipheriv","createDecipher","createDecipheriv"]);if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.MemberExpression&&node.callee.property.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&cipherMethods.has(node.callee.property.name)){checkCipherArgument(node)}if(node.callee.type===eslint_devkit_1.AST_NODE_TYPES.Identifier&&cipherMethods.has(node.callee.name)){checkCipherArgument(node)}}function checkCipherArgument(node){const firstArg=node.arguments[0];if(firstArg?.type===eslint_devkit_1.AST_NODE_TYPES.Literal&&typeof firstArg.value==="string"){const weakPattern=findWeakCipher(firstArg.value,additionalWeakCiphers);if(weakPattern){context.report({node:firstArg,messageId:"weakCipherAlgorithm",data:{algorithm:weakPattern.name,replacement:weakPattern.replacement},suggest:[{messageId:"useAes256Gcm",fix:fixer=>fixer.replaceText(firstArg,`"aes-256-gcm"`)},{messageId:"useChaCha20",fix:fixer=>fixer.replaceText(firstArg,`"chacha20-poly1305"`)}]})}}}return{CallExpression:checkCallExpression}}});