@poe-platform/safe-bash 0.1.71 → 0.1.73
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 +149 -24
- package/dist/safe-bash/browser.js.map +3 -3
- package/dist/safe-bash/commands/filesystem.d.ts.map +1 -1
- package/dist/safe-bash/commands/filesystem.js +49 -28
- package/dist/safe-bash/commands/filesystem.js.map +1 -1
- package/dist/safe-bash/commands/regex-execution/bounded-provider.d.ts +1 -1
- package/dist/safe-bash/commands/regex-execution/bounded-provider.d.ts.map +1 -1
- package/dist/safe-bash/commands/regex-execution/bounded-provider.js +135 -3
- package/dist/safe-bash/commands/regex-execution/bounded-provider.js.map +1 -1
- package/package.json +2 -2
|
@@ -3432,6 +3432,7 @@ function createDirectoryReader(maxEntries = 1e4) {
|
|
|
3432
3432
|
}
|
|
3433
3433
|
|
|
3434
3434
|
// packages/safe-bash/src/commands/filesystem.ts
|
|
3435
|
+
var MAX_RECURSIVE_DIRECTORY_DEPTH = 1024;
|
|
3435
3436
|
async function preflightOperands(context, operands, check) {
|
|
3436
3437
|
for (const operand of operands) {
|
|
3437
3438
|
try {
|
|
@@ -3514,11 +3515,19 @@ async function copy(context, source, target, flags, readDirectory, top = true, a
|
|
|
3514
3515
|
if (isPathWithin(physicalSource, physicalTarget)) throw new FsError("EINVAL", { path: target, message: "cannot copy a directory into itself" });
|
|
3515
3516
|
if (ancestors.has(physicalSource)) throw new FsError("ELOOP", { path: source });
|
|
3516
3517
|
if (targetStat && targetStat.type !== "directory") throw new FsError("ENOTDIR", { path: target });
|
|
3518
|
+
context.signal.throwIfAborted();
|
|
3519
|
+
if (ancestors.size > MAX_RECURSIVE_DIRECTORY_DEPTH) {
|
|
3520
|
+
throw new FsError("ELOOP", { path: source, message: `cp directory depth limit exceeded (${MAX_RECURSIVE_DIRECTORY_DEPTH})` });
|
|
3521
|
+
}
|
|
3517
3522
|
await admitFilesystemModes(context, "cp", ["recursive"], [target]);
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3523
|
+
ancestors.add(physicalSource);
|
|
3524
|
+
try {
|
|
3525
|
+
if (!targetStat && !preflight) await context.fs.mkdir(target, { mode: sourceStat.mode & 511, signal: context.signal });
|
|
3526
|
+
for (const entry of await readDirectory(context, source, true)) {
|
|
3527
|
+
await copy(context, joinPath(source, entry.name), joinPath(target, entry.name), flags, readDirectory, false, ancestors, preflight);
|
|
3528
|
+
}
|
|
3529
|
+
} finally {
|
|
3530
|
+
ancestors.delete(physicalSource);
|
|
3522
3531
|
}
|
|
3523
3532
|
} else if (preserveLink) {
|
|
3524
3533
|
await admitFilesystemModes(context, "cp", ["symlink"], [target]);
|
|
@@ -3856,25 +3865,33 @@ function filesystemCommands(maxDirectoryEntries) {
|
|
|
3856
3865
|
await admitFilesystemModes(context, "ls", ["directory"], [path]);
|
|
3857
3866
|
const physical = await context.fs.realpath(path, { signal: context.signal });
|
|
3858
3867
|
if (ancestors.has(physical)) throw new FsError("ELOOP", { path });
|
|
3859
|
-
|
|
3860
|
-
if (
|
|
3861
|
-
|
|
3862
|
-
`);
|
|
3863
|
-
headerWritten = true;
|
|
3864
|
-
}
|
|
3865
|
-
const entries = await readDirectory(context, path, true);
|
|
3866
|
-
const names = entries.map((entry) => entry.name).filter((name2) => parsed.flags.has("a") || parsed.flags.has("A") || !name2.startsWith("."));
|
|
3867
|
-
if (parsed.flags.has("a")) for (const name2 of [".", ".."]) {
|
|
3868
|
-
const index = names.findIndex((entry) => entry > name2);
|
|
3869
|
-
names.splice(index < 0 ? names.length : index, 0, name2);
|
|
3868
|
+
context.signal.throwIfAborted();
|
|
3869
|
+
if (ancestors.size > MAX_RECURSIVE_DIRECTORY_DEPTH) {
|
|
3870
|
+
throw new FsError("ELOOP", { path, message: `ls directory depth limit exceeded (${MAX_RECURSIVE_DIRECTORY_DEPTH})` });
|
|
3870
3871
|
}
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3872
|
+
ancestors.add(physical);
|
|
3873
|
+
try {
|
|
3874
|
+
if (header) {
|
|
3875
|
+
await output(context, `${headerWritten ? "\n" : ""}${display}:
|
|
3876
|
+
`);
|
|
3877
|
+
headerWritten = true;
|
|
3878
|
+
}
|
|
3879
|
+
const entries = await readDirectory(context, path, true);
|
|
3880
|
+
const names = entries.map((entry) => entry.name).filter((name2) => parsed.flags.has("a") || parsed.flags.has("A") || !name2.startsWith("."));
|
|
3881
|
+
if (parsed.flags.has("a")) for (const name2 of [".", ".."]) {
|
|
3882
|
+
const index = names.findIndex((entry) => entry > name2);
|
|
3883
|
+
names.splice(index < 0 ? names.length : index, 0, name2);
|
|
3884
|
+
}
|
|
3885
|
+
if (parsed.flags.has("r")) names.reverse();
|
|
3886
|
+
for (const name2 of names) await render(joinPath(path, name2), name2);
|
|
3887
|
+
if (parsed.flags.has("R")) for (const name2 of names) {
|
|
3888
|
+
if (name2 === "." || name2 === "..") continue;
|
|
3889
|
+
const child = joinPath(path, name2);
|
|
3890
|
+
const childStat = await context.fs[parsed.flags.has("L") ? "stat" : "lstat"](child, { signal: context.signal });
|
|
3891
|
+
if (childStat.type === "directory") await list(child, `${display.replace(/\/$/u, "")}/${name2}`, true, ancestors);
|
|
3892
|
+
}
|
|
3893
|
+
} finally {
|
|
3894
|
+
ancestors.delete(physical);
|
|
3878
3895
|
}
|
|
3879
3896
|
};
|
|
3880
3897
|
return eachOperand(context, operands, (operand) => list(pathOf(context, operand), operand, operands.length > 1 || parsed.flags.has("R")));
|
|
@@ -18696,7 +18713,7 @@ function descriptor(value2, limits) {
|
|
|
18696
18713
|
for (const flag of flags) if (typeof value2[flag] !== "boolean") fail("protocol", `invalid ${flag} flag`);
|
|
18697
18714
|
if (kind.value === "rg" && !["sensitive", "insensitive", "smart"].includes(value2.case)) fail("protocol", "invalid case flag");
|
|
18698
18715
|
if (value2.word || kind.value === "grep" && value2.insensitive || kind.value === "rg" && value2.case !== "sensitive") fail("unsupported", "only case-sensitive, non-word selection is supported");
|
|
18699
|
-
if (kind.value === "rg" && !value2.fixed) fail("unsupported", "rg regex
|
|
18716
|
+
if (kind.value === "rg" && !value2.fixed) fail("unsupported", "rg regex modes are unsupported; use fixed UTF-8 patterns");
|
|
18700
18717
|
array(value2.patterns, limits.maxPatterns, "pattern");
|
|
18701
18718
|
let bytes2 = 0;
|
|
18702
18719
|
for (let index = 0; index < value2.patterns.length; index++) {
|
|
@@ -18724,7 +18741,7 @@ function admit(input3, limits, signal) {
|
|
|
18724
18741
|
bytes2 += length;
|
|
18725
18742
|
}
|
|
18726
18743
|
const ledger = new EreLedger({ maxExpansionBytes: 1048576, maxExpansionFields: 8192 }, {
|
|
18727
|
-
patternBytes: limits.maxPatternBytes + 4,
|
|
18744
|
+
patternBytes: limits.maxPatternBytes + (selected.fixed ? 0 : 4),
|
|
18728
18745
|
subjectBytes: limits.maxInputBytes,
|
|
18729
18746
|
work: limits.maxWork,
|
|
18730
18747
|
allocationUnits: limits.maxAllocationUnits,
|
|
@@ -18770,8 +18787,116 @@ async function admitBre(pattern, ledger, signal) {
|
|
|
18770
18787
|
}
|
|
18771
18788
|
}
|
|
18772
18789
|
}
|
|
18790
|
+
async function validateUtf8(input3, ledger, signal) {
|
|
18791
|
+
for (let index = 0; index < input3.length; ) {
|
|
18792
|
+
const first = typeof input3 === "string" ? input3.charCodeAt(index) : input3[index];
|
|
18793
|
+
const width = first < 128 ? 1 : first >= 194 && first <= 223 ? 2 : first >= 224 && first <= 239 ? 3 : first >= 240 && first <= 244 ? 4 : 0;
|
|
18794
|
+
ledger.charge("work", Math.max(width, 1), signal);
|
|
18795
|
+
if (first === 0 || width === 0 || width > input3.length - index) fail("unsupported", "literal patterns and subjects require valid non-NUL UTF-8 bytes");
|
|
18796
|
+
for (let continuation = 1; continuation < width; continuation++) {
|
|
18797
|
+
const byte = typeof input3 === "string" ? input3.charCodeAt(index + continuation) : input3[index + continuation];
|
|
18798
|
+
if (byte < 128 || byte > 191 || continuation === 1 && (first === 224 && byte < 160 || first === 237 && byte > 159 || first === 240 && byte < 144 || first === 244 && byte > 143)) fail("unsupported", "literal patterns and subjects require valid non-NUL UTF-8 bytes");
|
|
18799
|
+
}
|
|
18800
|
+
index += width;
|
|
18801
|
+
await ledger.checkpoint(signal);
|
|
18802
|
+
}
|
|
18803
|
+
}
|
|
18804
|
+
async function literalBytes(pattern, selected, ledger, signal) {
|
|
18805
|
+
const { kind } = selected;
|
|
18806
|
+
let length = 0;
|
|
18807
|
+
if (kind === "grep") {
|
|
18808
|
+
await validateUtf8(pattern, ledger, signal);
|
|
18809
|
+
length = pattern.length;
|
|
18810
|
+
} else {
|
|
18811
|
+
for (let index = 0; index < pattern.length; ) {
|
|
18812
|
+
const scalar = pattern.codePointAt(index);
|
|
18813
|
+
const units = scalar > 65535 ? 2 : 1;
|
|
18814
|
+
ledger.charge("work", units, signal);
|
|
18815
|
+
if (scalar === 0 || scalar >= 55296 && scalar <= 57343) fail("unsupported", "rg literal patterns require non-NUL Unicode scalars for UTF-8");
|
|
18816
|
+
if (scalar === 10 && selected.kind === "rg" && !selected.nullData) fail("unsupported", "rg multiline matching is unsupported");
|
|
18817
|
+
length += scalar < 128 ? 1 : scalar < 2048 ? 2 : scalar < 65536 ? 3 : 4;
|
|
18818
|
+
index += units;
|
|
18819
|
+
await ledger.checkpoint(signal);
|
|
18820
|
+
}
|
|
18821
|
+
}
|
|
18822
|
+
ledger.charge("patternBytes", length, signal);
|
|
18823
|
+
ledger.charge("allocationUnits", length + 1, signal);
|
|
18824
|
+
const bytes2 = new Uint8Array(length);
|
|
18825
|
+
let offset = 0;
|
|
18826
|
+
for (let index = 0; index < pattern.length; ) {
|
|
18827
|
+
const scalar = kind === "grep" ? pattern.charCodeAt(index) : pattern.codePointAt(index);
|
|
18828
|
+
const width = kind === "grep" || scalar < 128 ? 1 : scalar < 2048 ? 2 : scalar < 65536 ? 3 : 4;
|
|
18829
|
+
ledger.charge("work", width, signal);
|
|
18830
|
+
if (width === 1) bytes2[offset++] = scalar;
|
|
18831
|
+
else {
|
|
18832
|
+
bytes2[offset++] = width === 2 ? 192 | scalar >> 6 : width === 3 ? 224 | scalar >> 12 : 240 | scalar >> 18;
|
|
18833
|
+
if (width === 4) bytes2[offset++] = 128 | scalar >> 12 & 63;
|
|
18834
|
+
if (width >= 3) bytes2[offset++] = 128 | scalar >> 6 & 63;
|
|
18835
|
+
bytes2[offset++] = 128 | scalar & 63;
|
|
18836
|
+
}
|
|
18837
|
+
index += kind === "rg" && scalar > 65535 ? 2 : 1;
|
|
18838
|
+
await ledger.checkpoint(signal);
|
|
18839
|
+
}
|
|
18840
|
+
return bytes2;
|
|
18841
|
+
}
|
|
18842
|
+
async function compileLiteral(bytes2, ledger, signal) {
|
|
18843
|
+
ledger.charge("states", bytes2.length, signal);
|
|
18844
|
+
ledger.charge("allocationUnits", bytes2.length * 4 + 3, signal);
|
|
18845
|
+
const fallback = new Uint32Array(bytes2.length);
|
|
18846
|
+
for (let index = 1, prefix2 = 0; index < bytes2.length; ) {
|
|
18847
|
+
ledger.charge("work", 1, signal);
|
|
18848
|
+
if (bytes2[index] === bytes2[prefix2]) fallback[index++] = ++prefix2;
|
|
18849
|
+
else if (prefix2 > 0) prefix2 = fallback[prefix2 - 1];
|
|
18850
|
+
else index++;
|
|
18851
|
+
await ledger.checkpoint(signal);
|
|
18852
|
+
}
|
|
18853
|
+
return { bytes: bytes2, fallback };
|
|
18854
|
+
}
|
|
18855
|
+
async function literalStart(program, subject, whole, ledger, signal) {
|
|
18856
|
+
const { bytes: bytes2, fallback } = program;
|
|
18857
|
+
ledger.charge("work", 1, signal);
|
|
18858
|
+
await ledger.checkpoint(signal);
|
|
18859
|
+
if (whole && bytes2.length !== subject.length || bytes2.length > subject.length) return -1;
|
|
18860
|
+
if (bytes2.length === 0) return 0;
|
|
18861
|
+
for (let index = 0, prefix2 = 0; index < subject.length; ) {
|
|
18862
|
+
ledger.charge("work", 1, signal);
|
|
18863
|
+
if (subject[index] === bytes2[prefix2]) {
|
|
18864
|
+
index++;
|
|
18865
|
+
if (++prefix2 === bytes2.length) return index - prefix2;
|
|
18866
|
+
} else if (prefix2 > 0) prefix2 = fallback[prefix2 - 1];
|
|
18867
|
+
else index++;
|
|
18868
|
+
await ledger.checkpoint(signal);
|
|
18869
|
+
}
|
|
18870
|
+
return -1;
|
|
18871
|
+
}
|
|
18872
|
+
async function executeLiteral(input3, signal) {
|
|
18873
|
+
const { descriptor: selected, rows, ledger } = input3;
|
|
18874
|
+
const programs2 = [];
|
|
18875
|
+
for (const pattern of selected.patterns) {
|
|
18876
|
+
programs2.push(await compileLiteral(await literalBytes(pattern, selected, ledger, signal), ledger, signal));
|
|
18877
|
+
}
|
|
18878
|
+
const results = [];
|
|
18879
|
+
for (const row of rows) {
|
|
18880
|
+
await validateUtf8(row.bytes, ledger, signal);
|
|
18881
|
+
let start = -1;
|
|
18882
|
+
let end = -1;
|
|
18883
|
+
for (const program of programs2) {
|
|
18884
|
+
const candidate = await literalStart(program, row.bytes, selected.whole, ledger, signal);
|
|
18885
|
+
if (candidate < 0) continue;
|
|
18886
|
+
if (start < 0 || candidate < start) {
|
|
18887
|
+
start = candidate;
|
|
18888
|
+
end = start + program.bytes.length;
|
|
18889
|
+
}
|
|
18890
|
+
if (selected.kind === "grep") break;
|
|
18891
|
+
}
|
|
18892
|
+
signal.throwIfAborted();
|
|
18893
|
+
results.push(start < 0 ? new Float64Array() : new Float64Array([start, end]));
|
|
18894
|
+
}
|
|
18895
|
+
return { id: input3.id, results };
|
|
18896
|
+
}
|
|
18773
18897
|
async function execute2(input3, signal) {
|
|
18774
18898
|
const { descriptor: selected, rows, ledger } = input3;
|
|
18899
|
+
if (selected.fixed) return executeLiteral(input3, signal);
|
|
18775
18900
|
const programs2 = [];
|
|
18776
18901
|
for (const pattern of selected.patterns) {
|
|
18777
18902
|
if (selected.kind === "grep" && !selected.fixed && !selected.extended) await admitBre(pattern, ledger, signal);
|