sh-ast 0.0.0 → 0.2.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 (81) hide show
  1. package/README.md +147 -0
  2. package/dist/analyze/ansi-c-escapes.d.ts +53 -0
  3. package/dist/analyze/ansi-c-escapes.d.ts.map +1 -0
  4. package/dist/analyze/ansi-c-escapes.js +255 -0
  5. package/dist/analyze/ansi-c-escapes.js.map +1 -0
  6. package/dist/analyze/decode-lit.d.ts +74 -0
  7. package/dist/analyze/decode-lit.d.ts.map +1 -0
  8. package/dist/analyze/decode-lit.js +114 -0
  9. package/dist/analyze/decode-lit.js.map +1 -0
  10. package/dist/analyze/enumerate-commands.d.ts +165 -0
  11. package/dist/analyze/enumerate-commands.d.ts.map +1 -0
  12. package/dist/analyze/enumerate-commands.js +396 -0
  13. package/dist/analyze/enumerate-commands.js.map +1 -0
  14. package/dist/analyze/index.d.ts +26 -0
  15. package/dist/analyze/index.d.ts.map +1 -0
  16. package/dist/analyze/index.js +30 -0
  17. package/dist/analyze/index.js.map +1 -0
  18. package/dist/analyze/node-helpers.d.ts +28 -0
  19. package/dist/analyze/node-helpers.d.ts.map +1 -0
  20. package/dist/analyze/node-helpers.js +37 -0
  21. package/dist/analyze/node-helpers.js.map +1 -0
  22. package/dist/analyze/resolve-argv0.d.ts +335 -0
  23. package/dist/analyze/resolve-argv0.d.ts.map +1 -0
  24. package/dist/analyze/resolve-argv0.js +510 -0
  25. package/dist/analyze/resolve-argv0.js.map +1 -0
  26. package/dist/analyze/resolve-word.d.ts +151 -0
  27. package/dist/analyze/resolve-word.d.ts.map +1 -0
  28. package/dist/analyze/resolve-word.js +207 -0
  29. package/dist/analyze/resolve-word.js.map +1 -0
  30. package/dist/analyze.d.ts +771 -0
  31. package/dist/deep-freeze.d.ts +14 -0
  32. package/dist/deep-freeze.d.ts.map +1 -0
  33. package/dist/deep-freeze.js +25 -0
  34. package/dist/deep-freeze.js.map +1 -0
  35. package/dist/errors.d.ts +180 -0
  36. package/dist/errors.d.ts.map +1 -0
  37. package/dist/errors.js +179 -0
  38. package/dist/errors.js.map +1 -0
  39. package/dist/index.d.ts +18 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +5 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/normalize.d.ts +52 -0
  44. package/dist/normalize.d.ts.map +1 -0
  45. package/dist/normalize.js +260 -0
  46. package/dist/normalize.js.map +1 -0
  47. package/dist/parse-depth-guard.d.ts +11 -0
  48. package/dist/parse-depth-guard.d.ts.map +1 -0
  49. package/dist/parse-depth-guard.js +633 -0
  50. package/dist/parse-depth-guard.js.map +1 -0
  51. package/dist/parse.d.ts +50 -0
  52. package/dist/parse.d.ts.map +1 -0
  53. package/dist/parse.js +125 -0
  54. package/dist/parse.js.map +1 -0
  55. package/dist/sh-ast.d.ts +1346 -0
  56. package/dist/types.d.ts +74 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +2 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/visitor-keys.d.ts +21 -0
  61. package/dist/visitor-keys.d.ts.map +1 -0
  62. package/dist/visitor-keys.js +23 -0
  63. package/dist/visitor-keys.js.map +1 -0
  64. package/dist/walk.d.ts +18 -0
  65. package/dist/walk.d.ts.map +1 -0
  66. package/dist/walk.js +40 -0
  67. package/dist/walk.js.map +1 -0
  68. package/dist/wasm-instance.d.ts +13 -0
  69. package/dist/wasm-instance.d.ts.map +1 -0
  70. package/dist/wasm-instance.js +88 -0
  71. package/dist/wasm-instance.js.map +1 -0
  72. package/generated/child-type-schema.d.ts +14 -0
  73. package/generated/child-type-schema.js +188 -0
  74. package/generated/node-types.d.ts +958 -0
  75. package/generated/position-fields.d.ts +21 -0
  76. package/generated/position-fields.js +50 -0
  77. package/generated/visitor-keys.d.ts +11 -0
  78. package/generated/visitor-keys.js +50 -0
  79. package/package.json +93 -10
  80. package/shim/sh-ast.wasm +0 -0
  81. package/shim/wasm_exec.js +654 -0
@@ -0,0 +1,510 @@
1
+ import { ShAnalyzeInvalidWrapperSpecError } from '../errors.js';
2
+ import { deepFreeze } from '../deep-freeze.js';
3
+ import { nodeArray } from './node-helpers.js';
4
+ /**
5
+ * `sh-ast/analyze`'s default {@link WrapperSpec} table — every entry's
6
+ * flag/operand handling is hand-derived from that wrapper's own manual
7
+ * page (or, for shell builtins, the Bash Reference Manual/POSIX Shell &
8
+ * Utilities volume), cited per entry below, and cross-checked empirically
9
+ * against the real utility where one was locally available (GNU coreutils
10
+ * `env`/`nice` directly; `sudo` via `sudo -h`'s usage/option summary only
11
+ * — never executed, per this package's "facts only" posture and basic
12
+ * safety hygiene). This table is *data*, not policy: it names no
13
+ * "dangerous" commands, makes no safety judgment, and a caller can freely
14
+ * replace or extend it via {@link ResolveArgv0Options.transparentWrappers}
15
+ * (see {@link resolveArgv0}'s criterion-4-style configurability guarantee).
16
+ *
17
+ * Frozen (including each entry object and its own array/regexp fields) so
18
+ * immutability of this shared, module-scoped table is a contract, not just
19
+ * a convention any importer happens to honor — mirrors `visitorKeys`'s and
20
+ * `CHILD_TYPE_SCHEMA`'s freezing in `visitor-keys.ts`/`normalize.ts`.
21
+ *
22
+ * `xargs` is deliberately **excluded**: unlike every entry here, `xargs`
23
+ * doesn't simply re-exec a single wrapped command word it can point to
24
+ * statically — it invokes `command [initial-arguments]` once per *batch*
25
+ * of additional arguments assembled from its own stdin at run time
26
+ * (batched per `-n`/`-L`/line-splitting rules, GNU xargs(1)). Treating it
27
+ * as transparent would misrepresent what's actually invoked: the "wrapped
28
+ * command" is only ever a syntactic *prefix* of the real, stdin-dependent
29
+ * argv, and that prefix can itself be entirely absent (bare `xargs` reruns
30
+ * each stdin-derived line as its own command). No `WrapperSpec` shape here
31
+ * can honestly express that, so `xargs` is left for a caller to model
32
+ * explicitly if their use case calls for it, rather than shipped as a
33
+ * silently-wrong default.
34
+ *
35
+ * @see https://www.gnu.org/software/bash/manual/bash.html#Bourne-Shell-Builtins — `command`, `exec` (Bash Reference Manual §4.1)
36
+ * @see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html — POSIX `command`
37
+ * @see https://www.gnu.org/software/coreutils/manual/html_node/env-invocation.html — GNU coreutils `env`
38
+ * @see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html — POSIX `env`
39
+ * @see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/nohup.html — POSIX `nohup`
40
+ * @see https://www.gnu.org/software/coreutils/manual/html_node/nice-invocation.html — GNU coreutils `nice`
41
+ * @see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/nice.html — POSIX `nice`
42
+ * @see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/time.html — POSIX `time` utility (distinct from the bash `time` *reserved word*, which mvdan/sh models as a transparent `TimeClause` — see {@link WrapperSpec}'s `time` entry below)
43
+ * @see https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html — GNU coreutils `timeout`
44
+ * @see https://www.sudo.ws/docs/man/sudo.man/ — `sudo(8)`
45
+ * @see https://www.gnu.org/software/findutils/manual/html_node/find_html/xargs-options.html — GNU `xargs` (why it's excluded, above)
46
+ * @public
47
+ */
48
+ export const DEFAULT_TRANSPARENT_WRAPPERS = deepFreeze([
49
+ // `command [-pVv] command_name [argument ...]` — Bash Reference Manual
50
+ // §4.1 / POSIX `command`. `-p` takes no operand and doesn't change what
51
+ // runs. `-v`/`-V`, however, make `command` itself *print* information
52
+ // about `command_name` (its path, or a human-readable description)
53
+ // rather than execute it at all — so `command -v rm x` never runs `rm`;
54
+ // the effective command is `command` itself, regardless of what follows
55
+ // (`stopsChainFlags`, not `noArgFlags` — see `WrapperSpec.stopsChainFlags`'s
56
+ // doc comment).
57
+ { names: ['command'], noArgFlags: ['-p'], stopsChainFlags: ['-v', '-V'] },
58
+ // `exec [-cl] [-a name] [command [arguments]]` — Bash Reference Manual
59
+ // §4.1. `-c`/`-l` take no operand; `-a name` supplies argv0 for the
60
+ // wrapped command and takes `name` as its operand.
61
+ { names: ['exec'], noArgFlags: ['-c', '-l'], argFlags: ['-a'] },
62
+ // `env [-i0v] [-u name] [-C dir] [name=value]... [utility
63
+ // [argument...]]` — POSIX `env` (`-i`) plus GNU coreutils env(1)'s
64
+ // commonly documented long/short forms. `VAR=val` operands preceding the
65
+ // utility are `env`'s own environment-setting mechanism (distinct from
66
+ // `CallExpr.assigns`). `-S`/`--split-string` is deliberately *not* an
67
+ // `argFlags` entry — see `unresolvableFlags` below.
68
+ {
69
+ names: ['env'],
70
+ skipAssignmentOperands: true,
71
+ noArgFlags: ['-i', '--ignore-environment', '-0', '--null', '-v', '--debug'],
72
+ argFlags: ['-u', '--unset', '-C', '--chdir'],
73
+ // GNU env(1) `-S`/`--split-string=S`: processes and splits `S` into
74
+ // separate arguments, splicing them into the invoked command's argv
75
+ // (documented use: shebang lines with multiple arguments) — verified
76
+ // empirically: `env -S 'echo hello world'` runs `echo` with args
77
+ // `hello`, `world`, not a command literally named by the operand text.
78
+ // The real wrapped command therefore lives *inside* `S`'s own text,
79
+ // not at a fixed "next word" position this table can name — reporting
80
+ // *any* word here as effective (the flag, `S`'s value, or whatever
81
+ // word happens to follow) would be a guess, not a fact. `resolveArgv0`
82
+ // stops here with `reason: 'embedded-command'` instead.
83
+ unresolvableFlags: ['-S', '--split-string'],
84
+ },
85
+ // `nohup utility [argument...]` — POSIX `nohup`. No documented options
86
+ // besides the utility itself; the word right after `nohup` is always
87
+ // the wrapped command.
88
+ { names: ['nohup'] },
89
+ // `nice [-n adjustment] utility [argument...]` (GNU coreutils nice(1))
90
+ // or the POSIX legacy attached form `nice -increment utility` (POSIX
91
+ // `nice`, e.g. `nice -19 cmd`). `noArgFlagPattern` recognizes the
92
+ // attached numeric form; `-n`/`--adjustment` take the adjustment as a
93
+ // separate operand word, or attached (`-n10`, verified empirically
94
+ // against GNU coreutils nice(1) — `nice -n10 cmd` runs `cmd`).
95
+ {
96
+ names: ['nice'],
97
+ argFlags: ['-n', '--adjustment'],
98
+ noArgFlagPattern: /^-\d+$/,
99
+ },
100
+ // `time [-p] utility [argument...]` — the *standalone* `time(1)` utility
101
+ // (POSIX), not bash's `time` reserved word: mvdan/sh always parses a
102
+ // statement-initial, unquoted `time` as a `TimeClause` node (see
103
+ // `enumerate-commands.ts`'s doc comment — `enumerateCommands` treats it
104
+ // as transparent and never emits a `CallExpr`/`CommandSite` for it), so
105
+ // this entry only ever matches an argv0 word reading literally `time`
106
+ // that reached `CallExpr.args` some other way (quoted — `"time" cmd`
107
+ // suppresses reserved-word recognition — or as another wrapper's
108
+ // operand, e.g. `env time cmd`). `-p` (POSIX) and GNU time(1)'s
109
+ // `--verbose`/`--portability` take no operand; `-o file`/`-f format`
110
+ // (GNU) take one.
111
+ {
112
+ names: ['time'],
113
+ noArgFlags: ['-p', '--verbose', '--portability'],
114
+ argFlags: ['-o', '--output', '-f', '--format'],
115
+ },
116
+ // `timeout [OPTION] DURATION COMMAND [ARG]...` — GNU coreutils
117
+ // timeout(1). `--preserve-status`/`--foreground`/`-v` take no operand;
118
+ // `-k`/`-s` take one; `DURATION` is a single mandatory positional
119
+ // operand before the wrapped command, regardless of how many flags
120
+ // precede it.
121
+ {
122
+ names: ['timeout'],
123
+ noArgFlags: ['--preserve-status', '--foreground', '-v', '--verbose'],
124
+ argFlags: ['-k', '--kill-after', '-s', '--signal'],
125
+ positionalOperandsBeforeCommand: 1,
126
+ },
127
+ // sudo(8). Only option forms that are documented (verified against real
128
+ // `sudo -h` usage/option output) to still be followed by a command are
129
+ // modeled: `-A`/`-b`/`-E`/`-H`/`-k`/`-n`/`-P`/`-S` take no operand;
130
+ // `-g group`/`-p prompt`/`-u user` take one, in any getopt-standard
131
+ // attached/clustered form (e.g. `-ualice`, `-Eu alice`). Deliberately
132
+ // excludes `-e`/`-i`/`-l`/`-s`/`-v`/`-K` — sudo(8) documents these as
133
+ // changing sudo's *mode* (edit, login shell, list, shell, validate,
134
+ // reset) such that no ordinary "wrapped command" word necessarily
135
+ // follows at all; modeling them as transparent would risk guessing a
136
+ // command that isn't really there. Also deliberately excludes
137
+ // `-D`/`--chdir`, `-C`/`--close-from`, `-R`/`--chroot`,
138
+ // `-T`/`--command-timeout`, `-h`/`--host`, `-U`/`--other-user`,
139
+ // `--preserve-env[=list]`, and every other option `sudo -h` documents
140
+ // that this table doesn't name above — an unrecognized flag-shaped word
141
+ // now correctly makes the whole resolution unresolvable (see
142
+ // `resolveArgv0`'s doc comment) rather than being silently misidentified
143
+ // as the wrapped command, so leaving these unmodeled is safe, not a gap.
144
+ // `VAR=value` operands before the command are sudo's own
145
+ // environment-setting mechanism, "similar to env" per sudo(8).
146
+ {
147
+ names: ['sudo'],
148
+ skipAssignmentOperands: true,
149
+ noArgFlags: ['-A', '-b', '-E', '-H', '-k', '-n', '-P', '-S'],
150
+ argFlags: ['-g', '-p', '-u'],
151
+ },
152
+ ]);
153
+ /** `true` iff `text` is shaped like a shell assignment operand (`NAME=...`). */
154
+ function isAssignmentShapedText(text) {
155
+ return /^[A-Za-z_][A-Za-z0-9_]*=/.test(text);
156
+ }
157
+ /** `true` iff `text` is a `--long-flag=value` attached form of one of `flags`. */
158
+ function matchesAttachedLongFlag(text, flags) {
159
+ return flags.some((flag) => flag.startsWith('--') && text.startsWith(`${flag}=`));
160
+ }
161
+ /**
162
+ * `true` iff `text` is shaped like a command-line flag: starts with `-`
163
+ * but isn't exactly `--` (the end-of-options marker, handled separately)
164
+ * or a bare `-` (POSIX reserves a lone `-` as an ordinary operand — e.g.
165
+ * "read from stdin" — never an option, so it is not flag-shaped here
166
+ * either).
167
+ */
168
+ function looksLikeFlag(text) {
169
+ return text.length > 1 && text.startsWith('-') && text !== '--';
170
+ }
171
+ /**
172
+ * `true` iff `text` matches one of `flags` for {@link WrapperSpec}'s
173
+ * "whole-token or attached-value" fields ({@link WrapperSpec.unresolvableFlags},
174
+ * {@link WrapperSpec.stopsChainFlags}): an exact match, a `--flag=value`
175
+ * attached form for a `--`-prefixed entry, or a `-Xvalue` attached form
176
+ * for a 2-character short entry. Unlike {@link WrapperSpec.argFlags}, this
177
+ * does not participate in short-flag *clustering* (see
178
+ * {@link matchShortFlagCluster}'s doc comment) — a flag combined into a
179
+ * cluster with other short flags (e.g. hypothetically `-iS'cmd'` for
180
+ * `env`) falls through to the generic unknown-flag path instead of this
181
+ * more specific one; still fails closed, just with a less specific reason.
182
+ */
183
+ function matchesWholeOrAttachedFlag(text, flags) {
184
+ return flags.some((flag) => {
185
+ if (text === flag)
186
+ return true;
187
+ if (flag.startsWith('--'))
188
+ return text.startsWith(`${flag}=`);
189
+ return flag.length === 2 && text.startsWith(flag) && text.length > flag.length;
190
+ });
191
+ }
192
+ /**
193
+ * Recognizes a short-option cluster/attached form for `text` against
194
+ * `spec`'s `noArgFlags`/`argFlags` — the getopt-standard shapes
195
+ * {@link WrapperSpec.argFlags}'s doc comment documents:
196
+ *
197
+ * - `-Xvalue` — a single `argFlags` character `X` with its operand
198
+ * attached directly (`-ualice`, `-n10`).
199
+ * - `-ABX` / `-ABXvalue` — one or more `noArgFlags` characters (`A`, `B`,
200
+ * …) clustered before a final `argFlags` character `X`, whose operand is
201
+ * either attached (`-Eualice`) or the next separate word (`-Eu alice`).
202
+ * - `-AB` — two or more `noArgFlags` characters clustered with no
203
+ * trailing `argFlags` character at all (`-iv`).
204
+ *
205
+ * Scans `text`'s characters left to right; the first character that is
206
+ * neither a recognized `noArgFlags` nor `argFlags` entry (for that single
207
+ * character, as `-X`) fails the whole match (`'no-match'`) — real getopt
208
+ * clustering has the same "first bad option char aborts" behavior, and a
209
+ * partially-matched guess would be worse than failing closed. Whole-token
210
+ * exact matches and `--`-prefixed long flags are handled earlier by the
211
+ * caller and never reach here (this function only ever sees text that
212
+ * didn't already match `noArgFlags`/`argFlags`/`noArgFlagPattern`/attached
213
+ * long-flag checks directly).
214
+ *
215
+ * Returns `'one'` if the whole `text` token is consumed with no further
216
+ * operand word needed (the final flag was `noArgFlags`, or an `argFlags`
217
+ * character had an attached operand), `'two'` if the next, separate word
218
+ * is the final `argFlags` character's operand, or `'no-match'` if `text`
219
+ * isn't a valid cluster/attached form for `spec` at all.
220
+ */
221
+ function matchShortFlagCluster(text, spec) {
222
+ if (!text.startsWith('-') || text.startsWith('--') || text.length < 2) {
223
+ return 'no-match';
224
+ }
225
+ const chars = text.slice(1);
226
+ for (let i = 0; i < chars.length; i += 1) {
227
+ const flagToken = `-${chars[i]}`;
228
+ if (spec.noArgFlags?.includes(flagToken) === true) {
229
+ continue;
230
+ }
231
+ if (spec.argFlags?.includes(flagToken) === true) {
232
+ return chars.slice(i + 1).length > 0 ? 'one' : 'two';
233
+ }
234
+ return 'no-match';
235
+ }
236
+ // Every character matched a noArgFlags entry — a pure no-operand
237
+ // cluster (e.g. `-iv`).
238
+ return 'one';
239
+ }
240
+ function findWrapperSpec(wrappers, text) {
241
+ return wrappers.find((spec) => spec.names.includes(text));
242
+ }
243
+ /**
244
+ * Walks `argv` forward from `startIndex`, skipping every word this `spec`
245
+ * structurally recognizes as its own flag, flag-operand (in any
246
+ * getopt-standard attached/clustered form — see
247
+ * {@link matchShortFlagCluster}'s doc comment), `VAR=val` operand, or
248
+ * required positional operand, and classifies what comes next:
249
+ *
250
+ * - `{ kind: 'next', index }` — either the wrapped command (an ordinary
251
+ * word not shaped like a flag), or a dynamic (`static: false`) word,
252
+ * which the caller's own loop already handles via `WordResolution`'s
253
+ * normal "never guessed through" rule.
254
+ * - `{ kind: 'truncated' }` — `spec`'s own words ran past the end of
255
+ * `argv` with no wrapped-command word found (a malformed or truncated
256
+ * invocation, e.g. bare `env` with nothing else, or `sudo -u` with no
257
+ * operand for `-u`).
258
+ * - `{ kind: 'unresolvable', reason }` — a *statically known* word that
259
+ * looks like a flag for this wrapper (unquoted, unescaped `-` prefix,
260
+ * not `--`) but matches none of `spec`'s recognized flag/operand shapes,
261
+ * or matches one of `spec.unresolvableFlags` (see
262
+ * {@link WrapperSpec.unresolvableFlags}'s doc comment). Such a word is
263
+ * *never* treated as the wrapped command candidate: a flag-shaped word
264
+ * this table doesn't recognize is far more likely to be an
265
+ * unmodeled/unsupported option than a command literally named e.g.
266
+ * `-D` — see {@link resolveArgv0}'s doc comment.
267
+ *
268
+ * A literal `--` (the standard getopt(3)/getopt_long(3) end-of-options
269
+ * marker — POSIX Utility Syntax Guidelines, guideline 10) is skipped like
270
+ * any other recognized token, but — critically — everything *after* it is
271
+ * no longer eligible for **any** flag/operand recognition at all
272
+ * (`noArgFlags`, `noArgFlagPattern`, `argFlags` in every form,
273
+ * `unresolvableFlags`, `stopsChainFlags`, or the generic
274
+ * flag-shape/`'unknown-flag'` check): once options end, a later word that
275
+ * merely *looks* like a flag (e.g. `-u`) is an ordinary operand — real
276
+ * `sudo(8)` documents this explicitly ("command line arguments after the
277
+ * `--` are passed to the command as-is"), and it's the entire point of
278
+ * `--` for every wrapper here. A wrapper's mandatory positional operand
279
+ * (e.g. `timeout`'s `DURATION`) is a distinct, non-flag concept and is
280
+ * still consumed normally after `--` — only *flag* recognition stops.
281
+ * `VAR=val` operand recognition (`skipAssignmentOperands`) is likewise
282
+ * unaffected by `--`, matching GNU env(1)'s own two-phase argument
283
+ * scanning (option parsing, then a separate `NAME=value` scan over
284
+ * whatever's left) — `--` only ever disables *option* (flag) parsing.
285
+ */
286
+ function advancePastWrapperOperands(argv, startIndex, spec) {
287
+ let index = startIndex;
288
+ let positionalSeen = 0;
289
+ let optionsEnded = false;
290
+ const requiredPositional = spec.positionalOperandsBeforeCommand ?? 0;
291
+ while (index < argv.length) {
292
+ // `index < argv.length` just checked above, so this is a real element.
293
+ const word = argv[index];
294
+ if (!word.static) {
295
+ // Can't be identified as this wrapper's own flag/operand text at
296
+ // all — never guessed through, so this word itself is the next
297
+ // chain element (the caller's loop will see it's unresolvable and
298
+ // stop there via `WordResolution`'s own `static: false`).
299
+ return { kind: 'next', index };
300
+ }
301
+ const text = word.text;
302
+ if (!optionsEnded && text === '--') {
303
+ optionsEnded = true;
304
+ index += 1;
305
+ continue;
306
+ }
307
+ if (!optionsEnded && spec.skipAssignmentOperands === true && isAssignmentShapedText(text)) {
308
+ index += 1;
309
+ continue;
310
+ }
311
+ if (!optionsEnded) {
312
+ if (spec.unresolvableFlags !== undefined &&
313
+ matchesWholeOrAttachedFlag(text, spec.unresolvableFlags)) {
314
+ return { kind: 'unresolvable', reason: 'embedded-command' };
315
+ }
316
+ if (spec.stopsChainFlags !== undefined &&
317
+ matchesWholeOrAttachedFlag(text, spec.stopsChainFlags)) {
318
+ // This wrapper's own invocation is the effective command — the
319
+ // caller must not advance past it at all. Modeled as "truncated":
320
+ // the chain simply ends at the wrapper word already pushed, the
321
+ // same outcome as a genuinely truncated invocation (see
322
+ // `resolveArgv0`'s loop) — nothing more to skip or resolve.
323
+ return { kind: 'truncated' };
324
+ }
325
+ if (spec.noArgFlags?.includes(text) === true) {
326
+ index += 1;
327
+ continue;
328
+ }
329
+ if (spec.noArgFlagPattern?.test(text) === true) {
330
+ index += 1;
331
+ continue;
332
+ }
333
+ if (spec.argFlags?.includes(text) === true) {
334
+ index += 2;
335
+ continue;
336
+ }
337
+ if (spec.argFlags !== undefined && matchesAttachedLongFlag(text, spec.argFlags)) {
338
+ index += 1;
339
+ continue;
340
+ }
341
+ const clusterMatch = matchShortFlagCluster(text, spec);
342
+ if (clusterMatch === 'one') {
343
+ index += 1;
344
+ continue;
345
+ }
346
+ if (clusterMatch === 'two') {
347
+ index += 2;
348
+ continue;
349
+ }
350
+ // A flag-shaped word this wrapper doesn't recognize is checked
351
+ // *before* the positional-operand fallback below: real getopt-based
352
+ // parsers reject an unrecognized option outright rather than
353
+ // reinterpreting it as a positional operand (verified against GNU
354
+ // timeout(1)'s getopt_long-based option parsing) — so a required
355
+ // positional slot (e.g. `timeout`'s `DURATION`) may only ever be
356
+ // filled by a non-flag-shaped word, never by an unrecognized `-x`.
357
+ // None of this applies once `optionsEnded` — see this function's
358
+ // doc comment.
359
+ if (looksLikeFlag(text)) {
360
+ return { kind: 'unresolvable', reason: 'unknown-flag' };
361
+ }
362
+ }
363
+ if (positionalSeen < requiredPositional) {
364
+ positionalSeen += 1;
365
+ index += 1;
366
+ continue;
367
+ }
368
+ return { kind: 'next', index };
369
+ }
370
+ return { kind: 'truncated' };
371
+ }
372
+ /** `true` iff `value` is a non-empty array of non-empty strings. */
373
+ function isNonEmptyStringArray(value) {
374
+ return (Array.isArray(value) &&
375
+ value.length > 0 &&
376
+ value.every((item) => typeof item === 'string' && item.length > 0));
377
+ }
378
+ /**
379
+ * Validates one injected {@link WrapperSpec} entry's shape, throwing
380
+ * {@link ShAnalyzeInvalidWrapperSpecError} on the first problem found —
381
+ * see {@link ResolveArgv0Options.transparentWrappers}'s doc comment for
382
+ * why this happens at the boundary rather than surfacing as a confusing
383
+ * native `TypeError` deep inside flag matching.
384
+ */
385
+ function validateWrapperSpec(spec, index) {
386
+ const context = `transparentWrappers[${String(index)}]`;
387
+ if (!isNonEmptyStringArray(spec.names)) {
388
+ throw new ShAnalyzeInvalidWrapperSpecError(`${context}.names must be a non-empty array of non-empty strings`);
389
+ }
390
+ const stringArrayFields = [
391
+ 'noArgFlags',
392
+ 'argFlags',
393
+ 'unresolvableFlags',
394
+ 'stopsChainFlags',
395
+ ];
396
+ for (const field of stringArrayFields) {
397
+ const value = spec[field];
398
+ if (value !== undefined &&
399
+ !(Array.isArray(value) && value.every((item) => typeof item === 'string'))) {
400
+ throw new ShAnalyzeInvalidWrapperSpecError(`${context}.${field} must be an array of strings when present`);
401
+ }
402
+ }
403
+ if (spec.skipAssignmentOperands !== undefined &&
404
+ typeof spec.skipAssignmentOperands !== 'boolean') {
405
+ throw new ShAnalyzeInvalidWrapperSpecError(`${context}.skipAssignmentOperands must be a boolean when present`);
406
+ }
407
+ if (spec.noArgFlagPattern !== undefined && !(spec.noArgFlagPattern instanceof RegExp)) {
408
+ throw new ShAnalyzeInvalidWrapperSpecError(`${context}.noArgFlagPattern must be a RegExp when present`);
409
+ }
410
+ if (spec.positionalOperandsBeforeCommand !== undefined &&
411
+ !(typeof spec.positionalOperandsBeforeCommand === 'number' &&
412
+ Number.isInteger(spec.positionalOperandsBeforeCommand) &&
413
+ spec.positionalOperandsBeforeCommand >= 0)) {
414
+ throw new ShAnalyzeInvalidWrapperSpecError(`${context}.positionalOperandsBeforeCommand must be a non-negative integer when present`);
415
+ }
416
+ }
417
+ /**
418
+ * Follows `site`'s argv0 through zero or more *transparent wrappers* —
419
+ * commands whose own argv0 isn't the command actually run, because they
420
+ * just locate and re-exec another command (`env FOO=1 rm -rf /`,
421
+ * `command rm`, `nohup rm`, `sudo -u x "$prog"`) — to the **effective**
422
+ * command: the one a permission/policy check must actually judge, since
423
+ * argv0 alone is trivially spoofable through any of these.
424
+ *
425
+ * The wrapper table ({@link ResolveArgv0Options.transparentWrappers},
426
+ * defaulting to {@link DEFAULT_TRANSPARENT_WRAPPERS}) is plain data: each
427
+ * {@link WrapperSpec} declares how to locate the word it wraps, and
428
+ * neither this function nor the table encodes any safety judgment about
429
+ * *which* commands are dangerous — matching {@link CommandSite}'s and
430
+ * {@link resolveWord}'s "facts only" posture. A caller can drop a default
431
+ * entry (that name then resolves as an ordinary, non-transparent
432
+ * command) or add their own (e.g. a project's `with-retry` helper) and
433
+ * both directions are followed identically to any built-in entry.
434
+ *
435
+ * **A statically-unknowable word is never guessed through** — three
436
+ * distinct cases all stop the chain immediately at that word rather than
437
+ * falling back to any other guess:
438
+ *
439
+ * 1. `argv0` or any wrapper's located word itself resolves `static: false`
440
+ * (an expansion, tilde, glob, …) — `sudo -u x "$prog"` reports
441
+ * `effective: { static: false, reason: 'expansion' }` with
442
+ * `chain: [<sudo>, <"$prog">]`, not `rm`/`sudo`/anything static.
443
+ * 2. A *statically known* word shaped like a flag (`-`-prefixed, not
444
+ * `--`) doesn't match any flag/operand shape the current
445
+ * {@link WrapperSpec} recognizes — `sudo -D /tmp rm x` (`-D` isn't
446
+ * modeled for `sudo`) reports an `effective` of `static: false` with
447
+ * `reason: 'unknown-flag'`, **not** `rm` (the pre-fix behavior: any
448
+ * unrecognized word, flag-shaped or not, was silently treated as the
449
+ * wrapped command — a false report that hid the real effective
450
+ * command behind a flag this table simply hadn't modeled yet).
451
+ * 3. A recognized flag's value structurally embeds the real command
452
+ * rather than naming it as a separate word — `env -S 'rm -rf /'`
453
+ * reports `effective: { static: false, reason: 'embedded-command' }`
454
+ * (see {@link WrapperSpec.unresolvableFlags}'s doc comment).
455
+ *
456
+ * `VAR=val` shell-assignment prefixes on the `CallExpr` itself
457
+ * (`CommandSite.node.assigns`, e.g. `FOO=bar rm x`) are always skipped and
458
+ * counted in {@link Argv0Resolution.assignmentsSkipped} — a mechanism
459
+ * distinct from a wrapper's own `VAR=val` *operands* (`env A=1 rm x`,
460
+ * `sudo A=1 rm x`), which {@link WrapperSpec.skipAssignmentOperands}
461
+ * handles per-wrapper.
462
+ *
463
+ * @param site - A {@link CommandSite}, typically from {@link enumerateCommands}.
464
+ * @param options - See {@link ResolveArgv0Options}.
465
+ * @throws TypeError if `site.argv` is empty — a programmer-error misuse of
466
+ * the API (every `CommandSite` `enumerateCommands` ever produces has at
467
+ * least one argv word; see its doc comment), not a "malformed shell
468
+ * source" case.
469
+ * @throws {@link ShAnalyzeInvalidWrapperSpecError} if
470
+ * `options.transparentWrappers` contains a malformed entry (see
471
+ * {@link ResolveArgv0Options.transparentWrappers}'s doc comment).
472
+ * @public
473
+ */
474
+ export function resolveArgv0(site, options) {
475
+ if (site.argv.length === 0) {
476
+ throw new TypeError('resolveArgv0 expects a CommandSite with at least one argv word');
477
+ }
478
+ const wrappers = options?.transparentWrappers ?? DEFAULT_TRANSPARENT_WRAPPERS;
479
+ if (options?.transparentWrappers !== undefined) {
480
+ wrappers.forEach(validateWrapperSpec);
481
+ }
482
+ const assignmentsSkipped = nodeArray(site.node.assigns).length;
483
+ const argv = site.argv;
484
+ const chain = [];
485
+ let index = 0;
486
+ while (index < argv.length) {
487
+ // `index < argv.length` just checked above, so this is a real element.
488
+ const word = argv[index];
489
+ chain.push(word);
490
+ if (!word.static)
491
+ break;
492
+ const spec = findWrapperSpec(wrappers, word.text);
493
+ if (spec === undefined)
494
+ break;
495
+ const result = advancePastWrapperOperands(argv, index + 1, spec);
496
+ if (result.kind === 'truncated')
497
+ break;
498
+ if (result.kind === 'unresolvable') {
499
+ chain.push({ static: false, reason: result.reason });
500
+ break;
501
+ }
502
+ index = result.index;
503
+ }
504
+ // `site.argv.length > 0` is checked above, so the loop above always runs
505
+ // at least one iteration and pushes `argv[0]` before any `break` — chain
506
+ // always has at least one element.
507
+ const effective = chain[chain.length - 1];
508
+ return { chain, effective, assignmentsSkipped };
509
+ }
510
+ //# sourceMappingURL=resolve-argv0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-argv0.js","sourceRoot":"","sources":["../../src/analyze/resolve-argv0.ts"],"names":[],"mappings":"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"}