@warlock.js/ai-workspace 5.44.0 → 5.46.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 +13 -0
- package/cjs/index.cjs +101 -35
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/shell-approval.type.d.mts +11 -1
- package/esm/contracts/shell-approval.type.d.mts.map +1 -1
- package/esm/contracts/tool-io.type.d.mts +10 -0
- package/esm/contracts/tool-io.type.d.mts.map +1 -1
- package/esm/contracts/workspace.contract.d.mts +12 -1
- package/esm/contracts/workspace.contract.d.mts.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/ops.d.mts.map +1 -1
- package/esm/ops.mjs +27 -10
- package/esm/ops.mjs.map +1 -1
- package/esm/output/spill.mjs +4 -1
- package/esm/output/spill.mjs.map +1 -1
- package/esm/policy/approval-helpers.mjs +22 -0
- package/esm/policy/approval-helpers.mjs.map +1 -0
- package/esm/policy/powershell-parser.mjs +6 -4
- package/esm/policy/powershell-parser.mjs.map +1 -1
- package/esm/policy/shell-check.d.mts.map +1 -1
- package/esm/policy/shell-check.mjs +4 -4
- package/esm/policy/shell-check.mjs.map +1 -1
- package/esm/policy/shell-interpreter.d.mts +20 -2
- package/esm/policy/shell-interpreter.d.mts.map +1 -1
- package/esm/policy/shell-interpreter.mjs +26 -12
- package/esm/policy/shell-interpreter.mjs.map +1 -1
- package/esm/policy/shell-parser.d.mts +3 -1
- package/esm/policy/shell-parser.d.mts.map +1 -1
- package/esm/policy/shell-parser.mjs +1 -1
- package/esm/policy/shell-parser.mjs.map +1 -1
- package/esm/tools/run-shell.d.mts +1 -1
- package/esm/tools/run-shell.d.mts.map +1 -1
- package/esm/tools/run-shell.mjs +2 -1
- package/esm/tools/run-shell.mjs.map +1 -1
- package/esm/tools/run-tests.d.mts +1 -1
- package/esm/tools/run-tests.mjs +1 -1
- package/esm/tools/run-tests.mjs.map +1 -1
- package/esm/workspace.d.mts.map +1 -1
- package/esm/workspace.mjs +12 -1
- package/esm/workspace.mjs.map +1 -1
- package/llms-full.txt +23 -17
- package/package.json +3 -3
- package/skills/use-a-workspace/SKILL.md +23 -17
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region ../../packages/ai-workspace/src/policy/approval-helpers.ts
|
|
2
|
+
/** The longest refusal reason passed on to the model, in characters. */
|
|
3
|
+
const MAX_REFUSAL_REASON = 500;
|
|
4
|
+
/** Whether two command patterns are the same (strings by value, RegExps by source and flags). */
|
|
5
|
+
function samePattern(a, b) {
|
|
6
|
+
if (typeof a === "string" || typeof b === "string") return a === b;
|
|
7
|
+
return a.source === b.source && a.flags === b.flags;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The tail of a refusal message for an owner's reason: `"."` without one,
|
|
11
|
+
* otherwise `" — <reason>"` cut at {@link MAX_REFUSAL_REASON} characters with
|
|
12
|
+
* a visible marker.
|
|
13
|
+
*/
|
|
14
|
+
function refusalReason(reason) {
|
|
15
|
+
const text = typeof reason === "string" ? reason.trim() : "";
|
|
16
|
+
if (text.length === 0) return ".";
|
|
17
|
+
return ` — ${text.length > 500 ? `${text.slice(0, 500)}… [truncated]` : text}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { refusalReason, samePattern };
|
|
22
|
+
//# sourceMappingURL=approval-helpers.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-helpers.mjs","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/policy/approval-helpers.ts"],"sourcesContent":["import type { WorkspaceCommandPattern } from \"../contracts\";\n\n/** The longest refusal reason passed on to the model, in characters. */\nexport const MAX_REFUSAL_REASON = 500;\n\n/** Whether two command patterns are the same (strings by value, RegExps by source and flags). */\nexport function samePattern(a: WorkspaceCommandPattern, b: WorkspaceCommandPattern): boolean {\n if (typeof a === \"string\" || typeof b === \"string\") {\n return a === b;\n }\n\n return a.source === b.source && a.flags === b.flags;\n}\n\n/**\n * The tail of a refusal message for an owner's reason: `\".\"` without one,\n * otherwise `\" — <reason>\"` cut at {@link MAX_REFUSAL_REASON} characters with\n * a visible marker.\n */\nexport function refusalReason(reason: string | undefined): string {\n const text = typeof reason === \"string\" ? reason.trim() : \"\";\n\n if (text.length === 0) {\n return \".\";\n }\n\n const shown =\n text.length > MAX_REFUSAL_REASON ? `${text.slice(0, MAX_REFUSAL_REASON)}… [truncated]` : text;\n\n return ` — ${shown}`;\n}\n"],"mappings":";;AAGA,MAAa,qBAAqB;;AAGlC,SAAgB,YAAY,GAA4B,GAAqC;CAC3F,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UACxC,OAAO,MAAM;CAGf,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE;AAChD;;;;;;AAOA,SAAgB,cAAc,QAAoC;CAChE,MAAM,OAAO,OAAO,WAAW,WAAW,OAAO,KAAK,IAAI;CAE1D,IAAI,KAAK,WAAW,GAClB,OAAO;CAMT,OAAO,MAFL,KAAK,eAA8B,GAAG,KAAK,MAAM,MAAqB,EAAE,iBAAiB;AAG7F"}
|
|
@@ -71,10 +71,11 @@ function unsupported(message) {
|
|
|
71
71
|
return new PowerShellRefusal("unsupported-syntax", message);
|
|
72
72
|
}
|
|
73
73
|
var PowerShellParser = class PowerShellParser {
|
|
74
|
-
constructor(src, commands = [], depth = 0) {
|
|
74
|
+
constructor(src, commands = [], depth = 0, chaining = true) {
|
|
75
75
|
this.src = src;
|
|
76
76
|
this.commands = commands;
|
|
77
77
|
this.depth = depth;
|
|
78
|
+
this.chaining = chaining;
|
|
78
79
|
this.pos = 0;
|
|
79
80
|
}
|
|
80
81
|
atEnd() {
|
|
@@ -116,6 +117,7 @@ var PowerShellParser = class PowerShellParser {
|
|
|
116
117
|
}
|
|
117
118
|
readListOperator() {
|
|
118
119
|
const char = this.peek();
|
|
120
|
+
if (!this.chaining && (char === "&" && this.peek(1) === "&" || char === "|" && this.peek(1) === "|")) throw unsupported(`Windows PowerShell 5.1 cannot run ${char}${char}; use ; or run each command in its own call instead.`);
|
|
119
121
|
if (char === "&" && this.peek(1) === "&") {
|
|
120
122
|
this.pos += 2;
|
|
121
123
|
return "&&";
|
|
@@ -370,7 +372,7 @@ var PowerShellParser = class PowerShellParser {
|
|
|
370
372
|
}
|
|
371
373
|
if (level !== 0) throw new PowerShellRefusal("unbalanced-quotes", "A `$(` subexpression is never closed.");
|
|
372
374
|
const inner = this.src.slice(open + 1, index);
|
|
373
|
-
new PowerShellParser(inner, this.commands, this.depth + 1).parseProgram([]);
|
|
375
|
+
new PowerShellParser(inner, this.commands, this.depth + 1, this.chaining).parseProgram([]);
|
|
374
376
|
this.pos = index + 1;
|
|
375
377
|
}
|
|
376
378
|
/** Read `$name`, `${name}` or `$env:NAME` at the cursor; a `$(` is refused. */
|
|
@@ -392,9 +394,9 @@ var PowerShellParser = class PowerShellParser {
|
|
|
392
394
|
}
|
|
393
395
|
};
|
|
394
396
|
/** Parse a PowerShell command line for the shell policy. Never throws. */
|
|
395
|
-
function parsePowerShellLine(line) {
|
|
397
|
+
function parsePowerShellLine(line, options) {
|
|
396
398
|
const segments = [];
|
|
397
|
-
const parser = new PowerShellParser(line);
|
|
399
|
+
const parser = new PowerShellParser(line, [], 0, options?.chaining !== false);
|
|
398
400
|
try {
|
|
399
401
|
parser.parseProgram(segments);
|
|
400
402
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"powershell-parser.mjs","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/policy/powershell-parser.ts"],"sourcesContent":["import type {\n ShellParseResult,\n ShellRedirect,\n ShellSegment,\n ShellSimpleCommand,\n ShellWord,\n} from \"./shell-parser\";\n\n/**\n * Parser for the PowerShell dialect (`powershell -Command` / `pwsh -Command`).\n *\n * Like the other dialects it is deliberately conservative: it finds every\n * simple command a line would run and every redirection target, and refuses\n * what it cannot read with confidence. Supported: `;` and line breaks, `&&`,\n * `||`, `|`, `$( )` subexpressions (their commands are checked too), the call\n * operator `&` in front of a command, single and double\n * quotes (including the typographic quotes PowerShell treats as quotes),\n * backtick escapes, `$name` / `${name}` / `$env:NAME` variables (dynamic\n * words), and the redirections `>`, `>>`, `2>`, `2>>`, `*>`, `2>&1`.\n *\n * Refused: script blocks and braces, bare parentheses,\n * `@( )` / `@{ }` / splatting / here-strings, the stop-parsing token `--%`,\n * `<` redirection, comments, the comma operator, compound-statement\n * keywords (`if`, `foreach`, `function`, `try`, …), dot-sourcing, a `&` that\n * is not a call operator (background jobs), and type literals.\n */\n\n/** Statement keywords that start a compound command; refused in command position. */\nconst POWERSHELL_KEYWORDS = new Set([\n \"if\",\n \"elseif\",\n \"else\",\n \"switch\",\n \"foreach\",\n \"for\",\n \"while\",\n \"do\",\n \"until\",\n \"function\",\n \"filter\",\n \"workflow\",\n \"param\",\n \"begin\",\n \"process\",\n \"end\",\n \"try\",\n \"catch\",\n \"finally\",\n \"trap\",\n \"data\",\n \"class\",\n \"enum\",\n \"using\",\n \"return\",\n \"break\",\n \"continue\",\n \"throw\",\n \"parallel\",\n \"sequence\",\n \"inlinescript\",\n]);\n\n/** Characters PowerShell reads as a single quote. */\nconst SINGLE_QUOTES = \"'‘’‚‛\";\n\n/** Characters PowerShell reads as a double quote. */\nconst DOUBLE_QUOTES = '\"“”„';\n\n/** Variable-name characters after `$`. */\nconst VARIABLE_CHAR = /[A-Za-z0-9_:?^$]/;\n\n/** Whether `char` (never the empty end-of-input string) is one of `set`. */\nfunction inSet(set: string, char: string): boolean {\n return char !== \"\" && set.includes(char);\n}\n\nclass PowerShellRefusal extends Error {\n public constructor(\n public readonly reason: \"unbalanced-quotes\" | \"unsupported-syntax\",\n message: string,\n ) {\n super(message);\n }\n}\n\nfunction unsupported(message: string): PowerShellRefusal {\n return new PowerShellRefusal(\"unsupported-syntax\", message);\n}\n\nclass PowerShellParser {\n private pos = 0;\n\n public constructor(\n private readonly src: string,\n public readonly commands: ShellSimpleCommand[] = [],\n private readonly depth = 0,\n ) {}\n\n private atEnd(): boolean {\n return this.pos >= this.src.length;\n }\n\n private peek(offset = 0): string {\n return this.src.charAt(this.pos + offset);\n }\n\n private skipBlanks(): void {\n while (this.peek() === \" \" || this.peek() === \"\\t\") {\n this.pos++;\n }\n }\n\n private atLineBreak(): boolean {\n return this.peek() === \"\\n\" || this.peek() === \"\\r\";\n }\n\n public parseProgram(segments: ShellSegment[]): void {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd()) {\n if (segments.length === 0) {\n throw unsupported(\"The command is empty.\");\n }\n\n if (\n segments[segments.length - 1]?.operator === \"&&\" ||\n segments[segments.length - 1]?.operator === \"||\"\n ) {\n throw unsupported(\"The line ends with an operator but no command follows it.\");\n }\n\n return;\n }\n\n const start = this.pos;\n\n this.parsePipeline();\n\n const end = this.pos;\n\n this.skipBlanks();\n\n const operator = this.readListOperator();\n\n segments.push({\n source: this.src.slice(start, end).trim(),\n ...(operator !== null ? { operator } : {}),\n });\n\n if (operator === null) {\n if (!this.atEnd()) {\n throw unsupported(`Unexpected \\`${this.peek()}\\`.`);\n }\n\n return;\n }\n\n if (operator === \";\" || operator === \"\\n\") {\n // Further separators and blanks are allowed after one; a trailing run is fine.\n while (this.peek() === \" \" || this.peek() === \"\\t\" || this.atLineBreak()) {\n this.pos++;\n }\n } else {\n this.skipBlanks();\n }\n }\n }\n\n private readListOperator(): string | null {\n const char = this.peek();\n\n if (char === \"&\" && this.peek(1) === \"&\") {\n this.pos += 2;\n\n return \"&&\";\n }\n\n if (char === \"|\" && this.peek(1) === \"|\") {\n this.pos += 2;\n\n return \"||\";\n }\n\n if (char === \";\") {\n this.pos++;\n\n return \";\";\n }\n\n if (this.atLineBreak()) {\n while (this.atLineBreak()) {\n this.pos++;\n }\n\n return \"\\n\";\n }\n\n return null;\n }\n\n private parsePipeline(): void {\n this.parseCommand();\n\n while (this.peek() === \"|\" && this.peek(1) !== \"|\") {\n this.pos++;\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || this.peek() === \";\") {\n throw unsupported(\"The line ends with `|` but no command follows it.\");\n }\n\n this.parseCommand();\n }\n }\n\n private atRedirectStart(): boolean {\n const char = this.peek();\n\n if (char === \">\") {\n return true;\n }\n\n return /[1-6*]/.test(char) && this.peek(1) === \">\";\n }\n\n private parseCommand(): void {\n const start = this.pos;\n const command: ShellSimpleCommand = { assignments: [], words: [], redirects: [], source: \"\" };\n let callOperator = false;\n\n if (this.peek() === \"&\" && this.peek(1) !== \"&\") {\n callOperator = true;\n this.pos++;\n this.skipBlanks();\n }\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || this.peek() === \";\" || this.peek() === \"|\") {\n break;\n }\n\n if (this.peek() === \"&\") {\n if (this.peek(1) === \"&\") {\n break;\n }\n\n throw unsupported(\n \"`&` is only supported as the call operator in front of a command (not as a background job).\",\n );\n }\n\n if (this.peek() === \"<\") {\n throw unsupported(\"`<` is not supported in PowerShell command lines.\");\n }\n\n if (this.atRedirectStart()) {\n command.redirects.push(this.parseRedirect());\n\n continue;\n }\n\n const word = this.parseWord();\n\n command.words.push(word);\n\n if (command.words.length === 1) {\n this.checkCommandWord(word);\n }\n }\n\n if (command.words.length === 0 && (callOperator || command.redirects.length === 0)) {\n throw unsupported(\"The line has an empty command (two operators in a row?).\");\n }\n\n command.source = this.src.slice(start, this.pos).trim();\n this.commands.push(command);\n }\n\n private checkCommandWord(word: ShellWord): void {\n if (word.raw.startsWith(\"[\")) {\n throw unsupported(`\\`${word.raw}\\` (a type literal) is not supported.`);\n }\n\n if (!word.quoted && word.value === \".\") {\n throw unsupported(\n \"Dot-sourcing (`. file`) runs a script in the current scope and is not supported.\",\n );\n }\n\n if (!word.quoted && POWERSHELL_KEYWORDS.has(word.value.toLowerCase())) {\n throw unsupported(`\\`${word.value}\\` starts a PowerShell statement, which is not supported.`);\n }\n }\n\n private parseRedirect(): ShellRedirect {\n let fd: number | undefined;\n\n if (this.peek() === \"*\") {\n this.pos++;\n } else if (/[1-6]/.test(this.peek())) {\n fd = Number(this.peek());\n this.pos++;\n }\n\n let op = \">\";\n\n if (this.peek(1) === \">\") {\n op = \">>\";\n }\n\n this.pos += op.length;\n\n if (op === \">\" && this.peek() === \"&\") {\n if (/[1-6]/.test(this.peek(1))) {\n const target: ShellWord = {\n raw: this.peek(1),\n value: this.peek(1),\n dynamic: false,\n quoted: false,\n };\n\n this.pos += 2;\n\n return { op: \">&\", ...(fd !== undefined ? { fd } : {}), target, type: \"dup\" };\n }\n\n throw unsupported(\"Only `>&1`-style stream merges (`2>&1`) are supported.\");\n }\n\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || /[;&|<>]/.test(this.peek())) {\n throw unsupported(`The redirection \\`${op}\\` has no target.`);\n }\n\n const target = this.parseWord();\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"write\" };\n }\n\n private parseWord(): ShellWord {\n const start = this.pos;\n let value = \"\";\n let dynamic = false;\n let quoted = false;\n\n if (this.src.startsWith(\"--%\", this.pos) && /[\\s;|&]|^$/.test(this.src.charAt(this.pos + 3))) {\n throw unsupported(\"The stop-parsing token `--%` is not supported.\");\n }\n\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (\n char === \" \" ||\n char === \"\\t\" ||\n char === \"\\n\" ||\n char === \"\\r\" ||\n char === \";\" ||\n char === \"|\" ||\n char === \"&\" ||\n char === \">\" ||\n char === \"<\"\n ) {\n break;\n }\n\n if (inSet(SINGLE_QUOTES, char)) {\n this.pos++;\n quoted = true;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A single quote is never closed.\");\n }\n\n const inner = this.peek();\n\n if (inSet(SINGLE_QUOTES, inner)) {\n if (inSet(SINGLE_QUOTES, this.peek(1))) {\n value += this.peek(1);\n this.pos += 2;\n\n continue;\n }\n\n this.pos++;\n\n break;\n }\n\n value += inner;\n this.pos++;\n }\n\n continue;\n }\n\n if (inSet(DOUBLE_QUOTES, char)) {\n this.pos++;\n quoted = true;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A double quote is never closed.\");\n }\n\n const inner = this.peek();\n\n if (inSet(DOUBLE_QUOTES, inner)) {\n if (inSet(DOUBLE_QUOTES, this.peek(1))) {\n value += this.peek(1);\n this.pos += 2;\n\n continue;\n }\n\n this.pos++;\n\n break;\n }\n\n if (inner === \"`\") {\n const next = this.peek(1);\n\n if (next !== \"\" && (inSet(DOUBLE_QUOTES, next) || next === \"`\" || next === \"$\")) {\n value += next;\n this.pos += 2;\n\n continue;\n }\n\n throw unsupported(\n 'Only `\"`, `` ` `` and `$` can follow a backtick inside double quotes.',\n );\n }\n\n if (inner === \"$\") {\n const read = this.readVariable();\n\n value += read;\n dynamic = true;\n\n continue;\n }\n\n value += inner;\n this.pos++;\n }\n\n continue;\n }\n\n if (char === \"`\") {\n const next = this.peek(1);\n\n if (next === \"\" || next === \"\\n\" || next === \"\\r\") {\n throw unsupported(\n \"A backtick at the end of a line (line continuation) is not supported.\",\n );\n }\n\n value += next;\n quoted = true;\n this.pos += 2;\n\n continue;\n }\n\n if (char === \"$\") {\n const wasLiteral = this.src.charAt(this.pos + 1);\n\n if (\n wasLiteral === \"\" ||\n !(wasLiteral === \"{\" || wasLiteral === \"(\" || VARIABLE_CHAR.test(wasLiteral))\n ) {\n value += \"$\";\n this.pos++;\n\n continue;\n }\n\n value += this.readVariable();\n dynamic = true;\n\n continue;\n }\n\n if (char === \"@\" && this.pos === start) {\n const next = this.peek(1);\n\n if (\n next === \"(\" ||\n next === \"{\" ||\n next === \"'\" ||\n next === '\"' ||\n /[A-Za-z_$]/.test(next)\n ) {\n throw unsupported(\n \"Arrays (`@( )`), hashtables, splatting (`@name`) and here-strings are not supported.\",\n );\n }\n }\n\n if (char === \"{\" || char === \"}\") {\n throw unsupported(\"Script blocks and braces are not supported.\");\n }\n\n if (char === \"(\" || char === \")\") {\n throw unsupported(\n \"Parentheses (grouping and `$( )` subexpressions) are not supported; quote the text.\",\n );\n }\n\n if (char === \",\") {\n throw unsupported(\n \"The comma operator builds an array and is not supported; quote the text.\",\n );\n }\n\n if (char === \"#\" && this.pos === start) {\n throw unsupported(\"Comments are not supported in a command line.\");\n }\n\n if (char === \"*\" || char === \"?\" || char === \"[\" || (char === \"~\" && this.pos === start)) {\n dynamic = true;\n }\n\n value += char;\n this.pos++;\n }\n\n return { raw: this.src.slice(start, this.pos), value, dynamic, quoted };\n }\n\n /**\n * Parse a `$( … )` subexpression: its commands run, so they are collected\n * (before the command that contains it) and checked like any other.\n */\n private parseSubexpression(): void {\n if (this.depth >= 8) {\n throw unsupported(\"`$( )` subexpressions are nested too deeply.\");\n }\n\n const open = this.pos;\n let level = 0;\n let index = open;\n\n for (; index < this.src.length; index++) {\n const char = this.src.charAt(index);\n\n if (inSet(SINGLE_QUOTES, char) || inSet(DOUBLE_QUOTES, char)) {\n const set = inSet(SINGLE_QUOTES, char) ? SINGLE_QUOTES : DOUBLE_QUOTES;\n\n index++;\n\n while (index < this.src.length) {\n const inner = this.src.charAt(index);\n\n if (inner === \"`\" && set === DOUBLE_QUOTES) {\n index += 2;\n\n continue;\n }\n\n if (inSet(set, inner)) {\n if (inSet(set, this.src.charAt(index + 1))) {\n index += 2;\n\n continue;\n }\n\n break;\n }\n\n index++;\n }\n\n continue;\n }\n\n if (char === \"`\") {\n index++;\n\n continue;\n }\n\n if (char === \"(\") {\n level++;\n } else if (char === \")\") {\n level--;\n\n if (level === 0) {\n break;\n }\n }\n }\n\n if (level !== 0) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A `$(` subexpression is never closed.\");\n }\n\n const inner = this.src.slice(open + 1, index);\n\n new PowerShellParser(inner, this.commands, this.depth + 1).parseProgram([]);\n this.pos = index + 1;\n }\n\n /** Read `$name`, `${name}` or `$env:NAME` at the cursor; a `$(` is refused. */\n private readVariable(): string {\n const begin = this.pos;\n\n this.pos++;\n\n if (this.peek() === \"(\") {\n this.parseSubexpression();\n\n return this.src.slice(begin, this.pos);\n }\n\n if (this.peek() === \"{\") {\n const close = this.src.indexOf(\"}\", this.pos);\n\n if (close === -1) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A `${` variable name is never closed.\");\n }\n\n this.pos = close + 1;\n\n return this.src.slice(begin, this.pos);\n }\n\n while (!this.atEnd() && VARIABLE_CHAR.test(this.peek())) {\n this.pos++;\n }\n\n return this.src.slice(begin, this.pos);\n }\n}\n\n/** Parse a PowerShell command line for the shell policy. Never throws. */\nexport function parsePowerShellLine(line: string): ShellParseResult {\n const segments: ShellSegment[] = [];\n const parser = new PowerShellParser(line);\n\n try {\n parser.parseProgram(segments);\n\n return { ok: true, commands: parser.commands, segments, background: false };\n } catch (error) {\n if (error instanceof PowerShellRefusal) {\n return { ok: false, reason: error.reason, message: error.message };\n }\n\n throw error;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"powershell-parser.mjs","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/policy/powershell-parser.ts"],"sourcesContent":["import type {\n ShellParseResult,\n ShellRedirect,\n ShellSegment,\n ShellSimpleCommand,\n ShellWord,\n} from \"./shell-parser\";\n\n/**\n * Parser for the PowerShell dialect (`powershell -Command` / `pwsh -Command`).\n *\n * Like the other dialects it is deliberately conservative: it finds every\n * simple command a line would run and every redirection target, and refuses\n * what it cannot read with confidence. Supported: `;` and line breaks, `&&`,\n * `||`, `|`, `$( )` subexpressions (their commands are checked too), the call\n * operator `&` in front of a command, single and double\n * quotes (including the typographic quotes PowerShell treats as quotes),\n * backtick escapes, `$name` / `${name}` / `$env:NAME` variables (dynamic\n * words), and the redirections `>`, `>>`, `2>`, `2>>`, `*>`, `2>&1`.\n *\n * Refused: script blocks and braces, bare parentheses,\n * `@( )` / `@{ }` / splatting / here-strings, the stop-parsing token `--%`,\n * `<` redirection, comments, the comma operator, compound-statement\n * keywords (`if`, `foreach`, `function`, `try`, …), dot-sourcing, a `&` that\n * is not a call operator (background jobs), and type literals.\n */\n\n/** Statement keywords that start a compound command; refused in command position. */\nconst POWERSHELL_KEYWORDS = new Set([\n \"if\",\n \"elseif\",\n \"else\",\n \"switch\",\n \"foreach\",\n \"for\",\n \"while\",\n \"do\",\n \"until\",\n \"function\",\n \"filter\",\n \"workflow\",\n \"param\",\n \"begin\",\n \"process\",\n \"end\",\n \"try\",\n \"catch\",\n \"finally\",\n \"trap\",\n \"data\",\n \"class\",\n \"enum\",\n \"using\",\n \"return\",\n \"break\",\n \"continue\",\n \"throw\",\n \"parallel\",\n \"sequence\",\n \"inlinescript\",\n]);\n\n/** Characters PowerShell reads as a single quote. */\nconst SINGLE_QUOTES = \"'‘’‚‛\";\n\n/** Characters PowerShell reads as a double quote. */\nconst DOUBLE_QUOTES = '\"“”„';\n\n/** Variable-name characters after `$`. */\nconst VARIABLE_CHAR = /[A-Za-z0-9_:?^$]/;\n\n/** Whether `char` (never the empty end-of-input string) is one of `set`. */\nfunction inSet(set: string, char: string): boolean {\n return char !== \"\" && set.includes(char);\n}\n\nclass PowerShellRefusal extends Error {\n public constructor(\n public readonly reason: \"unbalanced-quotes\" | \"unsupported-syntax\",\n message: string,\n ) {\n super(message);\n }\n}\n\nfunction unsupported(message: string): PowerShellRefusal {\n return new PowerShellRefusal(\"unsupported-syntax\", message);\n}\n\nclass PowerShellParser {\n private pos = 0;\n\n public constructor(\n private readonly src: string,\n public readonly commands: ShellSimpleCommand[] = [],\n private readonly depth = 0,\n private readonly chaining = true,\n ) {}\n\n private atEnd(): boolean {\n return this.pos >= this.src.length;\n }\n\n private peek(offset = 0): string {\n return this.src.charAt(this.pos + offset);\n }\n\n private skipBlanks(): void {\n while (this.peek() === \" \" || this.peek() === \"\\t\") {\n this.pos++;\n }\n }\n\n private atLineBreak(): boolean {\n return this.peek() === \"\\n\" || this.peek() === \"\\r\";\n }\n\n public parseProgram(segments: ShellSegment[]): void {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd()) {\n if (segments.length === 0) {\n throw unsupported(\"The command is empty.\");\n }\n\n if (\n segments[segments.length - 1]?.operator === \"&&\" ||\n segments[segments.length - 1]?.operator === \"||\"\n ) {\n throw unsupported(\"The line ends with an operator but no command follows it.\");\n }\n\n return;\n }\n\n const start = this.pos;\n\n this.parsePipeline();\n\n const end = this.pos;\n\n this.skipBlanks();\n\n const operator = this.readListOperator();\n\n segments.push({\n source: this.src.slice(start, end).trim(),\n ...(operator !== null ? { operator } : {}),\n });\n\n if (operator === null) {\n if (!this.atEnd()) {\n throw unsupported(`Unexpected \\`${this.peek()}\\`.`);\n }\n\n return;\n }\n\n if (operator === \";\" || operator === \"\\n\") {\n // Further separators and blanks are allowed after one; a trailing run is fine.\n while (this.peek() === \" \" || this.peek() === \"\\t\" || this.atLineBreak()) {\n this.pos++;\n }\n } else {\n this.skipBlanks();\n }\n }\n }\n\n private readListOperator(): string | null {\n const char = this.peek();\n\n if (\n !this.chaining &&\n ((char === \"&\" && this.peek(1) === \"&\") || (char === \"|\" && this.peek(1) === \"|\"))\n ) {\n throw unsupported(\n `Windows PowerShell 5.1 cannot run ${char}${char}; use ; or run each command in its own call instead.`,\n );\n }\n\n if (char === \"&\" && this.peek(1) === \"&\") {\n this.pos += 2;\n\n return \"&&\";\n }\n\n if (char === \"|\" && this.peek(1) === \"|\") {\n this.pos += 2;\n\n return \"||\";\n }\n\n if (char === \";\") {\n this.pos++;\n\n return \";\";\n }\n\n if (this.atLineBreak()) {\n while (this.atLineBreak()) {\n this.pos++;\n }\n\n return \"\\n\";\n }\n\n return null;\n }\n\n private parsePipeline(): void {\n this.parseCommand();\n\n while (this.peek() === \"|\" && this.peek(1) !== \"|\") {\n this.pos++;\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || this.peek() === \";\") {\n throw unsupported(\"The line ends with `|` but no command follows it.\");\n }\n\n this.parseCommand();\n }\n }\n\n private atRedirectStart(): boolean {\n const char = this.peek();\n\n if (char === \">\") {\n return true;\n }\n\n return /[1-6*]/.test(char) && this.peek(1) === \">\";\n }\n\n private parseCommand(): void {\n const start = this.pos;\n const command: ShellSimpleCommand = { assignments: [], words: [], redirects: [], source: \"\" };\n let callOperator = false;\n\n if (this.peek() === \"&\" && this.peek(1) !== \"&\") {\n callOperator = true;\n this.pos++;\n this.skipBlanks();\n }\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || this.peek() === \";\" || this.peek() === \"|\") {\n break;\n }\n\n if (this.peek() === \"&\") {\n if (this.peek(1) === \"&\") {\n break;\n }\n\n throw unsupported(\n \"`&` is only supported as the call operator in front of a command (not as a background job).\",\n );\n }\n\n if (this.peek() === \"<\") {\n throw unsupported(\"`<` is not supported in PowerShell command lines.\");\n }\n\n if (this.atRedirectStart()) {\n command.redirects.push(this.parseRedirect());\n\n continue;\n }\n\n const word = this.parseWord();\n\n command.words.push(word);\n\n if (command.words.length === 1) {\n this.checkCommandWord(word);\n }\n }\n\n if (command.words.length === 0 && (callOperator || command.redirects.length === 0)) {\n throw unsupported(\"The line has an empty command (two operators in a row?).\");\n }\n\n command.source = this.src.slice(start, this.pos).trim();\n this.commands.push(command);\n }\n\n private checkCommandWord(word: ShellWord): void {\n if (word.raw.startsWith(\"[\")) {\n throw unsupported(`\\`${word.raw}\\` (a type literal) is not supported.`);\n }\n\n if (!word.quoted && word.value === \".\") {\n throw unsupported(\n \"Dot-sourcing (`. file`) runs a script in the current scope and is not supported.\",\n );\n }\n\n if (!word.quoted && POWERSHELL_KEYWORDS.has(word.value.toLowerCase())) {\n throw unsupported(`\\`${word.value}\\` starts a PowerShell statement, which is not supported.`);\n }\n }\n\n private parseRedirect(): ShellRedirect {\n let fd: number | undefined;\n\n if (this.peek() === \"*\") {\n this.pos++;\n } else if (/[1-6]/.test(this.peek())) {\n fd = Number(this.peek());\n this.pos++;\n }\n\n let op = \">\";\n\n if (this.peek(1) === \">\") {\n op = \">>\";\n }\n\n this.pos += op.length;\n\n if (op === \">\" && this.peek() === \"&\") {\n if (/[1-6]/.test(this.peek(1))) {\n const target: ShellWord = {\n raw: this.peek(1),\n value: this.peek(1),\n dynamic: false,\n quoted: false,\n };\n\n this.pos += 2;\n\n return { op: \">&\", ...(fd !== undefined ? { fd } : {}), target, type: \"dup\" };\n }\n\n throw unsupported(\"Only `>&1`-style stream merges (`2>&1`) are supported.\");\n }\n\n this.skipBlanks();\n\n if (this.atEnd() || this.atLineBreak() || /[;&|<>]/.test(this.peek())) {\n throw unsupported(`The redirection \\`${op}\\` has no target.`);\n }\n\n const target = this.parseWord();\n\n return { op, ...(fd !== undefined ? { fd } : {}), target, type: \"write\" };\n }\n\n private parseWord(): ShellWord {\n const start = this.pos;\n let value = \"\";\n let dynamic = false;\n let quoted = false;\n\n if (this.src.startsWith(\"--%\", this.pos) && /[\\s;|&]|^$/.test(this.src.charAt(this.pos + 3))) {\n throw unsupported(\"The stop-parsing token `--%` is not supported.\");\n }\n\n while (!this.atEnd()) {\n const char = this.peek();\n\n if (\n char === \" \" ||\n char === \"\\t\" ||\n char === \"\\n\" ||\n char === \"\\r\" ||\n char === \";\" ||\n char === \"|\" ||\n char === \"&\" ||\n char === \">\" ||\n char === \"<\"\n ) {\n break;\n }\n\n if (inSet(SINGLE_QUOTES, char)) {\n this.pos++;\n quoted = true;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A single quote is never closed.\");\n }\n\n const inner = this.peek();\n\n if (inSet(SINGLE_QUOTES, inner)) {\n if (inSet(SINGLE_QUOTES, this.peek(1))) {\n value += this.peek(1);\n this.pos += 2;\n\n continue;\n }\n\n this.pos++;\n\n break;\n }\n\n value += inner;\n this.pos++;\n }\n\n continue;\n }\n\n if (inSet(DOUBLE_QUOTES, char)) {\n this.pos++;\n quoted = true;\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (this.atEnd()) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A double quote is never closed.\");\n }\n\n const inner = this.peek();\n\n if (inSet(DOUBLE_QUOTES, inner)) {\n if (inSet(DOUBLE_QUOTES, this.peek(1))) {\n value += this.peek(1);\n this.pos += 2;\n\n continue;\n }\n\n this.pos++;\n\n break;\n }\n\n if (inner === \"`\") {\n const next = this.peek(1);\n\n if (next !== \"\" && (inSet(DOUBLE_QUOTES, next) || next === \"`\" || next === \"$\")) {\n value += next;\n this.pos += 2;\n\n continue;\n }\n\n throw unsupported(\n 'Only `\"`, `` ` `` and `$` can follow a backtick inside double quotes.',\n );\n }\n\n if (inner === \"$\") {\n const read = this.readVariable();\n\n value += read;\n dynamic = true;\n\n continue;\n }\n\n value += inner;\n this.pos++;\n }\n\n continue;\n }\n\n if (char === \"`\") {\n const next = this.peek(1);\n\n if (next === \"\" || next === \"\\n\" || next === \"\\r\") {\n throw unsupported(\n \"A backtick at the end of a line (line continuation) is not supported.\",\n );\n }\n\n value += next;\n quoted = true;\n this.pos += 2;\n\n continue;\n }\n\n if (char === \"$\") {\n const wasLiteral = this.src.charAt(this.pos + 1);\n\n if (\n wasLiteral === \"\" ||\n !(wasLiteral === \"{\" || wasLiteral === \"(\" || VARIABLE_CHAR.test(wasLiteral))\n ) {\n value += \"$\";\n this.pos++;\n\n continue;\n }\n\n value += this.readVariable();\n dynamic = true;\n\n continue;\n }\n\n if (char === \"@\" && this.pos === start) {\n const next = this.peek(1);\n\n if (\n next === \"(\" ||\n next === \"{\" ||\n next === \"'\" ||\n next === '\"' ||\n /[A-Za-z_$]/.test(next)\n ) {\n throw unsupported(\n \"Arrays (`@( )`), hashtables, splatting (`@name`) and here-strings are not supported.\",\n );\n }\n }\n\n if (char === \"{\" || char === \"}\") {\n throw unsupported(\"Script blocks and braces are not supported.\");\n }\n\n if (char === \"(\" || char === \")\") {\n throw unsupported(\n \"Parentheses (grouping and `$( )` subexpressions) are not supported; quote the text.\",\n );\n }\n\n if (char === \",\") {\n throw unsupported(\n \"The comma operator builds an array and is not supported; quote the text.\",\n );\n }\n\n if (char === \"#\" && this.pos === start) {\n throw unsupported(\"Comments are not supported in a command line.\");\n }\n\n if (char === \"*\" || char === \"?\" || char === \"[\" || (char === \"~\" && this.pos === start)) {\n dynamic = true;\n }\n\n value += char;\n this.pos++;\n }\n\n return { raw: this.src.slice(start, this.pos), value, dynamic, quoted };\n }\n\n /**\n * Parse a `$( … )` subexpression: its commands run, so they are collected\n * (before the command that contains it) and checked like any other.\n */\n private parseSubexpression(): void {\n if (this.depth >= 8) {\n throw unsupported(\"`$( )` subexpressions are nested too deeply.\");\n }\n\n const open = this.pos;\n let level = 0;\n let index = open;\n\n for (; index < this.src.length; index++) {\n const char = this.src.charAt(index);\n\n if (inSet(SINGLE_QUOTES, char) || inSet(DOUBLE_QUOTES, char)) {\n const set = inSet(SINGLE_QUOTES, char) ? SINGLE_QUOTES : DOUBLE_QUOTES;\n\n index++;\n\n while (index < this.src.length) {\n const inner = this.src.charAt(index);\n\n if (inner === \"`\" && set === DOUBLE_QUOTES) {\n index += 2;\n\n continue;\n }\n\n if (inSet(set, inner)) {\n if (inSet(set, this.src.charAt(index + 1))) {\n index += 2;\n\n continue;\n }\n\n break;\n }\n\n index++;\n }\n\n continue;\n }\n\n if (char === \"`\") {\n index++;\n\n continue;\n }\n\n if (char === \"(\") {\n level++;\n } else if (char === \")\") {\n level--;\n\n if (level === 0) {\n break;\n }\n }\n }\n\n if (level !== 0) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A `$(` subexpression is never closed.\");\n }\n\n const inner = this.src.slice(open + 1, index);\n\n new PowerShellParser(inner, this.commands, this.depth + 1, this.chaining).parseProgram([]);\n this.pos = index + 1;\n }\n\n /** Read `$name`, `${name}` or `$env:NAME` at the cursor; a `$(` is refused. */\n private readVariable(): string {\n const begin = this.pos;\n\n this.pos++;\n\n if (this.peek() === \"(\") {\n this.parseSubexpression();\n\n return this.src.slice(begin, this.pos);\n }\n\n if (this.peek() === \"{\") {\n const close = this.src.indexOf(\"}\", this.pos);\n\n if (close === -1) {\n throw new PowerShellRefusal(\"unbalanced-quotes\", \"A `${` variable name is never closed.\");\n }\n\n this.pos = close + 1;\n\n return this.src.slice(begin, this.pos);\n }\n\n while (!this.atEnd() && VARIABLE_CHAR.test(this.peek())) {\n this.pos++;\n }\n\n return this.src.slice(begin, this.pos);\n }\n}\n\n/** Parse a PowerShell command line for the shell policy. Never throws. */\nexport function parsePowerShellLine(\n line: string,\n options?: { chaining?: boolean },\n): ShellParseResult {\n const segments: ShellSegment[] = [];\n const parser = new PowerShellParser(line, [], 0, options?.chaining !== false);\n\n try {\n parser.parseProgram(segments);\n\n return { ok: true, commands: parser.commands, segments, background: false };\n } catch (error) {\n if (error instanceof PowerShellRefusal) {\n return { ok: false, reason: error.reason, message: error.message };\n }\n\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4BA,MAAM,sCAAsB,IAAI,IAAI;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;AAGD,MAAM,gBAAgB;;AAGtB,MAAM,gBAAgB;;AAGtB,MAAM,gBAAgB;;AAGtB,SAAS,MAAM,KAAa,MAAuB;CACjD,OAAO,SAAS,MAAM,IAAI,SAAS,IAAI;AACzC;AAEA,IAAM,oBAAN,cAAgC,MAAM;CACpC,AAAO,YACL,AAAgB,QAChB,SACA;EACA,MAAM,OAAO;EAHG;CAIlB;AACF;AAEA,SAAS,YAAY,SAAoC;CACvD,OAAO,IAAI,kBAAkB,sBAAsB,OAAO;AAC5D;AAEA,IAAM,mBAAN,MAAM,iBAAiB;CAGrB,AAAO,YACL,AAAiB,KACjB,AAAgB,WAAiC,CAAC,GAClD,AAAiB,QAAQ,GACzB,AAAiB,WAAW,MAC5B;EAJiB;EACD;EACC;EACA;aANL;CAOX;CAEH,AAAQ,QAAiB;EACvB,OAAO,KAAK,OAAO,KAAK,IAAI;CAC9B;CAEA,AAAQ,KAAK,SAAS,GAAW;EAC/B,OAAO,KAAK,IAAI,OAAO,KAAK,MAAM,MAAM;CAC1C;CAEA,AAAQ,aAAmB;EACzB,OAAO,KAAK,KAAK,MAAM,OAAO,KAAK,KAAK,MAAM,KAC5C,KAAK;CAET;CAEA,AAAQ,cAAuB;EAC7B,OAAO,KAAK,KAAK,MAAM,QAAQ,KAAK,KAAK,MAAM;CACjD;CAEA,AAAO,aAAa,UAAgC;EAElD,OAAO,MAAM;GACX,KAAK,WAAW;GAEhB,IAAI,KAAK,MAAM,GAAG;IAChB,IAAI,SAAS,WAAW,GACtB,MAAM,YAAY,uBAAuB;IAG3C,IACE,SAAS,SAAS,SAAS,EAAE,EAAE,aAAa,QAC5C,SAAS,SAAS,SAAS,EAAE,EAAE,aAAa,MAE5C,MAAM,YAAY,2DAA2D;IAG/E;GACF;GAEA,MAAM,QAAQ,KAAK;GAEnB,KAAK,cAAc;GAEnB,MAAM,MAAM,KAAK;GAEjB,KAAK,WAAW;GAEhB,MAAM,WAAW,KAAK,iBAAiB;GAEvC,SAAS,KAAK;IACZ,QAAQ,KAAK,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;IACxC,GAAI,aAAa,OAAO,EAAE,SAAS,IAAI,CAAC;GAC1C,CAAC;GAED,IAAI,aAAa,MAAM;IACrB,IAAI,CAAC,KAAK,MAAM,GACd,MAAM,YAAY,gBAAgB,KAAK,KAAK,EAAE,IAAI;IAGpD;GACF;GAEA,IAAI,aAAa,OAAO,aAAa,MAEnC,OAAO,KAAK,KAAK,MAAM,OAAO,KAAK,KAAK,MAAM,OAAQ,KAAK,YAAY,GACrE,KAAK;QAGP,KAAK,WAAW;EAEpB;CACF;CAEA,AAAQ,mBAAkC;EACxC,MAAM,OAAO,KAAK,KAAK;EAEvB,IACE,CAAC,KAAK,aACJ,SAAS,OAAO,KAAK,KAAK,CAAC,MAAM,OAAS,SAAS,OAAO,KAAK,KAAK,CAAC,MAAM,MAE7E,MAAM,YACJ,qCAAqC,OAAO,KAAK,qDACnD;EAGF,IAAI,SAAS,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK;GACxC,KAAK,OAAO;GAEZ,OAAO;EACT;EAEA,IAAI,SAAS,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK;GACxC,KAAK,OAAO;GAEZ,OAAO;EACT;EAEA,IAAI,SAAS,KAAK;GAChB,KAAK;GAEL,OAAO;EACT;EAEA,IAAI,KAAK,YAAY,GAAG;GACtB,OAAO,KAAK,YAAY,GACtB,KAAK;GAGP,OAAO;EACT;EAEA,OAAO;CACT;CAEA,AAAQ,gBAAsB;EAC5B,KAAK,aAAa;EAElB,OAAO,KAAK,KAAK,MAAM,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK;GAClD,KAAK;GACL,KAAK,WAAW;GAEhB,IAAI,KAAK,MAAM,KAAK,KAAK,YAAY,KAAK,KAAK,KAAK,MAAM,KACxD,MAAM,YAAY,mDAAmD;GAGvE,KAAK,aAAa;EACpB;CACF;CAEA,AAAQ,kBAA2B;EACjC,MAAM,OAAO,KAAK,KAAK;EAEvB,IAAI,SAAS,KACX,OAAO;EAGT,OAAO,SAAS,KAAK,IAAI,KAAK,KAAK,KAAK,CAAC,MAAM;CACjD;CAEA,AAAQ,eAAqB;EAC3B,MAAM,QAAQ,KAAK;EACnB,MAAM,UAA8B;GAAE,aAAa,CAAC;GAAG,OAAO,CAAC;GAAG,WAAW,CAAC;GAAG,QAAQ;EAAG;EAC5F,IAAI,eAAe;EAEnB,IAAI,KAAK,KAAK,MAAM,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK;GAC/C,eAAe;GACf,KAAK;GACL,KAAK,WAAW;EAClB;EAGA,OAAO,MAAM;GACX,KAAK,WAAW;GAEhB,IAAI,KAAK,MAAM,KAAK,KAAK,YAAY,KAAK,KAAK,KAAK,MAAM,OAAO,KAAK,KAAK,MAAM,KAC/E;GAGF,IAAI,KAAK,KAAK,MAAM,KAAK;IACvB,IAAI,KAAK,KAAK,CAAC,MAAM,KACnB;IAGF,MAAM,YACJ,6FACF;GACF;GAEA,IAAI,KAAK,KAAK,MAAM,KAClB,MAAM,YAAY,mDAAmD;GAGvE,IAAI,KAAK,gBAAgB,GAAG;IAC1B,QAAQ,UAAU,KAAK,KAAK,cAAc,CAAC;IAE3C;GACF;GAEA,MAAM,OAAO,KAAK,UAAU;GAE5B,QAAQ,MAAM,KAAK,IAAI;GAEvB,IAAI,QAAQ,MAAM,WAAW,GAC3B,KAAK,iBAAiB,IAAI;EAE9B;EAEA,IAAI,QAAQ,MAAM,WAAW,MAAM,gBAAgB,QAAQ,UAAU,WAAW,IAC9E,MAAM,YAAY,0DAA0D;EAG9E,QAAQ,SAAS,KAAK,IAAI,MAAM,OAAO,KAAK,GAAG,CAAC,CAAC,KAAK;EACtD,KAAK,SAAS,KAAK,OAAO;CAC5B;CAEA,AAAQ,iBAAiB,MAAuB;EAC9C,IAAI,KAAK,IAAI,WAAW,GAAG,GACzB,MAAM,YAAY,KAAK,KAAK,IAAI,sCAAsC;EAGxE,IAAI,CAAC,KAAK,UAAU,KAAK,UAAU,KACjC,MAAM,YACJ,kFACF;EAGF,IAAI,CAAC,KAAK,UAAU,oBAAoB,IAAI,KAAK,MAAM,YAAY,CAAC,GAClE,MAAM,YAAY,KAAK,KAAK,MAAM,0DAA0D;CAEhG;CAEA,AAAQ,gBAA+B;EACrC,IAAI;EAEJ,IAAI,KAAK,KAAK,MAAM,KAClB,KAAK;OACA,IAAI,QAAQ,KAAK,KAAK,KAAK,CAAC,GAAG;GACpC,KAAK,OAAO,KAAK,KAAK,CAAC;GACvB,KAAK;EACP;EAEA,IAAI,KAAK;EAET,IAAI,KAAK,KAAK,CAAC,MAAM,KACnB,KAAK;EAGP,KAAK,OAAO,GAAG;EAEf,IAAI,OAAO,OAAO,KAAK,KAAK,MAAM,KAAK;GACrC,IAAI,QAAQ,KAAK,KAAK,KAAK,CAAC,CAAC,GAAG;IAC9B,MAAM,SAAoB;KACxB,KAAK,KAAK,KAAK,CAAC;KAChB,OAAO,KAAK,KAAK,CAAC;KAClB,SAAS;KACT,QAAQ;IACV;IAEA,KAAK,OAAO;IAEZ,OAAO;KAAE,IAAI;KAAM,GAAI,OAAO,SAAY,EAAE,GAAG,IAAI,CAAC;KAAI;KAAQ,MAAM;IAAM;GAC9E;GAEA,MAAM,YAAY,wDAAwD;EAC5E;EAEA,KAAK,WAAW;EAEhB,IAAI,KAAK,MAAM,KAAK,KAAK,YAAY,KAAK,UAAU,KAAK,KAAK,KAAK,CAAC,GAClE,MAAM,YAAY,qBAAqB,GAAG,kBAAkB;EAG9D,MAAM,SAAS,KAAK,UAAU;EAE9B,OAAO;GAAE;GAAI,GAAI,OAAO,SAAY,EAAE,GAAG,IAAI,CAAC;GAAI;GAAQ,MAAM;EAAQ;CAC1E;CAEA,AAAQ,YAAuB;EAC7B,MAAM,QAAQ,KAAK;EACnB,IAAI,QAAQ;EACZ,IAAI,UAAU;EACd,IAAI,SAAS;EAEb,IAAI,KAAK,IAAI,WAAW,OAAO,KAAK,GAAG,KAAK,aAAa,KAAK,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC,GACzF,MAAM,YAAY,gDAAgD;EAGpE,OAAO,CAAC,KAAK,MAAM,GAAG;GACpB,MAAM,OAAO,KAAK,KAAK;GAEvB,IACE,SAAS,OACT,SAAS,OACT,SAAS,QACT,SAAS,QACT,SAAS,OACT,SAAS,OACT,SAAS,OACT,SAAS,OACT,SAAS,KAET;GAGF,IAAI,MAAM,eAAe,IAAI,GAAG;IAC9B,KAAK;IACL,SAAS;IAGT,OAAO,MAAM;KACX,IAAI,KAAK,MAAM,GACb,MAAM,IAAI,kBAAkB,qBAAqB,iCAAiC;KAGpF,MAAM,QAAQ,KAAK,KAAK;KAExB,IAAI,MAAM,eAAe,KAAK,GAAG;MAC/B,IAAI,MAAM,eAAe,KAAK,KAAK,CAAC,CAAC,GAAG;OACtC,SAAS,KAAK,KAAK,CAAC;OACpB,KAAK,OAAO;OAEZ;MACF;MAEA,KAAK;MAEL;KACF;KAEA,SAAS;KACT,KAAK;IACP;IAEA;GACF;GAEA,IAAI,MAAM,eAAe,IAAI,GAAG;IAC9B,KAAK;IACL,SAAS;IAGT,OAAO,MAAM;KACX,IAAI,KAAK,MAAM,GACb,MAAM,IAAI,kBAAkB,qBAAqB,iCAAiC;KAGpF,MAAM,QAAQ,KAAK,KAAK;KAExB,IAAI,MAAM,eAAe,KAAK,GAAG;MAC/B,IAAI,MAAM,eAAe,KAAK,KAAK,CAAC,CAAC,GAAG;OACtC,SAAS,KAAK,KAAK,CAAC;OACpB,KAAK,OAAO;OAEZ;MACF;MAEA,KAAK;MAEL;KACF;KAEA,IAAI,UAAU,KAAK;MACjB,MAAM,OAAO,KAAK,KAAK,CAAC;MAExB,IAAI,SAAS,OAAO,MAAM,eAAe,IAAI,KAAK,SAAS,OAAO,SAAS,MAAM;OAC/E,SAAS;OACT,KAAK,OAAO;OAEZ;MACF;MAEA,MAAM,YACJ,wEACF;KACF;KAEA,IAAI,UAAU,KAAK;MACjB,MAAM,OAAO,KAAK,aAAa;MAE/B,SAAS;MACT,UAAU;MAEV;KACF;KAEA,SAAS;KACT,KAAK;IACP;IAEA;GACF;GAEA,IAAI,SAAS,KAAK;IAChB,MAAM,OAAO,KAAK,KAAK,CAAC;IAExB,IAAI,SAAS,MAAM,SAAS,QAAQ,SAAS,MAC3C,MAAM,YACJ,uEACF;IAGF,SAAS;IACT,SAAS;IACT,KAAK,OAAO;IAEZ;GACF;GAEA,IAAI,SAAS,KAAK;IAChB,MAAM,aAAa,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;IAE/C,IACE,eAAe,MACf,EAAE,eAAe,OAAO,eAAe,OAAO,cAAc,KAAK,UAAU,IAC3E;KACA,SAAS;KACT,KAAK;KAEL;IACF;IAEA,SAAS,KAAK,aAAa;IAC3B,UAAU;IAEV;GACF;GAEA,IAAI,SAAS,OAAO,KAAK,QAAQ,OAAO;IACtC,MAAM,OAAO,KAAK,KAAK,CAAC;IAExB,IACE,SAAS,OACT,SAAS,OACT,SAAS,OACT,SAAS,QACT,aAAa,KAAK,IAAI,GAEtB,MAAM,YACJ,sFACF;GAEJ;GAEA,IAAI,SAAS,OAAO,SAAS,KAC3B,MAAM,YAAY,6CAA6C;GAGjE,IAAI,SAAS,OAAO,SAAS,KAC3B,MAAM,YACJ,qFACF;GAGF,IAAI,SAAS,KACX,MAAM,YACJ,0EACF;GAGF,IAAI,SAAS,OAAO,KAAK,QAAQ,OAC/B,MAAM,YAAY,+CAA+C;GAGnE,IAAI,SAAS,OAAO,SAAS,OAAO,SAAS,OAAQ,SAAS,OAAO,KAAK,QAAQ,OAChF,UAAU;GAGZ,SAAS;GACT,KAAK;EACP;EAEA,OAAO;GAAE,KAAK,KAAK,IAAI,MAAM,OAAO,KAAK,GAAG;GAAG;GAAO;GAAS;EAAO;CACxE;;;;;CAMA,AAAQ,qBAA2B;EACjC,IAAI,KAAK,SAAS,GAChB,MAAM,YAAY,8CAA8C;EAGlE,MAAM,OAAO,KAAK;EAClB,IAAI,QAAQ;EACZ,IAAI,QAAQ;EAEZ,OAAO,QAAQ,KAAK,IAAI,QAAQ,SAAS;GACvC,MAAM,OAAO,KAAK,IAAI,OAAO,KAAK;GAElC,IAAI,MAAM,eAAe,IAAI,KAAK,MAAM,eAAe,IAAI,GAAG;IAC5D,MAAM,MAAM,MAAM,eAAe,IAAI,IAAI,gBAAgB;IAEzD;IAEA,OAAO,QAAQ,KAAK,IAAI,QAAQ;KAC9B,MAAM,QAAQ,KAAK,IAAI,OAAO,KAAK;KAEnC,IAAI,UAAU,OAAO,QAAQ,eAAe;MAC1C,SAAS;MAET;KACF;KAEA,IAAI,MAAM,KAAK,KAAK,GAAG;MACrB,IAAI,MAAM,KAAK,KAAK,IAAI,OAAO,QAAQ,CAAC,CAAC,GAAG;OAC1C,SAAS;OAET;MACF;MAEA;KACF;KAEA;IACF;IAEA;GACF;GAEA,IAAI,SAAS,KAAK;IAChB;IAEA;GACF;GAEA,IAAI,SAAS,KACX;QACK,IAAI,SAAS,KAAK;IACvB;IAEA,IAAI,UAAU,GACZ;GAEJ;EACF;EAEA,IAAI,UAAU,GACZ,MAAM,IAAI,kBAAkB,qBAAqB,uCAAuC;EAG1F,MAAM,QAAQ,KAAK,IAAI,MAAM,OAAO,GAAG,KAAK;EAE5C,IAAI,iBAAiB,OAAO,KAAK,UAAU,KAAK,QAAQ,GAAG,KAAK,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC;EACzF,KAAK,MAAM,QAAQ;CACrB;;CAGA,AAAQ,eAAuB;EAC7B,MAAM,QAAQ,KAAK;EAEnB,KAAK;EAEL,IAAI,KAAK,KAAK,MAAM,KAAK;GACvB,KAAK,mBAAmB;GAExB,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK,GAAG;EACvC;EAEA,IAAI,KAAK,KAAK,MAAM,KAAK;GACvB,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAK,KAAK,GAAG;GAE5C,IAAI,UAAU,IACZ,MAAM,IAAI,kBAAkB,qBAAqB,uCAAuC;GAG1F,KAAK,MAAM,QAAQ;GAEnB,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK,GAAG;EACvC;EAEA,OAAO,CAAC,KAAK,MAAM,KAAK,cAAc,KAAK,KAAK,KAAK,CAAC,GACpD,KAAK;EAGP,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK,GAAG;CACvC;AACF;;AAGA,SAAgB,oBACd,MACA,SACkB;CAClB,MAAM,WAA2B,CAAC;CAClC,MAAM,SAAS,IAAI,iBAAiB,MAAM,CAAC,GAAG,GAAG,SAAS,aAAa,KAAK;CAE5E,IAAI;EACF,OAAO,aAAa,QAAQ;EAE5B,OAAO;GAAE,IAAI;GAAM,UAAU,OAAO;GAAU;GAAU,YAAY;EAAM;CAC5E,SAAS,OAAO;EACd,IAAI,iBAAiB,mBACnB,OAAO;GAAE,IAAI;GAAO,QAAQ,MAAM;GAAQ,SAAS,MAAM;EAAQ;EAGnE,MAAM;CACR;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-check.d.mts","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/policy/shell-check.ts"],"mappings":";;;;;;;YAkCY;;iBAGK;;EAEf;;EAEA,QAAQ;;EAER;;;;;;iBAOe;;EAEf;;EAEA,SAAS;;EAET;;EAEA;;EAEA;;;;;;EAMA,MAAM;;EAEN;;EAEA,OAAO;;;
|
|
1
|
+
{"version":3,"file":"shell-check.d.mts","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/policy/shell-check.ts"],"mappings":";;;;;;;YAkCY;;iBAGK;;EAEf;;EAEA,QAAQ;;EAER;;;;;;iBAOe;;EAEf;;EAEA,SAAS;;EAET;;EAEA;;EAEA;;;;;;EAMA,MAAM;;EAEN;;EAEA,OAAO;;;iBA0QQ;;EAEf,sBAAsB;;EAEtB,OAAO;;EAEP,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAsgCG,kBACd,QAAQ,iBACR,iBACA,UAAU,oBACT;;;;;;;;;;;;;wBAyHa,qBACd,iBACA,SAAS,KAAK,2BACd,OAAO,YACN"}
|
|
@@ -178,7 +178,7 @@ function shellDialect() {
|
|
|
178
178
|
* stripped). The cmd dialect splits Windows paths too.
|
|
179
179
|
*/
|
|
180
180
|
function executableBasename(firstToken, dialect = "posix") {
|
|
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, "");
|
|
181
|
+
return (dialect === "cmd" || dialect === "powershell" || dialect === "powershell5" ? path.win32.basename(firstToken) : path.basename(firstToken)).replace(dialect === "powershell" || dialect === "powershell5" ? /\.(exe|cmd|bat|com|ps1)$/i : /\.(exe|cmd|bat|com)$/i, "");
|
|
182
182
|
}
|
|
183
183
|
/** Show a command in a refusal, abbreviated at a word boundary when long. */
|
|
184
184
|
function show(source) {
|
|
@@ -343,7 +343,7 @@ var LineChecker = class {
|
|
|
343
343
|
this.commands.push([...argv]);
|
|
344
344
|
const remembered = this.isRemembered(argv);
|
|
345
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)) {
|
|
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" || this.dialect === "powershell5") && POWERSHELL_OPAQUE.has(key)) {
|
|
347
347
|
if (approved) return;
|
|
348
348
|
if (this.askMode || rule?.effect === "ask") {
|
|
349
349
|
this.addAsk(argv, "nested", rule);
|
|
@@ -431,7 +431,7 @@ var LineChecker = class {
|
|
|
431
431
|
}
|
|
432
432
|
/** Check a redirection target from every possible cwd. */
|
|
433
433
|
checkRedirectTarget(word, access, cwds) {
|
|
434
|
-
if (this.dialect === "powershell" && !word.quoted && word.value.toLowerCase() === "$null") return;
|
|
434
|
+
if ((this.dialect === "powershell" || this.dialect === "powershell5") && !word.quoted && word.value.toLowerCase() === "$null") return;
|
|
435
435
|
if (word.dynamic) throw new Refusal("unsupported-syntax", `The redirection target \`${word.raw}\` must be a literal path (no variables, globs or \`~\`).`);
|
|
436
436
|
const value = word.value;
|
|
437
437
|
if (this.dialect === "posix" && DEVICE_TARGETS.has(value) || this.dialect === "cmd" && CMD_DEVICE_TARGETS.has(value.toLowerCase())) return;
|
|
@@ -649,7 +649,7 @@ function checkSimpleCommand(checker, simple, cwds, dialect) {
|
|
|
649
649
|
const first = simple.words[0];
|
|
650
650
|
if (first === void 0) return cwds;
|
|
651
651
|
const executable = first.dynamic ? "" : executableBasename(first.value, dialect).toLowerCase();
|
|
652
|
-
const powershell = dialect === "powershell";
|
|
652
|
+
const powershell = dialect === "powershell" || dialect === "powershell5";
|
|
653
653
|
if (executable === "cd" || powershell && POWERSHELL_CD_NAMES.has(executable)) {
|
|
654
654
|
checker.assertExecutableNotDenied(executable, simple.words.map((word) => word.value));
|
|
655
655
|
if (powershell) {
|
|
@@ -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 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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"}
|
|
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\" || dialect === \"powershell5\"\n ? path.win32.basename(firstToken)\n : path.basename(firstToken);\n\n return base.replace(\n dialect === \"powershell\" || dialect === \"powershell5\"\n ? /\\.(exe|cmd|bat|com|ps1)$/i\n : /\\.(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 &&\n (this.dialect === \"powershell\" || this.dialect === \"powershell5\") &&\n 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 (\n (this.dialect === \"powershell\" || this.dialect === \"powershell5\") &&\n !word.quoted &&\n word.value.toLowerCase() === \"$null\"\n ) {\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\" || dialect === \"powershell5\";\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":"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"}
|