sh-ast 0.0.0 → 0.1.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 +147 -0
- package/dist/analyze/ansi-c-escapes.d.ts +53 -0
- package/dist/analyze/ansi-c-escapes.d.ts.map +1 -0
- package/dist/analyze/ansi-c-escapes.js +255 -0
- package/dist/analyze/ansi-c-escapes.js.map +1 -0
- package/dist/analyze/decode-lit.d.ts +74 -0
- package/dist/analyze/decode-lit.d.ts.map +1 -0
- package/dist/analyze/decode-lit.js +114 -0
- package/dist/analyze/decode-lit.js.map +1 -0
- package/dist/analyze/enumerate-commands.d.ts +159 -0
- package/dist/analyze/enumerate-commands.d.ts.map +1 -0
- package/dist/analyze/enumerate-commands.js +390 -0
- package/dist/analyze/enumerate-commands.js.map +1 -0
- package/dist/analyze/index.d.ts +18 -0
- package/dist/analyze/index.d.ts.map +1 -0
- package/dist/analyze/index.js +27 -0
- package/dist/analyze/index.js.map +1 -0
- package/dist/analyze/node-helpers.d.ts +28 -0
- package/dist/analyze/node-helpers.d.ts.map +1 -0
- package/dist/analyze/node-helpers.js +37 -0
- package/dist/analyze/node-helpers.js.map +1 -0
- package/dist/analyze/resolve-word.d.ts +146 -0
- package/dist/analyze/resolve-word.d.ts.map +1 -0
- package/dist/analyze/resolve-word.js +202 -0
- package/dist/analyze/resolve-word.js.map +1 -0
- package/dist/analyze.d.ts +390 -0
- package/dist/deep-freeze.d.ts +14 -0
- package/dist/deep-freeze.d.ts.map +1 -0
- package/dist/deep-freeze.js +25 -0
- package/dist/deep-freeze.js.map +1 -0
- package/dist/errors.d.ts +115 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +106 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/normalize.d.ts +52 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +260 -0
- package/dist/normalize.js.map +1 -0
- package/dist/parse.d.ts +45 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +118 -0
- package/dist/parse.js.map +1 -0
- package/dist/sh-ast.d.ts +1270 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/visitor-keys.d.ts +21 -0
- package/dist/visitor-keys.d.ts.map +1 -0
- package/dist/visitor-keys.js +23 -0
- package/dist/visitor-keys.js.map +1 -0
- package/dist/walk.d.ts +18 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +40 -0
- package/dist/walk.js.map +1 -0
- package/dist/wasm-instance.d.ts +13 -0
- package/dist/wasm-instance.d.ts.map +1 -0
- package/dist/wasm-instance.js +88 -0
- package/dist/wasm-instance.js.map +1 -0
- package/generated/child-type-schema.d.ts +14 -0
- package/generated/child-type-schema.js +188 -0
- package/generated/node-types.d.ts +958 -0
- package/generated/position-fields.d.ts +21 -0
- package/generated/position-fields.js +50 -0
- package/generated/visitor-keys.d.ts +11 -0
- package/generated/visitor-keys.js +50 -0
- package/package.json +93 -10
- package/shim/sh-ast.wasm +0 -0
- package/shim/wasm_exec.js +654 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode-lit.js","sourceRoot":"","sources":["../../src/analyze/decode-lit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAErE,uGAAuG;AACvG,MAAM,kBAAkB,GAAwB,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAwBpE;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACxB,IAAI,IAAI,IAAI,CAAC;YACb,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAChC,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QACD,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/B,QAAQ,IAAI,EAAE,CAAC;QACjB,CAAC;QACD,IAAI,IAAI,EAAE,CAAC;QACX,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACrC,CAAC;AAED,uFAAuF;AACvF,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;AAEhF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACxB,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,IAAI,IAAI,IAAI,CAAC;gBACb,CAAC,IAAI,CAAC,CAAC;gBACP,SAAS;YACX,CAAC;YACD,oEAAoE;YACpE,IAAI,IAAI,EAAE,CAAC;YACX,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,IAAI,EAAE,CAAC;QACX,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { ShNode } from '../types.js';
|
|
2
|
+
import type { WordResolution } from './resolve-word.js';
|
|
3
|
+
/**
|
|
4
|
+
* A single frame of the path from the root of the tree down to a
|
|
5
|
+
* {@link CommandSite}, describing *how* the command is reached — never
|
|
6
|
+
* whether it is safe to run. `CommandSite.context` is an ordered stack of
|
|
7
|
+
* these, outermost frame first:
|
|
8
|
+
*
|
|
9
|
+
* - `'and'`/`'or'` (`side: 'right'`) — the right-hand operand of a
|
|
10
|
+
* `BinaryCmd` (mvdan/sh's node for both `&&` and `||`); only the right
|
|
11
|
+
* side is tagged; the left side inherits the surrounding context
|
|
12
|
+
* unchanged, since it runs unconditionally relative to this operator.
|
|
13
|
+
* - `'pipeline'` (`stage: n`) — one stage of a `|`/`|&` chain (also a
|
|
14
|
+
* `BinaryCmd`, left-associatively nested by mvdan/sh); every stage is
|
|
15
|
+
* tagged, 0-indexed left to right, regardless of whether the chain mixes
|
|
16
|
+
* `|` and `|&`.
|
|
17
|
+
* - `'subshell'` — inside a `Subshell` (`( ... )`).
|
|
18
|
+
* - `'cmdSubst'`/`'procSubst'` — inside a `CmdSubst` (`$(...)`/backticks) or
|
|
19
|
+
* `ProcSubst` (`<(...)`/`>(...)`) reached from *any* word-bearing
|
|
20
|
+
* position (an argument, a redirection target, a case subject, a loop's
|
|
21
|
+
* word list, an assignment value, a test/arithmetic operand, …) — not
|
|
22
|
+
* only from `CallExpr.args`.
|
|
23
|
+
* - `'if'` (`branch: 'cond' | 'then' | 'else'`) — inside an `IfClause`'s
|
|
24
|
+
* condition, then-branch, or else-branch (mvdan/sh nests `elif` chains as
|
|
25
|
+
* `IfClause.else` pointing to another `IfClause`, so an `elif`'s own
|
|
26
|
+
* condition/then are reached through an `{kind:'if',branch:'else'}` frame
|
|
27
|
+
* first, then their own `'cond'`/`'then'` frame — reflecting the real
|
|
28
|
+
* nesting rather than collapsing it).
|
|
29
|
+
* - `'case'` — inside one `CaseClause` branch's statement list (not the
|
|
30
|
+
* case subject word or the patterns).
|
|
31
|
+
* - `'loop'` (`role` is `'body'` or `'cond'`) — inside a `ForClause`'s
|
|
32
|
+
* statement list (`role: 'body'` only — a `for` loop has no
|
|
33
|
+
* statement-list condition) or a `WhileClause`'s (`role: 'cond'` for the
|
|
34
|
+
* condition, `role: 'body'` for the loop body).
|
|
35
|
+
* - `'function'` (`name`) — inside a `FuncDecl`'s body; `name` is the
|
|
36
|
+
* function's literal name text.
|
|
37
|
+
* - `'background'`/`'negated'` — the enclosing `Stmt` has mvdan/sh's
|
|
38
|
+
* Background/Negated flag set (`cmd &`, `! cmd`).
|
|
39
|
+
* - `'coproc'` — inside a `CoprocClause`'s statement (a `coproc` block,
|
|
40
|
+
* optionally named).
|
|
41
|
+
*
|
|
42
|
+
* A `Block` grouping (`{ ...; }`) and a `TimeClause` (`time cmd`) are
|
|
43
|
+
* deliberately transparent — grouping and timing a command doesn't change
|
|
44
|
+
* how it's reached, so no frame is added for either.
|
|
45
|
+
*
|
|
46
|
+
* This union may grow in a **minor** release — a future mvdan/sh grammar
|
|
47
|
+
* construct this module starts modeling can add a new `kind` variant
|
|
48
|
+
* without that being a breaking change (mirroring
|
|
49
|
+
* {@link WordResolutionReason}'s semver policy in `resolve-word.ts`). It is
|
|
50
|
+
* deliberately not sealed against extension elsewhere in the codebase.
|
|
51
|
+
* Every existing variant's shape (its extra fields, if any) is stable —
|
|
52
|
+
* only new variants are ever added — so an exhaustive compile-time
|
|
53
|
+
* `switch` over `.kind` should still include a `default` case to stay
|
|
54
|
+
* forward-compatible.
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
export type CommandContext = {
|
|
59
|
+
readonly kind: 'and';
|
|
60
|
+
readonly side: 'right';
|
|
61
|
+
} | {
|
|
62
|
+
readonly kind: 'or';
|
|
63
|
+
readonly side: 'right';
|
|
64
|
+
} | {
|
|
65
|
+
readonly kind: 'pipeline';
|
|
66
|
+
readonly stage: number;
|
|
67
|
+
} | {
|
|
68
|
+
readonly kind: 'subshell';
|
|
69
|
+
} | {
|
|
70
|
+
readonly kind: 'cmdSubst';
|
|
71
|
+
} | {
|
|
72
|
+
readonly kind: 'procSubst';
|
|
73
|
+
} | {
|
|
74
|
+
readonly kind: 'if';
|
|
75
|
+
readonly branch: 'then' | 'else' | 'cond';
|
|
76
|
+
} | {
|
|
77
|
+
readonly kind: 'case';
|
|
78
|
+
} | {
|
|
79
|
+
readonly kind: 'loop';
|
|
80
|
+
readonly role: 'body' | 'cond';
|
|
81
|
+
} | {
|
|
82
|
+
readonly kind: 'function';
|
|
83
|
+
readonly name: string;
|
|
84
|
+
} | {
|
|
85
|
+
readonly kind: 'background';
|
|
86
|
+
} | {
|
|
87
|
+
readonly kind: 'negated';
|
|
88
|
+
} | {
|
|
89
|
+
readonly kind: 'coproc';
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* One place in the tree where a command is actually invoked — a `CallExpr`
|
|
93
|
+
* node, together with its resolved words and the path used to reach it.
|
|
94
|
+
* Facts only, matching {@link resolveWord}'s posture: no safety verdict, no
|
|
95
|
+
* hardcoded command/wrapper list. A dynamic (`static: false`) `argv0` is a
|
|
96
|
+
* normal, expected result — not an error and not itself reported as
|
|
97
|
+
* "unknown"/"unsafe".
|
|
98
|
+
*
|
|
99
|
+
* @public
|
|
100
|
+
*/
|
|
101
|
+
export interface CommandSite {
|
|
102
|
+
/** The `CallExpr` node this site was found at. */
|
|
103
|
+
readonly node: ShNode;
|
|
104
|
+
/**
|
|
105
|
+
* `resolveWord` applied to the first word (`argv[0]`), with
|
|
106
|
+
* `{ context: 'command-argument' }` — every `CallExpr` word is an
|
|
107
|
+
* ordinary command-argument position, never an assignment value, so
|
|
108
|
+
* only a word-initial unquoted `~` triggers tilde expansion (an
|
|
109
|
+
* unquoted `~` after a `:`, e.g. `a:~/b`, is literal text here — see
|
|
110
|
+
* `ResolveWordOptions.context`'s doc comment).
|
|
111
|
+
*/
|
|
112
|
+
readonly argv0: WordResolution;
|
|
113
|
+
/** `resolveWord` applied to every word, in argument order (same `{ context: 'command-argument' }` as {@link CommandSite.argv0}). */
|
|
114
|
+
readonly argv: readonly WordResolution[];
|
|
115
|
+
/** The path from the tree root to this site, outermost frame first. */
|
|
116
|
+
readonly context: readonly CommandContext[];
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Enumerates every command invocation (`CallExpr`) reachable from `root`,
|
|
120
|
+
* with its resolved words ({@link resolveWord}) and the path used to reach
|
|
121
|
+
* it ({@link CommandContext}). Descends everywhere a command can occur:
|
|
122
|
+
* statement lists, both sides of `&&`/`||`/pipelines, subshells/blocks,
|
|
123
|
+
* if/case branches *and* conditions, loop bodies *and* conditions, function
|
|
124
|
+
* bodies, background/negated/coproc statements, and — critically —
|
|
125
|
+
* `CmdSubst`/`ProcSubst` nested inside words, wherever those words occur
|
|
126
|
+
* (arguments, redirection targets, case subjects/patterns, loop word
|
|
127
|
+
* lists, assignment values, test/arithmetic operands, …), not only inside
|
|
128
|
+
* `CallExpr.args`.
|
|
129
|
+
*
|
|
130
|
+
* An assignment-only `CallExpr` (`FOO=bar`, `args` empty) has no first word
|
|
131
|
+
* to resolve and is not itself a command invocation — no program runs — so
|
|
132
|
+
* it produces no {@link CommandSite}; any command substitution nested in
|
|
133
|
+
* its assigned value (`FOO=$(sub)`) is still found and reported.
|
|
134
|
+
*
|
|
135
|
+
* Results are sorted by source position (`node.range[0]`), so nested
|
|
136
|
+
* command substitutions and out-of-structural-order redirection targets
|
|
137
|
+
* still come back in source order regardless of traversal order.
|
|
138
|
+
*
|
|
139
|
+
* Facts only, matching {@link resolveWord}'s posture: no safety verdict, no
|
|
140
|
+
* command/wrapper allowlist or denylist, no dataflow. A statically unknown
|
|
141
|
+
* `argv0` (`static: false`) is a normal, expected result.
|
|
142
|
+
*
|
|
143
|
+
* A long *linear* chain — `|`/`|&`/`&&`/`||` of any realistic length — is
|
|
144
|
+
* traversed iteratively and never risks a stack overflow or trips the
|
|
145
|
+
* nesting-depth guard below, regardless of how many stages/links it has.
|
|
146
|
+
*
|
|
147
|
+
* @param root - Any `ShNode` — typically a `File` from `parseSync`, but any
|
|
148
|
+
* subtree (a `Stmt`, a `Command`, or even a bare `Word`) is handled.
|
|
149
|
+
* @throws {@link ShAnalyzeMaxDepthError} if `root`'s genuinely nested
|
|
150
|
+
* structure (subshells within subshells, chained command/process
|
|
151
|
+
* substitutions, deeply nested `if`/`case`/loop/function/`time`/`{ }`
|
|
152
|
+
* bodies, chained `elif`) exceeds this module's defensive recursion-depth
|
|
153
|
+
* guard — see that error's doc comment for why this fails closed instead
|
|
154
|
+
* of returning a partial result, and why a gate-style consumer should treat
|
|
155
|
+
* it as `deny`.
|
|
156
|
+
* @public
|
|
157
|
+
*/
|
|
158
|
+
export declare function enumerateCommands(root: ShNode): CommandSite[];
|
|
159
|
+
//# sourceMappingURL=enumerate-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enumerate-commands.d.ts","sourceRoot":"","sources":["../../src/analyze/enumerate-commands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GAC9B;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;CAAE,GAClE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAC/B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEhC;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,oIAAoI;IACpI,QAAQ,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,CAAC;IACzC,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;CAC7C;AA2aD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAU7D"}
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { ShAnalyzeMaxDepthError } from '../errors.js';
|
|
2
|
+
import { isShNodeShape, nodeArray, stringField } from './node-helpers.js';
|
|
3
|
+
import { resolveWord } from './resolve-word.js';
|
|
4
|
+
/**
|
|
5
|
+
* mvdan/sh v3.13.1's `syntax.BinCmdOperator` token values for `BinaryCmd.Op`
|
|
6
|
+
* — `AndStmt` (`&&`), `OrStmt` (`||`), `Pipe` (`|`), `PipeAll` (`|&`). These
|
|
7
|
+
* are not part of any documented wire contract (`normalize()` copies
|
|
8
|
+
* mvdan/sh's raw numeric token straight through — see design/ARCHITECTURE.md
|
|
9
|
+
* open question 4), so they're pinned here by empirical observation against
|
|
10
|
+
* this bridge's own shim rather than derived from an upstream constant, and
|
|
11
|
+
* locked in by a canary test (`enumerate-commands.test.ts`, "binary command
|
|
12
|
+
* operator identification") that would fail loudly if a future mvdan/sh
|
|
13
|
+
* version renumbered them.
|
|
14
|
+
*/
|
|
15
|
+
const BIN_CMD_OP_AND = 11;
|
|
16
|
+
const BIN_CMD_OP_OR = 12;
|
|
17
|
+
const BIN_CMD_OP_PIPE = 13;
|
|
18
|
+
const BIN_CMD_OP_PIPE_ALL = 14;
|
|
19
|
+
function isPipeOp(op) {
|
|
20
|
+
return op === BIN_CMD_OP_PIPE || op === BIN_CMD_OP_PIPE_ALL;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The maximum number of *genuinely nested* structural frames (subshells,
|
|
24
|
+
* command/process substitutions, `if`/`case`/loop/function/`time`/`{ }`
|
|
25
|
+
* bodies, chained `elif`) {@link enumerateCommands} descends through before
|
|
26
|
+
* throwing {@link ShAnalyzeMaxDepthError} — a defensive backstop against
|
|
27
|
+
* pathological/adversarial input (a native stack overflow is an
|
|
28
|
+
* uncontrolled crash, not a catchable error). A long *linear* chain
|
|
29
|
+
* (`|`/`&&`/`||` of any length) never grows this counter — see
|
|
30
|
+
* `flattenPipelineStages`'s and `visitBinaryCmd`'s doc comments — only real
|
|
31
|
+
* nesting does.
|
|
32
|
+
*
|
|
33
|
+
* This value is deliberately *not* a round, "generous"-sounding number
|
|
34
|
+
* like 10,000: measured empirically (synthetic nested-`Subshell` trees,
|
|
35
|
+
* this repo's actual `vitest` worker environment, default Node/V8 stack
|
|
36
|
+
* size — see `analyze-enumerate-commands.test.ts`'s depth-guard describe
|
|
37
|
+
* block), this module's own recursive descent through a real
|
|
38
|
+
* `Subshell`-in-`Subshell` chain hits a raw, uncatchable
|
|
39
|
+
* `RangeError: Maximum call stack size exceeded` starting somewhere around
|
|
40
|
+
* depth 1,000–1,500 (the exact point varies run to run — it's a hard
|
|
41
|
+
* native-stack limit, not a clean threshold). A depth guard set at 10,000
|
|
42
|
+
* would never actually fire: the real crash happens first. 500 leaves
|
|
43
|
+
* roughly 2–3x headroom below the *lowest* observed native-crash depth,
|
|
44
|
+
* while still comfortably exceeding any realistic hand-written script's
|
|
45
|
+
* nesting (this is pathological-input protection, not a normal-usage
|
|
46
|
+
* ceiling).
|
|
47
|
+
*/
|
|
48
|
+
const MAX_STRUCTURAL_DEPTH = 500;
|
|
49
|
+
function pushContext(context, frame) {
|
|
50
|
+
return [...context, frame];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Scans `value` — a word, an assignment, a test/arithmetic expression, a
|
|
54
|
+
* redirect, or an array of any of these — for `CmdSubst`/`ProcSubst`
|
|
55
|
+
* boundaries reachable from it, however deeply nested (through
|
|
56
|
+
* `DblQuoted`, `ParamExp`'s `Exp`/`Repl`/`Slice`/`nestedparam`, array
|
|
57
|
+
* elements, arithmetic operands, …), and hands each one found off to
|
|
58
|
+
* {@link visitStmtList} with the corresponding context frame pushed. This is
|
|
59
|
+
* a plain structural walk (discover children the same way {@link walk}
|
|
60
|
+
* does) rather than a hand-modeled traversal of every one of mvdan/sh's
|
|
61
|
+
* expression-shaped fields, because that set is large and not the part of
|
|
62
|
+
* the grammar this module's context-aware descent is about — the
|
|
63
|
+
* *command*-bearing structure (`BinaryCmd`, `IfClause`, loops, …) is
|
|
64
|
+
* hand-modeled in {@link visitCommand}; this helper's only job is finding
|
|
65
|
+
* where a word-shaped subtree stops being "just an expression" and starts
|
|
66
|
+
* containing statements again.
|
|
67
|
+
*
|
|
68
|
+
* `depth` is {@link visitStmt}'s structural-nesting counter, passed through
|
|
69
|
+
* unchanged for the generic per-field walk (a purely expression-shaped
|
|
70
|
+
* subtree — e.g. deeply nested `ParamExp` — is a different, out-of-scope
|
|
71
|
+
* risk from the command-structural nesting {@link MAX_STRUCTURAL_DEPTH}
|
|
72
|
+
* guards against) and incremented by one only when a `CmdSubst`/`ProcSubst`
|
|
73
|
+
* boundary is crossed, since that's a genuine additional level of
|
|
74
|
+
* command-bearing nesting (see {@link ShAnalyzeMaxDepthError}'s doc
|
|
75
|
+
* comment).
|
|
76
|
+
*/
|
|
77
|
+
function scanForHiddenCommands(value, context, sites, depth) {
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
for (const item of value)
|
|
80
|
+
scanForHiddenCommands(item, context, sites, depth);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!isShNodeShape(value))
|
|
84
|
+
return;
|
|
85
|
+
if (value.type === 'CmdSubst') {
|
|
86
|
+
visitStmtList(nodeArray(value.stmts), pushContext(context, { kind: 'cmdSubst' }), sites, depth + 1);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (value.type === 'ProcSubst') {
|
|
90
|
+
visitStmtList(nodeArray(value.stmts), pushContext(context, { kind: 'procSubst' }), sites, depth + 1);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
for (const field of Object.values(value)) {
|
|
94
|
+
scanForHiddenCommands(field, context, sites, depth);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function visitStmtList(stmts, context, sites, depth) {
|
|
98
|
+
for (const stmt of stmts)
|
|
99
|
+
visitStmt(stmt, context, sites, depth);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Visits one `Stmt`. Every genuinely-nested recursive path through this
|
|
103
|
+
* module funnels through here (directly, or via {@link visitStmtList}) —
|
|
104
|
+
* see {@link MAX_STRUCTURAL_DEPTH}'s doc comment — so this is where the
|
|
105
|
+
* depth guard is enforced: a `depth` that has grown past
|
|
106
|
+
* {@link MAX_STRUCTURAL_DEPTH} throws {@link ShAnalyzeMaxDepthError} rather
|
|
107
|
+
* than recursing further.
|
|
108
|
+
*/
|
|
109
|
+
function visitStmt(stmt, context, sites, depth) {
|
|
110
|
+
if (depth > MAX_STRUCTURAL_DEPTH) {
|
|
111
|
+
throw new ShAnalyzeMaxDepthError(MAX_STRUCTURAL_DEPTH);
|
|
112
|
+
}
|
|
113
|
+
let stmtContext = context;
|
|
114
|
+
if (stmt.negated === true)
|
|
115
|
+
stmtContext = pushContext(stmtContext, { kind: 'negated' });
|
|
116
|
+
if (stmt.background === true)
|
|
117
|
+
stmtContext = pushContext(stmtContext, { kind: 'background' });
|
|
118
|
+
scanForHiddenCommands(stmt.redirs, stmtContext, sites, depth);
|
|
119
|
+
const cmd = stmt.cmd;
|
|
120
|
+
if (isShNodeShape(cmd))
|
|
121
|
+
visitCommand(cmd, stmtContext, sites, depth);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Recovers the left-to-right stages of a `|`/`|&` pipeline from `cmd` (a
|
|
125
|
+
* `BinaryCmd` whose own `Op` is already known to be a pipe-family
|
|
126
|
+
* operator). mvdan/sh nests these left-associatively — `a | b | c` is
|
|
127
|
+
* `BinaryCmd(x: Stmt{BinaryCmd(x: a, y: b)}, y: Stmt{c})` — so a chain of
|
|
128
|
+
* `n` stages is `n - 1` levels of nested `BinaryCmd`. Recovering stages
|
|
129
|
+
* walks that left ("x") spine with an explicit work stack instead of
|
|
130
|
+
* recursing per level, so a pipeline of any realistic length (this module
|
|
131
|
+
* is tested to 5,000+ stages) uses O(1) native call-stack frames here,
|
|
132
|
+
* regardless of `n` — the array-backed stack lives on the heap, not the
|
|
133
|
+
* call stack. The work-stack ordering (push `y` then `x`, i.e. `x` on top)
|
|
134
|
+
* mirrors what the original recursive left-to-right depth-first walk did
|
|
135
|
+
* (fully expand `x` before `y`), including the — per this bridge's own
|
|
136
|
+
* left-associative-only model — hypothetical case of a further pipe-family
|
|
137
|
+
* `BinaryCmd` on the `y` side. Mixed `|`/`|&` chains flatten into a single
|
|
138
|
+
* pipeline (this bridge doesn't distinguish "stderr also piped" in
|
|
139
|
+
* {@link CommandContext} — only stage position).
|
|
140
|
+
*/
|
|
141
|
+
function flattenPipelineStages(cmd) {
|
|
142
|
+
const stages = [];
|
|
143
|
+
const workStack = [cmd.y, cmd.x];
|
|
144
|
+
while (workStack.length > 0) {
|
|
145
|
+
const operandStmt = workStack.pop();
|
|
146
|
+
if (!isShNodeShape(operandStmt))
|
|
147
|
+
continue;
|
|
148
|
+
const innerCmd = operandStmt.cmd;
|
|
149
|
+
if (isShNodeShape(innerCmd) && innerCmd.type === 'BinaryCmd' && isPipeOp(innerCmd.op)) {
|
|
150
|
+
// Push `y` then `x` so `x` — the side that keeps the chain going in
|
|
151
|
+
// the left-associative case — is popped (processed) first.
|
|
152
|
+
workStack.push(innerCmd.y, innerCmd.x);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
stages.push(operandStmt);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return stages;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Visits a non-pipe-family `BinaryCmd` (`&&`/`||`, or a future operator
|
|
162
|
+
* this module doesn't specifically recognize). Like pipelines, `&&`/`||`
|
|
163
|
+
* chains nest left-associatively — `a && b && c` is
|
|
164
|
+
* `BinaryCmd(x: Stmt{BinaryCmd(x: a, y: b)}, y: Stmt{c})`, and a mixed
|
|
165
|
+
* `a && b || c` chain is still a single left-nested spine
|
|
166
|
+
* (`(a && b) || c`) — so a long chain is walked iteratively here too,
|
|
167
|
+
* exactly mirroring {@link flattenPipelineStages}'s technique: descend the
|
|
168
|
+
* left ("x") spine with a `while` loop instead of recursion, collecting
|
|
169
|
+
* each level's right ("y") operand (and which context frame, if any, it
|
|
170
|
+
* gets — `'and'`/`'or'`, or none for an unrecognized operator, matching
|
|
171
|
+
* the original per-level logic) along the way, stopping either at a plain
|
|
172
|
+
* (non-`BinaryCmd`) leftmost operand or at a pipe-family `BinaryCmd` (a
|
|
173
|
+
* different sub-structure, visited via the ordinary `visitStmt` →
|
|
174
|
+
* `visitCommand` → `visitBinaryCmd` dispatch below — a single, bounded
|
|
175
|
+
* recursive step, not per-chain-length).
|
|
176
|
+
*
|
|
177
|
+
* Every operand this function ultimately visits (the collected right-hand
|
|
178
|
+
* operands and the leftmost operand) is one genuine structural hop away
|
|
179
|
+
* from this `BinaryCmd` node, so each gets `depth + 1` — the *same*
|
|
180
|
+
* incremented value for every one of them, regardless of chain length:
|
|
181
|
+
* they're siblings under this node's flattened spine, not nested inside
|
|
182
|
+
* one another, so the depth counter must not grow with stage count (only
|
|
183
|
+
* {@link flattenPipelineStages}'s sibling pipeline stages get the same
|
|
184
|
+
* treatment, for the same reason).
|
|
185
|
+
*/
|
|
186
|
+
function visitBinaryCmd(cmd, context, sites, depth) {
|
|
187
|
+
const op = cmd.op;
|
|
188
|
+
if (isPipeOp(op)) {
|
|
189
|
+
const stages = flattenPipelineStages(cmd);
|
|
190
|
+
stages.forEach((stage, index) => {
|
|
191
|
+
visitStmt(stage, pushContext(context, { kind: 'pipeline', stage: index }), sites, depth + 1);
|
|
192
|
+
});
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const rightOperands = [];
|
|
196
|
+
let spine = cmd;
|
|
197
|
+
for (;;) {
|
|
198
|
+
const spineOp = spine.op;
|
|
199
|
+
const frame = spineOp === BIN_CMD_OP_AND
|
|
200
|
+
? { kind: 'and', side: 'right' }
|
|
201
|
+
: spineOp === BIN_CMD_OP_OR
|
|
202
|
+
? { kind: 'or', side: 'right' }
|
|
203
|
+
: undefined;
|
|
204
|
+
const y = spine.y;
|
|
205
|
+
if (isShNodeShape(y))
|
|
206
|
+
rightOperands.push({ stmt: y, frame });
|
|
207
|
+
const x = spine.x;
|
|
208
|
+
if (!isShNodeShape(x))
|
|
209
|
+
break;
|
|
210
|
+
const innerCmd = x.cmd;
|
|
211
|
+
if (isShNodeShape(innerCmd) && innerCmd.type === 'BinaryCmd' && !isPipeOp(innerCmd.op)) {
|
|
212
|
+
spine = innerCmd;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
// `x` is the leftmost operand: not a further same-family BinaryCmd
|
|
216
|
+
// link, so this is where the spine walk ends — visit it with the
|
|
217
|
+
// *outer* (unmodified) context, exactly as the original recursive
|
|
218
|
+
// implementation visited `cmd.x`.
|
|
219
|
+
visitStmt(x, context, sites, depth + 1);
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
for (const { stmt, frame } of rightOperands) {
|
|
223
|
+
visitStmt(stmt, frame ? pushContext(context, frame) : context, sites, depth + 1);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function visitIfClause(clause, context, sites, depth) {
|
|
227
|
+
visitStmtList(nodeArray(clause.cond), pushContext(context, { kind: 'if', branch: 'cond' }), sites, depth);
|
|
228
|
+
visitStmtList(nodeArray(clause.then), pushContext(context, { kind: 'if', branch: 'then' }), sites, depth);
|
|
229
|
+
const elseClause = clause.else;
|
|
230
|
+
if (isShNodeShape(elseClause)) {
|
|
231
|
+
// A chained `elif` — mvdan/sh models it as `IfClause.Else` pointing to
|
|
232
|
+
// another `IfClause` — is one more genuine level of nesting than
|
|
233
|
+
// `cond`/`then` above, so it gets its own `depth + 1` (a long `elif`
|
|
234
|
+
// chain must still trip {@link MAX_STRUCTURAL_DEPTH}, since — unlike
|
|
235
|
+
// `&&`/`||`/pipelines — this module does not special-case flattening
|
|
236
|
+
// it).
|
|
237
|
+
visitIfClause(elseClause, pushContext(context, { kind: 'if', branch: 'else' }), sites, depth + 1);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function visitCommand(cmd, context, sites, depth) {
|
|
241
|
+
switch (cmd.type) {
|
|
242
|
+
case 'CallExpr': {
|
|
243
|
+
scanForHiddenCommands(cmd.assigns, context, sites, depth);
|
|
244
|
+
scanForHiddenCommands(cmd.args, context, sites, depth);
|
|
245
|
+
const args = nodeArray(cmd.args);
|
|
246
|
+
if (args.length > 0) {
|
|
247
|
+
const argv = args.map((word) => resolveWord(word, { context: 'command-argument' }));
|
|
248
|
+
sites.push({ node: cmd, argv0: argv[0], argv, context });
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
case 'BinaryCmd':
|
|
253
|
+
visitBinaryCmd(cmd, context, sites, depth);
|
|
254
|
+
return;
|
|
255
|
+
case 'Block':
|
|
256
|
+
// Transparent grouping (`{ ...; }`) — no context frame, but still one
|
|
257
|
+
// genuine level of structural nesting for depth-guard purposes (a
|
|
258
|
+
// pathological `{ { { ... } } }` chain must still trip the guard).
|
|
259
|
+
visitStmtList(nodeArray(cmd.stmts), context, sites, depth + 1);
|
|
260
|
+
return;
|
|
261
|
+
case 'Subshell':
|
|
262
|
+
visitStmtList(nodeArray(cmd.stmts), pushContext(context, { kind: 'subshell' }), sites, depth + 1);
|
|
263
|
+
return;
|
|
264
|
+
case 'IfClause':
|
|
265
|
+
visitIfClause(cmd, context, sites, depth + 1);
|
|
266
|
+
return;
|
|
267
|
+
case 'CaseClause': {
|
|
268
|
+
scanForHiddenCommands(cmd.word, context, sites, depth);
|
|
269
|
+
for (const item of nodeArray(cmd.items)) {
|
|
270
|
+
scanForHiddenCommands(item.patterns, context, sites, depth);
|
|
271
|
+
visitStmtList(nodeArray(item.stmts), pushContext(context, { kind: 'case' }), sites, depth + 1);
|
|
272
|
+
}
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
case 'ForClause': {
|
|
276
|
+
scanForHiddenCommands(cmd.loop, context, sites, depth);
|
|
277
|
+
visitStmtList(nodeArray(cmd.do), pushContext(context, { kind: 'loop', role: 'body' }), sites, depth + 1);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
case 'WhileClause': {
|
|
281
|
+
visitStmtList(nodeArray(cmd.cond), pushContext(context, { kind: 'loop', role: 'cond' }), sites, depth + 1);
|
|
282
|
+
visitStmtList(nodeArray(cmd.do), pushContext(context, { kind: 'loop', role: 'body' }), sites, depth + 1);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
case 'FuncDecl': {
|
|
286
|
+
const nameNode = cmd.name;
|
|
287
|
+
const nameText = isShNodeShape(nameNode) ? stringField(nameNode, 'value') : '';
|
|
288
|
+
const body = cmd.body;
|
|
289
|
+
if (isShNodeShape(body)) {
|
|
290
|
+
visitStmt(body, pushContext(context, { kind: 'function', name: nameText }), sites, depth + 1);
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
case 'CoprocClause': {
|
|
295
|
+
scanForHiddenCommands(cmd.name, context, sites, depth);
|
|
296
|
+
const stmt = cmd.stmt;
|
|
297
|
+
if (isShNodeShape(stmt)) {
|
|
298
|
+
visitStmt(stmt, pushContext(context, { kind: 'coproc' }), sites, depth + 1);
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
case 'DeclClause':
|
|
303
|
+
scanForHiddenCommands(cmd.args, context, sites, depth);
|
|
304
|
+
return;
|
|
305
|
+
case 'LetClause':
|
|
306
|
+
scanForHiddenCommands(cmd.exprs, context, sites, depth);
|
|
307
|
+
return;
|
|
308
|
+
case 'TestClause':
|
|
309
|
+
scanForHiddenCommands(cmd.x, context, sites, depth);
|
|
310
|
+
return;
|
|
311
|
+
case 'ArithmCmd':
|
|
312
|
+
scanForHiddenCommands(cmd.x, context, sites, depth);
|
|
313
|
+
return;
|
|
314
|
+
case 'TestDecl': {
|
|
315
|
+
scanForHiddenCommands(cmd.description, context, sites, depth);
|
|
316
|
+
const body = cmd.body;
|
|
317
|
+
if (isShNodeShape(body))
|
|
318
|
+
visitStmt(body, context, sites, depth + 1);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
case 'TimeClause': {
|
|
322
|
+
// Transparent (`time cmd`) — no context frame, but still one genuine
|
|
323
|
+
// level of structural nesting for depth-guard purposes.
|
|
324
|
+
const stmt = cmd.stmt;
|
|
325
|
+
if (isShNodeShape(stmt))
|
|
326
|
+
visitStmt(stmt, context, sites, depth + 1);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
default:
|
|
330
|
+
// Not one of mvdan/sh's `syntax.Command` variants — e.g. a `Word` or
|
|
331
|
+
// `Assign` passed directly as `enumerateCommands`' root, or a future
|
|
332
|
+
// Command type this module doesn't know yet. Fall back to scanning it
|
|
333
|
+
// as an expression subtree rather than silently finding nothing.
|
|
334
|
+
scanForHiddenCommands(cmd, context, sites, depth);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Enumerates every command invocation (`CallExpr`) reachable from `root`,
|
|
339
|
+
* with its resolved words ({@link resolveWord}) and the path used to reach
|
|
340
|
+
* it ({@link CommandContext}). Descends everywhere a command can occur:
|
|
341
|
+
* statement lists, both sides of `&&`/`||`/pipelines, subshells/blocks,
|
|
342
|
+
* if/case branches *and* conditions, loop bodies *and* conditions, function
|
|
343
|
+
* bodies, background/negated/coproc statements, and — critically —
|
|
344
|
+
* `CmdSubst`/`ProcSubst` nested inside words, wherever those words occur
|
|
345
|
+
* (arguments, redirection targets, case subjects/patterns, loop word
|
|
346
|
+
* lists, assignment values, test/arithmetic operands, …), not only inside
|
|
347
|
+
* `CallExpr.args`.
|
|
348
|
+
*
|
|
349
|
+
* An assignment-only `CallExpr` (`FOO=bar`, `args` empty) has no first word
|
|
350
|
+
* to resolve and is not itself a command invocation — no program runs — so
|
|
351
|
+
* it produces no {@link CommandSite}; any command substitution nested in
|
|
352
|
+
* its assigned value (`FOO=$(sub)`) is still found and reported.
|
|
353
|
+
*
|
|
354
|
+
* Results are sorted by source position (`node.range[0]`), so nested
|
|
355
|
+
* command substitutions and out-of-structural-order redirection targets
|
|
356
|
+
* still come back in source order regardless of traversal order.
|
|
357
|
+
*
|
|
358
|
+
* Facts only, matching {@link resolveWord}'s posture: no safety verdict, no
|
|
359
|
+
* command/wrapper allowlist or denylist, no dataflow. A statically unknown
|
|
360
|
+
* `argv0` (`static: false`) is a normal, expected result.
|
|
361
|
+
*
|
|
362
|
+
* A long *linear* chain — `|`/`|&`/`&&`/`||` of any realistic length — is
|
|
363
|
+
* traversed iteratively and never risks a stack overflow or trips the
|
|
364
|
+
* nesting-depth guard below, regardless of how many stages/links it has.
|
|
365
|
+
*
|
|
366
|
+
* @param root - Any `ShNode` — typically a `File` from `parseSync`, but any
|
|
367
|
+
* subtree (a `Stmt`, a `Command`, or even a bare `Word`) is handled.
|
|
368
|
+
* @throws {@link ShAnalyzeMaxDepthError} if `root`'s genuinely nested
|
|
369
|
+
* structure (subshells within subshells, chained command/process
|
|
370
|
+
* substitutions, deeply nested `if`/`case`/loop/function/`time`/`{ }`
|
|
371
|
+
* bodies, chained `elif`) exceeds this module's defensive recursion-depth
|
|
372
|
+
* guard — see that error's doc comment for why this fails closed instead
|
|
373
|
+
* of returning a partial result, and why a gate-style consumer should treat
|
|
374
|
+
* it as `deny`.
|
|
375
|
+
* @public
|
|
376
|
+
*/
|
|
377
|
+
export function enumerateCommands(root) {
|
|
378
|
+
const sites = [];
|
|
379
|
+
if (root.type === 'File') {
|
|
380
|
+
visitStmtList(nodeArray(root.stmts), [], sites, 0);
|
|
381
|
+
}
|
|
382
|
+
else if (root.type === 'Stmt') {
|
|
383
|
+
visitStmt(root, [], sites, 0);
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
visitCommand(root, [], sites, 0);
|
|
387
|
+
}
|
|
388
|
+
return [...sites].sort((a, b) => a.node.range[0] - b.node.range[0]);
|
|
389
|
+
}
|
|
390
|
+
//# sourceMappingURL=enumerate-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enumerate-commands.js","sourceRoot":"","sources":["../../src/analyze/enumerate-commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sh-ast/analyze` — the static-analysis layer built on top of the
|
|
3
|
+
* normalized AST from the package root. Its first primitive,
|
|
4
|
+
* {@link resolveWord}, answers "is this word statically a known string,
|
|
5
|
+
* and if so which?" — the question every downstream lint rule or command
|
|
6
|
+
* inventory asks first. Its second, {@link enumerateCommands}, answers
|
|
7
|
+
* "where are all the commands, and how is each one reached?" — finding
|
|
8
|
+
* every `CallExpr` anywhere in the tree, including ones hidden inside
|
|
9
|
+
* command/process substitutions nested in words. Facts only: no policy, no
|
|
10
|
+
* hardcoded command/wrapper lists (see {@link WordResolution}'s and
|
|
11
|
+
* {@link CommandSite}'s doc comments).
|
|
12
|
+
*/
|
|
13
|
+
export { resolveWord } from './resolve-word.js';
|
|
14
|
+
export type { ResolveWordOptions, WordResolution, WordResolutionReason } from './resolve-word.js';
|
|
15
|
+
export { enumerateCommands } from './enumerate-commands.js';
|
|
16
|
+
export type { CommandContext, CommandSite } from './enumerate-commands.js';
|
|
17
|
+
export { ShAnalyzeMaxDepthError } from '../errors.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analyze/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAElG,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAK3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sh-ast/analyze` — the static-analysis layer built on top of the
|
|
3
|
+
* normalized AST from the package root. Its first primitive,
|
|
4
|
+
* {@link resolveWord}, answers "is this word statically a known string,
|
|
5
|
+
* and if so which?" — the question every downstream lint rule or command
|
|
6
|
+
* inventory asks first. Its second, {@link enumerateCommands}, answers
|
|
7
|
+
* "where are all the commands, and how is each one reached?" — finding
|
|
8
|
+
* every `CallExpr` anywhere in the tree, including ones hidden inside
|
|
9
|
+
* command/process substitutions nested in words. Facts only: no policy, no
|
|
10
|
+
* hardcoded command/wrapper lists (see {@link WordResolution}'s and
|
|
11
|
+
* {@link CommandSite}'s doc comments).
|
|
12
|
+
*/
|
|
13
|
+
export { resolveWord } from './resolve-word.js';
|
|
14
|
+
export { enumerateCommands } from './enumerate-commands.js';
|
|
15
|
+
// Re-exported from the root errors module so a consumer of enumerateCommands
|
|
16
|
+
// can `import { ShAnalyzeMaxDepthError } from 'sh-ast/analyze'` without also
|
|
17
|
+
// reaching into the root `sh-ast` entry point.
|
|
18
|
+
export { ShAnalyzeMaxDepthError } from '../errors.js';
|
|
19
|
+
// `ShNode` (resolveWord's parameter type) is deliberately *not* re-exported
|
|
20
|
+
// here: it's the root `sh-ast` entry point's type (see `sh-ast`'s own
|
|
21
|
+
// `index.ts`), and every caller of `resolveWord` already has a `ShNode` in
|
|
22
|
+
// hand from `parseSync`/`walk` there. This is a known, intentional
|
|
23
|
+
// "ae-forgotten-export" in this subpath's api-report — `sh-ast/analyze`
|
|
24
|
+
// consumes the root layer's type without re-exporting it, rather than
|
|
25
|
+
// re-exporting it (and its own transitive doc-linked types) just to
|
|
26
|
+
// silence API Extractor.
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analyze/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,6EAA6E;AAC7E,6EAA6E;AAC7E,+CAA+C;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,4EAA4E;AAC5E,sEAAsE;AACtE,2EAA2E;AAC3E,mEAAmE;AACnE,wEAAwE;AACxE,sEAAsE;AACtE,oEAAoE;AACpE,yBAAyB"}
|