@sjawhar/pi-legion-envoy 5.17.3 → 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.
Files changed (2) hide show
  1. package/dist/legion.js +510 -47
  2. 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.3",
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
- if (!value.some((piece) => piece.noElements === true))
41439
- return value;
41440
- return value.map((piece) => piece.noElements === true ? literal2("") : piece);
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",
@@ -41804,8 +41816,10 @@ function resolveArrayIndex(index, st, ctx) {
41804
41816
  }
41805
41817
  function parameter(name, quoted, st, ctx) {
41806
41818
  if (name === "@" || name === "*") {
41807
- if (st.positional === undefined)
41808
- return [[unknown2(`\`$${name}\` (the positional parameters)`)]];
41819
+ if (st.positional === undefined) {
41820
+ const piece = unknown2(`\`$${name}\` (the positional parameters)`);
41821
+ return [[name === "*" && quoted ? piece : { ...piece, unknownCount: true }]];
41822
+ }
41809
41823
  return elements(st.positional, name === "*" && quoted);
41810
41824
  }
41811
41825
  if (/^[0-9]+$/.test(name)) {
@@ -41880,11 +41894,14 @@ function parameterExpansion(part, quoted, st, ctx) {
41880
41894
  if (part.index === "@" || part.index === "*") {
41881
41895
  if (array2 !== undefined) {
41882
41896
  const unknownElement = array2.get(UNKNOWN_ARRAY_INDEX);
41883
- if (unknownElement !== undefined)
41884
- return [[...unknownElement]];
41897
+ if (unknownElement !== undefined) {
41898
+ if (part.index === "*" && quoted)
41899
+ return [[...unknownElement]];
41900
+ return [unknownElement.map((piece) => ({ ...piece, unknownCount: true }))];
41901
+ }
41885
41902
  return elements([...array2.values()], part.index === "*" && quoted);
41886
41903
  }
41887
- 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);
41888
41905
  }
41889
41906
  if (part.index !== undefined) {
41890
41907
  const index = resolveArrayIndex(part.index, st, ctx);
@@ -42103,10 +42120,18 @@ function braceAlternatives(part) {
42103
42120
  return [[{ kind: "glob", text: part.text }]];
42104
42121
  return inner.split(",").map((alternative) => [literal2(alternative)]);
42105
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
+ }
42106
42131
  function expandPart(part, first, st, ctx) {
42107
42132
  switch (part.type) {
42108
42133
  case "Literal":
42109
- return [unquotedLiteral(part.text, first ? 0 : undefined, st, ctx)];
42134
+ return [unquotedLiteral(part.text, first ? tildeAt(part.text) : undefined, st, ctx)];
42110
42135
  case "SingleQuoted":
42111
42136
  case "AnsiCQuoted":
42112
42137
  return [[literal2(part.value)]];
@@ -42162,7 +42187,7 @@ function textLength(pieces) {
42162
42187
  function expandWord(word, st, ctx) {
42163
42188
  let alternatives = [[]];
42164
42189
  if (word.parts === undefined)
42165
- alternatives = [unquotedLiteral(word.text, 0, st, ctx)];
42190
+ alternatives = [unquotedLiteral(word.text, tildeAt(word.text), st, ctx)];
42166
42191
  else {
42167
42192
  word.parts.forEach((part, index) => {
42168
42193
  alternatives = product(alternatives, expandPart(part, index === 0, st, ctx));
@@ -42188,6 +42213,9 @@ function args(words, st, ctx) {
42188
42213
  if (exp.some((piece) => piece.kind === "glob")) {
42189
42214
  return { text: word.text, exp, fields: "unknown" };
42190
42215
  }
42216
+ if (exp.some((piece) => piece.unknownCount === true)) {
42217
+ return { text: word.text, exp, fields: "unknown" };
42218
+ }
42191
42219
  if (unquoted && exp.some((piece) => piece.kind === "unknown" || /[ \t\n]/.test(piece.text))) {
42192
42220
  return { text: word.text, exp, fields: "unknown" };
42193
42221
  }
@@ -42227,6 +42255,9 @@ function mayBeOption(arg, matches) {
42227
42255
  const { text, whole } = readableWord(arg);
42228
42256
  if (whole)
42229
42257
  return matches(text);
42258
+ return mayBeSomeOption(text);
42259
+ }
42260
+ function mayBeSomeOption(text) {
42230
42261
  return text === "" || /^--?[a-zA-Z-]*$/.test(text);
42231
42262
  }
42232
42263
  function valueInWord(arg, count) {
@@ -42682,6 +42713,15 @@ function spendPatternWork(work, ctx) {
42682
42713
  function outputChanged(before, after) {
42683
42714
  return before !== undefined && (after === undefined || JSON.stringify(before) !== JSON.stringify(after));
42684
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
+ }
42685
42725
  function walkNode(node, st, ctx, pipeIn) {
42686
42726
  if (++ctx.steps.count > MAX_WALK_STEPS) {
42687
42727
  const site = siteOf(node, st);
@@ -42805,11 +42845,7 @@ function walkNode(node, st, ctx, pipeIn) {
42805
42845
  }
42806
42846
  } else {
42807
42847
  const body = clone2(st);
42808
- const signalSafe = words.every((word) => {
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`)]);
42848
+ assignScalar(body, name, loopValue(node.type, name, words));
42813
42849
  if (node.type === "Select")
42814
42850
  assignScalar(body, "REPLY", [unknown2("`$REPLY`, read from input")]);
42815
42851
  uncertainly(ctx, () => walkList(node.body.commands, body, ctx));
@@ -43624,24 +43660,78 @@ function dispatch(invocation, outer, ctx) {
43624
43660
  return;
43625
43661
  case "rm":
43626
43662
  case "unlink":
43627
- checkTargets(base, "delete", operands(rest, "").operands, false, st, ctx, site);
43663
+ checkTargets(base, "delete", operands(rest, "").operands, ACTS_ON_LINK, st, ctx, site);
43628
43664
  return;
43629
43665
  case "mv": {
43630
- const found = operands(rest, "tS", ["--target-directory", "--suffix"]);
43631
- const targets = [...found.operands];
43632
- const at = rest.findIndex((arg) => {
43633
- const text = literalText(arg.exp);
43634
- return text === "-t" || text === "--target-directory";
43635
- });
43636
- if (at !== -1 && rest[at + 1] !== undefined)
43637
- targets.push(rest[at + 1]);
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 = [];
43638
43693
  for (const arg of rest) {
43639
- const text = literalText(arg.exp);
43640
- if (text?.startsWith("--target-directory=")) {
43641
- targets.push({ text: arg.text, exp: [literal2(text.slice(19))] });
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;
43642
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
+ });
43710
+ }
43711
+ }
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);
43643
43727
  }
43644
- checkTargets("mv", "move", targets, false, st, ctx, site);
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);
43645
43735
  return;
43646
43736
  }
43647
43737
  case "rsync": {
@@ -43663,9 +43753,9 @@ function dispatch(invocation, outer, ctx) {
43663
43753
  "--remote-option",
43664
43754
  "--rsync-path",
43665
43755
  "--temp-dir"
43666
- ]).operands.at(-1);
43756
+ ]).operands.filter(passesAnArgument).at(-1);
43667
43757
  if (destination !== undefined) {
43668
- checkTargets("rsync", "synchronize and delete into", [destination], true, st, ctx, site);
43758
+ checkTargets("rsync", "synchronize and delete into", [destination], ACTS_THROUGH_LINK, st, ctx, site);
43669
43759
  }
43670
43760
  return;
43671
43761
  }
@@ -43697,14 +43787,14 @@ function dispatch(invocation, outer, ctx) {
43697
43787
  targetDirectories.push(target);
43698
43788
  }
43699
43789
  }
43700
- checkTargets(base, "extract and overwrite in", targetDirectories, true, st, ctx, site);
43790
+ checkTargets(base, "extract and overwrite in", targetDirectories, ACTS_THROUGH_LINK, st, ctx, site);
43701
43791
  return;
43702
43792
  }
43703
43793
  case "shred":
43704
- checkTargets("shred", "shred", operands(rest, "ns", ["--iterations", "--size", "--random-source"]).operands, false, st, ctx, site);
43794
+ checkTargets("shred", "shred", operands(rest, "ns", ["--iterations", "--size", "--random-source"]).operands, ACTS_ON_BOTH, st, ctx, site);
43705
43795
  return;
43706
43796
  case "truncate":
43707
- checkTargets("truncate", "truncate", operands(rest, "sr", ["--size", "--reference"]).operands, true, st, ctx, site);
43797
+ checkTargets("truncate", "truncate", operands(rest, "sr", ["--size", "--reference"]).operands, ACTS_THROUGH_LINK, st, ctx, site);
43708
43798
  return;
43709
43799
  case "chmod":
43710
43800
  case "chown":
@@ -43778,8 +43868,8 @@ function dispatch(invocation, outer, ctx) {
43778
43868
  return false;
43779
43869
  const { text, whole } = readableWord(word);
43780
43870
  const compact = text.replace("-", "");
43781
- const reachable = commands.filter((command) => command.replace("-", "").startsWith(compact));
43782
- return whole ? reachable.length === 1 : reachable.length > 0;
43871
+ const normalized = commands.map((command) => command.replace("-", ""));
43872
+ return whole ? completeName(compact, normalized) !== undefined : normalized.some((command) => command.startsWith(compact));
43783
43873
  };
43784
43874
  let killing;
43785
43875
  for (const segment of segments) {
@@ -44023,11 +44113,50 @@ function nameAndValue(arg) {
44023
44113
  }
44024
44114
  return { name, value: undefined };
44025
44115
  }
44026
- function checkTargets(program, verb, targets, follow, st, ctx, site) {
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) {
44027
44152
  for (const target of targets) {
44028
- const verdict = judgePath(target.exp, st, ctx, { follow, overwrite: false });
44029
- if (!verdict.ok) {
44030
- throw refusal(site, `${program} would ${verb} \`${target.text}\` (${verdict.resolution})`, ctx);
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
+ }
44031
44160
  }
44032
44161
  const text = literalText(target.exp);
44033
44162
  if (text !== undefined && (text.startsWith("/") || st.cwd !== undefined)) {
@@ -44035,6 +44164,339 @@ function checkTargets(program, verb, targets, follow, st, ctx, site) {
44035
44164
  }
44036
44165
  }
44037
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
+ }
44038
44500
  function checkRecursiveMode(program, list, st, ctx, site) {
44039
44501
  const flags = program === "chmod" ? "cfvR" : "cfvRhHLP";
44040
44502
  const found = [];
@@ -44063,7 +44525,7 @@ function checkRecursiveMode(program, list, st, ctx, site) {
44063
44525
  }
44064
44526
  const verb = program === "chmod" ? "recursively change the mode of" : "recursively change the owner of";
44065
44527
  if (recursive) {
44066
- checkTargets(`${program} -R`, verb, reference ? found : found.slice(1), true, st, ctx, site);
44528
+ checkTargets(`${program} -R`, verb, reference ? found : found.slice(1), ACTS_THROUGH_LINK, st, ctx, site);
44067
44529
  return;
44068
44530
  }
44069
44531
  for (const [index, arg] of found.entries()) {
@@ -44073,7 +44535,7 @@ function checkRecursiveMode(program, list, st, ctx, site) {
44073
44535
  const targets = reference || arg.fields === "unknown" ? others : others.slice(1);
44074
44536
  if (targets.length === 0)
44075
44537
  continue;
44076
- checkTargets(`${program} -R`, verb, targets, true, st, ctx, site);
44538
+ checkTargets(`${program} -R`, verb, targets, ACTS_THROUGH_LINK, st, ctx, site);
44077
44539
  }
44078
44540
  }
44079
44541
  function checkFind(list, st, ctx, site) {
@@ -44119,8 +44581,9 @@ function checkFind(list, st, ctx, site) {
44119
44581
  continue;
44120
44582
  const inner = unwrap2(list.slice(i + index + 1, i + end), st, ctx, site);
44121
44583
  const program = path4.basename(literalText(inner.argv[0]?.exp) ?? "");
44122
- if (FILE_COMMANDS.has(program) || program === "xargs")
44584
+ if (FILE_COMMANDS.has(program) || program === "xargs" || program === "sed" && sedWrites(inner.argv.slice(1)).inPlace) {
44123
44585
  action = `find ${word} ${program}`;
44586
+ }
44124
44587
  if (SHELLS.has(program)) {
44125
44588
  action = `find ${word} ${program}`;
44126
44589
  shell = inner.argv;
@@ -44130,10 +44593,10 @@ function checkFind(list, st, ctx, site) {
44130
44593
  if (rootsBeforePredicate === undefined)
44131
44594
  return;
44132
44595
  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, false, st, ctx, site);
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);
44134
44597
  return;
44135
44598
  }
44136
- checkTargets(action, "delete or change what it finds under", targets, false, st, ctx, site);
44599
+ checkTargets(action, "delete or change what it finds under", targets, ACTS_ON_LINK, st, ctx, site);
44137
44600
  if (shell === undefined)
44138
44601
  return;
44139
44602
  const found = targets.length === 1 ? targets[0]?.exp ?? [] : [unknown2("a path `find` found")];
@@ -44171,7 +44634,7 @@ function checkXargs(list, st, ctx, site) {
44171
44634
  };
44172
44635
  });
44173
44636
  const program = path4.basename(literalText(argv[0]?.exp) ?? "");
44174
- 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) {
44175
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`);
44176
44639
  }
44177
44640
  const overlay = new Map;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "5.17.3",
3
+ "version": "5.17.4",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [