@eslint-sh/eslint-plugin 0.6.0 → 0.8.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 (147) hide show
  1. package/README.md +19 -8
  2. package/dist/command-harvest/builtins/bash.d.ts +1 -1
  3. package/dist/command-harvest/builtins/bash.js +1 -1
  4. package/dist/command-harvest/builtins/bats.d.ts +2 -0
  5. package/dist/command-harvest/builtins/bats.js +3 -0
  6. package/dist/command-harvest/wrapper-table.d.ts +3 -19
  7. package/dist/command-harvest/wrapper-table.js +8 -38
  8. package/dist/configs/bash-3-2.d.ts +7 -6
  9. package/dist/configs/bash-3-2.js +5 -3
  10. package/dist/configs/posix-portable.d.ts +1 -1
  11. package/dist/configs/posix-portable.js +1 -0
  12. package/dist/declaration-conventions/read-declared-commands.d.ts +2 -2
  13. package/dist/declaration-conventions/read-declared-commands.js +2 -2
  14. package/dist/eslint-plugin-syntax.d.ts +4 -0
  15. package/dist/eslint-plugin.d.ts +86 -40
  16. package/dist/index.d.ts +5 -2
  17. package/dist/index.js +4 -2
  18. package/dist/language/shell-language-options.d.ts +18 -15
  19. package/dist/language/shell-language-options.js +33 -26
  20. package/dist/language/shell-language.d.ts +10 -9
  21. package/dist/language/shell-language.js +18 -35
  22. package/dist/language/shell-source-code.d.ts +17 -9
  23. package/dist/language/shell-source-code.js +53 -12
  24. package/dist/rules/bash-variable-semantics.d.ts +22 -0
  25. package/dist/rules/bash-variable-semantics.js +41 -0
  26. package/dist/rules/compat/create-compat-rule.js +1 -1
  27. package/dist/rules/compat/declaration-options.d.ts +2 -2
  28. package/dist/rules/compat/declaration-options.js +5 -2
  29. package/dist/rules/compat/no-append-all-redirect.js +1 -1
  30. package/dist/rules/compat/no-associative-arrays.js +2 -2
  31. package/dist/rules/compat/no-brace-sequence-padding-step.d.ts +15 -0
  32. package/dist/rules/compat/no-brace-sequence-padding-step.js +287 -0
  33. package/dist/rules/compat/no-case-attributes.js +3 -3
  34. package/dist/rules/compat/no-case-fallthrough.js +2 -2
  35. package/dist/rules/compat/no-case-modification.js +2 -2
  36. package/dist/rules/compat/no-coproc.js +1 -1
  37. package/dist/rules/compat/no-declare-g.js +3 -3
  38. package/dist/rules/compat/no-double-bracket.js +2 -2
  39. package/dist/rules/compat/no-epoch-variables.js +2 -2
  40. package/dist/rules/compat/no-fd-variable.js +1 -1
  41. package/dist/rules/compat/no-globstar.js +1 -1
  42. package/dist/rules/compat/no-lastpipe-semantics.js +2 -2
  43. package/dist/rules/compat/no-let.js +2 -1
  44. package/dist/rules/compat/no-mapfile.js +4 -2
  45. package/dist/rules/compat/no-nameref.js +3 -3
  46. package/dist/rules/compat/no-negative-array-index.js +39 -9
  47. package/dist/rules/compat/no-nounset-empty-array.js +15 -4
  48. package/dist/rules/compat/no-parameter-transformation.js +2 -2
  49. package/dist/rules/compat/no-pipe-all.js +1 -1
  50. package/dist/rules/compat/no-printf-time-format.js +4 -2
  51. package/dist/rules/compat/no-source.js +2 -1
  52. package/dist/rules/compat/no-test-v.js +4 -2
  53. package/dist/rules/compat/no-unicode-escape.js +2 -2
  54. package/dist/rules/compat/no-wait-n.js +4 -2
  55. package/dist/rules/compat/nounset-array-proof.d.ts +8 -0
  56. package/dist/rules/compat/nounset-array-proof.js +563 -0
  57. package/dist/rules/compat/posix-absent-command.d.ts +7 -8
  58. package/dist/rules/compat/posix-absent-command.js +17 -15
  59. package/dist/rules/compat/shopt-options.js +3 -1
  60. package/dist/rules/compat/version-gates.d.ts +10 -10
  61. package/dist/rules/compat/version-gates.js +21 -13
  62. package/dist/rules/create-wrapper-rule.js +12 -7
  63. package/dist/rules/index.js +2 -0
  64. package/dist/rules/no-dynamic-invocation.d.ts +0 -8
  65. package/dist/rules/no-dynamic-invocation.js +7 -15
  66. package/dist/rules/no-eval.js +3 -1
  67. package/dist/rules/no-masked-exit-status.js +15 -8
  68. package/dist/rules/no-secret-env-reads.d.ts +4 -3
  69. package/dist/rules/no-secret-env-reads.js +37 -14
  70. package/dist/rules/no-secret-flags.d.ts +2 -1
  71. package/dist/rules/no-secret-flags.js +7 -8
  72. package/dist/rules/no-unchecked-cd.js +3 -1
  73. package/dist/rules/no-undeclared-commands.d.ts +1 -15
  74. package/dist/rules/no-undeclared-commands.js +3 -15
  75. package/dist/rules/no-unused-declarations.d.ts +1 -18
  76. package/dist/rules/no-unused-declarations.js +9 -34
  77. package/dist/rules/prefer-local.js +4 -3
  78. package/dist/rules/quote-expansions.js +3 -28
  79. package/dist/rules/require-network-wrapper.d.ts +2 -17
  80. package/dist/rules/require-network-wrapper.js +33 -4
  81. package/dist/rules/require-tty-guard.js +30 -1
  82. package/dist/rules/resolve-declaration-convention.d.ts +5 -6
  83. package/dist/rules/resolve-declaration-convention.js +5 -6
  84. package/dist/rules/resolve-network-wrapper.d.ts +19 -0
  85. package/dist/rules/resolve-network-wrapper.js +57 -26
  86. package/dist/rules/resolve-transparent-wrappers.d.ts +43 -0
  87. package/dist/rules/resolve-transparent-wrappers.js +98 -0
  88. package/dist/rules/set-options.d.ts +2 -1
  89. package/dist/rules/set-options.js +17 -5
  90. package/dist/rules/shebang-interpreter.js +4 -2
  91. package/dist/rules/transparent-wrappers.d.ts +4 -3
  92. package/dist/rules/transparent-wrappers.js +5 -4
  93. package/dist/rules/user-pattern.d.ts +5 -3
  94. package/dist/rules/user-pattern.js +6 -4
  95. package/dist/syntax/index.d.ts +4 -0
  96. package/dist/syntax/index.js +4 -0
  97. package/docs/compat.md +90 -34
  98. package/docs/custom-rule-tutorial.md +9 -3
  99. package/docs/examples/no-listed-commands-rule.ts +2 -2
  100. package/docs/rules/README.md +38 -5
  101. package/docs/rules/_template.md +2 -2
  102. package/docs/rules/compat/no-append-all-redirect.md +12 -12
  103. package/docs/rules/compat/no-associative-arrays.md +14 -14
  104. package/docs/rules/compat/no-brace-sequence-padding-step.md +90 -0
  105. package/docs/rules/compat/no-case-attributes.md +13 -13
  106. package/docs/rules/compat/no-case-fallthrough.md +12 -12
  107. package/docs/rules/compat/no-case-modification.md +11 -11
  108. package/docs/rules/compat/no-coproc.md +13 -13
  109. package/docs/rules/compat/no-declare-g.md +13 -13
  110. package/docs/rules/compat/no-double-bracket.md +2 -2
  111. package/docs/rules/compat/no-epoch-variables.md +13 -13
  112. package/docs/rules/compat/no-fd-variable.md +12 -12
  113. package/docs/rules/compat/no-globstar.md +13 -13
  114. package/docs/rules/compat/no-lastpipe-semantics.md +13 -13
  115. package/docs/rules/compat/no-let.md +2 -2
  116. package/docs/rules/compat/no-mapfile.md +16 -16
  117. package/docs/rules/compat/no-nameref.md +13 -13
  118. package/docs/rules/compat/no-negative-array-index.md +14 -14
  119. package/docs/rules/compat/no-nounset-empty-array.md +72 -17
  120. package/docs/rules/compat/no-parameter-transformation.md +19 -19
  121. package/docs/rules/compat/no-pipe-all.md +13 -13
  122. package/docs/rules/compat/no-printf-time-format.md +13 -13
  123. package/docs/rules/compat/no-source.md +2 -2
  124. package/docs/rules/compat/no-test-v.md +12 -12
  125. package/docs/rules/compat/no-unicode-escape.md +13 -13
  126. package/docs/rules/compat/no-wait-n.md +13 -13
  127. package/docs/rules/no-dynamic-invocation.md +17 -12
  128. package/docs/rules/no-eval.md +6 -1
  129. package/docs/rules/no-masked-exit-status.md +4 -0
  130. package/docs/rules/no-secret-env-reads.md +34 -3
  131. package/docs/rules/no-secret-flags.md +19 -2
  132. package/docs/rules/no-unchecked-cd.md +9 -2
  133. package/docs/rules/no-undeclared-commands.md +16 -15
  134. package/docs/rules/no-unused-declarations.md +17 -20
  135. package/docs/rules/prefer-local.md +20 -2
  136. package/docs/rules/quote-expansions.md +8 -5
  137. package/docs/rules/require-network-wrapper.md +22 -17
  138. package/docs/rules/require-shebang.md +10 -4
  139. package/docs/rules/require-strict-mode.md +6 -0
  140. package/docs/rules/require-tty-guard.md +4 -1
  141. package/docs/shellcheck.md +10 -5
  142. package/docs/transparent-wrappers.md +97 -0
  143. package/docs/untrusted-code.md +440 -0
  144. package/docs/wrapper-rules.md +9 -0
  145. package/package.json +2 -2
  146. package/dist/language/end-of-input-marker.d.ts +0 -29
  147. package/dist/language/end-of-input-marker.js +0 -88
