@sjawhar/pi-legion-envoy 5.17.2 → 5.17.4
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 +738 -97
- 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.4",
|
|
33477
33477
|
type: "module",
|
|
33478
33478
|
omp: {
|
|
33479
33479
|
extensions: [
|
|
@@ -34776,6 +34776,8 @@ function createGoLegionTool(deps) {
|
|
|
34776
34776
|
import {
|
|
34777
34777
|
closeSync,
|
|
34778
34778
|
existsSync,
|
|
34779
|
+
lstatSync,
|
|
34780
|
+
opendirSync,
|
|
34779
34781
|
openSync,
|
|
34780
34782
|
readFileSync as readFileSync3,
|
|
34781
34783
|
readSync,
|
|
@@ -41435,9 +41437,15 @@ function isUnset(value) {
|
|
|
41435
41437
|
}
|
|
41436
41438
|
var NO_ELEMENTS = { kind: "literal", text: "", noElements: true };
|
|
41437
41439
|
function stored(value) {
|
|
41438
|
-
|
|
41439
|
-
|
|
41440
|
-
|
|
41440
|
+
const counted = value.map((piece) => {
|
|
41441
|
+
if (piece.unknownCount !== true)
|
|
41442
|
+
return piece;
|
|
41443
|
+
const { unknownCount: _, ...rest } = piece;
|
|
41444
|
+
return rest;
|
|
41445
|
+
});
|
|
41446
|
+
if (!counted.some((piece) => piece.noElements === true))
|
|
41447
|
+
return counted;
|
|
41448
|
+
return counted.map((piece) => piece.noElements === true ? literal2("") : piece);
|
|
41441
41449
|
}
|
|
41442
41450
|
var ANY_NAME = "\x00";
|
|
41443
41451
|
var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -41477,7 +41485,7 @@ function wrapRefusal(error48, site, label) {
|
|
|
41477
41485
|
}
|
|
41478
41486
|
var MAX_DEPTH = 8;
|
|
41479
41487
|
var MAX_WALK_STEPS = 1e5;
|
|
41480
|
-
var WALK_LIMIT = `the guard reached its walk limit of ${MAX_WALK_STEPS} nodes; run a smaller script`;
|
|
41488
|
+
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
41489
|
var MAX_ALTERNATIVES = 64;
|
|
41482
41490
|
var MAX_PATTERN_VALUE = 512;
|
|
41483
41491
|
var PATTERN_WORK_PER_STEP = 1024;
|
|
@@ -41500,6 +41508,10 @@ var FILE_COMMANDS = new Set([
|
|
|
41500
41508
|
"rm",
|
|
41501
41509
|
"unlink",
|
|
41502
41510
|
"mv",
|
|
41511
|
+
"cp",
|
|
41512
|
+
"dd",
|
|
41513
|
+
"install",
|
|
41514
|
+
"ln",
|
|
41503
41515
|
"shred",
|
|
41504
41516
|
"truncate",
|
|
41505
41517
|
"chmod",
|
|
@@ -41507,6 +41519,51 @@ var FILE_COMMANDS = new Set([
|
|
|
41507
41519
|
"chgrp"
|
|
41508
41520
|
]);
|
|
41509
41521
|
var SIGNAL_COMMANDS = new Set(["kill", "pkill", "killall", "killall5"]);
|
|
41522
|
+
var FIND_VALUED_PREDICATES = new Set([
|
|
41523
|
+
"-amin",
|
|
41524
|
+
"-anewer",
|
|
41525
|
+
"-atime",
|
|
41526
|
+
"-cmin",
|
|
41527
|
+
"-cnewer",
|
|
41528
|
+
"-ctime",
|
|
41529
|
+
"-fls",
|
|
41530
|
+
"-fprint",
|
|
41531
|
+
"-fprint0",
|
|
41532
|
+
"-fprintf",
|
|
41533
|
+
"-fstype",
|
|
41534
|
+
"-gid",
|
|
41535
|
+
"-group",
|
|
41536
|
+
"-ilname",
|
|
41537
|
+
"-iname",
|
|
41538
|
+
"-inum",
|
|
41539
|
+
"-ipath",
|
|
41540
|
+
"-iregex",
|
|
41541
|
+
"-iwholename",
|
|
41542
|
+
"-links",
|
|
41543
|
+
"-lname",
|
|
41544
|
+
"-maxdepth",
|
|
41545
|
+
"-mindepth",
|
|
41546
|
+
"-mmin",
|
|
41547
|
+
"-mtime",
|
|
41548
|
+
"-name",
|
|
41549
|
+
"-newer",
|
|
41550
|
+
"-newerat",
|
|
41551
|
+
"-newerct",
|
|
41552
|
+
"-newermt",
|
|
41553
|
+
"-path",
|
|
41554
|
+
"-perm",
|
|
41555
|
+
"-printf",
|
|
41556
|
+
"-regex",
|
|
41557
|
+
"-regextype",
|
|
41558
|
+
"-samefile",
|
|
41559
|
+
"-size",
|
|
41560
|
+
"-type",
|
|
41561
|
+
"-uid",
|
|
41562
|
+
"-used",
|
|
41563
|
+
"-user",
|
|
41564
|
+
"-wholename",
|
|
41565
|
+
"-xtype"
|
|
41566
|
+
]);
|
|
41510
41567
|
var FUNCTION_OUTPUT_SILENT = {
|
|
41511
41568
|
":": true,
|
|
41512
41569
|
break: true,
|
|
@@ -41759,8 +41816,10 @@ function resolveArrayIndex(index, st, ctx) {
|
|
|
41759
41816
|
}
|
|
41760
41817
|
function parameter(name, quoted, st, ctx) {
|
|
41761
41818
|
if (name === "@" || name === "*") {
|
|
41762
|
-
if (st.positional === undefined)
|
|
41763
|
-
|
|
41819
|
+
if (st.positional === undefined) {
|
|
41820
|
+
const piece = unknown2(`\`$${name}\` (the positional parameters)`);
|
|
41821
|
+
return [[name === "*" && quoted ? piece : { ...piece, unknownCount: true }]];
|
|
41822
|
+
}
|
|
41764
41823
|
return elements(st.positional, name === "*" && quoted);
|
|
41765
41824
|
}
|
|
41766
41825
|
if (/^[0-9]+$/.test(name)) {
|
|
@@ -41835,11 +41894,14 @@ function parameterExpansion(part, quoted, st, ctx) {
|
|
|
41835
41894
|
if (part.index === "@" || part.index === "*") {
|
|
41836
41895
|
if (array2 !== undefined) {
|
|
41837
41896
|
const unknownElement = array2.get(UNKNOWN_ARRAY_INDEX);
|
|
41838
|
-
if (unknownElement !== undefined)
|
|
41839
|
-
|
|
41897
|
+
if (unknownElement !== undefined) {
|
|
41898
|
+
if (part.index === "*" && quoted)
|
|
41899
|
+
return [[...unknownElement]];
|
|
41900
|
+
return [unknownElement.map((piece) => ({ ...piece, unknownCount: true }))];
|
|
41901
|
+
}
|
|
41840
41902
|
return elements([...array2.values()], part.index === "*" && quoted);
|
|
41841
41903
|
}
|
|
41842
|
-
return parameter(name, quoted, st, ctx);
|
|
41904
|
+
return parameter(name, quoted, st, ctx).map((alt) => !(part.index === "*" && quoted) && alt.some((piece) => piece.kind === "unknown") ? alt.map((piece) => ({ ...piece, unknownCount: true })) : alt);
|
|
41843
41905
|
}
|
|
41844
41906
|
if (part.index !== undefined) {
|
|
41845
41907
|
const index = resolveArrayIndex(part.index, st, ctx);
|
|
@@ -42058,10 +42120,18 @@ function braceAlternatives(part) {
|
|
|
42058
42120
|
return [[{ kind: "glob", text: part.text }]];
|
|
42059
42121
|
return inner.split(",").map((alternative) => [literal2(alternative)]);
|
|
42060
42122
|
}
|
|
42123
|
+
function tildeAt(text) {
|
|
42124
|
+
if (text.startsWith("~"))
|
|
42125
|
+
return 0;
|
|
42126
|
+
const equals = text.indexOf("=");
|
|
42127
|
+
if (equals <= 0 || !IDENTIFIER.test(text.slice(0, equals)))
|
|
42128
|
+
return;
|
|
42129
|
+
return text.charAt(equals + 1) === "~" ? equals + 1 : undefined;
|
|
42130
|
+
}
|
|
42061
42131
|
function expandPart(part, first, st, ctx) {
|
|
42062
42132
|
switch (part.type) {
|
|
42063
42133
|
case "Literal":
|
|
42064
|
-
return [unquotedLiteral(part.text, first ?
|
|
42134
|
+
return [unquotedLiteral(part.text, first ? tildeAt(part.text) : undefined, st, ctx)];
|
|
42065
42135
|
case "SingleQuoted":
|
|
42066
42136
|
case "AnsiCQuoted":
|
|
42067
42137
|
return [[literal2(part.value)]];
|
|
@@ -42117,7 +42187,7 @@ function textLength(pieces) {
|
|
|
42117
42187
|
function expandWord(word, st, ctx) {
|
|
42118
42188
|
let alternatives = [[]];
|
|
42119
42189
|
if (word.parts === undefined)
|
|
42120
|
-
alternatives = [unquotedLiteral(word.text,
|
|
42190
|
+
alternatives = [unquotedLiteral(word.text, tildeAt(word.text), st, ctx)];
|
|
42121
42191
|
else {
|
|
42122
42192
|
word.parts.forEach((part, index) => {
|
|
42123
42193
|
alternatives = product(alternatives, expandPart(part, index === 0, st, ctx));
|
|
@@ -42143,6 +42213,9 @@ function args(words, st, ctx) {
|
|
|
42143
42213
|
if (exp.some((piece) => piece.kind === "glob")) {
|
|
42144
42214
|
return { text: word.text, exp, fields: "unknown" };
|
|
42145
42215
|
}
|
|
42216
|
+
if (exp.some((piece) => piece.unknownCount === true)) {
|
|
42217
|
+
return { text: word.text, exp, fields: "unknown" };
|
|
42218
|
+
}
|
|
42146
42219
|
if (unquoted && exp.some((piece) => piece.kind === "unknown" || /[ \t\n]/.test(piece.text))) {
|
|
42147
42220
|
return { text: word.text, exp, fields: "unknown" };
|
|
42148
42221
|
}
|
|
@@ -42166,6 +42239,46 @@ function literalText(exp) {
|
|
|
42166
42239
|
return;
|
|
42167
42240
|
return exp.map((piece) => piece.text).join("");
|
|
42168
42241
|
}
|
|
42242
|
+
function readableWord(arg) {
|
|
42243
|
+
const whole = literalText(arg.exp);
|
|
42244
|
+
if (whole !== undefined)
|
|
42245
|
+
return { text: whole, whole: true };
|
|
42246
|
+
let text = "";
|
|
42247
|
+
for (const piece of arg.exp) {
|
|
42248
|
+
if (piece.kind !== "literal")
|
|
42249
|
+
break;
|
|
42250
|
+
text += piece.text;
|
|
42251
|
+
}
|
|
42252
|
+
return { text, whole: false };
|
|
42253
|
+
}
|
|
42254
|
+
function mayBeOption(arg, matches) {
|
|
42255
|
+
const { text, whole } = readableWord(arg);
|
|
42256
|
+
if (whole)
|
|
42257
|
+
return matches(text);
|
|
42258
|
+
return mayBeSomeOption(text);
|
|
42259
|
+
}
|
|
42260
|
+
function mayBeSomeOption(text) {
|
|
42261
|
+
return text === "" || /^--?[a-zA-Z-]*$/.test(text);
|
|
42262
|
+
}
|
|
42263
|
+
function valueInWord(arg, count) {
|
|
42264
|
+
const pieces = [];
|
|
42265
|
+
let dropped = 0;
|
|
42266
|
+
for (const piece of arg.exp) {
|
|
42267
|
+
if (dropped >= count) {
|
|
42268
|
+
pieces.push(piece);
|
|
42269
|
+
continue;
|
|
42270
|
+
}
|
|
42271
|
+
if (piece.kind !== "literal")
|
|
42272
|
+
return;
|
|
42273
|
+
if (piece.text.length + dropped <= count) {
|
|
42274
|
+
dropped += piece.text.length;
|
|
42275
|
+
continue;
|
|
42276
|
+
}
|
|
42277
|
+
pieces.push({ ...piece, text: piece.text.slice(count - dropped) });
|
|
42278
|
+
dropped = count;
|
|
42279
|
+
}
|
|
42280
|
+
return dropped < count || pieces.length === 0 ? undefined : pieces;
|
|
42281
|
+
}
|
|
42169
42282
|
function visitParts(parts, st, ctx) {
|
|
42170
42283
|
for (const part of parts ?? []) {
|
|
42171
42284
|
switch (part.type) {
|
|
@@ -42600,6 +42713,15 @@ function spendPatternWork(work, ctx) {
|
|
|
42600
42713
|
function outputChanged(before, after) {
|
|
42601
42714
|
return before !== undefined && (after === undefined || JSON.stringify(before) !== JSON.stringify(after));
|
|
42602
42715
|
}
|
|
42716
|
+
function loopValue(type, name, words) {
|
|
42717
|
+
const pids = words.every((word) => {
|
|
42718
|
+
const text = literalText(word.exp);
|
|
42719
|
+
return text === "" || word.exp.length > 0 && word.exp.every((piece) => piece.descendantPid);
|
|
42720
|
+
});
|
|
42721
|
+
if (pids)
|
|
42722
|
+
return [unknown2(`\`$${name}\`, a loop variable`, true)];
|
|
42723
|
+
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`)];
|
|
42724
|
+
}
|
|
42603
42725
|
function walkNode(node, st, ctx, pipeIn) {
|
|
42604
42726
|
if (++ctx.steps.count > MAX_WALK_STEPS) {
|
|
42605
42727
|
const site = siteOf(node, st);
|
|
@@ -42723,11 +42845,7 @@ function walkNode(node, st, ctx, pipeIn) {
|
|
|
42723
42845
|
}
|
|
42724
42846
|
} else {
|
|
42725
42847
|
const body = clone2(st);
|
|
42726
|
-
|
|
42727
|
-
const text = literalText(word.exp);
|
|
42728
|
-
return text === "" || word.exp.length > 0 && word.exp.every((piece) => piece.descendantPid);
|
|
42729
|
-
});
|
|
42730
|
-
assignScalar(body, name, signalSafe ? [unknown2(`\`$${name}\`, a loop variable`, true)] : [unknown2(`\`$${name}\`, a loop variable`)]);
|
|
42848
|
+
assignScalar(body, name, loopValue(node.type, name, words));
|
|
42731
42849
|
if (node.type === "Select")
|
|
42732
42850
|
assignScalar(body, "REPLY", [unknown2("`$REPLY`, read from input")]);
|
|
42733
42851
|
uncertainly(ctx, () => walkList(node.body.commands, body, ctx));
|
|
@@ -43087,6 +43205,7 @@ function operands(list, valued, longValued = []) {
|
|
|
43087
43205
|
return { options, operands: found };
|
|
43088
43206
|
}
|
|
43089
43207
|
function unwrap2(argv, st, ctx, site) {
|
|
43208
|
+
const alts = [];
|
|
43090
43209
|
let list = argv;
|
|
43091
43210
|
let state = st;
|
|
43092
43211
|
for (let guard = 0;guard < 16; guard += 1) {
|
|
@@ -43095,13 +43214,19 @@ function unwrap2(argv, st, ctx, site) {
|
|
|
43095
43214
|
break;
|
|
43096
43215
|
const base = path4.basename(name);
|
|
43097
43216
|
const rest = list.slice(1);
|
|
43098
|
-
const skip = (valued, longValued = []) => {
|
|
43217
|
+
const skip = (valued, longValued = [], positional = false) => {
|
|
43099
43218
|
let i = 0;
|
|
43100
43219
|
for (;i < rest.length; i += 1) {
|
|
43101
|
-
const
|
|
43220
|
+
const arg = rest[i];
|
|
43221
|
+
const text = literalText(arg.exp);
|
|
43102
43222
|
if (text === "--")
|
|
43103
43223
|
return rest.slice(i + 1);
|
|
43104
|
-
if (text === undefined
|
|
43224
|
+
if (text === undefined) {
|
|
43225
|
+
if (positional || !mayBeOption(arg, (option) => option.startsWith("-")))
|
|
43226
|
+
break;
|
|
43227
|
+
continue;
|
|
43228
|
+
}
|
|
43229
|
+
if (!text.startsWith("-") || text === "-")
|
|
43105
43230
|
break;
|
|
43106
43231
|
if (text.startsWith("--")) {
|
|
43107
43232
|
if (!text.includes("=") && longValued.includes(text))
|
|
@@ -43115,8 +43240,10 @@ function unwrap2(argv, st, ctx, site) {
|
|
|
43115
43240
|
}
|
|
43116
43241
|
return rest.slice(i);
|
|
43117
43242
|
};
|
|
43118
|
-
if ((base === "busybox" || base === "toybox") && rest[0] !== undefined)
|
|
43243
|
+
if ((base === "busybox" || base === "toybox") && rest[0] !== undefined && literalText(rest[0].exp) !== undefined) {
|
|
43119
43244
|
list = rest;
|
|
43245
|
+
continue;
|
|
43246
|
+
}
|
|
43120
43247
|
if (base === "sudo" || base === "doas")
|
|
43121
43248
|
list = skip("ughpCDrtUT", ["--user", "--group"]);
|
|
43122
43249
|
else if (base === "nice")
|
|
@@ -43128,15 +43255,23 @@ function unwrap2(argv, st, ctx, site) {
|
|
|
43128
43255
|
else if (base === "command") {
|
|
43129
43256
|
const flags = rest.map((arg) => literalText(arg.exp));
|
|
43130
43257
|
if (flags[0] === "-v" || flags[0] === "-V")
|
|
43131
|
-
return { argv: [], st: state };
|
|
43258
|
+
return { argv: [], st: state, alts };
|
|
43132
43259
|
list = skip("");
|
|
43133
43260
|
} else if (base === "stdbuf")
|
|
43134
43261
|
list = skip("ioe", ["--input", "--output", "--error"]);
|
|
43135
43262
|
else if (base === "ionice")
|
|
43136
43263
|
list = skip("cnt", ["--class", "--classdata"]);
|
|
43137
43264
|
else if (base === "timeout") {
|
|
43138
|
-
const
|
|
43139
|
-
list =
|
|
43265
|
+
const stopped = skip("sk", ["--signal", "--kill-after"], true);
|
|
43266
|
+
list = stopped.slice(1);
|
|
43267
|
+
for (let at = rest.length - stopped.length;at < rest.length; at += 1) {
|
|
43268
|
+
if (literalText(rest[at]?.exp) !== undefined)
|
|
43269
|
+
break;
|
|
43270
|
+
alts.push(rest.slice(at + 1));
|
|
43271
|
+
}
|
|
43272
|
+
const read = skip("sk", ["--signal", "--kill-after"]);
|
|
43273
|
+
if (read.length !== stopped.length)
|
|
43274
|
+
alts.push(read.slice(1));
|
|
43140
43275
|
} else if (base === "env") {
|
|
43141
43276
|
let i = 0;
|
|
43142
43277
|
const overlay = new Map;
|
|
@@ -43176,7 +43311,7 @@ function unwrap2(argv, st, ctx, site) {
|
|
|
43176
43311
|
overlay.set(arg.exp[0].text.slice(0, arg.exp[0].text.indexOf("=")), [
|
|
43177
43312
|
unknown2("an `env` assignment")
|
|
43178
43313
|
]);
|
|
43179
|
-
} else
|
|
43314
|
+
} else if (text === undefined && mayBeOption(arg, (option) => option.startsWith("-"))) {} else
|
|
43180
43315
|
break;
|
|
43181
43316
|
}
|
|
43182
43317
|
if (i !== -1)
|
|
@@ -43193,7 +43328,7 @@ function unwrap2(argv, st, ctx, site) {
|
|
|
43193
43328
|
} else
|
|
43194
43329
|
break;
|
|
43195
43330
|
}
|
|
43196
|
-
return { argv: list, st: state };
|
|
43331
|
+
return { argv: list, st: state, alts };
|
|
43197
43332
|
}
|
|
43198
43333
|
function runFunction(name, definition, args2, outer, ctx) {
|
|
43199
43334
|
if (outer.runningFunctions.has(name))
|
|
@@ -43278,6 +43413,9 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43278
43413
|
const unwrapped = unwrap2(invocation.args, outer, ctx, invocation.site);
|
|
43279
43414
|
const argv = unwrapped.argv;
|
|
43280
43415
|
const st = unwrapped.st;
|
|
43416
|
+
for (const other of unwrapped.alts) {
|
|
43417
|
+
dispatch({ ...invocation, args: other }, clone2(outer), ctx);
|
|
43418
|
+
}
|
|
43281
43419
|
const name = literalText(argv[0]?.exp);
|
|
43282
43420
|
if (name === undefined)
|
|
43283
43421
|
return;
|
|
@@ -43425,15 +43563,21 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43425
43563
|
const leading = head[0];
|
|
43426
43564
|
const spaced = literalText(head) === "-v";
|
|
43427
43565
|
const joined = !spaced && leading?.kind === "literal" && leading.text.startsWith("-v") && (leading.text.length > 2 || head.length > 1);
|
|
43428
|
-
|
|
43429
|
-
|
|
43566
|
+
const first = rest[0];
|
|
43567
|
+
const mayBeV = !spaced && !joined && first !== undefined && rest[1] !== undefined && mayBeOption(first, (text) => text.startsWith("-v"));
|
|
43568
|
+
if (spaced || joined || mayBeV) {
|
|
43569
|
+
const variable = spaced || mayBeV ? literalText(rest[1]?.exp) : literalText([
|
|
43430
43570
|
{ ...leading, text: leading?.text.slice(2) ?? "" },
|
|
43431
43571
|
...head.slice(1)
|
|
43432
43572
|
]);
|
|
43433
|
-
const printed = rest.slice(spaced ? 2 : 1);
|
|
43573
|
+
const printed = rest.slice(spaced || mayBeV ? 2 : 1);
|
|
43434
43574
|
const [format, value, ...others] = printed;
|
|
43435
43575
|
let assigned;
|
|
43436
|
-
if (
|
|
43576
|
+
if (mayBeV) {
|
|
43577
|
+
assigned = [
|
|
43578
|
+
unknown2(`\`$${variable ?? "?"}\` (printf with an option word the guard cannot read, which may be \`-v\`)`)
|
|
43579
|
+
];
|
|
43580
|
+
} else if (literalText(format?.exp) === "%s" && value !== undefined && others.length === 0) {
|
|
43437
43581
|
assigned = value.exp;
|
|
43438
43582
|
} else {
|
|
43439
43583
|
const text = printfText(printed, MAX_VALUE_LENGTH);
|
|
@@ -43516,24 +43660,78 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43516
43660
|
return;
|
|
43517
43661
|
case "rm":
|
|
43518
43662
|
case "unlink":
|
|
43519
|
-
checkTargets(base, "delete", operands(rest, "").operands,
|
|
43663
|
+
checkTargets(base, "delete", operands(rest, "").operands, ACTS_ON_LINK, st, ctx, site);
|
|
43520
43664
|
return;
|
|
43521
43665
|
case "mv": {
|
|
43522
|
-
const found =
|
|
43523
|
-
|
|
43524
|
-
|
|
43525
|
-
|
|
43526
|
-
|
|
43527
|
-
|
|
43528
|
-
|
|
43529
|
-
|
|
43666
|
+
const found = writeArguments(rest, "tS", MV_OPTIONS);
|
|
43667
|
+
if (found.flags.has("--help") || found.flags.has("--version"))
|
|
43668
|
+
return;
|
|
43669
|
+
const targets = [...found.operands, ...writeDestinations(found, site)];
|
|
43670
|
+
checkTargets("mv", "move", targets, ACTS_ON_LINK, st, ctx, site);
|
|
43671
|
+
return;
|
|
43672
|
+
}
|
|
43673
|
+
case "cp": {
|
|
43674
|
+
const found = writeArguments(rest, "tS", CP_OPTIONS);
|
|
43675
|
+
if (found.flags.has("--help") || found.flags.has("--version"))
|
|
43676
|
+
return;
|
|
43677
|
+
const unlinks = found.flags.has("b") || found.flags.has("--backup") || found.flags.has("--remove-destination") || found.uncertain;
|
|
43678
|
+
const destinations = writeDestinations(found, site);
|
|
43679
|
+
checkTargets("cp", "overwrite", destinations, unlinks ? WRITES_EITHER : WRITES_THROUGH_LINK, st, ctx, site);
|
|
43680
|
+
checkCopyContents(found, destinations, st, ctx, site);
|
|
43681
|
+
return;
|
|
43682
|
+
}
|
|
43683
|
+
case "install": {
|
|
43684
|
+
const found = writeArguments(rest, "mogtS", INSTALL_OPTIONS);
|
|
43685
|
+
if (found.flags.has("--help") || found.flags.has("--version"))
|
|
43686
|
+
return;
|
|
43687
|
+
const directories = found.flags.has("d") || found.flags.has("--directory");
|
|
43688
|
+
checkTargets("install", directories ? "create or change the mode of" : "overwrite", directories ? found.operands : writeDestinations(found, site), WRITES_EITHER, st, ctx, site);
|
|
43689
|
+
return;
|
|
43690
|
+
}
|
|
43691
|
+
case "dd": {
|
|
43692
|
+
const targets = [];
|
|
43530
43693
|
for (const arg of rest) {
|
|
43531
|
-
const text =
|
|
43532
|
-
if (text
|
|
43533
|
-
|
|
43694
|
+
const { text, whole } = readableWord(arg);
|
|
43695
|
+
if (whole && longOption(text, ["--help", "--version"]) !== undefined)
|
|
43696
|
+
return;
|
|
43697
|
+
if (text.startsWith("of=")) {
|
|
43698
|
+
const carried = valueInWord(arg, 3);
|
|
43699
|
+
targets.push({
|
|
43700
|
+
text: arg.text,
|
|
43701
|
+
exp: carried ?? [unknown2("an `of=` operand the guard cannot read")]
|
|
43702
|
+
});
|
|
43703
|
+
continue;
|
|
43704
|
+
}
|
|
43705
|
+
if (!whole && "of=".startsWith(text)) {
|
|
43706
|
+
targets.push({
|
|
43707
|
+
text: arg.text,
|
|
43708
|
+
exp: [unknown2("a `dd` operand the guard cannot read, which may name its `of=` file")]
|
|
43709
|
+
});
|
|
43534
43710
|
}
|
|
43535
43711
|
}
|
|
43536
|
-
checkTargets("
|
|
43712
|
+
checkTargets("dd", "write", targets, WRITES_THROUGH_LINK, st, ctx, site);
|
|
43713
|
+
return;
|
|
43714
|
+
}
|
|
43715
|
+
case "ln": {
|
|
43716
|
+
const found = writeArguments(rest, "St", LN_OPTIONS);
|
|
43717
|
+
if (found.flags.has("--help") || found.flags.has("--version"))
|
|
43718
|
+
return;
|
|
43719
|
+
const sole = found.operands[0];
|
|
43720
|
+
const single = found.operands.length === 1 && found.directory === undefined && !found.uncertain && sole !== undefined && readableWord(sole).whole;
|
|
43721
|
+
const destinations = single ? [{ text: ".", exp: [literal2(".")] }] : writeDestinations(found, site);
|
|
43722
|
+
checkTargets("ln", "overwrite", destinations, WRITES_EITHER, st, ctx, site);
|
|
43723
|
+
const symbolic = found.flags.has("s") || found.flags.has("--symbolic");
|
|
43724
|
+
if (!symbolic) {
|
|
43725
|
+
const sources = found.operands.filter((arg) => !destinations.includes(arg));
|
|
43726
|
+
checkTargets("ln", "hard-link", sources, LINKS_TO, st, ctx, site);
|
|
43727
|
+
}
|
|
43728
|
+
return;
|
|
43729
|
+
}
|
|
43730
|
+
case "sed": {
|
|
43731
|
+
const writes = sedWrites(rest);
|
|
43732
|
+
const program = writes.inferred ? "sed, whose option word the guard cannot read and which may be `-i`," : "sed -i";
|
|
43733
|
+
checkTargets(program, "rewrite in place", writes.replaced, WRITES_LINK, st, ctx, site);
|
|
43734
|
+
checkTargets(program, "rewrite in place", writes.followed, WRITES_EITHER, st, ctx, site);
|
|
43537
43735
|
return;
|
|
43538
43736
|
}
|
|
43539
43737
|
case "rsync": {
|
|
@@ -43555,41 +43753,48 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43555
43753
|
"--remote-option",
|
|
43556
43754
|
"--rsync-path",
|
|
43557
43755
|
"--temp-dir"
|
|
43558
|
-
]).operands.at(-1);
|
|
43756
|
+
]).operands.filter(passesAnArgument).at(-1);
|
|
43559
43757
|
if (destination !== undefined) {
|
|
43560
|
-
checkTargets("rsync", "synchronize and delete into", [destination],
|
|
43758
|
+
checkTargets("rsync", "synchronize and delete into", [destination], ACTS_THROUGH_LINK, st, ctx, site);
|
|
43561
43759
|
}
|
|
43562
43760
|
return;
|
|
43563
43761
|
}
|
|
43564
43762
|
case "tar":
|
|
43565
43763
|
case "unzip": {
|
|
43566
43764
|
const extracting = base === "unzip" || rest.some((arg, index) => {
|
|
43567
|
-
const text =
|
|
43568
|
-
|
|
43765
|
+
const { text, whole } = readableWord(arg);
|
|
43766
|
+
if (!whole && index === 0 && /^[A-Za-z]*$/.test(text))
|
|
43767
|
+
return true;
|
|
43768
|
+
return mayBeOption(arg, (mode) => mode === "--extract" || mode === "--get" || mode.startsWith("-") && !mode.startsWith("--") && mode.includes("x") || index === 0 && /^[A-Za-z]*x[A-Za-z]*$/.test(mode));
|
|
43569
43769
|
});
|
|
43570
43770
|
if (!extracting)
|
|
43571
43771
|
return;
|
|
43572
43772
|
const targetDirectories = [];
|
|
43573
43773
|
const short = base === "tar" ? "-C" : "-d";
|
|
43574
43774
|
const long = base === "tar" ? "--directory" : "--destination";
|
|
43775
|
+
const letter = short.slice(1);
|
|
43575
43776
|
for (const [index, arg] of rest.entries()) {
|
|
43576
|
-
const text =
|
|
43577
|
-
|
|
43777
|
+
const { text, whole } = readableWord(arg);
|
|
43778
|
+
const clusterAt = text.startsWith("-") && !text.startsWith("--") ? text.indexOf(letter, 1) : -1;
|
|
43779
|
+
const carried = text.startsWith(`${long}=`) ? valueInWord(arg, long.length + 1) : clusterAt !== -1 && (!whole || text.length > clusterAt + 1) ? valueInWord(arg, clusterAt + 1) : undefined;
|
|
43780
|
+
if (carried !== undefined) {
|
|
43781
|
+
targetDirectories.push({ text: arg.text, exp: carried });
|
|
43782
|
+
continue;
|
|
43783
|
+
}
|
|
43784
|
+
if (clusterAt !== -1 || mayBeOption(arg, (option) => option === short || option === long)) {
|
|
43578
43785
|
const target = rest[index + 1];
|
|
43579
43786
|
if (target !== undefined)
|
|
43580
43787
|
targetDirectories.push(target);
|
|
43581
|
-
} else if (text?.startsWith(`${long}=`)) {
|
|
43582
|
-
targetDirectories.push({ text: arg.text, exp: [literal2(text.slice(long.length + 1))] });
|
|
43583
43788
|
}
|
|
43584
43789
|
}
|
|
43585
|
-
checkTargets(base, "extract and overwrite in", targetDirectories,
|
|
43790
|
+
checkTargets(base, "extract and overwrite in", targetDirectories, ACTS_THROUGH_LINK, st, ctx, site);
|
|
43586
43791
|
return;
|
|
43587
43792
|
}
|
|
43588
43793
|
case "shred":
|
|
43589
|
-
checkTargets("shred", "shred", operands(rest, "ns", ["--iterations", "--size", "--random-source"]).operands,
|
|
43794
|
+
checkTargets("shred", "shred", operands(rest, "ns", ["--iterations", "--size", "--random-source"]).operands, ACTS_ON_BOTH, st, ctx, site);
|
|
43590
43795
|
return;
|
|
43591
43796
|
case "truncate":
|
|
43592
|
-
checkTargets("truncate", "truncate", operands(rest, "sr", ["--size", "--reference"]).operands,
|
|
43797
|
+
checkTargets("truncate", "truncate", operands(rest, "sr", ["--size", "--reference"]).operands, ACTS_THROUGH_LINK, st, ctx, site);
|
|
43593
43798
|
return;
|
|
43594
43799
|
case "chmod":
|
|
43595
43800
|
case "chown":
|
|
@@ -43627,41 +43832,73 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43627
43832
|
case "killall":
|
|
43628
43833
|
case "killall5":
|
|
43629
43834
|
throw new Refusal(site.snippet, site.line, `${base} selects processes by name or pattern, which reaches other agents' processes on a ` + "shared machine; a pane signals only processes it started. Stop a process you started " + 'with the hub tool (`op: "stop"`), or `kill` the pid of a child you started from this pane');
|
|
43630
|
-
case "fuser":
|
|
43631
|
-
|
|
43835
|
+
case "fuser": {
|
|
43836
|
+
const kills = rest.some((arg) => mayBeOption(arg, (text) => /^-[a-zA-Z]*k/.test(text) || text === "--kill"));
|
|
43837
|
+
const readable2 = rest.every((arg) => literalText(arg.exp) !== undefined);
|
|
43838
|
+
const mayBeSeveral = rest.some((arg) => arg.fields === "unknown");
|
|
43839
|
+
if (kills && (readable2 || mayBeSeveral || rest.length > 1)) {
|
|
43632
43840
|
throw new Refusal(site.snippet, site.line, "`fuser -k` kills every process using a file, other agents' included; a pane signals only " + "processes it started");
|
|
43633
43841
|
}
|
|
43634
43842
|
return;
|
|
43843
|
+
}
|
|
43635
43844
|
case "tmux": {
|
|
43636
43845
|
let socket;
|
|
43637
43846
|
let socketIsPath = false;
|
|
43638
|
-
|
|
43639
|
-
|
|
43640
|
-
|
|
43641
|
-
|
|
43642
|
-
|
|
43643
|
-
|
|
43644
|
-
|
|
43645
|
-
|
|
43646
|
-
|
|
43847
|
+
let current = { words: [], speculative: false };
|
|
43848
|
+
const segments = [current];
|
|
43849
|
+
for (const arg of rest) {
|
|
43850
|
+
const word = literalText(arg.exp);
|
|
43851
|
+
const trailing = word !== ";" && word !== undefined && word.endsWith(";");
|
|
43852
|
+
if (trailing)
|
|
43853
|
+
current.words.push({ text: arg.text, exp: [literal2(word.slice(0, -1))] });
|
|
43854
|
+
else if (word !== ";")
|
|
43855
|
+
current.words.push(arg);
|
|
43856
|
+
if (word === ";" || trailing) {
|
|
43857
|
+
current = { words: [], speculative: false };
|
|
43858
|
+
segments.push(current);
|
|
43859
|
+
} else if (word === undefined || arg.fields === "unknown") {
|
|
43860
|
+
current = { words: [], speculative: true };
|
|
43861
|
+
segments.push(current);
|
|
43647
43862
|
}
|
|
43648
43863
|
}
|
|
43649
43864
|
const killCommands = ["kill-server", "kill-session", "kill-window", "kill-pane"];
|
|
43650
43865
|
const respawnCommands = ["respawn-pane", "respawn-window"];
|
|
43651
|
-
const
|
|
43652
|
-
|
|
43653
|
-
if (text === undefined)
|
|
43654
|
-
return false;
|
|
43655
|
-
const compact = text.replace("-", "");
|
|
43656
|
-
return respawnCommands.filter((command) => command.replace("-", "").startsWith(compact)).length === 1;
|
|
43657
|
-
}) && rest.some((arg) => literalText(arg.exp) === "-k");
|
|
43658
|
-
const killing = rest.find((arg) => {
|
|
43659
|
-
const text = literalText(arg.exp);
|
|
43660
|
-
if (text === undefined)
|
|
43866
|
+
const names = (word, commands) => {
|
|
43867
|
+
if (word === undefined)
|
|
43661
43868
|
return false;
|
|
43869
|
+
const { text, whole } = readableWord(word);
|
|
43662
43870
|
const compact = text.replace("-", "");
|
|
43663
|
-
|
|
43664
|
-
|
|
43871
|
+
const normalized = commands.map((command) => command.replace("-", ""));
|
|
43872
|
+
return whole ? completeName(compact, normalized) !== undefined : normalized.some((command) => command.startsWith(compact));
|
|
43873
|
+
};
|
|
43874
|
+
let killing;
|
|
43875
|
+
for (const segment of segments) {
|
|
43876
|
+
const words = segment.words;
|
|
43877
|
+
let subcommand;
|
|
43878
|
+
for (let i = 0;i < words.length; i += 1) {
|
|
43879
|
+
const arg = words[i];
|
|
43880
|
+
const { text, whole } = readableWord(arg);
|
|
43881
|
+
const attached = (option) => text.startsWith(option) && (!whole || text.length > option.length) ? valueInWord(arg, option.length) : undefined;
|
|
43882
|
+
const carried = attached("-L") ?? attached("-S") ?? attached("--socket=");
|
|
43883
|
+
if (carried !== undefined) {
|
|
43884
|
+
socket = { text: arg.text, exp: carried };
|
|
43885
|
+
socketIsPath = !text.startsWith("-L");
|
|
43886
|
+
} else if (text === "-L" || text === "-S" || text === "--socket") {
|
|
43887
|
+
socket = words[i + 1];
|
|
43888
|
+
socketIsPath = text !== "-L";
|
|
43889
|
+
i += 1;
|
|
43890
|
+
} else if (text === "-f" || text === "-T") {
|
|
43891
|
+
i += 1;
|
|
43892
|
+
} else if (subcommand === undefined && !text.startsWith("-")) {
|
|
43893
|
+
subcommand = arg;
|
|
43894
|
+
}
|
|
43895
|
+
}
|
|
43896
|
+
if (segment.speculative && (subcommand === undefined || readableWord(subcommand).text === ""))
|
|
43897
|
+
continue;
|
|
43898
|
+
const respawning = names(subcommand, respawnCommands) && words.some((arg) => mayBeOption(arg, (text) => text === "-k"));
|
|
43899
|
+
if (names(subcommand, killCommands) || respawning)
|
|
43900
|
+
killing ??= subcommand;
|
|
43901
|
+
}
|
|
43665
43902
|
if (killing === undefined)
|
|
43666
43903
|
return;
|
|
43667
43904
|
let resolvedSocket;
|
|
@@ -43685,13 +43922,19 @@ function dispatch(invocation, outer, ctx) {
|
|
|
43685
43922
|
if (resolvedSocket !== undefined && judgePath(resolvedSocket, st, ctx, { follow: true, overwrite: false }).ok) {
|
|
43686
43923
|
return;
|
|
43687
43924
|
}
|
|
43688
|
-
throw new Refusal(site.snippet, site.line, `\`tmux ${
|
|
43925
|
+
throw new Refusal(site.snippet, site.line, `\`tmux ${killing.text}\` needs a socket path inside this pane's roots; the ` + "resolved socket can end panes and processes this pane did not start");
|
|
43926
|
+
}
|
|
43927
|
+
case "busybox":
|
|
43928
|
+
case "toybox": {
|
|
43929
|
+
if (rest.length === 0)
|
|
43930
|
+
return;
|
|
43931
|
+
throw new Refusal(site.snippet, site.line, `the guard cannot read the applet \`${rest[0].text}\` ${base} would run, and the ` + "applets it carries include `rm`, `sh` and `killall`, which reach paths and processes " + `this pane did not start; name the applet (\`${base} rm \u2026\`) so it can be checked`);
|
|
43689
43932
|
}
|
|
43690
43933
|
default:
|
|
43691
43934
|
break;
|
|
43692
43935
|
}
|
|
43693
|
-
if (SHELLS.has(base)
|
|
43694
|
-
runShell(
|
|
43936
|
+
if (SHELLS.has(base)) {
|
|
43937
|
+
runShell(rest, invocation, st, ctx);
|
|
43695
43938
|
return;
|
|
43696
43939
|
}
|
|
43697
43940
|
const interpreter = interpreterLanguage(base);
|
|
@@ -43721,8 +43964,9 @@ function changeDirectory(target, st, ctx) {
|
|
|
43721
43964
|
function declaration(builtin, list, st, ctx, site) {
|
|
43722
43965
|
const options = list.map((arg) => literalText(arg.exp) ?? "").filter((text) => /^[-+][a-zA-Z]+$/.test(text));
|
|
43723
43966
|
const option = (letter) => options.some((text) => text.startsWith("-") && text.includes(letter));
|
|
43724
|
-
|
|
43725
|
-
|
|
43967
|
+
const mayBeNameref = list.some((arg) => mayBeOption(arg, (text) => /^-[a-zA-Z]*n/.test(text)));
|
|
43968
|
+
if (builtin !== "export" && mayBeNameref) {
|
|
43969
|
+
throw new Refusal(site.snippet, site.line, option("n") ? "the guard does not follow a nameref (`declare -n`), whose writes land in the variable it names" : `an option word \`${builtin}\` carries that the guard cannot read may be \`-n\`, a nameref ` + "whose writes land in the variable it names and which the guard does not follow; write " + "the option as a literal");
|
|
43726
43970
|
}
|
|
43727
43971
|
if (option("f") || option("F"))
|
|
43728
43972
|
return;
|
|
@@ -43869,11 +44113,50 @@ function nameAndValue(arg) {
|
|
|
43869
44113
|
}
|
|
43870
44114
|
return { name, value: undefined };
|
|
43871
44115
|
}
|
|
43872
|
-
|
|
44116
|
+
var ACTS_ON_LINK = {
|
|
44117
|
+
followed: false,
|
|
44118
|
+
unfollowed: true,
|
|
44119
|
+
overwrite: false
|
|
44120
|
+
};
|
|
44121
|
+
var ACTS_THROUGH_LINK = {
|
|
44122
|
+
followed: true,
|
|
44123
|
+
unfollowed: false,
|
|
44124
|
+
overwrite: false
|
|
44125
|
+
};
|
|
44126
|
+
var ACTS_ON_BOTH = {
|
|
44127
|
+
followed: true,
|
|
44128
|
+
unfollowed: true,
|
|
44129
|
+
overwrite: false
|
|
44130
|
+
};
|
|
44131
|
+
var WRITES_THROUGH_LINK = {
|
|
44132
|
+
followed: true,
|
|
44133
|
+
unfollowed: false,
|
|
44134
|
+
overwrite: true
|
|
44135
|
+
};
|
|
44136
|
+
var WRITES_LINK = {
|
|
44137
|
+
followed: false,
|
|
44138
|
+
unfollowed: true,
|
|
44139
|
+
overwrite: true
|
|
44140
|
+
};
|
|
44141
|
+
var WRITES_EITHER = {
|
|
44142
|
+
followed: true,
|
|
44143
|
+
unfollowed: true,
|
|
44144
|
+
overwrite: true
|
|
44145
|
+
};
|
|
44146
|
+
var LINKS_TO = {
|
|
44147
|
+
followed: true,
|
|
44148
|
+
unfollowed: false,
|
|
44149
|
+
overwrite: true
|
|
44150
|
+
};
|
|
44151
|
+
function checkTargets(program, verb, targets, reach, st, ctx, site) {
|
|
43873
44152
|
for (const target of targets) {
|
|
43874
|
-
|
|
43875
|
-
|
|
43876
|
-
|
|
44153
|
+
for (const follow of [true, false]) {
|
|
44154
|
+
if (!(follow ? reach.followed : reach.unfollowed))
|
|
44155
|
+
continue;
|
|
44156
|
+
const verdict = judgePath(target.exp, st, ctx, { follow, overwrite: reach.overwrite });
|
|
44157
|
+
if (!verdict.ok) {
|
|
44158
|
+
throw refusal(site, `${program} would ${verb} \`${target.text}\` (${verdict.resolution})`, ctx);
|
|
44159
|
+
}
|
|
43877
44160
|
}
|
|
43878
44161
|
const text = literalText(target.exp);
|
|
43879
44162
|
if (text !== undefined && (text.startsWith("/") || st.cwd !== undefined)) {
|
|
@@ -43881,6 +44164,339 @@ function checkTargets(program, verb, targets, follow, st, ctx, site) {
|
|
|
43881
44164
|
}
|
|
43882
44165
|
}
|
|
43883
44166
|
}
|
|
44167
|
+
function completeName(prefix, names) {
|
|
44168
|
+
if (names.includes(prefix))
|
|
44169
|
+
return prefix;
|
|
44170
|
+
const reachable = names.filter((name) => name.startsWith(prefix));
|
|
44171
|
+
return reachable.length === 1 ? reachable[0] : undefined;
|
|
44172
|
+
}
|
|
44173
|
+
function longOption(text, names) {
|
|
44174
|
+
return completeName(text.split("=", 1)[0], names);
|
|
44175
|
+
}
|
|
44176
|
+
var GNU_INFORMATION = { "--help": false, "--version": false };
|
|
44177
|
+
var MV_OPTIONS = {
|
|
44178
|
+
"--backup": false,
|
|
44179
|
+
"--context": false,
|
|
44180
|
+
"--debug": false,
|
|
44181
|
+
"--force": false,
|
|
44182
|
+
"--interactive": false,
|
|
44183
|
+
"--no-clobber": false,
|
|
44184
|
+
"--no-copy": false,
|
|
44185
|
+
"--no-target-directory": false,
|
|
44186
|
+
"--strip-trailing-slashes": false,
|
|
44187
|
+
"--suffix": true,
|
|
44188
|
+
"--target-directory": true,
|
|
44189
|
+
"--update": false,
|
|
44190
|
+
"--verbose": false,
|
|
44191
|
+
...GNU_INFORMATION
|
|
44192
|
+
};
|
|
44193
|
+
var CP_OPTIONS = {
|
|
44194
|
+
"--archive": false,
|
|
44195
|
+
"--attributes-only": false,
|
|
44196
|
+
"--backup": false,
|
|
44197
|
+
"--context": false,
|
|
44198
|
+
"--copy-contents": false,
|
|
44199
|
+
"--debug": false,
|
|
44200
|
+
"--dereference": false,
|
|
44201
|
+
"--force": false,
|
|
44202
|
+
"--interactive": false,
|
|
44203
|
+
"--link": false,
|
|
44204
|
+
"--no-clobber": false,
|
|
44205
|
+
"--no-dereference": false,
|
|
44206
|
+
"--no-preserve": true,
|
|
44207
|
+
"--no-target-directory": false,
|
|
44208
|
+
"--one-file-system": false,
|
|
44209
|
+
"--parents": false,
|
|
44210
|
+
"--preserve": false,
|
|
44211
|
+
"--recursive": false,
|
|
44212
|
+
"--reflink": false,
|
|
44213
|
+
"--remove-destination": false,
|
|
44214
|
+
"--sparse": true,
|
|
44215
|
+
"--strip-trailing-slashes": false,
|
|
44216
|
+
"--suffix": true,
|
|
44217
|
+
"--symbolic-link": false,
|
|
44218
|
+
"--target-directory": true,
|
|
44219
|
+
"--update": false,
|
|
44220
|
+
"--verbose": false,
|
|
44221
|
+
...GNU_INFORMATION
|
|
44222
|
+
};
|
|
44223
|
+
var INSTALL_OPTIONS = {
|
|
44224
|
+
"--backup": false,
|
|
44225
|
+
"--compare": false,
|
|
44226
|
+
"--context": false,
|
|
44227
|
+
"--debug": false,
|
|
44228
|
+
"--directory": false,
|
|
44229
|
+
"--group": true,
|
|
44230
|
+
"--mode": true,
|
|
44231
|
+
"--no-target-directory": false,
|
|
44232
|
+
"--owner": true,
|
|
44233
|
+
"--preserve-context": false,
|
|
44234
|
+
"--preserve-timestamps": false,
|
|
44235
|
+
"--strip": false,
|
|
44236
|
+
"--strip-program": true,
|
|
44237
|
+
"--suffix": true,
|
|
44238
|
+
"--target-directory": true,
|
|
44239
|
+
"--verbose": false,
|
|
44240
|
+
...GNU_INFORMATION
|
|
44241
|
+
};
|
|
44242
|
+
var LN_OPTIONS = {
|
|
44243
|
+
"--backup": false,
|
|
44244
|
+
"--directory": false,
|
|
44245
|
+
"--force": false,
|
|
44246
|
+
"--interactive": false,
|
|
44247
|
+
"--logical": false,
|
|
44248
|
+
"--no-dereference": false,
|
|
44249
|
+
"--no-target-directory": false,
|
|
44250
|
+
"--physical": false,
|
|
44251
|
+
"--relative": false,
|
|
44252
|
+
"--suffix": true,
|
|
44253
|
+
"--symbolic": false,
|
|
44254
|
+
"--target-directory": true,
|
|
44255
|
+
"--verbose": false,
|
|
44256
|
+
...GNU_INFORMATION
|
|
44257
|
+
};
|
|
44258
|
+
function passesAnArgument(arg) {
|
|
44259
|
+
return arg.fields !== "none";
|
|
44260
|
+
}
|
|
44261
|
+
function writeArguments(rest, valued, long) {
|
|
44262
|
+
const names = Object.keys(long);
|
|
44263
|
+
const operands2 = [];
|
|
44264
|
+
const flags = new Set;
|
|
44265
|
+
let directory;
|
|
44266
|
+
let uncertain;
|
|
44267
|
+
for (let i = 0;i < rest.length; i += 1) {
|
|
44268
|
+
const arg = rest[i];
|
|
44269
|
+
const { text, whole } = readableWord(arg);
|
|
44270
|
+
if (whole && text === "--") {
|
|
44271
|
+
operands2.push(...rest.slice(i + 1).filter(passesAnArgument));
|
|
44272
|
+
break;
|
|
44273
|
+
}
|
|
44274
|
+
if (!whole || !text.startsWith("-") || text === "-") {
|
|
44275
|
+
if (!whole && mayBeSomeOption(text))
|
|
44276
|
+
uncertain = arg;
|
|
44277
|
+
if (passesAnArgument(arg))
|
|
44278
|
+
operands2.push(arg);
|
|
44279
|
+
continue;
|
|
44280
|
+
}
|
|
44281
|
+
let takesValue = false;
|
|
44282
|
+
let offset;
|
|
44283
|
+
if (text.startsWith("--")) {
|
|
44284
|
+
const option = longOption(text, names);
|
|
44285
|
+
if (option === undefined)
|
|
44286
|
+
continue;
|
|
44287
|
+
flags.add(option);
|
|
44288
|
+
takesValue = long[option] === true && !text.includes("=");
|
|
44289
|
+
if (option === "--target-directory")
|
|
44290
|
+
offset = text.indexOf("=") + 1 || text.length;
|
|
44291
|
+
} else {
|
|
44292
|
+
for (let index = 1;index < text.length; index += 1) {
|
|
44293
|
+
const letter = text.charAt(index);
|
|
44294
|
+
flags.add(letter);
|
|
44295
|
+
if (!valued.includes(letter))
|
|
44296
|
+
continue;
|
|
44297
|
+
takesValue = index === text.length - 1;
|
|
44298
|
+
if (letter === "t")
|
|
44299
|
+
offset = index + 1;
|
|
44300
|
+
break;
|
|
44301
|
+
}
|
|
44302
|
+
}
|
|
44303
|
+
const next = takesValue ? rest[i + 1] : undefined;
|
|
44304
|
+
if (takesValue)
|
|
44305
|
+
i += 1;
|
|
44306
|
+
if (offset === undefined)
|
|
44307
|
+
continue;
|
|
44308
|
+
const value = valueInWord(arg, offset);
|
|
44309
|
+
directory = value === undefined ? next : { text: arg.text, exp: value };
|
|
44310
|
+
}
|
|
44311
|
+
return { operands: operands2, flags, directory, uncertain };
|
|
44312
|
+
}
|
|
44313
|
+
function writeDestinations(found, site) {
|
|
44314
|
+
if (found.operands.length > 1 && (found.flags.has("T") || found.flags.has("--no-target-directory"))) {
|
|
44315
|
+
return [found.operands.at(-1)];
|
|
44316
|
+
}
|
|
44317
|
+
if (found.directory !== undefined && !found.uncertain)
|
|
44318
|
+
return [found.directory];
|
|
44319
|
+
if (found.operands.length < 2 || found.uncertain) {
|
|
44320
|
+
const word = found.uncertain?.text ?? found.operands.at(-1)?.text ?? "missing destination";
|
|
44321
|
+
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`);
|
|
44322
|
+
}
|
|
44323
|
+
return [found.operands.at(-1)];
|
|
44324
|
+
}
|
|
44325
|
+
function checkCopyContents(found, destinations, st, ctx, site) {
|
|
44326
|
+
const recursive = found.uncertain || ["r", "R", "a", "--recursive", "--archive"].some((flag) => found.flags.has(flag));
|
|
44327
|
+
const direct = found.flags.has("T") || found.flags.has("--no-target-directory");
|
|
44328
|
+
for (const destination of destinations) {
|
|
44329
|
+
const text = literalText(destination.exp);
|
|
44330
|
+
if (text === undefined || !path4.isAbsolute(text) && st.cwd === undefined)
|
|
44331
|
+
continue;
|
|
44332
|
+
const dir = realExisting(path4.resolve(st.cwd ?? "/", text));
|
|
44333
|
+
if (!statSync(dir, { throwIfNoEntry: false })?.isDirectory())
|
|
44334
|
+
continue;
|
|
44335
|
+
for (const source of found.operands) {
|
|
44336
|
+
if (destinations.includes(source))
|
|
44337
|
+
continue;
|
|
44338
|
+
const name = literalText(source.exp);
|
|
44339
|
+
const from = name === undefined ? undefined : path4.resolve(st.cwd ?? "/", name);
|
|
44340
|
+
const knownTree = from !== undefined && statSync(from, { throwIfNoEntry: false })?.isDirectory();
|
|
44341
|
+
const root = direct || name === undefined ? dir : path4.join(dir, found.flags.has("--parents") ? name : path4.basename(name));
|
|
44342
|
+
const pending = [root];
|
|
44343
|
+
while (pending.length > 0) {
|
|
44344
|
+
const target = pending.pop();
|
|
44345
|
+
checkTargets("cp", "overwrite", [{ text: name === undefined ? source.text : target, exp: [literal2(target)] }], WRITES_THROUGH_LINK, st, ctx, site);
|
|
44346
|
+
if (!recursive && !(name === undefined && target === root))
|
|
44347
|
+
continue;
|
|
44348
|
+
if (!lstatSync(target, { throwIfNoEntry: false })?.isDirectory())
|
|
44349
|
+
continue;
|
|
44350
|
+
const entries = opendirSync(target);
|
|
44351
|
+
try {
|
|
44352
|
+
for (let entry = entries.readSync();entry !== null; entry = entries.readSync()) {
|
|
44353
|
+
if (++ctx.steps.count > MAX_WALK_STEPS) {
|
|
44354
|
+
throw new Refusal(site.snippet, site.line, WALK_LIMIT);
|
|
44355
|
+
}
|
|
44356
|
+
const child = path4.join(target, entry.name);
|
|
44357
|
+
if (knownTree && !existsSync(path4.join(from, path4.relative(root, child))))
|
|
44358
|
+
continue;
|
|
44359
|
+
pending.push(child);
|
|
44360
|
+
}
|
|
44361
|
+
} finally {
|
|
44362
|
+
entries.closeSync();
|
|
44363
|
+
}
|
|
44364
|
+
}
|
|
44365
|
+
}
|
|
44366
|
+
}
|
|
44367
|
+
}
|
|
44368
|
+
var SED_OPTIONS = {
|
|
44369
|
+
"--debug": false,
|
|
44370
|
+
"--expression": true,
|
|
44371
|
+
"--file": true,
|
|
44372
|
+
"--follow-symlinks": false,
|
|
44373
|
+
"--in-place": false,
|
|
44374
|
+
"--line-length": true,
|
|
44375
|
+
"--null-data": false,
|
|
44376
|
+
"--posix": false,
|
|
44377
|
+
"--quiet": false,
|
|
44378
|
+
"--regexp-extended": false,
|
|
44379
|
+
"--sandbox": false,
|
|
44380
|
+
"--separate": false,
|
|
44381
|
+
"--silent": false,
|
|
44382
|
+
"--unbuffered": false,
|
|
44383
|
+
...GNU_INFORMATION
|
|
44384
|
+
};
|
|
44385
|
+
function sedWrites(list) {
|
|
44386
|
+
const names = Object.keys(SED_OPTIONS);
|
|
44387
|
+
let inPlace = false;
|
|
44388
|
+
let follow = false;
|
|
44389
|
+
let expression = false;
|
|
44390
|
+
let splitOption = false;
|
|
44391
|
+
let ended = false;
|
|
44392
|
+
let reopened = false;
|
|
44393
|
+
let takesNext = false;
|
|
44394
|
+
const unread = [];
|
|
44395
|
+
const operands2 = [];
|
|
44396
|
+
for (let i = 0;i < list.length; i += 1) {
|
|
44397
|
+
const arg = list[i];
|
|
44398
|
+
const { text, whole } = readableWord(arg);
|
|
44399
|
+
const afterUnread = takesNext;
|
|
44400
|
+
takesNext = false;
|
|
44401
|
+
const several = arg.fields === "unknown";
|
|
44402
|
+
if (ended) {
|
|
44403
|
+
operands2.push({ arg, option: false, several });
|
|
44404
|
+
continue;
|
|
44405
|
+
}
|
|
44406
|
+
if (whole && text === "--") {
|
|
44407
|
+
if (reopened)
|
|
44408
|
+
operands2.push({ arg, option: false, several: false });
|
|
44409
|
+
if (afterUnread)
|
|
44410
|
+
reopened = true;
|
|
44411
|
+
else
|
|
44412
|
+
ended = true;
|
|
44413
|
+
continue;
|
|
44414
|
+
}
|
|
44415
|
+
if (several) {
|
|
44416
|
+
splitOption = true;
|
|
44417
|
+
takesNext = true;
|
|
44418
|
+
operands2.push({ arg, option: true, several });
|
|
44419
|
+
continue;
|
|
44420
|
+
}
|
|
44421
|
+
if (!text.startsWith("-") || whole && text === "-") {
|
|
44422
|
+
const option = !whole && text === "";
|
|
44423
|
+
if (option) {
|
|
44424
|
+
unread.push(arg);
|
|
44425
|
+
takesNext = true;
|
|
44426
|
+
}
|
|
44427
|
+
operands2.push({ arg, option, several });
|
|
44428
|
+
continue;
|
|
44429
|
+
}
|
|
44430
|
+
if (reopened)
|
|
44431
|
+
operands2.push({ arg, option: false, several });
|
|
44432
|
+
if (text.startsWith("--")) {
|
|
44433
|
+
const option = longOption(text, names);
|
|
44434
|
+
if (whole && !reopened && (option === "--help" || option === "--version")) {
|
|
44435
|
+
return { replaced: [], followed: [], inPlace: false, inferred: false };
|
|
44436
|
+
}
|
|
44437
|
+
if (option === "--in-place")
|
|
44438
|
+
inPlace = true;
|
|
44439
|
+
if (option === "--follow-symlinks")
|
|
44440
|
+
follow = true;
|
|
44441
|
+
if (option === "--expression" || option === "--file")
|
|
44442
|
+
expression = true;
|
|
44443
|
+
if (option === undefined && !whole)
|
|
44444
|
+
unread.push(arg);
|
|
44445
|
+
if ((option === undefined ? !whole : SED_OPTIONS[option]) && !text.includes("=")) {
|
|
44446
|
+
if (whole && !reopened)
|
|
44447
|
+
i += 1;
|
|
44448
|
+
else
|
|
44449
|
+
takesNext = true;
|
|
44450
|
+
}
|
|
44451
|
+
continue;
|
|
44452
|
+
}
|
|
44453
|
+
let settled = false;
|
|
44454
|
+
for (const [offset, letter] of [...text.slice(1)].entries()) {
|
|
44455
|
+
if (letter === "i") {
|
|
44456
|
+
inPlace = true;
|
|
44457
|
+
settled = true;
|
|
44458
|
+
break;
|
|
44459
|
+
}
|
|
44460
|
+
if ("efl".includes(letter)) {
|
|
44461
|
+
if (letter !== "l")
|
|
44462
|
+
expression = true;
|
|
44463
|
+
if (offset === text.length - 2) {
|
|
44464
|
+
if (whole && !reopened)
|
|
44465
|
+
i += 1;
|
|
44466
|
+
else
|
|
44467
|
+
takesNext = true;
|
|
44468
|
+
}
|
|
44469
|
+
settled = true;
|
|
44470
|
+
break;
|
|
44471
|
+
}
|
|
44472
|
+
}
|
|
44473
|
+
if (!settled && !whole) {
|
|
44474
|
+
unread.push(arg);
|
|
44475
|
+
takesNext = true;
|
|
44476
|
+
}
|
|
44477
|
+
}
|
|
44478
|
+
const unsettled = (settled) => settled ? 0 : 1;
|
|
44479
|
+
const replaced = [];
|
|
44480
|
+
const followed = [];
|
|
44481
|
+
let before = false;
|
|
44482
|
+
for (const operand of operands2) {
|
|
44483
|
+
const maybe = operand.option && !operand.several;
|
|
44484
|
+
const others = unread.length - (maybe ? 1 : 0);
|
|
44485
|
+
const needed = unsettled(inPlace || splitOption) + unsettled(expression || splitOption || operand.several || before);
|
|
44486
|
+
if (others >= needed + unsettled(follow || splitOption))
|
|
44487
|
+
followed.push(operand.arg);
|
|
44488
|
+
else if (others >= needed)
|
|
44489
|
+
replaced.push(operand.arg);
|
|
44490
|
+
if (!maybe)
|
|
44491
|
+
before = true;
|
|
44492
|
+
}
|
|
44493
|
+
return {
|
|
44494
|
+
replaced,
|
|
44495
|
+
followed,
|
|
44496
|
+
inPlace: inPlace || splitOption || unread.length > 0,
|
|
44497
|
+
inferred: !inPlace
|
|
44498
|
+
};
|
|
44499
|
+
}
|
|
43884
44500
|
function checkRecursiveMode(program, list, st, ctx, site) {
|
|
43885
44501
|
const flags = program === "chmod" ? "cfvR" : "cfvRhHLP";
|
|
43886
44502
|
const found = [];
|
|
@@ -43907,11 +44523,20 @@ function checkRecursiveMode(program, list, st, ctx, site) {
|
|
|
43907
44523
|
}
|
|
43908
44524
|
found.push(arg);
|
|
43909
44525
|
}
|
|
43910
|
-
if (!recursive)
|
|
43911
|
-
return;
|
|
43912
|
-
const targets = reference ? found : found.slice(1);
|
|
43913
44526
|
const verb = program === "chmod" ? "recursively change the mode of" : "recursively change the owner of";
|
|
43914
|
-
|
|
44527
|
+
if (recursive) {
|
|
44528
|
+
checkTargets(`${program} -R`, verb, reference ? found : found.slice(1), ACTS_THROUGH_LINK, st, ctx, site);
|
|
44529
|
+
return;
|
|
44530
|
+
}
|
|
44531
|
+
for (const [index, arg] of found.entries()) {
|
|
44532
|
+
if (!mayBeOption(arg, (text) => /^-[A-Za-z]*R/.test(text)))
|
|
44533
|
+
continue;
|
|
44534
|
+
const others = arg.fields === "unknown" ? found : found.filter((_, at) => at !== index);
|
|
44535
|
+
const targets = reference || arg.fields === "unknown" ? others : others.slice(1);
|
|
44536
|
+
if (targets.length === 0)
|
|
44537
|
+
continue;
|
|
44538
|
+
checkTargets(`${program} -R`, verb, targets, ACTS_THROUGH_LINK, st, ctx, site);
|
|
44539
|
+
}
|
|
43915
44540
|
}
|
|
43916
44541
|
function checkFind(list, st, ctx, site) {
|
|
43917
44542
|
let i = 0;
|
|
@@ -43926,17 +44551,27 @@ function checkFind(list, st, ctx, site) {
|
|
|
43926
44551
|
break;
|
|
43927
44552
|
}
|
|
43928
44553
|
const starts = [];
|
|
44554
|
+
let rootsBeforePredicate;
|
|
43929
44555
|
for (;i < list.length; i += 1) {
|
|
43930
|
-
const
|
|
44556
|
+
const arg = list[i];
|
|
44557
|
+
const text = literalText(arg.exp);
|
|
43931
44558
|
if (text !== undefined && (text.startsWith("-") || text === "(" || text === "!" || text === ")"))
|
|
43932
44559
|
break;
|
|
43933
|
-
|
|
44560
|
+
if (rootsBeforePredicate === undefined && mayBeOption(arg, (option) => option.startsWith("-"))) {
|
|
44561
|
+
rootsBeforePredicate = [...starts];
|
|
44562
|
+
}
|
|
44563
|
+
starts.push(arg);
|
|
43934
44564
|
}
|
|
43935
44565
|
const targets = starts.length > 0 ? starts : [{ text: ".", exp: [literal2(".")] }];
|
|
43936
44566
|
const expression = list.slice(i).map((arg) => literalText(arg.exp));
|
|
43937
44567
|
let action;
|
|
43938
44568
|
let shell;
|
|
44569
|
+
let consumedByPredicate = false;
|
|
43939
44570
|
for (const [index, word] of expression.entries()) {
|
|
44571
|
+
const isValue = consumedByPredicate;
|
|
44572
|
+
consumedByPredicate = word !== undefined && FIND_VALUED_PREDICATES.has(word);
|
|
44573
|
+
if (word === undefined && !isValue)
|
|
44574
|
+
action ??= "find with a predicate the guard cannot read";
|
|
43940
44575
|
if (word === "-delete")
|
|
43941
44576
|
action = "find -delete";
|
|
43942
44577
|
if (word !== "-exec" && word !== "-execdir" && word !== "-ok" && word !== "-okdir")
|
|
@@ -43946,16 +44581,22 @@ function checkFind(list, st, ctx, site) {
|
|
|
43946
44581
|
continue;
|
|
43947
44582
|
const inner = unwrap2(list.slice(i + index + 1, i + end), st, ctx, site);
|
|
43948
44583
|
const program = path4.basename(literalText(inner.argv[0]?.exp) ?? "");
|
|
43949
|
-
if (FILE_COMMANDS.has(program) || program === "xargs")
|
|
44584
|
+
if (FILE_COMMANDS.has(program) || program === "xargs" || program === "sed" && sedWrites(inner.argv.slice(1)).inPlace) {
|
|
43950
44585
|
action = `find ${word} ${program}`;
|
|
44586
|
+
}
|
|
43951
44587
|
if (SHELLS.has(program)) {
|
|
43952
44588
|
action = `find ${word} ${program}`;
|
|
43953
44589
|
shell = inner.argv;
|
|
43954
44590
|
}
|
|
43955
44591
|
}
|
|
43956
|
-
if (action === undefined)
|
|
44592
|
+
if (action === undefined) {
|
|
44593
|
+
if (rootsBeforePredicate === undefined)
|
|
44594
|
+
return;
|
|
44595
|
+
const under = rootsBeforePredicate.length > 0 ? rootsBeforePredicate : [{ text: ".", exp: [literal2(".")] }];
|
|
44596
|
+
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);
|
|
43957
44597
|
return;
|
|
43958
|
-
|
|
44598
|
+
}
|
|
44599
|
+
checkTargets(action, "delete or change what it finds under", targets, ACTS_ON_LINK, st, ctx, site);
|
|
43959
44600
|
if (shell === undefined)
|
|
43960
44601
|
return;
|
|
43961
44602
|
const found = targets.length === 1 ? targets[0]?.exp ?? [] : [unknown2("a path `find` found")];
|
|
@@ -43993,7 +44634,7 @@ function checkXargs(list, st, ctx, site) {
|
|
|
43993
44634
|
};
|
|
43994
44635
|
});
|
|
43995
44636
|
const program = path4.basename(literalText(argv[0]?.exp) ?? "");
|
|
43996
|
-
if (FILE_COMMANDS.has(program) || SIGNAL_COMMANDS.has(program)) {
|
|
44637
|
+
if (FILE_COMMANDS.has(program) || SIGNAL_COMMANDS.has(program) || program === "sed" && sedWrites(argv.slice(1)).inPlace) {
|
|
43997
44638
|
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`);
|
|
43998
44639
|
}
|
|
43999
44640
|
const overlay = new Map;
|
|
@@ -44231,7 +44872,7 @@ function runInterpreter(base, language, list, invocation, st, ctx) {
|
|
|
44231
44872
|
for (;i < words.length; i += 1) {
|
|
44232
44873
|
const word = words[i];
|
|
44233
44874
|
if (word === undefined)
|
|
44234
|
-
|
|
44875
|
+
continue;
|
|
44235
44876
|
if (codeFlags.includes(word)) {
|
|
44236
44877
|
runCode(language, list[i + 1], invocation, st, ctx, site);
|
|
44237
44878
|
return;
|