eslint-plugin-node-security 4.6.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +1 -0
  2. package/package.json +5 -8
  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
package/README.md CHANGED
@@ -93,6 +93,7 @@ See the [ESLint Version Support Policy](../../docs/ESLINT_VERSION_SUPPORT.md)
93
93
  | [no-data-in-temp-storage](https://eslint.interlace.tools/docs/security/plugin-node-security/rules/no-data-in-temp-storage?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-node-security) | CWE-312 | | | Temporary directories (/tmp, /var/tmp, temp/) are often world-readable or persist longer than expected | 🟢 | | | | | |
94
94
  | [no-deprecated-buffer](https://eslint.interlace.tools/docs/security/plugin-node-security/rules/no-deprecated-buffer?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-node-security) | CWE-676 | | | Disallow the deprecated `new Buffer()` constructor and `Buffer()` factory call. | 🟢 | | | | 💡 | |
95
95
  | [no-deprecated-cipher-method](https://eslint.interlace.tools/docs/security/plugin-node-security/rules/no-deprecated-cipher-method?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-node-security) | CWE-327 | A02:2021 | | Disallow deprecated crypto.createCipher/createDecipher methods | 🟢 | | | | | |
96
+ | [no-dynamic-command-string](https://eslint.interlace.tools/docs/security/plugin-node-security/rules/no-dynamic-command-string?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-node-security) | CWE-77 | A03:2021 | | Detects dynamically assembled command strings handed to a shell flag (bash -c) or to a command-runner that… | 🟢 | | | | | |
96
97
  | [no-dynamic-dependency-loading](https://eslint.interlace.tools/docs/security/plugin-node-security/rules/no-dynamic-dependency-loading?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-node-security) | CWE-1104 | | | This rule detects dynamically constructed paths in require() and import() statements | 🟢 | | | | | |
97
98
  | [no-dynamic-require](https://eslint.interlace.tools/docs/security/plugin-node-security/rules/no-dynamic-require?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-node-security) | | | | Forbid require() calls with non-literal arguments | 🟢 | | | | | |
98
99
  | [no-ecb-mode](https://eslint.interlace.tools/docs/security/plugin-node-security/rules/no-ecb-mode?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-node-security) | CWE-327 | A02:2021 | | Disallow ECB encryption mode (use GCM or CBC instead) | 🟢 | | | | | |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-node-security",
3
- "version": "4.6.0",
3
+ "version": "4.7.0",
4
4
  "description": "ESLint plugin for Node.js security — detects command injection, path traversal, SSRF, zip slip, and weak crypto (MD5/SHA-1, ECB, static IV) in fs, child_process, vm, and crypto.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -24,7 +24,7 @@
24
24
  "homepage": "https://eslint.interlace.tools/docs/security/plugin-node-security?utm_source=npm&utm_medium=referral&utm_campaign=eslint-plugin-node-security",
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "https://github.com/ofri-peretz/eslint",
27
+ "url": "git+https://github.com/ofri-peretz/eslint.git",
28
28
  "directory": "packages/eslint-plugin-node-security"
29
29
  },
30
30
  "bugs": {
@@ -35,11 +35,8 @@
35
35
  },
36
36
  "files": [
37
37
  "src/",
38
- "dist/",
39
38
  "README.md",
40
- "LICENSE",
41
- "CHANGELOG.md",
42
- "AGENTS.md"
39
+ "LICENSE"
43
40
  ],
44
41
  "keywords": [
45
42
  "eslint",
@@ -82,7 +79,7 @@
82
79
  },
83
80
  "dependencies": {
84
81
  "tslib": "^2.3.0",
85
- "@interlace/eslint-devkit": "^1.4.4"
82
+ "@interlace/eslint-devkit": "^1.6.0"
86
83
  },
87
84
  "devDependencies": {
88
85
  "@typescript-eslint/parser": "^8.46.2",
@@ -99,6 +96,6 @@
99
96
  },
100
97
  "funding": {
101
98
  "type": "github",
102
- "url": "https://github.com/ofri-peretz/eslint"
99
+ "url": "git+https://github.com/ofri-peretz/eslint.git"
103
100
  }
104
101
  }
package/src/index.d.ts CHANGED
@@ -1,10 +1,17 @@
1
- /**
2
- * eslint-plugin-node-security
3
- *
4
- * Security rules for Node.js built-in modules (fs, child_process, vm, path, etc.)
5
- */
6
- import { TSESLint } from '@interlace/eslint-devkit';
7
- export declare const rules: Record<string, TSESLint.RuleModule<string, readonly unknown[]>>;
8
- export declare const plugin: TSESLint.FlatConfig.Plugin;
9
- export declare const configs: Record<string, TSESLint.FlatConfig.Config>;
10
- export default plugin;
1
+ // Minimal declaration written by scripts/build-package.ts.
2
+ //
3
+ // tsc's generated entry declaration inlined every inferred rule-option type
4
+ // (up to 166 kB per plugin) for an API no consumer calls directly. This types
5
+ // what a flat config actually touches. Per-rule option types remain available
6
+ // from the "./types" subpath export where a plugin provides one.
7
+ import type { Linter, Rule } from 'eslint';
8
+
9
+ export declare const rules: Record<string, Rule.RuleModule>;
10
+ export declare const configs: Record<string, Linter.Config>;
11
+ export declare const plugin: {
12
+ meta: { name: string; version: string };
13
+ rules: Record<string, Rule.RuleModule>;
14
+ };
15
+
16
+ declare const _default: typeof plugin;
17
+ export default _default;
package/src/index.js CHANGED
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * eslint-plugin-node-security
4
- *
5
- * Security rules for Node.js built-in modules (fs, child_process, vm, path, etc.)
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.configs = exports.plugin = exports.rules = void 0;
9
4
  const detect_child_process_1 = require("./rules/detect-child-process");
@@ -19,8 +14,8 @@ const no_arbitrary_file_access_1 = require("./rules/no-arbitrary-file-access");
19
14
  const no_data_in_temp_storage_1 = require("./rules/no-data-in-temp-storage");
20
15
  const no_ssrf_1 = require("./rules/no-ssrf");
21
16
  const no_shell_injection_1 = require("./rules/no-shell-injection");
17
+ const no_dynamic_command_string_1 = require("./rules/no-dynamic-command-string");
22
18
  const no_dynamic_algorithm_selection_1 = require("./rules/no-dynamic-algorithm-selection");
23
- // Migrated rules from secure-coding
24
19
  const detect_suspicious_dependencies_1 = require("./rules/detect-suspicious-dependencies");
25
20
  const lock_file_1 = require("./rules/lock-file");
26
21
  const no_dynamic_dependency_loading_1 = require("./rules/no-dynamic-dependency-loading");
@@ -29,7 +24,6 @@ const require_secure_credential_storage_1 = require("./rules/require-secure-cred
29
24
  const require_secure_deletion_1 = require("./rules/require-secure-deletion");
30
25
  const require_storage_encryption_1 = require("./rules/require-storage-encryption");
31
26
  const no_dynamic_require_1 = require("./rules/no-dynamic-require");
32
- // Migrated rules from crypto
33
27
  const no_cryptojs_1 = require("./rules/no-cryptojs");
34
28
  const no_cryptojs_weak_random_1 = require("./rules/no-cryptojs-weak-random");
35
29
  const no_deprecated_cipher_method_1 = require("./rules/no-deprecated-cipher-method");
@@ -58,8 +52,8 @@ exports.rules = {
58
52
  'no-data-in-temp-storage': no_data_in_temp_storage_1.noDataInTempStorage,
59
53
  'no-ssrf': no_ssrf_1.noSsrf,
60
54
  'no-shell-injection': no_shell_injection_1.noShellInjection,
55
+ 'no-dynamic-command-string': no_dynamic_command_string_1.noDynamicCommandString,
61
56
  'no-dynamic-algorithm-selection': no_dynamic_algorithm_selection_1.noDynamicAlgorithmSelection,
62
- // Migrated rules
63
57
  'detect-suspicious-dependencies': detect_suspicious_dependencies_1.detectSuspiciousDependencies,
64
58
  'lock-file': lock_file_1.lockFile,
65
59
  'no-dynamic-dependency-loading': no_dynamic_dependency_loading_1.noDynamicDependencyLoading,
@@ -68,7 +62,6 @@ exports.rules = {
68
62
  'require-secure-deletion': require_secure_deletion_1.requireSecureDeletion,
69
63
  'require-storage-encryption': require_storage_encryption_1.requireStorageEncryption,
70
64
  'no-dynamic-require': no_dynamic_require_1.noDynamicRequire,
71
- // Migrated crypto rules
72
65
  'no-cryptojs': no_cryptojs_1.noCryptojs,
73
66
  'no-cryptojs-weak-random': no_cryptojs_weak_random_1.noCryptojsWeakRandom,
74
67
  'no-deprecated-cipher-method': no_deprecated_cipher_method_1.noDeprecatedCipherMethod,
@@ -87,7 +80,7 @@ exports.rules = {
87
80
  exports.plugin = {
88
81
  meta: {
89
82
  name: 'eslint-plugin-node-security',
90
- version: '4.6.0',
83
+ version: '4.7.0',
91
84
  },
92
85
  rules: exports.rules,
93
86
  };
@@ -96,22 +89,8 @@ const recommendedRules = {
96
89
  'node-security/detect-eval-with-expression': 'error',
97
90
  'node-security/detect-non-literal-fs-filename': 'error',
98
91
  'node-security/no-unsafe-dynamic-require': 'error',
99
- // no-buffer-overread demoted 2026-05-09 — 95% of Wild hits on adversarial
100
- // Edge corpus + insufficient fixture coverage for the README §1 promotion
101
- // gate. Per `npm run ilb:severity-audit` it's both edge-error AND
102
- // volume-error risk. Promote back to 'error' once Edge ratio drops to
103
- // ≤ 50% (see `benchmarks/AUDIT_PATTERNS.md` §3.6 — needs typed-array
104
- // detection like `detect-object-injection` got in audit iter-1).
105
92
  'node-security/no-buffer-overread': 'warn',
106
- // Added in 4.1.0. Set to 'warn' in `recommended` to avoid breaking
107
- // adopters who already use the preset and have legacy `Buffer()` calls.
108
- // Promote to 'error' on the next major bump.
109
93
  'node-security/no-deprecated-buffer': 'error',
110
- // Added in 4.5.0. Unconditional flag on `Buffer.allocUnsafe()` — a real but
111
- // legitimate performance API — so it ships as 'warn', not 'error'. The rule
112
- // does no dataflow, so a correctly-overwritten buffer still reports.
113
- // Upstream `security-node/detect-buffer-unsafe-allocation` ships this off by
114
- // default; 'warn' is the middle ground.
115
94
  'node-security/no-unsafe-buffer-alloc': 'warn',
116
95
  'node-security/no-toctou-vulnerability': 'error',
117
96
  'node-security/no-zip-slip': 'error',
@@ -119,21 +98,12 @@ const recommendedRules = {
119
98
  'node-security/no-data-in-temp-storage': 'error',
120
99
  'node-security/no-ssrf': 'warn',
121
100
  'node-security/no-shell-injection': 'error',
101
+ 'node-security/no-dynamic-command-string': 'error',
122
102
  'node-security/no-dynamic-algorithm-selection': 'error',
123
- // Added to `recommended` 2026-08-02. `secure-coding/no-insecure-comparison`
124
- // was removed from every `secure-coding` preset in favour of this rule, but
125
- // this rule was not in any `recommended` preset — so CWE-697 timing-unsafe
126
- // comparison had no preset coverage anywhere in the ecosystem, and the
127
- // migration note pointed at a rule users would have had to enable by hand.
128
- // Enters at 'warn' rather than 'error' for the same reason as
129
- // `no-deprecated-buffer` above: adopters already on this preset shouldn't
130
- // have CI turn red on a version bump. Promote on the next major.
131
103
  'node-security/no-timing-unsafe-compare': 'warn',
132
- // Migrated Rules
133
104
  'node-security/detect-suspicious-dependencies': 'warn',
134
- 'node-security/lock-file': 'warn', // filesystem check — not pure AST; CI enforces this better
105
+ 'node-security/lock-file': 'warn',
135
106
  'node-security/require-dependency-integrity': 'error',
136
- // Crypto rules in recommended
137
107
  'node-security/no-weak-hash-algorithm': 'error',
138
108
  'node-security/no-weak-cipher-algorithm': 'error',
139
109
  'node-security/no-static-iv': 'error',
@@ -159,4 +129,3 @@ exports.configs = {
159
129
  },
160
130
  };
161
131
  exports.default = exports.plugin;
162
- //# sourceMappingURL=index.js.map
package/src/oxlint.d.ts CHANGED
@@ -1,20 +1,17 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
- /**
7
- * oxlint sub-export.
8
- *
9
- * Consumers wire oxlint to this entry via:
10
- * { "jsPlugins": ["eslint-plugin-node-security/oxlint"] }
11
- *
12
- * oxlint's JS plugin loader does `require('eslint-plugin-node-security/oxlint')` and reads
13
- * `.rules` off the result. The compiled output of `export = plugin` is
14
- * `module.exports = plugin`, which gives oxlint the plugin object directly
15
- * (`{ meta, rules }`) — no wrapper, no `.default` indirection.
16
- *
17
- * Generated by scripts/generate-oxlint-shims.mjs. Do not edit by hand.
18
- */
19
- import { plugin } from './index';
20
- export = plugin;
1
+ // Minimal declaration written by scripts/build-package.ts.
2
+ //
3
+ // tsc's generated entry declaration inlined every inferred rule-option type
4
+ // (up to 166 kB per plugin) for an API no consumer calls directly. This types
5
+ // what a flat config actually touches. Per-rule option types remain available
6
+ // from the "./types" subpath export where a plugin provides one.
7
+ import type { Linter, Rule } from 'eslint';
8
+
9
+ export declare const rules: Record<string, Rule.RuleModule>;
10
+ export declare const configs: Record<string, Linter.Config>;
11
+ export declare const plugin: {
12
+ meta: { name: string; version: string };
13
+ rules: Record<string, Rule.RuleModule>;
14
+ };
15
+
16
+ declare const _default: typeof plugin;
17
+ export default _default;
package/src/oxlint.js CHANGED
@@ -1,22 +1,3 @@
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
- /**
8
- * oxlint sub-export.
9
- *
10
- * Consumers wire oxlint to this entry via:
11
- * { "jsPlugins": ["eslint-plugin-node-security/oxlint"] }
12
- *
13
- * oxlint's JS plugin loader does `require('eslint-plugin-node-security/oxlint')` and reads
14
- * `.rules` off the result. The compiled output of `export = plugin` is
15
- * `module.exports = plugin`, which gives oxlint the plugin object directly
16
- * (`{ meta, rules }`) — no wrapper, no `.default` indirection.
17
- *
18
- * Generated by scripts/generate-oxlint-shims.mjs. Do not edit by hand.
19
- */
20
2
  const index_1 = require("./index");
21
3
  module.exports = index_1.plugin;
22
- //# sourceMappingURL=oxlint.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.detectChildProcess = exports.generateRefactoringSteps = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -15,7 +10,6 @@ const COMMAND_PATTERNS = [
15
10
  vulnerability: 'command-injection',
16
11
  safeAlternatives: ['execFile', 'spawn'],
17
12
  example: {
18
- // oxlint-disable-next-line no-template-curly-in-string
19
13
  bad: 'exec(`git clone ${repoUrl}`)',
20
14
  good: [
21
15
  'execFile(\'git\', [\'clone\', repoUrl], {shell: false})',
@@ -29,9 +23,7 @@ const COMMAND_PATTERNS = [
29
23
  dangerous: true,
30
24
  vulnerability: 'command-injection',
31
25
  safeAlternatives: ['execFileSync', 'spawnSync'],
32
- // oxlint-disable-next-line no-template-curly-in-string
33
26
  example: {
34
- // oxlint-disable-next-line no-template-curly-in-string
35
27
  bad: 'execSync(`npm install ${packageName}`)',
36
28
  good: [
37
29
  'execFileSync(\'npm\', [\'install\', packageName], {shell: false})',
@@ -125,13 +117,6 @@ const COMMAND_PATTERNS = [
125
117
  effort: '15-20 minutes'
126
118
  }
127
119
  ];
128
- /**
129
- * Generate refactoring steps based on the pattern.
130
- *
131
- * Module-scope (rather than inline in `create()`) so the `default` branch —
132
- * unreachable through COMMAND_PATTERNS, which only contains methods with
133
- * dedicated cases — is directly unit-testable.
134
- */
135
120
  const generateRefactoringSteps = (pattern) => {
136
121
  switch (pattern.method) {
137
122
  case 'exec':
@@ -214,7 +199,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
214
199
  confidence: 'medium',
215
200
  },
216
201
  messages: {
217
- // 🎯 Token optimization: 44% reduction (55→31 tokens) - removes ❌/✅/📚 labels
218
202
  childProcessCommandInjection: (0, eslint_devkit_1.formatLLMMessage)({
219
203
  icon: eslint_devkit_1.MessageIcons.WARNING,
220
204
  issueName: 'Command injection',
@@ -331,9 +315,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
331
315
  create(context) {
332
316
  const options = context.options[0] || {};
333
317
  const { allowLiteralStrings = false, allowLiteralSpawn = false, additionalMethods = [], } = options;
334
- /**
335
- * Child process methods that can be dangerous (Set for O(1) lookup)
336
- */
337
318
  const dangerousMethodsSet = new Set([
338
319
  'exec',
339
320
  'execSync',
@@ -345,16 +326,8 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
345
326
  'forkSync',
346
327
  ...additionalMethods
347
328
  ]);
348
- /**
349
- * Track imported child_process identifiers so we can flag calls like
350
- * `exec()` or `cp.exec()` in addition to `child_process.exec()`.
351
- */
352
329
  const moduleAliases = new Set(['child_process']);
353
330
  const importedMethods = new Set();
354
- /**
355
- * Check if a node contains string interpolation or concatenation
356
- */
357
- // oxlint-disable-next-line consistent-function-scoping
358
331
  const containsDynamicStrings = (node) => {
359
332
  if (node.type === 'TemplateLiteral') {
360
333
  return node.expressions.length > 0;
@@ -362,26 +335,18 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
362
335
  if (node.type === 'BinaryExpression' && node.operator === '+') {
363
336
  return true;
364
337
  }
365
- // Check for variable references
366
338
  if (node.type === 'Identifier') {
367
339
  return true;
368
340
  }
369
341
  return false;
370
342
  };
371
- /**
372
- * Check if command and arguments are literals (safe for execFile/spawn patterns)
373
- * We only care about the command (arg 0) and args array (arg 1).
374
- * The options object (arg 2) is irrelevant for command injection.
375
- */
376
343
  const hasOnlyLiteralArgs = (args) => {
377
344
  if (args.length === 0)
378
345
  return false;
379
- // First argument must be a literal string (the command)
380
346
  const command = args[0];
381
347
  if (command.type !== 'Literal' || typeof command.value !== 'string') {
382
348
  return false;
383
349
  }
384
- // Second argument (if present) must be a literal array of literal strings
385
350
  if (args.length >= 2) {
386
351
  const argsArray = args[1];
387
352
  if (argsArray.type === 'ArrayExpression') {
@@ -391,45 +356,29 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
391
356
  }
392
357
  }
393
358
  else if (argsArray.type !== 'Literal') {
394
- // If second arg is not array or literal, it's dynamic
395
359
  return false;
396
360
  }
397
361
  }
398
- // Options object (arg 2+) is irrelevant for command injection safety
399
- // It may contain callbacks, cwd, env, etc. which are not injection vectors
400
362
  return true;
401
363
  };
402
- /**
403
- * Check if spawn/spawnSync has { shell: false } option
404
- */
405
364
  const hasShellFalseOption = (node) => {
406
- // Options is typically the 3rd argument for spawn(cmd, args, options)
407
365
  const optionsArg = node.arguments[2];
408
366
  if (!optionsArg || optionsArg.type !== eslint_devkit_1.AST_NODE_TYPES.ObjectExpression) {
409
- // No options = default shell: false for spawn
410
367
  return true;
411
368
  }
412
369
  for (const prop of optionsArg.properties) {
413
370
  if (prop.type === eslint_devkit_1.AST_NODE_TYPES.Property &&
414
371
  prop.key.type === eslint_devkit_1.AST_NODE_TYPES.Identifier &&
415
372
  prop.key.name === 'shell') {
416
- // shell: false is safe
417
373
  if (prop.value.type === eslint_devkit_1.AST_NODE_TYPES.Literal && prop.value.value === false) {
418
374
  return true;
419
375
  }
420
- // shell: true or shell: someVar is not safe
421
376
  return false;
422
377
  }
423
378
  }
424
- // No shell property = default is false = safe
425
379
  return true;
426
380
  };
427
- /**
428
- * Check if a variable is validated against an allowlist before use
429
- * Looks for patterns like: if (ALLOWED.includes(arg)) or if (!ALLOWED.includes(arg)) { return/throw }
430
- */
431
381
  const hasPrecedingAllowlistValidation = (node) => {
432
- // Helper: check if an arg node contains a validated variable
433
382
  const makeArgChecker = (validatedVarNames) => {
434
383
  const check = (argNode) => {
435
384
  if (argNode.type === 'Identifier' && validatedVarNames.has(argNode.name))
@@ -444,10 +393,8 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
444
393
  };
445
394
  return check;
446
395
  };
447
- // Helper: check if a guard clause IfStatement validates any of our call's args
448
396
  const checkGuardClause = (ifNode) => {
449
397
  const test = ifNode.test;
450
- // Pattern 1: if (ALLOWED.includes(arg)) { ... our call is inside ... }
451
398
  if (test.type === 'CallExpression' &&
452
399
  test.callee.type === 'MemberExpression' &&
453
400
  test.callee.property.type === 'Identifier' &&
@@ -463,7 +410,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
463
410
  return true;
464
411
  }
465
412
  }
466
- // Pattern 2: if (!ALLOWED.includes(arg)) { throw/return } — guard clause
467
413
  if (test.type === eslint_devkit_1.AST_NODE_TYPES.UnaryExpression && test.operator === '!' &&
468
414
  test.argument.type === eslint_devkit_1.AST_NODE_TYPES.CallExpression &&
469
415
  test.argument.callee.type === eslint_devkit_1.AST_NODE_TYPES.MemberExpression &&
@@ -483,7 +429,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
483
429
  validatedVarNames.add(testArg.name);
484
430
  }
485
431
  const check = makeArgChecker(validatedVarNames.size > 0 ? validatedVarNames : new Set(['*']));
486
- // If we have specific validated var names, check them; otherwise check any identifier
487
432
  if (validatedVarNames.size > 0) {
488
433
  for (const arg of node.arguments) {
489
434
  if (check(arg))
@@ -491,7 +436,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
491
436
  }
492
437
  }
493
438
  else {
494
- // No specific args in includes() - treat as generic guard
495
439
  for (const arg of node.arguments) {
496
440
  if (arg.type === 'Identifier' ||
497
441
  (arg.type === eslint_devkit_1.AST_NODE_TYPES.ArrayExpression && arg.elements.some(el => el?.type === 'Identifier'))) {
@@ -503,7 +447,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
503
447
  }
504
448
  return false;
505
449
  };
506
- // Pass 1: Walk up parent chain looking for ancestor IfStatements
507
450
  let current = node.parent;
508
451
  while (current) {
509
452
  if (current.type === 'IfStatement') {
@@ -512,10 +455,7 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
512
455
  }
513
456
  current = current.parent;
514
457
  }
515
- // Pass 2: Look for guard clause IfStatements as preceding siblings in the same block
516
- // This handles: function f(x) { if (!allowed.includes(x)) throw ...; execFile('cmd', [x]); }
517
458
  let stmt = node.parent;
518
- // Walk up to find the statement that contains our call in a block
519
459
  while (stmt && stmt.parent && stmt.parent.type !== eslint_devkit_1.AST_NODE_TYPES.BlockStatement) {
520
460
  stmt = stmt.parent;
521
461
  }
@@ -523,7 +463,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
523
463
  const block = stmt.parent;
524
464
  const callIndex = block.body.indexOf(stmt);
525
465
  if (callIndex > 0) {
526
- // Check preceding siblings for guard clause IfStatements
527
466
  for (let i = 0; i < callIndex; i++) {
528
467
  const sibling = block.body[i];
529
468
  if (sibling.type === 'IfStatement') {
@@ -535,24 +474,13 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
535
474
  }
536
475
  return false;
537
476
  };
538
- /**
539
- * Extract command and arguments for analysis.
540
- * `method` comes from getChildProcessCall, which already resolved the
541
- * callee shape — re-deriving it here would duplicate that logic behind
542
- * an unreachable defensive branch.
543
- */
544
477
  const extractCommandInfo = (node, method) => {
545
478
  const sourceCode = context.sourceCode;
546
479
  const args = node.arguments.map((arg) => sourceCode.getText(arg)).join(', ');
547
480
  const pattern = COMMAND_PATTERNS.find(p => p.method === method) || null;
548
- // Check if arguments contain dynamic content
549
481
  const isDynamic = node.arguments.some((arg) => containsDynamicStrings(arg));
550
482
  return { args, pattern, isDynamic };
551
483
  };
552
- /**
553
- * Determine risk level based on the call pattern
554
- */
555
- // oxlint-disable-next-line consistent-function-scoping
556
484
  const determineRiskLevel = (pattern, isDynamic) => {
557
485
  if (pattern?.dangerous && isDynamic) {
558
486
  return 'critical';
@@ -562,24 +490,18 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
562
490
  }
563
491
  return 'medium';
564
492
  };
565
- /**
566
- * Determine whether the callee refers to a child_process API.
567
- */
568
493
  const getChildProcessCall = (node) => {
569
- // child_process.exec(...)
570
494
  if (node.callee.type === 'MemberExpression' &&
571
495
  node.callee.property.type === 'Identifier') {
572
496
  const methodName = node.callee.property.name;
573
497
  if (!dangerousMethodsSet.has(methodName)) {
574
498
  return null;
575
499
  }
576
- // child_process.exec(...) or alias.exec(...)
577
500
  if (node.callee.object.type === 'Identifier' &&
578
501
  moduleAliases.has(node.callee.object.name)) {
579
502
  return { method: methodName, calleeNode: node.callee };
580
503
  }
581
504
  }
582
- // exec(...) when imported directly from child_process
583
505
  if (node.callee.type === 'Identifier' && dangerousMethodsSet.has(node.callee.name)) {
584
506
  if (importedMethods.has(node.callee.name)) {
585
507
  return { method: node.callee.name, calleeNode: node.callee };
@@ -587,9 +509,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
587
509
  }
588
510
  return null;
589
511
  };
590
- /**
591
- * Check child_process calls for security issues
592
- */
593
512
  const checkChildProcessCall = (node) => {
594
513
  const detected = getChildProcessCall(node);
595
514
  if (!detected) {
@@ -597,43 +516,26 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
597
516
  }
598
517
  const { method } = detected;
599
518
  const { args, pattern, isDynamic } = extractCommandInfo(node, method);
600
- // ALWAYS safe: exec/execSync called with a single string literal that
601
- // contains no interpolation — there is no user input to inject. The
602
- // rule discourages exec() in general (because it's easy to add a
603
- // template-literal later), but a fully-literal call is structurally
604
- // safe and shouldn't be flagged. Fire-on-stylistic-discouragement is
605
- // out of scope for a security rule.
606
519
  if ((method === 'exec' || method === 'execSync') && !isDynamic && hasOnlyLiteralArgs(node.arguments)) {
607
520
  return;
608
521
  }
609
- // Allow literal strings if configured (legacy option, kept for
610
- // backward compat with existing user configs).
611
522
  if (allowLiteralStrings && method === 'exec' && !isDynamic) {
612
523
  return;
613
524
  }
614
- // Allow safe methods with literal args if configured
615
- // execFile, execFileSync, spawn, spawnSync are inherently safer than exec
616
- // when using literal command + literal args array
617
525
  const saferMethods = new Set(['spawn', 'spawnSync', 'execFile', 'execFileSync']);
618
526
  if (allowLiteralSpawn && saferMethods.has(method) && hasOnlyLiteralArgs(node.arguments)) {
619
527
  return;
620
528
  }
621
- // ALWAYS safe: literal command + ALL literal args (no dynamic input at all).
622
- // For execFile/execFileSync: no shell by default, all-literal = nothing to inject.
623
- // For spawn/spawnSync: requires shell:false + all-literal args.
624
529
  if (saferMethods.has(method) && hasOnlyLiteralArgs(node.arguments)) {
625
530
  const isExecFile = method === 'execFile' || method === 'execFileSync';
626
531
  if (isExecFile || hasShellFalseOption(node)) {
627
532
  return;
628
533
  }
629
534
  }
630
- // Allow safe methods when args are validated against an allowlist
631
- // Pattern: if (ALLOWED.includes(arg)) { execFile('cmd', [arg]) }
632
535
  const allSafeMethods = ['execFile', 'execFileSync', 'spawn', 'spawnSync'];
633
536
  if (allSafeMethods.includes(method) && hasPrecedingAllowlistValidation(node)) {
634
537
  return;
635
538
  }
636
- // Report the security issue
637
539
  const riskLevel = determineRiskLevel(pattern, isDynamic);
638
540
  const steps = pattern ? (0, exports.generateRefactoringSteps)(pattern) : 'Review and secure command execution';
639
541
  const alternatives = pattern?.safeAlternatives.join(', ') || 'execFile, spawn with validation';
@@ -673,9 +575,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
673
575
  ]
674
576
  });
675
577
  };
676
- /**
677
- * Track imports/requires of child_process to catch alias usage.
678
- */
679
578
  const trackChildProcessImport = (node) => {
680
579
  if (node.source.value !== 'child_process') {
681
580
  return;
@@ -689,14 +588,10 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
689
588
  }
690
589
  }
691
590
  };
692
- /**
693
- * Track CommonJS require patterns.
694
- */
695
591
  const trackChildProcessRequire = (node) => {
696
592
  if (!node.init) {
697
593
  return;
698
594
  }
699
- // const cp = require('child_process');
700
595
  if (node.id.type === 'Identifier' &&
701
596
  node.init.type === 'CallExpression' &&
702
597
  node.init.callee.type === 'Identifier' &&
@@ -707,7 +602,6 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
707
602
  moduleAliases.add(node.id.name);
708
603
  return;
709
604
  }
710
- // const { exec } = require('child_process');
711
605
  if (node.id.type === 'ObjectPattern' &&
712
606
  node.init?.type === 'CallExpression' &&
713
607
  node.init.callee.type === 'Identifier' &&
@@ -729,4 +623,3 @@ exports.detectChildProcess = (0, eslint_devkit_2.createRule)({
729
623
  };
730
624
  },
731
625
  });
732
- //# sourceMappingURL=index.js.map