@@ -24,19 +24,19 @@ The portable alternative is `[ -n "${name+set}" ]`. `${name+set}` expands to `se
24
24
  This is a version-compat rule. Whether it reports depends only on `languageOptions`, as
25
25
  described in [version compatibility](../../compat.md):
26
26
 
27
- - `shellVersion` unset: never reports. An unset version means the newest Bash.
28
- - `dialect: "bash"` (the default) or `"bats"`: reports when `shellVersion` is older than `4.2`.
29
- - `dialect: "posix"`: reports `test -v` and `[ -v ]` at any `shellVersion`; dash has no `-v` test.
27
+ - `minBashVersion` unset: no Bash version constraint, so no report under `bash` or `bats`.
28
+ - `dialect: "bash"` (the default) or `"bats"`: reports when `minBashVersion` is older than `4.2`.
29
+ - `dialect: "posix"`: reports `test -v` and `[ -v ]` regardless of Bash releases; dash has no `-v` test.
30
30
  - `dialect: "mksh"` or `"zsh"`: never reports.
31
31
 
32
32
  ## Options
33
33
 
34
- This rule has no options. Set the target through `languageOptions`:
34
+ This rule has no options. Set the required support through `languageOptions`:
35
35
 
36
36
  ```js
37
37
  // eslint.config.js
