@eslint-sh/eslint-plugin 0.4.0 → 0.6.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 +223 -17
- package/dist/command-harvest/builtins/bash.d.ts +36 -0
- package/dist/command-harvest/builtins/bash.js +98 -0
- package/dist/command-harvest/builtins/bats.d.ts +45 -0
- package/dist/command-harvest/builtins/bats.js +33 -0
- package/dist/command-harvest/builtins/index.d.ts +18 -0
- package/dist/command-harvest/builtins/index.js +39 -0
- package/dist/command-harvest/builtins/posix.d.ts +51 -0
- package/dist/command-harvest/builtins/posix.js +93 -0
- package/dist/command-harvest/harvest-commands.d.ts +66 -0
- package/dist/command-harvest/harvest-commands.js +97 -0
- package/dist/command-harvest/wrapper-table.d.ts +24 -0
- package/dist/command-harvest/wrapper-table.js +49 -0
- package/dist/configs/agent-authored.d.ts +41 -0
- package/dist/configs/agent-authored.js +33 -0
- package/dist/configs/bash-3-2.d.ts +28 -0
- package/dist/configs/bash-3-2.js +50 -0
- package/dist/configs/posix-portable.d.ts +40 -0
- package/dist/configs/posix-portable.js +52 -0
- package/dist/configs/recommended.d.ts +52 -0
- package/dist/configs/recommended.js +34 -0
- package/dist/declaration-conventions/comment-pragma.d.ts +33 -0
- package/dist/declaration-conventions/comment-pragma.js +153 -0
- package/dist/declaration-conventions/declaration-convention.d.ts +189 -0
- package/dist/declaration-conventions/declaration-convention.js +1 -0
- package/dist/declaration-conventions/read-declared-commands.d.ts +70 -0
- package/dist/declaration-conventions/read-declared-commands.js +128 -0
- package/dist/eslint-plugin-syntax-analyze.d.ts +23 -0
- package/dist/eslint-plugin-syntax.d.ts +27 -0
- package/dist/eslint-plugin.d.ts +1211 -52
- package/dist/index.d.ts +56 -8
- package/dist/index.js +53 -6
- package/dist/language/command-resolution.d.ts +124 -0
- package/dist/language/command-resolution.js +120 -0
- package/dist/language/end-of-input-marker.d.ts +29 -0
- package/dist/language/end-of-input-marker.js +88 -0
- package/dist/language/line-breaks.d.ts +18 -0
- package/dist/language/line-breaks.js +22 -0
- package/dist/language/lint-view-types.d.ts +104 -0
- package/dist/language/lint-view-types.js +1 -0
- package/dist/language/non-shell-file-guard.d.ts +35 -0
- package/dist/language/non-shell-file-guard.js +59 -0
- package/dist/language/selector-classes.d.ts +0 -1
- package/dist/language/selector-classes.js +0 -1
- package/dist/language/shell-language-options.d.ts +7 -1
- package/dist/language/shell-language-options.js +4 -4
- package/dist/language/shell-language.d.ts +10 -2
- package/dist/language/shell-language.js +59 -8
- package/dist/language/shell-source-code.d.ts +342 -5
- package/dist/language/shell-source-code.js +559 -30
- package/dist/language/source-coordinates.d.ts +13 -2
- package/dist/language/source-coordinates.js +68 -14
- package/dist/language/syntax-view.d.ts +60 -39
- package/dist/language/syntax-view.js +200 -45
- package/dist/language/word-resolution.d.ts +4 -3
- package/dist/language/word-resolution.js +4 -3
- package/dist/rules/compat/create-compat-rule.d.ts +59 -0
- package/dist/rules/compat/create-compat-rule.js +65 -0
- package/dist/rules/compat/declaration-options.d.ts +19 -0
- package/dist/rules/compat/declaration-options.js +110 -0
- package/dist/rules/compat/no-append-all-redirect.d.ts +13 -0
- package/dist/rules/compat/no-append-all-redirect.js +73 -0
- package/dist/rules/compat/no-associative-arrays.d.ts +12 -0
- package/dist/rules/compat/no-associative-arrays.js +39 -0
- package/dist/rules/compat/no-case-attributes.d.ts +12 -0
- package/dist/rules/compat/no-case-attributes.js +63 -0
- package/dist/rules/compat/no-case-fallthrough.d.ts +12 -0
- package/dist/rules/compat/no-case-fallthrough.js +67 -0
- package/dist/rules/compat/no-case-modification.d.ts +35 -0
- package/dist/rules/compat/no-case-modification.js +86 -0
- package/dist/rules/compat/no-coproc.d.ts +12 -0
- package/dist/rules/compat/no-coproc.js +47 -0
- package/dist/rules/compat/no-declare-g.d.ts +12 -0
- package/dist/rules/compat/no-declare-g.js +59 -0
- package/dist/rules/compat/no-double-bracket.d.ts +12 -0
- package/dist/rules/compat/no-double-bracket.js +52 -0
- package/dist/rules/compat/no-epoch-variables.d.ts +12 -0
- package/dist/rules/compat/no-epoch-variables.js +57 -0
- package/dist/rules/compat/no-fd-variable.d.ts +12 -0
- package/dist/rules/compat/no-fd-variable.js +48 -0
- package/dist/rules/compat/no-globstar.d.ts +12 -0
- package/dist/rules/compat/no-globstar.js +39 -0
- package/dist/rules/compat/no-lastpipe-semantics.d.ts +12 -0
- package/dist/rules/compat/no-lastpipe-semantics.js +53 -0
- package/dist/rules/compat/no-let.d.ts +12 -0
- package/dist/rules/compat/no-let.js +62 -0
- package/dist/rules/compat/no-mapfile.d.ts +13 -0
- package/dist/rules/compat/no-mapfile.js +122 -0
- package/dist/rules/compat/no-nameref.d.ts +12 -0
- package/dist/rules/compat/no-nameref.js +44 -0
- package/dist/rules/compat/no-negative-array-index.d.ts +13 -0
- package/dist/rules/compat/no-negative-array-index.js +123 -0
- package/dist/rules/compat/no-nounset-empty-array.d.ts +13 -0
- package/dist/rules/compat/no-nounset-empty-array.js +158 -0
- package/dist/rules/compat/no-parameter-transformation.d.ts +13 -0
- package/dist/rules/compat/no-parameter-transformation.js +123 -0
- package/dist/rules/compat/no-pipe-all.d.ts +13 -0
- package/dist/rules/compat/no-pipe-all.js +68 -0
- package/dist/rules/compat/no-printf-time-format.d.ts +12 -0
- package/dist/rules/compat/no-printf-time-format.js +99 -0
- package/dist/rules/compat/no-source.d.ts +13 -0
- package/dist/rules/compat/no-source.js +103 -0
- package/dist/rules/compat/no-test-v.d.ts +12 -0
- package/dist/rules/compat/no-test-v.js +280 -0
- package/dist/rules/compat/no-unicode-escape.d.ts +14 -0
- package/dist/rules/compat/no-unicode-escape.js +120 -0
- package/dist/rules/compat/no-wait-n.d.ts +12 -0
- package/dist/rules/compat/no-wait-n.js +73 -0
- package/dist/rules/compat/operator-range.d.ts +10 -0
- package/dist/rules/compat/operator-range.js +15 -0
- package/dist/rules/compat/posix-absent-command.d.ts +12 -0
- package/dist/rules/compat/posix-absent-command.js +134 -0
- package/dist/rules/compat/shopt-options.d.ts +9 -0
- package/dist/rules/compat/shopt-options.js +63 -0
- package/dist/rules/compat/version-gates.d.ts +76 -0
- package/dist/rules/compat/version-gates.js +197 -0
- package/dist/rules/create-rule.d.ts +91 -0
- package/dist/rules/create-rule.js +51 -0
- package/dist/rules/create-wrapper-rule.d.ts +166 -0
- package/dist/rules/create-wrapper-rule.js +407 -0
- package/dist/rules/define-shell-rule.d.ts +184 -0
- package/dist/rules/define-shell-rule.js +56 -0
- package/dist/rules/index.d.ts +26 -0
- package/dist/rules/index.js +88 -0
- package/dist/rules/max-script-complexity.d.ts +28 -0
- package/dist/rules/max-script-complexity.js +130 -0
- package/dist/rules/no-dynamic-invocation.d.ts +35 -0
- package/dist/rules/no-dynamic-invocation.js +117 -0
- package/dist/rules/no-eval.d.ts +13 -0
- package/dist/rules/no-eval.js +89 -0
- package/dist/rules/no-masked-exit-status.d.ts +21 -0
- package/dist/rules/no-masked-exit-status.js +667 -0
- package/dist/rules/no-secret-env-reads.d.ts +24 -0
- package/dist/rules/no-secret-env-reads.js +133 -0
- package/dist/rules/no-secret-flags.d.ts +32 -0
- package/dist/rules/no-secret-flags.js +162 -0
- package/dist/rules/no-unchecked-cd.d.ts +44 -0
- package/dist/rules/no-unchecked-cd.js +250 -0
- package/dist/rules/no-undeclared-commands.d.ts +20 -0
- package/dist/rules/no-undeclared-commands.js +115 -0
- package/dist/rules/no-unused-declarations.d.ts +23 -0
- package/dist/rules/no-unused-declarations.js +259 -0
- package/dist/rules/prefer-local.d.ts +16 -0
- package/dist/rules/prefer-local.js +249 -0
- package/dist/rules/quote-expansions.d.ts +13 -0
- package/dist/rules/quote-expansions.js +229 -0
- package/dist/rules/require-network-wrapper.d.ts +40 -0
- package/dist/rules/require-network-wrapper.js +244 -0
- package/dist/rules/require-shebang.d.ts +29 -0
- package/dist/rules/require-shebang.js +101 -0
- package/dist/rules/require-strict-mode.d.ts +52 -0
- package/dist/rules/require-strict-mode.js +177 -0
- package/dist/rules/require-tty-guard.d.ts +13 -0
- package/dist/rules/require-tty-guard.js +289 -0
- package/dist/rules/resolve-declaration-convention.d.ts +96 -0
- package/dist/rules/resolve-declaration-convention.js +109 -0
- package/dist/rules/resolve-network-wrapper.d.ts +19 -0
- package/dist/rules/resolve-network-wrapper.js +116 -0
- package/dist/rules/set-options.d.ts +109 -0
- package/dist/rules/set-options.js +201 -0
- package/dist/rules/shebang-interpreter.d.ts +18 -0
- package/dist/rules/shebang-interpreter.js +81 -0
- package/dist/rules/shebang-matches-dialect.d.ts +12 -0
- package/dist/rules/shebang-matches-dialect.js +69 -0
- package/dist/rules/transparent-wrappers.d.ts +52 -0
- package/dist/rules/transparent-wrappers.js +99 -0
- package/dist/rules/user-pattern.d.ts +63 -0
- package/dist/rules/user-pattern.js +184 -0
- package/dist/syntax/analyze.d.ts +19 -0
- package/dist/syntax/analyze.js +21 -0
- package/dist/syntax/index.d.ts +23 -0
- package/dist/syntax/index.js +25 -0
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -1
- package/docs/compat.md +347 -0
- package/docs/custom-rule-tutorial.md +780 -34
- package/docs/declaration-conventions.md +478 -0
- package/docs/examples/audit-wrapper-pack.mjs +15 -0
- package/docs/examples/dep-line-convention.mjs +22 -0
- package/docs/examples/eslint.config.mjs +3 -2
- package/docs/examples/no-and-or-as-if-rule.mjs +61 -0
- package/docs/examples/no-backticks-rule-tester.mjs +106 -0
- package/docs/examples/no-backticks-rule.mjs +60 -0
- package/docs/examples/no-curl-in-functions-rule.mjs +53 -0
- package/docs/examples/no-function-keyword-rule.mjs +59 -0
- package/docs/examples/no-listed-commands-rule.ts +63 -0
- package/docs/examples/no-rm-rf-rule.mjs +26 -15
- package/docs/examples/no-unquoted-rm-variable-rule.ts +54 -0
- package/docs/examples/sidecar-convention.mjs +38 -0
- package/docs/examples/sidecar-snapshot-convention.mjs +26 -0
- package/docs/examples/sorted-declarations-rule.mjs +52 -0
- package/docs/reading-nodes.md +159 -0
- package/docs/rules/README.md +78 -0
- package/docs/rules/_template.md +83 -0
- package/docs/rules/compat/no-append-all-redirect.md +87 -0
- package/docs/rules/compat/no-associative-arrays.md +95 -0
- package/docs/rules/compat/no-case-attributes.md +82 -0
- package/docs/rules/compat/no-case-fallthrough.md +90 -0
- package/docs/rules/compat/no-case-modification.md +74 -0
- package/docs/rules/compat/no-coproc.md +76 -0
- package/docs/rules/compat/no-declare-g.md +80 -0
- package/docs/rules/compat/no-double-bracket.md +72 -0
- package/docs/rules/compat/no-epoch-variables.md +79 -0
- package/docs/rules/compat/no-fd-variable.md +78 -0
- package/docs/rules/compat/no-globstar.md +74 -0
- package/docs/rules/compat/no-lastpipe-semantics.md +73 -0
- package/docs/rules/compat/no-let.md +78 -0
- package/docs/rules/compat/no-mapfile.md +118 -0
- package/docs/rules/compat/no-nameref.md +81 -0
- package/docs/rules/compat/no-negative-array-index.md +87 -0
- package/docs/rules/compat/no-nounset-empty-array.md +103 -0
- package/docs/rules/compat/no-parameter-transformation.md +94 -0
- package/docs/rules/compat/no-pipe-all.md +85 -0
- package/docs/rules/compat/no-printf-time-format.md +75 -0
- package/docs/rules/compat/no-source.md +98 -0
- package/docs/rules/compat/no-test-v.md +82 -0
- package/docs/rules/compat/no-unicode-escape.md +91 -0
- package/docs/rules/compat/no-wait-n.md +85 -0
- package/docs/rules/max-script-complexity.md +109 -0
- package/docs/rules/no-dynamic-invocation.md +139 -0
- package/docs/rules/no-eval.md +106 -0
- package/docs/rules/no-masked-exit-status.md +239 -0
- package/docs/rules/no-secret-env-reads.md +173 -0
- package/docs/rules/no-secret-flags.md +169 -0
- package/docs/rules/no-unchecked-cd.md +146 -0
- package/docs/rules/no-undeclared-commands.md +141 -0
- package/docs/rules/no-unused-declarations.md +216 -0
- package/docs/rules/prefer-local.md +221 -0
- package/docs/rules/quote-expansions.md +157 -0
- package/docs/rules/require-network-wrapper.md +171 -0
- package/docs/rules/require-shebang.md +157 -0
- package/docs/rules/require-strict-mode.md +253 -0
- package/docs/rules/require-tty-guard.md +128 -0
- package/docs/rules/shebang-matches-dialect.md +120 -0
- package/docs/selectors.md +2 -2
- package/docs/shellcheck.md +86 -0
- package/docs/wrapper-rules.md +206 -0
- package/package.json +26 -20
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/language/selector-classes.d.ts.map +0 -1
- package/dist/language/selector-classes.js.map +0 -1
- package/dist/language/shell-language-options.d.ts.map +0 -1
- package/dist/language/shell-language-options.js.map +0 -1
- package/dist/language/shell-language.d.ts.map +0 -1
- package/dist/language/shell-language.js.map +0 -1
- package/dist/language/shell-source-code.d.ts.map +0 -1
- package/dist/language/shell-source-code.js.map +0 -1
- package/dist/language/source-coordinates.d.ts.map +0 -1
- package/dist/language/source-coordinates.js.map +0 -1
- package/dist/language/syntax-view.d.ts.map +0 -1
- package/dist/language/syntax-view.js.map +0 -1
- package/dist/language/word-resolution.d.ts.map +0 -1
- package/dist/language/word-resolution.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { noAppendAllRedirect } from './compat/no-append-all-redirect.js';
|
|
2
|
+
import { noAssociativeArrays } from './compat/no-associative-arrays.js';
|
|
3
|
+
import { noCaseAttributes } from './compat/no-case-attributes.js';
|
|
4
|
+
import { noCaseFallthrough } from './compat/no-case-fallthrough.js';
|
|
5
|
+
import { noCaseModification } from './compat/no-case-modification.js';
|
|
6
|
+
import { noCoproc } from './compat/no-coproc.js';
|
|
7
|
+
import { noDeclareG } from './compat/no-declare-g.js';
|
|
8
|
+
import { noDoubleBracket } from './compat/no-double-bracket.js';
|
|
9
|
+
import { noEpochVariables } from './compat/no-epoch-variables.js';
|
|
10
|
+
import { noFdVariable } from './compat/no-fd-variable.js';
|
|
11
|
+
import { noGlobstar } from './compat/no-globstar.js';
|
|
12
|
+
import { noLastpipeSemantics } from './compat/no-lastpipe-semantics.js';
|
|
13
|
+
import { noLet } from './compat/no-let.js';
|
|
14
|
+
import { noMapfile } from './compat/no-mapfile.js';
|
|
15
|
+
import { noNameref } from './compat/no-nameref.js';
|
|
16
|
+
import { noNegativeArrayIndex } from './compat/no-negative-array-index.js';
|
|
17
|
+
import { noNounsetEmptyArray } from './compat/no-nounset-empty-array.js';
|
|
18
|
+
import { noParameterTransformation } from './compat/no-parameter-transformation.js';
|
|
19
|
+
import { noPipeAll } from './compat/no-pipe-all.js';
|
|
20
|
+
import { noPrintfTimeFormat } from './compat/no-printf-time-format.js';
|
|
21
|
+
import { noSource } from './compat/no-source.js';
|
|
22
|
+
import { noTestV } from './compat/no-test-v.js';
|
|
23
|
+
import { noUnicodeEscape } from './compat/no-unicode-escape.js';
|
|
24
|
+
import { noWaitN } from './compat/no-wait-n.js';
|
|
25
|
+
import { maxScriptComplexity } from './max-script-complexity.js';
|
|
26
|
+
import { noDynamicInvocation } from './no-dynamic-invocation.js';
|
|
27
|
+
import { noEval } from './no-eval.js';
|
|
28
|
+
import { noMaskedExitStatus } from './no-masked-exit-status.js';
|
|
29
|
+
import { noSecretEnvReads } from './no-secret-env-reads.js';
|
|
30
|
+
import { noSecretFlags } from './no-secret-flags.js';
|
|
31
|
+
import { noUncheckedCd } from './no-unchecked-cd.js';
|
|
32
|
+
import { noUndeclaredCommands } from './no-undeclared-commands.js';
|
|
33
|
+
import { noUnusedDeclarations } from './no-unused-declarations.js';
|
|
34
|
+
import { preferLocal } from './prefer-local.js';
|
|
35
|
+
import { quoteExpansions } from './quote-expansions.js';
|
|
36
|
+
import { requireNetworkWrapper } from './require-network-wrapper.js';
|
|
37
|
+
import { requireShebang } from './require-shebang.js';
|
|
38
|
+
import { requireStrictMode } from './require-strict-mode.js';
|
|
39
|
+
import { requireTtyGuard } from './require-tty-guard.js';
|
|
40
|
+
import { shebangMatchesDialect } from './shebang-matches-dialect.js';
|
|
41
|
+
/**
|
|
42
|
+
* Built-in rules keyed by rule name (without the `sh/` namespace a user writes
|
|
43
|
+
* in their config).
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
export const rules = {
|
|
48
|
+
'compat/no-append-all-redirect': noAppendAllRedirect,
|
|
49
|
+
'compat/no-associative-arrays': noAssociativeArrays,
|
|
50
|
+
'compat/no-case-attributes': noCaseAttributes,
|
|
51
|
+
'compat/no-case-fallthrough': noCaseFallthrough,
|
|
52
|
+
'compat/no-case-modification': noCaseModification,
|
|
53
|
+
'compat/no-coproc': noCoproc,
|
|
54
|
+
'compat/no-declare-g': noDeclareG,
|
|
55
|
+
'compat/no-double-bracket': noDoubleBracket,
|
|
56
|
+
'compat/no-epoch-variables': noEpochVariables,
|
|
57
|
+
'compat/no-fd-variable': noFdVariable,
|
|
58
|
+
'compat/no-globstar': noGlobstar,
|
|
59
|
+
'compat/no-lastpipe-semantics': noLastpipeSemantics,
|
|
60
|
+
'compat/no-let': noLet,
|
|
61
|
+
'compat/no-mapfile': noMapfile,
|
|
62
|
+
'compat/no-nameref': noNameref,
|
|
63
|
+
'compat/no-negative-array-index': noNegativeArrayIndex,
|
|
64
|
+
'compat/no-nounset-empty-array': noNounsetEmptyArray,
|
|
65
|
+
'compat/no-parameter-transformation': noParameterTransformation,
|
|
66
|
+
'compat/no-pipe-all': noPipeAll,
|
|
67
|
+
'compat/no-printf-time-format': noPrintfTimeFormat,
|
|
68
|
+
'compat/no-source': noSource,
|
|
69
|
+
'compat/no-test-v': noTestV,
|
|
70
|
+
'compat/no-unicode-escape': noUnicodeEscape,
|
|
71
|
+
'compat/no-wait-n': noWaitN,
|
|
72
|
+
'max-script-complexity': maxScriptComplexity,
|
|
73
|
+
'no-dynamic-invocation': noDynamicInvocation,
|
|
74
|
+
'no-eval': noEval,
|
|
75
|
+
'no-masked-exit-status': noMaskedExitStatus,
|
|
76
|
+
'no-secret-env-reads': noSecretEnvReads,
|
|
77
|
+
'no-secret-flags': noSecretFlags,
|
|
78
|
+
'no-unchecked-cd': noUncheckedCd,
|
|
79
|
+
'no-undeclared-commands': noUndeclaredCommands,
|
|
80
|
+
'no-unused-declarations': noUnusedDeclarations,
|
|
81
|
+
'prefer-local': preferLocal,
|
|
82
|
+
'quote-expansions': quoteExpansions,
|
|
83
|
+
'require-network-wrapper': requireNetworkWrapper,
|
|
84
|
+
'require-shebang': requireShebang,
|
|
85
|
+
'require-strict-mode': requireStrictMode,
|
|
86
|
+
'require-tty-guard': requireTtyGuard,
|
|
87
|
+
'shebang-matches-dialect': shebangMatchesDialect,
|
|
88
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rule's single options object, as written in a config. Each field is a
|
|
3
|
+
* non-negative integer limit (`0` allows none of that construct), `null` to
|
|
4
|
+
* switch that one limit off, or omitted to take the default.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export interface MaxScriptComplexityOptions {
|
|
9
|
+
/** Most function declarations a file may contain (default 20). */
|
|
10
|
+
readonly maxFunctions?: number | null;
|
|
11
|
+
/** Deepest control-structure nesting a file may contain (default 4). */
|
|
12
|
+
readonly maxNesting?: number | null;
|
|
13
|
+
/** Most statements a file may contain (default 300). */
|
|
14
|
+
readonly maxStatements?: number | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The rule's report kinds, one per limit: `tooManyFunctions`,
|
|
18
|
+
* `tooMuchNesting`, `tooManyStatements`.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export type MaxScriptComplexityMessageId = 'tooManyFunctions' | 'tooMuchNesting' | 'tooManyStatements';
|
|
23
|
+
/**
|
|
24
|
+
* The `sh/max-script-complexity` rule module.
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
export declare const maxScriptComplexity: import("./define-shell-rule.js").ShellRule<[MaxScriptComplexityOptions], MaxScriptComplexityMessageId>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { createRule } from './create-rule.js';
|
|
2
|
+
/** The registry name of this rule (`sh/max-script-complexity` in a user's config). */
|
|
3
|
+
const RULE_NAME = 'max-script-complexity';
|
|
4
|
+
/** The limits applied when an option is omitted. */
|
|
5
|
+
const DEFAULT_LIMITS = {
|
|
6
|
+
maxFunctions: 20,
|
|
7
|
+
maxNesting: 4,
|
|
8
|
+
maxStatements: 300,
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Resolve one configured limit: an omitted field takes the default, while an
|
|
12
|
+
* explicit `null` stays `null` (the limit is off). `??` is deliberately not
|
|
13
|
+
* used because it would turn `null` back into the default.
|
|
14
|
+
*/
|
|
15
|
+
function resolveLimit(configured, fallback) {
|
|
16
|
+
return configured === undefined ? fallback : configured;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Selectors for every construct that adds one nesting level. `elif`/`else`
|
|
20
|
+
* arms are `IfClause` children of an `IfClause` and so are excluded, and a
|
|
21
|
+
* function's body braces (`FuncDecl > Stmt > Block`) are the function's
|
|
22
|
+
* syntax, not a brace group.
|
|
23
|
+
*/
|
|
24
|
+
const NESTING_SELECTORS = [
|
|
25
|
+
'IfClause:not(IfClause > IfClause)',
|
|
26
|
+
'ForClause',
|
|
27
|
+
'WhileClause',
|
|
28
|
+
'CaseClause',
|
|
29
|
+
'Subshell',
|
|
30
|
+
'Block:not(FuncDecl > Stmt > Block)',
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Selector for a counted statement: not an operand of `&&`, `||` or `|`, and
|
|
34
|
+
* not a function's body wrapper (the declaration is already one statement).
|
|
35
|
+
*/
|
|
36
|
+
const STATEMENT_SELECTOR = 'Stmt:not(BinaryCmd > Stmt):not(FuncDecl > Stmt)';
|
|
37
|
+
/**
|
|
38
|
+
* The `sh/max-script-complexity` rule module.
|
|
39
|
+
*
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
export const maxScriptComplexity = createRule({
|
|
43
|
+
name: RULE_NAME,
|
|
44
|
+
meta: {
|
|
45
|
+
type: 'suggestion',
|
|
46
|
+
docs: {
|
|
47
|
+
description: 'Cap the number of functions, the nesting depth and the statement count of a script',
|
|
48
|
+
},
|
|
49
|
+
schema: [
|
|
50
|
+
{
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
maxFunctions: { type: ['integer', 'null'], minimum: 0 },
|
|
54
|
+
maxNesting: { type: ['integer', 'null'], minimum: 0 },
|
|
55
|
+
maxStatements: { type: ['integer', 'null'], minimum: 0 },
|
|
56
|
+
},
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
defaultOptions: [{ ...DEFAULT_LIMITS }],
|
|
61
|
+
messages: {
|
|
62
|
+
tooManyFunctions: 'This script declares {{count}} functions, more than the {{max}} allowed. Split it into smaller scripts.',
|
|
63
|
+
tooMuchNesting: 'This construct is nested {{depth}} levels deep, more than the {{max}} allowed. Flatten it or move the inner part into a function.',
|
|
64
|
+
tooManyStatements: 'This script has more than {{max}} statements. Split it into smaller scripts.',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
create(context) {
|
|
68
|
+
const { sourceCode } = context;
|
|
69
|
+
const [options] = context.options;
|
|
70
|
+
const maxFunctions = resolveLimit(options.maxFunctions, DEFAULT_LIMITS.maxFunctions);
|
|
71
|
+
const maxNesting = resolveLimit(options.maxNesting, DEFAULT_LIMITS.maxNesting);
|
|
72
|
+
const maxStatements = resolveLimit(options.maxStatements, DEFAULT_LIMITS.maxStatements);
|
|
73
|
+
let functionCount = 0;
|
|
74
|
+
let depth = 0;
|
|
75
|
+
let nestingReported = false;
|
|
76
|
+
let statementCount = 0;
|
|
77
|
+
let statementsReported = false;
|
|
78
|
+
const handlers = {
|
|
79
|
+
FuncDecl() {
|
|
80
|
+
functionCount++;
|
|
81
|
+
},
|
|
82
|
+
[STATEMENT_SELECTOR](node) {
|
|
83
|
+
statementCount++;
|
|
84
|
+
if (maxStatements !== null && statementCount > maxStatements && !statementsReported) {
|
|
85
|
+
statementsReported = true;
|
|
86
|
+
context.report({
|
|
87
|
+
node,
|
|
88
|
+
messageId: 'tooManyStatements',
|
|
89
|
+
data: { max: String(maxStatements) },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
'File:exit'() {
|
|
94
|
+
if (maxFunctions === null || functionCount <= maxFunctions) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Line 1 in full, from the line's own length (as in `require-shebang`).
|
|
98
|
+
const firstLine = sourceCode.lines[0] ?? '';
|
|
99
|
+
context.report({
|
|
100
|
+
loc: {
|
|
101
|
+
start: { line: 1, column: 1 },
|
|
102
|
+
end: { line: 1, column: firstLine.length + 1 },
|
|
103
|
+
},
|
|
104
|
+
messageId: 'tooManyFunctions',
|
|
105
|
+
data: { count: String(functionCount), max: String(maxFunctions) },
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
// Entry and exit must be registered per selector: ESLint reads `:exit`
|
|
110
|
+
// only as a trailing marker on a whole selector string, so a comma-joined
|
|
111
|
+
// list would pair one exit handler with just its last alternative.
|
|
112
|
+
for (const selector of NESTING_SELECTORS) {
|
|
113
|
+
handlers[selector] = (node) => {
|
|
114
|
+
depth++;
|
|
115
|
+
if (maxNesting !== null && depth > maxNesting && !nestingReported) {
|
|
116
|
+
nestingReported = true;
|
|
117
|
+
context.report({
|
|
118
|
+
node,
|
|
119
|
+
messageId: 'tooMuchNesting',
|
|
120
|
+
data: { depth: String(depth), max: String(maxNesting) },
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
handlers[`${selector}:exit`] = () => {
|
|
125
|
+
depth--;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return handlers;
|
|
129
|
+
},
|
|
130
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message ids this rule reports with: `dynamicInvocation` when the command
|
|
3
|
+
* that runs is not statically known, and `embeddedCommand` when the command
|
|
4
|
+
* runs code embedded in a string (`bash -c …`).
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export type NoDynamicInvocationMessageId = 'dynamicInvocation' | 'embeddedCommand';
|
|
9
|
+
/**
|
|
10
|
+
* The rule's single options object.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export interface NoDynamicInvocationOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Names of dispatcher functions. A dynamic invocation lexically inside a
|
|
17
|
+
* function with one of these names, at any depth, does not report
|
|
18
|
+
* `dynamicInvocation`. Embedded commands still report.
|
|
19
|
+
*/
|
|
20
|
+
readonly allowInFunctions?: readonly string[];
|
|
21
|
+
/**
|
|
22
|
+
* Command names to treat as transparent wrappers, in addition to the
|
|
23
|
+
* built-in list: the word after one is the command that runs. Each is
|
|
24
|
+
* followed by name only, with no options of its own, so a flag after it
|
|
25
|
+
* makes the effective command unknown. A name already in the built-in list
|
|
26
|
+
* keeps its built-in option handling.
|
|
27
|
+
*/
|
|
28
|
+
readonly allowWrappers?: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The `sh/no-dynamic-invocation` rule module.
|
|
32
|
+
*
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
export declare const noDynamicInvocation: import("./define-shell-rule.js").ShellRule<[NoDynamicInvocationOptions], NoDynamicInvocationMessageId>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sh/no-dynamic-invocation`: reports every command whose effective command
|
|
3
|
+
* cannot be statically enumerated, so a script's call graph can be read off
|
|
4
|
+
* its source.
|
|
5
|
+
*
|
|
6
|
+
* This is the load-bearing rule for review-and-sign workflows: a reviewer
|
|
7
|
+
* vouching for a script has to know every command it can run. A command word
|
|
8
|
+
* that is only known at run time (`"$cmd" args`, `${tool} x`,
|
|
9
|
+
* `$(pick-tool) x`, `"${cmd[@]}"`) defeats that, and so does a command
|
|
10
|
+
* string handed to another shell (`bash -c "$x"`, `sh -c '…'`).
|
|
11
|
+
*
|
|
12
|
+
* The rule asks, never decides:
|
|
13
|
+
*
|
|
14
|
+
* - Every command in the file comes from `ShellSourceCode.getCommandSites`,
|
|
15
|
+
* so commands nested in command substitutions, process substitutions,
|
|
16
|
+
* pipelines and function bodies are all checked, each once.
|
|
17
|
+
* - Which command each one runs comes from `resolveInvokedCommand`, which
|
|
18
|
+
* follows transparent wrappers (`exec`, `sudo`, `time`, `env`, …) and never
|
|
19
|
+
* guesses through a word it cannot know. A non-static effective command
|
|
20
|
+
* reports. `'embedded-command'` reports as `embeddedCommand`; every other
|
|
21
|
+
* reason (an expansion, a glob, a tilde, an unrecognized wrapper flag, …)
|
|
22
|
+
* reports as `dynamicInvocation`.
|
|
23
|
+
* - The wrappers followed are `sh-ast/analyze`'s defaults, Bash's `builtin`,
|
|
24
|
+
* shells invoked as commands (so `bash -c` is an embedded command and
|
|
25
|
+
* `bash script.sh` runs `script.sh`), and the names in `allowWrappers`.
|
|
26
|
+
*
|
|
27
|
+
* The analysis is syntactic by design: `cmd=ls; "$cmd"` reports, because
|
|
28
|
+
* resolving it would need dataflow. Aliases need no handling: they are not
|
|
29
|
+
* expanded in non-interactive scripts, so an alias name is an ordinary static
|
|
30
|
+
* command word. The rule offers no fix and no suggestion; choosing the
|
|
31
|
+
* command is a human decision.
|
|
32
|
+
*/
|
|
33
|
+
import { DEFAULT_TRANSPARENT_WRAPPERS } from 'sh-ast/analyze';
|
|
34
|
+
import { createRule } from './create-rule.js';
|
|
35
|
+
import { BUILTIN_WRAPPER, SHELL_WRAPPER } from './transparent-wrappers.js';
|
|
36
|
+
/** The registry name of this rule (`sh/no-dynamic-invocation` in a user's config). */
|
|
37
|
+
const RULE_NAME = 'no-dynamic-invocation';
|
|
38
|
+
/**
|
|
39
|
+
* The wrappers every invocation is followed through before any configured in
|
|
40
|
+
* `allowWrappers`: `sh-ast/analyze`'s defaults, then Bash's `builtin`, then
|
|
41
|
+
* shells invoked as commands. `resolveArgv0` uses the first entry whose name
|
|
42
|
+
* matches, so the built-in entries take precedence over a user's.
|
|
43
|
+
*/
|
|
44
|
+
const BUILT_IN_WRAPPERS = [
|
|
45
|
+
...DEFAULT_TRANSPARENT_WRAPPERS,
|
|
46
|
+
BUILTIN_WRAPPER,
|
|
47
|
+
SHELL_WRAPPER,
|
|
48
|
+
];
|
|
49
|
+
/** Schema for a list of distinct, non-empty names. */
|
|
50
|
+
const NAME_LIST_SCHEMA = {
|
|
51
|
+
type: 'array',
|
|
52
|
+
items: { type: 'string', minLength: 1 },
|
|
53
|
+
uniqueItems: true,
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Whether `context` places a command inside a function named in `names`, at
|
|
57
|
+
* any depth. A function name that is not valid UTF-8 reaches analysis as
|
|
58
|
+
* bytes (`Uint8Array`), and a configured name is always a string, so such a
|
|
59
|
+
* function never matches.
|
|
60
|
+
*/
|
|
61
|
+
function isInsideFunction(context, names) {
|
|
62
|
+
return context.some((frame) => frame.kind === 'function' && typeof frame.name === 'string' && names.has(frame.name));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The `sh/no-dynamic-invocation` rule module.
|
|
66
|
+
*
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export const noDynamicInvocation = createRule({
|
|
70
|
+
name: RULE_NAME,
|
|
71
|
+
meta: {
|
|
72
|
+
type: 'problem',
|
|
73
|
+
docs: {
|
|
74
|
+
description: 'Disallow commands whose executed command cannot be statically determined',
|
|
75
|
+
},
|
|
76
|
+
schema: [
|
|
77
|
+
{
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
allowInFunctions: NAME_LIST_SCHEMA,
|
|
81
|
+
allowWrappers: NAME_LIST_SCHEMA,
|
|
82
|
+
},
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
defaultOptions: [{ allowInFunctions: [], allowWrappers: [] }],
|
|
87
|
+
messages: {
|
|
88
|
+
dynamicInvocation: 'The command this runs is not known until run time, so the script cannot be statically reviewed. Name the command with a literal word instead.',
|
|
89
|
+
embeddedCommand: 'This runs shell code embedded in a string, which cannot be statically reviewed. Write the commands directly instead.',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
create(context) {
|
|
93
|
+
const { sourceCode } = context;
|
|
94
|
+
// `defaultOptions` guarantees the options object; either list may still be omitted in it.
|
|
95
|
+
const [{ allowInFunctions = [], allowWrappers = [] }] = context.options;
|
|
96
|
+
const dispatchers = new Set(allowInFunctions);
|
|
97
|
+
const transparentWrappers = [
|
|
98
|
+
...BUILT_IN_WRAPPERS,
|
|
99
|
+
...allowWrappers.map((name) => ({ names: [name] })),
|
|
100
|
+
];
|
|
101
|
+
return {
|
|
102
|
+
File() {
|
|
103
|
+
for (const site of sourceCode.getCommandSites()) {
|
|
104
|
+
const { effective } = site.resolveInvokedCommand({ transparentWrappers });
|
|
105
|
+
if (effective.static)
|
|
106
|
+
continue;
|
|
107
|
+
if (effective.reason === 'embedded-command') {
|
|
108
|
+
context.report({ node: site.node, messageId: 'embeddedCommand' });
|
|
109
|
+
}
|
|
110
|
+
else if (!isInsideFunction(site.context, dispatchers)) {
|
|
111
|
+
context.report({ node: site.node, messageId: 'dynamicInvocation' });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message ids this rule reports with: `noEval` for any `eval`, and
|
|
3
|
+
* `dynamicSource` for a `source` / `.` whose path word is not static.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export type NoEvalMessageId = 'noEval' | 'dynamicSource';
|
|
8
|
+
/**
|
|
9
|
+
* The `sh/no-eval` rule module.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export declare const noEval: import("./define-shell-rule.js").ShellRule<[], NoEvalMessageId>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sh/no-eval`: reports shell code whose text is only assembled at run time,
|
|
3
|
+
* and therefore cannot be reviewed before it runs.
|
|
4
|
+
*
|
|
5
|
+
* Two constructs break eslint-sh's review contract this way:
|
|
6
|
+
*
|
|
7
|
+
* - **`eval`**, in any form. The arguments `eval` runs are re-parsed as shell
|
|
8
|
+
* code when the line executes, so the code a reviewer reads is not the code
|
|
9
|
+
* that runs. Even `eval "literal"` is reported: the literal is still a second
|
|
10
|
+
* layer of shell parsing that tooling (this linter included) never sees as
|
|
11
|
+
* commands. `eval` is recognized as the *effective* command, after following
|
|
12
|
+
* transparent wrappers (`command eval …`, `builtin eval …`, `exec eval …`)
|
|
13
|
+
* through `ShellSourceCode.resolveInvokedCommand`.
|
|
14
|
+
* - **`source` / `.` of a non-static path.** Loading a file whose path is only
|
|
15
|
+
* known at run time (`source "$DIR/lib.sh"`, `. $(pick)`) runs code the
|
|
16
|
+
* reviewer cannot locate. Like `eval`, it is recognized as the effective
|
|
17
|
+
* command, so `command source "$p"` and `builtin . "$p"` report too; the
|
|
18
|
+
* path is the word after the effective command in the call's arguments. A path word is static exactly when
|
|
19
|
+
* `ShellSourceCode.resolveWordText` resolves it; this rule never decides
|
|
20
|
+
* static-ness itself.
|
|
21
|
+
*
|
|
22
|
+
* Out of scope, by design: a command word that is itself dynamic
|
|
23
|
+
* (`$cmd args`) and embedded command strings (`bash -c "$x"`) belong to
|
|
24
|
+
* `no-dynamic-invocation`. The rule has no options and offers no fix or
|
|
25
|
+
* suggestion, because no mechanical rewrite of `eval` preserves behavior.
|
|
26
|
+
*/
|
|
27
|
+
import { DEFAULT_TRANSPARENT_WRAPPERS } from 'sh-ast/analyze';
|
|
28
|
+
import { createRule } from './create-rule.js';
|
|
29
|
+
import { BUILTIN_WRAPPER } from './transparent-wrappers.js';
|
|
30
|
+
/** The registry name of this rule (`sh/no-eval` in a user's config). */
|
|
31
|
+
const RULE_NAME = 'no-eval';
|
|
32
|
+
/**
|
|
33
|
+
* The wrappers followed to find the command an invocation actually runs:
|
|
34
|
+
* `sh-ast/analyze`'s defaults (`command`, `exec`, `env`, `sudo`, …) plus
|
|
35
|
+
* Bash's `builtin`, so `builtin eval …` is recognized as `eval`.
|
|
36
|
+
*/
|
|
37
|
+
const TRANSPARENT_WRAPPERS = [
|
|
38
|
+
...DEFAULT_TRANSPARENT_WRAPPERS,
|
|
39
|
+
BUILTIN_WRAPPER,
|
|
40
|
+
];
|
|
41
|
+
/** The command names that load and run another file in the current shell. */
|
|
42
|
+
const SOURCE_COMMANDS = new Set(['source', '.']);
|
|
43
|
+
/**
|
|
44
|
+
* The `sh/no-eval` rule module.
|
|
45
|
+
*
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
export const noEval = createRule({
|
|
49
|
+
name: RULE_NAME,
|
|
50
|
+
meta: {
|
|
51
|
+
type: 'problem',
|
|
52
|
+
docs: {
|
|
53
|
+
description: 'Disallow `eval`, and `source` or `.` of a path that is not a static literal',
|
|
54
|
+
},
|
|
55
|
+
schema: [],
|
|
56
|
+
messages: {
|
|
57
|
+
noEval: '`eval` runs text as shell code at run time, so the code it executes cannot be statically reviewed. Write the commands directly instead.',
|
|
58
|
+
dynamicSource: '`{{command}}` loads a path that is not a static literal, so the code it runs cannot be statically reviewed. Use a literal path instead.',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
create(context) {
|
|
62
|
+
const { sourceCode } = context;
|
|
63
|
+
return {
|
|
64
|
+
CallExpr(node) {
|
|
65
|
+
const invoked = sourceCode.resolveInvokedCommand(node, {
|
|
66
|
+
transparentWrappers: TRANSPARENT_WRAPPERS,
|
|
67
|
+
});
|
|
68
|
+
if (!invoked?.effective.static) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const command = invoked.effective.text;
|
|
72
|
+
if (command === 'eval') {
|
|
73
|
+
context.report({ node, messageId: 'noEval' });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!SOURCE_COMMANDS.has(command)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// The path is the word right after the effective command, wherever the
|
|
80
|
+
// wrapper chain placed that command in the call's arguments.
|
|
81
|
+
const effectiveIndex = invoked.chainIndices.at(-1);
|
|
82
|
+
const path = effectiveIndex === undefined ? undefined : node.args[effectiveIndex + 1];
|
|
83
|
+
if (path !== undefined && sourceCode.resolveWordText(path) === null) {
|
|
84
|
+
context.report({ node: path, messageId: 'dynamicSource', data: { command } });
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rule's single options object.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export interface NoMaskedExitStatusOptions {
|
|
7
|
+
/** Report only where `errexit` is known to be on. Defaults to `false`. */
|
|
8
|
+
readonly onlyWithErrexit?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* With `onlyWithErrexit`, count options on the shebang line (`#!/bin/bash -e`)
|
|
11
|
+
* as enabled, as `require-strict-mode`'s option of the same name does.
|
|
12
|
+
* Defaults to `false`.
|
|
13
|
+
*/
|
|
14
|
+
readonly allowShebangOnly?: boolean;
|
|
15
|
+
/** The declaration builtins to check. Defaults to all five. */
|
|
16
|
+
readonly builtins?: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
/** Message ids this rule reports with. @internal */
|
|
19
|
+
export type NoMaskedExitStatusMessageId = 'maskedExitStatus' | 'declareThenAssign' | 'assignThenExport' | 'assignThenReadonly';
|
|
20
|
+
/** Built-in rule `sh/no-masked-exit-status`. @internal */
|
|
21
|
+
export declare const noMaskedExitStatus: import("./define-shell-rule.js").ShellRule<[NoMaskedExitStatusOptions], NoMaskedExitStatusMessageId>;
|