sh-ast 0.1.0 → 0.3.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/dist/analyze/enumerate-commands.d.ts +7 -1
- package/dist/analyze/enumerate-commands.d.ts.map +1 -1
- package/dist/analyze/enumerate-commands.js +7 -1
- package/dist/analyze/enumerate-commands.js.map +1 -1
- package/dist/analyze/index.d.ts +12 -4
- package/dist/analyze/index.d.ts.map +1 -1
- package/dist/analyze/index.js +18 -15
- package/dist/analyze/index.js.map +1 -1
- package/dist/analyze/resolve-argv0.d.ts +335 -0
- package/dist/analyze/resolve-argv0.d.ts.map +1 -0
- package/dist/analyze/resolve-argv0.js +510 -0
- package/dist/analyze/resolve-argv0.js.map +1 -0
- package/dist/analyze/resolve-word.d.ts +5 -0
- package/dist/analyze/resolve-word.d.ts.map +1 -1
- package/dist/analyze/resolve-word.js +5 -0
- package/dist/analyze/resolve-word.js.map +1 -1
- package/dist/analyze.d.ts +592 -211
- package/dist/errors.d.ts +81 -16
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +90 -17
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/parse-depth-guard.d.ts +11 -0
- package/dist/parse-depth-guard.d.ts.map +1 -0
- package/dist/parse-depth-guard.js +633 -0
- package/dist/parse-depth-guard.js.map +1 -0
- package/dist/parse.d.ts +7 -2
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +15 -8
- package/dist/parse.js.map +1 -1
- package/dist/sh-ast.d.ts +165 -45
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/wasm-instance.js +3 -3
- package/dist/wasm-instance.js.map +1 -1
- package/generated/node-types.d.ts +44 -0
- package/package.json +1 -1
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
import { ShParseMaxDepthError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* The maximum estimated structural nesting depth {@link assertParseDepthWithinLimit}
|
|
4
|
+
* accepts before refusing to hand `text` to the WASM parser at all. A single
|
|
5
|
+
* hard-coded, non-configurable safe default — see that function's doc comment
|
|
6
|
+
* for the empirical justification.
|
|
7
|
+
*
|
|
8
|
+
* Measured empirically (this repo's `vitest` environment, Node 24, the
|
|
9
|
+
* committed linux/amd64 `sh-ast.wasm` shim — which runs identically on
|
|
10
|
+
* darwin, since WASM bytecode is host-portable even though the *build* of
|
|
11
|
+
* that `.wasm` is not; see `shim/`'s rebuild-match CI job): feeding
|
|
12
|
+
* `parseSync` a flat top-level script consisting of `N` genuinely nested
|
|
13
|
+
* frames hits a raw, uncatchable-from-inside-the-shim
|
|
14
|
+
* `RangeError: Maximum call stack size exceeded` (thrown from `wasm-function`
|
|
15
|
+
* stack frames — i.e. inside mvdan/sh's own recursive-descent parser,
|
|
16
|
+
* running inside the WASM instance) at these depths, depending on which
|
|
17
|
+
* syntax construct supplies the nesting:
|
|
18
|
+
*
|
|
19
|
+
* | construct | first crash observed at N = |
|
|
20
|
+
* | -------------------------------------------- | ---------------------------- |
|
|
21
|
+
* | `$(...)` command substitution (worst case) | 252 |
|
|
22
|
+
* | `${a:-${a:-...}}` nested parameter expansion | ~400–600 |
|
|
23
|
+
* | `f(){ ... }` nested function definitions | ~300–350 |
|
|
24
|
+
* | `case`/`esac` nesting | ~350–400 |
|
|
25
|
+
* | `while`/`done` nesting | ~450–800 |
|
|
26
|
+
* | `$((...))` nested arithmetic expansion | ~450–800 |
|
|
27
|
+
* | `(...)` nested subshells | ~1,000–1,500 |
|
|
28
|
+
* | `\|`/`&&`/`\|\|` chained pipeline/list stages | ~1,000 |
|
|
29
|
+
* | `if`/`fi`, `for`/`done`, `{ }` block nesting | ~1,000 |
|
|
30
|
+
*
|
|
31
|
+
* `$(...)` command substitution is the tightest bound by a wide margin —
|
|
32
|
+
* each level costs the parser noticeably more native stack than a bare
|
|
33
|
+
* subshell paren — so the limit below is sized against *that* vector, not
|
|
34
|
+
* the more forgiving ones. The exact crash point is a hard native-stack
|
|
35
|
+
* limit, not a clean threshold, and it shrinks further the deeper the
|
|
36
|
+
* *caller's own* JS call stack already is when it invokes `parseSync`
|
|
37
|
+
* (measured: with as little as ~50–1,000 frames of unrelated pre-existing
|
|
38
|
+
* recursion already on the stack, the same 252-deep `$(...)` input that
|
|
39
|
+
* succeeds from a fresh top-level script instead crashes at a noticeably
|
|
40
|
+
* lower depth) — exactly the situation a real embedding (an ESLint rule, a
|
|
41
|
+
* CLI, a permission hook) is in, never a pristine empty stack.
|
|
42
|
+
*
|
|
43
|
+
* 150 leaves roughly 100 frames (⁓1.7x) of headroom below the *lowest*
|
|
44
|
+
* observed native-crash depth (252, `$(...)`) measured from a fresh stack,
|
|
45
|
+
* and was independently confirmed safe with as much as 3,000 frames of
|
|
46
|
+
* *additional*, unrelated caller-stack depth already consumed before the
|
|
47
|
+
* pathological input is scanned — comfortably past what any realistic
|
|
48
|
+
* caller stack looks like. It is not a round, "generous"-sounding number
|
|
49
|
+
* like 10,000 for the same reason `MAX_STRUCTURAL_DEPTH` (in
|
|
50
|
+
* `analyze/enumerate-commands.ts`) isn't: a limit that high would never
|
|
51
|
+
* actually fire before the real, uncatchable crash does. 150 also
|
|
52
|
+
* comfortably exceeds any realistic hand-written script's nesting — this
|
|
53
|
+
* is pathological-input protection, not a normal-usage ceiling (a
|
|
54
|
+
* deliberately deep-but-legitimate ~100-level script is still accepted;
|
|
55
|
+
* see `parse-depth-guard.test.ts`).
|
|
56
|
+
*/
|
|
57
|
+
const MAX_PARSE_NESTING_DEPTH = 150;
|
|
58
|
+
const WHITESPACE = new Set([' ', '\t', '\n', '\r']);
|
|
59
|
+
/**
|
|
60
|
+
* Characters that end a bare (unquoted) word during the word-boundary scan
|
|
61
|
+
* `estimateMaxNestingDepth` uses to recognize `if`/`for`/`while`/`until`/
|
|
62
|
+
* `case`/`select` (openers) and `fi`/`done`/`esac` (closers). Deliberately
|
|
63
|
+
* broader than strict shell tokenization needs to be — the goal is never to
|
|
64
|
+
* *merge* two words that should be separate (which could hide a keyword),
|
|
65
|
+
* not to perfectly reject non-keyword-boundary characters.
|
|
66
|
+
*/
|
|
67
|
+
const WORD_DELIMITERS = new Set([
|
|
68
|
+
';',
|
|
69
|
+
'|',
|
|
70
|
+
'&',
|
|
71
|
+
'(',
|
|
72
|
+
')',
|
|
73
|
+
'{',
|
|
74
|
+
'}',
|
|
75
|
+
'<',
|
|
76
|
+
'>',
|
|
77
|
+
'`',
|
|
78
|
+
'"',
|
|
79
|
+
"'",
|
|
80
|
+
'#',
|
|
81
|
+
'\\',
|
|
82
|
+
' ',
|
|
83
|
+
'\t',
|
|
84
|
+
'\n',
|
|
85
|
+
'\r',
|
|
86
|
+
]);
|
|
87
|
+
/**
|
|
88
|
+
* Keywords that open a genuinely-nested structural region, other than
|
|
89
|
+
* `case` (see {@link CASE_OPENER_KEYWORD} — it needs a distinct
|
|
90
|
+
* {@link ScanContext} kind, not this generic one, to avoid a case arm's
|
|
91
|
+
* pattern-terminating `)` being misread as this region's own closing
|
|
92
|
+
* paren). Paired with {@link STRUCTURAL_CLOSER_KEYWORDS}.
|
|
93
|
+
*/
|
|
94
|
+
const STRUCTURAL_OPENER_KEYWORDS = new Set(['if', 'for', 'while', 'until', 'select']);
|
|
95
|
+
/** Keywords that close a genuinely-nested structural region opened by {@link STRUCTURAL_OPENER_KEYWORDS}. */
|
|
96
|
+
const STRUCTURAL_CLOSER_KEYWORDS = new Set(['fi', 'done']);
|
|
97
|
+
/**
|
|
98
|
+
* `case` needs its own {@link ScanContext} kind (`'case'`), distinct from
|
|
99
|
+
* the generic `'other'` used for `(`/`{`/`if`/`for`/`while`/`until`/
|
|
100
|
+
* `select`: each `case` arm is terminated by a bare `)` —
|
|
101
|
+
* `case WORD in PATTERN)` — that is **not** a closing paren for anything.
|
|
102
|
+
* If `case` pushed a generic `'other'` region, the first arm's `)` would
|
|
103
|
+
* blind-pop-close that region immediately (see {@link estimateMaxNestingDepth}'s
|
|
104
|
+
* "never matches specific opener/closer pairs" design), silently
|
|
105
|
+
* *under-counting* the case body's real nesting contribution for every
|
|
106
|
+
* arm after the first — the one direction this estimator must never be
|
|
107
|
+
* wrong in. A `'case'` region instead ignores a bare `)` outright (a
|
|
108
|
+
* pattern terminator, not a real closer) and is only closed by the
|
|
109
|
+
* word-boundary `esac` keyword; a *real* nested paren opened inside an
|
|
110
|
+
* arm's action list (e.g. `case x in a) (subshell) ;; esac`) still pushes
|
|
111
|
+
* its own `'other'` region on top of `'case'` and is closed by its own
|
|
112
|
+
* `)` exactly as usual, since that `)` pops the (different) top-of-stack
|
|
113
|
+
* entry, never the `'case'` entry beneath it.
|
|
114
|
+
*/
|
|
115
|
+
const CASE_OPENER_KEYWORD = 'case';
|
|
116
|
+
const CASE_CLOSER_KEYWORD = 'esac';
|
|
117
|
+
function isWordChar(ch) {
|
|
118
|
+
return ch !== undefined && !WORD_DELIMITERS.has(ch);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Conservatively upper-bounds the structural nesting depth mvdan/sh's
|
|
122
|
+
* parser will recurse through for `text`, via a single left-to-right
|
|
123
|
+
* character scan — **without** actually parsing the shell grammar. This is
|
|
124
|
+
* a heuristic, not a real shell tokenizer: see the module doc comment for
|
|
125
|
+
* the false-positive risk this trades for correctness.
|
|
126
|
+
*
|
|
127
|
+
* Combines two running counters into one "effective depth" (`bracketDepth +
|
|
128
|
+
* chainLen`), checked against `limit` after every increment:
|
|
129
|
+
*
|
|
130
|
+
* - `bracketDepth`, incremented for each of: an unquoted `(` or `{` (covers
|
|
131
|
+
* subshells, brace groups, function bodies, `$(...)` command/`$((...))`
|
|
132
|
+
* arithmetic substitution, process substitution, and — doubly, which
|
|
133
|
+
* only makes the estimate *more* conservative — each paren of
|
|
134
|
+
* `$((...))`), an unquoted backtick command-substitution open, and a
|
|
135
|
+
* word-boundary `if`/`for`/`while`/`until`/`case`/`select` keyword; and
|
|
136
|
+
* decremented for the corresponding `)`, `}`, closing backtick, or
|
|
137
|
+
* `fi`/`done`/`esac` keyword. **Never** matches specific opener/closer
|
|
138
|
+
* *pairs* — any closer decrements whatever is currently open, and a
|
|
139
|
+
* closer seen with nothing open is simply ignored (never lets the
|
|
140
|
+
* counter go negative) — so a malformed/mismatched-bracket input can
|
|
141
|
+
* only ever make this estimate *more* conservative (higher), never miss
|
|
142
|
+
* real nesting.
|
|
143
|
+
* - `chainLen`, tracking how many `|`/`|&`/`&&`/`\|\|` pipeline/list-operator
|
|
144
|
+
* tokens are chained in the *current* statement — mvdan/sh's parser
|
|
145
|
+
* recurses per chain link the same way it does per bracket, so a long
|
|
146
|
+
* flat `a|a|a|...` chain is just as much a real stack-depth risk as
|
|
147
|
+
* nested subshells are (measured: ~1,000 stages — see
|
|
148
|
+
* `MAX_PARSE_NESTING_DEPTH`'s doc comment), even though it is *not*
|
|
149
|
+
* nested tree structure. Reset to `0` on a statement boundary (`;`, an
|
|
150
|
+
* unescaped newline, or an unpaired `&`), and saved/restored (not
|
|
151
|
+
* dropped) around a bracket/keyword region so a chain that continues
|
|
152
|
+
* after a nested subshell (`a | (b) | c`) still accumulates its full
|
|
153
|
+
* length rather than silently restarting. Correctly **not** counted: a
|
|
154
|
+
* bare `|` while the innermost `case`'s current arm is still in
|
|
155
|
+
* *pattern* position (`PATTERN1|PATTERN2|...)`) — that's glob-pattern
|
|
156
|
+
* alternation, not a pipeline (see `casePatternStack`'s doc comment) —
|
|
157
|
+
* while a `|` in that same arm's *action* list (after its terminating
|
|
158
|
+
* `)`) is a real pipeline and is counted normally.
|
|
159
|
+
*
|
|
160
|
+
* Skips (does not count structure inside): single-quoted strings, `$'...'`
|
|
161
|
+
* ANSI-C-quoted strings, `#`-comments (only recognized at a word boundary,
|
|
162
|
+
* matching shell comment syntax), and backslash-escaped characters — none
|
|
163
|
+
* of these can contain an expansion mvdan/sh recurses into. Double-quoted
|
|
164
|
+
* strings are handled more carefully: their *literal* content (including
|
|
165
|
+
* multibyte characters — scanning is by JS string index, i.e. UTF-16 code
|
|
166
|
+
* unit, which never misaligns with a surrogate pair or any ASCII structural
|
|
167
|
+
* character) is skipped, but a `$(...)` command substitution or backtick
|
|
168
|
+
* expansion **nested inside** a double-quoted string is still a real,
|
|
169
|
+
* depth-bearing recursion in mvdan/sh's grammar and is still counted; once
|
|
170
|
+
* that nested region closes, scanning correctly resumes in "inside a
|
|
171
|
+
* double-quoted string" mode rather than losing track of the enclosing
|
|
172
|
+
* quote.
|
|
173
|
+
*
|
|
174
|
+
* Known, accepted false-positive sources (all documented here rather than
|
|
175
|
+
* papered over — see the module doc comment's "may over-estimate, must
|
|
176
|
+
* never under-estimate" rule):
|
|
177
|
+
* - A keyword word (`if`, `done`, …) used as a plain *argument*
|
|
178
|
+
* (`echo if`) is indistinguishable from real command-position syntax by
|
|
179
|
+
* this scanner and is still counted, since this scanner never tracks
|
|
180
|
+
* grammatical position — only real parsing does that.
|
|
181
|
+
* - A bare `(`/`{` inside a double-quoted string is correctly *not*
|
|
182
|
+
* counted (see above), but the same characters appearing entirely
|
|
183
|
+
* outside of any quoting are always counted even in contexts a full
|
|
184
|
+
* parser might not treat as nesting.
|
|
185
|
+
* - A pipeline/list chain that runs many *unrelated* statements in a row,
|
|
186
|
+
* each separated by `;`/newline, correctly resets `chainLen` between
|
|
187
|
+
* them — but a script with an unusually long *single* chain of harmless
|
|
188
|
+
* `true | true | true | …` well past what any real script would ever
|
|
189
|
+
* write is rejected exactly like a genuinely pathological one, since
|
|
190
|
+
* this scanner cannot distinguish "long but benign" from "adversarial"
|
|
191
|
+
* chain length any more than it can for bracket nesting.
|
|
192
|
+
*
|
|
193
|
+
* Heredoc bodies (`<<EOF` / `<<-EOF` / `<<'EOF'` through the matching
|
|
194
|
+
* delimiter line) are tracked, not merely skipped as ordinary text: a
|
|
195
|
+
* heredoc body is scanned line-by-line for its terminating delimiter, and
|
|
196
|
+
* — critically — a `$(...)` or backtick expansion appearing *inside* the
|
|
197
|
+
* body (still real recursion in mvdan/sh's grammar for an unquoted
|
|
198
|
+
* heredoc) is still counted, exactly as inside a double-quoted string.
|
|
199
|
+
* This is deliberate, not incidental: without heredoc-aware handling, a
|
|
200
|
+
* heredoc body containing plausible-looking closer text (stray `)`/`}`/
|
|
201
|
+
* `fi`/`done`/`esac` — completely inert as far as the real parser is
|
|
202
|
+
* concerned, since it's just heredoc body data) would blind-pop-close
|
|
203
|
+
* *real* open regions from **outside** the heredoc, silently
|
|
204
|
+
* *under-counting* genuine nesting split across a `<<EOF ... EOF` boundary
|
|
205
|
+
* — exactly the failure this estimator must never produce.
|
|
206
|
+
*
|
|
207
|
+
* Stops scanning and returns as soon as the running depth exceeds `limit`
|
|
208
|
+
* (returning `limit + 1`, not necessarily the "true" final maximum) — this
|
|
209
|
+
* keeps the guard itself cheap even against a very large adversarial input,
|
|
210
|
+
* since only "did it exceed the limit" is ever needed by
|
|
211
|
+
* {@link assertParseDepthWithinLimit}.
|
|
212
|
+
*
|
|
213
|
+
* Not exported beyond this module — {@link assertParseDepthWithinLimit} is
|
|
214
|
+
* the only caller; exercised indirectly, through `parseSync`, by
|
|
215
|
+
* `test/parse-depth-guard.test.ts`.
|
|
216
|
+
*/
|
|
217
|
+
function estimateMaxNestingDepth(text, limit) {
|
|
218
|
+
let bracketDepth = 0;
|
|
219
|
+
let chainLen = 0;
|
|
220
|
+
let maxDepth = 0;
|
|
221
|
+
const contextStack = [];
|
|
222
|
+
const chainLenStack = [];
|
|
223
|
+
const heredocInfoStack = [];
|
|
224
|
+
const pendingHeredocs = [];
|
|
225
|
+
const n = text.length;
|
|
226
|
+
let i = 0;
|
|
227
|
+
// Parallel to `contextStack`, but pushed/popped only in lockstep with a
|
|
228
|
+
// `'case'` entry specifically (not with every entry): `true` while
|
|
229
|
+
// scanning the *pattern* list of the innermost open `case`'s current arm
|
|
230
|
+
// (`PATTERN)` — where a bare `|` is alternation, not a pipeline — and
|
|
231
|
+
// `false` once that arm's terminating `)` has been seen (its *action*
|
|
232
|
+
// list, `LIST` in `PATTERN) LIST ;;`, where `|` is once again a real
|
|
233
|
+
// pipeline). Reset back to `true` at that arm's `;;`/`;&`/`;;&`
|
|
234
|
+
// terminator (see the `;` handler below) for the next arm's pattern.
|
|
235
|
+
const casePatternStack = [];
|
|
236
|
+
// Read cursor into `pendingHeredocs` (append-only — see
|
|
237
|
+
// `startNextHeredocIfAny`) instead of `Array.prototype.shift()`, which is
|
|
238
|
+
// O(k) per call (re-indexes the whole remaining array) and so made a
|
|
239
|
+
// script queuing many heredocs an accidental O(k²) cost overall — this
|
|
240
|
+
// guard must stay cheap even against a large adversarial input (see this
|
|
241
|
+
// function's doc comment) for *every* operation it performs, not just the
|
|
242
|
+
// early-exit-on-limit-exceeded one.
|
|
243
|
+
let pendingHeredocCursor = 0;
|
|
244
|
+
function noteDepth() {
|
|
245
|
+
const total = bracketDepth + chainLen;
|
|
246
|
+
if (total > maxDepth)
|
|
247
|
+
maxDepth = total;
|
|
248
|
+
}
|
|
249
|
+
function open() {
|
|
250
|
+
bracketDepth++;
|
|
251
|
+
noteDepth();
|
|
252
|
+
}
|
|
253
|
+
function close() {
|
|
254
|
+
if (bracketDepth > 0)
|
|
255
|
+
bracketDepth--;
|
|
256
|
+
}
|
|
257
|
+
function pushRegion(kind) {
|
|
258
|
+
contextStack.push(kind);
|
|
259
|
+
chainLenStack.push(chainLen);
|
|
260
|
+
chainLen = 0;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Pops `contextStack`'s top entry — and, in lockstep, restores `chainLen`
|
|
264
|
+
* from `chainLenStack` — **only if** it actually equals `kind`; otherwise
|
|
265
|
+
* does nothing at all and returns `false`. Every caller **must** gate its
|
|
266
|
+
* own `close()` (or any other closing side effect) on this return value:
|
|
267
|
+
* a closer token whose region doesn't actually match the top of the
|
|
268
|
+
* stack (a stray `}` with no open `{`, an `esac` with no open `case`, …)
|
|
269
|
+
* is inert text as far as the *real* parser is concerned, and must be
|
|
270
|
+
* exactly as inert here — closing anyway (the bug this comment guards
|
|
271
|
+
* against: see `ShParseMaxDepthError`'s regression tests for
|
|
272
|
+
* `'case x in a}) '.repeat(n) + ... `) lets a self-canceling stray-closer
|
|
273
|
+
* input decrement `bracketDepth` once per repetition while `contextStack`
|
|
274
|
+
* (the *real* nesting mvdan/sh's parser will recurse through) keeps
|
|
275
|
+
* growing unboundedly — silently hiding real, unbounded nesting from this
|
|
276
|
+
* estimator, which is the one direction it must never be wrong in.
|
|
277
|
+
*/
|
|
278
|
+
function popRegion(kind) {
|
|
279
|
+
if (contextStack.length > 0 && contextStack[contextStack.length - 1] === kind) {
|
|
280
|
+
contextStack.pop();
|
|
281
|
+
chainLen = chainLenStack.pop() ?? 0;
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
function toggleBacktick() {
|
|
287
|
+
if (popRegion('backtick')) {
|
|
288
|
+
close();
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
pushRegion('backtick');
|
|
292
|
+
open();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function resetChain() {
|
|
296
|
+
chainLen = 0;
|
|
297
|
+
}
|
|
298
|
+
function chainLink() {
|
|
299
|
+
chainLen++;
|
|
300
|
+
noteDepth();
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* `true` iff the innermost open region is `'case'` and its current arm is
|
|
304
|
+
* still in *pattern* position — see `casePatternStack`'s doc comment.
|
|
305
|
+
*/
|
|
306
|
+
function inCasePatternPosition() {
|
|
307
|
+
if (contextStack.length === 0 ||
|
|
308
|
+
contextStack[contextStack.length - 1] !== 'case' ||
|
|
309
|
+
casePatternStack.length === 0) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
return casePatternStack[casePatternStack.length - 1];
|
|
313
|
+
}
|
|
314
|
+
function startNextHeredocIfAny() {
|
|
315
|
+
if (pendingHeredocCursor < pendingHeredocs.length) {
|
|
316
|
+
// `pendingHeredocCursor < pendingHeredocs.length` just checked above,
|
|
317
|
+
// so this is a real element.
|
|
318
|
+
const next = pendingHeredocs[pendingHeredocCursor];
|
|
319
|
+
pendingHeredocCursor++;
|
|
320
|
+
pushRegion('heredoc');
|
|
321
|
+
heredocInfoStack.push(next);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
while (i < n) {
|
|
325
|
+
if (maxDepth > limit)
|
|
326
|
+
return maxDepth;
|
|
327
|
+
const topKind = contextStack.length > 0 ? contextStack[contextStack.length - 1] : undefined;
|
|
328
|
+
const ch = text[i];
|
|
329
|
+
if (topKind === 'heredoc') {
|
|
330
|
+
const atLineStart = i === 0 || text[i - 1] === '\n';
|
|
331
|
+
// `heredocInfoStack` is pushed/popped in lockstep with every
|
|
332
|
+
// `'heredoc'` entry in `contextStack` (see `startNextHeredocIfAny`
|
|
333
|
+
// and the delimiter-match branch below) — `topKind === 'heredoc'`
|
|
334
|
+
// here already guarantees a corresponding entry exists.
|
|
335
|
+
const info = heredocInfoStack[heredocInfoStack.length - 1];
|
|
336
|
+
if (atLineStart) {
|
|
337
|
+
let lineEnd = i;
|
|
338
|
+
while (lineEnd < n && text[lineEnd] !== '\n')
|
|
339
|
+
lineEnd++;
|
|
340
|
+
const lineRaw = text.slice(i, lineEnd);
|
|
341
|
+
const compareLine = info.stripLeadingTabs ? lineRaw.replace(/^\t+/, '') : lineRaw;
|
|
342
|
+
if (compareLine === info.delimiter) {
|
|
343
|
+
popRegion('heredoc');
|
|
344
|
+
heredocInfoStack.pop();
|
|
345
|
+
i = lineEnd < n ? lineEnd + 1 : lineEnd;
|
|
346
|
+
startNextHeredocIfAny();
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Heredoc body content, not the terminator line: `$(...)`/backtick
|
|
351
|
+
// are still real recursion (see the module doc comment); everything
|
|
352
|
+
// else — including a bare `(`/`{`/keyword-looking text and multibyte
|
|
353
|
+
// characters — is inert body data.
|
|
354
|
+
if (ch === '\\') {
|
|
355
|
+
i += 2;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (ch === '$' && text[i + 1] === '(') {
|
|
359
|
+
pushRegion('other');
|
|
360
|
+
open();
|
|
361
|
+
i += 2;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (ch === '`') {
|
|
365
|
+
toggleBacktick();
|
|
366
|
+
i++;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
i++;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
if (topKind === 'dquote') {
|
|
373
|
+
if (ch === '\\') {
|
|
374
|
+
i += 2;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (ch === '"') {
|
|
378
|
+
contextStack.pop();
|
|
379
|
+
i++;
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (ch === '$' && text[i + 1] === '(') {
|
|
383
|
+
pushRegion('other');
|
|
384
|
+
open();
|
|
385
|
+
i += 2;
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
if (ch === '`') {
|
|
389
|
+
toggleBacktick();
|
|
390
|
+
i++;
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
// Literal double-quoted content — including multibyte characters —
|
|
394
|
+
// never affects depth.
|
|
395
|
+
i++;
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
// "code" scanning mode (top-level, or inside an already-open paren/
|
|
399
|
+
// brace/keyword/backtick region — all of those contain ordinary shell
|
|
400
|
+
// code, so they share this same scanning mode).
|
|
401
|
+
if (ch === '\\') {
|
|
402
|
+
i += 2;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (ch === '#') {
|
|
406
|
+
const prev = i > 0 ? text[i - 1] : undefined;
|
|
407
|
+
if (prev === undefined || WHITESPACE.has(prev) || WORD_DELIMITERS.has(prev)) {
|
|
408
|
+
while (i < n && text[i] !== '\n')
|
|
409
|
+
i++;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
i++;
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
if (ch === "'") {
|
|
416
|
+
i++;
|
|
417
|
+
while (i < n && text[i] !== "'")
|
|
418
|
+
i++;
|
|
419
|
+
if (i < n)
|
|
420
|
+
i++;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (ch === '$' && text[i + 1] === "'") {
|
|
424
|
+
i += 2;
|
|
425
|
+
while (i < n && text[i] !== "'") {
|
|
426
|
+
if (text[i] === '\\') {
|
|
427
|
+
i += 2;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
i++;
|
|
431
|
+
}
|
|
432
|
+
if (i < n)
|
|
433
|
+
i++;
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (ch === '"') {
|
|
437
|
+
contextStack.push('dquote');
|
|
438
|
+
i++;
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (ch === '`') {
|
|
442
|
+
toggleBacktick();
|
|
443
|
+
i++;
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
if (ch === '(' || ch === '{') {
|
|
447
|
+
pushRegion('other');
|
|
448
|
+
open();
|
|
449
|
+
i++;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (ch === ')') {
|
|
453
|
+
// A bare `)` directly inside a `case` region (no intervening real
|
|
454
|
+
// opener) is that arm's pattern terminator, not a closing paren —
|
|
455
|
+
// see CASE_OPENER_KEYWORD's doc comment. Leave the region open, but
|
|
456
|
+
// this arm's pattern list has ended: everything until the next
|
|
457
|
+
// `;;`/`;&`/`;;&` is now its *action* list (see `casePatternStack`'s
|
|
458
|
+
// doc comment) — a bare `|` there is a real pipeline again.
|
|
459
|
+
if (contextStack.length > 0 && contextStack[contextStack.length - 1] === 'case') {
|
|
460
|
+
if (casePatternStack.length > 0) {
|
|
461
|
+
casePatternStack[casePatternStack.length - 1] = false;
|
|
462
|
+
}
|
|
463
|
+
i++;
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (popRegion('other'))
|
|
467
|
+
close();
|
|
468
|
+
i++;
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (ch === '}') {
|
|
472
|
+
if (popRegion('other'))
|
|
473
|
+
close();
|
|
474
|
+
i++;
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
if (ch === '<' && text[i + 1] === '<' && text[i + 2] !== '<') {
|
|
478
|
+
// A `<<`/`<<-` heredoc redirect — queue its delimiter; the body
|
|
479
|
+
// itself starts at the *next* newline (there may be more of this
|
|
480
|
+
// line — another redirect, another heredoc, the rest of the command
|
|
481
|
+
// — still to scan first).
|
|
482
|
+
let j = i + 2;
|
|
483
|
+
let stripLeadingTabs = false;
|
|
484
|
+
if (text[j] === '-') {
|
|
485
|
+
stripLeadingTabs = true;
|
|
486
|
+
j++;
|
|
487
|
+
}
|
|
488
|
+
while (j < n && (text[j] === ' ' || text[j] === '\t'))
|
|
489
|
+
j++;
|
|
490
|
+
let delimiter = '';
|
|
491
|
+
if (text[j] === "'" || text[j] === '"') {
|
|
492
|
+
const quote = text[j];
|
|
493
|
+
j++;
|
|
494
|
+
while (j < n && text[j] !== quote) {
|
|
495
|
+
delimiter += text[j];
|
|
496
|
+
j++;
|
|
497
|
+
}
|
|
498
|
+
if (j < n)
|
|
499
|
+
j++;
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
while (j < n && isWordChar(text[j])) {
|
|
503
|
+
if (text[j] === '\\') {
|
|
504
|
+
j++;
|
|
505
|
+
if (j < n) {
|
|
506
|
+
delimiter += text[j];
|
|
507
|
+
j++;
|
|
508
|
+
}
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
delimiter += text[j];
|
|
512
|
+
j++;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
pendingHeredocs.push({ delimiter, stripLeadingTabs });
|
|
516
|
+
i = j;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (ch === ';') {
|
|
520
|
+
// `;;`/`;;&` (both start with two semicolons) or `;&` end the
|
|
521
|
+
// *current* case arm and return to *pattern* position for the next
|
|
522
|
+
// one — see `casePatternStack`'s doc comment. A single `;` mid-arm
|
|
523
|
+
// (an ordinary statement separator within one arm's action list)
|
|
524
|
+
// does not; only the first `;` of one of these two-character
|
|
525
|
+
// sequences flips the flag, matched by lookahead without consuming
|
|
526
|
+
// extra characters (the loop still advances one `;` at a time below,
|
|
527
|
+
// which is harmless — `resetChain()`/this flag flip are each
|
|
528
|
+
// idempotent).
|
|
529
|
+
if (contextStack.length > 0 &&
|
|
530
|
+
contextStack[contextStack.length - 1] === 'case' &&
|
|
531
|
+
casePatternStack.length > 0 &&
|
|
532
|
+
!casePatternStack[casePatternStack.length - 1] &&
|
|
533
|
+
(text[i + 1] === ';' || text[i + 1] === '&')) {
|
|
534
|
+
casePatternStack[casePatternStack.length - 1] = true;
|
|
535
|
+
}
|
|
536
|
+
resetChain();
|
|
537
|
+
i++;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (ch === '\n') {
|
|
541
|
+
resetChain();
|
|
542
|
+
i++;
|
|
543
|
+
startNextHeredocIfAny();
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (ch === '&' && text[i + 1] === '&') {
|
|
547
|
+
if (!inCasePatternPosition())
|
|
548
|
+
chainLink();
|
|
549
|
+
i += 2;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
if (ch === '|' && text[i + 1] === '|') {
|
|
553
|
+
if (!inCasePatternPosition())
|
|
554
|
+
chainLink();
|
|
555
|
+
i += 2;
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
if (ch === '|' && text[i + 1] === '&') {
|
|
559
|
+
if (!inCasePatternPosition())
|
|
560
|
+
chainLink();
|
|
561
|
+
i += 2;
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
if (ch === '|') {
|
|
565
|
+
// A bare `|` while the innermost `case`'s current arm is still in
|
|
566
|
+
// *pattern* position (`PATTERN1|PATTERN2|...)`) is glob-pattern
|
|
567
|
+
// alternation, not a pipeline — mvdan/sh's parser doesn't recurse per
|
|
568
|
+
// alternative the way it does per pipeline stage, so counting it as
|
|
569
|
+
// a chain link would falsely reject e.g. a 200-alternative case arm
|
|
570
|
+
// that real bash parses trivially. Once this arm's action list
|
|
571
|
+
// begins (after its terminating `)`), `|` is a real pipeline again —
|
|
572
|
+
// see `casePatternStack`'s and the `)` handler's doc comments.
|
|
573
|
+
if (!inCasePatternPosition())
|
|
574
|
+
chainLink();
|
|
575
|
+
i++;
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (ch === '&') {
|
|
579
|
+
// An unpaired `&` (background-job separator) ends the current
|
|
580
|
+
// pipeline/list the same way `;` does.
|
|
581
|
+
resetChain();
|
|
582
|
+
i++;
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
if (isWordChar(ch)) {
|
|
586
|
+
let j = i + 1;
|
|
587
|
+
while (j < n && isWordChar(text[j]))
|
|
588
|
+
j++;
|
|
589
|
+
const word = text.slice(i, j);
|
|
590
|
+
if (STRUCTURAL_OPENER_KEYWORDS.has(word)) {
|
|
591
|
+
pushRegion('other');
|
|
592
|
+
open();
|
|
593
|
+
}
|
|
594
|
+
else if (STRUCTURAL_CLOSER_KEYWORDS.has(word)) {
|
|
595
|
+
if (popRegion('other'))
|
|
596
|
+
close();
|
|
597
|
+
}
|
|
598
|
+
else if (word === CASE_OPENER_KEYWORD) {
|
|
599
|
+
pushRegion('case');
|
|
600
|
+
casePatternStack.push(true);
|
|
601
|
+
open();
|
|
602
|
+
}
|
|
603
|
+
else if (word === CASE_CLOSER_KEYWORD) {
|
|
604
|
+
if (popRegion('case')) {
|
|
605
|
+
close();
|
|
606
|
+
casePatternStack.pop();
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
i = j;
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
// Whitespace or another shell metacharacter with no structural meaning
|
|
613
|
+
// for this scanner's purposes (`<`, `>`).
|
|
614
|
+
i++;
|
|
615
|
+
}
|
|
616
|
+
return maxDepth;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Throws {@link ShParseMaxDepthError} if `text`'s conservatively-estimated
|
|
620
|
+
* structural nesting depth (see {@link estimateMaxNestingDepth}) exceeds
|
|
621
|
+
* {@link MAX_PARSE_NESTING_DEPTH} — called by {@link parseSync} *before* it
|
|
622
|
+
* ever hands `text` to the WASM shim, so pathological input never reaches
|
|
623
|
+
* (and never risks crashing) the shared WASM instance in the first place.
|
|
624
|
+
*
|
|
625
|
+
* @internal
|
|
626
|
+
*/
|
|
627
|
+
export function assertParseDepthWithinLimit(text) {
|
|
628
|
+
const estimated = estimateMaxNestingDepth(text, MAX_PARSE_NESTING_DEPTH);
|
|
629
|
+
if (estimated > MAX_PARSE_NESTING_DEPTH) {
|
|
630
|
+
throw new ShParseMaxDepthError(MAX_PARSE_NESTING_DEPTH, estimated);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
//# sourceMappingURL=parse-depth-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-depth-guard.js","sourceRoot":"","sources":["../src/parse-depth-guard.ts"],"names":[],"mappings":"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"}
|
package/dist/parse.d.ts
CHANGED
|
@@ -34,9 +34,14 @@ export declare function isResultEnvelope(value: unknown): value is ResultEnvelop
|
|
|
34
34
|
* position appears only inside `.message`, which is mvdan/sh's own
|
|
35
35
|
* formatted string, verbatim. Throws {@link ShInvalidDialectError} when
|
|
36
36
|
* {@link ParseOptions.dialect} is not a supported dialect. Throws
|
|
37
|
-
* {@link
|
|
37
|
+
* {@link ShInternalError} for failures that should never happen given
|
|
38
38
|
* a correctly-behaving shim (malformed envelope, unexpected root node
|
|
39
|
-
* type, shim contract violations).
|
|
39
|
+
* type, shim contract violations). Throws {@link ShParseMaxDepthError} if
|
|
40
|
+
* `text`'s conservatively estimated structural nesting depth exceeds the
|
|
41
|
+
* limit this bridge accepts — checked, and thrown, *before* `text` is ever
|
|
42
|
+
* handed to the WASM shim, since mvdan/sh's own parser has no recovery path
|
|
43
|
+
* for exhausting its stack on pathological nesting (see that error's doc
|
|
44
|
+
* comment and `parse-depth-guard.ts`).
|
|
40
45
|
*
|
|
41
46
|
* @public
|
|
42
47
|
*/
|