38
38
  {
39
- languageOptions: { dialect: 'bash', shellVersion: '3.2' },
39
+ languageOptions: { dialect: 'bash', minBashVersion: '3.2' },
40
40
  rules: {
41
41
  'sh/compat/no-test-v': 'error',
42
42
  },
@@ -47,9 +47,9 @@ This rule has no options. Set the target through `languageOptions`:
47
47
 
48
48
  ### Incorrect
49
49
 
50
- With `shellVersion: "3.2"`:
50
+ With `minBashVersion: "3.2"`:
51
51
 
52
- <!-- example: {"dialect": "bash", "shellVersion": "3.2", "findings": 3} -->
52
+ <!-- example: {"dialect": "bash", "minBashVersion": "3.2", "findings": 3} -->
53
53
  ```sh
54
54
  if [[ -v CI ]]; then echo ci; fi
55
55
  if test -v HOME; then echo home; fi
@@ -58,20 +58,20 @@ if test -v HOME; then echo home; fi
58
58
 
59
59
  ### Correct
60
60
 
61
- With `shellVersion: "3.2"`:
61
+ With `minBashVersion: "3.2"`:
62
62
 
63
- <!-- example: {"dialect": "bash", "shellVersion": "3.2"} -->
63
+ <!-- example: {"dialect": "bash", "minBashVersion": "3.2"} -->
64
64
  ```sh
65
65
  if [ -n "${CI+set}" ]; then echo ci; fi
66
66
  [ "$flag" = -v ] && verbose=1
67
67
  grep -v '^#' config
68
68
  ```
69
69
 
70
- With `shellVersion: "4.2"` or newer, or with no `shellVersion`, the `-v` test is fine.
70
+ With `minBashVersion: "4.2"` or newer, or with no `minBashVersion`, the `-v` test is fine.
71
71
 
72
72
  ## When not to use it
73
73
 
74
- Turn the rule off when your scripts never run on Bash older than 4.2, or leave `shellVersion`
74
+ Turn the rule off when your scripts never run on Bash older than 4.2, or leave `minBashVersion`
75
75
  unset, which makes the rule report nothing.
76
76
 
77
77
  ## Relationship to shellcheck
@@ -79,4 +79,4 @@ unset, which makes the rule report nothing.
79
79
  eslint-sh is a convention and contract linter: a policy layer, where shellcheck is the gotcha
80
80
  layer. Run shellcheck beside it.
81
81
 
82
- shellcheck reports `[[` and `test -v` only for a script it treats as POSIX `sh` (its SC3xxx portability checks). It does not know which Bash release a script must run on, so it cannot report `[[ -v x ]]` for a Bash 3.2 target. This rule exists for that target.
82
+ shellcheck reports `[[` and `test -v` only for a script it treats as POSIX `sh` (its SC3xxx portability checks). It does not know which Bash release a script must run on, so it cannot report `[[ -v x ]]` for a script that must support Bash 3.2. This rule checks that support requirement.
@@ -27,18 +27,18 @@ Under `dialect: "posix"` the parser reads `$'…'` as a literal `$` followed by
27
27
  This is a version-compat rule. Whether it reports depends only on `languageOptions`, as
28
28
  described in [version compatibility](../../compat.md):
29
29
 
30
- - `shellVersion` unset: never reports. An unset version means the newest Bash.
31
- - `dialect: "bash"` (the default) or `"bats"`: reports an escape when `shellVersion` is older than its release (4.2, or 4.3 for U+0080 to U+00FF).
30
+ - `minBashVersion` unset: no Bash version constraint, so no report under `bash` or `bats`.
31
+ - `dialect: "bash"` (the default) or `"bats"`: reports an escape when `minBashVersion` is older than its release (4.2, or 4.3 for U+0080 to U+00FF).
32
32
  - `dialect: "mksh"` or `"zsh"`: never reports.
33
33
 
34
34
  ## Options
35
35
 
36
- This rule has no options. Set the target through `languageOptions`:
36
+ This rule has no options. Set the required support through `languageOptions`:
37
37
 
38
38
  ```js
39
39
  // eslint.config.js
40
40
  {
41
- languageOptions: { dialect: 'bash', shellVersion: '3.2' },
41
+ languageOptions: { dialect: 'bash', minBashVersion: '3.2' },
42
42
  rules: {
43
43
  'sh/compat/no-unicode-escape': 'error',
44
44
  },
@@ -49,17 +49,17 @@ This rule has no options. Set the target through `languageOptions`:
49
49
 
50
50
  ### Incorrect
51
51
 
52
- With `shellVersion: "3.2"`:
52
+ With `minBashVersion: "3.2"`:
53
53
 
54
- <!-- example: {"dialect": "bash", "shellVersion": "3.2", "findings": 2} -->
54
+ <!-- example: {"dialect": "bash", "minBashVersion": "3.2", "findings": 2} -->
55
55
  ```sh
56
56
  ok=$'\u2713'
57
57
  printf '%s\n' $'caf\u00e9'
58
58
  ```
59
59
 
60
- With `shellVersion: "4.2"`, only the Latin-1 range reports:
60
+ With `minBashVersion: "4.2"`, only the Latin-1 range reports:
61
61
 
62
- <!-- example: {"dialect": "bash", "shellVersion": "4.2", "findings": 1} -->
62
+ <!-- example: {"dialect": "bash", "minBashVersion": "4.2", "findings": 1} -->
63
63
  ```sh
64
64
  ok=$'\u2713'
65
65
  printf '%s\n' $'caf\u00e9'
@@ -67,20 +67,20 @@ printf '%s\n' $'caf\u00e9'
67
67
 
68
68
  ### Correct
69
69
 
70
- With `shellVersion: "3.2"`:
70
+ With `minBashVersion: "3.2"`:
71
71
 
72
- <!-- example: {"dialect": "bash", "shellVersion": "3.2"} -->
72
+ <!-- example: {"dialect": "bash", "minBashVersion": "3.2"} -->
73
73
  ```sh
74
74
  ok=$'\342\234\223'
75
75
  printf '%s\n' $'caf\303\251'
76
76
  echo '\u00e9 is written literally here'
77
77
  ```
78
78
 
79
- With `shellVersion: "4.3"` or newer, or with no `shellVersion`, every escape is fine.
79
+ With `minBashVersion: "4.3"` or newer, or with no `minBashVersion`, every escape is fine.
80
80
 
81
81
  ## When not to use it
82
82
 
83
- Turn the rule off when your scripts never run on Bash older than 4.3, or leave `shellVersion`
83
+ Turn the rule off when your scripts never run on Bash older than 4.3, or leave `minBashVersion`
84
84
  unset, which makes the rule report nothing.
85
85
 
86
86
  ## Relationship to shellcheck
@@ -88,4 +88,4 @@ unset, which makes the rule report nothing.
88
88
  eslint-sh is a convention and contract linter: a policy layer, where shellcheck is the gotcha
89
89
  layer. Run shellcheck beside it.
90
90
 
91
- shellcheck reports `$'…'` only for a script it treats as POSIX `sh` (its SC3xxx portability checks). It does not know which Bash release a script must run on, so it cannot report a `\u` escape for a Bash 3.2 or 4.2 target. This rule exists for that target.
91
+ shellcheck reports `$'…'` only for a script it treats as POSIX `sh` (its SC3xxx portability checks). It does not know which Bash release a script must run on, so it cannot report a `\u` escape for a script that must support Bash 3.2 or 4.2. This rule checks that support requirement.
@@ -1,6 +1,6 @@
1
1
  # `sh/compat/no-wait-n`
2
2
 
3
- Disallow `wait -n` in scripts whose target shell lacks it: Bash older than 4.3, including the
3
+ Disallow `wait -n` in scripts that must support a shell lacking it: Bash older than 4.3, including the
4
4
  Bash 3.2 that macOS ships as `/bin/bash`, and POSIX sh.
5
5
 
6
6
  | Configs | Fixable | Languages |
@@ -20,19 +20,19 @@ The portable alternative is to wait for one job at a time by its PID: keep each
20
20
  This is a version-compat rule. Whether it reports depends only on `languageOptions`, as
21
21
  described in [version compatibility](../../compat.md):
22
22
 
23
- - `shellVersion` unset: never reports. An unset version means the newest Bash.
24
- - `dialect: "bash"` (the default) or `"bats"`: reports when `shellVersion` is older than `4.3`.
25
- - `dialect: "posix"`: reports at any `shellVersion`; POSIX `wait` has no options.
23
+ - `minBashVersion` unset: no Bash version constraint, so no report under `bash` or `bats`.
24
+ - `dialect: "bash"` (the default) or `"bats"`: reports when `minBashVersion` is older than `4.3`.
25
+ - `dialect: "posix"`: reports regardless of Bash releases; POSIX `wait` has no options.
26
26
  - `dialect: "mksh"` or `"zsh"`: never reports.
27
27
 
28
28
  ## Options
29
29
 
30
- This rule has no options. Set the target through `languageOptions`:
30
+ This rule has no options. Set the required support through `languageOptions`:
31
31
 
32
32
  ```js
33
33
  // eslint.config.js
34
34
  {
35
- languageOptions: { dialect: 'bash', shellVersion: '3.2' },
35
+ languageOptions: { dialect: 'bash', minBashVersion: '3.2' },
36
36
  rules: {
37
37
  'sh/compat/no-wait-n': 'error',
38
38
  },
@@ -43,9 +43,9 @@ This rule has no options. Set the target through `languageOptions`:
43
43
 
44
44
  ### Incorrect
45
45
 
46
- With `shellVersion: "3.2"`:
46
+ With `minBashVersion: "3.2"`:
47
47
 
48
- <!-- example: {"dialect": "bash", "shellVersion": "3.2", "findings": 1} -->
48
+ <!-- example: {"dialect": "bash", "minBashVersion": "3.2", "findings": 1} -->
49
49
  ```sh
50
50
  for job in a b c d; do
51
51
  run "$job" &
@@ -56,9 +56,9 @@ wait
56
56
 
57
57
  ### Correct
58
58
 
59
- With `shellVersion: "3.2"`:
59
+ With `minBashVersion: "3.2"`:
60
60
 
61
- <!-- example: {"dialect": "bash", "shellVersion": "3.2"} -->
61
+ <!-- example: {"dialect": "bash", "minBashVersion": "3.2"} -->
62
62
  ```sh
63
63
  pids=
64
64
  for job in a b c d; do
@@ -70,11 +70,11 @@ for pid in $pids; do
70
70
  done
71
71
  ```
72
72
 
73
- With `shellVersion: "4.3"` or newer, or with no `shellVersion`, `wait -n` is fine.
73
+ With `minBashVersion: "4.3"` or newer, or with no `minBashVersion`, `wait -n` is fine.
74
74
 
75
75
  ## When not to use it
76
76
 
77
- Turn the rule off when your scripts never run on Bash older than 4.3, or leave `shellVersion`
77
+ Turn the rule off when your scripts never run on Bash older than 4.3, or leave `minBashVersion`
78
78
  unset, which makes the rule report nothing.
79
79
 
80
80
  ## Relationship to shellcheck
@@ -82,4 +82,4 @@ unset, which makes the rule report nothing.
82
82
  eslint-sh is a convention and contract linter: a policy layer, where shellcheck is the gotcha
83
83
  layer. Run shellcheck beside it.
84
84
 
85
- shellcheck reports `wait` options only for a script it treats as POSIX `sh` (its SC3xxx portability checks). It does not know which Bash release a script must run on, so it cannot report `wait -n` for a Bash 4.2 target. This rule exists for that target.
85
+ shellcheck reports `wait` options only for a script it treats as POSIX `sh` (its SC3xxx portability checks). It does not know which Bash release a script must run on, so it cannot report `wait -n` for a script that must support Bash 4.2. This rule checks that support requirement.
@@ -30,7 +30,8 @@ wrappers_: commands that run the word after them. `time ls` runs `ls`, and
30
30
  `bash ./build.sh` runs `./build.sh`. `bash -c STRING`, including a one-letter cluster such as
31
31
  `bash -lc STRING`, runs an embedded string. `bash -s` and a bare `bash` read commands from
32
32
  standard input, so the shell itself is the command.
33
- - Any names you add with `allowWrappers`.
33
+ - Project wrappers declared in `settings: { sh: { transparentWrappers: ['with-retry'] } }`,
34
+ and the shared network wrapper. See [shared transparent wrappers](../transparent-wrappers.md).
34
35
 
35
36
  A command then reports in one of two ways:
36
37
 
@@ -45,8 +46,9 @@ A command then reports in one of two ways:
45
46
 
46
47
  The rule is syntactic by design. It doesn't track values, so `cmd=ls; "$cmd"` reports. Aliases
47
48
  need no handling: non-interactive shells don't expand them, so an alias name is an ordinary
48
- static command word. Wrapper names match exactly, so `/bin/bash -c …` is a static command named
49
- `/bin/bash`, not a shell wrapper.
49
+ static command word. Utility wrappers match absolute paths by basename, so `/bin/bash -c …` runs an
50
+ embedded command. Configured utility wrappers also match relative paths; builtin specs
51
+ require exact names.
50
52
 
51
53
  It does not depend on `languageOptions`.
52
54
 
@@ -56,18 +58,20 @@ It does not depend on `languageOptions`.
56
58
  `dynamicInvocation` inside a function with one of these names, at any nesting depth, does not
57
59
  report. The same command outside that function still reports. `embeddedCommand` still reports
58
60
  inside these functions. Each use of the dispatcher stays visible at its call sites.
59
- - `allowWrappers` (`string[]`, default `[]`): extra transparent wrappers, added to the built-in
60
- list above. The word after one is checked as the command that runs. An added wrapper has no
61
- known options, so a flag right after it (`with-retry -n 3 curl`) makes the command unknown and
62
- reports. A name already in the built-in list keeps its built-in option handling.
61
+ Project wrapper facts belong in shared settings, with no rule override. The former
62
+ `allowWrappers` option is rejected. It added checks rather than suppressing them:
63
+ move its list to shared settings to preserve those checks across all wrapper-following
64
+ rules. Use `allowInFunctions` or a justified ESLint disable comment for an actual allowance.
65
+ See [migration](../transparent-wrappers.md#migration).
63
66
 
64
67
  ```js
65
68
  // eslint.config.js
66
69
  {
70
+ settings: { sh: { transparentWrappers: ['with-retry'] } },
67
71
  rules: {
68
72
  'sh/no-dynamic-invocation': [
69
73
  'error',
70
- { allowInFunctions: ['dispatch'], allowWrappers: ['with-retry'] },
74
+ { allowInFunctions: ['dispatch'] },
71
75
  ],
72
76
  },
73
77
  }
@@ -91,9 +95,9 @@ bash -c "$script" # embeddedCommand: a string of shell code
91
95
  sh -c 'make test' # embeddedCommand, even for a literal string
92
96
  ```
93
97
 
94
- With `{ allowWrappers: ['with-retry'] }`:
98
+ With `settings: { sh: { transparentWrappers: ['with-retry'] } }`:
95
99
 
96
- <!-- example: {"options": {"allowWrappers": ["with-retry"]}, "findings": 1, "messageIds": ["dynamicInvocation"]} -->
100
+ <!-- example: {"settings": {"sh": {"transparentWrappers": ["with-retry"]}}, "findings": 1, "messageIds": ["dynamicInvocation"]} -->
97
101
  ```sh
98
102
  with-retry "$c" # the wrapped command is dynamic
99
103
  ```
@@ -110,9 +114,10 @@ alias ll='ls -l'
110
114
  ll # an alias name is a static command word
111
115
  ```
112
116
 
113
- With `{ allowInFunctions: ['dispatch'], allowWrappers: ['with-retry'] }`:
117
+ With `{ allowInFunctions: ['dispatch'] }` and
118
+ `settings: { sh: { transparentWrappers: ['with-retry'] } }`:
114
119
 
115
- <!-- example: {"options": {"allowInFunctions": ["dispatch"], "allowWrappers": ["with-retry"]}} -->
120
+ <!-- example: {"options": {"allowInFunctions": ["dispatch"]}, "settings": {"sh": {"transparentWrappers": ["with-retry"]}}} -->
116
121
  ```sh
117
122
  dispatch() {
118
123
  "$@" # allowed inside the dispatcher
@@ -7,6 +7,10 @@ of code a script runs can be reviewed before it runs.
7
7
  | ------- | ------------ | --------- |
8
8
  | `recommended`, `agent-authored` | not fixable | `sh/sh` |
9
9
 
10
+ Project wrapper facts are shared through
11
+ `settings: { sh: { transparentWrappers: ['with-retry'] } }`. This rule follows
12
+ those wrappers and the shared network wrapper; see [shared transparent wrappers](../transparent-wrappers.md).
13
+
10
14
  ## Rule details
11
15
 
12
16
  A script you can review is a script whose code is all in front of you. Two constructs defeat
@@ -98,7 +102,8 @@ still report.
98
102
  eslint-sh is a convention and contract linter: a policy layer, where shellcheck is the gotcha
99
103
  layer. Run shellcheck beside it.
100
104
 
101
- This rule is one of eslint-sh's two deliberate overlaps with shellcheck. shellcheck flags
105
+ This rule is one of eslint-sh's
106
+ [deliberate overlaps with shellcheck](../shellcheck.md#deliberate-overlaps). shellcheck flags
102
107
  `eval` only in specific misuse patterns, such as SC2294 (`eval` on an array negates the benefit
103
108
  of arrays), and flags `source` of a non-constant path as SC1090 because shellcheck cannot follow
104
109
  it. Both treat these as hazards to work around. This rule treats them as a policy: code that
@@ -6,6 +6,10 @@ Disallow `local`, `declare`, `typeset`, `export` and `readonly` with a value tak
6
6
  | ---------------- | ----------------------------------------- | --------- |
7
7
  | `agent-authored` | suggestions (split into two statements) | `sh/sh` |
8
8
 
9
+ Project wrapper facts are shared through
10
+ `settings: { sh: { transparentWrappers: ['with-retry'] } }`. This rule follows
11
+ those wrappers and the shared network wrapper; see [shared transparent wrappers](../transparent-wrappers.md).
12
+
9
13
  ## Rule details
10
14
 
11
15
  `local x=$(cmd)` runs `cmd`, then runs `local`, and the statement's exit status is the status of
@@ -6,6 +6,10 @@ Disallow reading secret-shaped environment variables, such as `$GITHUB_TOKEN`, o
6
6
  | ---------------- | ----------- | --------- |
7
7
  | `agent-authored` (error) | not fixable | `sh/sh` |
8
8
 
9
+ Project wrapper facts are shared through
10
+ `settings: { sh: { transparentWrappers: ['with-retry'] } }`. This rule follows
11
+ those wrappers and the shared network wrapper; see [shared transparent wrappers](../transparent-wrappers.md).
12
+
9
13
  ## Rule details
10
14
 
11
15
  Once a secret is expanded into a command's arguments, every user on the machine can read it
@@ -34,6 +38,13 @@ Four things are not reported:
34
38
  command is resolved through transparent wrappers (`sudo vault …` is `vault`), and a command run
35
39
  by path matches by basename.
36
40
 
41
+ Only arguments actually passed after the final resolved sink command receive this
42
+ exemption. Wrapper-owned options and operands are checked normally. With an approved
43
+ `sink` and `settings: { sh: { transparentWrappers: [{ names: ['with-retry'], argFlags: ['--log'] }] } }`,
44
+ `with-retry --log "$API_TOKEN" sink` reports, while
45
+ `with-retry --log audit.log sink "$API_TOKEN"` stays clean. Likewise, the secret in
46
+ `env -u "$API_TOKEN" sink` is owned by `env`, so approving `sink` does not exempt it.
47
+
37
48
  Matching is a name pattern, not secret detection.
38
49
 
39
50
  ### Expected false positives
@@ -59,12 +70,16 @@ The rule matches parameter expansions only. These reads are not reported:
59
70
  ## Options
60
71
 
61
72
  - `patterns` (`string[]`, at least one entry). Regular-expression sources tested against each
62
- variable's name. They **replace** the default list. Sources are compiled case-sensitively. An
63
- invalid source fails config validation.
73
+ variable's name. They **replace** the default list. Sources use `u` (Unicode, case-sensitive):
74
+ `patterns: ['token']` does not catch `$GITHUB_TOKEN`, while `patterns: ['TOKEN']` does.
75
+ Lowercase names such as `$sort_key` do not match the uppercase default patterns.
76
+ See [regular-expression options](./README.md#regular-expression-options) for compilation and
77
+ configuration errors, which name the rule, option index and source.
64
78
 
65
79
  Default: `["(_TOKEN|_SECRET|_PASSWORD|_KEY)$"]`.
66
80
 
67
- - `allowedSinks` (`string[]`). Command names whose arguments may read a secret-shaped variable.
81
+ - `allowedSinks` (`string[]`). Command names whose own arguments may read a secret-shaped variable. Wrapper-owned
82
+ operands are not exempt.
68
83
 
69
84
  Default: `[]`.
70
85
 
@@ -80,6 +95,22 @@ The rule matches parameter expansions only. These reads are not reported:
80
95
  }
81
96
  ```
82
97
 
98
+ Regex syntax errors fail when this rule first runs on a file, naming the rule, option and
99
+ pattern. `--print-config` will not catch any regex syntax errors. See the
100
+ [shared compilation contract](./README.md#regular-expression-options).
101
+
102
+ ### Matching examples
103
+
104
+ These examples use only this rule; `patterns` replaces the defaults. Option values below are JSON.
105
+
106
+ | Option value | Shell input | Findings |
107
+ | --- | --- | --- |
108
+ | `{"patterns":["token"]}` | `echo "$GITHUB_TOKEN"` | 0 |
109
+ | `{"patterns":["TOKEN"]}` | `echo "$GITHUB_TOKEN"` | 1 |
110
+ | `{}` | `echo "$sort_key"` | 0 |
111
+ | `{"patterns":["^\\p{Lu}+$"]}` | `echo "$TOKEN"` | 1 |
112
+ | `{"patterns":["^\\p{Lu}+$"]}` | `echo "$token"` | 0 |
113
+
83
114
  ### Patterns run against script-controlled text
84
115
 
85
116
  `patterns` is tested against every variable name the linted script expands, and the script's author
@@ -60,8 +60,10 @@ positive with a disable comment that says why, or narrow the `patterns` option f
60
60
 
61
61
  - `patterns` (`string[]`, at least one entry). Regular-expression sources tested against each flag's
62
62
  name, without `=` or the value. They **replace** the default list. Each source is compiled
63
- case-insensitively, like the default. An invalid source fails config validation with an error that
64
- names the rule and the source.
63
+ with `iu` (Unicode, case-insensitive), like the default. For example, `patterns: ['token']`
64
+ catches `cli --TOKEN=abc`; capitals in a custom pattern also match lowercase flag names.
65
+ See [regular-expression options](./README.md#regular-expression-options) for compilation and
66
+ configuration errors, which name the rule, option index and source.
65
67
 
66
68
  Default: `["^(?!.*-(?:stdin|file)$)--?(?:access-|auth-?)?(?:password|passwd|token|secret|api-?key|auth(?:key|orization)?)(?![a-z])"]`.
67
69
 
@@ -78,6 +80,21 @@ positive with a disable comment that says why, or narrow the `patterns` option f
78
80
  }
79
81
  ```
80
82
 
83
+ Regex syntax errors fail when this rule first runs on a file, naming the rule, option and
84
+ pattern. `--print-config` will not catch any regex syntax errors. See the
85
+ [shared compilation contract](./README.md#regular-expression-options).
86
+
87
+ ### Matching examples
88
+
89
+ These examples use only this rule; `patterns` replaces the defaults. Option values below are JSON.
90
+
91
+ | Option value | Shell input | Findings |
92
+ | --- | --- | --- |
93
+ | `{"patterns":["token"]}` | `cli --TOKEN=abc` | 1 |
94
+ | `{"patterns":["TOKEN"]}` | `cli --token=abc` | 1 |
95
+ | `{"patterns":["token"]}` | `cli --other=abc` | 0 |
96
+ | `{"patterns":["^--\\p{Lu}+$"]}` | `cli --TOKEN=abc` | 1 |
97
+
81
98
  ### Patterns run against script-controlled text
82
99
 
83
100
  `patterns` is tested against every flag name the linted script writes, and the script's author
@@ -6,6 +6,10 @@ Require the failure of `cd`, `pushd` and `popd` to be checked, so a script never
6
6
  | ------------------------------- | ---------------------------- | --------- |
7
7
  | `recommended`, `agent-authored` | suggestions (append `\|\| exit`) | `sh/sh` |
8
8
 
9
+ Project wrapper facts are shared through
10
+ `settings: { sh: { transparentWrappers: ['with-retry'] } }`. This rule follows
11
+ those wrappers and the shared network wrapper; see [shared transparent wrappers](../transparent-wrappers.md).
12
+
9
13
  ## Rule details
10
14
 
11
15
  When `cd` fails, the script keeps running in the directory it was already in, and every relative
@@ -31,7 +35,8 @@ read as `cd`.
31
35
  by itself, so the rule is quiet there. "Prologue" is read exactly as
32
36
  [`require-strict-mode`](./require-strict-mode.md) reads it: the first five top-level statements,
33
37
  tracked in order, so a later `set +e` undoes an earlier `set -e`, and a `set -e` outside that
34
- window does not count. Set `ignoreWhenErrexit` to `false` to report anyway.
38
+ window does not count. Only `set` statements count; shebang flags do not. Set
39
+ `ignoreWhenErrexit` to `false` to report anyway.
35
40
 
36
41
  **Files run by bats.** With `languageOptions.dialect` set to `"bats"`, a bare `cd` (or `pushd` or
37
42
  `popd`) written directly in the body of a `@test` block, or of a top-level `setup`, `teardown`,
@@ -143,4 +148,6 @@ shellcheck overlaps with SC2164 ("Use `cd ... || exit` in case `cd` fails"), and
143
148
  enforces the same practice. It exists anyway because it offers a suggestion, covers `pushd` and
144
149
  `popd`, can be turned off per file by `set -e` detection that follows the same prologue reading as
145
150
  `require-strict-mode`, and is part of the shipped presets, so the policy applies without running
146
- shellcheck. If you already run shellcheck with SC2164 enabled, the two agree on the common cases.
151
+ shellcheck. The checks differ when errexit comes from a shebang such as `#!/bin/bash -e`:
152
+ shellcheck treats it as `set -e` and suppresses SC2164, while `ignoreWhenErrexit` reads only
153
+ `set` statements in the prologue, so this rule still reports an unchecked `cd`.
@@ -14,7 +14,7 @@ machine it lands on. When your project states each script's dependencies, this r
14
14
  statement complete: it reports each command the script uses but does not declare.
15
15
 
16
16
  The rule needs a **declaration convention**, which says how your project declares commands. Bind
17
- one with `settings.sh.declarationConvention`. The plugin ships one, `commentPragma`, which reads
17
+ one with `settings: { sh: { declarationConvention: commentPragma } }`. The plugin ships one, `commentPragma`, which reads
18
18
  `# uses: jq curl` comments from a script's header. See
19
19
  [Declaration conventions](../declaration-conventions.md) to write your own. **With no convention
20
20
  bound, the rule reports nothing**, so turning it on is safe until your project picks a convention.
@@ -61,16 +61,17 @@ In a file without a shebang, the message says to add the line as the first line
61
61
  instead. A convention without a hint ends the message with the generic sentence
62
62
  "Declare it with the project's declaration convention." A command run by path (`./build.sh`) is reported by that path.
63
63
 
64
- ## Options
64
+ ## Shared settings
65
65
 
66
- - `transparentWrappers` (`string[]`, default `[]`): names of your project's own wrapper
67
- commands, added to the built-in wrappers above. The word after a listed wrapper is the command
68
- it runs, so with `['with-retry']`, `with-retry curl x` uses both `with-retry` and `curl`. A
69
- listed wrapper takes no flags: in `with-retry -n 3 curl`, the rule stops at `-n` and does not
70
- count `curl`. If the wrapper is a function defined in the same file, only the wrapped command
71
- needs declaring. Your network wrapper doesn't need listing here when it is set in
72
- `settings.sh.networkWrapper`: this rule looks through it, with the options its spec declares
73
- (see [`sh/require-network-wrapper`](./require-network-wrapper.md#declaring-the-wrapper-once-settingsshnetworkwrapper)).
66
+ This rule has no options. Declare project wrapper facts once with
67
+ `settings: { sh: { transparentWrappers: ['with-retry'] } }`.
68
+ Every wrapper-following rule reads that setting, including the other declaration rule.
69
+ Entries accept names or flag-bearing specs, for example
70
+ `settings: { sh: { transparentWrappers: [{ names: ['with-retry'], argFlags: ['-n'] }] } }`.
71
+ Then `with-retry -n 3 curl` uses both `with-retry` and `curl`.
72
+ The shared network wrapper is also followed automatically. Utility wrappers invoked
73
+ by absolute or relative path count by basename; builtin specs require exact names.
74
+ See [shared transparent wrappers](../transparent-wrappers.md) for precedence and migration.
74
75
 
75
76
  The convention is not a rule option. Bind it in `settings`, because ESLint copies rule options
76
77
  with `structuredClone`, which cannot copy a convention's methods.
@@ -84,9 +85,9 @@ export default [
84
85
  files: ['**/*.sh'],
85
86
  plugins: { sh },
86
87
  language: 'sh/sh',
87
- settings: { sh: { declarationConvention: commentPragma } },
88
+ settings: { sh: { declarationConvention: commentPragma, transparentWrappers: ['with-retry'] } },
88
89
  rules: {
89
- 'sh/no-undeclared-commands': ['error', { transparentWrappers: ['with-retry'] }],
90
+ 'sh/no-undeclared-commands': 'error',
90
91
  },
91
92
  },
92
93
  ];
@@ -94,11 +95,11 @@ export default [
94
95
 
95
96
  ## Examples
96
97
 
97
- With `commentPragma` bound and `{ transparentWrappers: ['with-retry'] }`:
98
+ With `commentPragma` bound and `settings: { sh: { transparentWrappers: ['with-retry'] } }`:
98
99
 
99
100
  ### Incorrect
100
101
 
101
- <!-- example: {"convention": "commentPragma", "options": {"transparentWrappers": ["with-retry"]}, "findings": 6} -->
102
+ <!-- example: {"convention": "commentPragma", "settings": {"sh": {"transparentWrappers": ["with-retry"]}}, "findings": 6} -->
102
103
  ```sh
103
104
  #!/usr/bin/env bash
104
105
  # uses: jq
@@ -110,7 +111,7 @@ with-retry git pull # git, run by the wrapper, is not declared (nor is
110
111
 
111
112
  ### Correct
112
113
 
113
- <!-- example: {"convention": "commentPragma", "options": {"transparentWrappers": ["with-retry"]}} -->
114
+ <!-- example: {"convention": "commentPragma", "settings": {"sh": {"transparentWrappers": ["with-retry"]}}} -->
114
115
  ```sh
115
116
  #!/usr/bin/env bash
116
117
  # uses: curl jq date
@@ -15,7 +15,7 @@ script's real surface. This rule reports each declared command the script never
15
15
  converse of [`sh/no-undeclared-commands`](./no-undeclared-commands.md).
16
16
 
17
17
  The rule needs a **declaration convention**, which says how your project declares commands. Bind
18
- one with `settings.sh.declarationConvention`. The plugin ships one, `commentPragma`, which reads
18
+ one with `settings: { sh: { declarationConvention: commentPragma } }`. The plugin ships one, `commentPragma`, which reads
19
19
  `# uses: jq curl` comments from a script's header. See
20
20
  [Declaration conventions](../declaration-conventions.md) to write your own. **With no convention
21
21
  bound, the rule reports nothing**, so turning it on is safe until your project picks a convention.
@@ -135,18 +135,17 @@ the whole line goes with its `\n` or `\r\n`. When several names on one line are
135
135
  edits touch, so ESLint applies one per pass. `eslint --fix` runs passes until nothing changes, so
136
136
  a line whose every name is unused is removed entirely.
137
137
 
138
- ## Options
138
+ ## Shared settings
139
139
 
140
- - `transparentWrappers` (`string[]`, default `[]`): names of your project's own wrapper
141
- commands, added to the built-in wrappers. The word after a listed wrapper is the command it
142
- runs. With `['with-retry']`, `with-retry curl x` runs `curl`, and `with-retry "$tool"` runs a
143
- command the rule can't name, so the file is incomplete usage and reports nothing. A listed
144
- wrapper takes no flags, so `with-retry -n 3 curl` is incomplete usage too: the rule stops at
145
- `-n` and can't tell what runs. Give this option the same value as
146
- `sh/no-undeclared-commands`' option of the same name, or the two rules will disagree about what
147
- the script uses. Your network wrapper doesn't need listing here when it is set in
148
- `settings.sh.networkWrapper`: this rule looks through it, with the options its spec declares
149
- (see [`sh/require-network-wrapper`](./require-network-wrapper.md#declaring-the-wrapper-once-settingsshnetworkwrapper)).
140
+ This rule has no options. Declare project wrapper facts once with
141
+ `settings: { sh: { transparentWrappers: ['with-retry'] } }`.
142
+ Every wrapper-following rule reads that setting, including the other declaration rule.
143
+ Entries accept names or flag-bearing specs, for example
144
+ `settings: { sh: { transparentWrappers: [{ names: ['with-retry'], argFlags: ['-n'] }] } }`.
145
+ Then `with-retry -n 3 curl` uses both `with-retry` and `curl`.
146
+ The shared network wrapper is also followed automatically. Utility wrappers invoked
147
+ by absolute or relative path count by basename; builtin specs require exact names.
148
+ See [shared transparent wrappers](../transparent-wrappers.md) for precedence and migration.
150
149
 
151
150
  The convention is not a rule option. Bind it in `settings`, because ESLint copies rule options
152
151
  with `structuredClone`, which cannot copy a convention's methods.
@@ -155,17 +154,15 @@ with `structuredClone`, which cannot copy a convention's methods.
155
154
  // eslint.config.js
156
155
  import sh, { commentPragma } from '@eslint-sh/eslint-plugin';
157
156
 
158
- const wrappers = { transparentWrappers: ['with-retry'] };
159
-
160
157
  export default [
161
158
  {
162
159
  files: ['**/*.sh'],
163
160
  plugins: { sh },
164
161
  language: 'sh/sh',
165
- settings: { sh: { declarationConvention: commentPragma } },
162
+ settings: { sh: { declarationConvention: commentPragma, transparentWrappers: ['with-retry'] } },
166
163
  rules: {
167
- 'sh/no-undeclared-commands': ['error', wrappers],
168
- 'sh/no-unused-declarations': ['error', wrappers],
164
+ 'sh/no-undeclared-commands': 'error',
165
+ 'sh/no-unused-declarations': 'error',
169
166
  },
170
167
  },
171
168
  ];
@@ -173,11 +170,11 @@ export default [
173
170
 
174
171
  ## Examples
175
172
 
176
- With `commentPragma` bound and `{ transparentWrappers: ['with-retry'] }`:
173
+ With `commentPragma` bound and `settings: { sh: { transparentWrappers: ['with-retry'] } }`:
177
174
 
178
175
  ### Incorrect
179
176
 
180
- <!-- example: {"convention": "commentPragma", "options": {"transparentWrappers": ["with-retry"]}, "findings": 3} -->
177
+ <!-- example: {"convention": "commentPragma", "settings": {"sh": {"transparentWrappers": ["with-retry"]}}, "findings": 3} -->
181
178
  ```sh
182
179
  #!/usr/bin/env bash
183
180
  # uses: jq curl docker # docker is never run
@@ -189,7 +186,7 @@ curl -s "$URL" | jq .
189
186
 
190
187
  ### Correct
191
188
 
192
- <!-- example: {"convention": "commentPragma", "options": {"transparentWrappers": ["with-retry"]}} -->
189
+ <!-- example: {"convention": "commentPragma", "settings": {"sh": {"transparentWrappers": ["with-retry"]}}} -->
193
190
  ```sh
194
191
  #!/usr/bin/env bash
195
192
  # uses: jq curl git sudo date with-retry