eslint-plugin-node-security 4.6.0 → 4.7.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 (120) hide show
  1. package/README.md +1 -0
  2. package/package.json +5 -18
  3. package/src/index.d.ts +17 -10
  4. package/src/index.js +5 -36
  5. package/src/oxlint.d.ts +17 -20
  6. package/src/oxlint.js +0 -19
  7. package/src/rules/detect-child-process/index.js +0 -107
  8. package/src/rules/detect-eval-with-expression/index.js +2 -48
  9. package/src/rules/detect-non-literal-fs-filename/index.js +0 -82
  10. package/src/rules/detect-suspicious-dependencies/index.js +0 -12
  11. package/src/rules/lock-file/index.js +0 -18
  12. package/src/rules/no-arbitrary-file-access/index.js +0 -52
  13. package/src/rules/no-buffer-overread/index.js +3 -96
  14. package/src/rules/no-cryptojs/index.js +2 -10
  15. package/src/rules/no-cryptojs-weak-random/index.js +1 -13
  16. package/src/rules/no-data-in-temp-storage/index.js +0 -12
  17. package/src/rules/no-deprecated-buffer/index.js +0 -15
  18. package/src/rules/no-deprecated-cipher-method/index.js +0 -9
  19. package/src/rules/no-dynamic-algorithm-selection/index.js +0 -13
  20. package/src/rules/no-dynamic-command-string/index.js +201 -0
  21. package/src/rules/no-dynamic-dependency-loading/index.js +0 -13
  22. package/src/rules/no-dynamic-require/index.js +0 -10
  23. package/src/rules/no-ecb-mode/index.js +0 -7
  24. package/src/rules/no-insecure-key-derivation/index.js +0 -10
  25. package/src/rules/no-insecure-rsa-padding/index.js +0 -9
  26. package/src/rules/no-math-random-crypto/index.js +1 -17
  27. package/src/rules/no-self-signed-certs/index.js +1 -9
  28. package/src/rules/no-sha1-hash/index.js +0 -9
  29. package/src/rules/no-shell-injection/index.js +0 -20
  30. package/src/rules/no-ssrf/index.js +4 -80
  31. package/src/rules/no-static-iv/index.js +2 -21
  32. package/src/rules/no-timing-unsafe-compare/index.js +1 -11
  33. package/src/rules/no-toctou-vulnerability/index.js +0 -22
  34. package/src/rules/no-unsafe-buffer-alloc/index.js +0 -13
  35. package/src/rules/no-unsafe-dynamic-require/index.js +0 -22
  36. package/src/rules/no-weak-cipher-algorithm/index.js +0 -19
  37. package/src/rules/no-weak-hash-algorithm/index.js +0 -22
  38. package/src/rules/no-zip-slip/index.js +3 -81
  39. package/src/rules/prefer-native-crypto/index.js +4 -11
  40. package/src/rules/require-dependency-integrity/index.js +0 -11
  41. package/src/rules/require-secure-credential-storage/index.js +0 -12
  42. package/src/rules/require-secure-deletion/index.js +0 -29
  43. package/src/rules/require-storage-encryption/index.js +0 -12
  44. package/src/types/index.js +0 -6
  45. package/CHANGELOG.md +0 -413
  46. package/src/index.js.map +0 -1
  47. package/src/oxlint.js.map +0 -1
  48. package/src/rules/detect-child-process/index.d.ts +0 -52
  49. package/src/rules/detect-child-process/index.js.map +0 -1
  50. package/src/rules/detect-eval-with-expression/index.d.ts +0 -46
  51. package/src/rules/detect-eval-with-expression/index.js.map +0 -1
  52. package/src/rules/detect-non-literal-fs-filename/index.d.ts +0 -52
  53. package/src/rules/detect-non-literal-fs-filename/index.js.map +0 -1
  54. package/src/rules/detect-suspicious-dependencies/index.d.ts +0 -12
  55. package/src/rules/detect-suspicious-dependencies/index.js.map +0 -1
  56. package/src/rules/lock-file/index.d.ts +0 -13
  57. package/src/rules/lock-file/index.js.map +0 -1
  58. package/src/rules/no-arbitrary-file-access/index.d.ts +0 -12
  59. package/src/rules/no-arbitrary-file-access/index.js.map +0 -1
  60. package/src/rules/no-buffer-overread/index.d.ts +0 -39
  61. package/src/rules/no-buffer-overread/index.js.map +0 -1
  62. package/src/rules/no-cryptojs/index.d.ts +0 -24
  63. package/src/rules/no-cryptojs/index.js.map +0 -1
  64. package/src/rules/no-cryptojs-weak-random/index.d.ts +0 -24
  65. package/src/rules/no-cryptojs-weak-random/index.js.map +0 -1
  66. package/src/rules/no-data-in-temp-storage/index.d.ts +0 -14
  67. package/src/rules/no-data-in-temp-storage/index.js.map +0 -1
  68. package/src/rules/no-deprecated-buffer/index.d.ts +0 -23
  69. package/src/rules/no-deprecated-buffer/index.js.map +0 -1
  70. package/src/rules/no-deprecated-cipher-method/index.d.ts +0 -23
  71. package/src/rules/no-deprecated-cipher-method/index.js.map +0 -1
  72. package/src/rules/no-dynamic-algorithm-selection/index.d.ts +0 -26
  73. package/src/rules/no-dynamic-algorithm-selection/index.js.map +0 -1
  74. package/src/rules/no-dynamic-dependency-loading/index.d.ts +0 -12
  75. package/src/rules/no-dynamic-dependency-loading/index.js.map +0 -1
  76. package/src/rules/no-dynamic-require/index.d.ts +0 -21
  77. package/src/rules/no-dynamic-require/index.js.map +0 -1
  78. package/src/rules/no-ecb-mode/index.d.ts +0 -23
  79. package/src/rules/no-ecb-mode/index.js.map +0 -1
  80. package/src/rules/no-insecure-key-derivation/index.d.ts +0 -24
  81. package/src/rules/no-insecure-key-derivation/index.js.map +0 -1
  82. package/src/rules/no-insecure-rsa-padding/index.d.ts +0 -24
  83. package/src/rules/no-insecure-rsa-padding/index.js.map +0 -1
  84. package/src/rules/no-math-random-crypto/index.d.ts +0 -26
  85. package/src/rules/no-math-random-crypto/index.js.map +0 -1
  86. package/src/rules/no-self-signed-certs/index.d.ts +0 -23
  87. package/src/rules/no-self-signed-certs/index.js.map +0 -1
  88. package/src/rules/no-sha1-hash/index.d.ts +0 -24
  89. package/src/rules/no-sha1-hash/index.js.map +0 -1
  90. package/src/rules/no-shell-injection/index.d.ts +0 -20
  91. package/src/rules/no-shell-injection/index.js.map +0 -1
  92. package/src/rules/no-ssrf/index.d.ts +0 -25
  93. package/src/rules/no-ssrf/index.js.map +0 -1
  94. package/src/rules/no-static-iv/index.d.ts +0 -23
  95. package/src/rules/no-static-iv/index.js.map +0 -1
  96. package/src/rules/no-timing-unsafe-compare/index.d.ts +0 -23
  97. package/src/rules/no-timing-unsafe-compare/index.js.map +0 -1
  98. package/src/rules/no-toctou-vulnerability/index.d.ts +0 -26
  99. package/src/rules/no-toctou-vulnerability/index.js.map +0 -1
  100. package/src/rules/no-unsafe-buffer-alloc/index.d.ts +0 -47
  101. package/src/rules/no-unsafe-buffer-alloc/index.js.map +0 -1
  102. package/src/rules/no-unsafe-dynamic-require/index.d.ts +0 -19
  103. package/src/rules/no-unsafe-dynamic-require/index.js.map +0 -1
  104. package/src/rules/no-weak-cipher-algorithm/index.d.ts +0 -25
  105. package/src/rules/no-weak-cipher-algorithm/index.js.map +0 -1
  106. package/src/rules/no-weak-hash-algorithm/index.d.ts +0 -25
  107. package/src/rules/no-weak-hash-algorithm/index.js.map +0 -1
  108. package/src/rules/no-zip-slip/index.d.ts +0 -35
  109. package/src/rules/no-zip-slip/index.js.map +0 -1
  110. package/src/rules/prefer-native-crypto/index.d.ts +0 -23
  111. package/src/rules/prefer-native-crypto/index.js.map +0 -1
  112. package/src/rules/require-dependency-integrity/index.d.ts +0 -12
  113. package/src/rules/require-dependency-integrity/index.js.map +0 -1
  114. package/src/rules/require-secure-credential-storage/index.d.ts +0 -12
  115. package/src/rules/require-secure-credential-storage/index.js.map +0 -1
  116. package/src/rules/require-secure-deletion/index.d.ts +0 -17
  117. package/src/rules/require-secure-deletion/index.js.map +0 -1
  118. package/src/rules/require-storage-encryption/index.d.ts +0 -12
  119. package/src/rules/require-storage-encryption/index.js.map +0 -1
  120. package/src/types/index.js.map +0 -1
