@poe-platform/safe-bash 0.1.423 → 0.1.424
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 +113 -16
- package/dist/safe-bash/browser.js.map +4 -4
- 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
|
@@ -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,
|