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.
- package/README.md +1 -0
- package/package.json +5 -8
- package/src/index.d.ts +17 -10
- package/src/index.js +5 -36
- package/src/oxlint.d.ts +17 -20
- package/src/oxlint.js +0 -19
- package/src/rules/detect-child-process/index.js +0 -107
- package/src/rules/detect-eval-with-expression/index.js +2 -48
- package/src/rules/detect-non-literal-fs-filename/index.js +0 -82
- package/src/rules/detect-suspicious-dependencies/index.js +0 -12
- package/src/rules/lock-file/index.js +0 -18
- package/src/rules/no-arbitrary-file-access/index.js +0 -52
- package/src/rules/no-buffer-overread/index.js +3 -96
- package/src/rules/no-cryptojs/index.js +2 -10
- package/src/rules/no-cryptojs-weak-random/index.js +1 -13
- package/src/rules/no-data-in-temp-storage/index.js +0 -12
- package/src/rules/no-deprecated-buffer/index.js +0 -15
- package/src/rules/no-deprecated-cipher-method/index.js +0 -9
- package/src/rules/no-dynamic-algorithm-selection/index.js +0 -13
- package/src/rules/no-dynamic-command-string/index.js +201 -0
- package/src/rules/no-dynamic-dependency-loading/index.js +0 -13
- package/src/rules/no-dynamic-require/index.js +0 -10
- package/src/rules/no-ecb-mode/index.js +0 -7
- package/src/rules/no-insecure-key-derivation/index.js +0 -10
- package/src/rules/no-insecure-rsa-padding/index.js +0 -9
- package/src/rules/no-math-random-crypto/index.js +1 -17
- package/src/rules/no-self-signed-certs/index.js +1 -9
- package/src/rules/no-sha1-hash/index.js +0 -9
- package/src/rules/no-shell-injection/index.js +0 -20
- package/src/rules/no-ssrf/index.js +4 -80
- package/src/rules/no-static-iv/index.js +2 -21
- package/src/rules/no-timing-unsafe-compare/index.js +1 -11
- package/src/rules/no-toctou-vulnerability/index.js +0 -22
- package/src/rules/no-unsafe-buffer-alloc/index.js +0 -13
- package/src/rules/no-unsafe-dynamic-require/index.js +0 -22
- package/src/rules/no-weak-cipher-algorithm/index.js +0 -19
- package/src/rules/no-weak-hash-algorithm/index.js +0 -22
- package/src/rules/no-zip-slip/index.js +3 -81
- package/src/rules/prefer-native-crypto/index.js +4 -11
- package/src/rules/require-dependency-integrity/index.js +0 -11
- package/src/rules/require-secure-credential-storage/index.js +0 -12
- package/src/rules/require-secure-deletion/index.js +0 -29
- package/src/rules/require-storage-encryption/index.js +0 -12
- package/src/types/index.js +0 -6
- package/CHANGELOG.md +0 -413
- package/src/index.js.map +0 -1
- package/src/oxlint.js.map +0 -1
- package/src/rules/detect-child-process/index.d.ts +0 -52
- package/src/rules/detect-child-process/index.js.map +0 -1
- package/src/rules/detect-eval-with-expression/index.d.ts +0 -46
- package/src/rules/detect-eval-with-expression/index.js.map +0 -1
- package/src/rules/detect-non-literal-fs-filename/index.d.ts +0 -52
- package/src/rules/detect-non-literal-fs-filename/index.js.map +0 -1
- package/src/rules/detect-suspicious-dependencies/index.d.ts +0 -12
- package/src/rules/detect-suspicious-dependencies/index.js.map +0 -1
- package/src/rules/lock-file/index.d.ts +0 -13
- package/src/rules/lock-file/index.js.map +0 -1
- package/src/rules/no-arbitrary-file-access/index.d.ts +0 -12
- package/src/rules/no-arbitrary-file-access/index.js.map +0 -1
- package/src/rules/no-buffer-overread/index.d.ts +0 -39
- package/src/rules/no-buffer-overread/index.js.map +0 -1
- package/src/rules/no-cryptojs/index.d.ts +0 -24
- package/src/rules/no-cryptojs/index.js.map +0 -1
- package/src/rules/no-cryptojs-weak-random/index.d.ts +0 -24
- package/src/rules/no-cryptojs-weak-random/index.js.map +0 -1
- package/src/rules/no-data-in-temp-storage/index.d.ts +0 -14
- package/src/rules/no-data-in-temp-storage/index.js.map +0 -1
- package/src/rules/no-deprecated-buffer/index.d.ts +0 -23
- package/src/rules/no-deprecated-buffer/index.js.map +0 -1
- package/src/rules/no-deprecated-cipher-method/index.d.ts +0 -23
- package/src/rules/no-deprecated-cipher-method/index.js.map +0 -1
- package/src/rules/no-dynamic-algorithm-selection/index.d.ts +0 -26
- package/src/rules/no-dynamic-algorithm-selection/index.js.map +0 -1
- package/src/rules/no-dynamic-dependency-loading/index.d.ts +0 -12
- package/src/rules/no-dynamic-dependency-loading/index.js.map +0 -1
- package/src/rules/no-dynamic-require/index.d.ts +0 -21
- package/src/rules/no-dynamic-require/index.js.map +0 -1
- package/src/rules/no-ecb-mode/index.d.ts +0 -23
- package/src/rules/no-ecb-mode/index.js.map +0 -1
- package/src/rules/no-insecure-key-derivation/index.d.ts +0 -24
- package/src/rules/no-insecure-key-derivation/index.js.map +0 -1
- package/src/rules/no-insecure-rsa-padding/index.d.ts +0 -24
- package/src/rules/no-insecure-rsa-padding/index.js.map +0 -1
- package/src/rules/no-math-random-crypto/index.d.ts +0 -26
- package/src/rules/no-math-random-crypto/index.js.map +0 -1
- package/src/rules/no-self-signed-certs/index.d.ts +0 -23
- package/src/rules/no-self-signed-certs/index.js.map +0 -1
- package/src/rules/no-sha1-hash/index.d.ts +0 -24
- package/src/rules/no-sha1-hash/index.js.map +0 -1
- package/src/rules/no-shell-injection/index.d.ts +0 -20
- package/src/rules/no-shell-injection/index.js.map +0 -1
- package/src/rules/no-ssrf/index.d.ts +0 -25
- package/src/rules/no-ssrf/index.js.map +0 -1
- package/src/rules/no-static-iv/index.d.ts +0 -23
- package/src/rules/no-static-iv/index.js.map +0 -1
- package/src/rules/no-timing-unsafe-compare/index.d.ts +0 -23
- package/src/rules/no-timing-unsafe-compare/index.js.map +0 -1
- package/src/rules/no-toctou-vulnerability/index.d.ts +0 -26
- package/src/rules/no-toctou-vulnerability/index.js.map +0 -1
- package/src/rules/no-unsafe-buffer-alloc/index.d.ts +0 -47
- package/src/rules/no-unsafe-buffer-alloc/index.js.map +0 -1
- package/src/rules/no-unsafe-dynamic-require/index.d.ts +0 -19
- package/src/rules/no-unsafe-dynamic-require/index.js.map +0 -1
- package/src/rules/no-weak-cipher-algorithm/index.d.ts +0 -25
- package/src/rules/no-weak-cipher-algorithm/index.js.map +0 -1
- package/src/rules/no-weak-hash-algorithm/index.d.ts +0 -25
- package/src/rules/no-weak-hash-algorithm/index.js.map +0 -1
- package/src/rules/no-zip-slip/index.d.ts +0 -35
- package/src/rules/no-zip-slip/index.js.map +0 -1
- package/src/rules/prefer-native-crypto/index.d.ts +0 -23
- package/src/rules/prefer-native-crypto/index.js.map +0 -1
- package/src/rules/require-dependency-integrity/index.d.ts +0 -12
- package/src/rules/require-dependency-integrity/index.js.map +0 -1
- package/src/rules/require-secure-credential-storage/index.d.ts +0 -12
- package/src/rules/require-secure-credential-storage/index.js.map +0 -1
- package/src/rules/require-secure-deletion/index.d.ts +0 -17
- package/src/rules/require-secure-deletion/index.js.map +0 -1
- package/src/rules/require-storage-encryption/index.d.ts +0 -12
- package/src/rules/require-storage-encryption/index.js.map +0 -1
- 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.
|
|
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.
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export declare const
|
|
10
|
-
export
|
|
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.
|
|
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',
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|