@poe-platform/safe-bash 0.1.423 → 0.1.425
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 +2 -2
- package/dist/safe-bash/browser.js +123 -21
- package/dist/safe-bash/browser.js.map +4 -4
- package/dist/safe-bash/commands/network/private-address.d.ts.map +1 -1
- package/dist/safe-bash/commands/network/private-address.js +10 -3
- package/dist/safe-bash/commands/network/private-address.js.map +1 -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 +9 -2
- package/dist/safe-bash/commands/text-programs/awk-runtime.js.map +1 -1
- package/dist/safe-bash/commands/text-programs/sed.d.ts.map +1 -1
- package/dist/safe-bash/commands/text-programs/sed.js +7 -2
- package/dist/safe-bash/commands/text-programs/sed.js.map +1 -1
- package/dist/safe-bash/commands/text-programs/shared.d.ts +1 -0
- package/dist/safe-bash/commands/text-programs/shared.d.ts.map +1 -1
- package/dist/safe-bash/commands/text-programs/shared.js.map +1 -1
- package/dist/safe-bash/shell/cleanup.d.ts +1 -0
- package/dist/safe-bash/shell/cleanup.d.ts.map +1 -1
- package/dist/safe-bash/shell/cleanup.js +13 -0
- package/dist/safe-bash/shell/cleanup.js.map +1 -1
- package/dist/safe-bash/shell/path-lookup.d.ts +11 -0
- package/dist/safe-bash/shell/path-lookup.d.ts.map +1 -0
- package/dist/safe-bash/shell/path-lookup.js +91 -0
- package/dist/safe-bash/shell/path-lookup.js.map +1 -0
- package/dist/safe-bash/shell/runtime.d.ts +2 -0
- package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
- package/dist/safe-bash/shell/runtime.js +18 -20
- package/dist/safe-bash/shell/runtime.js.map +1 -1
- package/dist/safe-bash/shell/types.d.ts +1 -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
package/README.md
CHANGED
|
@@ -270,7 +270,7 @@ For SafeJS host integration, `makeSafeJsShellModule` exposes shell execution and
|
|
|
270
270
|
| --- | --- |
|
|
271
271
|
| `fs` | Required filesystem; no implicit host access. |
|
|
272
272
|
| `cwd` | Initial virtual directory; defaults to `/`. |
|
|
273
|
-
| `env` | Initial exported variables; defaults to an empty map, with `PWD` set from `cwd`. No host environment inheritance. Never pass host `process.env` or any secret-bearing object: everything in `env` is readable by executed scripts (`env`, `printenv`, `$VAR`), and on Cloudflare Workers with `nodejs_compat` `process.env` contains the Worker's secret bindings. The shell
|
|
273
|
+
| `env` | Initial exported variables; defaults to an empty map, with `PWD` set from `cwd`. No host environment inheritance. Never pass host `process.env` or any secret-bearing object: everything in `env` is readable by executed scripts (`env`, `printenv`, `$VAR`), and on Cloudflare Workers with `nodejs_compat` `process.env` contains the Worker's secret bindings. The shell warns only for the identical host `process.env` object, not copies, and does not filter values. |
|
|
274
274
|
| `commands` | Existing `CommandRegistry`; defaults to an empty registry. |
|
|
275
275
|
| `limits` | Resource limits listed below. |
|
|
276
276
|
|
|
@@ -289,7 +289,7 @@ provided. Pass an `AbortSignal` as `signal` to cancel. [Option types](src/shell/
|
|
|
289
289
|
| `maxExpansionFields` | 10,000 |
|
|
290
290
|
| `maxExpansionBytes` | 16 MiB |
|
|
291
291
|
| `maxWallClockMs` | 30 seconds |
|
|
292
|
-
| `maxCpuMs` | 30 seconds,
|
|
292
|
+
| `maxCpuMs` | 30 seconds elapsed including waits; checkpoint-enforced, not CPU accounting or preemptive enforcement. |
|
|
293
293
|
| `pipeHighWaterMark` | 64 KiB |
|
|
294
294
|
|
|
295
295
|
Always call `dispose()` when finished. Shell failures normally produce an exit
|
|
@@ -9195,6 +9195,7 @@ var InvocationScope = class _InvocationScope {
|
|
|
9195
9195
|
parent;
|
|
9196
9196
|
#children = /* @__PURE__ */ new Set();
|
|
9197
9197
|
#callbacks = [];
|
|
9198
|
+
#finalizers = [];
|
|
9198
9199
|
#work = /* @__PURE__ */ new Set();
|
|
9199
9200
|
#controller = new AbortController();
|
|
9200
9201
|
#closed = false;
|
|
@@ -9202,6 +9203,10 @@ var InvocationScope = class _InvocationScope {
|
|
|
9202
9203
|
get signal() {
|
|
9203
9204
|
return this.#controller.signal;
|
|
9204
9205
|
}
|
|
9206
|
+
registerFinalizer(finalize) {
|
|
9207
|
+
this.assertOpen();
|
|
9208
|
+
this.#finalizers.push(finalize);
|
|
9209
|
+
}
|
|
9205
9210
|
assertOpen() {
|
|
9206
9211
|
this.callerSignal?.throwIfAborted();
|
|
9207
9212
|
if (this.#closed) throw this.signal.reason;
|
|
@@ -9256,6 +9261,13 @@ var InvocationScope = class _InvocationScope {
|
|
|
9256
9261
|
...this.#work
|
|
9257
9262
|
]);
|
|
9258
9263
|
} finally {
|
|
9264
|
+
for (const finalize of this.#finalizers.splice(0)) {
|
|
9265
|
+
try {
|
|
9266
|
+
finalize();
|
|
9267
|
+
} catch (error) {
|
|
9268
|
+
this.failures.push(error);
|
|
9269
|
+
}
|
|
9270
|
+
}
|
|
9259
9271
|
if (this.parent) this.parent.#children.delete(this);
|
|
9260
9272
|
}
|
|
9261
9273
|
});
|
|
@@ -11605,6 +11617,89 @@ async function matchEre(program, subject, ledger, signal) {
|
|
|
11605
11617
|
return Object.freeze({ matched: false, captures: Object.freeze([]), values: Object.freeze([]) });
|
|
11606
11618
|
}
|
|
11607
11619
|
|
|
11620
|
+
// packages/safe-bash/src/shell/path-lookup.ts
|
|
11621
|
+
init_platform();
|
|
11622
|
+
var maxCacheEntries = 256;
|
|
11623
|
+
var maxCacheBytes = 64 * 1024;
|
|
11624
|
+
var PathLookup = class {
|
|
11625
|
+
#entries = /* @__PURE__ */ new Map();
|
|
11626
|
+
#bytes = 0;
|
|
11627
|
+
#pending = 0;
|
|
11628
|
+
#generation = 0;
|
|
11629
|
+
suspend() {
|
|
11630
|
+
this.#entries.clear();
|
|
11631
|
+
this.#bytes = 0;
|
|
11632
|
+
this.#generation++;
|
|
11633
|
+
this.#pending++;
|
|
11634
|
+
let closed = false;
|
|
11635
|
+
return () => {
|
|
11636
|
+
if (closed) return;
|
|
11637
|
+
closed = true;
|
|
11638
|
+
this.#pending--;
|
|
11639
|
+
this.#generation++;
|
|
11640
|
+
};
|
|
11641
|
+
}
|
|
11642
|
+
suspendUntilClosed(scope) {
|
|
11643
|
+
scope.assertOpen();
|
|
11644
|
+
const resume = this.suspend();
|
|
11645
|
+
scope.registerFinalizer(resume);
|
|
11646
|
+
}
|
|
11647
|
+
async isFile(fs, path, signal) {
|
|
11648
|
+
signal.throwIfAborted();
|
|
11649
|
+
const cached = this.#entries.get(path);
|
|
11650
|
+
if (cached) return cached.file;
|
|
11651
|
+
const generation = this.#generation;
|
|
11652
|
+
let file;
|
|
11653
|
+
try {
|
|
11654
|
+
file = (await fs.stat(path, { signal })).type === "file";
|
|
11655
|
+
} catch (error) {
|
|
11656
|
+
signal.throwIfAborted();
|
|
11657
|
+
const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
11658
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
11659
|
+
file = false;
|
|
11660
|
+
}
|
|
11661
|
+
signal.throwIfAborted();
|
|
11662
|
+
const bytes2 = import_buffer.Buffer.byteLength(path) + 64;
|
|
11663
|
+
if (!this.#pending && generation === this.#generation && bytes2 <= maxCacheBytes && !this.#entries.has(path)) {
|
|
11664
|
+
while (this.#entries.size >= maxCacheEntries || this.#bytes + bytes2 > maxCacheBytes) {
|
|
11665
|
+
const oldest = this.#entries.entries().next().value;
|
|
11666
|
+
this.#bytes -= oldest[1].bytes;
|
|
11667
|
+
this.#entries.delete(oldest[0]);
|
|
11668
|
+
}
|
|
11669
|
+
this.#entries.set(path, { file, bytes: bytes2 });
|
|
11670
|
+
this.#bytes += bytes2;
|
|
11671
|
+
}
|
|
11672
|
+
return file;
|
|
11673
|
+
}
|
|
11674
|
+
};
|
|
11675
|
+
function* pathTargets(name2, path, limits, signal, fail2) {
|
|
11676
|
+
signal.throwIfAborted();
|
|
11677
|
+
if (path !== void 0 && import_buffer.Buffer.byteLength(path) > limits.maxExpansionBytes) fail2("maxExpansionBytes");
|
|
11678
|
+
if (name2.includes("/") || path === void 0) {
|
|
11679
|
+
if (limits.maxExpansionFields < 1) fail2("maxExpansionFields");
|
|
11680
|
+
yield name2;
|
|
11681
|
+
return;
|
|
11682
|
+
}
|
|
11683
|
+
let fields = 1;
|
|
11684
|
+
for (let separator = path.indexOf(":"); separator >= 0; separator = path.indexOf(":", separator + 1)) {
|
|
11685
|
+
signal.throwIfAborted();
|
|
11686
|
+
if (++fields > limits.maxExpansionFields) fail2("maxExpansionFields");
|
|
11687
|
+
}
|
|
11688
|
+
if (fields > limits.maxExpansionFields) fail2("maxExpansionFields");
|
|
11689
|
+
let start = 0;
|
|
11690
|
+
let consulted = 0;
|
|
11691
|
+
while (start <= path.length) {
|
|
11692
|
+
signal.throwIfAborted();
|
|
11693
|
+
if (consulted >= limits.maxPathComponents) fail2("maxPathComponents");
|
|
11694
|
+
consulted++;
|
|
11695
|
+
const end = path.indexOf(":", start);
|
|
11696
|
+
const component = path.slice(start, end < 0 ? path.length : end);
|
|
11697
|
+
yield `${component || "."}${component.endsWith("/") ? "" : "/"}${name2}`;
|
|
11698
|
+
if (end < 0) return;
|
|
11699
|
+
start = end + 1;
|
|
11700
|
+
}
|
|
11701
|
+
}
|
|
11702
|
+
|
|
11608
11703
|
// packages/safe-bash/src/shell/runtime.ts
|
|
11609
11704
|
var defaultLimits = {
|
|
11610
11705
|
maxParseUnits: defaultMaxParseUnits,
|
|
@@ -11612,6 +11707,7 @@ var defaultLimits = {
|
|
|
11612
11707
|
maxOutputBytes: 16 * 1024 * 1024,
|
|
11613
11708
|
maxCommands: 1e4,
|
|
11614
11709
|
maxFileSystemOperations: 1e5,
|
|
11710
|
+
maxPathComponents: 64,
|
|
11615
11711
|
maxRedirects: 64,
|
|
11616
11712
|
maxPipelineStages: 64,
|
|
11617
11713
|
maxLoopIterations: 1e4,
|
|
@@ -11705,6 +11801,7 @@ var Budget = class {
|
|
|
11705
11801
|
}
|
|
11706
11802
|
limits;
|
|
11707
11803
|
executionScope = Object.freeze({});
|
|
11804
|
+
pathLookup = new PathLookup();
|
|
11708
11805
|
parsing;
|
|
11709
11806
|
values;
|
|
11710
11807
|
commands = 0;
|
|
@@ -13914,9 +14011,11 @@ var Runtime = class _Runtime {
|
|
|
13914
14011
|
});
|
|
13915
14012
|
} };
|
|
13916
14013
|
};
|
|
14014
|
+
const resumePathCache = this.budget.pathLookup.suspend();
|
|
13917
14015
|
const release = () => {
|
|
13918
14016
|
if (closed) return;
|
|
13919
14017
|
closed = true;
|
|
14018
|
+
resumePathCache();
|
|
13920
14019
|
if (--file.references === 0 && this.outputFiles.get(path) === file) this.outputFiles.delete(path);
|
|
13921
14020
|
};
|
|
13922
14021
|
let target2;
|
|
@@ -14191,6 +14290,7 @@ var Runtime = class _Runtime {
|
|
|
14191
14290
|
if (argumentValues.values.every((value2) => typeof value2 === "string")) Reflect.deleteProperty(context, "argumentValues");
|
|
14192
14291
|
const middleware = this.middleware.map((handler) => (context2, next) => {
|
|
14193
14292
|
scope.assertOpen();
|
|
14293
|
+
this.budget.pathLookup.suspendUntilClosed(scope);
|
|
14194
14294
|
let downstream;
|
|
14195
14295
|
const raw = handler(context2, () => {
|
|
14196
14296
|
downstream = next();
|
|
@@ -14384,6 +14484,7 @@ var Runtime = class _Runtime {
|
|
|
14384
14484
|
await this.diagnostic({ ...io, ...context2 }, `${context2.command}: command not found`);
|
|
14385
14485
|
return { exitCode: 127 };
|
|
14386
14486
|
}
|
|
14487
|
+
this.budget.pathLookup.suspendUntilClosed(scope);
|
|
14387
14488
|
const raw = definition.execute(forwarded);
|
|
14388
14489
|
const observed = this.observeRuntimeReturn(raw, runtimeFrame);
|
|
14389
14490
|
return await interruptible(observed, this.signal);
|
|
@@ -14537,25 +14638,20 @@ ${functionDisplay(name2, state.functions.get(name2))}`;
|
|
|
14537
14638
|
}
|
|
14538
14639
|
async searchPaths(name2, state, all = false, discovery = false) {
|
|
14539
14640
|
if (!name2) return [];
|
|
14540
|
-
const path = state.variables.PATH;
|
|
14541
|
-
if (path !== void 0 && import_buffer.Buffer.byteLength(path) > this.budget.limits.maxExpansionBytes) this.budget.fail("maxExpansionBytes");
|
|
14542
|
-
const components = name2.includes("/") || path === void 0 ? [void 0] : path.split(":");
|
|
14543
|
-
if (components.length > this.budget.limits.maxExpansionFields) this.budget.fail("maxExpansionFields");
|
|
14544
14641
|
let denied;
|
|
14545
14642
|
const matches = [];
|
|
14546
|
-
for (const
|
|
14547
|
-
this.signal.throwIfAborted();
|
|
14548
|
-
const target = component === void 0 ? name2 : `${component || "."}${component?.endsWith("/") ? "" : "/"}${name2}`;
|
|
14643
|
+
for (const target of pathTargets(name2, state.variables.PATH, this.budget.limits, this.signal, (limit) => this.budget.fail(limit))) {
|
|
14549
14644
|
const resolved = resolvePath(state.cwd, target);
|
|
14550
14645
|
try {
|
|
14551
14646
|
const options3 = { signal: this.signal };
|
|
14552
|
-
if (
|
|
14647
|
+
if (!await interruptible(this.budget.pathLookup.isFile(this.fs, resolved, this.signal), this.signal)) continue;
|
|
14553
14648
|
if (this.fs.capabilities.permissions !== true) throw new CommandFailure(`${target}: execution permissions are not supported by this filesystem`, 126);
|
|
14554
14649
|
await interruptible(this.fs.access(resolved, ACCESS_MODES.X_OK, options3), this.signal);
|
|
14555
14650
|
matches.push(target);
|
|
14556
14651
|
if (!all) return matches;
|
|
14557
14652
|
} catch (error) {
|
|
14558
14653
|
this.signal.throwIfAborted();
|
|
14654
|
+
if (error instanceof ShellLimitError) throw error;
|
|
14559
14655
|
if (error instanceof CommandFailure) {
|
|
14560
14656
|
if (discovery) continue;
|
|
14561
14657
|
throw error;
|
|
@@ -14805,6 +14901,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
|
|
|
14805
14901
|
const runtimeFrame = {};
|
|
14806
14902
|
const middleware = this.middleware.map((handler) => (context2, next) => {
|
|
14807
14903
|
scope.assertOpen();
|
|
14904
|
+
this.budget.pathLookup.suspendUntilClosed(scope);
|
|
14808
14905
|
let downstream;
|
|
14809
14906
|
const raw = handler(context2, () => {
|
|
14810
14907
|
downstream = next();
|
|
@@ -14887,7 +14984,10 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
|
|
|
14887
14984
|
return { exitCode: await runtime.interpreter(forwarded, state2, childIO, loadedSource) };
|
|
14888
14985
|
}
|
|
14889
14986
|
if (direct) return { exitCode: await runtime.scriptFile(forwarded, state2, childIO, command2, forwarded.args, true) };
|
|
14890
|
-
if (definition)
|
|
14987
|
+
if (definition) {
|
|
14988
|
+
runtime.budget.pathLookup.suspendUntilClosed(childIO[invocationScope]);
|
|
14989
|
+
return definition.execute(forwarded);
|
|
14990
|
+
}
|
|
14891
14991
|
await writeDiagnostic(forwarded.stderr, `env: ${command2}: command not found
|
|
14892
14992
|
`);
|
|
14893
14993
|
return { exitCode: 127 };
|
|
@@ -14907,6 +15007,7 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
|
|
|
14907
15007
|
args: argumentValues.args,
|
|
14908
15008
|
argumentValues
|
|
14909
15009
|
}, state, io, async (runtime, forwarded) => {
|
|
15010
|
+
runtime.budget.pathLookup.suspendUntilClosed(io[invocationScope]);
|
|
14910
15011
|
let failed = false;
|
|
14911
15012
|
let failure;
|
|
14912
15013
|
let failureReport;
|
|
@@ -15079,16 +15180,11 @@ ${prefix2} line ${offset + line}: \`${source.split("\n")[line - 1] ?? ""}'
|
|
|
15079
15180
|
try {
|
|
15080
15181
|
const options3 = { signal: this.signal };
|
|
15081
15182
|
if (filename && !filename.includes("/") && state.variables.PATH) {
|
|
15082
|
-
if (import_buffer.Buffer.byteLength(state.variables.PATH) > this.budget.limits.maxExpansionBytes) this.budget.fail("maxExpansionBytes");
|
|
15083
|
-
const components = state.variables.PATH.split(":");
|
|
15084
|
-
if (components.length > this.budget.limits.maxExpansionFields) this.budget.fail("maxExpansionFields");
|
|
15085
15183
|
let found = false;
|
|
15086
|
-
for (const
|
|
15087
|
-
this.signal.throwIfAborted();
|
|
15088
|
-
const candidate = `${component || "."}${component.endsWith("/") ? "" : "/"}${filename}`;
|
|
15184
|
+
for (const candidate of pathTargets(filename, state.variables.PATH, this.budget.limits, this.signal, (limit) => this.budget.fail(limit))) {
|
|
15089
15185
|
const path2 = resolvePath(state.cwd, candidate);
|
|
15090
15186
|
try {
|
|
15091
|
-
if (
|
|
15187
|
+
if (!await interruptible(this.budget.pathLookup.isFile(this.fs, path2, this.signal), this.signal)) continue;
|
|
15092
15188
|
await interruptible(this.fs.access(path2, ACCESS_MODES.R_OK, options3), this.signal);
|
|
15093
15189
|
target = candidate;
|
|
15094
15190
|
found = true;
|
|
@@ -17697,6 +17793,7 @@ var cloudflareWorkerLimits = Object.freeze({
|
|
|
17697
17793
|
maxOutputBytes: 4 * 1024 * 1024,
|
|
17698
17794
|
maxCommands: 1e3,
|
|
17699
17795
|
maxFileSystemOperations: 1e4,
|
|
17796
|
+
maxPathComponents: 64,
|
|
17700
17797
|
maxRedirects: 64,
|
|
17701
17798
|
maxPipelineStages: 64,
|
|
17702
17799
|
maxLoopIterations: 1e3,
|
|
@@ -17844,7 +17941,7 @@ init_platform();
|
|
|
17844
17941
|
// packages/safe-bash/src/commands/network/private-address.ts
|
|
17845
17942
|
init_platform();
|
|
17846
17943
|
function privateIPv4(first, second) {
|
|
17847
|
-
return first === 10 || first === 127 || first === 0 || first === 169 && second === 254 || first === 192 && second === 168 || first === 172 && second >= 16 && second <= 31;
|
|
17944
|
+
return first === 10 || first === 127 || first === 0 || first === 169 && second === 254 || first === 192 && second === 168 || first === 172 && second >= 16 && second <= 31 || first === 100 && second >= 64 && second <= 127 || first === 198 && second >= 18 && second <= 19 || first >= 224;
|
|
17848
17945
|
}
|
|
17849
17946
|
function privateHostname(input3) {
|
|
17850
17947
|
const hostname = input3.toLowerCase().replace(/\.$/u, "").replace(/^\[|\]$/gu, "");
|
|
@@ -17860,10 +17957,12 @@ function privateHostname(input3) {
|
|
|
17860
17957
|
const mappedIPv4 = hextets.slice(0, 5).every((value2) => value2 === 0) && hextets[5] === 65535;
|
|
17861
17958
|
const translatedIPv4 = hextets.slice(0, 4).every((value2) => value2 === 0) && hextets[4] === 65535 && hextets[5] === 0;
|
|
17862
17959
|
const nat64IPv4 = hextets[0] === 100 && hextets[1] === 65435 && hextets.slice(2, 6).every((value2) => value2 === 0);
|
|
17863
|
-
|
|
17960
|
+
const compatibleIPv4 = hextets.slice(0, 6).every((value2) => value2 === 0);
|
|
17961
|
+
if (mappedIPv4 || translatedIPv4 || nat64IPv4 || compatibleIPv4) {
|
|
17864
17962
|
return privateIPv4(hextets[6] >>> 8, hextets[6] & 255);
|
|
17865
17963
|
}
|
|
17866
|
-
|
|
17964
|
+
if (hextets[0] === 8194) return privateIPv4(hextets[1] >>> 8, hextets[1] & 255);
|
|
17965
|
+
return (hextets[0] & 65024) === 64512 || (hextets[0] & 65472) === 65152 || (hextets[0] & 65472) === 65216 || (hextets[0] & 65280) === 65280 || hextets[0] === 100 && hextets[1] === 65435 && hextets[2] === 1;
|
|
17867
17966
|
}
|
|
17868
17967
|
const parts2 = hostname.split(".");
|
|
17869
17968
|
if (parts2.length !== 4 || parts2.some((part) => !part || Array.from(part).some((character) => character < "0" || character > "9"))) return false;
|
|
@@ -19047,7 +19146,7 @@ async function substitute(text, pattern, replacement, budget, global, occurrence
|
|
|
19047
19146
|
}
|
|
19048
19147
|
|
|
19049
19148
|
// packages/safe-bash/src/commands/text-programs/sed.ts
|
|
19050
|
-
function parse(source, extended, separator) {
|
|
19149
|
+
function parse(source, extended, separator, maxProgramInstructions) {
|
|
19051
19150
|
if (source.length > 1024 * 1024) throw new ProgramError("sed program exceeds 1 MiB");
|
|
19052
19151
|
const result = [];
|
|
19053
19152
|
const groups = [];
|
|
@@ -19143,6 +19242,7 @@ function parse(source, extended, separator) {
|
|
|
19143
19242
|
continue;
|
|
19144
19243
|
}
|
|
19145
19244
|
if (offset === source.length) break;
|
|
19245
|
+
if (result.length >= maxProgramInstructions) throw new ProgramError("program instruction limit exceeded");
|
|
19146
19246
|
const first = address();
|
|
19147
19247
|
horizontal();
|
|
19148
19248
|
let second;
|
|
@@ -19532,6 +19632,8 @@ async function execute(program, context, files, quiet, budget, separator, output
|
|
|
19532
19632
|
}
|
|
19533
19633
|
function sedCommand(options3 = {}) {
|
|
19534
19634
|
const definition = command("sed", async (context) => {
|
|
19635
|
+
const maxProgramInstructions = options3.maxProgramInstructions === void 0 ? 1024 : options3.maxProgramInstructions;
|
|
19636
|
+
if (!Number.isSafeInteger(maxProgramInstructions) || maxProgramInstructions < 1) throw new ProgramError("maxProgramInstructions must be a positive safe integer");
|
|
19535
19637
|
const budget = new Budget3(context, options3);
|
|
19536
19638
|
const sources = [];
|
|
19537
19639
|
const files = [];
|
|
@@ -19580,7 +19682,7 @@ function sedCommand(options3 = {}) {
|
|
|
19580
19682
|
sources.push(byteString(files.shift()));
|
|
19581
19683
|
}
|
|
19582
19684
|
if (sources[0]?.startsWith("#n")) quiet = true;
|
|
19583
|
-
const program = parse(sources.join("\n"), extended, separator);
|
|
19685
|
+
const program = parse(sources.join("\n"), extended, separator, maxProgramInstructions);
|
|
19584
19686
|
const outputFiles = program.flatMap((instruction) => instruction.kind !== "r" && instruction.file !== void 0 ? [instruction.file] : []);
|
|
19585
19687
|
await assertPathRequirements(context, sedRequirements, ["script-output"], outputFiles);
|
|
19586
19688
|
if (inPlace !== void 0 || outputFiles.length) {
|