@poe-platform/safe-bash 0.1.74 → 0.1.76
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/safe-bash/browser.js +435 -176
- package/dist/safe-bash/browser.js.map +4 -4
- package/dist/safe-bash/commands/structured/limits.js +3 -1
- package/dist/safe-bash/commands/structured/limits.js.map +1 -1
- package/dist/safe-bash/commands/structured/split.d.ts.map +1 -1
- package/dist/safe-bash/commands/structured/split.js +7 -2
- package/dist/safe-bash/commands/structured/split.js.map +1 -1
- package/dist/safe-bash/commands/structured/values.d.ts.map +1 -1
- package/dist/safe-bash/commands/structured/values.js +4 -2
- package/dist/safe-bash/commands/structured/values.js.map +1 -1
- package/dist/safe-bash/commands/text-programs/awk-reader.d.ts +25 -0
- package/dist/safe-bash/commands/text-programs/awk-reader.d.ts.map +1 -0
- package/dist/safe-bash/commands/text-programs/awk-reader.js +180 -0
- package/dist/safe-bash/commands/text-programs/awk-reader.js.map +1 -0
- package/dist/safe-bash/commands/text-programs/awk-retention.d.ts +11 -0
- package/dist/safe-bash/commands/text-programs/awk-retention.d.ts.map +1 -0
- package/dist/safe-bash/commands/text-programs/awk-retention.js +31 -0
- package/dist/safe-bash/commands/text-programs/awk-retention.js.map +1 -0
- package/dist/safe-bash/commands/text-programs/awk-runtime.d.ts +11 -1
- package/dist/safe-bash/commands/text-programs/awk-runtime.d.ts.map +1 -1
- package/dist/safe-bash/commands/text-programs/awk-runtime.js +291 -191
- package/dist/safe-bash/commands/text-programs/awk-runtime.js.map +1 -1
- package/dist/safe-bash/commands/text-programs/awk.d.ts.map +1 -1
- package/dist/safe-bash/commands/text-programs/awk.js +2 -1
- package/dist/safe-bash/commands/text-programs/awk.js.map +1 -1
- package/dist/safe-bash/commands/text.d.ts.map +1 -1
- package/dist/safe-bash/commands/text.js +194 -52
- package/dist/safe-bash/commands/text.js.map +1 -1
- package/dist/safe-bash/shell/arithmetic-parameters.d.ts +2 -0
- package/dist/safe-bash/shell/arithmetic-parameters.d.ts.map +1 -1
- package/dist/safe-bash/shell/arithmetic-parameters.js +1 -1
- package/dist/safe-bash/shell/arithmetic-parameters.js.map +1 -1
- package/dist/safe-bash/shell/arithmetic.d.ts +4 -3
- package/dist/safe-bash/shell/arithmetic.d.ts.map +1 -1
- package/dist/safe-bash/shell/arithmetic.js +19 -7
- package/dist/safe-bash/shell/arithmetic.js.map +1 -1
- package/dist/safe-bash/shell/arrays/syntax.d.ts +6 -5
- package/dist/safe-bash/shell/arrays/syntax.d.ts.map +1 -1
- package/dist/safe-bash/shell/arrays/syntax.js +22 -12
- package/dist/safe-bash/shell/arrays/syntax.js.map +1 -1
- package/dist/safe-bash/shell/index.d.ts +1 -1
- package/dist/safe-bash/shell/index.d.ts.map +1 -1
- package/dist/safe-bash/shell/parse-budget.d.ts +10 -0
- package/dist/safe-bash/shell/parse-budget.d.ts.map +1 -0
- package/dist/safe-bash/shell/parse-budget.js +29 -0
- package/dist/safe-bash/shell/parse-budget.js.map +1 -0
- package/dist/safe-bash/shell/parser.d.ts +6 -4
- package/dist/safe-bash/shell/parser.d.ts.map +1 -1
- package/dist/safe-bash/shell/parser.js +80 -29
- package/dist/safe-bash/shell/parser.js.map +1 -1
- package/dist/safe-bash/shell/runtime.d.ts +3 -0
- package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
- package/dist/safe-bash/shell/runtime.js +136 -78
- package/dist/safe-bash/shell/runtime.js.map +1 -1
- package/dist/safe-bash/shell/shell.js +2 -2
- package/dist/safe-bash/shell/shell.js.map +1 -1
- package/dist/safe-bash/shell/types.d.ts +4 -0
- package/dist/safe-bash/shell/types.d.ts.map +1 -1
- package/dist/safe-bash/shell/types.js.map +1 -1
- package/dist/safe-bash/shell/worker-limits.d.ts.map +1 -1
- package/dist/safe-bash/shell/worker-limits.js +1 -0
- package/dist/safe-bash/shell/worker-limits.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ShellLimitError } from "./types.js";
|
|
2
|
+
export declare const defaultMaxParseUnits = 262144;
|
|
3
|
+
export declare class ParseBudget {
|
|
4
|
+
#private;
|
|
5
|
+
private readonly signal?;
|
|
6
|
+
private readonly onLimit?;
|
|
7
|
+
constructor(maximum?: number, signal?: AbortSignal | undefined, onLimit?: ((error: ShellLimitError) => void) | undefined);
|
|
8
|
+
admit(units?: number): void;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=parse-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-budget.d.ts","sourceRoot":"","sources":["../../src/shell/parse-budget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,oBAAoB,SAAU,CAAC;AAE5C,qBAAa,WAAW;;IAIsB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAAe,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAhG,OAAO,SAAuB,EAAmB,MAAM,CAAC,EAAE,WAAW,YAAA,EAAmB,OAAO,CAAC,GAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,aAAA;IAK9I,KAAK,CAAC,KAAK,SAAI,GAAG,IAAI;CAWvB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ShellLimitError } from "./types.js";
|
|
2
|
+
export const defaultMaxParseUnits = 262_144;
|
|
3
|
+
export class ParseBudget {
|
|
4
|
+
signal;
|
|
5
|
+
onLimit;
|
|
6
|
+
#remaining;
|
|
7
|
+
#failure;
|
|
8
|
+
constructor(maximum = defaultMaxParseUnits, signal, onLimit) {
|
|
9
|
+
this.signal = signal;
|
|
10
|
+
this.onLimit = onLimit;
|
|
11
|
+
if (!Number.isSafeInteger(maximum) || maximum < 0)
|
|
12
|
+
throw new RangeError("maxParseUnits must be a nonnegative safe integer");
|
|
13
|
+
this.#remaining = maximum;
|
|
14
|
+
}
|
|
15
|
+
admit(units = 1) {
|
|
16
|
+
this.signal?.throwIfAborted();
|
|
17
|
+
if (this.#failure)
|
|
18
|
+
throw this.#failure;
|
|
19
|
+
if (!Number.isSafeInteger(units) || units < 0)
|
|
20
|
+
throw new RangeError("Parse admission must be a nonnegative safe integer");
|
|
21
|
+
if (units > this.#remaining) {
|
|
22
|
+
const error = this.#failure = new ShellLimitError("maxParseUnits");
|
|
23
|
+
this.onLimit?.(error);
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
this.#remaining -= units;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=parse-budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-budget.js","sourceRoot":"","sources":["../../src/shell/parse-budget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAE5C,MAAM,OAAO,WAAW;IAIuC;IAAuC;IAHpG,UAAU,CAAS;IACnB,QAAQ,CAA8B;IAEtC,YAAY,OAAO,GAAG,oBAAoB,EAAmB,MAAoB,EAAmB,OAA0C;QAAjF,WAAM,GAAN,MAAM,CAAc;QAAmB,YAAO,GAAP,OAAO,CAAmC;QAC5I,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,kDAAkD,CAAC,CAAC;QAC5H,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,KAAK,GAAG,CAAC;QACb,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;QAC9B,IAAI,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,CAAC,QAAQ,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,oDAAoD,CAAC,CAAC;QAC1H,IAAI,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,eAAe,CAAC,eAAe,CAAC,CAAC;YACnE,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;YACtB,MAAM,KAAK,CAAC;QACd,CAAC;QACD,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ShellParseOptions } from "./types.js";
|
|
2
|
+
import { ParseBudget } from "./parse-budget.js";
|
|
1
3
|
import type { ArithmeticProgram } from "./arithmetic.js";
|
|
2
4
|
import type { ConditionalExpression } from "./conditional.js";
|
|
3
5
|
import type { ByteShellValue } from "../contracts/value.js";
|
|
@@ -136,13 +138,13 @@ export interface Script {
|
|
|
136
138
|
readonly printedLines?: ReadonlyMap<Command, number>;
|
|
137
139
|
readonly printedNewlines?: number;
|
|
138
140
|
}
|
|
139
|
-
export declare function parseShell(source: string, depth?: number): Script;
|
|
140
|
-
export declare function hereDocumentWords(document: HereDocument, line: number, byteLocale: boolean, warnings: string[]): Generator<Word>;
|
|
141
|
-
export declare function parseShellUnit(source: string, position?: number, byteLocale?: boolean): {
|
|
141
|
+
export declare function parseShell(source: string, depth?: number, options?: ShellParseOptions): Script;
|
|
142
|
+
export declare function hereDocumentWords(document: HereDocument, line: number, byteLocale: boolean, warnings: string[], budget?: ParseBudget): Generator<Word>;
|
|
143
|
+
export declare function parseShellUnit(source: string, position?: number, byteLocale?: boolean, budget?: ParseBudget): {
|
|
142
144
|
script: Script;
|
|
143
145
|
next: number;
|
|
144
146
|
};
|
|
145
|
-
export declare function parseShellInputUnit(source: string, byteLocale?: boolean): {
|
|
147
|
+
export declare function parseShellInputUnit(source: string, byteLocale?: boolean, budget?: ParseBudget): {
|
|
146
148
|
script: Script;
|
|
147
149
|
next: number;
|
|
148
150
|
} | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/shell/parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/shell/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ShellSyntaxError } from "./types.js";
|
|
2
|
+
import { ParseBudget } from "./parse-budget.js";
|
|
2
3
|
import { arithmeticEnd, prepareArithmetic } from "./arithmetic.js";
|
|
3
4
|
import { arraySelector, compoundEntry, compoundHead, elementAssignment, getArrayAssignment, scalarAssignmentName, setArrayAssignment, setArraySelector, setQuoteMarker } from "./arrays/syntax.js";
|
|
4
5
|
import { conditionalBinary, conditionalUnary } from "./conditional.js";
|
|
@@ -10,7 +11,8 @@ export class HereDocumentSyntaxError extends Error {
|
|
|
10
11
|
this.diagnostic = diagnostic;
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
|
-
function printedSimpleLines(lists, separators) {
|
|
14
|
+
function printedSimpleLines(lists, separators, budget) {
|
|
15
|
+
budget.admit();
|
|
14
16
|
const printedLines = new Map();
|
|
15
17
|
let line = 1;
|
|
16
18
|
for (let index = 0; index < lists.length; index++) {
|
|
@@ -33,6 +35,7 @@ function printedSimpleLines(lists, separators) {
|
|
|
33
35
|
class IncompleteShellInput extends Error {
|
|
34
36
|
}
|
|
35
37
|
class Lexer {
|
|
38
|
+
budget;
|
|
36
39
|
source;
|
|
37
40
|
depth;
|
|
38
41
|
warnings;
|
|
@@ -48,7 +51,8 @@ class Lexer {
|
|
|
48
51
|
conditionalPattern;
|
|
49
52
|
documents = [];
|
|
50
53
|
newlineOffsets = [];
|
|
51
|
-
constructor(source, depth, warnings = [], lineOffset = 0, byteLocale = false, documentLine, partial = false) {
|
|
54
|
+
constructor(budget, source, depth, warnings = [], lineOffset = 0, byteLocale = false, documentLine, partial = false) {
|
|
55
|
+
this.budget = budget;
|
|
52
56
|
this.source = source;
|
|
53
57
|
this.depth = depth;
|
|
54
58
|
this.warnings = warnings;
|
|
@@ -77,6 +81,7 @@ class Lexer {
|
|
|
77
81
|
throw new ShellSyntaxError(message, this.position, 2, undefined, unclosedQuote);
|
|
78
82
|
}
|
|
79
83
|
next() {
|
|
84
|
+
this.budget.admit();
|
|
80
85
|
while (this.position < this.source.length) {
|
|
81
86
|
const current = this.source[this.position];
|
|
82
87
|
if (current === " " || current === "\t" || this.conditional && current === "\n") {
|
|
@@ -128,6 +133,7 @@ class Lexer {
|
|
|
128
133
|
this.error("Tokenizer made no progress");
|
|
129
134
|
let document;
|
|
130
135
|
if (delimiterOperator) {
|
|
136
|
+
this.budget.admit();
|
|
131
137
|
document = {
|
|
132
138
|
delimiter: word.parts.map((part) => part.kind === "text" ? part.value : "").join(""),
|
|
133
139
|
quoted: word.parts.some((part) => part.quoted), stripTabs: delimiterOperator === "<<-", offset,
|
|
@@ -135,6 +141,7 @@ class Lexer {
|
|
|
135
141
|
};
|
|
136
142
|
this.documents.push(document);
|
|
137
143
|
}
|
|
144
|
+
this.budget.admit();
|
|
138
145
|
return { kind: "word", value: word.plain ?? "", offset, end: this.position, word: { ...word, spelling: this.source.slice(offset, this.position) }, ...(document ? { document } : {}) };
|
|
139
146
|
}
|
|
140
147
|
readDocuments() {
|
|
@@ -175,10 +182,12 @@ class Lexer {
|
|
|
175
182
|
const current = this.source[this.position];
|
|
176
183
|
if (current === "$" || current === "`") {
|
|
177
184
|
if (text) {
|
|
185
|
+
this.budget.admit(2);
|
|
178
186
|
yield { offset: 0, parts: [{ kind: "text", value: text, quoted: true }] };
|
|
179
187
|
text = "";
|
|
180
188
|
}
|
|
181
189
|
const offset = this.position;
|
|
190
|
+
this.budget.admit();
|
|
182
191
|
const parts = [];
|
|
183
192
|
try {
|
|
184
193
|
this.expansion(parts, true);
|
|
@@ -205,12 +214,15 @@ class Lexer {
|
|
|
205
214
|
this.position++;
|
|
206
215
|
}
|
|
207
216
|
if (text.length >= 1024) {
|
|
217
|
+
this.budget.admit(2);
|
|
208
218
|
yield { offset: 0, parts: [{ kind: "text", value: text, quoted: true }] };
|
|
209
219
|
text = "";
|
|
210
220
|
}
|
|
211
221
|
}
|
|
212
|
-
if (text)
|
|
222
|
+
if (text) {
|
|
223
|
+
this.budget.admit(2);
|
|
213
224
|
yield { offset: 0, parts: [{ kind: "text", value: text, quoted: true }] };
|
|
225
|
+
}
|
|
214
226
|
}
|
|
215
227
|
documentSubstitutionError(source, error, backtick = false) {
|
|
216
228
|
const line = this.documentLine + source.slice(0, error.offset).split("\n").length;
|
|
@@ -267,6 +279,7 @@ class Lexer {
|
|
|
267
279
|
this.error("Unterminated delimiter expansion syntax");
|
|
268
280
|
}
|
|
269
281
|
word(terminator, enclosingQuoted = false, literal = false, arithmetic = false) {
|
|
282
|
+
this.budget.admit();
|
|
270
283
|
const offset = this.position;
|
|
271
284
|
const reduction = this.printedNewlineReduction;
|
|
272
285
|
const unprinted = this.unprintedWords;
|
|
@@ -293,6 +306,7 @@ class Lexer {
|
|
|
293
306
|
setQuoteMarker(previous, false);
|
|
294
307
|
}
|
|
295
308
|
else {
|
|
309
|
+
this.budget.admit();
|
|
296
310
|
const part = { kind: "text", value: projection, quoted, ...(typeof value === "string" ? {} : { byteValue: value }) };
|
|
297
311
|
setQuoteMarker(part, synthetic);
|
|
298
312
|
parts.push(part);
|
|
@@ -440,6 +454,7 @@ class Lexer {
|
|
|
440
454
|
catch (error) {
|
|
441
455
|
if (!(error instanceof TypeError) || !("code" in error) || error.code !== "ERR_ENCODING_INVALID_ENCODED_DATA")
|
|
442
456
|
throw error;
|
|
457
|
+
this.budget.admit(2);
|
|
443
458
|
return shellValueFromBytes(raw);
|
|
444
459
|
}
|
|
445
460
|
}
|
|
@@ -491,6 +506,7 @@ class Lexer {
|
|
|
491
506
|
this.error("Unterminated ANSI-C quote", { quote: "'", line: quoteLine });
|
|
492
507
|
}
|
|
493
508
|
expansion(parts, quoted) {
|
|
509
|
+
this.budget.admit();
|
|
494
510
|
const line = this.documentLine ?? this.lineAt(this.position);
|
|
495
511
|
if (this.source[this.position] === "`") {
|
|
496
512
|
this.position++;
|
|
@@ -504,11 +520,12 @@ class Lexer {
|
|
|
504
520
|
this.error("Unterminated command substitution");
|
|
505
521
|
this.position++;
|
|
506
522
|
try {
|
|
507
|
-
parts.push({ kind: "substitution", script: parseSource(source, this.depth + 1, this.warnings, line - 1, this.byteLocale), line, quoted });
|
|
523
|
+
parts.push({ kind: "substitution", script: parseSource(source, this.depth + 1, this.warnings, line - 1, this.byteLocale, this.budget), line, quoted });
|
|
508
524
|
}
|
|
509
525
|
catch (error) {
|
|
510
526
|
if (this.documentLine === undefined || !(error instanceof ShellSyntaxError) || /nesting|exceeds/u.test(error.reason))
|
|
511
527
|
throw error;
|
|
528
|
+
this.budget.admit();
|
|
512
529
|
parts.push({ kind: "failed-substitution", diagnostic: this.documentSubstitutionError(source, error, true).diagnostic, quoted });
|
|
513
530
|
}
|
|
514
531
|
return;
|
|
@@ -520,7 +537,7 @@ class Lexer {
|
|
|
520
537
|
const start = this.position + 2;
|
|
521
538
|
const end = arithmeticEnd(this.source, start);
|
|
522
539
|
const source = this.source.slice(start, end);
|
|
523
|
-
parts.push({ kind: "arithmetic", expression: prepareArithmetic(source), source, line, quoted });
|
|
540
|
+
parts.push({ kind: "arithmetic", expression: prepareArithmetic(source, this.budget), source, line, quoted });
|
|
524
541
|
this.position = end + 2;
|
|
525
542
|
}
|
|
526
543
|
else if (this.source[this.position] === "(") {
|
|
@@ -528,7 +545,8 @@ class Lexer {
|
|
|
528
545
|
let nested;
|
|
529
546
|
let script;
|
|
530
547
|
try {
|
|
531
|
-
|
|
548
|
+
this.budget.admit();
|
|
549
|
+
nested = new Parser(this.budget, this.source.slice(start), this.depth + 1, this.warnings, this.lineAt(start) - 1, undefined, this.byteLocale);
|
|
532
550
|
script = nested.script(new Set([")"]));
|
|
533
551
|
}
|
|
534
552
|
catch (error) {
|
|
@@ -570,7 +588,7 @@ class Lexer {
|
|
|
570
588
|
const end = this.source.indexOf("]", start);
|
|
571
589
|
if (end < 0)
|
|
572
590
|
this.error("Unterminated indexed-array subscript");
|
|
573
|
-
selector = arraySelector(this.source.slice(start, end), start);
|
|
591
|
+
selector = arraySelector(this.source.slice(start, end), start, this.budget);
|
|
574
592
|
this.position = end + 1;
|
|
575
593
|
if (this.source[this.position] !== "}")
|
|
576
594
|
this.error("Unsupported indexed-array operator");
|
|
@@ -599,6 +617,7 @@ class Lexer {
|
|
|
599
617
|
this.position++;
|
|
600
618
|
substringLength = this.word("}", true, false, true);
|
|
601
619
|
}
|
|
620
|
+
this.budget.admit();
|
|
602
621
|
substring = { offset, ...(substringLength ? { length: substringLength } : {}), source: this.source.slice(parameterStart, this.position + 1) };
|
|
603
622
|
}
|
|
604
623
|
if (this.source[this.position] !== "}")
|
|
@@ -621,15 +640,18 @@ class Lexer {
|
|
|
621
640
|
}
|
|
622
641
|
}
|
|
623
642
|
class Parser {
|
|
643
|
+
budget;
|
|
624
644
|
lexer;
|
|
625
645
|
current;
|
|
626
646
|
lookahead;
|
|
627
647
|
nesting = 0;
|
|
628
648
|
openCommands = [];
|
|
629
|
-
constructor(source, depth, warnings = [], lineOffset = 0, position, byteLocale = false, partial = false) {
|
|
649
|
+
constructor(budget, source, depth, warnings = [], lineOffset = 0, position, byteLocale = false, partial = false) {
|
|
650
|
+
this.budget = budget;
|
|
630
651
|
if (position === undefined && depth === 0 && source.includes("\0"))
|
|
631
652
|
throw new ShellSyntaxError("NUL bytes are not valid shell source", source.indexOf("\0"));
|
|
632
|
-
|
|
653
|
+
budget.admit();
|
|
654
|
+
this.lexer = new Lexer(budget, source, depth, warnings, lineOffset, byteLocale, undefined, partial);
|
|
633
655
|
this.lexer.position = position ?? 0;
|
|
634
656
|
this.current = this.lexer.next();
|
|
635
657
|
}
|
|
@@ -656,11 +678,13 @@ class Parser {
|
|
|
656
678
|
newlines() { while (this.is("\n"))
|
|
657
679
|
this.advance(); }
|
|
658
680
|
script(stops = new Set(), inputUnit = false) {
|
|
681
|
+
this.budget.admit();
|
|
659
682
|
const lists = [];
|
|
660
683
|
const separators = [];
|
|
661
684
|
this.newlines();
|
|
662
685
|
const line = this.lexer.lineAt(this.current.offset);
|
|
663
686
|
while (this.current.kind !== "end" && !stops.has(this.current.value)) {
|
|
687
|
+
this.budget.admit();
|
|
664
688
|
const pipelines = [this.pipeline()];
|
|
665
689
|
const operators = [];
|
|
666
690
|
while (this.is("&&") || this.is("||")) {
|
|
@@ -681,21 +705,24 @@ class Parser {
|
|
|
681
705
|
else if (!this.isEnd() && !this.is(")") && !(stops.has(this.current.value) && [";;", ";&", ";;&"].includes(this.current.value)))
|
|
682
706
|
this.error("Expected command separator");
|
|
683
707
|
}
|
|
684
|
-
const printed = printedSimpleLines(lists, separators);
|
|
708
|
+
const printed = printedSimpleLines(lists, separators, this.budget);
|
|
685
709
|
return { lists, line, ...printed };
|
|
686
710
|
}
|
|
687
711
|
pipeline() {
|
|
712
|
+
this.budget.admit();
|
|
688
713
|
const negate = this.is("!");
|
|
689
714
|
if (negate)
|
|
690
715
|
this.advance();
|
|
691
716
|
const commands = [this.command()];
|
|
692
717
|
while (this.is("|") || this.is("|&")) {
|
|
693
718
|
const operator = this.advance();
|
|
694
|
-
if (operator.value === "|&")
|
|
719
|
+
if (operator.value === "|&") {
|
|
720
|
+
this.budget.admit(3);
|
|
695
721
|
commands.at(-1).redirects.push({
|
|
696
722
|
descriptor: 2, operator: ">&", implicitPipeline: true,
|
|
697
723
|
target: { offset: operator.offset, plain: "1", spelling: "1", parts: [{ kind: "text", value: "1", quoted: false }] },
|
|
698
724
|
});
|
|
725
|
+
}
|
|
699
726
|
this.newlines();
|
|
700
727
|
commands.push(this.command());
|
|
701
728
|
}
|
|
@@ -704,10 +731,12 @@ class Parser {
|
|
|
704
731
|
command() {
|
|
705
732
|
if (++this.nesting + this.lexer.depth > 64)
|
|
706
733
|
this.error("Syntax nesting exceeds 64");
|
|
734
|
+
this.budget.admit();
|
|
707
735
|
const line = this.lexer.lineAt(this.current.offset);
|
|
708
736
|
this.openCommands.push({ name: this.current.value, line });
|
|
709
737
|
try {
|
|
710
738
|
const command = this.commandInner();
|
|
739
|
+
this.budget.admit();
|
|
711
740
|
return { ...command, line: command.line ?? line };
|
|
712
741
|
}
|
|
713
742
|
finally {
|
|
@@ -716,6 +745,7 @@ class Parser {
|
|
|
716
745
|
}
|
|
717
746
|
}
|
|
718
747
|
commandInner() {
|
|
748
|
+
this.budget.admit();
|
|
719
749
|
let command;
|
|
720
750
|
if (this.is("[[")) {
|
|
721
751
|
const start = this.current.end;
|
|
@@ -737,7 +767,10 @@ class Parser {
|
|
|
737
767
|
admit();
|
|
738
768
|
this.advance();
|
|
739
769
|
const expression = primary(depth + 1);
|
|
740
|
-
|
|
770
|
+
if (expression.kind === "not" && !grouped.has(expression))
|
|
771
|
+
return expression.operand;
|
|
772
|
+
this.budget.admit();
|
|
773
|
+
return { kind: "not", operand: expression };
|
|
741
774
|
}
|
|
742
775
|
if (this.is("(")) {
|
|
743
776
|
this.advance();
|
|
@@ -747,6 +780,7 @@ class Parser {
|
|
|
747
780
|
return expression;
|
|
748
781
|
}
|
|
749
782
|
admit();
|
|
783
|
+
this.budget.admit();
|
|
750
784
|
const first = operand();
|
|
751
785
|
if (conditionalBinary.has(this.current.value)) {
|
|
752
786
|
const operator = this.current.value;
|
|
@@ -763,6 +797,7 @@ class Parser {
|
|
|
763
797
|
let expression = primary(depth);
|
|
764
798
|
while (this.is("&&")) {
|
|
765
799
|
admit();
|
|
800
|
+
this.budget.admit();
|
|
766
801
|
this.advance();
|
|
767
802
|
expression = { kind: "and", left: expression, right: primary(depth) };
|
|
768
803
|
}
|
|
@@ -772,6 +807,7 @@ class Parser {
|
|
|
772
807
|
let expression = conjunction(depth);
|
|
773
808
|
while (this.is("||")) {
|
|
774
809
|
admit();
|
|
810
|
+
this.budget.admit();
|
|
775
811
|
this.advance();
|
|
776
812
|
expression = { kind: "or", left: expression, right: conjunction(depth) };
|
|
777
813
|
}
|
|
@@ -789,7 +825,7 @@ class Parser {
|
|
|
789
825
|
const start = this.current.offset + 2;
|
|
790
826
|
const end = arithmeticEnd(this.lexer.source, start);
|
|
791
827
|
const source = this.lexer.source.slice(start, end);
|
|
792
|
-
command = { kind: "arithmetic", expression: prepareArithmetic(source), source, redirects: [] };
|
|
828
|
+
command = { kind: "arithmetic", expression: prepareArithmetic(source, this.budget), source, redirects: [] };
|
|
793
829
|
this.lexer.position = end + 2;
|
|
794
830
|
this.lookahead = undefined;
|
|
795
831
|
this.current = this.lexer.next();
|
|
@@ -810,6 +846,7 @@ class Parser {
|
|
|
810
846
|
const condition = this.nonemptyScript(new Set(["then"]));
|
|
811
847
|
this.expect("then");
|
|
812
848
|
const body = this.nonemptyScript(new Set(["elif", "else", "fi"]));
|
|
849
|
+
this.budget.admit();
|
|
813
850
|
branches.push({ condition, body });
|
|
814
851
|
if (!this.is("elif"))
|
|
815
852
|
break;
|
|
@@ -849,6 +886,7 @@ class Parser {
|
|
|
849
886
|
if (![";;", ";&", ";;&", "esac"].includes(this.current.value))
|
|
850
887
|
this.error("Expected case terminator");
|
|
851
888
|
const terminator = this.current.value;
|
|
889
|
+
this.budget.admit();
|
|
852
890
|
clauses.push({ patterns, body, terminator });
|
|
853
891
|
if (terminator === "esac")
|
|
854
892
|
break;
|
|
@@ -903,7 +941,7 @@ class Parser {
|
|
|
903
941
|
this.error("Expected brace function body");
|
|
904
942
|
command = { kind: "function", name, body: this.command(), redirects: [] };
|
|
905
943
|
}
|
|
906
|
-
else if (this.current.kind === "word" && !compoundHead(this.current.word) && this.peek().value === "(") {
|
|
944
|
+
else if (this.current.kind === "word" && !compoundHead(this.current.word, this.budget) && this.peek().value === "(") {
|
|
907
945
|
const name = this.advance().value;
|
|
908
946
|
if (!/^[a-zA-Z_][a-zA-Z_0-9]*$/u.test(name))
|
|
909
947
|
this.error("Invalid function name");
|
|
@@ -930,23 +968,24 @@ class Parser {
|
|
|
930
968
|
else if (this.current.kind === "word") {
|
|
931
969
|
line ??= wordLine;
|
|
932
970
|
let word = this.advance().word;
|
|
933
|
-
const head = compoundHead(word);
|
|
971
|
+
const head = compoundHead(word, this.budget);
|
|
934
972
|
if (head && this.is("(")) {
|
|
935
973
|
this.advance();
|
|
936
974
|
const entries = [];
|
|
937
975
|
this.newlines();
|
|
938
976
|
while (this.current.kind === "word") {
|
|
939
|
-
entries.push(compoundEntry(this.advance().word));
|
|
977
|
+
entries.push(compoundEntry(this.advance().word, this.budget));
|
|
940
978
|
this.newlines();
|
|
941
979
|
}
|
|
942
980
|
if (!this.is(")"))
|
|
943
981
|
this.error("Unsupported indexed-array compound assignment");
|
|
944
982
|
const end = this.advance().end;
|
|
983
|
+
this.budget.admit(2);
|
|
945
984
|
word = { offset: word.offset, parts: word.parts, spelling: this.lexer.source.slice(word.offset, end) };
|
|
946
985
|
setArrayAssignment(word, { kind: "compound", ...head, entries });
|
|
947
986
|
}
|
|
948
987
|
else {
|
|
949
|
-
const assignment = words.every(previous => getArrayAssignment(previous) || scalarAssignmentName(previous)) ? elementAssignment(word) : undefined;
|
|
988
|
+
const assignment = words.every(previous => getArrayAssignment(previous) || scalarAssignmentName(previous)) ? elementAssignment(word, this.budget) : undefined;
|
|
950
989
|
if (assignment)
|
|
951
990
|
setArrayAssignment(word, assignment);
|
|
952
991
|
}
|
|
@@ -981,6 +1020,7 @@ class Parser {
|
|
|
981
1020
|
}
|
|
982
1021
|
if (!/^(?:>|>>|<|<<|<<-|<<<|>&|<&|>\||&>)$/u.test(this.current.value) || this.current.kind !== "operator")
|
|
983
1022
|
return undefined;
|
|
1023
|
+
this.budget.admit();
|
|
984
1024
|
const operator = this.advance().value;
|
|
985
1025
|
descriptor ??= operator.startsWith("<") ? 0 : 1;
|
|
986
1026
|
if (!Number.isSafeInteger(descriptor) || descriptor > 255)
|
|
@@ -991,32 +1031,42 @@ class Parser {
|
|
|
991
1031
|
return { descriptor, operator, target: target.word, line: this.lexer.lineAt(Math.max(target.offset, target.end - 1)), ...((operator === "<&" || operator === ">&") && this.lexer.source[target.end - 1] === "-" ? { move: true } : {}), ...(target.document ? { document: target.document } : {}) };
|
|
992
1032
|
}
|
|
993
1033
|
}
|
|
994
|
-
export function parseShell(source, depth = 0) {
|
|
1034
|
+
export function parseShell(source, depth = 0, options = {}) {
|
|
1035
|
+
const budget = new ParseBudget(options.maxParseUnits);
|
|
995
1036
|
const warnings = [];
|
|
996
|
-
const script = parseSource(source, depth, warnings);
|
|
1037
|
+
const script = parseSource(source, depth, warnings, 0, false, budget);
|
|
1038
|
+
budget.admit();
|
|
997
1039
|
return { ...script, ...(warnings.length ? { warnings } : {}) };
|
|
998
1040
|
}
|
|
999
|
-
export function* hereDocumentWords(document, line, byteLocale, warnings) {
|
|
1000
|
-
if (document.quoted)
|
|
1041
|
+
export function* hereDocumentWords(document, line, byteLocale, warnings, budget = new ParseBudget()) {
|
|
1042
|
+
if (document.quoted) {
|
|
1043
|
+
budget.admit(2);
|
|
1001
1044
|
yield { offset: document.offset, parts: [{ kind: "text", value: document.body, quoted: true }] };
|
|
1002
|
-
|
|
1003
|
-
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
budget.admit();
|
|
1048
|
+
yield* new Lexer(budget, document.body, document.depth, warnings, line - 1, byteLocale, line).documentWords();
|
|
1049
|
+
}
|
|
1004
1050
|
}
|
|
1005
|
-
export function parseShellUnit(source, position = 0, byteLocale = false) {
|
|
1051
|
+
export function parseShellUnit(source, position = 0, byteLocale = false, budget = new ParseBudget()) {
|
|
1006
1052
|
const warnings = [];
|
|
1007
|
-
|
|
1053
|
+
budget.admit();
|
|
1054
|
+
const parser = new Parser(budget, source, 0, warnings, 0, position, byteLocale);
|
|
1008
1055
|
const script = parser.script(new Set(), true);
|
|
1009
1056
|
const next = parser.current.end;
|
|
1010
1057
|
const nul = source.indexOf("\0", position);
|
|
1011
1058
|
if (nul >= 0 && nul < next)
|
|
1012
1059
|
throw new ShellSyntaxError("NUL bytes are not valid shell source", nul);
|
|
1060
|
+
budget.admit(2);
|
|
1013
1061
|
return { script: { ...script, ...(warnings.length ? { warnings } : {}) }, next };
|
|
1014
1062
|
}
|
|
1015
|
-
export function parseShellInputUnit(source, byteLocale = false) {
|
|
1063
|
+
export function parseShellInputUnit(source, byteLocale = false, budget = new ParseBudget()) {
|
|
1016
1064
|
const warnings = [];
|
|
1017
1065
|
try {
|
|
1018
|
-
|
|
1066
|
+
budget.admit();
|
|
1067
|
+
const parser = new Parser(budget, source, 0, warnings, 0, 0, byteLocale, true);
|
|
1019
1068
|
const script = parser.script(new Set(), true);
|
|
1069
|
+
budget.admit(2);
|
|
1020
1070
|
return { script: { ...script, ...(warnings.length ? { warnings } : {}) }, next: parser.current.end };
|
|
1021
1071
|
}
|
|
1022
1072
|
catch (error) {
|
|
@@ -1027,8 +1077,9 @@ export function parseShellInputUnit(source, byteLocale = false) {
|
|
|
1027
1077
|
throw error;
|
|
1028
1078
|
}
|
|
1029
1079
|
}
|
|
1030
|
-
function parseSource(source, depth, warnings, lineOffset
|
|
1031
|
-
|
|
1080
|
+
function parseSource(source, depth, warnings, lineOffset, byteLocale, budget) {
|
|
1081
|
+
budget.admit();
|
|
1082
|
+
const parser = new Parser(budget, source, depth, warnings, lineOffset, undefined, byteLocale);
|
|
1032
1083
|
const script = parser.script();
|
|
1033
1084
|
if (parser.current.kind !== "end")
|
|
1034
1085
|
parser.error("Unexpected token");
|