@warlock.js/ai-workspace 5.43.0 → 5.44.0
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/CHANGELOG.md +18 -0
- package/README.md +14 -1
- package/cjs/index.cjs +1781 -141
- package/cjs/index.cjs.map +1 -1
- package/esm/backends/local.d.mts.map +1 -1
- package/esm/backends/local.mjs +36 -22
- package/esm/backends/local.mjs.map +1 -1
- package/esm/contracts/index.d.mts +5 -1
- package/esm/contracts/shell-approval.type.d.mts +78 -0
- package/esm/contracts/shell-approval.type.d.mts.map +1 -0
- package/esm/contracts/tool-io.type.d.mts +35 -1
- package/esm/contracts/tool-io.type.d.mts.map +1 -1
- package/esm/contracts/workspace-backend.contract.d.mts +11 -1
- package/esm/contracts/workspace-backend.contract.d.mts.map +1 -1
- package/esm/contracts/workspace-edit.type.d.mts +66 -0
- package/esm/contracts/workspace-edit.type.d.mts.map +1 -0
- package/esm/contracts/workspace-ops.contract.d.mts +7 -1
- package/esm/contracts/workspace-ops.contract.d.mts.map +1 -1
- package/esm/contracts/workspace-output.type.d.mts +57 -0
- package/esm/contracts/workspace-output.type.d.mts.map +1 -0
- package/esm/contracts/workspace-policy.type.d.mts +38 -1
- package/esm/contracts/workspace-policy.type.d.mts.map +1 -1
- package/esm/contracts/workspace.contract.d.mts +11 -0
- package/esm/contracts/workspace.contract.d.mts.map +1 -1
- package/esm/edit/apply-replacement.mjs +58 -0
- package/esm/edit/apply-replacement.mjs.map +1 -0
- package/esm/edit/match.d.mts +16 -0
- package/esm/edit/match.d.mts.map +1 -0
- package/esm/edit/match.mjs +214 -0
- package/esm/edit/match.mjs.map +1 -0
- package/esm/errors.d.mts +12 -1
- package/esm/errors.d.mts.map +1 -1
- package/esm/errors.mjs +1 -0
- package/esm/errors.mjs.map +1 -1
- package/esm/hooks/after-edit.mjs +36 -0
- package/esm/hooks/after-edit.mjs.map +1 -0
- package/esm/index.d.mts +7 -2
- package/esm/index.mjs +3 -2
- package/esm/ops.d.mts +3 -1
- package/esm/ops.d.mts.map +1 -1
- package/esm/ops.mjs +196 -75
- package/esm/ops.mjs.map +1 -1
- package/esm/output/spill-dir.mjs +15 -0
- package/esm/output/spill-dir.mjs.map +1 -0
- package/esm/output/spill.mjs +220 -0
- package/esm/output/spill.mjs.map +1 -0
- package/esm/policy/policy.d.mts +3 -1
- package/esm/policy/policy.d.mts.map +1 -1
- package/esm/policy/policy.mjs +12 -4
- package/esm/policy/policy.mjs.map +1 -1
- package/esm/policy/powershell-aliases.mjs +275 -0
- package/esm/policy/powershell-aliases.mjs.map +1 -0
- package/esm/policy/powershell-parser.mjs +418 -0
- package/esm/policy/powershell-parser.mjs.map +1 -0
- package/esm/policy/shell-check.d.mts +32 -2
- package/esm/policy/shell-check.d.mts.map +1 -1
- package/esm/policy/shell-check.mjs +183 -31
- package/esm/policy/shell-check.mjs.map +1 -1
- package/esm/policy/shell-interpreter.d.mts +45 -0
- package/esm/policy/shell-interpreter.d.mts.map +1 -0
- package/esm/policy/shell-interpreter.mjs +109 -0
- package/esm/policy/shell-interpreter.mjs.map +1 -0
- package/esm/policy/shell-parser.d.mts +31 -0
- package/esm/policy/shell-parser.d.mts.map +1 -0
- package/esm/policy/shell-parser.mjs +3 -0
- package/esm/policy/shell-parser.mjs.map +1 -1
- package/esm/tools/glob.mjs +1 -0
- package/esm/tools/glob.mjs.map +1 -1
- package/esm/tools/run-shell.d.mts +10 -0
- package/esm/tools/run-shell.d.mts.map +1 -1
- package/esm/tools/run-shell.mjs +18 -1
- package/esm/tools/run-shell.mjs.map +1 -1
- package/esm/tools/run-tests.d.mts +7 -0
- package/esm/tools/run-tests.d.mts.map +1 -1
- package/esm/tools/run-tests.mjs +5 -4
- package/esm/tools/run-tests.mjs.map +1 -1
- package/esm/tools/write-file.mjs +3 -2
- package/esm/tools/write-file.mjs.map +1 -1
- package/esm/workspace.d.mts.map +1 -1
- package/esm/workspace.mjs +16 -5
- package/esm/workspace.mjs.map +1 -1
- package/llms-full.txt +64 -17
- package/package.json +3 -3
- package/skills/use-a-workspace/SKILL.md +64 -17
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { isInsideJailLexically } from "./jail.mjs";
|
|
2
|
+
import { POWERSHELL_CD_NAMES, POWERSHELL_DIRECTORY_STACK, canonicalPowerShellArgv, normalizePowerShellCd } from "./powershell-aliases.mjs";
|
|
3
|
+
import { currentShellHost, resolveShell } from "./shell-interpreter.mjs";
|
|
2
4
|
import { parseShellLine } from "./shell-parser.mjs";
|
|
3
5
|
import { analyzeArgvCommand, tokenizeCommand } from "./tokenize-command.mjs";
|
|
4
6
|
import path from "node:path";
|
|
@@ -53,6 +55,26 @@ const OPAQUE_RUNNERS = /* @__PURE__ */ new Set([
|
|
|
53
55
|
"stdbuf",
|
|
54
56
|
"flock"
|
|
55
57
|
]);
|
|
58
|
+
/**
|
|
59
|
+
* PowerShell cmdlets and aliases that run code or commands the policy cannot
|
|
60
|
+
* see, or change what later names mean (lowercase).
|
|
61
|
+
*/
|
|
62
|
+
const POWERSHELL_OPAQUE = /* @__PURE__ */ new Set([
|
|
63
|
+
"iex",
|
|
64
|
+
"invoke-expression",
|
|
65
|
+
"invoke-command",
|
|
66
|
+
"icm",
|
|
67
|
+
"start-process",
|
|
68
|
+
"saps",
|
|
69
|
+
"start",
|
|
70
|
+
"add-type",
|
|
71
|
+
"set-alias",
|
|
72
|
+
"sal",
|
|
73
|
+
"new-alias",
|
|
74
|
+
"nal",
|
|
75
|
+
"invoke-item",
|
|
76
|
+
"ii"
|
|
77
|
+
]);
|
|
56
78
|
/** POSIX shell builtins that evaluate code or change how later names resolve. */
|
|
57
79
|
const SHELL_OPAQUE_BUILTINS = /* @__PURE__ */ new Set([
|
|
58
80
|
"eval",
|
|
@@ -145,7 +167,7 @@ const MAX_WRAPPER_DEPTH = 8;
|
|
|
145
167
|
const MAX_CD_CANDIDATES = 32;
|
|
146
168
|
/** Longest command shown verbatim in a refusal before it is abbreviated. */
|
|
147
169
|
const MAX_SHOWN_COMMAND = 48;
|
|
148
|
-
/** The dialect
|
|
170
|
+
/** The dialect `interpreter: "sh"` parses on this platform. */
|
|
149
171
|
function shellDialect() {
|
|
150
172
|
return process.platform === "win32" ? "cmd" : "posix";
|
|
151
173
|
}
|
|
@@ -156,7 +178,7 @@ function shellDialect() {
|
|
|
156
178
|
* stripped). The cmd dialect splits Windows paths too.
|
|
157
179
|
*/
|
|
158
180
|
function executableBasename(firstToken, dialect = "posix") {
|
|
159
|
-
return (dialect === "cmd" ? path.win32.basename(firstToken) : path.basename(firstToken)).replace(/\.(exe|cmd|bat|com)$/i, "");
|
|
181
|
+
return (dialect === "cmd" || dialect === "powershell" ? path.win32.basename(firstToken) : path.basename(firstToken)).replace(dialect === "powershell" ? /\.(exe|cmd|bat|com|ps1)$/i : /\.(exe|cmd|bat|com)$/i, "");
|
|
160
182
|
}
|
|
161
183
|
/** Show a command in a refusal, abbreviated at a word boundary when long. */
|
|
162
184
|
function show(source) {
|
|
@@ -200,25 +222,75 @@ var Refusal = class extends Error {
|
|
|
200
222
|
};
|
|
201
223
|
/** Per-line checker state. */
|
|
202
224
|
var LineChecker = class {
|
|
203
|
-
constructor(policy, shell, line, dialect) {
|
|
225
|
+
constructor(policy, shell, line, dialect, interpreter, remembered = []) {
|
|
204
226
|
this.policy = policy;
|
|
205
227
|
this.shell = shell;
|
|
206
228
|
this.line = line;
|
|
207
229
|
this.dialect = dialect;
|
|
230
|
+
this.interpreter = interpreter;
|
|
208
231
|
this.commands = [];
|
|
209
232
|
this.paths = [];
|
|
233
|
+
this.asks = [];
|
|
210
234
|
this.deny = new Set((shell.deny ?? []).map((name) => name.toLowerCase()));
|
|
211
235
|
this.allow = shell.allow ?? [];
|
|
212
236
|
this.patterns = (shell.denyPatterns ?? []).map(compilePattern);
|
|
213
|
-
this.
|
|
214
|
-
|
|
237
|
+
this.rules = (shell.rules ?? []).map((rule) => ({
|
|
238
|
+
rule,
|
|
239
|
+
pattern: compilePattern(rule.match)
|
|
240
|
+
}));
|
|
241
|
+
this.remembered = remembered.map(compilePattern);
|
|
242
|
+
this.askMode = shell.onAsk !== void 0;
|
|
243
|
+
this.hasCommandRules = this.deny.size > 0 || this.patterns.length > 0 || this.rules.length > 0 || shell.allowCommand !== void 0;
|
|
215
244
|
}
|
|
216
245
|
/** Whether argument hooks (`denyPatterns` / `allowCommand`) need literal argv. */
|
|
217
246
|
get hasArgumentHooks() {
|
|
218
247
|
return this.patterns.length > 0 || this.shell.allowCommand !== void 0;
|
|
219
248
|
}
|
|
220
249
|
get shellMode() {
|
|
221
|
-
return this.interpreter
|
|
250
|
+
return this.interpreter !== "none";
|
|
251
|
+
}
|
|
252
|
+
/** The last rule that matches `argv` (later rules win). */
|
|
253
|
+
matchRule(argv) {
|
|
254
|
+
for (let index = this.rules.length - 1; index >= 0; index--) {
|
|
255
|
+
const compiled = this.rules[index];
|
|
256
|
+
if (compiled.pattern.test(argv, this.dialect)) return compiled.rule;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
isRemembered(argv) {
|
|
260
|
+
return this.remembered.some((pattern) => pattern.test(argv, this.dialect));
|
|
261
|
+
}
|
|
262
|
+
/** Record a command that needs a decision (once per argv and reason). */
|
|
263
|
+
addAsk(argv, reason, rule) {
|
|
264
|
+
const key = `${reason}\u0000${argv.join("\0")}`;
|
|
265
|
+
if (this.asks.some((ask) => `${ask.reason}\u0000${ask.argv.join("\0")}` === key)) return;
|
|
266
|
+
this.asks.push({
|
|
267
|
+
argv: [...argv],
|
|
268
|
+
reason,
|
|
269
|
+
...rule !== void 0 ? { rule } : {}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/** Refuse when the matching rule denies. */
|
|
273
|
+
assertNotDenied(argv, rule) {
|
|
274
|
+
if (rule?.effect !== "deny") return;
|
|
275
|
+
const explanation = rule.reason !== void 0 && rule.reason.trim() !== "" ? ` ${rule.reason.trim()}` : "";
|
|
276
|
+
throw new Refusal("denied-pattern", `${show(argv.join(" "))} is denied by a workspace shell rule.${explanation}`, executableBasename(argv[0] ?? "", this.dialect));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Check the canonical form of a PowerShell alias (`Remove-Item -Recurse
|
|
280
|
+
* -Force x` as `rm -rf x`): deny, `denyPatterns`, `allowCommand` and the
|
|
281
|
+
* rules, but not the `allow` list (which names the spelling that was written).
|
|
282
|
+
*/
|
|
283
|
+
checkCanonical(argv) {
|
|
284
|
+
const key = executableBasename(argv[0] ?? "", this.dialect).toLowerCase();
|
|
285
|
+
if (this.deny.has(key)) throw new Refusal("denied-executable", `\`${key}\` is denied by the workspace shell policy (in ${show(argv.join(" "))}).`, key);
|
|
286
|
+
this.checkHooks(argv, true);
|
|
287
|
+
const rule = this.matchRule(argv);
|
|
288
|
+
this.assertNotDenied(argv, rule);
|
|
289
|
+
if (rule?.effect === "ask" && !this.isRemembered(argv)) this.addAsk(argv, "rule", rule);
|
|
290
|
+
}
|
|
291
|
+
/** Refuse a denied executable (used for `cd` spellings). */
|
|
292
|
+
assertExecutableNotDenied(key, argv) {
|
|
293
|
+
if (this.deny.has(key)) throw new Refusal("denied-executable", `\`${key}\` is denied by the workspace shell policy (in ${show(argv.join(" "))}).`, key);
|
|
222
294
|
}
|
|
223
295
|
/** Run `denyPatterns` and `allowCommand` on one argv. */
|
|
224
296
|
checkHooks(argv, nested) {
|
|
@@ -266,15 +338,34 @@ var LineChecker = class {
|
|
|
266
338
|
throw new Refusal("not-allowed", `\`${executable}\` is not in the workspace shell allowlist. Allowed commands: ${listed}${this.allow.length > 20 ? ", …" : ""}.`, executable);
|
|
267
339
|
}
|
|
268
340
|
this.checkHooks(argv, depth > 0);
|
|
341
|
+
const rule = this.matchRule(argv);
|
|
342
|
+
this.assertNotDenied(argv, rule);
|
|
269
343
|
this.commands.push([...argv]);
|
|
270
|
-
|
|
344
|
+
const remembered = this.isRemembered(argv);
|
|
345
|
+
const approved = rule?.effect === "allow" || remembered || this.explicitlyAllowed(executable);
|
|
346
|
+
if (OPAQUE_RUNNERS.has(key) || this.shellMode && this.dialect === "posix" && SHELL_OPAQUE_BUILTINS.has(key) || this.shellMode && this.dialect === "cmd" && CMD_OPAQUE_BUILTINS.has(key) || this.shellMode && this.dialect === "powershell" && POWERSHELL_OPAQUE.has(key)) {
|
|
347
|
+
if (approved) return;
|
|
348
|
+
if (this.askMode || rule?.effect === "ask") {
|
|
349
|
+
this.addAsk(argv, "nested", rule);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
271
352
|
this.checkOpaque(executable, argv);
|
|
272
353
|
return;
|
|
273
354
|
}
|
|
274
355
|
if (this.shellMode && this.dialect === "posix" && ARITHMETIC_SENSITIVE_BUILTINS.has(key)) this.checkArithmeticSensitive(executable, argv, dynamic);
|
|
275
356
|
const ranges = this.unwrap(key, argv);
|
|
357
|
+
if (!approved && ranges === void 0) {
|
|
358
|
+
if (rule?.effect === "ask") this.addAsk(argv, "rule", rule);
|
|
359
|
+
else if (rule === void 0 && this.askMode && this.allow.length === 0) this.addAsk(argv, "rule");
|
|
360
|
+
} else if (!approved && rule?.effect === "ask") this.addAsk(argv, "rule", rule);
|
|
276
361
|
if (ranges === void 0) return;
|
|
277
|
-
if (ranges === null || depth >= MAX_WRAPPER_DEPTH)
|
|
362
|
+
if (ranges === null || depth >= MAX_WRAPPER_DEPTH) {
|
|
363
|
+
if (this.askMode) {
|
|
364
|
+
this.addAsk(argv, "nested", rule);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
throw new Refusal("nested-command", `\`${executable}\` is used with options the workspace shell policy cannot read (in ${show(argv.join(" "))}). Run the inner command directly instead.`, executable);
|
|
368
|
+
}
|
|
278
369
|
for (const [start, end] of ranges) this.checkArgv(argv.slice(start, end), dynamic.slice(start, end), depth + 1);
|
|
279
370
|
}
|
|
280
371
|
/**
|
|
@@ -340,6 +431,7 @@ var LineChecker = class {
|
|
|
340
431
|
}
|
|
341
432
|
/** Check a redirection target from every possible cwd. */
|
|
342
433
|
checkRedirectTarget(word, access, cwds) {
|
|
434
|
+
if (this.dialect === "powershell" && !word.quoted && word.value.toLowerCase() === "$null") return;
|
|
343
435
|
if (word.dynamic) throw new Refusal("unsupported-syntax", `The redirection target \`${word.raw}\` must be a literal path (no variables, globs or \`~\`).`);
|
|
344
436
|
const value = word.value;
|
|
345
437
|
if (this.dialect === "posix" && DEVICE_TARGETS.has(value) || this.dialect === "cmd" && CMD_DEVICE_TARGETS.has(value.toLowerCase())) return;
|
|
@@ -532,11 +624,17 @@ function argvModeRefusal(command) {
|
|
|
532
624
|
if (token === "(" || token === ")") return new Refusal("metacharacter", `\`${token}\` is not supported in argv mode: put it inside quotes to pass it as text.${suffix}`);
|
|
533
625
|
return new Refusal("metacharacter", `\`${token === "\n" ? "\\n" : token}\` is not supported in argv mode: run one command per call, and quote any argument that contains it.${suffix}`);
|
|
534
626
|
}
|
|
535
|
-
/** Check a shell-mode
|
|
627
|
+
/** Check a shell-mode line. */
|
|
536
628
|
function checkShellLine(checker, policy, command, dialect) {
|
|
537
629
|
if (/^[-+]/.test(command)) throw new Refusal("unsupported-syntax", "A command line may not start with `-` or `+`.");
|
|
538
630
|
const parsed = parseShellLine(command, dialect);
|
|
539
|
-
if (!parsed.ok)
|
|
631
|
+
if (!parsed.ok) {
|
|
632
|
+
if (checker.askMode) {
|
|
633
|
+
checker.addAsk([command], "unparsed");
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
throw new Refusal(parsed.reason, `${parsed.message} The workspace shell policy only runs lines it can check.`);
|
|
637
|
+
}
|
|
540
638
|
if (parsed.background) throw new Refusal("unsupported-syntax", "`&` (run in the background) is not supported in a command line: start a long-running command with background: true instead.");
|
|
541
639
|
let cwds = [path.resolve(policy.cwd)];
|
|
542
640
|
for (const simple of parsed.commands) cwds = checkSimpleCommand(checker, simple, cwds, dialect);
|
|
@@ -550,16 +648,38 @@ function checkSimpleCommand(checker, simple, cwds, dialect) {
|
|
|
550
648
|
for (const redirect of simple.redirects) if (redirect.type === "read" || redirect.type === "write") checker.checkRedirectTarget(redirect.target, redirect.type, cwds);
|
|
551
649
|
const first = simple.words[0];
|
|
552
650
|
if (first === void 0) return cwds;
|
|
553
|
-
if (!first.dynamic && first.value.includes("=")) throw new Refusal("unsupported-syntax", `\`${first.raw}\` is not a plain command name or \`NAME=value\` assignment.`);
|
|
554
|
-
if (/^[-+]/.test(first.value)) throw new Refusal("unsupported-syntax", `\`${first.raw}\` is not a command name.`);
|
|
555
|
-
if (checker.hasArgumentHooks) {
|
|
556
|
-
const dynamic = simple.words.find((word) => word.dynamic);
|
|
557
|
-
if (dynamic !== void 0) throw new Refusal("unsupported-syntax", `\`${dynamic.raw}\` is decided when the line runs, so it cannot be checked against the workspace shell policy's command rules. Write the value literally (quote globs).`);
|
|
558
|
-
}
|
|
559
651
|
const executable = first.dynamic ? "" : executableBasename(first.value, dialect).toLowerCase();
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
|
|
652
|
+
const powershell = dialect === "powershell";
|
|
653
|
+
if (executable === "cd" || powershell && POWERSHELL_CD_NAMES.has(executable)) {
|
|
654
|
+
checker.assertExecutableNotDenied(executable, simple.words.map((word) => word.value));
|
|
655
|
+
if (powershell) {
|
|
656
|
+
const words = normalizePowerShellCd(simple.words);
|
|
657
|
+
if (words === null) throw new Refusal("unsupported-syntax", "`Set-Location` is supported with one directory (`-Path` / `-LiteralPath` or positional) only.", "cd");
|
|
658
|
+
return checker.checkCd(words, cwds);
|
|
659
|
+
}
|
|
660
|
+
return checker.checkCd(simple.words, cwds);
|
|
661
|
+
}
|
|
662
|
+
try {
|
|
663
|
+
if (!first.dynamic && first.value.includes("=")) throw new Refusal("unsupported-syntax", `\`${first.raw}\` is not a plain command name or \`NAME=value\` assignment.`);
|
|
664
|
+
if (/^[-+]/.test(first.value)) throw new Refusal("unsupported-syntax", `\`${first.raw}\` is not a command name.`);
|
|
665
|
+
if (checker.hasArgumentHooks) {
|
|
666
|
+
const dynamic = simple.words.find((word) => word.dynamic);
|
|
667
|
+
if (dynamic !== void 0) throw new Refusal("unsupported-syntax", `\`${dynamic.raw}\` is decided when the line runs, so it cannot be checked against the workspace shell policy's command rules. Write the value literally (quote globs).`);
|
|
668
|
+
}
|
|
669
|
+
if (dialect === "posix" && (executable === "pushd" || executable === "popd") || powershell && POWERSHELL_DIRECTORY_STACK.has(executable)) throw new Refusal("unsupported-syntax", `\`${executable}\` is not supported; use \`${powershell ? "Set-Location" : "cd"}\`.`);
|
|
670
|
+
const argv = simple.words.map((word) => word.dynamic ? word.raw : word.value);
|
|
671
|
+
checker.checkArgv(argv, simple.words.map((word) => word.dynamic), 0);
|
|
672
|
+
if (powershell) {
|
|
673
|
+
const canonical = canonicalPowerShellArgv(argv);
|
|
674
|
+
if (canonical !== void 0 && canonical.join("\0") !== argv.join("\0")) checker.checkCanonical(canonical);
|
|
675
|
+
}
|
|
676
|
+
} catch (error) {
|
|
677
|
+
if (error instanceof Refusal && error.reason === "unsupported-syntax" && checker.askMode) {
|
|
678
|
+
checker.addAsk(simple.words.map((word) => word.dynamic ? word.raw : word.value), "unparsed");
|
|
679
|
+
return cwds;
|
|
680
|
+
}
|
|
681
|
+
throw error;
|
|
682
|
+
}
|
|
563
683
|
return cwds;
|
|
564
684
|
}
|
|
565
685
|
/**
|
|
@@ -587,35 +707,44 @@ function checkSimpleCommand(checker, simple, cwds, dialect) {
|
|
|
587
707
|
* checkShellCommand({ cwd, shell: { allow: ["git"] } }, "git add -A && git commit -m wip");
|
|
588
708
|
* // { allowed: false, reason: "metacharacter", message: "`&&` is not supported in argv mode: run `git add -A`, then `git commit -m wip`, …" }
|
|
589
709
|
*/
|
|
590
|
-
function checkShellCommand(policy, command) {
|
|
591
|
-
|
|
710
|
+
function checkShellCommand(policy, command, options) {
|
|
711
|
+
const resolved = options?.resolved ?? resolveShell(policy.shell, options?.host ?? currentShellHost());
|
|
712
|
+
return runCheck(policy, command, resolved.interpreter, resolved.dialect, options?.remembered);
|
|
592
713
|
}
|
|
593
|
-
|
|
594
|
-
* {@link checkShellCommand} with the shell-mode dialect chosen explicitly
|
|
595
|
-
* instead of from the platform (used to test the cmd.exe rules anywhere).
|
|
596
|
-
* Argv mode ignores `dialect`.
|
|
597
|
-
*/
|
|
598
|
-
function checkShellCommandInDialect(policy, command, dialect) {
|
|
714
|
+
function runCheck(policy, command, interpreter, dialect, remembered) {
|
|
599
715
|
const shell = policy.shell;
|
|
600
716
|
if (!shell) return {
|
|
601
717
|
allowed: false,
|
|
602
718
|
reason: "not-allowed",
|
|
603
719
|
message: "The workspace has no shell policy, so no command may run.",
|
|
720
|
+
asks: [],
|
|
604
721
|
commands: [],
|
|
605
722
|
paths: []
|
|
606
723
|
};
|
|
607
|
-
const
|
|
608
|
-
const checker = new LineChecker(policy, shell, command,
|
|
724
|
+
const shellMode = interpreter !== "none";
|
|
725
|
+
const checker = new LineChecker(policy, shell, command, shellMode ? dialect : "posix", interpreter, remembered);
|
|
609
726
|
try {
|
|
610
727
|
if (command.trim() === "") throw new Refusal("not-allowed", "The command is empty.");
|
|
611
|
-
if (
|
|
728
|
+
if (shellMode) checkShellLine(checker, policy, command, dialect);
|
|
612
729
|
else {
|
|
613
730
|
const argv = tokenizeCommand(command);
|
|
614
731
|
if (argv === null) throw argvModeRefusal(command);
|
|
615
732
|
checker.checkArgv(argv, argv.map(() => false), 0);
|
|
616
733
|
}
|
|
734
|
+
if (checker.asks.length > 0) {
|
|
735
|
+
const first = checker.asks[0];
|
|
736
|
+
return {
|
|
737
|
+
allowed: false,
|
|
738
|
+
reason: "approval-required",
|
|
739
|
+
message: `${show(first.argv.join(" "))} needs approval, and the workspace shell policy has no approval handler (shell.onAsk).${first.rule?.reason !== void 0 ? ` ${first.rule.reason}` : ""}`,
|
|
740
|
+
asks: checker.asks,
|
|
741
|
+
commands: checker.commands,
|
|
742
|
+
paths: checker.paths
|
|
743
|
+
};
|
|
744
|
+
}
|
|
617
745
|
return {
|
|
618
746
|
allowed: true,
|
|
747
|
+
asks: [],
|
|
619
748
|
commands: checker.commands,
|
|
620
749
|
paths: checker.paths
|
|
621
750
|
};
|
|
@@ -627,12 +756,35 @@ function checkShellCommandInDialect(policy, command, dialect) {
|
|
|
627
756
|
message: error.message,
|
|
628
757
|
...error.executable !== void 0 ? { executable: error.executable } : {},
|
|
629
758
|
...error.refusedPath !== void 0 ? { path: error.refusedPath } : {},
|
|
759
|
+
asks: [],
|
|
630
760
|
commands: checker.commands,
|
|
631
761
|
paths: checker.paths
|
|
632
762
|
};
|
|
633
763
|
}
|
|
634
764
|
}
|
|
765
|
+
/**
|
|
766
|
+
* The simple commands a line would run (pipeline members, `&&` / `||` / `;`
|
|
767
|
+
* parts, subshells and substitutions included), without checking anything.
|
|
768
|
+
* A line the parser cannot read with confidence comes back with `opaque`
|
|
769
|
+
* set to the reason and no commands.
|
|
770
|
+
*
|
|
771
|
+
* The line is read with the policy's shell dialect when its `interpreter`
|
|
772
|
+
* is a shell, and with the platform's default shell otherwise.
|
|
773
|
+
*
|
|
774
|
+
* @example
|
|
775
|
+
* classifyShellCommand("a | b"); // { commands: [["a"], ["b"]] }
|
|
776
|
+
*/
|
|
777
|
+
function classifyShellCommand(command, policy, host) {
|
|
778
|
+
const resolved = resolveShell(policy?.shell, host ?? currentShellHost());
|
|
779
|
+
const dialect = resolved.interpreter === "none" ? shellDialect() : resolved.dialect;
|
|
780
|
+
const parsed = parseShellLine(command, dialect);
|
|
781
|
+
if (!parsed.ok) return {
|
|
782
|
+
commands: [],
|
|
783
|
+
opaque: parsed.message
|
|
784
|
+
};
|
|
785
|
+
return { commands: parsed.commands.filter((simple) => simple.words.length > 0).map((simple) => simple.words.map((word) => word.dynamic ? word.raw : word.value)) };
|
|
786
|
+
}
|
|
635
787
|
|
|
636
788
|
//#endregion
|
|
637
|
-
export { checkShellCommand,
|
|
789
|
+
export { checkShellCommand, classifyShellCommand, executableBasename, shellDialect };
|
|
638
790
|
//# sourceMappingURL=shell-check.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-check.mjs","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/policy/shell-check.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { WorkspaceShellRefusalReason } from \"../errors\";\nimport type {\n WorkspaceCommandPattern,\n WorkspacePolicy,\n WorkspaceShellInterpreter,\n WorkspaceShellPolicy,\n} from \"../contracts\";\nimport { isInsideJailLexically } from \"./jail\";\nimport {\n parseShellLine,\n type ShellDialect,\n type ShellSegment,\n type ShellSimpleCommand,\n type ShellWord,\n} from \"./shell-parser\";\nimport { analyzeArgvCommand, tokenizeCommand } from \"./tokenize-command\";\n\n/** How a shell-mode line touches a path the jail has to vet. */\nexport type WorkspaceShellPathAccess = \"read\" | \"write\" | \"cwd\";\n\n/** A path a shell-mode line redirects to/from or `cd`s into. */\nexport interface WorkspaceShellPath {\n /** Absolute path, resolved lexically against the workspace root (or an earlier `cd`). */\n path: string;\n /** What the line does with it. */\n access: WorkspaceShellPathAccess;\n /** The target as written in the line. */\n target: string;\n}\n\n/**\n * The shell policy's verdict on one command line — see\n * {@link checkShellCommand}.\n */\nexport interface WorkspaceShellCheck {\n /** Whether the line may run (path targets still get a symlink-aware recheck in `exec`). */\n allowed: boolean;\n /** Why it was refused. */\n reason?: WorkspaceShellRefusalReason;\n /** Model-facing refusal text: the cause, and how to fix the command. */\n message?: string;\n /** The executable the refusal is about, when there is one. */\n executable?: string;\n /** The redirection target or `cd` directory a `\"path-escape\"` refusal is about. */\n path?: string;\n /** The argv of every simple command checked, in order (wrapped commands included). */\n commands: string[][];\n /** Shell mode: every redirection target and `cd` directory, for the jail recheck. */\n paths: WorkspaceShellPath[];\n}\n\n/**\n * Programs and shell builtins that run a command or code the policy cannot\n * see (a script string, a file, a command read from input, another user).\n * With deny rules in force they would hide a denied command, so they are\n * refused unless the policy names them in `allow`.\n */\nconst OPAQUE_RUNNERS = new Set([\n // Shells and interpreters of command strings.\n \"sh\",\n \"bash\",\n \"dash\",\n \"zsh\",\n \"ksh\",\n \"mksh\",\n \"ash\",\n \"yash\",\n \"posh\",\n \"fish\",\n \"csh\",\n \"tcsh\",\n \"busybox\",\n \"cmd\",\n \"powershell\",\n \"pwsh\",\n \"wsl\",\n // Run a command as someone else, elsewhere, or out of the process group.\n \"sudo\",\n \"doas\",\n \"su\",\n \"runuser\",\n \"pkexec\",\n \"runas\",\n \"chroot\",\n \"nsenter\",\n \"unshare\",\n \"setsid\",\n \"systemd-run\",\n \"start-stop-daemon\",\n \"daemonize\",\n // Run commands built from input or on a schedule.\n \"xargs\",\n \"parallel\",\n \"watch\",\n \"script\",\n \"expect\",\n \"at\",\n \"batch\",\n \"screen\",\n \"tmux\",\n \"stdbuf\",\n \"flock\",\n]);\n\n/** POSIX shell builtins that evaluate code or change how later names resolve. */\nconst SHELL_OPAQUE_BUILTINS = new Set([\n \"eval\",\n \".\",\n \"source\",\n \"alias\",\n \"unalias\",\n \"hash\",\n \"enable\",\n \"trap\",\n \"let\",\n \"declare\",\n \"typeset\",\n \"local\",\n \"set\",\n \"shopt\",\n \"fc\",\n \"bind\",\n \"complete\",\n \"compgen\",\n \"compopt\",\n \"mapfile\",\n \"readarray\",\n]);\n\n/** cmd.exe builtins that run commands or change the environment. */\nconst CMD_OPAQUE_BUILTINS = new Set([\"call\", \"start\", \"set\", \"pushd\", \"popd\"]);\n\n/**\n * Shell builtins whose arguments bash may evaluate as arithmetic (array\n * subscripts such as `a[$(cmd)]`): their arguments must be literal and free\n * of `[`.\n */\nconst ARITHMETIC_SENSITIVE_BUILTINS = new Set([\n \"test\",\n \"[\",\n \"printf\",\n \"read\",\n \"unset\",\n \"export\",\n \"readonly\",\n \"getopts\",\n \"wait\",\n \"kill\",\n \"shift\",\n \"return\",\n \"exit\",\n \"break\",\n \"continue\",\n \"ulimit\",\n]);\n\n/**\n * Variables a shell-mode line may not assign: they change how the shell\n * parses, traces or resolves what follows (`CDPATH` would move `cd` out of\n * the jail's sight, `PS4` is expanded — command substitutions included —\n * on every traced line, `IFS` re-splits expansions).\n */\nconst SENSITIVE_VARIABLES = new Set([\n \"CDPATH\",\n \"ENV\",\n \"BASH_ENV\",\n \"SHELLOPTS\",\n \"BASHOPTS\",\n \"IFS\",\n \"PS1\",\n \"PS2\",\n \"PS3\",\n \"PS4\",\n \"PROMPT_COMMAND\",\n \"BASH_XTRACEFD\",\n \"GLOBIGNORE\",\n \"EXECIGNORE\",\n \"BASH_LOADABLES_PATH\",\n]);\n\n/** Redirection targets that are always fine even though they sit outside the jail. */\nconst DEVICE_TARGETS = new Set([\"/dev/null\", \"/dev/stdout\", \"/dev/stderr\"]);\n\n/** cmd.exe's null device. */\nconst CMD_DEVICE_TARGETS = new Set([\"nul\"]);\n\n/** Deepest chain of wrappers (`env time nice …`) the policy unwraps. */\nconst MAX_WRAPPER_DEPTH = 8;\n\n/** Most distinct directories a line may `cd` between. */\nconst MAX_CD_CANDIDATES = 32;\n\n/** Longest command shown verbatim in a refusal before it is abbreviated. */\nconst MAX_SHOWN_COMMAND = 48;\n\n/** The dialect shell mode parses on this platform. */\nexport function shellDialect(): ShellDialect {\n return process.platform === \"win32\" ? \"cmd\" : \"posix\";\n}\n\n/**\n * Reduce an argv's first element to the basename the allow/deny policy is\n * keyed on. `\"npm\"` → `\"npm\"`; `\"/usr/bin/node\"` → `\"node\"`; `\"node.exe\"`\n * → `\"node\"` (the `.exe`/`.cmd`/`.bat`/`.com` Windows extension is\n * stripped). The cmd dialect splits Windows paths too.\n */\nexport function executableBasename(firstToken: string, dialect: ShellDialect = \"posix\"): string {\n const base = dialect === \"cmd\" ? path.win32.basename(firstToken) : path.basename(firstToken);\n\n return base.replace(/\\.(exe|cmd|bat|com)$/i, \"\");\n}\n\n/** Show a command in a refusal, abbreviated at a word boundary when long. */\nfunction show(source: string): string {\n const flat = source.replace(/\\s+/g, \" \").trim();\n\n if (flat.length <= MAX_SHOWN_COMMAND) {\n return `\\`${flat}\\``;\n }\n\n const cut = flat.lastIndexOf(\" \", MAX_SHOWN_COMMAND);\n\n return `\\`${flat.slice(0, cut > 0 ? cut : MAX_SHOWN_COMMAND)} …\\``;\n}\n\n/** Compile `denyPatterns` once per check. */\ninterface CompiledPattern {\n source: WorkspaceCommandPattern;\n test(argv: string[], dialect: ShellDialect): boolean;\n}\n\nfunction compilePattern(pattern: WorkspaceCommandPattern): CompiledPattern {\n if (pattern instanceof RegExp) {\n const regex = new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, \"\"));\n\n return {\n source: pattern,\n test: (argv, dialect) =>\n regex.test([executableBasename(argv[0] ?? \"\", dialect), ...argv.slice(1)].join(\" \")),\n };\n }\n\n const tokens = tokenizeCommand(pattern);\n\n if (tokens === null) {\n // Not representable as an argv: match it as text inside the joined argv.\n return {\n source: pattern,\n test: (argv, dialect) =>\n [executableBasename(argv[0] ?? \"\", dialect), ...argv.slice(1)].join(\" \").includes(pattern),\n };\n }\n\n return {\n source: pattern,\n test: (argv, dialect) => {\n if (argv.length < tokens.length) {\n return false;\n }\n\n return tokens.every((token, index) => {\n const actual = argv[index] ?? \"\";\n\n return index === 0\n ? executableBasename(actual, dialect).toLowerCase() ===\n executableBasename(token, dialect).toLowerCase()\n : actual === token;\n });\n },\n };\n}\n\n/** Thrown inside the checker to stop at the first refusal. */\nclass Refusal extends Error {\n public constructor(\n public readonly reason: WorkspaceShellRefusalReason,\n message: string,\n public readonly executable?: string,\n public readonly refusedPath?: string,\n ) {\n super(message);\n }\n}\n\n/** Per-line checker state. */\nclass LineChecker {\n public readonly commands: string[][] = [];\n\n public readonly paths: WorkspaceShellPath[] = [];\n\n private readonly deny: Set<string>;\n\n private readonly allow: string[];\n\n private readonly patterns: CompiledPattern[];\n\n private readonly hasCommandRules: boolean;\n\n private readonly interpreter: WorkspaceShellInterpreter;\n\n public constructor(\n private readonly policy: WorkspacePolicy,\n private readonly shell: WorkspaceShellPolicy,\n private readonly line: string,\n private readonly dialect: ShellDialect,\n ) {\n this.deny = new Set((shell.deny ?? []).map((name) => name.toLowerCase()));\n this.allow = shell.allow ?? [];\n this.patterns = (shell.denyPatterns ?? []).map(compilePattern);\n this.interpreter = shell.interpreter ?? \"none\";\n this.hasCommandRules =\n this.deny.size > 0 || this.patterns.length > 0 || shell.allowCommand !== undefined;\n }\n\n /** Whether argument hooks (`denyPatterns` / `allowCommand`) need literal argv. */\n public get hasArgumentHooks(): boolean {\n return this.patterns.length > 0 || this.shell.allowCommand !== undefined;\n }\n\n private get shellMode(): boolean {\n return this.interpreter === \"sh\";\n }\n\n /** Run `denyPatterns` and `allowCommand` on one argv. */\n private checkHooks(argv: string[], nested: boolean): void {\n const shown = show(argv.join(\" \"));\n\n for (const pattern of this.patterns) {\n if (pattern.test(argv, this.dialect)) {\n throw new Refusal(\n \"denied-pattern\",\n `${shown} matches a command pattern the workspace shell policy denies.`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n }\n\n const hook = this.shell.allowCommand;\n\n if (hook === undefined) {\n return;\n }\n\n let verdict: boolean | string;\n\n try {\n verdict = hook([...argv], { command: this.line, interpreter: this.interpreter, nested });\n } catch {\n verdict = false;\n }\n\n if (verdict === true) {\n return;\n }\n\n const explanation =\n typeof verdict === \"string\" && verdict.trim() !== \"\" ? ` ${verdict.trim()}` : \"\";\n\n throw new Refusal(\n \"not-allowed\",\n `${shown} was refused by the workspace shell policy.${explanation}`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n\n /** Whether the policy names `executable` in a non-empty `allow` list. */\n private explicitlyAllowed(executable: string): boolean {\n return this.allow.includes(executable);\n }\n\n /** Refuse a command runner the policy cannot see into, unless explicitly allowed. */\n private checkOpaque(executable: string, argv: string[]): void {\n if (this.explicitlyAllowed(executable) || !this.hasCommandRules) {\n return;\n }\n\n throw new Refusal(\n \"nested-command\",\n `\\`${executable}\\` runs a command the workspace shell policy cannot inspect (in ${show(argv.join(\" \"))}). Run the inner command directly instead.`,\n executable,\n );\n }\n\n /**\n * Check one argv: `deny`, `allow`, `denyPatterns`, `allowCommand`, then\n * follow wrappers and `find -exec` to the command they run. `dynamic[i]`\n * says whether `argv[i]` is decided at run time (shell mode).\n */\n public checkArgv(argv: string[], dynamic: boolean[], depth: number): void {\n const first = argv[0] ?? \"\";\n const executable = executableBasename(first, this.dialect);\n const key = executable.toLowerCase();\n\n if (executable === \"\") {\n throw new Refusal(\"not-allowed\", \"The command has no executable name.\");\n }\n\n if (dynamic[0] === true) {\n throw new Refusal(\n \"unsupported-syntax\",\n `The command name \\`${first}\\` is decided when the line runs, so the workspace shell policy cannot check it. Write the command name literally.`,\n );\n }\n\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${executable}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n executable,\n );\n }\n\n if (this.allow.length > 0 && !this.allow.includes(executable)) {\n const listed = this.allow.slice(0, 20).join(\", \");\n\n throw new Refusal(\n \"not-allowed\",\n `\\`${executable}\\` is not in the workspace shell allowlist. Allowed commands: ${listed}${this.allow.length > 20 ? \", …\" : \"\"}.`,\n executable,\n );\n }\n\n this.checkHooks(argv, depth > 0);\n this.commands.push([...argv]);\n\n if (\n OPAQUE_RUNNERS.has(key) ||\n (this.shellMode && this.dialect === \"posix\" && SHELL_OPAQUE_BUILTINS.has(key)) ||\n (this.shellMode && this.dialect === \"cmd\" && CMD_OPAQUE_BUILTINS.has(key))\n ) {\n this.checkOpaque(executable, argv);\n\n return;\n }\n\n if (this.shellMode && this.dialect === \"posix\" && ARITHMETIC_SENSITIVE_BUILTINS.has(key)) {\n this.checkArithmeticSensitive(executable, argv, dynamic);\n }\n\n const ranges = this.unwrap(key, argv);\n\n if (ranges === undefined) {\n return;\n }\n\n if (ranges === null || depth >= MAX_WRAPPER_DEPTH) {\n throw new Refusal(\n \"nested-command\",\n `\\`${executable}\\` is used with options the workspace shell policy cannot read (in ${show(argv.join(\" \"))}). Run the inner command directly instead.`,\n executable,\n );\n }\n\n for (const [start, end] of ranges) {\n this.checkArgv(argv.slice(start, end), dynamic.slice(start, end), depth + 1);\n }\n }\n\n /**\n * Builtins bash may evaluate arguments of as arithmetic (array subscripts\n * like `a[$(cmd)]` run the substitution): arguments must be literal, free\n * of `[`, and must not name a variable that changes how the shell runs.\n */\n private checkArithmeticSensitive(executable: string, argv: string[], dynamic: boolean[]): void {\n for (let index = 1; index < argv.length; index++) {\n const argument = argv[index] ?? \"\";\n\n if (dynamic[index] === true) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` arguments must be literal here (\\`${argument}\\` is not): bash may evaluate them as arithmetic.`,\n executable,\n );\n }\n\n if (argument.includes(\"[\") && argument !== \"[\" && argument !== \"]\") {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` arguments with \\`[\\` (array subscripts, evaluated as arithmetic) are not supported.`,\n executable,\n );\n }\n\n const name = argument.split(\"=\")[0] ?? \"\";\n\n if (SENSITIVE_VARIABLES.has(name) || name.startsWith(\"BASH_FUNC_\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `Changing \\`${name}\\` is not supported: it changes how the shell runs the commands that follow.`,\n executable,\n );\n }\n }\n }\n\n /**\n * The argv ranges a wrapper runs. `undefined` — not a wrapper (or one that\n * runs nothing, like a bare `env`); `null` — a wrapper whose options the\n * policy cannot read (refused).\n */\n private unwrap(key: string, argv: string[]): Array<[number, number]> | null | undefined {\n switch (key) {\n case \"env\":\n return suffixFrom(argv, unwrapEnv(argv));\n case \"nice\":\n return suffixFrom(argv, unwrapNice(argv));\n case \"nohup\":\n return suffixFrom(argv, unwrapSimple(argv, []));\n case \"time\":\n return suffixFrom(argv, unwrapSimple(argv, [\"-p\"]));\n case \"timeout\":\n return suffixFrom(argv, unwrapTimeout(argv));\n case \"command\":\n return argv.slice(1).some((arg) => arg === \"-v\" || arg === \"-V\")\n ? undefined\n : suffixFrom(argv, unwrapSimple(argv, [\"-p\"]));\n case \"builtin\":\n return suffixFrom(argv, unwrapSimple(argv, []));\n case \"exec\":\n return this.dialect === \"posix\" ? suffixFrom(argv, unwrapExec(argv)) : undefined;\n case \"find\":\n return unwrapFind(argv);\n default:\n return undefined;\n }\n }\n\n /** Check a `cd` (shell mode): literal, one operand, inside the jail from every possible cwd. */\n public checkCd(words: ShellWord[], cwds: string[]): string[] {\n const argv = words.map((word) => word.value);\n\n if (this.deny.has(\"cd\")) {\n throw new Refusal(\"denied-executable\", \"`cd` is denied by the workspace shell policy.\", \"cd\");\n }\n\n this.checkHooks(argv, false);\n this.commands.push(argv);\n\n const operands = words.slice(1);\n\n while (\n this.dialect === \"posix\" &&\n operands.length > 0 &&\n [\"-P\", \"-L\", \"--\"].includes(operands[0]?.value ?? \"\")\n ) {\n operands.shift();\n }\n\n const target = operands[0];\n\n if (operands.length !== 1 || target === undefined || target.value === \"-\" || target.dynamic) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`cd` needs exactly one literal directory inside the workspace (no `-`, `~` or variables).\",\n \"cd\",\n );\n }\n\n const next = new Set(cwds);\n\n for (const cwd of cwds) {\n const resolved = this.resolveTarget(cwd, target.value);\n\n this.assertInJail(resolved, target.value, \"cd\");\n this.paths.push({ path: resolved, access: \"cwd\", target: target.value });\n next.add(resolved);\n }\n\n if (next.size > MAX_CD_CANDIDATES) {\n throw new Refusal(\"unsupported-syntax\", \"The line changes directory too many times.\", \"cd\");\n }\n\n return [...next];\n }\n\n /** Check a redirection target from every possible cwd. */\n public checkRedirectTarget(word: ShellWord, access: \"read\" | \"write\", cwds: string[]): void {\n if (word.dynamic) {\n throw new Refusal(\n \"unsupported-syntax\",\n `The redirection target \\`${word.raw}\\` must be a literal path (no variables, globs or \\`~\\`).`,\n );\n }\n\n const value = word.value;\n\n if (\n (this.dialect === \"posix\" && DEVICE_TARGETS.has(value)) ||\n (this.dialect === \"cmd\" && CMD_DEVICE_TARGETS.has(value.toLowerCase()))\n ) {\n return;\n }\n\n for (const cwd of cwds) {\n const resolved = this.resolveTarget(cwd, value);\n\n this.assertInJail(resolved, value, \"redirect\");\n this.paths.push({ path: resolved, access, target: value });\n }\n }\n\n private resolveTarget(cwd: string, target: string): string {\n return path.resolve(cwd, target);\n }\n\n private assertInJail(resolved: string, target: string, kind: \"cd\" | \"redirect\"): void {\n if (isInsideJailLexically(this.policy, resolved)) {\n return;\n }\n\n throw new Refusal(\n \"path-escape\",\n kind === \"cd\"\n ? `\\`cd ${target}\\` leaves the workspace jail (or enters a denied path); commands must stay inside the workspace.`\n : `The redirection target \\`${target}\\` is outside the workspace jail or on a denied path.`,\n undefined,\n target,\n );\n }\n}\n\n/**\n * Turn a wrapper's \"inner command starts at `start`\" into a range; a\n * wrapper that runs nothing (`start === argv.length`) becomes `undefined`.\n */\nfunction suffixFrom(\n argv: string[],\n start: number | null,\n): Array<[number, number]> | null | undefined {\n if (start === null) {\n return null;\n }\n\n return start < argv.length ? [[start, argv.length]] : undefined;\n}\n\n/** `nohup` / `time` / `command` / `builtin`: skip the listed flags and `--`. */\nfunction unwrapSimple(argv: string[], flags: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (flags.includes(arg)) {\n index++;\n\n continue;\n }\n\n if (arg.startsWith(\"-\") && arg !== \"-\") {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** `env [-i] [-u NAME] [NAME=value]… command…` */\nfunction unwrapEnv(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-i\" || arg === \"-\" || arg === \"--ignore-environment\") {\n index++;\n\n continue;\n }\n\n if (arg === \"-u\" || arg === \"--unset\") {\n index += 2;\n\n continue;\n }\n\n if (arg.startsWith(\"--unset=\") || (arg.startsWith(\"-u\") && arg.length > 2)) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n while (index < argv.length && /^[^=]+=/.test(argv[index] ?? \"\")) {\n index++;\n }\n\n return index;\n}\n\n/** `nice [-n N | -N | --adjustment=N] command…` */\nfunction unwrapNice(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-n\" || arg === \"--adjustment\") {\n index += 2;\n\n continue;\n }\n\n if (/^-n-?[0-9]+$/.test(arg) || /^--adjustment=-?[0-9]+$/.test(arg) || /^-[0-9]+$/.test(arg)) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** `timeout [options] DURATION command…` */\nfunction unwrapTimeout(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-s\" || arg === \"--signal\" || arg === \"-k\" || arg === \"--kill-after\") {\n index += 2;\n\n continue;\n }\n\n if (\n /^-[sk].+$/.test(arg) ||\n arg.startsWith(\"--signal=\") ||\n arg.startsWith(\"--kill-after=\") ||\n arg === \"--preserve-status\" ||\n arg === \"--foreground\" ||\n arg === \"-v\" ||\n arg === \"--verbose\"\n ) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n // The duration, then the command.\n if (index >= argv.length - 1) {\n return null;\n }\n\n return index + 1;\n}\n\n/** `exec [-cl] [-a name] command…` (bash). */\nfunction unwrapExec(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-c\" || arg === \"-l\" || arg === \"-cl\" || arg === \"-lc\") {\n index++;\n\n continue;\n }\n\n if (arg === \"-a\") {\n index += 2;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** The commands `find -exec / -execdir / -ok / -okdir … ;|+` runs. */\nfunction unwrapFind(argv: string[]): Array<[number, number]> | null | undefined {\n const inner: Array<[number, number]> = [];\n\n for (let index = 1; index < argv.length; index++) {\n const arg = argv[index] ?? \"\";\n\n if (arg !== \"-exec\" && arg !== \"-execdir\" && arg !== \"-ok\" && arg !== \"-okdir\") {\n continue;\n }\n\n const start = index + 1;\n let end = start;\n\n while (end < argv.length && argv[end] !== \";\" && argv[end] !== \"+\") {\n end++;\n }\n\n if (end >= argv.length || end === start) {\n return null;\n }\n\n inner.push([start, end]);\n index = end;\n }\n\n return inner.length > 0 ? inner : undefined;\n}\n\n/** Build the argv-mode refusal for a line that is not a single argv. */\nfunction argvModeRefusal(command: string): Refusal {\n const analysis = analyzeArgvCommand(command);\n\n if (analysis.ok) {\n // Not reached: callers only ask for a refusal of a line that failed.\n return new Refusal(\"metacharacter\", \"The command cannot run as a single argv.\");\n }\n\n if (analysis.type === \"empty\") {\n return new Refusal(\"not-allowed\", \"The command is empty.\");\n }\n\n if (analysis.type === \"unbalanced-quotes\") {\n return new Refusal(\n \"unbalanced-quotes\",\n `The command has an unbalanced \\`${analysis.quote}\\` quote. Close it, or quote the argument with the other kind of quote.`,\n );\n }\n\n const token = analysis.token;\n const suffix = \" (run_shell executes a single command without a shell.)\";\n const parsed = parseShellLine(command, \"posix\");\n const segments: ShellSegment[] = parsed.ok ? parsed.segments : [];\n const listOperator = token === \"&&\" || token === \"||\" || token === \";\" || token === \"\\n\";\n\n if (listOperator && segments.length >= 2) {\n const shownOperator = token === \"\\n\" ? \"A line break\" : `\\`${token}\\``;\n const firstWords = (segments[0]?.source ?? \"\").trim().split(/\\s+/);\n\n if (firstWords[0] === \"cd\") {\n const rest = segments.slice(1).map((segment) => show(segment.source));\n\n return new Refusal(\n \"metacharacter\",\n `${shownOperator} is not supported in argv mode, and \\`cd\\` does not carry over: every command starts in the workspace root. Run ${rest.join(\", then \")} on its own, with paths relative to the root.${suffix}`,\n );\n }\n\n const steps = segments.map((segment) => show(segment.source));\n const joiner = token === \"||\" ? \", and if it fails, \" : \", then \";\n\n return new Refusal(\n \"metacharacter\",\n `${shownOperator} is not supported in argv mode: run ${steps.join(joiner)}, as separate commands.${suffix}`,\n );\n }\n\n if (token === \"&\" && segments.length >= 1) {\n const steps = segments.map((segment) => show(segment.source));\n const run =\n steps.length === 1\n ? `run ${steps[0] ?? \"\"} without it`\n : `run ${steps.join(\", then \")} as separate commands`;\n\n return new Refusal(\n \"metacharacter\",\n `\\`&\\` is not supported in argv mode: ${run}; start a long-running command with background: true.${suffix}`,\n );\n }\n\n if (token === \"|\") {\n const pipeline = command\n .split(\"|\")\n .map((part) => part.trim())\n .filter(Boolean);\n\n return new Refusal(\n \"metacharacter\",\n `\\`|\\` (a pipe) is not supported in argv mode: run ${show(pipeline[0] ?? command)} on its own and read its output yourself.${suffix}`,\n );\n }\n\n if (token === \">\" || token === \">>\" || token === \">&\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` (redirection) is not supported in argv mode: run the command without it and save the output with write_file if you need it.${suffix}`,\n );\n }\n\n if (token === \"<\" || token === \"<<\" || token === \"<&\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` (input redirection) is not supported in argv mode: pass the file path to the command as an argument.${suffix}`,\n );\n }\n\n if (token === \"$(\" || token === \"`\") {\n return new Refusal(\n \"metacharacter\",\n `Command substitution (${token === \"$(\" ? \"`$(…)`\" : \"backticks\"}) is not supported in argv mode: run the inner command first, then pass its output literally.${suffix}`,\n );\n }\n\n if (token === \"$\" || token === \"${\") {\n return new Refusal(\n \"metacharacter\",\n `\\`$\\` (variable expansion) is not supported in argv mode: write the value literally, or put the \\`$\\` inside quotes to pass it as text.${suffix}`,\n );\n }\n\n if (token === \"(\" || token === \")\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` is not supported in argv mode: put it inside quotes to pass it as text.${suffix}`,\n );\n }\n\n return new Refusal(\n \"metacharacter\",\n `\\`${token === \"\\n\" ? \"\\\\n\" : token}\\` is not supported in argv mode: run one command per call, and quote any argument that contains it.${suffix}`,\n );\n}\n\n/** Check a shell-mode (`interpreter: \"sh\"`) line. */\nfunction checkShellLine(\n checker: LineChecker,\n policy: WorkspacePolicy,\n command: string,\n dialect: ShellDialect,\n): void {\n if (/^[-+]/.test(command)) {\n throw new Refusal(\"unsupported-syntax\", \"A command line may not start with `-` or `+`.\");\n }\n\n const parsed = parseShellLine(command, dialect);\n\n if (!parsed.ok) {\n throw new Refusal(\n parsed.reason,\n `${parsed.message} The workspace shell policy only runs lines it can check.`,\n );\n }\n\n if (parsed.background) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`&` (run in the background) is not supported in a command line: start a long-running command with background: true instead.\",\n );\n }\n\n let cwds = [path.resolve(policy.cwd)];\n\n for (const simple of parsed.commands) {\n cwds = checkSimpleCommand(checker, simple, cwds, dialect);\n }\n}\n\n/** Check one parsed simple command; returns the possible working directories after it. */\nfunction checkSimpleCommand(\n checker: LineChecker,\n simple: ShellSimpleCommand,\n cwds: string[],\n dialect: ShellDialect,\n): string[] {\n for (const assignment of simple.assignments) {\n const name = assignment.assignmentName ?? \"\";\n\n if (SENSITIVE_VARIABLES.has(name) || name.startsWith(\"BASH_FUNC_\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `Assigning \\`${name}\\` is not supported: it changes how the shell runs the commands that follow.`,\n );\n }\n }\n\n for (const redirect of simple.redirects) {\n if (redirect.type === \"read\" || redirect.type === \"write\") {\n checker.checkRedirectTarget(redirect.target, redirect.type, cwds);\n }\n }\n\n const first = simple.words[0];\n\n if (first === undefined) {\n return cwds;\n }\n\n if (!first.dynamic && first.value.includes(\"=\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${first.raw}\\` is not a plain command name or \\`NAME=value\\` assignment.`,\n );\n }\n\n if (/^[-+]/.test(first.value)) {\n throw new Refusal(\"unsupported-syntax\", `\\`${first.raw}\\` is not a command name.`);\n }\n\n if (checker.hasArgumentHooks) {\n const dynamic = simple.words.find((word) => word.dynamic);\n\n if (dynamic !== undefined) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${dynamic.raw}\\` is decided when the line runs, so it cannot be checked against the workspace shell policy's command rules. Write the value literally (quote globs).`,\n );\n }\n }\n\n const executable = first.dynamic ? \"\" : executableBasename(first.value, dialect).toLowerCase();\n\n if (executable === \"cd\") {\n return checker.checkCd(simple.words, cwds);\n }\n\n if (dialect === \"posix\" && (executable === \"pushd\" || executable === \"popd\")) {\n throw new Refusal(\"unsupported-syntax\", `\\`${executable}\\` is not supported; use \\`cd\\`.`);\n }\n\n checker.checkArgv(\n simple.words.map((word) => (word.dynamic ? word.raw : word.value)),\n simple.words.map((word) => word.dynamic),\n 0,\n );\n\n return cwds;\n}\n\n/**\n * The shell policy's verdict on a command line, without touching the\n * filesystem.\n *\n * - **argv mode** (`interpreter: \"none\"`, the default): the line must\n * tokenize into one argv; otherwise it is refused with a\n * `\"metacharacter\"` / `\"unbalanced-quotes\"` reason and a message that\n * names the operator and suggests the split.\n * - **shell mode** (`interpreter: \"sh\"`): the line is parsed and every\n * simple command — inside `;`, `&&`, `||`, pipes, subshells, `$( )`,\n * backticks and here-documents — is checked; redirection targets and `cd`\n * directories must stay in the jail (checked lexically here; `exec`\n * re-checks them with symlinks resolved).\n *\n * Every argv goes through `deny` (case-insensitive, wins), `allow`\n * (exhaustive when non-empty), `denyPatterns` and `allowCommand`.\n * Wrappers (`env`, `time`, `timeout`, `nice`, `nohup`, `command`,\n * `builtin`, `exec`) and `find -exec` are followed to the command they run.\n * A runner the policy cannot see into (`bash -c`, `eval`, `xargs`, `sudo`,\n * …) is refused while the policy has deny rules, unless `allow` names it.\n *\n * @example\n * checkShellCommand({ cwd, shell: { allow: [\"git\"] } }, \"git add -A && git commit -m wip\");\n * // { allowed: false, reason: \"metacharacter\", message: \"`&&` is not supported in argv mode: run `git add -A`, then `git commit -m wip`, …\" }\n */\nexport function checkShellCommand(policy: WorkspacePolicy, command: string): WorkspaceShellCheck {\n return checkShellCommandInDialect(policy, command, shellDialect());\n}\n\n/**\n * {@link checkShellCommand} with the shell-mode dialect chosen explicitly\n * instead of from the platform (used to test the cmd.exe rules anywhere).\n * Argv mode ignores `dialect`.\n */\nexport function checkShellCommandInDialect(\n policy: WorkspacePolicy,\n command: string,\n dialect: ShellDialect,\n): WorkspaceShellCheck {\n const shell = policy.shell;\n\n if (!shell) {\n return {\n allowed: false,\n reason: \"not-allowed\",\n message: \"The workspace has no shell policy, so no command may run.\",\n commands: [],\n paths: [],\n };\n }\n\n const interpreter = shell.interpreter ?? \"none\";\n const checker = new LineChecker(policy, shell, command, interpreter === \"sh\" ? dialect : \"posix\");\n\n try {\n if (command.trim() === \"\") {\n throw new Refusal(\"not-allowed\", \"The command is empty.\");\n }\n\n if (interpreter === \"sh\") {\n checkShellLine(checker, policy, command, dialect);\n } else {\n const argv = tokenizeCommand(command);\n\n if (argv === null) {\n throw argvModeRefusal(command);\n }\n\n checker.checkArgv(\n argv,\n argv.map(() => false),\n 0,\n );\n }\n\n return { allowed: true, commands: checker.commands, paths: checker.paths };\n } catch (error) {\n if (!(error instanceof Refusal)) {\n throw error;\n }\n\n return {\n allowed: false,\n reason: error.reason,\n message: error.message,\n ...(error.executable !== undefined ? { executable: error.executable } : {}),\n ...(error.refusedPath !== undefined ? { path: error.refusedPath } : {}),\n commands: checker.commands,\n paths: checker.paths,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shell-check.mjs","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/policy/shell-check.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { WorkspaceShellRefusalReason } from \"../errors\";\nimport type {\n WorkspaceCommandPattern,\n WorkspaceCommandRule,\n WorkspacePolicy,\n WorkspaceShellAsk,\n WorkspaceShellClassification,\n WorkspaceShellPolicy,\n} from \"../contracts\";\nimport { isInsideJailLexically } from \"./jail\";\nimport {\n canonicalPowerShellArgv,\n normalizePowerShellCd,\n POWERSHELL_CD_NAMES,\n POWERSHELL_DIRECTORY_STACK,\n} from \"./powershell-aliases\";\nimport {\n currentShellHost,\n resolveShell,\n type ResolvedShell,\n type ResolvedShellInterpreter,\n type ShellHost,\n} from \"./shell-interpreter\";\nimport {\n parseShellLine,\n type ShellDialect,\n type ShellSegment,\n type ShellSimpleCommand,\n type ShellWord,\n} from \"./shell-parser\";\nimport { analyzeArgvCommand, tokenizeCommand } from \"./tokenize-command\";\n\n/** How a shell-mode line touches a path the jail has to vet. */\nexport type WorkspaceShellPathAccess = \"read\" | \"write\" | \"cwd\";\n\n/** A path a shell-mode line redirects to/from or `cd`s into. */\nexport interface WorkspaceShellPath {\n /** Absolute path, resolved lexically against the workspace root (or an earlier `cd`). */\n path: string;\n /** What the line does with it. */\n access: WorkspaceShellPathAccess;\n /** The target as written in the line. */\n target: string;\n}\n\n/**\n * The shell policy's verdict on one command line — see\n * {@link checkShellCommand}.\n */\nexport interface WorkspaceShellCheck {\n /** Whether the line may run (path targets still get a symlink-aware recheck in `exec`). */\n allowed: boolean;\n /** Why it was refused. */\n reason?: WorkspaceShellRefusalReason;\n /** Model-facing refusal text: the cause, and how to fix the command. */\n message?: string;\n /** The executable the refusal is about, when there is one. */\n executable?: string;\n /** The redirection target or `cd` directory a `\"path-escape\"` refusal is about. */\n path?: string;\n /**\n * Commands that need a decision from `shell.onAsk` before the line may run.\n * When non-empty, `allowed` is `false` with reason `\"approval-required\"`;\n * the ops layer asks, and refuses with `\"approval-denied\"` if the answer is no.\n */\n asks: WorkspaceShellAsk[];\n /** The argv of every simple command checked, in order (wrapped commands included). */\n commands: string[][];\n /** Shell mode: every redirection target and `cd` directory, for the jail recheck. */\n paths: WorkspaceShellPath[];\n}\n\n/**\n * Programs and shell builtins that run a command or code the policy cannot\n * see (a script string, a file, a command read from input, another user).\n * With deny rules in force they would hide a denied command, so they are\n * refused unless the policy names them in `allow`.\n */\nconst OPAQUE_RUNNERS = new Set([\n // Shells and interpreters of command strings.\n \"sh\",\n \"bash\",\n \"dash\",\n \"zsh\",\n \"ksh\",\n \"mksh\",\n \"ash\",\n \"yash\",\n \"posh\",\n \"fish\",\n \"csh\",\n \"tcsh\",\n \"busybox\",\n \"cmd\",\n \"powershell\",\n \"pwsh\",\n \"wsl\",\n // Run a command as someone else, elsewhere, or out of the process group.\n \"sudo\",\n \"doas\",\n \"su\",\n \"runuser\",\n \"pkexec\",\n \"runas\",\n \"chroot\",\n \"nsenter\",\n \"unshare\",\n \"setsid\",\n \"systemd-run\",\n \"start-stop-daemon\",\n \"daemonize\",\n // Run commands built from input or on a schedule.\n \"xargs\",\n \"parallel\",\n \"watch\",\n \"script\",\n \"expect\",\n \"at\",\n \"batch\",\n \"screen\",\n \"tmux\",\n \"stdbuf\",\n \"flock\",\n]);\n\n/**\n * PowerShell cmdlets and aliases that run code or commands the policy cannot\n * see, or change what later names mean (lowercase).\n */\nconst POWERSHELL_OPAQUE = new Set([\n \"iex\",\n \"invoke-expression\",\n \"invoke-command\",\n \"icm\",\n \"start-process\",\n \"saps\",\n \"start\",\n \"add-type\",\n \"set-alias\",\n \"sal\",\n \"new-alias\",\n \"nal\",\n \"invoke-item\",\n \"ii\",\n]);\n\n/** POSIX shell builtins that evaluate code or change how later names resolve. */\nconst SHELL_OPAQUE_BUILTINS = new Set([\n \"eval\",\n \".\",\n \"source\",\n \"alias\",\n \"unalias\",\n \"hash\",\n \"enable\",\n \"trap\",\n \"let\",\n \"declare\",\n \"typeset\",\n \"local\",\n \"set\",\n \"shopt\",\n \"fc\",\n \"bind\",\n \"complete\",\n \"compgen\",\n \"compopt\",\n \"mapfile\",\n \"readarray\",\n]);\n\n/** cmd.exe builtins that run commands or change the environment. */\nconst CMD_OPAQUE_BUILTINS = new Set([\"call\", \"start\", \"set\", \"pushd\", \"popd\"]);\n\n/**\n * Shell builtins whose arguments bash may evaluate as arithmetic (array\n * subscripts such as `a[$(cmd)]`): their arguments must be literal and free\n * of `[`.\n */\nconst ARITHMETIC_SENSITIVE_BUILTINS = new Set([\n \"test\",\n \"[\",\n \"printf\",\n \"read\",\n \"unset\",\n \"export\",\n \"readonly\",\n \"getopts\",\n \"wait\",\n \"kill\",\n \"shift\",\n \"return\",\n \"exit\",\n \"break\",\n \"continue\",\n \"ulimit\",\n]);\n\n/**\n * Variables a shell-mode line may not assign: they change how the shell\n * parses, traces or resolves what follows (`CDPATH` would move `cd` out of\n * the jail's sight, `PS4` is expanded — command substitutions included —\n * on every traced line, `IFS` re-splits expansions).\n */\nconst SENSITIVE_VARIABLES = new Set([\n \"CDPATH\",\n \"ENV\",\n \"BASH_ENV\",\n \"SHELLOPTS\",\n \"BASHOPTS\",\n \"IFS\",\n \"PS1\",\n \"PS2\",\n \"PS3\",\n \"PS4\",\n \"PROMPT_COMMAND\",\n \"BASH_XTRACEFD\",\n \"GLOBIGNORE\",\n \"EXECIGNORE\",\n \"BASH_LOADABLES_PATH\",\n]);\n\n/** Redirection targets that are always fine even though they sit outside the jail. */\nconst DEVICE_TARGETS = new Set([\"/dev/null\", \"/dev/stdout\", \"/dev/stderr\"]);\n\n/** cmd.exe's null device. */\nconst CMD_DEVICE_TARGETS = new Set([\"nul\"]);\n\n/** Deepest chain of wrappers (`env time nice …`) the policy unwraps. */\nconst MAX_WRAPPER_DEPTH = 8;\n\n/** Most distinct directories a line may `cd` between. */\nconst MAX_CD_CANDIDATES = 32;\n\n/** Longest command shown verbatim in a refusal before it is abbreviated. */\nconst MAX_SHOWN_COMMAND = 48;\n\n/** The dialect `interpreter: \"sh\"` parses on this platform. */\nexport function shellDialect(): ShellDialect {\n return process.platform === \"win32\" ? \"cmd\" : \"posix\";\n}\n\n/**\n * Reduce an argv's first element to the basename the allow/deny policy is\n * keyed on. `\"npm\"` → `\"npm\"`; `\"/usr/bin/node\"` → `\"node\"`; `\"node.exe\"`\n * → `\"node\"` (the `.exe`/`.cmd`/`.bat`/`.com` Windows extension is\n * stripped). The cmd dialect splits Windows paths too.\n */\nexport function executableBasename(firstToken: string, dialect: ShellDialect = \"posix\"): string {\n const base =\n dialect === \"cmd\" || dialect === \"powershell\"\n ? path.win32.basename(firstToken)\n : path.basename(firstToken);\n\n return base.replace(\n dialect === \"powershell\" ? /\\.(exe|cmd|bat|com|ps1)$/i : /\\.(exe|cmd|bat|com)$/i,\n \"\",\n );\n}\n\n/** Show a command in a refusal, abbreviated at a word boundary when long. */\nfunction show(source: string): string {\n const flat = source.replace(/\\s+/g, \" \").trim();\n\n if (flat.length <= MAX_SHOWN_COMMAND) {\n return `\\`${flat}\\``;\n }\n\n const cut = flat.lastIndexOf(\" \", MAX_SHOWN_COMMAND);\n\n return `\\`${flat.slice(0, cut > 0 ? cut : MAX_SHOWN_COMMAND)} …\\``;\n}\n\n/** Compile `denyPatterns` once per check. */\ninterface CompiledPattern {\n source: WorkspaceCommandPattern;\n test(argv: string[], dialect: ShellDialect): boolean;\n}\n\nfunction compilePattern(pattern: WorkspaceCommandPattern): CompiledPattern {\n if (pattern instanceof RegExp) {\n const regex = new RegExp(pattern.source, pattern.flags.replace(/[gy]/g, \"\"));\n\n return {\n source: pattern,\n test: (argv, dialect) =>\n regex.test([executableBasename(argv[0] ?? \"\", dialect), ...argv.slice(1)].join(\" \")),\n };\n }\n\n const tokens = tokenizeCommand(pattern);\n\n if (tokens === null) {\n // Not representable as an argv: match it as text inside the joined argv.\n return {\n source: pattern,\n test: (argv, dialect) =>\n [executableBasename(argv[0] ?? \"\", dialect), ...argv.slice(1)].join(\" \").includes(pattern),\n };\n }\n\n return {\n source: pattern,\n test: (argv, dialect) => {\n if (argv.length < tokens.length) {\n return false;\n }\n\n return tokens.every((token, index) => {\n const actual = argv[index] ?? \"\";\n\n return index === 0\n ? executableBasename(actual, dialect).toLowerCase() ===\n executableBasename(token, dialect).toLowerCase()\n : actual === token;\n });\n },\n };\n}\n\n/** Thrown inside the checker to stop at the first refusal. */\nclass Refusal extends Error {\n public constructor(\n public readonly reason: WorkspaceShellRefusalReason,\n message: string,\n public readonly executable?: string,\n public readonly refusedPath?: string,\n ) {\n super(message);\n }\n}\n\n/** Options a check takes beyond the policy and the line. */\nexport interface ShellCheckOptions {\n /** Patterns a host approved earlier with `remember`; they allow what would ask, never what is denied. */\n remembered?: readonly WorkspaceCommandPattern[];\n /** The host the `\"auto\"` interpreter is resolved against (tests inject one). */\n host?: ShellHost;\n /** The already-resolved shell, to skip resolving it again. */\n resolved?: ResolvedShell;\n}\n\n/** A compiled `shell.rules` entry. */\ninterface CompiledRule {\n rule: WorkspaceCommandRule;\n pattern: CompiledPattern;\n}\n\n/** Per-line checker state. */\nclass LineChecker {\n public readonly commands: string[][] = [];\n\n public readonly paths: WorkspaceShellPath[] = [];\n\n public readonly asks: WorkspaceShellAsk[] = [];\n\n private readonly deny: Set<string>;\n\n private readonly allow: string[];\n\n private readonly patterns: CompiledPattern[];\n\n private readonly rules: CompiledRule[];\n\n private readonly remembered: CompiledPattern[];\n\n private readonly hasCommandRules: boolean;\n\n /** Whether `shell.onAsk` is set: unmatched, unreadable and opaque commands ask. */\n public readonly askMode: boolean;\n\n public constructor(\n private readonly policy: WorkspacePolicy,\n private readonly shell: WorkspaceShellPolicy,\n private readonly line: string,\n private readonly dialect: ShellDialect,\n private readonly interpreter: ResolvedShellInterpreter,\n remembered: readonly WorkspaceCommandPattern[] = [],\n ) {\n this.deny = new Set((shell.deny ?? []).map((name) => name.toLowerCase()));\n this.allow = shell.allow ?? [];\n this.patterns = (shell.denyPatterns ?? []).map(compilePattern);\n this.rules = (shell.rules ?? []).map((rule) => ({ rule, pattern: compilePattern(rule.match) }));\n this.remembered = remembered.map(compilePattern);\n this.askMode = shell.onAsk !== undefined;\n this.hasCommandRules =\n this.deny.size > 0 ||\n this.patterns.length > 0 ||\n this.rules.length > 0 ||\n shell.allowCommand !== undefined;\n }\n\n /** Whether argument hooks (`denyPatterns` / `allowCommand`) need literal argv. */\n public get hasArgumentHooks(): boolean {\n return this.patterns.length > 0 || this.shell.allowCommand !== undefined;\n }\n\n private get shellMode(): boolean {\n return this.interpreter !== \"none\";\n }\n\n /** The last rule that matches `argv` (later rules win). */\n private matchRule(argv: string[]): WorkspaceCommandRule | undefined {\n for (let index = this.rules.length - 1; index >= 0; index--) {\n const compiled = this.rules[index] as CompiledRule;\n\n if (compiled.pattern.test(argv, this.dialect)) {\n return compiled.rule;\n }\n }\n\n return undefined;\n }\n\n private isRemembered(argv: string[]): boolean {\n return this.remembered.some((pattern) => pattern.test(argv, this.dialect));\n }\n\n /** Record a command that needs a decision (once per argv and reason). */\n public addAsk(\n argv: string[],\n reason: WorkspaceShellAsk[\"reason\"],\n rule?: WorkspaceCommandRule,\n ): void {\n const key = `${reason}\\u0000${argv.join(\"\\u0000\")}`;\n\n if (this.asks.some((ask) => `${ask.reason}\\u0000${ask.argv.join(\"\\u0000\")}` === key)) {\n return;\n }\n\n this.asks.push({ argv: [...argv], reason, ...(rule !== undefined ? { rule } : {}) });\n }\n\n /** Refuse when the matching rule denies. */\n private assertNotDenied(argv: string[], rule: WorkspaceCommandRule | undefined): void {\n if (rule?.effect !== \"deny\") {\n return;\n }\n\n const explanation =\n rule.reason !== undefined && rule.reason.trim() !== \"\" ? ` ${rule.reason.trim()}` : \"\";\n\n throw new Refusal(\n \"denied-pattern\",\n `${show(argv.join(\" \"))} is denied by a workspace shell rule.${explanation}`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n\n /**\n * Check the canonical form of a PowerShell alias (`Remove-Item -Recurse\n * -Force x` as `rm -rf x`): deny, `denyPatterns`, `allowCommand` and the\n * rules, but not the `allow` list (which names the spelling that was written).\n */\n public checkCanonical(argv: string[]): void {\n const key = executableBasename(argv[0] ?? \"\", this.dialect).toLowerCase();\n\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${key}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n key,\n );\n }\n\n this.checkHooks(argv, true);\n\n const rule = this.matchRule(argv);\n\n this.assertNotDenied(argv, rule);\n\n if (rule?.effect === \"ask\" && !this.isRemembered(argv)) {\n this.addAsk(argv, \"rule\", rule);\n }\n }\n\n /** Refuse a denied executable (used for `cd` spellings). */\n public assertExecutableNotDenied(key: string, argv: string[]): void {\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${key}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n key,\n );\n }\n }\n\n /** Run `denyPatterns` and `allowCommand` on one argv. */\n private checkHooks(argv: string[], nested: boolean): void {\n const shown = show(argv.join(\" \"));\n\n for (const pattern of this.patterns) {\n if (pattern.test(argv, this.dialect)) {\n throw new Refusal(\n \"denied-pattern\",\n `${shown} matches a command pattern the workspace shell policy denies.`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n }\n\n const hook = this.shell.allowCommand;\n\n if (hook === undefined) {\n return;\n }\n\n let verdict: boolean | string;\n\n try {\n verdict = hook([...argv], { command: this.line, interpreter: this.interpreter, nested });\n } catch {\n verdict = false;\n }\n\n if (verdict === true) {\n return;\n }\n\n const explanation =\n typeof verdict === \"string\" && verdict.trim() !== \"\" ? ` ${verdict.trim()}` : \"\";\n\n throw new Refusal(\n \"not-allowed\",\n `${shown} was refused by the workspace shell policy.${explanation}`,\n executableBasename(argv[0] ?? \"\", this.dialect),\n );\n }\n\n /** Whether the policy names `executable` in a non-empty `allow` list. */\n private explicitlyAllowed(executable: string): boolean {\n return this.allow.includes(executable);\n }\n\n /** Refuse a command runner the policy cannot see into, unless explicitly allowed. */\n private checkOpaque(executable: string, argv: string[]): void {\n if (this.explicitlyAllowed(executable) || !this.hasCommandRules) {\n return;\n }\n\n throw new Refusal(\n \"nested-command\",\n `\\`${executable}\\` runs a command the workspace shell policy cannot inspect (in ${show(argv.join(\" \"))}). Run the inner command directly instead.`,\n executable,\n );\n }\n\n /**\n * Check one argv: `deny`, `allow`, `denyPatterns`, `allowCommand`, then\n * follow wrappers and `find -exec` to the command they run. `dynamic[i]`\n * says whether `argv[i]` is decided at run time (shell mode).\n */\n public checkArgv(argv: string[], dynamic: boolean[], depth: number): void {\n const first = argv[0] ?? \"\";\n const executable = executableBasename(first, this.dialect);\n const key = executable.toLowerCase();\n\n if (executable === \"\") {\n throw new Refusal(\"not-allowed\", \"The command has no executable name.\");\n }\n\n if (dynamic[0] === true) {\n throw new Refusal(\n \"unsupported-syntax\",\n `The command name \\`${first}\\` is decided when the line runs, so the workspace shell policy cannot check it. Write the command name literally.`,\n );\n }\n\n if (this.deny.has(key)) {\n throw new Refusal(\n \"denied-executable\",\n `\\`${executable}\\` is denied by the workspace shell policy (in ${show(argv.join(\" \"))}).`,\n executable,\n );\n }\n\n if (this.allow.length > 0 && !this.allow.includes(executable)) {\n const listed = this.allow.slice(0, 20).join(\", \");\n\n throw new Refusal(\n \"not-allowed\",\n `\\`${executable}\\` is not in the workspace shell allowlist. Allowed commands: ${listed}${this.allow.length > 20 ? \", …\" : \"\"}.`,\n executable,\n );\n }\n\n this.checkHooks(argv, depth > 0);\n\n const rule = this.matchRule(argv);\n\n this.assertNotDenied(argv, rule);\n this.commands.push([...argv]);\n\n const remembered = this.isRemembered(argv);\n const approved = rule?.effect === \"allow\" || remembered || this.explicitlyAllowed(executable);\n\n if (\n OPAQUE_RUNNERS.has(key) ||\n (this.shellMode && this.dialect === \"posix\" && SHELL_OPAQUE_BUILTINS.has(key)) ||\n (this.shellMode && this.dialect === \"cmd\" && CMD_OPAQUE_BUILTINS.has(key)) ||\n (this.shellMode && this.dialect === \"powershell\" && POWERSHELL_OPAQUE.has(key))\n ) {\n if (approved) {\n return;\n }\n\n if (this.askMode || rule?.effect === \"ask\") {\n this.addAsk(argv, \"nested\", rule);\n\n return;\n }\n\n this.checkOpaque(executable, argv);\n\n return;\n }\n\n if (this.shellMode && this.dialect === \"posix\" && ARITHMETIC_SENSITIVE_BUILTINS.has(key)) {\n this.checkArithmeticSensitive(executable, argv, dynamic);\n }\n\n const ranges = this.unwrap(key, argv);\n\n // A command no rule or allow list covers asks — except a wrapper, which\n // asks through the command it runs.\n if (!approved && ranges === undefined) {\n if (rule?.effect === \"ask\") {\n this.addAsk(argv, \"rule\", rule);\n } else if (rule === undefined && this.askMode && this.allow.length === 0) {\n this.addAsk(argv, \"rule\");\n }\n } else if (!approved && rule?.effect === \"ask\") {\n this.addAsk(argv, \"rule\", rule);\n }\n\n if (ranges === undefined) {\n return;\n }\n\n if (ranges === null || depth >= MAX_WRAPPER_DEPTH) {\n if (this.askMode) {\n this.addAsk(argv, \"nested\", rule);\n\n return;\n }\n\n throw new Refusal(\n \"nested-command\",\n `\\`${executable}\\` is used with options the workspace shell policy cannot read (in ${show(argv.join(\" \"))}). Run the inner command directly instead.`,\n executable,\n );\n }\n\n for (const [start, end] of ranges) {\n this.checkArgv(argv.slice(start, end), dynamic.slice(start, end), depth + 1);\n }\n }\n\n /**\n * Builtins bash may evaluate arguments of as arithmetic (array subscripts\n * like `a[$(cmd)]` run the substitution): arguments must be literal, free\n * of `[`, and must not name a variable that changes how the shell runs.\n */\n private checkArithmeticSensitive(executable: string, argv: string[], dynamic: boolean[]): void {\n for (let index = 1; index < argv.length; index++) {\n const argument = argv[index] ?? \"\";\n\n if (dynamic[index] === true) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` arguments must be literal here (\\`${argument}\\` is not): bash may evaluate them as arithmetic.`,\n executable,\n );\n }\n\n if (argument.includes(\"[\") && argument !== \"[\" && argument !== \"]\") {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` arguments with \\`[\\` (array subscripts, evaluated as arithmetic) are not supported.`,\n executable,\n );\n }\n\n const name = argument.split(\"=\")[0] ?? \"\";\n\n if (SENSITIVE_VARIABLES.has(name) || name.startsWith(\"BASH_FUNC_\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `Changing \\`${name}\\` is not supported: it changes how the shell runs the commands that follow.`,\n executable,\n );\n }\n }\n }\n\n /**\n * The argv ranges a wrapper runs. `undefined` — not a wrapper (or one that\n * runs nothing, like a bare `env`); `null` — a wrapper whose options the\n * policy cannot read (refused).\n */\n private unwrap(key: string, argv: string[]): Array<[number, number]> | null | undefined {\n switch (key) {\n case \"env\":\n return suffixFrom(argv, unwrapEnv(argv));\n case \"nice\":\n return suffixFrom(argv, unwrapNice(argv));\n case \"nohup\":\n return suffixFrom(argv, unwrapSimple(argv, []));\n case \"time\":\n return suffixFrom(argv, unwrapSimple(argv, [\"-p\"]));\n case \"timeout\":\n return suffixFrom(argv, unwrapTimeout(argv));\n case \"command\":\n return argv.slice(1).some((arg) => arg === \"-v\" || arg === \"-V\")\n ? undefined\n : suffixFrom(argv, unwrapSimple(argv, [\"-p\"]));\n case \"builtin\":\n return suffixFrom(argv, unwrapSimple(argv, []));\n case \"exec\":\n return this.dialect === \"posix\" ? suffixFrom(argv, unwrapExec(argv)) : undefined;\n case \"find\":\n return unwrapFind(argv);\n default:\n return undefined;\n }\n }\n\n /** Check a `cd` (shell mode): literal, one operand, inside the jail from every possible cwd. */\n public checkCd(words: ShellWord[], cwds: string[]): string[] {\n const argv = words.map((word) => word.value);\n\n if (this.deny.has(\"cd\")) {\n throw new Refusal(\"denied-executable\", \"`cd` is denied by the workspace shell policy.\", \"cd\");\n }\n\n this.checkHooks(argv, false);\n this.commands.push(argv);\n\n const operands = words.slice(1);\n\n while (\n this.dialect === \"posix\" &&\n operands.length > 0 &&\n [\"-P\", \"-L\", \"--\"].includes(operands[0]?.value ?? \"\")\n ) {\n operands.shift();\n }\n\n const target = operands[0];\n\n if (operands.length !== 1 || target === undefined || target.value === \"-\" || target.dynamic) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`cd` needs exactly one literal directory inside the workspace (no `-`, `~` or variables).\",\n \"cd\",\n );\n }\n\n const next = new Set(cwds);\n\n for (const cwd of cwds) {\n const resolved = this.resolveTarget(cwd, target.value);\n\n this.assertInJail(resolved, target.value, \"cd\");\n this.paths.push({ path: resolved, access: \"cwd\", target: target.value });\n next.add(resolved);\n }\n\n if (next.size > MAX_CD_CANDIDATES) {\n throw new Refusal(\"unsupported-syntax\", \"The line changes directory too many times.\", \"cd\");\n }\n\n return [...next];\n }\n\n /** Check a redirection target from every possible cwd. */\n public checkRedirectTarget(word: ShellWord, access: \"read\" | \"write\", cwds: string[]): void {\n if (this.dialect === \"powershell\" && !word.quoted && word.value.toLowerCase() === \"$null\") {\n return;\n }\n\n if (word.dynamic) {\n throw new Refusal(\n \"unsupported-syntax\",\n `The redirection target \\`${word.raw}\\` must be a literal path (no variables, globs or \\`~\\`).`,\n );\n }\n\n const value = word.value;\n\n if (\n (this.dialect === \"posix\" && DEVICE_TARGETS.has(value)) ||\n (this.dialect === \"cmd\" && CMD_DEVICE_TARGETS.has(value.toLowerCase()))\n ) {\n return;\n }\n\n for (const cwd of cwds) {\n const resolved = this.resolveTarget(cwd, value);\n\n this.assertInJail(resolved, value, \"redirect\");\n this.paths.push({ path: resolved, access, target: value });\n }\n }\n\n private resolveTarget(cwd: string, target: string): string {\n return path.resolve(cwd, target);\n }\n\n private assertInJail(resolved: string, target: string, kind: \"cd\" | \"redirect\"): void {\n if (isInsideJailLexically(this.policy, resolved)) {\n return;\n }\n\n throw new Refusal(\n \"path-escape\",\n kind === \"cd\"\n ? `\\`cd ${target}\\` leaves the workspace jail (or enters a denied path); commands must stay inside the workspace.`\n : `The redirection target \\`${target}\\` is outside the workspace jail or on a denied path.`,\n undefined,\n target,\n );\n }\n}\n\n/**\n * Turn a wrapper's \"inner command starts at `start`\" into a range; a\n * wrapper that runs nothing (`start === argv.length`) becomes `undefined`.\n */\nfunction suffixFrom(\n argv: string[],\n start: number | null,\n): Array<[number, number]> | null | undefined {\n if (start === null) {\n return null;\n }\n\n return start < argv.length ? [[start, argv.length]] : undefined;\n}\n\n/** `nohup` / `time` / `command` / `builtin`: skip the listed flags and `--`. */\nfunction unwrapSimple(argv: string[], flags: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (flags.includes(arg)) {\n index++;\n\n continue;\n }\n\n if (arg.startsWith(\"-\") && arg !== \"-\") {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** `env [-i] [-u NAME] [NAME=value]… command…` */\nfunction unwrapEnv(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-i\" || arg === \"-\" || arg === \"--ignore-environment\") {\n index++;\n\n continue;\n }\n\n if (arg === \"-u\" || arg === \"--unset\") {\n index += 2;\n\n continue;\n }\n\n if (arg.startsWith(\"--unset=\") || (arg.startsWith(\"-u\") && arg.length > 2)) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n while (index < argv.length && /^[^=]+=/.test(argv[index] ?? \"\")) {\n index++;\n }\n\n return index;\n}\n\n/** `nice [-n N | -N | --adjustment=N] command…` */\nfunction unwrapNice(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-n\" || arg === \"--adjustment\") {\n index += 2;\n\n continue;\n }\n\n if (/^-n-?[0-9]+$/.test(arg) || /^--adjustment=-?[0-9]+$/.test(arg) || /^-[0-9]+$/.test(arg)) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** `timeout [options] DURATION command…` */\nfunction unwrapTimeout(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-s\" || arg === \"--signal\" || arg === \"-k\" || arg === \"--kill-after\") {\n index += 2;\n\n continue;\n }\n\n if (\n /^-[sk].+$/.test(arg) ||\n arg.startsWith(\"--signal=\") ||\n arg.startsWith(\"--kill-after=\") ||\n arg === \"--preserve-status\" ||\n arg === \"--foreground\" ||\n arg === \"-v\" ||\n arg === \"--verbose\"\n ) {\n index++;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n // The duration, then the command.\n if (index >= argv.length - 1) {\n return null;\n }\n\n return index + 1;\n}\n\n/** `exec [-cl] [-a name] command…` (bash). */\nfunction unwrapExec(argv: string[]): number | null {\n let index = 1;\n\n while (index < argv.length) {\n const arg = argv[index] ?? \"\";\n\n if (arg === \"-c\" || arg === \"-l\" || arg === \"-cl\" || arg === \"-lc\") {\n index++;\n\n continue;\n }\n\n if (arg === \"-a\") {\n index += 2;\n\n continue;\n }\n\n if (arg === \"--\") {\n index++;\n\n break;\n }\n\n if (arg.startsWith(\"-\")) {\n return null;\n }\n\n break;\n }\n\n return index;\n}\n\n/** The commands `find -exec / -execdir / -ok / -okdir … ;|+` runs. */\nfunction unwrapFind(argv: string[]): Array<[number, number]> | null | undefined {\n const inner: Array<[number, number]> = [];\n\n for (let index = 1; index < argv.length; index++) {\n const arg = argv[index] ?? \"\";\n\n if (arg !== \"-exec\" && arg !== \"-execdir\" && arg !== \"-ok\" && arg !== \"-okdir\") {\n continue;\n }\n\n const start = index + 1;\n let end = start;\n\n while (end < argv.length && argv[end] !== \";\" && argv[end] !== \"+\") {\n end++;\n }\n\n if (end >= argv.length || end === start) {\n return null;\n }\n\n inner.push([start, end]);\n index = end;\n }\n\n return inner.length > 0 ? inner : undefined;\n}\n\n/** Build the argv-mode refusal for a line that is not a single argv. */\nfunction argvModeRefusal(command: string): Refusal {\n const analysis = analyzeArgvCommand(command);\n\n if (analysis.ok) {\n // Not reached: callers only ask for a refusal of a line that failed.\n return new Refusal(\"metacharacter\", \"The command cannot run as a single argv.\");\n }\n\n if (analysis.type === \"empty\") {\n return new Refusal(\"not-allowed\", \"The command is empty.\");\n }\n\n if (analysis.type === \"unbalanced-quotes\") {\n return new Refusal(\n \"unbalanced-quotes\",\n `The command has an unbalanced \\`${analysis.quote}\\` quote. Close it, or quote the argument with the other kind of quote.`,\n );\n }\n\n const token = analysis.token;\n const suffix = \" (run_shell executes a single command without a shell.)\";\n const parsed = parseShellLine(command, \"posix\");\n const segments: ShellSegment[] = parsed.ok ? parsed.segments : [];\n const listOperator = token === \"&&\" || token === \"||\" || token === \";\" || token === \"\\n\";\n\n if (listOperator && segments.length >= 2) {\n const shownOperator = token === \"\\n\" ? \"A line break\" : `\\`${token}\\``;\n const firstWords = (segments[0]?.source ?? \"\").trim().split(/\\s+/);\n\n if (firstWords[0] === \"cd\") {\n const rest = segments.slice(1).map((segment) => show(segment.source));\n\n return new Refusal(\n \"metacharacter\",\n `${shownOperator} is not supported in argv mode, and \\`cd\\` does not carry over: every command starts in the workspace root. Run ${rest.join(\", then \")} on its own, with paths relative to the root.${suffix}`,\n );\n }\n\n const steps = segments.map((segment) => show(segment.source));\n const joiner = token === \"||\" ? \", and if it fails, \" : \", then \";\n\n return new Refusal(\n \"metacharacter\",\n `${shownOperator} is not supported in argv mode: run ${steps.join(joiner)}, as separate commands.${suffix}`,\n );\n }\n\n if (token === \"&\" && segments.length >= 1) {\n const steps = segments.map((segment) => show(segment.source));\n const run =\n steps.length === 1\n ? `run ${steps[0] ?? \"\"} without it`\n : `run ${steps.join(\", then \")} as separate commands`;\n\n return new Refusal(\n \"metacharacter\",\n `\\`&\\` is not supported in argv mode: ${run}; start a long-running command with background: true.${suffix}`,\n );\n }\n\n if (token === \"|\") {\n const pipeline = command\n .split(\"|\")\n .map((part) => part.trim())\n .filter(Boolean);\n\n return new Refusal(\n \"metacharacter\",\n `\\`|\\` (a pipe) is not supported in argv mode: run ${show(pipeline[0] ?? command)} on its own and read its output yourself.${suffix}`,\n );\n }\n\n if (token === \">\" || token === \">>\" || token === \">&\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` (redirection) is not supported in argv mode: run the command without it and save the output with write_file if you need it.${suffix}`,\n );\n }\n\n if (token === \"<\" || token === \"<<\" || token === \"<&\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` (input redirection) is not supported in argv mode: pass the file path to the command as an argument.${suffix}`,\n );\n }\n\n if (token === \"$(\" || token === \"`\") {\n return new Refusal(\n \"metacharacter\",\n `Command substitution (${token === \"$(\" ? \"`$(…)`\" : \"backticks\"}) is not supported in argv mode: run the inner command first, then pass its output literally.${suffix}`,\n );\n }\n\n if (token === \"$\" || token === \"${\") {\n return new Refusal(\n \"metacharacter\",\n `\\`$\\` (variable expansion) is not supported in argv mode: write the value literally, or put the \\`$\\` inside quotes to pass it as text.${suffix}`,\n );\n }\n\n if (token === \"(\" || token === \")\") {\n return new Refusal(\n \"metacharacter\",\n `\\`${token}\\` is not supported in argv mode: put it inside quotes to pass it as text.${suffix}`,\n );\n }\n\n return new Refusal(\n \"metacharacter\",\n `\\`${token === \"\\n\" ? \"\\\\n\" : token}\\` is not supported in argv mode: run one command per call, and quote any argument that contains it.${suffix}`,\n );\n}\n\n/** Check a shell-mode line. */\nfunction checkShellLine(\n checker: LineChecker,\n policy: WorkspacePolicy,\n command: string,\n dialect: ShellDialect,\n): void {\n if (/^[-+]/.test(command)) {\n throw new Refusal(\"unsupported-syntax\", \"A command line may not start with `-` or `+`.\");\n }\n\n const parsed = parseShellLine(command, dialect);\n\n if (!parsed.ok) {\n if (checker.askMode) {\n checker.addAsk([command], \"unparsed\");\n\n return;\n }\n\n throw new Refusal(\n parsed.reason,\n `${parsed.message} The workspace shell policy only runs lines it can check.`,\n );\n }\n\n if (parsed.background) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`&` (run in the background) is not supported in a command line: start a long-running command with background: true instead.\",\n );\n }\n\n let cwds = [path.resolve(policy.cwd)];\n\n for (const simple of parsed.commands) {\n cwds = checkSimpleCommand(checker, simple, cwds, dialect);\n }\n}\n\n/** Check one parsed simple command; returns the possible working directories after it. */\nfunction checkSimpleCommand(\n checker: LineChecker,\n simple: ShellSimpleCommand,\n cwds: string[],\n dialect: ShellDialect,\n): string[] {\n for (const assignment of simple.assignments) {\n const name = assignment.assignmentName ?? \"\";\n\n if (SENSITIVE_VARIABLES.has(name) || name.startsWith(\"BASH_FUNC_\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `Assigning \\`${name}\\` is not supported: it changes how the shell runs the commands that follow.`,\n );\n }\n }\n\n for (const redirect of simple.redirects) {\n if (redirect.type === \"read\" || redirect.type === \"write\") {\n checker.checkRedirectTarget(redirect.target, redirect.type, cwds);\n }\n }\n\n const first = simple.words[0];\n\n if (first === undefined) {\n return cwds;\n }\n\n const executable = first.dynamic ? \"\" : executableBasename(first.value, dialect).toLowerCase();\n const powershell = dialect === \"powershell\";\n\n if (executable === \"cd\" || (powershell && POWERSHELL_CD_NAMES.has(executable))) {\n // A `cd` the policy cannot follow would let later targets escape the jail:\n // it is refused outright, never turned into an ask.\n checker.assertExecutableNotDenied(\n executable,\n simple.words.map((word) => word.value),\n );\n\n if (powershell) {\n const words = normalizePowerShellCd(simple.words);\n\n if (words === null) {\n throw new Refusal(\n \"unsupported-syntax\",\n \"`Set-Location` is supported with one directory (`-Path` / `-LiteralPath` or positional) only.\",\n \"cd\",\n );\n }\n\n return checker.checkCd(words, cwds);\n }\n\n return checker.checkCd(simple.words, cwds);\n }\n\n try {\n if (!first.dynamic && first.value.includes(\"=\")) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${first.raw}\\` is not a plain command name or \\`NAME=value\\` assignment.`,\n );\n }\n\n if (/^[-+]/.test(first.value)) {\n throw new Refusal(\"unsupported-syntax\", `\\`${first.raw}\\` is not a command name.`);\n }\n\n if (checker.hasArgumentHooks) {\n const dynamic = simple.words.find((word) => word.dynamic);\n\n if (dynamic !== undefined) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${dynamic.raw}\\` is decided when the line runs, so it cannot be checked against the workspace shell policy's command rules. Write the value literally (quote globs).`,\n );\n }\n }\n\n if (\n (dialect === \"posix\" && (executable === \"pushd\" || executable === \"popd\")) ||\n (powershell && POWERSHELL_DIRECTORY_STACK.has(executable))\n ) {\n throw new Refusal(\n \"unsupported-syntax\",\n `\\`${executable}\\` is not supported; use \\`${powershell ? \"Set-Location\" : \"cd\"}\\`.`,\n );\n }\n\n const argv = simple.words.map((word) => (word.dynamic ? word.raw : word.value));\n\n checker.checkArgv(\n argv,\n simple.words.map((word) => word.dynamic),\n 0,\n );\n\n if (powershell) {\n const canonical = canonicalPowerShellArgv(argv);\n\n if (canonical !== undefined && canonical.join(\"\\u0000\") !== argv.join(\"\\u0000\")) {\n checker.checkCanonical(canonical);\n }\n }\n } catch (error) {\n // A command the policy cannot read is put to the host instead of refused.\n if (error instanceof Refusal && error.reason === \"unsupported-syntax\" && checker.askMode) {\n checker.addAsk(\n simple.words.map((word) => (word.dynamic ? word.raw : word.value)),\n \"unparsed\",\n );\n\n return cwds;\n }\n\n throw error;\n }\n\n return cwds;\n}\n\n/**\n * The shell policy's verdict on a command line, without touching the\n * filesystem.\n *\n * - **argv mode** (`interpreter: \"none\"`, the default): the line must\n * tokenize into one argv; otherwise it is refused with a\n * `\"metacharacter\"` / `\"unbalanced-quotes\"` reason and a message that\n * names the operator and suggests the split.\n * - **shell mode** (`interpreter: \"sh\"`): the line is parsed and every\n * simple command — inside `;`, `&&`, `||`, pipes, subshells, `$( )`,\n * backticks and here-documents — is checked; redirection targets and `cd`\n * directories must stay in the jail (checked lexically here; `exec`\n * re-checks them with symlinks resolved).\n *\n * Every argv goes through `deny` (case-insensitive, wins), `allow`\n * (exhaustive when non-empty), `denyPatterns` and `allowCommand`.\n * Wrappers (`env`, `time`, `timeout`, `nice`, `nohup`, `command`,\n * `builtin`, `exec`) and `find -exec` are followed to the command they run.\n * A runner the policy cannot see into (`bash -c`, `eval`, `xargs`, `sudo`,\n * …) is refused while the policy has deny rules, unless `allow` names it.\n *\n * @example\n * checkShellCommand({ cwd, shell: { allow: [\"git\"] } }, \"git add -A && git commit -m wip\");\n * // { allowed: false, reason: \"metacharacter\", message: \"`&&` is not supported in argv mode: run `git add -A`, then `git commit -m wip`, …\" }\n */\nexport function checkShellCommand(\n policy: WorkspacePolicy,\n command: string,\n options?: ShellCheckOptions,\n): WorkspaceShellCheck {\n const resolved =\n options?.resolved ?? resolveShell(policy.shell, options?.host ?? currentShellHost());\n\n return runCheck(policy, command, resolved.interpreter, resolved.dialect, options?.remembered);\n}\n\n/**\n * {@link checkShellCommand} with the shell-mode dialect chosen explicitly\n * instead of from the interpreter (used to test the cmd.exe and PowerShell\n * rules anywhere). Argv mode ignores `dialect`.\n */\nexport function checkShellCommandInDialect(\n policy: WorkspacePolicy,\n command: string,\n dialect: ShellDialect,\n options?: ShellCheckOptions,\n): WorkspaceShellCheck {\n const resolved =\n options?.resolved ?? resolveShell(policy.shell, options?.host ?? currentShellHost());\n\n return runCheck(policy, command, resolved.interpreter, dialect, options?.remembered);\n}\n\nfunction runCheck(\n policy: WorkspacePolicy,\n command: string,\n interpreter: ResolvedShellInterpreter,\n dialect: ShellDialect,\n remembered?: readonly WorkspaceCommandPattern[],\n): WorkspaceShellCheck {\n const shell = policy.shell;\n\n if (!shell) {\n return {\n allowed: false,\n reason: \"not-allowed\",\n message: \"The workspace has no shell policy, so no command may run.\",\n asks: [],\n commands: [],\n paths: [],\n };\n }\n\n const shellMode = interpreter !== \"none\";\n const checker = new LineChecker(\n policy,\n shell,\n command,\n shellMode ? dialect : \"posix\",\n interpreter,\n remembered,\n );\n\n try {\n if (command.trim() === \"\") {\n throw new Refusal(\"not-allowed\", \"The command is empty.\");\n }\n\n if (shellMode) {\n checkShellLine(checker, policy, command, dialect);\n } else {\n const argv = tokenizeCommand(command);\n\n if (argv === null) {\n throw argvModeRefusal(command);\n }\n\n checker.checkArgv(\n argv,\n argv.map(() => false),\n 0,\n );\n }\n\n if (checker.asks.length > 0) {\n const first = checker.asks[0] as WorkspaceShellAsk;\n const shown = show(first.argv.join(\" \"));\n const why = first.rule?.reason !== undefined ? ` ${first.rule.reason}` : \"\";\n\n return {\n allowed: false,\n reason: \"approval-required\",\n message: `${shown} needs approval, and the workspace shell policy has no approval handler (shell.onAsk).${why}`,\n asks: checker.asks,\n commands: checker.commands,\n paths: checker.paths,\n };\n }\n\n return { allowed: true, asks: [], commands: checker.commands, paths: checker.paths };\n } catch (error) {\n if (!(error instanceof Refusal)) {\n throw error;\n }\n\n return {\n allowed: false,\n reason: error.reason,\n message: error.message,\n ...(error.executable !== undefined ? { executable: error.executable } : {}),\n ...(error.refusedPath !== undefined ? { path: error.refusedPath } : {}),\n asks: [],\n commands: checker.commands,\n paths: checker.paths,\n };\n }\n}\n\n/**\n * The simple commands a line would run (pipeline members, `&&` / `||` / `;`\n * parts, subshells and substitutions included), without checking anything.\n * A line the parser cannot read with confidence comes back with `opaque`\n * set to the reason and no commands.\n *\n * The line is read with the policy's shell dialect when its `interpreter`\n * is a shell, and with the platform's default shell otherwise.\n *\n * @example\n * classifyShellCommand(\"a | b\"); // { commands: [[\"a\"], [\"b\"]] }\n */\nexport function classifyShellCommand(\n command: string,\n policy?: Pick<WorkspacePolicy, \"shell\">,\n host?: ShellHost,\n): WorkspaceShellClassification {\n const resolved = resolveShell(policy?.shell, host ?? currentShellHost());\n const dialect = resolved.interpreter === \"none\" ? shellDialect() : resolved.dialect;\n const parsed = parseShellLine(command, dialect);\n\n if (!parsed.ok) {\n return { commands: [], opaque: parsed.message };\n }\n\n return {\n commands: parsed.commands\n .filter((simple) => simple.words.length > 0)\n .map((simple) => simple.words.map((word) => (word.dynamic ? word.raw : word.value))),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AA+EA,MAAM,iCAAiB,IAAI,IAAI;CAE7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;AAMD,MAAM,oCAAoB,IAAI,IAAI;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;AAGD,MAAM,wCAAwB,IAAI,IAAI;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;AAGD,MAAM,sCAAsB,IAAI,IAAI;CAAC;CAAQ;CAAS;CAAO;CAAS;AAAM,CAAC;;;;;;AAO7E,MAAM,gDAAgC,IAAI,IAAI;CAC5C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;AAQD,MAAM,sCAAsB,IAAI,IAAI;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;AAGD,MAAM,iCAAiB,IAAI,IAAI;CAAC;CAAa;CAAe;AAAa,CAAC;;AAG1E,MAAM,qCAAqB,IAAI,IAAI,CAAC,KAAK,CAAC;;AAG1C,MAAM,oBAAoB;;AAG1B,MAAM,oBAAoB;;AAG1B,MAAM,oBAAoB;;AAG1B,SAAgB,eAA6B;CAC3C,OAAO,QAAQ,aAAa,UAAU,QAAQ;AAChD;;;;;;;AAQA,SAAgB,mBAAmB,YAAoB,UAAwB,SAAiB;CAM9F,QAJE,YAAY,SAAS,YAAY,eAC7B,KAAK,MAAM,SAAS,UAAU,IAC9B,KAAK,SAAS,UAAU,EAEnB,CAAC,QACV,YAAY,eAAe,8BAA8B,yBACzD,EACF;AACF;;AAGA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"}
|