@sjawhar/pi-legion-envoy 5.17.3 → 5.17.5
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/legion.js +723 -91
- package/package.json +1 -1
package/dist/legion.js
CHANGED
|
@@ -33473,7 +33473,7 @@ import { logger } from "@oh-my-pi/pi-utils";
|
|
|
33473
33473
|
// package.json
|
|
33474
33474
|
var package_default = {
|
|
33475
33475
|
name: "@sjawhar/pi-legion-envoy",
|
|
33476
|
-
version: "5.17.
|
|
33476
|
+
version: "5.17.5",
|
|
33477
33477
|
type: "module",
|
|
33478
33478
|
omp: {
|
|
33479
33479
|
extensions: [
|
|
@@ -34776,8 +34776,11 @@ function createGoLegionTool(deps) {
|
|
|
34776
34776
|
import {
|
|
34777
34777
|
closeSync,
|
|
34778
34778
|
existsSync,
|
|
34779
|
+
lstatSync,
|
|
34780
|
+
opendirSync,
|
|
34779
34781
|
openSync,
|
|
34780
34782
|
readFileSync as readFileSync3,
|
|
34783
|
+
readlinkSync,
|
|
34781
34784
|
readSync,
|
|
34782
34785
|
realpathSync,
|
|
34783
34786
|
statSync
|
|
@@ -41435,9 +41438,15 @@ function isUnset(value) {
|
|
|
41435
41438
|
}
|
|
41436
41439
|
var NO_ELEMENTS = { kind: "literal", text: "", noElements: true };
|
|
41437
41440
|
function stored(value) {
|
|
41438
|
-
|
|
41439
|
-
|
|
41440
|
-
|
|
41441
|
+
const counted = value.map((piece) => {
|
|
41442
|
+
if (piece.unknownCount !== true)
|
|
41443
|
+
return piece;
|
|
41444
|
+
const { unknownCount: _, ...rest } = piece;
|
|
41445
|
+
return rest;
|
|
41446
|
+
});
|
|
41447
|
+
if (!counted.some((piece) => piece.noElements === true))
|
|
41448
|
+
return counted;
|
|
41449
|
+
return counted.map((piece) => piece.noElements === true ? literal2("") : piece);
|
|
41441
41450
|
}
|
|
41442
41451
|
var ANY_NAME = "\x00";
|
|
41443
41452
|
var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -41477,7 +41486,7 @@ function wrapRefusal(error48, site, label) {
|
|
|
41477
41486
|
}
|
|
41478
41487
|
var MAX_DEPTH = 8;
|
|
41479
41488
|
var MAX_WALK_STEPS = 1e5;
|
|
41480
|
-
var WALK_LIMIT = `the guard reached its walk limit of ${MAX_WALK_STEPS} nodes; run a smaller script`;
|
|
41489
|
+
var WALK_LIMIT = `the guard reached its walk limit of ${MAX_WALK_STEPS} steps (syntax nodes, pattern matching, and the destination entries a copy inspects); run a smaller script, or copy into a smaller destination`;
|
|
41481
41490
|
var MAX_ALTERNATIVES = 64;
|
|
41482
41491
|
var MAX_PATTERN_VALUE = 512;
|
|
41483
41492
|
var PATTERN_WORK_PER_STEP = 1024;
|
|
@@ -41500,6 +41509,10 @@ var FILE_COMMANDS = new Set([
|
|
|
41500
41509
|
"rm",
|
|
41501
41510
|
"unlink",
|
|
41502
41511
|
"mv",
|
|
41512
|
+
"cp",
|
|
41513
|
+
"dd",
|
|
41514
|
+
"install",
|
|
41515
|
+
"ln",
|
|
41503
41516
|
"shred",
|
|
41504
41517
|
"truncate",
|
|
41505
41518
|
"chmod",
|
|
@@ -41612,6 +41625,70 @@ function realish(abs, followFinal) {
|
|
|
41612
41625
|
return realExisting(resolved);
|
|
41613
41626
|
return path4.join(realExisting(path4.dirname(resolved)), path4.basename(resolved));
|
|
41614
41627
|
}
|
|
41628
|
+
function readlinkTarget(link) {
|
|
41629
|
+
try {
|
|
41630
|
+
return readlinkSync(link);
|
|
41631
|
+
} catch {
|
|
41632
|
+
return "a target the guard cannot read";
|
|
41633
|
+
}
|
|
41634
|
+
}
|
|
41635
|
+
function physical(cwd, text, followFinal) {
|
|
41636
|
+
const joined = text.startsWith("/") ? text : `${cwd}/${text}`;
|
|
41637
|
+
const parts = joined.split("/");
|
|
41638
|
+
const last = parts.findLastIndex((part) => part !== "" && part !== ".");
|
|
41639
|
+
const pending = [];
|
|
41640
|
+
let real = "/";
|
|
41641
|
+
for (let i = 0;i < parts.length; i += 1) {
|
|
41642
|
+
const part = parts[i];
|
|
41643
|
+
if (part === "" || part === ".")
|
|
41644
|
+
continue;
|
|
41645
|
+
if (pending.length > 0) {
|
|
41646
|
+
if (part === "..") {
|
|
41647
|
+
return {
|
|
41648
|
+
unknown: `\`${path4.join(real, ...pending)}\`, which does not exist yet, so the guard ` + "cannot know where the `..` after it leads"
|
|
41649
|
+
};
|
|
41650
|
+
}
|
|
41651
|
+
pending.push(part);
|
|
41652
|
+
continue;
|
|
41653
|
+
}
|
|
41654
|
+
if (part === "..") {
|
|
41655
|
+
real = path4.dirname(real);
|
|
41656
|
+
continue;
|
|
41657
|
+
}
|
|
41658
|
+
const candidate = real === "/" ? `/${part}` : `${real}/${part}`;
|
|
41659
|
+
if (!followFinal && i === last) {
|
|
41660
|
+
return { real: candidate };
|
|
41661
|
+
}
|
|
41662
|
+
let link;
|
|
41663
|
+
try {
|
|
41664
|
+
link = lstatSync(candidate).isSymbolicLink();
|
|
41665
|
+
} catch (error48) {
|
|
41666
|
+
if (error48.code !== "ENOENT") {
|
|
41667
|
+
return { unknown: `\`${candidate}\`, which the guard cannot read (${messageFor(error48)})` };
|
|
41668
|
+
}
|
|
41669
|
+
pending.push(part);
|
|
41670
|
+
continue;
|
|
41671
|
+
}
|
|
41672
|
+
if (!link) {
|
|
41673
|
+
real = candidate;
|
|
41674
|
+
continue;
|
|
41675
|
+
}
|
|
41676
|
+
try {
|
|
41677
|
+
real = realpathSync(candidate);
|
|
41678
|
+
} catch (error48) {
|
|
41679
|
+
const code = error48.code;
|
|
41680
|
+
const why = code === "ENOENT" ? `points at \`${readlinkTarget(candidate)}\`, which is not there` : `cannot be followed (${messageFor(error48)})`;
|
|
41681
|
+
return { unknown: `the link \`${candidate}\` ${why}, so the guard cannot resolve this path` };
|
|
41682
|
+
}
|
|
41683
|
+
}
|
|
41684
|
+
return { real: pending.length === 0 ? real : path4.join(real, ...pending) };
|
|
41685
|
+
}
|
|
41686
|
+
function kernelPath(from, text) {
|
|
41687
|
+
if (from === undefined && !text.startsWith("/"))
|
|
41688
|
+
return;
|
|
41689
|
+
const resolved = physical(from ?? "/", text, true);
|
|
41690
|
+
return "unknown" in resolved ? undefined : resolved.real;
|
|
41691
|
+
}
|
|
41615
41692
|
function isProtectedScratchComponent(component, roots, partial2 = false) {
|
|
41616
41693
|
return roots.protectedScratch.some((name) => component.startsWith(name) || partial2 && name.startsWith(component)) || roots.protectedDirectories.some((name) => component === name || partial2 && name.startsWith(component));
|
|
41617
41694
|
}
|
|
@@ -41644,12 +41721,16 @@ function judgePath(exp, st, ctx, options) {
|
|
|
41644
41721
|
resolution: `relative to a working directory unknown after ${st.cwdWhy}`
|
|
41645
41722
|
};
|
|
41646
41723
|
}
|
|
41647
|
-
const
|
|
41648
|
-
|
|
41724
|
+
const resolved2 = physical(st.cwd ?? "/", text, options.follow || text.endsWith("/"));
|
|
41725
|
+
if ("unknown" in resolved2) {
|
|
41726
|
+
return { ok: false, resolution: resolved2.unknown, unresolved: true };
|
|
41727
|
+
}
|
|
41728
|
+
const real2 = resolved2.real;
|
|
41649
41729
|
if (inWorkspace(real2, roots) || inScratch(real2, roots))
|
|
41650
41730
|
return { ok: true };
|
|
41651
41731
|
if (options.overwrite && !existsSync(real2))
|
|
41652
41732
|
return { ok: true };
|
|
41733
|
+
const abs = path4.resolve(st.cwd ?? "/", text);
|
|
41653
41734
|
return { ok: false, resolution: real2 === abs ? real2 : `${abs}, which resolves to ${real2}` };
|
|
41654
41735
|
}
|
|
41655
41736
|
const piece = exp[open];
|
|
@@ -41664,11 +41745,13 @@ function judgePath(exp, st, ctx, options) {
|
|
|
41664
41745
|
return { ok: false, resolution: `relative to a working directory unknown after ${st.cwdWhy}` };
|
|
41665
41746
|
}
|
|
41666
41747
|
const component = prefix.slice(slash + 1);
|
|
41667
|
-
|
|
41668
|
-
|
|
41669
|
-
|
|
41748
|
+
const stemText = slash === -1 ? "." : slash === 0 ? "/" : prefix.slice(0, slash);
|
|
41749
|
+
const resolved = physical(st.cwd ?? "/", stemText, true);
|
|
41750
|
+
if ("unknown" in resolved) {
|
|
41751
|
+
return { ok: false, resolution: resolved.unknown, unresolved: true };
|
|
41670
41752
|
}
|
|
41671
|
-
const
|
|
41753
|
+
const dotGlob = piece.kind === "glob" && (component === "" ? piece.text : component).startsWith(".");
|
|
41754
|
+
const real = dotGlob ? path4.dirname(resolved.real) : resolved.real;
|
|
41672
41755
|
const allowed = inWorkspace(real, roots) || inScratch(real, roots) || real === roots.scratch && component !== "" && !isProtectedScratchComponent(component, roots, true);
|
|
41673
41756
|
if (allowed)
|
|
41674
41757
|
return { ok: true };
|
|
@@ -41804,8 +41887,10 @@ function resolveArrayIndex(index, st, ctx) {
|
|
|
41804
41887
|
}
|
|
41805
41888
|
function parameter(name, quoted, st, ctx) {
|
|
41806
41889
|
if (name === "@" || name === "*") {
|
|
41807
|
-
if (st.positional === undefined)
|
|
41808
|
-
|
|
41890
|
+
if (st.positional === undefined) {
|
|
41891
|
+
const piece = unknown2(`\`$${name}\` (the positional parameters)`);
|
|
41892
|
+
return [[name === "*" && quoted ? piece : { ...piece, unknownCount: true }]];
|
|
41893
|
+
}
|
|
41809
41894
|
return elements(st.positional, name === "*" && quoted);
|
|
41810
41895
|
}
|
|
41811
41896
|
if (/^[0-9]+$/.test(name)) {
|
|
@@ -41880,11 +41965,14 @@ function parameterExpansion(part, quoted, st, ctx) {
|
|
|
41880
41965
|
if (part.index === "@" || part.index === "*") {
|
|
41881
41966
|
if (array2 !== undefined) {
|
|
41882
41967
|
const unknownElement = array2.get(UNKNOWN_ARRAY_INDEX);
|
|
41883
|
-
if (unknownElement !== undefined)
|
|
41884
|
-
|
|
41968
|
+
if (unknownElement !== undefined) {
|
|
41969
|
+
if (part.index === "*" && quoted)
|
|
41970
|
+
return [[...unknownElement]];
|
|
41971
|
+
return [unknownElement.map((piece) => ({ ...piece, unknownCount: true }))];
|
|
41972
|
+
}
|
|
41885
41973
|
return elements([...array2.values()], part.index === "*" && quoted);
|
|
41886
41974
|
}
|
|
41887
|
-
return parameter(name, quoted, st, ctx);
|
|
41975
|
+
return parameter(name, quoted, st, ctx).map((alt) => !(part.index === "*" && quoted) && alt.some((piece) => piece.kind === "unknown") ? alt.map((piece) => ({ ...piece, unknownCount: true })) : alt);
|
|
41888
41976
|
}
|
|
41889
41977
|
if (part.index !== undefined) {
|
|
41890
41978
|
const index = resolveArrayIndex(part.index, st, ctx);
|
|
@@ -41997,12 +42085,13 @@ function patternExpansion(part, quoted, st, ctx) {
|
|
|
41997
42085
|
return [unquotedLiteral(result.replaceAll("\\", "\\\\"), undefined, st, ctx)];
|
|
41998
42086
|
}
|
|
41999
42087
|
function substitution(script, text, st, ctx) {
|
|
42000
|
-
const pidRead = /^\$\(
|
|
42001
|
-
const variableRead = /^\$\(
|
|
42088
|
+
const pidRead = /^\$\(<\s*(.+?)\s*\)$/.exec(text);
|
|
42089
|
+
const variableRead = /^\$\(<\s*"?\$([A-Za-z_][A-Za-z0-9_]*)"?\s*\)$/.exec(text);
|
|
42002
42090
|
const directFile = pidRead?.[1]?.includes("$") === true ? undefined : pidRead?.[1];
|
|
42003
42091
|
const file2 = directFile ?? (variableRead === null ? undefined : literalText(lookup(variableRead[1], st, ctx)));
|
|
42004
|
-
|
|
42005
|
-
|
|
42092
|
+
const pidFile = file2 === undefined ? undefined : kernelPath(st.cwd, file2);
|
|
42093
|
+
if (pidFile !== undefined) {
|
|
42094
|
+
const pid = st.pidFiles.get(pidFile);
|
|
42006
42095
|
if (pid !== undefined)
|
|
42007
42096
|
return [[...pid]];
|
|
42008
42097
|
}
|
|
@@ -42056,22 +42145,70 @@ function substitutionInvocation(node, st, ctx) {
|
|
|
42056
42145
|
return;
|
|
42057
42146
|
return { base: path4.basename(name), command: node, rest: argv.slice(1) };
|
|
42058
42147
|
}
|
|
42148
|
+
function gnuDirname(text) {
|
|
42149
|
+
const trimmed = text.replace(/\/+$/, "");
|
|
42150
|
+
if (trimmed === "")
|
|
42151
|
+
return "/";
|
|
42152
|
+
const cut = trimmed.replace(/\/+[^/]+$/, "");
|
|
42153
|
+
if (cut === trimmed)
|
|
42154
|
+
return ".";
|
|
42155
|
+
return cut === "" ? "/" : cut;
|
|
42156
|
+
}
|
|
42157
|
+
function gnuBasename(text) {
|
|
42158
|
+
const trimmed = text.replace(/\/+$/, "");
|
|
42159
|
+
if (trimmed === "")
|
|
42160
|
+
return text === "" ? "" : "/";
|
|
42161
|
+
return trimmed.slice(trimmed.lastIndexOf("/") + 1);
|
|
42162
|
+
}
|
|
42059
42163
|
function substitutionPath(base, list, st) {
|
|
42060
42164
|
if (!["dirname", "basename", "realpath", "readlink"].includes(base))
|
|
42061
42165
|
return;
|
|
42062
|
-
const
|
|
42063
|
-
if (
|
|
42166
|
+
const found = operands(list, "");
|
|
42167
|
+
if (base === "basename" && found.options.some((option) => option === "--multiple" || option.startsWith("--suffix") || /^-[^-]*[as]/.test(option))) {
|
|
42168
|
+
return;
|
|
42169
|
+
}
|
|
42170
|
+
const suffixed = base === "basename" && found.operands.length === 2;
|
|
42171
|
+
if (found.operands.length !== 1 && !suffixed)
|
|
42172
|
+
return;
|
|
42173
|
+
const target = literalText(found.operands[0]?.exp);
|
|
42174
|
+
const suffix = suffixed ? literalText(found.operands[1]?.exp) : "";
|
|
42175
|
+
if (target === undefined || suffix === undefined || st.cwd === undefined)
|
|
42064
42176
|
return;
|
|
42065
42177
|
if (target === "")
|
|
42066
42178
|
return base === "dirname" ? "." : "";
|
|
42067
|
-
const abs = path4.resolve(st.cwd, target);
|
|
42068
42179
|
if (base === "dirname")
|
|
42069
|
-
return
|
|
42070
|
-
if (base === "basename")
|
|
42071
|
-
|
|
42072
|
-
|
|
42073
|
-
|
|
42074
|
-
|
|
42180
|
+
return gnuDirname(target);
|
|
42181
|
+
if (base === "basename") {
|
|
42182
|
+
const name = gnuBasename(target);
|
|
42183
|
+
return suffix !== "" && name !== suffix && name.endsWith(suffix) ? name.slice(0, -suffix.length) : name;
|
|
42184
|
+
}
|
|
42185
|
+
if (base === "readlink") {
|
|
42186
|
+
const mode = lastModeFlag(found.options, "fem", {
|
|
42187
|
+
"--canonicalize": "f",
|
|
42188
|
+
"--canonicalize-existing": "e",
|
|
42189
|
+
"--canonicalize-missing": "m"
|
|
42190
|
+
});
|
|
42191
|
+
if (mode !== "f" && mode !== "m")
|
|
42192
|
+
return;
|
|
42193
|
+
}
|
|
42194
|
+
if (base === "realpath") {
|
|
42195
|
+
if (found.options.some((option) => option.startsWith("--relative")))
|
|
42196
|
+
return;
|
|
42197
|
+
const mode = lastModeFlag(found.options, "sLP", {
|
|
42198
|
+
"--strip": "s",
|
|
42199
|
+
"--no-symlinks": "s",
|
|
42200
|
+
"--logical": "L",
|
|
42201
|
+
"--physical": "P"
|
|
42202
|
+
});
|
|
42203
|
+
if (mode === "s" || mode === "L") {
|
|
42204
|
+
const here = kernelPath("/", st.cwd);
|
|
42205
|
+
if (here === undefined)
|
|
42206
|
+
return;
|
|
42207
|
+
const lexical = path4.resolve(here, target);
|
|
42208
|
+
return mode === "s" ? lexical : kernelPath("/", lexical);
|
|
42209
|
+
}
|
|
42210
|
+
}
|
|
42211
|
+
return kernelPath(st.cwd, target);
|
|
42075
42212
|
}
|
|
42076
42213
|
function mktempPath(command, st, ctx) {
|
|
42077
42214
|
const words = command.suffix;
|
|
@@ -42090,7 +42227,7 @@ function mktempPath(command, st, ctx) {
|
|
|
42090
42227
|
else if (value.startsWith("-p") && value.length > 2)
|
|
42091
42228
|
directory = [literal2(value.slice(2))];
|
|
42092
42229
|
else if (!value.startsWith("-") && value.includes("/")) {
|
|
42093
|
-
directory = [literal2(path4.
|
|
42230
|
+
directory = [literal2(path4.dirname(value))];
|
|
42094
42231
|
}
|
|
42095
42232
|
}
|
|
42096
42233
|
const base = directory ?? [literal2(ctx.env.TMPDIR ?? "/tmp")];
|
|
@@ -42103,10 +42240,18 @@ function braceAlternatives(part) {
|
|
|
42103
42240
|
return [[{ kind: "glob", text: part.text }]];
|
|
42104
42241
|
return inner.split(",").map((alternative) => [literal2(alternative)]);
|
|
42105
42242
|
}
|
|
42243
|
+
function tildeAt(text) {
|
|
42244
|
+
if (text.startsWith("~"))
|
|
42245
|
+
return 0;
|
|
42246
|
+
const equals = text.indexOf("=");
|
|
42247
|
+
if (equals <= 0 || !IDENTIFIER.test(text.slice(0, equals)))
|
|
42248
|
+
return;
|
|
42249
|
+
return text.charAt(equals + 1) === "~" ? equals + 1 : undefined;
|
|
42250
|
+
}
|
|
42106
42251
|
function expandPart(part, first, st, ctx) {
|
|
42107
42252
|
switch (part.type) {
|
|
42108
42253
|
case "Literal":
|
|
42109
|
-
return [unquotedLiteral(part.text, first ?
|
|
42254
|
+
return [unquotedLiteral(part.text, first ? tildeAt(part.text) : undefined, st, ctx)];
|
|
42110
42255
|
case "SingleQuoted":
|
|
42111
42256
|
case "AnsiCQuoted":
|
|
42112
42257
|
return [[literal2(part.value)]];
|
|
@@ -42162,7 +42307,7 @@ function textLength(pieces) {
|
|
|
42162
42307
|
function expandWord(word, st, ctx) {
|
|
42163
42308
|
let alternatives = [[]];
|
|
42164
42309
|
if (word.parts === undefined)
|
|
42165
|
-
alternatives = [unquotedLiteral(word.text,
|
|
42310
|
+
alternatives = [unquotedLiteral(word.text, tildeAt(word.text), st, ctx)];
|
|
42166
42311
|
else {
|
|
42167
42312
|
word.parts.forEach((part, index) => {
|
|
42168
42313
|
alternatives = product(alternatives, expandPart(part, index === 0, st, ctx));
|
|
@@ -42188,6 +42333,9 @@ function args(words, st, ctx) {
|
|
|
42188
42333
|
if (exp.some((piece) => piece.kind === "glob")) {
|
|
42189
42334
|
return { text: word.text, exp, fields: "unknown" };
|
|
42190
42335
|
}
|
|
42336
|
+
if (exp.some((piece) => piece.unknownCount === true)) {
|
|
42337
|
+
return { text: word.text, exp, fields: "unknown" };
|
|
42338
|
+
}
|
|
42191
42339
|
if (unquoted && exp.some((piece) => piece.kind === "unknown" || /[ \t\n]/.test(piece.text))) {
|
|
42192
42340
|
return { text: word.text, exp, fields: "unknown" };
|
|
42193
42341
|
}
|
|
@@ -42227,6 +42375,9 @@ function mayBeOption(arg, matches) {
|
|
|
42227
42375
|
const { text, whole } = readableWord(arg);
|
|
42228
42376
|
if (whole)
|
|
42229
42377
|
return matches(text);
|
|
42378
|
+
return mayBeSomeOption(text);
|
|
42379
|
+
}
|
|
42380
|
+
function mayBeSomeOption(text) {
|
|
42230
42381
|
return text === "" || /^--?[a-zA-Z-]*$/.test(text);
|
|
42231
42382
|
}
|
|
42232
42383
|
function valueInWord(arg, count) {
|
|
@@ -42522,6 +42673,10 @@ function modelPidFile(st, ctx, file2, pid) {
|
|
|
42522
42673
|
model.set(file2, [unknown2("a pid a branch or loop body wrote to this file", true)]);
|
|
42523
42674
|
}
|
|
42524
42675
|
}
|
|
42676
|
+
function forgetFileModels(st, ctx, why) {
|
|
42677
|
+
st.pidFiles.clear();
|
|
42678
|
+
ctx.unnameableWrite.why ??= why;
|
|
42679
|
+
}
|
|
42525
42680
|
function handlerVars(trap, vars) {
|
|
42526
42681
|
const { vars: branch, merged } = trap;
|
|
42527
42682
|
if (branch === undefined || merged === undefined)
|
|
@@ -42682,6 +42837,15 @@ function spendPatternWork(work, ctx) {
|
|
|
42682
42837
|
function outputChanged(before, after) {
|
|
42683
42838
|
return before !== undefined && (after === undefined || JSON.stringify(before) !== JSON.stringify(after));
|
|
42684
42839
|
}
|
|
42840
|
+
function loopValue(type, name, words) {
|
|
42841
|
+
const pids = words.every((word) => {
|
|
42842
|
+
const text = literalText(word.exp);
|
|
42843
|
+
return text === "" || word.exp.length > 0 && word.exp.every((piece) => piece.descendantPid);
|
|
42844
|
+
});
|
|
42845
|
+
if (pids)
|
|
42846
|
+
return [unknown2(`\`$${name}\`, a loop variable`, true)];
|
|
42847
|
+
return type === "For" && words.length === 1 && words[0]?.exp[0]?.kind === "literal" && words[0].exp[0].text !== "" && !words[0].exp[0].text.startsWith("-") && words[0]?.exp.some((piece) => piece.kind === "glob") ? words[0].exp : [unknown2(`\`$${name}\`, a loop variable`)];
|
|
42848
|
+
}
|
|
42685
42849
|
function walkNode(node, st, ctx, pipeIn) {
|
|
42686
42850
|
if (++ctx.steps.count > MAX_WALK_STEPS) {
|
|
42687
42851
|
const site = siteOf(node, st);
|
|
@@ -42805,11 +42969,7 @@ function walkNode(node, st, ctx, pipeIn) {
|
|
|
42805
42969
|
}
|
|
42806
42970
|
} else {
|
|
42807
42971
|
const body = clone2(st);
|
|
42808
|
-
|
|
42809
|
-
const text = literalText(word.exp);
|
|
42810
|
-
return text === "" || word.exp.length > 0 && word.exp.every((piece) => piece.descendantPid);
|
|
42811
|
-
});
|
|
42812
|
-
assignScalar(body, name, signalSafe ? [unknown2(`\`$${name}\`, a loop variable`, true)] : [unknown2(`\`$${name}\`, a loop variable`)]);
|
|
42972
|
+
assignScalar(body, name, loopValue(node.type, name, words));
|
|
42813
42973
|
if (node.type === "Select")
|
|
42814
42974
|
assignScalar(body, "REPLY", [unknown2("`$REPLY`, read from input")]);
|
|
42815
42975
|
uncertainly(ctx, () => walkList(node.body.commands, body, ctx));
|
|
@@ -42908,13 +43068,15 @@ function checkRedirects(redirects, command, site, st, ctx) {
|
|
|
42908
43068
|
if (writes || operator === ">&") {
|
|
42909
43069
|
const verdict = judgePath(exp, st, ctx, { follow: true, overwrite: true });
|
|
42910
43070
|
if (!verdict.ok) {
|
|
42911
|
-
throw refusal(site, `the redirection \`${operator}\` would overwrite \`${redirect.target.text}\` (${verdict.resolution})`, ctx);
|
|
43071
|
+
throw refusal(site, `the redirection \`${operator}\` would overwrite \`${redirect.target.text}\` (${verdict.resolution})`, ctx, verdict.unresolved);
|
|
42912
43072
|
}
|
|
42913
43073
|
}
|
|
42914
43074
|
const target = literalText(exp);
|
|
42915
|
-
|
|
43075
|
+
const file2 = target === undefined ? undefined : kernelPath(st.cwd, target);
|
|
43076
|
+
if (file2 === undefined) {
|
|
43077
|
+
forgetFileModels(st, ctx, `the redirection \`${operator} ${redirect.target.text}\``);
|
|
42916
43078
|
continue;
|
|
42917
|
-
|
|
43079
|
+
}
|
|
42918
43080
|
let content = null;
|
|
42919
43081
|
if (command?.name === "cat" && command.args.length === 0) {
|
|
42920
43082
|
if (heredoc !== undefined)
|
|
@@ -42948,9 +43110,9 @@ function groupOutputFile(statement, st, ctx) {
|
|
|
42948
43110
|
}
|
|
42949
43111
|
const targets = expandWord(redirect.target, st, ctx);
|
|
42950
43112
|
const target = targets.length === 1 ? literalText(targets[0]) : undefined;
|
|
42951
|
-
if (target === undefined
|
|
43113
|
+
if (target === undefined)
|
|
42952
43114
|
return;
|
|
42953
|
-
return
|
|
43115
|
+
return kernelPath(st.cwd, target);
|
|
42954
43116
|
}
|
|
42955
43117
|
function walkRenderingGroup(group, st, ctx) {
|
|
42956
43118
|
let content = "";
|
|
@@ -43069,8 +43231,10 @@ function printfText(list, limit, script = false) {
|
|
|
43069
43231
|
} while (conversions > 0 && index < values.length);
|
|
43070
43232
|
return out;
|
|
43071
43233
|
}
|
|
43072
|
-
function refusal(site, detail, ctx) {
|
|
43073
|
-
|
|
43234
|
+
function refusal(site, detail, ctx, unresolved = false) {
|
|
43235
|
+
const where = unresolved ? `. Every target is resolved as the kernel resolves it, against ${describeRoots(ctx.roots)}` : `, outside ${describeRoots(ctx.roots)}`;
|
|
43236
|
+
const hint = unresolved ? "Make the unresolved path component resolvable in an earlier command \u2014 create it, make " + "it readable, or fix a dangling or looping symlink \u2014 then run this command." : HINT;
|
|
43237
|
+
return new Refusal(site.snippet, site.line, `${detail}${where}. ${hint}`);
|
|
43074
43238
|
}
|
|
43075
43239
|
function handleCommand(command, st, ctx, pipeIn) {
|
|
43076
43240
|
const site = siteOf(command, st);
|
|
@@ -43168,6 +43332,16 @@ function operands(list, valued, longValued = []) {
|
|
|
43168
43332
|
}
|
|
43169
43333
|
return { options, operands: found };
|
|
43170
43334
|
}
|
|
43335
|
+
function lastModeFlag(options, letters, long = {}) {
|
|
43336
|
+
return options.flatMap((option) => {
|
|
43337
|
+
const named = long[option];
|
|
43338
|
+
if (named !== undefined)
|
|
43339
|
+
return [named];
|
|
43340
|
+
if (!/^-[a-zA-Z]+$/.test(option))
|
|
43341
|
+
return [];
|
|
43342
|
+
return [...option.slice(1)].filter((letter) => letters.includes(letter));
|
|
43343
|
+
}).at(-1);
|
|
43344
|
+
}
|
|
43171
43345
|
function unwrap2(argv, st, ctx, site) {
|
|
43172
43346
|
const alts = [];
|
|
43173
43347
|
let list = argv;
|
|
@@ -43251,10 +43425,10 @@ function unwrap2(argv, st, ctx, site) {
|
|
|
43251
43425
|
i += 1;
|
|
43252
43426
|
} else if (text === "-C" || text === "--chdir") {
|
|
43253
43427
|
const dir = literalText(rest[i + 1]?.exp);
|
|
43254
|
-
cwd = dir === undefined
|
|
43428
|
+
cwd = dir === undefined ? undefined : kernelPath(state.cwd, dir);
|
|
43255
43429
|
i += 1;
|
|
43256
43430
|
} else if (text?.startsWith("--chdir=")) {
|
|
43257
|
-
cwd =
|
|
43431
|
+
cwd = kernelPath(state.cwd, text.slice(8));
|
|
43258
43432
|
} else if (text === "-S" || text === "--split-string") {
|
|
43259
43433
|
const split = literalText(rest[i + 1]?.exp);
|
|
43260
43434
|
const tail = rest.slice(i + 2);
|
|
@@ -43421,10 +43595,25 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43421
43595
|
outer.output = rest.length === 1 && rest[0] !== undefined ? [...outer.output, rest[0].exp] : undefined;
|
|
43422
43596
|
}
|
|
43423
43597
|
return;
|
|
43424
|
-
case "cd":
|
|
43598
|
+
case "cd": {
|
|
43599
|
+
const found = operands(rest, "");
|
|
43600
|
+
if (found.options.some((option) => !/^-[LPe]+$/.test(option))) {
|
|
43601
|
+
outer.cwd = undefined;
|
|
43602
|
+
outer.cwdWhy = "`cd` with an option the guard does not model";
|
|
43603
|
+
return;
|
|
43604
|
+
}
|
|
43605
|
+
changeDirectory(found.operands[0], lastModeFlag(found.options, "LP") === "P", outer, ctx);
|
|
43606
|
+
return;
|
|
43607
|
+
}
|
|
43425
43608
|
case "pushd": {
|
|
43426
|
-
const
|
|
43427
|
-
|
|
43609
|
+
const found = operands(rest, "");
|
|
43610
|
+
const operand = literalText(found.operands[0]?.exp);
|
|
43611
|
+
if (found.options.length > 0 || found.operands[0] === undefined || operand?.startsWith("+") === true) {
|
|
43612
|
+
outer.cwd = undefined;
|
|
43613
|
+
outer.cwdWhy = "`pushd` with a directory stack or option the guard does not model";
|
|
43614
|
+
return;
|
|
43615
|
+
}
|
|
43616
|
+
changeDirectory(found.operands[0], false, outer, ctx);
|
|
43428
43617
|
return;
|
|
43429
43618
|
}
|
|
43430
43619
|
case "popd":
|
|
@@ -43624,24 +43813,78 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43624
43813
|
return;
|
|
43625
43814
|
case "rm":
|
|
43626
43815
|
case "unlink":
|
|
43627
|
-
checkTargets(base, "delete", operands(rest, "").operands,
|
|
43816
|
+
checkTargets(base, "delete", operands(rest, "").operands, ACTS_ON_LINK, st, ctx, site);
|
|
43628
43817
|
return;
|
|
43629
43818
|
case "mv": {
|
|
43630
|
-
const found =
|
|
43631
|
-
|
|
43632
|
-
|
|
43633
|
-
|
|
43634
|
-
|
|
43635
|
-
|
|
43636
|
-
|
|
43637
|
-
|
|
43819
|
+
const found = writeArguments(rest, "tS", MV_OPTIONS);
|
|
43820
|
+
if (found.flags.has("--help") || found.flags.has("--version"))
|
|
43821
|
+
return;
|
|
43822
|
+
const targets = [...found.operands, ...writeDestinations(found, site)];
|
|
43823
|
+
checkTargets("mv", "move", targets, ACTS_ON_LINK, st, ctx, site);
|
|
43824
|
+
return;
|
|
43825
|
+
}
|
|
43826
|
+
case "cp": {
|
|
43827
|
+
const found = writeArguments(rest, "tS", CP_OPTIONS);
|
|
43828
|
+
if (found.flags.has("--help") || found.flags.has("--version"))
|
|
43829
|
+
return;
|
|
43830
|
+
const unlinks = found.flags.has("b") || found.flags.has("--backup") || found.flags.has("--remove-destination") || found.uncertain;
|
|
43831
|
+
const destinations = writeDestinations(found, site);
|
|
43832
|
+
checkTargets("cp", "overwrite", destinations, unlinks ? WRITES_EITHER : WRITES_THROUGH_LINK, st, ctx, site);
|
|
43833
|
+
checkCopyContents(found, destinations, st, ctx, site);
|
|
43834
|
+
return;
|
|
43835
|
+
}
|
|
43836
|
+
case "install": {
|
|
43837
|
+
const found = writeArguments(rest, "mogtS", INSTALL_OPTIONS);
|
|
43838
|
+
if (found.flags.has("--help") || found.flags.has("--version"))
|
|
43839
|
+
return;
|
|
43840
|
+
const directories = found.flags.has("d") || found.flags.has("--directory");
|
|
43841
|
+
checkTargets("install", directories ? "create or change the mode of" : "overwrite", directories ? found.operands : writeDestinations(found, site), WRITES_EITHER, st, ctx, site);
|
|
43842
|
+
return;
|
|
43843
|
+
}
|
|
43844
|
+
case "dd": {
|
|
43845
|
+
const targets = [];
|
|
43638
43846
|
for (const arg of rest) {
|
|
43639
|
-
const text =
|
|
43640
|
-
if (text
|
|
43641
|
-
|
|
43847
|
+
const { text, whole } = readableWord(arg);
|
|
43848
|
+
if (whole && longOption(text, ["--help", "--version"]) !== undefined)
|
|
43849
|
+
return;
|
|
43850
|
+
if (text.startsWith("of=")) {
|
|
43851
|
+
const carried = valueInWord(arg, 3);
|
|
43852
|
+
targets.push({
|
|
43853
|
+
text: arg.text,
|
|
43854
|
+
exp: carried ?? [unknown2("an `of=` operand the guard cannot read")]
|
|
43855
|
+
});
|
|
43856
|
+
continue;
|
|
43642
43857
|
}
|
|
43858
|
+
if (!whole && "of=".startsWith(text)) {
|
|
43859
|
+
targets.push({
|
|
43860
|
+
text: arg.text,
|
|
43861
|
+
exp: [unknown2("a `dd` operand the guard cannot read, which may name its `of=` file")]
|
|
43862
|
+
});
|
|
43863
|
+
}
|
|
43864
|
+
}
|
|
43865
|
+
checkTargets("dd", "write", targets, WRITES_THROUGH_LINK, st, ctx, site);
|
|
43866
|
+
return;
|
|
43867
|
+
}
|
|
43868
|
+
case "ln": {
|
|
43869
|
+
const found = writeArguments(rest, "St", LN_OPTIONS);
|
|
43870
|
+
if (found.flags.has("--help") || found.flags.has("--version"))
|
|
43871
|
+
return;
|
|
43872
|
+
const sole = found.operands[0];
|
|
43873
|
+
const single = found.operands.length === 1 && found.directory === undefined && !found.uncertain && sole !== undefined && readableWord(sole).whole;
|
|
43874
|
+
const destinations = single ? [{ text: ".", exp: [literal2(".")] }] : writeDestinations(found, site);
|
|
43875
|
+
checkTargets("ln", "overwrite", destinations, WRITES_EITHER, st, ctx, site);
|
|
43876
|
+
const symbolic = found.flags.has("s") || found.flags.has("--symbolic");
|
|
43877
|
+
if (!symbolic) {
|
|
43878
|
+
const sources = found.operands.filter((arg) => !destinations.includes(arg));
|
|
43879
|
+
checkTargets("ln", "hard-link", sources, LINKS_TO, st, ctx, site);
|
|
43643
43880
|
}
|
|
43644
|
-
|
|
43881
|
+
return;
|
|
43882
|
+
}
|
|
43883
|
+
case "sed": {
|
|
43884
|
+
const writes = sedWrites(rest);
|
|
43885
|
+
const program = writes.inferred ? "sed, whose option word the guard cannot read and which may be `-i`," : "sed -i";
|
|
43886
|
+
checkTargets(program, "rewrite in place", writes.replaced, WRITES_LINK, st, ctx, site);
|
|
43887
|
+
checkTargets(program, "rewrite in place", writes.followed, WRITES_EITHER, st, ctx, site);
|
|
43645
43888
|
return;
|
|
43646
43889
|
}
|
|
43647
43890
|
case "rsync": {
|
|
@@ -43663,9 +43906,9 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43663
43906
|
"--remote-option",
|
|
43664
43907
|
"--rsync-path",
|
|
43665
43908
|
"--temp-dir"
|
|
43666
|
-
]).operands.at(-1);
|
|
43909
|
+
]).operands.filter(passesAnArgument).at(-1);
|
|
43667
43910
|
if (destination !== undefined) {
|
|
43668
|
-
checkTargets("rsync", "synchronize and delete into", [destination],
|
|
43911
|
+
checkTargets("rsync", "synchronize and delete into", [destination], ACTS_THROUGH_LINK, st, ctx, site);
|
|
43669
43912
|
}
|
|
43670
43913
|
return;
|
|
43671
43914
|
}
|
|
@@ -43697,14 +43940,14 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43697
43940
|
targetDirectories.push(target);
|
|
43698
43941
|
}
|
|
43699
43942
|
}
|
|
43700
|
-
checkTargets(base, "extract and overwrite in", targetDirectories,
|
|
43943
|
+
checkTargets(base, "extract and overwrite in", targetDirectories, ACTS_THROUGH_LINK, st, ctx, site);
|
|
43701
43944
|
return;
|
|
43702
43945
|
}
|
|
43703
43946
|
case "shred":
|
|
43704
|
-
checkTargets("shred", "shred", operands(rest, "ns", ["--iterations", "--size", "--random-source"]).operands,
|
|
43947
|
+
checkTargets("shred", "shred", operands(rest, "ns", ["--iterations", "--size", "--random-source"]).operands, ACTS_ON_BOTH, st, ctx, site);
|
|
43705
43948
|
return;
|
|
43706
43949
|
case "truncate":
|
|
43707
|
-
checkTargets("truncate", "truncate", operands(rest, "sr", ["--size", "--reference"]).operands,
|
|
43950
|
+
checkTargets("truncate", "truncate", operands(rest, "sr", ["--size", "--reference"]).operands, ACTS_THROUGH_LINK, st, ctx, site);
|
|
43708
43951
|
return;
|
|
43709
43952
|
case "chmod":
|
|
43710
43953
|
case "chown":
|
|
@@ -43722,16 +43965,18 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43722
43965
|
if (!append) {
|
|
43723
43966
|
const verdict = judgePath(arg.exp, st, ctx, { follow: true, overwrite: true });
|
|
43724
43967
|
if (!verdict.ok) {
|
|
43725
|
-
throw refusal(site, `tee would overwrite \`${arg.text}\` (${verdict.resolution})`, ctx);
|
|
43968
|
+
throw refusal(site, `tee would overwrite \`${arg.text}\` (${verdict.resolution})`, ctx, verdict.unresolved);
|
|
43726
43969
|
}
|
|
43727
43970
|
}
|
|
43728
43971
|
const target = literalText(arg.exp);
|
|
43729
|
-
|
|
43730
|
-
|
|
43731
|
-
st.
|
|
43732
|
-
|
|
43733
|
-
modelWrite(st, ctx, file2, append ? UNREAD_FILE : text === null ? UNRENDERED_WRITE : readable(text) ?? OVERLONG_WRITE);
|
|
43972
|
+
const file2 = target === undefined ? undefined : kernelPath(st.cwd, target);
|
|
43973
|
+
if (file2 === undefined) {
|
|
43974
|
+
forgetFileModels(st, ctx, `\`tee ${arg.text}\``);
|
|
43975
|
+
continue;
|
|
43734
43976
|
}
|
|
43977
|
+
st.pidFiles.delete(file2);
|
|
43978
|
+
const text = heredoc === undefined ? null : heredocText(heredoc);
|
|
43979
|
+
modelWrite(st, ctx, file2, append ? UNREAD_FILE : text === null ? UNRENDERED_WRITE : readable(text) ?? OVERLONG_WRITE);
|
|
43735
43980
|
}
|
|
43736
43981
|
return;
|
|
43737
43982
|
}
|
|
@@ -43778,8 +44023,8 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43778
44023
|
return false;
|
|
43779
44024
|
const { text, whole } = readableWord(word);
|
|
43780
44025
|
const compact = text.replace("-", "");
|
|
43781
|
-
const
|
|
43782
|
-
return whole ?
|
|
44026
|
+
const normalized = commands.map((command) => command.replace("-", ""));
|
|
44027
|
+
return whole ? completeName(compact, normalized) !== undefined : normalized.some((command) => command.startsWith(compact));
|
|
43783
44028
|
};
|
|
43784
44029
|
let killing;
|
|
43785
44030
|
for (const segment of segments) {
|
|
@@ -43856,7 +44101,7 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43856
44101
|
runFile(argv[0], rest, childState(st, invocation.overlay), ctx, site, "auto");
|
|
43857
44102
|
}
|
|
43858
44103
|
}
|
|
43859
|
-
function changeDirectory(target, st, ctx) {
|
|
44104
|
+
function changeDirectory(target, physicalMode, st, ctx) {
|
|
43860
44105
|
if (target === undefined) {
|
|
43861
44106
|
const home = literalText(lookup("HOME", st, ctx));
|
|
43862
44107
|
st.cwd = home;
|
|
@@ -43869,7 +44114,13 @@ function changeDirectory(target, st, ctx) {
|
|
|
43869
44114
|
st.cwdWhy = `\`cd ${target.text}\``;
|
|
43870
44115
|
return;
|
|
43871
44116
|
}
|
|
43872
|
-
|
|
44117
|
+
if (!physicalMode) {
|
|
44118
|
+
st.cwd = path4.resolve(st.cwd, text);
|
|
44119
|
+
return;
|
|
44120
|
+
}
|
|
44121
|
+
st.cwd = kernelPath(st.cwd, text);
|
|
44122
|
+
if (st.cwd === undefined)
|
|
44123
|
+
st.cwdWhy = `\`cd -P ${target.text}\``;
|
|
43873
44124
|
}
|
|
43874
44125
|
function declaration(builtin, list, st, ctx, site) {
|
|
43875
44126
|
const options = list.map((arg) => literalText(arg.exp) ?? "").filter((text) => /^[-+][a-zA-Z]+$/.test(text));
|
|
@@ -44023,17 +44274,389 @@ function nameAndValue(arg) {
|
|
|
44023
44274
|
}
|
|
44024
44275
|
return { name, value: undefined };
|
|
44025
44276
|
}
|
|
44026
|
-
|
|
44277
|
+
var ACTS_ON_LINK = {
|
|
44278
|
+
followed: false,
|
|
44279
|
+
unfollowed: true,
|
|
44280
|
+
overwrite: false
|
|
44281
|
+
};
|
|
44282
|
+
var ACTS_THROUGH_LINK = {
|
|
44283
|
+
followed: true,
|
|
44284
|
+
unfollowed: false,
|
|
44285
|
+
overwrite: false
|
|
44286
|
+
};
|
|
44287
|
+
var ACTS_ON_BOTH = {
|
|
44288
|
+
followed: true,
|
|
44289
|
+
unfollowed: true,
|
|
44290
|
+
overwrite: false
|
|
44291
|
+
};
|
|
44292
|
+
var WRITES_THROUGH_LINK = {
|
|
44293
|
+
followed: true,
|
|
44294
|
+
unfollowed: false,
|
|
44295
|
+
overwrite: true
|
|
44296
|
+
};
|
|
44297
|
+
var WRITES_LINK = {
|
|
44298
|
+
followed: false,
|
|
44299
|
+
unfollowed: true,
|
|
44300
|
+
overwrite: true
|
|
44301
|
+
};
|
|
44302
|
+
var WRITES_EITHER = {
|
|
44303
|
+
followed: true,
|
|
44304
|
+
unfollowed: true,
|
|
44305
|
+
overwrite: true
|
|
44306
|
+
};
|
|
44307
|
+
var LINKS_TO = {
|
|
44308
|
+
followed: true,
|
|
44309
|
+
unfollowed: false,
|
|
44310
|
+
overwrite: true
|
|
44311
|
+
};
|
|
44312
|
+
function checkTargets(program, verb, targets, reach, st, ctx, site) {
|
|
44027
44313
|
for (const target of targets) {
|
|
44028
|
-
|
|
44029
|
-
|
|
44030
|
-
|
|
44314
|
+
for (const follow of [true, false]) {
|
|
44315
|
+
if (!(follow ? reach.followed : reach.unfollowed))
|
|
44316
|
+
continue;
|
|
44317
|
+
const verdict = judgePath(target.exp, st, ctx, { follow, overwrite: reach.overwrite });
|
|
44318
|
+
if (!verdict.ok) {
|
|
44319
|
+
throw refusal(site, `${program} would ${verb} \`${target.text}\` (${verdict.resolution})`, ctx, verdict.unresolved);
|
|
44320
|
+
}
|
|
44031
44321
|
}
|
|
44032
44322
|
const text = literalText(target.exp);
|
|
44033
|
-
|
|
44034
|
-
|
|
44323
|
+
const file2 = text === undefined ? undefined : kernelPath(st.cwd, text);
|
|
44324
|
+
if (file2 !== undefined)
|
|
44325
|
+
st.pidFiles.delete(file2);
|
|
44326
|
+
}
|
|
44327
|
+
}
|
|
44328
|
+
function completeName(prefix, names) {
|
|
44329
|
+
if (names.includes(prefix))
|
|
44330
|
+
return prefix;
|
|
44331
|
+
const reachable = names.filter((name) => name.startsWith(prefix));
|
|
44332
|
+
return reachable.length === 1 ? reachable[0] : undefined;
|
|
44333
|
+
}
|
|
44334
|
+
function longOption(text, names) {
|
|
44335
|
+
return completeName(text.split("=", 1)[0], names);
|
|
44336
|
+
}
|
|
44337
|
+
var GNU_INFORMATION = { "--help": false, "--version": false };
|
|
44338
|
+
var MV_OPTIONS = {
|
|
44339
|
+
"--backup": false,
|
|
44340
|
+
"--context": false,
|
|
44341
|
+
"--debug": false,
|
|
44342
|
+
"--force": false,
|
|
44343
|
+
"--interactive": false,
|
|
44344
|
+
"--no-clobber": false,
|
|
44345
|
+
"--no-copy": false,
|
|
44346
|
+
"--no-target-directory": false,
|
|
44347
|
+
"--strip-trailing-slashes": false,
|
|
44348
|
+
"--suffix": true,
|
|
44349
|
+
"--target-directory": true,
|
|
44350
|
+
"--update": false,
|
|
44351
|
+
"--verbose": false,
|
|
44352
|
+
...GNU_INFORMATION
|
|
44353
|
+
};
|
|
44354
|
+
var CP_OPTIONS = {
|
|
44355
|
+
"--archive": false,
|
|
44356
|
+
"--attributes-only": false,
|
|
44357
|
+
"--backup": false,
|
|
44358
|
+
"--context": false,
|
|
44359
|
+
"--copy-contents": false,
|
|
44360
|
+
"--debug": false,
|
|
44361
|
+
"--dereference": false,
|
|
44362
|
+
"--force": false,
|
|
44363
|
+
"--interactive": false,
|
|
44364
|
+
"--link": false,
|
|
44365
|
+
"--no-clobber": false,
|
|
44366
|
+
"--no-dereference": false,
|
|
44367
|
+
"--no-preserve": true,
|
|
44368
|
+
"--no-target-directory": false,
|
|
44369
|
+
"--one-file-system": false,
|
|
44370
|
+
"--parents": false,
|
|
44371
|
+
"--preserve": false,
|
|
44372
|
+
"--recursive": false,
|
|
44373
|
+
"--reflink": false,
|
|
44374
|
+
"--remove-destination": false,
|
|
44375
|
+
"--sparse": true,
|
|
44376
|
+
"--strip-trailing-slashes": false,
|
|
44377
|
+
"--suffix": true,
|
|
44378
|
+
"--symbolic-link": false,
|
|
44379
|
+
"--target-directory": true,
|
|
44380
|
+
"--update": false,
|
|
44381
|
+
"--verbose": false,
|
|
44382
|
+
...GNU_INFORMATION
|
|
44383
|
+
};
|
|
44384
|
+
var INSTALL_OPTIONS = {
|
|
44385
|
+
"--backup": false,
|
|
44386
|
+
"--compare": false,
|
|
44387
|
+
"--context": false,
|
|
44388
|
+
"--debug": false,
|
|
44389
|
+
"--directory": false,
|
|
44390
|
+
"--group": true,
|
|
44391
|
+
"--mode": true,
|
|
44392
|
+
"--no-target-directory": false,
|
|
44393
|
+
"--owner": true,
|
|
44394
|
+
"--preserve-context": false,
|
|
44395
|
+
"--preserve-timestamps": false,
|
|
44396
|
+
"--strip": false,
|
|
44397
|
+
"--strip-program": true,
|
|
44398
|
+
"--suffix": true,
|
|
44399
|
+
"--target-directory": true,
|
|
44400
|
+
"--verbose": false,
|
|
44401
|
+
...GNU_INFORMATION
|
|
44402
|
+
};
|
|
44403
|
+
var LN_OPTIONS = {
|
|
44404
|
+
"--backup": false,
|
|
44405
|
+
"--directory": false,
|
|
44406
|
+
"--force": false,
|
|
44407
|
+
"--interactive": false,
|
|
44408
|
+
"--logical": false,
|
|
44409
|
+
"--no-dereference": false,
|
|
44410
|
+
"--no-target-directory": false,
|
|
44411
|
+
"--physical": false,
|
|
44412
|
+
"--relative": false,
|
|
44413
|
+
"--suffix": true,
|
|
44414
|
+
"--symbolic": false,
|
|
44415
|
+
"--target-directory": true,
|
|
44416
|
+
"--verbose": false,
|
|
44417
|
+
...GNU_INFORMATION
|
|
44418
|
+
};
|
|
44419
|
+
function passesAnArgument(arg) {
|
|
44420
|
+
return arg.fields !== "none";
|
|
44421
|
+
}
|
|
44422
|
+
function writeArguments(rest, valued, long) {
|
|
44423
|
+
const names = Object.keys(long);
|
|
44424
|
+
const operands2 = [];
|
|
44425
|
+
const flags = new Set;
|
|
44426
|
+
let directory;
|
|
44427
|
+
let uncertain;
|
|
44428
|
+
for (let i = 0;i < rest.length; i += 1) {
|
|
44429
|
+
const arg = rest[i];
|
|
44430
|
+
const { text, whole } = readableWord(arg);
|
|
44431
|
+
if (whole && text === "--") {
|
|
44432
|
+
operands2.push(...rest.slice(i + 1).filter(passesAnArgument));
|
|
44433
|
+
break;
|
|
44434
|
+
}
|
|
44435
|
+
if (!whole || !text.startsWith("-") || text === "-") {
|
|
44436
|
+
if (!whole && mayBeSomeOption(text))
|
|
44437
|
+
uncertain = arg;
|
|
44438
|
+
if (passesAnArgument(arg))
|
|
44439
|
+
operands2.push(arg);
|
|
44440
|
+
continue;
|
|
44035
44441
|
}
|
|
44442
|
+
let takesValue = false;
|
|
44443
|
+
let offset;
|
|
44444
|
+
if (text.startsWith("--")) {
|
|
44445
|
+
const option = longOption(text, names);
|
|
44446
|
+
if (option === undefined)
|
|
44447
|
+
continue;
|
|
44448
|
+
flags.add(option);
|
|
44449
|
+
takesValue = long[option] === true && !text.includes("=");
|
|
44450
|
+
if (option === "--target-directory")
|
|
44451
|
+
offset = text.indexOf("=") + 1 || text.length;
|
|
44452
|
+
} else {
|
|
44453
|
+
for (let index = 1;index < text.length; index += 1) {
|
|
44454
|
+
const letter = text.charAt(index);
|
|
44455
|
+
flags.add(letter);
|
|
44456
|
+
if (!valued.includes(letter))
|
|
44457
|
+
continue;
|
|
44458
|
+
takesValue = index === text.length - 1;
|
|
44459
|
+
if (letter === "t")
|
|
44460
|
+
offset = index + 1;
|
|
44461
|
+
break;
|
|
44462
|
+
}
|
|
44463
|
+
}
|
|
44464
|
+
const next = takesValue ? rest[i + 1] : undefined;
|
|
44465
|
+
if (takesValue)
|
|
44466
|
+
i += 1;
|
|
44467
|
+
if (offset === undefined)
|
|
44468
|
+
continue;
|
|
44469
|
+
const value = valueInWord(arg, offset);
|
|
44470
|
+
directory = value === undefined ? next : { text: arg.text, exp: value };
|
|
44471
|
+
}
|
|
44472
|
+
return { operands: operands2, flags, directory, uncertain };
|
|
44473
|
+
}
|
|
44474
|
+
function writeDestinations(found, site) {
|
|
44475
|
+
if (found.operands.length > 1 && (found.flags.has("T") || found.flags.has("--no-target-directory"))) {
|
|
44476
|
+
return [found.operands.at(-1)];
|
|
44036
44477
|
}
|
|
44478
|
+
if (found.directory !== undefined && !found.uncertain)
|
|
44479
|
+
return [found.directory];
|
|
44480
|
+
if (found.operands.length < 2 || found.uncertain) {
|
|
44481
|
+
const word = found.uncertain?.text ?? found.operands.at(-1)?.text ?? "missing destination";
|
|
44482
|
+
throw new Refusal(site.snippet, site.line, `the guard cannot read a write destination from \`${word}\`; put \`--\` before operands, or use \`-T\` to name the last operand as the destination`);
|
|
44483
|
+
}
|
|
44484
|
+
return [found.operands.at(-1)];
|
|
44485
|
+
}
|
|
44486
|
+
function checkCopyContents(found, destinations, st, ctx, site) {
|
|
44487
|
+
const recursive = found.uncertain || ["r", "R", "a", "--recursive", "--archive"].some((flag) => found.flags.has(flag));
|
|
44488
|
+
const direct = found.flags.has("T") || found.flags.has("--no-target-directory");
|
|
44489
|
+
for (const destination of destinations) {
|
|
44490
|
+
const text = literalText(destination.exp);
|
|
44491
|
+
if (text === undefined || !path4.isAbsolute(text) && st.cwd === undefined)
|
|
44492
|
+
continue;
|
|
44493
|
+
const dir = realExisting(path4.resolve(st.cwd ?? "/", text));
|
|
44494
|
+
if (!statSync(dir, { throwIfNoEntry: false })?.isDirectory())
|
|
44495
|
+
continue;
|
|
44496
|
+
for (const source of found.operands) {
|
|
44497
|
+
if (destinations.includes(source))
|
|
44498
|
+
continue;
|
|
44499
|
+
const name = literalText(source.exp);
|
|
44500
|
+
const from = name === undefined ? undefined : path4.resolve(st.cwd ?? "/", name);
|
|
44501
|
+
const knownTree = from !== undefined && statSync(from, { throwIfNoEntry: false })?.isDirectory();
|
|
44502
|
+
const root = direct || name === undefined ? dir : path4.join(dir, found.flags.has("--parents") ? name : path4.basename(name));
|
|
44503
|
+
const pending = [root];
|
|
44504
|
+
while (pending.length > 0) {
|
|
44505
|
+
const target = pending.pop();
|
|
44506
|
+
checkTargets("cp", "overwrite", [{ text: name === undefined ? source.text : target, exp: [literal2(target)] }], WRITES_THROUGH_LINK, st, ctx, site);
|
|
44507
|
+
if (!recursive && !(name === undefined && target === root))
|
|
44508
|
+
continue;
|
|
44509
|
+
if (!lstatSync(target, { throwIfNoEntry: false })?.isDirectory())
|
|
44510
|
+
continue;
|
|
44511
|
+
const entries = opendirSync(target);
|
|
44512
|
+
try {
|
|
44513
|
+
for (let entry = entries.readSync();entry !== null; entry = entries.readSync()) {
|
|
44514
|
+
if (++ctx.steps.count > MAX_WALK_STEPS) {
|
|
44515
|
+
throw new Refusal(site.snippet, site.line, WALK_LIMIT);
|
|
44516
|
+
}
|
|
44517
|
+
const child = path4.join(target, entry.name);
|
|
44518
|
+
if (knownTree && !existsSync(path4.join(from, path4.relative(root, child))))
|
|
44519
|
+
continue;
|
|
44520
|
+
pending.push(child);
|
|
44521
|
+
}
|
|
44522
|
+
} finally {
|
|
44523
|
+
entries.closeSync();
|
|
44524
|
+
}
|
|
44525
|
+
}
|
|
44526
|
+
}
|
|
44527
|
+
}
|
|
44528
|
+
}
|
|
44529
|
+
var SED_OPTIONS = {
|
|
44530
|
+
"--debug": false,
|
|
44531
|
+
"--expression": true,
|
|
44532
|
+
"--file": true,
|
|
44533
|
+
"--follow-symlinks": false,
|
|
44534
|
+
"--in-place": false,
|
|
44535
|
+
"--line-length": true,
|
|
44536
|
+
"--null-data": false,
|
|
44537
|
+
"--posix": false,
|
|
44538
|
+
"--quiet": false,
|
|
44539
|
+
"--regexp-extended": false,
|
|
44540
|
+
"--sandbox": false,
|
|
44541
|
+
"--separate": false,
|
|
44542
|
+
"--silent": false,
|
|
44543
|
+
"--unbuffered": false,
|
|
44544
|
+
...GNU_INFORMATION
|
|
44545
|
+
};
|
|
44546
|
+
function sedWrites(list) {
|
|
44547
|
+
const names = Object.keys(SED_OPTIONS);
|
|
44548
|
+
let inPlace = false;
|
|
44549
|
+
let follow = false;
|
|
44550
|
+
let expression = false;
|
|
44551
|
+
let splitOption = false;
|
|
44552
|
+
let ended = false;
|
|
44553
|
+
let reopened = false;
|
|
44554
|
+
let takesNext = false;
|
|
44555
|
+
const unread = [];
|
|
44556
|
+
const operands2 = [];
|
|
44557
|
+
for (let i = 0;i < list.length; i += 1) {
|
|
44558
|
+
const arg = list[i];
|
|
44559
|
+
const { text, whole } = readableWord(arg);
|
|
44560
|
+
const afterUnread = takesNext;
|
|
44561
|
+
takesNext = false;
|
|
44562
|
+
const several = arg.fields === "unknown";
|
|
44563
|
+
if (ended) {
|
|
44564
|
+
operands2.push({ arg, option: false, several });
|
|
44565
|
+
continue;
|
|
44566
|
+
}
|
|
44567
|
+
if (whole && text === "--") {
|
|
44568
|
+
if (reopened)
|
|
44569
|
+
operands2.push({ arg, option: false, several: false });
|
|
44570
|
+
if (afterUnread)
|
|
44571
|
+
reopened = true;
|
|
44572
|
+
else
|
|
44573
|
+
ended = true;
|
|
44574
|
+
continue;
|
|
44575
|
+
}
|
|
44576
|
+
if (several) {
|
|
44577
|
+
splitOption = true;
|
|
44578
|
+
takesNext = true;
|
|
44579
|
+
operands2.push({ arg, option: true, several });
|
|
44580
|
+
continue;
|
|
44581
|
+
}
|
|
44582
|
+
if (!text.startsWith("-") || whole && text === "-") {
|
|
44583
|
+
const option = !whole && text === "";
|
|
44584
|
+
if (option) {
|
|
44585
|
+
unread.push(arg);
|
|
44586
|
+
takesNext = true;
|
|
44587
|
+
}
|
|
44588
|
+
operands2.push({ arg, option, several });
|
|
44589
|
+
continue;
|
|
44590
|
+
}
|
|
44591
|
+
if (reopened)
|
|
44592
|
+
operands2.push({ arg, option: false, several });
|
|
44593
|
+
if (text.startsWith("--")) {
|
|
44594
|
+
const option = longOption(text, names);
|
|
44595
|
+
if (whole && !reopened && (option === "--help" || option === "--version")) {
|
|
44596
|
+
return { replaced: [], followed: [], inPlace: false, inferred: false };
|
|
44597
|
+
}
|
|
44598
|
+
if (option === "--in-place")
|
|
44599
|
+
inPlace = true;
|
|
44600
|
+
if (option === "--follow-symlinks")
|
|
44601
|
+
follow = true;
|
|
44602
|
+
if (option === "--expression" || option === "--file")
|
|
44603
|
+
expression = true;
|
|
44604
|
+
if (option === undefined && !whole)
|
|
44605
|
+
unread.push(arg);
|
|
44606
|
+
if ((option === undefined ? !whole : SED_OPTIONS[option]) && !text.includes("=")) {
|
|
44607
|
+
if (whole && !reopened)
|
|
44608
|
+
i += 1;
|
|
44609
|
+
else
|
|
44610
|
+
takesNext = true;
|
|
44611
|
+
}
|
|
44612
|
+
continue;
|
|
44613
|
+
}
|
|
44614
|
+
let settled = false;
|
|
44615
|
+
for (const [offset, letter] of [...text.slice(1)].entries()) {
|
|
44616
|
+
if (letter === "i") {
|
|
44617
|
+
inPlace = true;
|
|
44618
|
+
settled = true;
|
|
44619
|
+
break;
|
|
44620
|
+
}
|
|
44621
|
+
if ("efl".includes(letter)) {
|
|
44622
|
+
if (letter !== "l")
|
|
44623
|
+
expression = true;
|
|
44624
|
+
if (offset === text.length - 2) {
|
|
44625
|
+
if (whole && !reopened)
|
|
44626
|
+
i += 1;
|
|
44627
|
+
else
|
|
44628
|
+
takesNext = true;
|
|
44629
|
+
}
|
|
44630
|
+
settled = true;
|
|
44631
|
+
break;
|
|
44632
|
+
}
|
|
44633
|
+
}
|
|
44634
|
+
if (!settled && !whole) {
|
|
44635
|
+
unread.push(arg);
|
|
44636
|
+
takesNext = true;
|
|
44637
|
+
}
|
|
44638
|
+
}
|
|
44639
|
+
const unsettled = (settled) => settled ? 0 : 1;
|
|
44640
|
+
const replaced = [];
|
|
44641
|
+
const followed = [];
|
|
44642
|
+
let before = false;
|
|
44643
|
+
for (const operand of operands2) {
|
|
44644
|
+
const maybe = operand.option && !operand.several;
|
|
44645
|
+
const others = unread.length - (maybe ? 1 : 0);
|
|
44646
|
+
const needed = unsettled(inPlace || splitOption) + unsettled(expression || splitOption || operand.several || before);
|
|
44647
|
+
if (others >= needed + unsettled(follow || splitOption))
|
|
44648
|
+
followed.push(operand.arg);
|
|
44649
|
+
else if (others >= needed)
|
|
44650
|
+
replaced.push(operand.arg);
|
|
44651
|
+
if (!maybe)
|
|
44652
|
+
before = true;
|
|
44653
|
+
}
|
|
44654
|
+
return {
|
|
44655
|
+
replaced,
|
|
44656
|
+
followed,
|
|
44657
|
+
inPlace: inPlace || splitOption || unread.length > 0,
|
|
44658
|
+
inferred: !inPlace
|
|
44659
|
+
};
|
|
44037
44660
|
}
|
|
44038
44661
|
function checkRecursiveMode(program, list, st, ctx, site) {
|
|
44039
44662
|
const flags = program === "chmod" ? "cfvR" : "cfvRhHLP";
|
|
@@ -44063,7 +44686,7 @@ function checkRecursiveMode(program, list, st, ctx, site) {
|
|
|
44063
44686
|
}
|
|
44064
44687
|
const verb = program === "chmod" ? "recursively change the mode of" : "recursively change the owner of";
|
|
44065
44688
|
if (recursive) {
|
|
44066
|
-
checkTargets(`${program} -R`, verb, reference ? found : found.slice(1),
|
|
44689
|
+
checkTargets(`${program} -R`, verb, reference ? found : found.slice(1), ACTS_THROUGH_LINK, st, ctx, site);
|
|
44067
44690
|
return;
|
|
44068
44691
|
}
|
|
44069
44692
|
for (const [index, arg] of found.entries()) {
|
|
@@ -44073,7 +44696,7 @@ function checkRecursiveMode(program, list, st, ctx, site) {
|
|
|
44073
44696
|
const targets = reference || arg.fields === "unknown" ? others : others.slice(1);
|
|
44074
44697
|
if (targets.length === 0)
|
|
44075
44698
|
continue;
|
|
44076
|
-
checkTargets(`${program} -R`, verb, targets,
|
|
44699
|
+
checkTargets(`${program} -R`, verb, targets, ACTS_THROUGH_LINK, st, ctx, site);
|
|
44077
44700
|
}
|
|
44078
44701
|
}
|
|
44079
44702
|
function checkFind(list, st, ctx, site) {
|
|
@@ -44119,8 +44742,9 @@ function checkFind(list, st, ctx, site) {
|
|
|
44119
44742
|
continue;
|
|
44120
44743
|
const inner = unwrap2(list.slice(i + index + 1, i + end), st, ctx, site);
|
|
44121
44744
|
const program = path4.basename(literalText(inner.argv[0]?.exp) ?? "");
|
|
44122
|
-
if (FILE_COMMANDS.has(program) || program === "xargs")
|
|
44745
|
+
if (FILE_COMMANDS.has(program) || program === "xargs" || program === "sed" && sedWrites(inner.argv.slice(1)).inPlace) {
|
|
44123
44746
|
action = `find ${word} ${program}`;
|
|
44747
|
+
}
|
|
44124
44748
|
if (SHELLS.has(program)) {
|
|
44125
44749
|
action = `find ${word} ${program}`;
|
|
44126
44750
|
shell = inner.argv;
|
|
@@ -44130,10 +44754,10 @@ function checkFind(list, st, ctx, site) {
|
|
|
44130
44754
|
if (rootsBeforePredicate === undefined)
|
|
44131
44755
|
return;
|
|
44132
44756
|
const under = rootsBeforePredicate.length > 0 ? rootsBeforePredicate : [{ text: ".", exp: [literal2(".")] }];
|
|
44133
|
-
checkTargets("find with a root the guard cannot read, which may be a predicate", "delete or change what it finds under", under,
|
|
44757
|
+
checkTargets("find with a root the guard cannot read, which may be a predicate", "delete or change what it finds under", under, ACTS_ON_LINK, st, ctx, site);
|
|
44134
44758
|
return;
|
|
44135
44759
|
}
|
|
44136
|
-
checkTargets(action, "delete or change what it finds under", targets,
|
|
44760
|
+
checkTargets(action, "delete or change what it finds under", targets, ACTS_ON_LINK, st, ctx, site);
|
|
44137
44761
|
if (shell === undefined)
|
|
44138
44762
|
return;
|
|
44139
44763
|
const found = targets.length === 1 ? targets[0]?.exp ?? [] : [unknown2("a path `find` found")];
|
|
@@ -44171,7 +44795,7 @@ function checkXargs(list, st, ctx, site) {
|
|
|
44171
44795
|
};
|
|
44172
44796
|
});
|
|
44173
44797
|
const program = path4.basename(literalText(argv[0]?.exp) ?? "");
|
|
44174
|
-
if (FILE_COMMANDS.has(program) || SIGNAL_COMMANDS.has(program)) {
|
|
44798
|
+
if (FILE_COMMANDS.has(program) || SIGNAL_COMMANDS.has(program) || program === "sed" && sedWrites(argv.slice(1)).inPlace) {
|
|
44175
44799
|
throw new Refusal(site.snippet, site.line, `\`xargs ${program}\` takes its targets from standard input, which the guard cannot see; name ` + `the paths or pids on the command line (or use \`find <dir> -delete\`) so it can check them`);
|
|
44176
44800
|
}
|
|
44177
44801
|
const overlay = new Map;
|
|
@@ -44454,7 +45078,14 @@ function runFile(file2, positional, st, ctx, site, kind, source = false) {
|
|
|
44454
45078
|
if (text === undefined || !text.startsWith("/") && st.cwd === undefined) {
|
|
44455
45079
|
throw new Refusal(site.snippet, site.line, `the guard cannot resolve the script \`${file2.text}\` (${file2.exp.find((p) => p.kind !== "literal")?.why ?? `a working directory unknown after ${st.cwdWhy}`}), so it cannot read what it runs`);
|
|
44456
45080
|
}
|
|
44457
|
-
|
|
45081
|
+
if (ctx.unnameableWrite.why !== undefined) {
|
|
45082
|
+
throw new Refusal(site.snippet, site.line, `${ctx.unnameableWrite.why} writes a file the guard cannot name, so it cannot tell whether \`${file2.text}\` is the file that was written`);
|
|
45083
|
+
}
|
|
45084
|
+
const resolved = physical(st.cwd ?? "/", text, true);
|
|
45085
|
+
if ("unknown" in resolved) {
|
|
45086
|
+
throw new Refusal(site.snippet, site.line, `the guard cannot resolve the script \`${file2.text}\` (${resolved.unknown}), so it cannot read what it runs`);
|
|
45087
|
+
}
|
|
45088
|
+
const abs = resolved.real;
|
|
44458
45089
|
let content;
|
|
44459
45090
|
if (st.files.has(abs)) {
|
|
44460
45091
|
const written = st.files.get(abs);
|
|
@@ -44582,7 +45213,7 @@ function checkCode(language, code, st, ctx, ipython = false) {
|
|
|
44582
45213
|
overwrite: verb === "overwrite"
|
|
44583
45214
|
});
|
|
44584
45215
|
if (!verdict.ok) {
|
|
44585
|
-
throw refusal({ snippet: call, line: 1 }, `${call} would ${verb} ${verdict.resolution}`, ctx);
|
|
45216
|
+
throw refusal({ snippet: call, line: 1 }, `${call} would ${verb} ${verdict.resolution}`, ctx, verdict.unresolved);
|
|
44586
45217
|
}
|
|
44587
45218
|
},
|
|
44588
45219
|
signal: (call, pid, group) => {
|
|
@@ -44653,6 +45284,7 @@ function createPaneGuard(options) {
|
|
|
44653
45284
|
ompPid: options.ompPid,
|
|
44654
45285
|
parentOf: options.parentOf ?? procParent,
|
|
44655
45286
|
steps: { count: 0 },
|
|
45287
|
+
unnameableWrite: { why: undefined },
|
|
44656
45288
|
uncertain: { depth: 0 }
|
|
44657
45289
|
});
|
|
44658
45290
|
const initial = (cwd, source) => ({
|