@@ -1,59 +1,34 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noSsrf = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
- // HTTP client functions that make outbound requests
11
5
  const HTTP_CLIENT_FUNCTIONS = new Set([
12
- 'fetch', // built-in / node-fetch
13
- 'got', // got
14
- 'nodeFetch', // node-fetch
15
- 'undici', // undici
6
+ 'fetch',
7
+ 'got',
8
+ 'nodeFetch',
9
+ 'undici',
16
10
  ]);
17
- // HTTP client method calls (e.g., axios.get, http.request)
18
11
  const HTTP_CLIENT_METHODS = new Set([
19
12
  'get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'request',
20
13
  ]);
21
- // Object names that are HTTP client libraries
22
- // `needle.get(url)` is covered here; the `needle('get', url)` verb-first form
23
- // is not — the URL sits in argument 1, and this rule only reads argument 0.
24
14
  const HTTP_CLIENT_OBJECTS = new Set([
25
15
  'axios', 'got', 'superagent', 'request', 'http', 'https', 'undici', 'needle',
26
16
  ]);
27
- // Function names that indicate URL validation
28
17
  const VALIDATION_FUNCTION_NAMES = new Set([
29
18
  'validateUrl', 'validateURL', 'isValidUrl', 'isSafeUrl', 'isAllowed',
30
19
  'isValidURL', 'checkUrl', 'checkURL', 'sanitizeUrl', 'sanitizeURL',
31
20
  ]);
32
- // Substrings in identifier names that suggest user input
33
21
  const USER_INPUT_SUBSTRINGS = [
34
22
  'url', 'endpoint', 'uri', 'href', 'link',
35
23
  'target', 'dest', 'source', 'host',
36
24
  'user', 'input', 'param',
37
25
  ];
38
- /**
39
- * Check if a function parameter name suggests user input
40
- */
41
26
  function isUserInputParamName(name) {
42
27
  const lower = name.toLowerCase();
43
28
  return USER_INPUT_SUBSTRINGS.some(sub => lower.includes(sub));
44
29
  }
45
- // Object roots whose members carry attacker-controlled data in the common
46
- // server frameworks: Express `req`/`request`, Koa `ctx`, Lambda `event`.
47
30
  const REQUEST_ROOT_NAMES = new Set(['req', 'request', 'ctx', 'event']);
48
- // Keys of a Node `http.request(options)` object that actually name a URL.
49
- // `host`/`hostname`/`path`/`port` are deliberately absent: a helper that
50
- // parameterises them is ordinary internal plumbing, not evidence of user flow.
51
31
  const URL_OPTION_KEYS = new Set(['url', 'href', 'uri']);
52
- /**
53
- * True when the expression reads off a request object — `req.query.url`,
54
- * `ctx.request.body.target`, `event.queryStringParameters.u`. Everything
55
- * hanging off a request is untrusted, so the root name is the whole test.
56
- */
57
32
  function isRequestSourced(node) {
58
33
  let current = node;
59
34
  while (current.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression) {
@@ -63,26 +38,6 @@ function isRequestSourced(node) {
63
38
  current.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
64
39
  REQUEST_ROOT_NAMES.has(current.name.toLowerCase()));
65
40
  }
66
- /**
67
- * Does the URL argument carry untrusted data into the request?
68
- *
69
- * The rule reports on evidence of flow, never on the mere presence of a
70
- * dynamic argument. Three shapes qualify:
71
- *
72
- * 1. The whole argument is a user-input-named identifier — `fetch(userUrl)`.
73
- * This is the rule's documented naming heuristic: a bare identifier in
74
- * URL position *is* the URL.
75
- * 2. Any part of the expression reads from a request object —
76
- * `fetch(req.query.url)`, `https.request({ host: req.query.h })`.
77
- * 3. A template literal URL interpolates either of the above —
78
- * `fetch(\`https://\${userHost}/x\`)`.
79
- *
80
- * Everything else is not evidence. In particular an options object whose
81
- * fields are plain locals — `https.request({ host, path, method: 'GET' })`,
82
- * benchmarks/corpus/CWE-444/safe/request-default-parser.js — used to be
83
- * reported unconditionally because a non-Identifier argument bypassed the
84
- * name gate entirely.
85
- */
86
41
  function carriesUntrustedUrl(node) {
87
42
  switch (node.type) {
88
43
  case eslint_devkit_1.AST_NODE_TYPES.Identifier:
@@ -91,15 +46,11 @@ function carriesUntrustedUrl(node) {
91
46
  return isRequestSourced(node);
92
47
  case eslint_devkit_1.AST_NODE_TYPES.TemplateLiteral:
93
48
  return node.expressions.some(carriesUntrustedUrl);
94
- // `new URL(userUrl)` / `String(req.query.url)` — inspect the arguments.
95
49
  case eslint_devkit_1.AST_NODE_TYPES.CallExpression:
96
50
  case eslint_devkit_1.AST_NODE_TYPES.NewExpression:
97
51
  return node.arguments.some(carriesUntrustedUrl);
98
- // `'https://host' + userPath`
99
52
  case eslint_devkit_1.AST_NODE_TYPES.BinaryExpression:
100
53
  return carriesUntrustedUrl(node.left) || carriesUntrustedUrl(node.right);
101
- // `http.request({ url: x, host: y })` — only URL-naming keys count by
102
- // name; every other key still counts if its value is request-sourced.
103
54
  case eslint_devkit_1.AST_NODE_TYPES.ObjectExpression:
104
55
  return node.properties.some(property => {
105
56
  if (property.type !== eslint_devkit_1.AST_NODE_TYPES.Property)
@@ -118,24 +69,17 @@ function carriesUntrustedUrl(node) {
118
69
  return false;
119
70
  }
120
71
  }
121
- /**
122
- * AST-based check: does this node contain a validation pattern?
123
- * Walks the node tree looking for known validation constructs.
124
- */
125
72
  function nodeContainsValidation(node) {
126
- // new URL(x) — URL constructor (parsing/validation)
127
73
  if (node.type === eslint_devkit_1.AST_NODE_TYPES.NewExpression &&
128
74
  node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
129
75
  node.callee.name === 'URL') {
130
76
  return true;
131
77
  }
132
- // validateUrl(x), isValidUrl(x), etc.
133
78
  if (node.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
134
79
  node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
135
80
  VALIDATION_FUNCTION_NAMES.has(node.callee.name)) {
136
81
  return true;
137
82
  }
138
- // arr.includes(x), set.has(x), x.startsWith('...'), regex.test(x)
139
83
  if (node.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
140
84
  node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
141
85
  node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
@@ -144,7 +88,6 @@ function nodeContainsValidation(node) {
144
88
  return true;
145
89
  }
146
90
  }
147
- // hostname === '...' or host === '...'
148
91
  if (node.type === eslint_devkit_1.AST_NODE_TYPES.BinaryExpression &&
149
92
  (node.operator === '===' || node.operator === '==') &&
150
93
  ((node.left.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
@@ -155,13 +98,10 @@ function nodeContainsValidation(node) {
155
98
  (node.right.property.name === 'hostname' || node.right.property.name === 'host')))) {
156
99
  return true;
157
100
  }
158
- // throw new Error(...) — guard clause
159
101
  if (node.type === eslint_devkit_1.AST_NODE_TYPES.ThrowStatement) {
160
102
  return true;
161
103
  }
162
- // Keys to skip: non-child properties that cause circular refs or aren't AST children
163
104
  const SKIP_KEYS = new Set(['parent', 'range', 'loc', 'tokens', 'comments', 'start', 'end']);
164
- // Recurse into child nodes
165
105
  for (const key of Object.keys(node)) {
166
106
  if (SKIP_KEYS.has(key))
167
107
  continue;
@@ -181,11 +121,7 @@ function nodeContainsValidation(node) {
181
121
  }
182
122
  return false;
183
123
  }
184
- /**
185
- * Check if there is URL validation before the HTTP call using AST walking
186
- */
187
124
  function hasValidationBefore(node) {
188
- // Walk up to find the containing block
189
125
  let current = node.parent;
190
126
  while (current) {
191
127
  const parent = current.parent;
@@ -194,14 +130,12 @@ function hasValidationBefore(node) {
194
130
  if (parent.type === eslint_devkit_1.AST_NODE_TYPES.BlockStatement || parent.type === eslint_devkit_1.AST_NODE_TYPES.Program) {
195
131
  const body = parent.body;
196
132
  const idx = body.indexOf(current);
197
- // Check previous sibling statements for validation patterns
198
133
  for (let i = idx - 1; i >= 0 && i >= idx - 10; i--) {
199
134
  if (nodeContainsValidation(body[i])) {
200
135
  return true;
201
136
  }
202
137
  }
203
138
  }
204
- // Check if inside an if-block where the condition contains validation
205
139
  if (parent.type === eslint_devkit_1.AST_NODE_TYPES.IfStatement && parent.test) {
206
140
  if (nodeContainsValidation(parent.test)) {
207
141
  return true;
@@ -255,12 +189,10 @@ exports.noSsrf = (0, eslint_devkit_1.createRule)({
255
189
  return {
256
190
  CallExpression(node) {
257
191
  let isHttpCall = false;
258
- // 1. Direct function call: fetch(url), got(url)
259
192
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
260
193
  HTTP_CLIENT_FUNCTIONS.has(node.callee.name)) {
261
194
  isHttpCall = true;
262
195
  }
263
- // 2. Method call: axios.get(url), http.request(url)
264
196
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
265
197
  node.callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
266
198
  node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
@@ -270,19 +202,12 @@ exports.noSsrf = (0, eslint_devkit_1.createRule)({
270
202
  }
271
203
  if (!isHttpCall)
272
204
  return;
273
- // Check the first argument (the URL)
274
205
  const urlArg = node.arguments[0];
275
206
  if (!urlArg)
276
207
  return;
277
- // Static URLs — fetch('https://api.example.com'), fetch(`https://…`) —
278
- // carry nothing untrusted and fall out at the evidence gate below.
279
- // Check if there is URL validation before this call
280
208
  if (hasValidationBefore(node)) {
281
209
  return;
282
210
  }
283
- // Require evidence that untrusted data reaches the URL. Applies to
284
- // every argument shape — an options object or a template literal used
285
- // to bypass this gate entirely and report unconditionally.
286
211
  if (!carriesUntrustedUrl(urlArg)) {
287
212
  return;
288
213
  }
@@ -294,4 +219,3 @@ exports.noSsrf = (0, eslint_devkit_1.createRule)({
294
219
  };
295
220
  },
296
221
  });
297
- //# sourceMappingURL=index.js.map
@@ -1,18 +1,10 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noStaticIv = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
- /**
11
- * Patterns that indicate a hardcoded IV
12
- */
13
5
  const STATIC_IV_PATTERNS = [
14
- /^[0-9a-f]+$/i, // Hex string
15
- /^[A-Za-z0-9+/]+=*$/, // Base64
6
+ /^[0-9a-f]+$/i,
7
+ /^[A-Za-z0-9+/]+=*$/,
16
8
  ];
17
9
  exports.noStaticIv = (0, eslint_devkit_1.createRule)({
18
10
  name: 'no-static-iv',
@@ -67,10 +59,8 @@ exports.noStaticIv = (0, eslint_devkit_1.createRule)({
67
59
  const { allowInTests = false } = options;
68
60
  const filename = context.filename;
69
61
  const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
70
- // Track variables assigned from randomBytes
71
62
  const randomIvVariables = new Set();
72
63
  function checkVariableDeclarator(node) {
73
- // Track: const iv = crypto.randomBytes(16)
74
64
  if (node.id.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
75
65
  node.init?.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression) {
76
66
  const init = node.init;
@@ -88,7 +78,6 @@ exports.noStaticIv = (0, eslint_devkit_1.createRule)({
88
78
  function checkCallExpression(node) {
89
79
  if (isTestFile)
90
80
  return;
91
- // Check for createCipheriv calls
92
81
  const isCipherivCall = (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
93
82
  node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
94
83
  (node.callee.property.name === 'createCipheriv' || node.callee.property.name === 'createDecipheriv')) ||
@@ -100,14 +89,12 @@ exports.noStaticIv = (0, eslint_devkit_1.createRule)({
100
89
  }
101
90
  }
102
91
  function checkIvArgument(ivArg) {
103
- // Check for string literal IV
104
92
  if (ivArg.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof ivArg.value === 'string') {
105
93
  const value = ivArg.value;
106
94
  if (STATIC_IV_PATTERNS.some(p => p.test(value)) || value.length >= 8) {
107
95
  reportStaticIv(ivArg);
108
96
  }
109
97
  }
110
- // Check for Buffer.from('static')
111
98
  if (ivArg.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
112
99
  ivArg.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
113
100
  ivArg.callee.object.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
@@ -118,7 +105,6 @@ exports.noStaticIv = (0, eslint_devkit_1.createRule)({
118
105
  if (firstArg?.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof firstArg.value === 'string') {
119
106
  reportStaticIv(ivArg);
120
107
  }
121
- // Check for new Uint8Array([...])
122
108
  if (firstArg?.type === eslint_devkit_1.AST_NODE_TYPES.ArrayExpression) {
123
109
  const allLiterals = firstArg.elements.every((el) => el?.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof el.value === 'number');
124
110
  if (allLiterals) {
@@ -126,11 +112,7 @@ exports.noStaticIv = (0, eslint_devkit_1.createRule)({
126
112
  }
127
113
  }
128
114
  }
129
- // Check for variable reference that's NOT from randomBytes
130
115
  if (ivArg.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
131
- // If we've tracked this variable as coming from randomBytes, it's OK
132
- // Otherwise, we can't be sure - only flag if we have evidence it's static
133
- // For now, we don't report variables as we can't always determine their source
134
116
  }
135
117
  }
136
118
  function reportStaticIv(node) {
@@ -145,4 +127,3 @@ exports.noStaticIv = (0, eslint_devkit_1.createRule)({
145
127
  };
146
128
  },
147
129
  });
148
- //# sourceMappingURL=index.js.map
@@ -1,18 +1,11 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noTimingUnsafeCompare = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
5
  const DEFAULT_SECRET_PATTERNS = [
11
- // Common secret names (camelCase, snake_case, kebab-case)
12
6
  'token', 'secret', 'key', 'password', 'hash', 'signature',
13
7
  'mac', 'hmac', 'digest', 'apiKey', 'api_key', 'api-key',
14
8
  'auth', 'credential', 'bearer', 'jwt', 'csrf', 'nonce',
15
- // PII and sensitive data patterns
16
9
  'ssn', 'social_security', 'social-security',
17
10
  'pii', 'private_key', 'private-key', 'privateKey',
18
11
  'access_token', 'access-token', 'accessToken',
@@ -87,12 +80,10 @@ exports.noTimingUnsafeCompare = (0, eslint_devkit_1.createRule)({
87
80
  return false;
88
81
  }
89
82
  function checkBinaryExpression(node) {
90
- // Check for === or == comparisons
91
83
  if (node.operator !== '===' && node.operator !== '==' &&
92
84
  node.operator !== '!==' && node.operator !== '!=') {
93
85
  return;
94
86
  }
95
- // Check if either side looks like a secret
96
87
  const leftIsSecret = isSecretIdentifier(node.left);
97
88
  const rightIsSecret = isSecretIdentifier(node.right);
98
89
  if (leftIsSecret || rightIsSecret) {
@@ -102,7 +93,7 @@ exports.noTimingUnsafeCompare = (0, eslint_devkit_1.createRule)({
102
93
  suggest: [
103
94
  {
104
95
  messageId: 'useTimingSafeEqual',
105
- fix: () => null, // Complex refactoring
96
+ fix: () => null,
106
97
  },
107
98
  ],
108
99
  });
@@ -113,4 +104,3 @@ exports.noTimingUnsafeCompare = (0, eslint_devkit_1.createRule)({
113
104
  };
114
105
  },
115
106
  });
116
- //# sourceMappingURL=index.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noToctouVulnerability = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -86,11 +81,7 @@ exports.noToctouVulnerability = (0, eslint_devkit_2.createRule)({
86
81
  return {};
87
82
  }
88
83
  const sourceCode = context.sourceCode;
89
- /**
90
- * Check for TOCTOU patterns
91
- */
92
84
  function checkCallExpression(node) {
93
- // 1. Identify the file operation (Use)
94
85
  let useMethodName = '';
95
86
  if (node.callee.type === 'MemberExpression' && node.callee.property.type === 'Identifier') {
96
87
  const objectName = node.callee.object.type === 'Identifier' ? node.callee.object.name : '';
@@ -108,22 +99,14 @@ exports.noToctouVulnerability = (0, eslint_devkit_2.createRule)({
108
99
  const useArg = node.arguments[0];
109
100
  if (!useArg)
110
101
  return;
111
- // 2. Walk up to find the condition (Check)
112
102
  let current = node.parent;
113
103
  while (current) {
114
104
  if (current.type === 'IfStatement') {
115
- // Extract the condition node
116
105
  let condition = current.test;
117
- // Handle negated condition: if (!exists(path)) { create(path) } -> also TOCTOU but different logic?
118
- // Actually TOCTOU is usually Check(exists) -> Use(read).
119
- // If (!exists) -> create is Check -> Use.
120
- // But strict TOCTOU is checking state then acting.
121
- // If checking for negation
122
106
  if (condition.type === 'UnaryExpression' && condition.operator === '!') {
123
107
  condition = condition.argument;
124
108
  }
125
109
  if (condition.type === 'CallExpression') {
126
- // Check if it's a file check method
127
110
  let checkMethodName = '';
128
111
  if (condition.callee.type === 'MemberExpression' && condition.callee.property.type === 'Identifier') {
129
112
  checkMethodName = condition.callee.property.name;
@@ -133,15 +116,12 @@ exports.noToctouVulnerability = (0, eslint_devkit_2.createRule)({
133
116
  }
134
117
  const checkMethods = ['existsSync', 'statSync', 'accessSync', 'exists', 'stat', 'access'];
135
118
  if (checkMethods.includes(checkMethodName)) {
136
- // Compare arguments
137
119
  const checkArg = condition.arguments[0];
138
120
  if (checkArg) {
139
- // Method 1: Identifier match (same variable)
140
121
  if (checkArg.type === 'Identifier' && useArg.type === 'Identifier' && checkArg.name === useArg.name) {
141
122
  reportToctou(node);
142
123
  return;
143
124
  }
144
- // Method 2: Text match (fallback)
145
125
  const checkArgText = sourceCode.getText(checkArg).replace(/\s/g, '');
146
126
  const useArgText = sourceCode.getText(useArg).replace(/\s/g, '');
147
127
  if (checkArgText === useArgText) {
@@ -150,7 +130,6 @@ exports.noToctouVulnerability = (0, eslint_devkit_2.createRule)({
150
130
  }
151
131
  }
152
132
  }
153
- // Handle stats.isFile() / stats.isDirectory() pattern
154
133
  if (condition.callee.type === 'MemberExpression' &&
155
134
  condition.callee.property.type === 'Identifier' &&
156
135
  ['isFile', 'isDirectory'].includes(condition.callee.property.name) &&
@@ -214,4 +193,3 @@ exports.noToctouVulnerability = (0, eslint_devkit_2.createRule)({
214
193
  };
215
194
  },
216
195
  });
217
- //# sourceMappingURL=index.js.map
@@ -1,13 +1,7 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noUnsafeBufferAlloc = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
- /** The two uninitialized-memory allocators on `Buffer`. */
11
5
  const UNSAFE_ALLOCATORS = new Set(['allocUnsafe', 'allocUnsafeSlow']);
12
6
  exports.noUnsafeBufferAlloc = (0, eslint_devkit_1.createRule)({
13
7
  name: 'no-unsafe-buffer-alloc',
@@ -49,10 +43,6 @@ exports.noUnsafeBufferAlloc = (0, eslint_devkit_1.createRule)({
49
43
  },
50
44
  defaultOptions: [],
51
45
  create(context) {
52
- /**
53
- * True when the call's result is immediately zeroed in the same
54
- * expression: `Buffer.allocUnsafe(n).fill(0)`. Parent-node shape only.
55
- */
56
46
  function isFilledInPlace(node) {
57
47
  const parent = node.parent;
58
48
  if (parent?.type !== eslint_devkit_1.AST_NODE_TYPES.MemberExpression ||
@@ -62,8 +52,6 @@ exports.noUnsafeBufferAlloc = (0, eslint_devkit_1.createRule)({
62
52
  parent.property.name !== 'fill') {
63
53
  return false;
64
54
  }
65
- // `.fill` must actually be invoked — `f(buf.fill)` passes the method
66
- // around, it does not zero anything.
67
55
  const grandparent = parent.parent;
68
56
  return (grandparent?.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
69
57
  grandparent.callee === parent);
@@ -97,4 +85,3 @@ exports.noUnsafeBufferAlloc = (0, eslint_devkit_1.createRule)({
97
85
  };
98
86
  },
99
87
  });
100
- //# sourceMappingURL=index.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noUnsafeDynamicRequire = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -49,14 +44,7 @@ exports.noUnsafeDynamicRequire = (0, eslint_devkit_2.createRule)({
49
44
  },
50
45
  ],
51
46
  create(context) {
52
- /**
53
- * Track variables that reference require
54
- * Maps variable name to the node where it was assigned
55
- */
56
47
  const requireVariables = new Set();
57
- /**
58
- * Check if a node is a reference to require
59
- */
60
48
  const isRequireReference = (node) => {
61
49
  if (node.type === 'Identifier' && node.name === 'require') {
62
50
  return true;
@@ -66,10 +54,6 @@ exports.noUnsafeDynamicRequire = (0, eslint_devkit_2.createRule)({
66
54
  }
67
55
  return false;
68
56
  };
69
- /**
70
- * Check if argument is dynamic (not a literal)
71
- */
72
- // oxlint-disable-next-line consistent-function-scoping
73
57
  const isDynamicArgument = (arg) => {
74
58
  if (arg.type === 'Literal')
75
59
  return false;
@@ -79,7 +63,6 @@ exports.noUnsafeDynamicRequire = (0, eslint_devkit_2.createRule)({
79
63
  };
80
64
  return {
81
65
  VariableDeclarator(node) {
82
- // Track when require is assigned to a variable
83
66
  if (node.id.type === 'Identifier' && node.init) {
84
67
  if (node.init.type === 'Identifier' && node.init.name === 'require') {
85
68
  requireVariables.add(node.id.name);
@@ -87,21 +70,17 @@ exports.noUnsafeDynamicRequire = (0, eslint_devkit_2.createRule)({
87
70
  }
88
71
  },
89
72
  CallExpression(node) {
90
- // Check for require() calls (direct or via variable)
91
73
  if (node.callee.type !== 'Identifier') {
92
74
  return;
93
75
  }
94
- // Check if callee is require or a variable that references require
95
76
  if (!isRequireReference(node.callee)) {
96
77
  return;
97
78
  }
98
- // Must have at least one argument
99
79
  if (node.arguments.length === 0)
100
80
  return;
101
81
  const firstArg = node.arguments[0];
102
82
  if (firstArg.type === 'SpreadElement')
103
83
  return;
104
- // Check if dynamic
105
84
  if (!isDynamicArgument(firstArg))
106
85
  return;
107
86
  context.report({
@@ -112,4 +91,3 @@ exports.noUnsafeDynamicRequire = (0, eslint_devkit_2.createRule)({
112
91
  };
113
92
  },
114
93
  });
115
- //# sourceMappingURL=index.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noWeakCipherAlgorithm = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -45,17 +40,12 @@ const WEAK_CIPHER_PATTERNS = [
45
40
  replacement: 'aes-256-gcm',
46
41
  },
47
42
  ];
48
- /**
49
- * Check if a string contains a weak cipher algorithm
50
- */
51
43
  function findWeakCipher(value, additionalPatterns) {
52
- // Check standard patterns
53
44
  for (const pattern of WEAK_CIPHER_PATTERNS) {
54
45
  if (pattern.pattern.test(value)) {
55
46
  return pattern;
56
47
  }
57
48
  }
58
- // Check additional patterns
59
49
  for (const additionalPattern of additionalPatterns) {
60
50
  const regex = new RegExp(`\\b${additionalPattern}\\b`, 'i');
61
51
  if (regex.test(value)) {
@@ -137,28 +127,20 @@ exports.noWeakCipherAlgorithm = (0, eslint_devkit_1.createRule)({
137
127
  const { additionalWeakCiphers = [], allowInTests = false, } = options;
138
128
  const filename = context.filename;
139
129
  const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
140
- /**
141
- * Check if a call expression uses a weak cipher
142
- */
143
130
  function checkCallExpression(node) {
144
131
  if (isTestFile)
145
132
  return;
146
133
  const cipherMethods = new Set(['createCipher', 'createCipheriv', 'createDecipher', 'createDecipheriv']);
147
- // Check for crypto.createCipher*() pattern
148
134
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
149
135
  node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
150
136
  cipherMethods.has(node.callee.property.name)) {
151
137
  checkCipherArgument(node);
152
138
  }
153
- // Check for standalone createCipher*() pattern
154
139
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
155
140
  cipherMethods.has(node.callee.name)) {
156
141
  checkCipherArgument(node);
157
142
  }
158
143
  }
159
- /**
160
- * Check the algorithm argument passed to createCipher*
161
- */
162
144
  function checkCipherArgument(node) {
163
145
  const firstArg = node.arguments[0];
164
146
  if (firstArg?.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof firstArg.value === 'string') {
@@ -190,4 +172,3 @@ exports.noWeakCipherAlgorithm = (0, eslint_devkit_1.createRule)({
190
172
  };
191
173
  },
192
174
  });
193
- //# sourceMappingURL=index.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noWeakHashAlgorithm = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -33,17 +28,12 @@ const WEAK_HASH_PATTERNS = [
33
28
  replacement: 'sha256',
34
29
  },
35
30
  ];
36
- /**
37
- * Check if a string contains a weak hash algorithm
38
- */
39
31
  function findWeakHash(value, additionalPatterns) {
40
- // Check standard patterns
41
32
  for (const pattern of WEAK_HASH_PATTERNS) {
42
33
  if (pattern.pattern.test(value)) {
43
34
  return pattern;
44
35
  }
45
36
  }
46
- // Check additional patterns
47
37
  for (const additionalPattern of additionalPatterns) {
48
38
  const regex = new RegExp(`\\b${additionalPattern}\\b`, 'i');
49
39
  if (regex.test(value)) {
@@ -133,29 +123,21 @@ exports.noWeakHashAlgorithm = (0, eslint_devkit_1.createRule)({
133
123
  const { additionalWeakAlgorithms = [], allowInTests = false, } = options;
134
124
  const filename = context.filename;
135
125
  const isTestFile = allowInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
136
- /**
137
- * Check if a call expression uses a weak hash
138
- */
139
126
  function checkCallExpression(node) {
140
127
  if (isTestFile)
141
128
  return;
142
- // Check for crypto.createHash() pattern
143
129
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
144
130
  node.callee.property.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
145
131
  node.callee.property.name === 'createHash') {
146
132
  checkHashArgument(node);
147
133
  }
148
- // Check for standalone createHash() pattern
149
134
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
150
135
  node.callee.name === 'createHash') {
151
136
  checkHashArgument(node);
152
137
  }
153
- // Check for crypto-hash package: sha1(), md5()
154
138
  if (node.callee.type === eslint_devkit_1.AST_NODE_TYPES.Identifier) {
155
139
  const funcName = node.callee.name.toLowerCase();
156
140
  if (funcName === 'sha1' || funcName === 'md5' || funcName === 'md4') {
157
- // funcName is one of sha1/md5/md4, each of which always matches a
158
- // WEAK_HASH_PATTERNS entry, so findWeakHash cannot return null here.
159
141
  const weakPattern = findWeakHash(funcName, additionalWeakAlgorithms);
160
142
  context.report({
161
143
  node,
@@ -179,9 +161,6 @@ exports.noWeakHashAlgorithm = (0, eslint_devkit_1.createRule)({
179
161
  }
180
162
  }
181
163
  }
182
- /**
183
- * Check the algorithm argument passed to createHash
184
- */
185
164
  function checkHashArgument(node) {
186
165
  for (const arg of node.arguments) {
187
166
  if (arg.type === eslint_devkit_1.AST_NODE_TYPES.Literal && typeof arg.value === 'string') {
@@ -218,4 +197,3 @@ exports.noWeakHashAlgorithm = (0, eslint_devkit_1.createRule)({
218
197
  };
219
198
  },
220
199
  });
221
- //# sourceMappingURL=index.js.map