wave-agent-sdk 0.11.4 → 0.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/managers/backgroundTaskManager.js +3 -3
- package/dist/managers/mcpManager.d.ts.map +1 -1
- package/dist/managers/mcpManager.js +4 -1
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +35 -3
- package/dist/utils/bashParser.d.ts +4 -0
- package/dist/utils/bashParser.d.ts.map +1 -1
- package/dist/utils/bashParser.js +11 -0
- package/package.json +1 -1
- package/src/managers/backgroundTaskManager.ts +3 -3
- package/src/managers/mcpManager.ts +4 -1
- package/src/managers/permissionManager.ts +38 -2
- package/src/utils/bashParser.ts +13 -0
|
@@ -16,7 +16,7 @@ export class BackgroundTaskManager {
|
|
|
16
16
|
this.callbacks.onBackgroundTasksChange?.(Array.from(this.tasks.values()));
|
|
17
17
|
}
|
|
18
18
|
generateId() {
|
|
19
|
-
return `task_${this.nextId++}`;
|
|
19
|
+
return `task_${process.pid}_${this.nextId++}`;
|
|
20
20
|
}
|
|
21
21
|
addTask(task) {
|
|
22
22
|
this.tasks.set(task.id, task);
|
|
@@ -41,7 +41,7 @@ export class BackgroundTaskManager {
|
|
|
41
41
|
});
|
|
42
42
|
// Create log file
|
|
43
43
|
const logPath = path.join(os.tmpdir(), `wave-task-${id}.log`);
|
|
44
|
-
const logStream = fs.createWriteStream(logPath, { flags: "
|
|
44
|
+
const logStream = fs.createWriteStream(logPath, { flags: "w" });
|
|
45
45
|
const shell = {
|
|
46
46
|
id,
|
|
47
47
|
type: "shell",
|
|
@@ -156,7 +156,7 @@ export class BackgroundTaskManager {
|
|
|
156
156
|
const startTime = Date.now();
|
|
157
157
|
// Create log file
|
|
158
158
|
const logPath = path.join(os.tmpdir(), `wave-task-${id}.log`);
|
|
159
|
-
const logStream = fs.createWriteStream(logPath, { flags: "
|
|
159
|
+
const logStream = fs.createWriteStream(logPath, { flags: "w" });
|
|
160
160
|
// Write initial output to log file
|
|
161
161
|
if (initialStdout) {
|
|
162
162
|
logStream.write(stripAnsiColors(initialStdout));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcpManager.d.ts","sourceRoot":"","sources":["../../src/managers/mcpManager.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EACV,MAAM,EACN,eAAe,EACf,SAAS,EACT,OAAO,EACP,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAQ3B,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;CACxD;AAID,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,UAAU;IASnB,OAAO,CAAC,SAAS;IARnB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,OAAO,CAA2C;IAC1D,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,SAAS,CAAsB;gBAG7B,SAAS,EAAE,SAAS,EAC5B,OAAO,GAAE,iBAAsB;IAKjC;;OAEG;IACG,UAAU,CACd,OAAO,EAAE,MAAM,EACf,WAAW,GAAE,OAAe,GAC3B,OAAO,CAAC,IAAI,CAAC;IAwCV,kBAAkB,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAO/C,UAAU,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA0CvC,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAWrD,SAAS,IAAI,SAAS,GAAG,IAAI;IAI7B,aAAa,IAAI,eAAe,EAAE;IAIlC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAIpD,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IASzE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO;IAyBzD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAgB7B,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"mcpManager.d.ts","sourceRoot":"","sources":["../../src/managers/mcpManager.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EACV,MAAM,EACN,eAAe,EACf,SAAS,EACT,OAAO,EACP,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAQ3B,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;CACxD;AAID,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,UAAU;IASnB,OAAO,CAAC,SAAS;IARnB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,OAAO,CAA2C;IAC1D,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,SAAS,CAAsB;gBAG7B,SAAS,EAAE,SAAS,EAC5B,OAAO,GAAE,iBAAsB;IAKjC;;OAEG;IACG,UAAU,CACd,OAAO,EAAE,MAAM,EACf,WAAW,GAAE,OAAe,GAC3B,OAAO,CAAC,IAAI,CAAC;IAwCV,kBAAkB,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAO/C,UAAU,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA0CvC,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAWrD,SAAS,IAAI,SAAS,GAAG,IAAI;IAI7B,aAAa,IAAI,eAAe,EAAE;IAIlC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAIpD,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IASzE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO;IAyBzD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAgB7B,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA+F7C,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2BtD,oBAAoB,IAAI,OAAO,EAAE;IAW3B,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACtD,CAAC;YAsDY,uBAAuB;IA8D/B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B;;OAEG;IACH,iBAAiB,IAAI,UAAU,EAAE;IA6BjC;;OAEG;IACH,iBAAiB,IAAI,0BAA0B,EAAE;IAIjD;;OAEG;IACG,wBAAwB,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC;IActB;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAcjC"}
|
|
@@ -168,7 +168,10 @@ export class McpManager {
|
|
|
168
168
|
const transport = new StdioClientTransport({
|
|
169
169
|
command: server.config.command,
|
|
170
170
|
args: server.config.args || [],
|
|
171
|
-
env:
|
|
171
|
+
env: {
|
|
172
|
+
...process.env,
|
|
173
|
+
...(server.config.env || {}),
|
|
174
|
+
},
|
|
172
175
|
cwd: this.workdir, // Use the agent's workdir as the process working directory
|
|
173
176
|
});
|
|
174
177
|
// Create client
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissionManager.d.ts","sourceRoot":"","sources":["../../src/managers/permissionManager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACf,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"permissionManager.d.ts","sourceRoot":"","sources":["../../src/managers/permissionManager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACf,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAiBhD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AA8DlD,MAAM,WAAW,wBAAwB;IACvC,+CAA+C;IAC/C,wBAAwB,CAAC,EAAE,cAAc,CAAC;IAC1C,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,0DAA0D;IAC1D,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,yDAAyD;IACzD,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,8DAA8D;IAC9D,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,iBAAiB;IAiB1B,OAAO,CAAC,SAAS;IAhBnB,OAAO,CAAC,wBAAwB,CAAC,CAAiB;IAClD,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,qBAAqB,CAAgB;IAC7C,OAAO,CAAC,2BAA2B,CAAgB;IACnD,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,gCAAgC,CAAC,CAAiC;IAC1E,OAAO,CAAC,OAAO,CAAC,CAAS;gBAGf,SAAS,EAAE,SAAS,EAC5B,OAAO,GAAE,wBAA6B;IAgBxC;;OAEG;IACI,mCAAmC,CACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,GACvC,IAAI;IAIP;;OAEG;IACH,8BAA8B,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI;IAcrE;;OAEG;IACI,2BAA2B,IAAI,cAAc,GAAG,SAAS;IAIhE;;OAEG;IACI,eAAe,IAAI,MAAM,EAAE;IAIlC;;OAEG;IACI,cAAc,IAAI,MAAM,EAAE;IAIjC;;OAEG;IACI,uBAAuB,IAAI,MAAM,EAAE;IAI1C;;OAEG;IACI,sBAAsB,IAAI,MAAM,EAAE;IAIzC;;OAEG;IACI,wBAAwB,IAAI,MAAM,EAAE;IAI3C;;OAEG;IACI,sBAAsB,IAAI,MAAM,EAAE;IAIzC;;OAEG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIzC;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIxC;;OAEG;IACI,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAI/C;;OAEG;IACI,mBAAmB,IAAI,IAAI;IAIlC;;OAEG;IACH,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI;IASxD;;OAEG;IACI,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAW5D;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIpC;;OAEG;IACI,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAItD;;OAEG;IACI,eAAe,IAAI,MAAM,GAAG,SAAS;IAI5C;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkCxB;;OAEG;IACH,uBAAuB,CAAC,iBAAiB,CAAC,EAAE,cAAc,GAAG,cAAc;IAI3E;;OAEG;IACH,8BAA8B,CAC5B,iBAAiB,CAAC,EAAE,cAAc,GACjC,cAAc;IAejB;;;OAGG;IACG,eAAe,CACnB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,kBAAkB,CAAC;IAqP9B;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAO3C;;OAEG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAc9C;;OAEG;IACH,aAAa,CACX,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,cAAc,EAC9B,QAAQ,CAAC,EAAE,kBAAkB,EAC7B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,qBAAqB;IAoFxB;;OAEG;IACH,OAAO,CAAC,WAAW;IA0EnB;;OAEG;IACH,OAAO,CAAC,eAAe;IAkHvB;;;;;;;OAOG;IACI,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IA+FjE;;;OAGG;IACU,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA4C5D"}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { minimatch } from "minimatch";
|
|
10
10
|
import { RESTRICTED_TOOLS } from "../types/permissions.js";
|
|
11
|
-
import { splitBashCommand, stripEnvVars, stripRedirections, hasWriteRedirections, getSmartPrefix, DANGEROUS_COMMANDS, } from "../utils/bashParser.js";
|
|
11
|
+
import { splitBashCommand, stripEnvVars, stripRedirections, hasWriteRedirections, isBashWriteRedirect, getSmartPrefix, DANGEROUS_COMMANDS, } from "../utils/bashParser.js";
|
|
12
12
|
import { isPathInside } from "../utils/pathSafety.js";
|
|
13
13
|
import { BASH_TOOL_NAME, EDIT_TOOL_NAME, WRITE_TOOL_NAME, READ_TOOL_NAME, } from "../constants/tools.js";
|
|
14
14
|
const SAFE_COMMANDS = [
|
|
@@ -23,6 +23,7 @@ const SAFE_COMMANDS = [
|
|
|
23
23
|
"head",
|
|
24
24
|
"tail",
|
|
25
25
|
"wc",
|
|
26
|
+
"sleep",
|
|
26
27
|
];
|
|
27
28
|
const DEFAULT_ALLOWED_RULES = [
|
|
28
29
|
"Bash(git status*)",
|
|
@@ -63,6 +64,7 @@ const DEFAULT_ALLOWED_RULES = [
|
|
|
63
64
|
"Bash(head*)",
|
|
64
65
|
"Bash(tail*)",
|
|
65
66
|
"Bash(wc*)",
|
|
67
|
+
"Bash(sleep*)",
|
|
66
68
|
];
|
|
67
69
|
import { logger } from "../utils/globalLogger.js";
|
|
68
70
|
export class PermissionManager {
|
|
@@ -264,6 +266,34 @@ export class PermissionManager {
|
|
|
264
266
|
* Called by individual tools after validation/diff, before real operation
|
|
265
267
|
*/
|
|
266
268
|
async checkPermission(context) {
|
|
269
|
+
// 0. Intercept Bash file writing operations
|
|
270
|
+
if (context.toolName === BASH_TOOL_NAME && context.toolInput?.command) {
|
|
271
|
+
const command = String(context.toolInput.command);
|
|
272
|
+
if (isBashWriteRedirect(command)) {
|
|
273
|
+
// Check if this specific command is explicitly allowed by a rule that includes redirection
|
|
274
|
+
const isExplicitlyAllowed = [
|
|
275
|
+
...this.instanceAllowedRules,
|
|
276
|
+
...this.temporaryRules,
|
|
277
|
+
...this.allowedRules,
|
|
278
|
+
...DEFAULT_ALLOWED_RULES,
|
|
279
|
+
].some((rule) => {
|
|
280
|
+
if (rule.startsWith("Bash(") && rule.endsWith(")")) {
|
|
281
|
+
const pattern = rule.substring(5, rule.length - 1);
|
|
282
|
+
// If the pattern itself has write redirections, we check if it matches
|
|
283
|
+
if (hasWriteRedirections(pattern)) {
|
|
284
|
+
return this.matchesRule(context, rule);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
});
|
|
289
|
+
if (!isExplicitlyAllowed) {
|
|
290
|
+
return {
|
|
291
|
+
behavior: "deny",
|
|
292
|
+
message: "Bash-based file writing operations (e.g., using '>', '>>', or 'cat <<EOF > file') are not allowed. Please use the dedicated 'Write' or 'Edit' tools instead for file modifications.",
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
267
297
|
// 0. Check instance-specific denied rules first - Deny always takes precedence
|
|
268
298
|
for (const rule of this.instanceDeniedRules) {
|
|
269
299
|
if (this.matchesRule(context, rule)) {
|
|
@@ -627,7 +657,8 @@ export class PermissionManager {
|
|
|
627
657
|
cmd === "cat" ||
|
|
628
658
|
cmd === "head" ||
|
|
629
659
|
cmd === "tail" ||
|
|
630
|
-
cmd === "wc"
|
|
660
|
+
cmd === "wc" ||
|
|
661
|
+
cmd === "sleep") {
|
|
631
662
|
return true;
|
|
632
663
|
}
|
|
633
664
|
if (workdir) {
|
|
@@ -715,7 +746,8 @@ export class PermissionManager {
|
|
|
715
746
|
cmd === "cat" ||
|
|
716
747
|
cmd === "head" ||
|
|
717
748
|
cmd === "tail" ||
|
|
718
|
-
cmd === "wc"
|
|
749
|
+
cmd === "wc" ||
|
|
750
|
+
cmd === "sleep") {
|
|
719
751
|
isSafe = true;
|
|
720
752
|
}
|
|
721
753
|
else {
|
|
@@ -15,6 +15,10 @@ export declare function stripRedirections(command: string): string;
|
|
|
15
15
|
* Checks if a bash command contains any write redirections (>, >>, &>, 2>, >|).
|
|
16
16
|
*/
|
|
17
17
|
export declare function hasWriteRedirections(command: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Alias for hasWriteRedirections, used for semantic clarity in permission checks.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isBashWriteRedirect(command: string): boolean;
|
|
18
22
|
/**
|
|
19
23
|
* Blacklist of dangerous commands that should not be safely prefix-matched
|
|
20
24
|
* and should not have persistent permissions.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bashParser.d.ts","sourceRoot":"","sources":["../../src/utils/bashParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAmH1D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA2CpD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAuHzD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"bashParser.d.ts","sourceRoot":"","sources":["../../src/utils/bashParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAmH1D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA2CpD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAuHzD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAkG7D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,UAa9B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA8L7D"}
|
package/dist/utils/bashParser.js
CHANGED
|
@@ -350,11 +350,22 @@ export function hasWriteRedirections(command) {
|
|
|
350
350
|
i = k - 1; // Move the main loop index to the end of the target
|
|
351
351
|
continue;
|
|
352
352
|
}
|
|
353
|
+
// Ignore file descriptor redirections like 2>&1, >&2, etc.
|
|
354
|
+
if (target.startsWith("&") && /^\d+$/.test(target.substring(1))) {
|
|
355
|
+
i = k - 1;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
353
358
|
return true;
|
|
354
359
|
}
|
|
355
360
|
}
|
|
356
361
|
return false;
|
|
357
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Alias for hasWriteRedirections, used for semantic clarity in permission checks.
|
|
365
|
+
*/
|
|
366
|
+
export function isBashWriteRedirect(command) {
|
|
367
|
+
return hasWriteRedirections(command);
|
|
368
|
+
}
|
|
358
369
|
/**
|
|
359
370
|
* Blacklist of dangerous commands that should not be safely prefix-matched
|
|
360
371
|
* and should not have persistent permissions.
|
package/package.json
CHANGED
|
@@ -35,7 +35,7 @@ export class BackgroundTaskManager {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
public generateId(): string {
|
|
38
|
-
return `task_${this.nextId++}`;
|
|
38
|
+
return `task_${process.pid}_${this.nextId++}`;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
public addTask(task: BackgroundTask): void {
|
|
@@ -69,7 +69,7 @@ export class BackgroundTaskManager {
|
|
|
69
69
|
|
|
70
70
|
// Create log file
|
|
71
71
|
const logPath = path.join(os.tmpdir(), `wave-task-${id}.log`);
|
|
72
|
-
const logStream = fs.createWriteStream(logPath, { flags: "
|
|
72
|
+
const logStream = fs.createWriteStream(logPath, { flags: "w" });
|
|
73
73
|
|
|
74
74
|
const shell: BackgroundShell = {
|
|
75
75
|
id,
|
|
@@ -198,7 +198,7 @@ export class BackgroundTaskManager {
|
|
|
198
198
|
|
|
199
199
|
// Create log file
|
|
200
200
|
const logPath = path.join(os.tmpdir(), `wave-task-${id}.log`);
|
|
201
|
-
const logStream = fs.createWriteStream(logPath, { flags: "
|
|
201
|
+
const logStream = fs.createWriteStream(logPath, { flags: "w" });
|
|
202
202
|
|
|
203
203
|
// Write initial output to log file
|
|
204
204
|
if (initialStdout) {
|
|
@@ -228,7 +228,10 @@ export class McpManager {
|
|
|
228
228
|
const transport = new StdioClientTransport({
|
|
229
229
|
command: server.config.command,
|
|
230
230
|
args: server.config.args || [],
|
|
231
|
-
env:
|
|
231
|
+
env: {
|
|
232
|
+
...(process.env as Record<string, string>),
|
|
233
|
+
...(server.config.env || {}),
|
|
234
|
+
},
|
|
232
235
|
cwd: this.workdir, // Use the agent's workdir as the process working directory
|
|
233
236
|
});
|
|
234
237
|
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
stripEnvVars,
|
|
22
22
|
stripRedirections,
|
|
23
23
|
hasWriteRedirections,
|
|
24
|
+
isBashWriteRedirect,
|
|
24
25
|
getSmartPrefix,
|
|
25
26
|
DANGEROUS_COMMANDS,
|
|
26
27
|
} from "../utils/bashParser.js";
|
|
@@ -46,6 +47,7 @@ const SAFE_COMMANDS = [
|
|
|
46
47
|
"head",
|
|
47
48
|
"tail",
|
|
48
49
|
"wc",
|
|
50
|
+
"sleep",
|
|
49
51
|
];
|
|
50
52
|
|
|
51
53
|
const DEFAULT_ALLOWED_RULES = [
|
|
@@ -87,6 +89,7 @@ const DEFAULT_ALLOWED_RULES = [
|
|
|
87
89
|
"Bash(head*)",
|
|
88
90
|
"Bash(tail*)",
|
|
89
91
|
"Bash(wc*)",
|
|
92
|
+
"Bash(sleep*)",
|
|
90
93
|
];
|
|
91
94
|
|
|
92
95
|
import { logger } from "../utils/globalLogger.js";
|
|
@@ -367,6 +370,37 @@ export class PermissionManager {
|
|
|
367
370
|
async checkPermission(
|
|
368
371
|
context: ToolPermissionContext,
|
|
369
372
|
): Promise<PermissionDecision> {
|
|
373
|
+
// 0. Intercept Bash file writing operations
|
|
374
|
+
if (context.toolName === BASH_TOOL_NAME && context.toolInput?.command) {
|
|
375
|
+
const command = String(context.toolInput.command);
|
|
376
|
+
if (isBashWriteRedirect(command)) {
|
|
377
|
+
// Check if this specific command is explicitly allowed by a rule that includes redirection
|
|
378
|
+
const isExplicitlyAllowed = [
|
|
379
|
+
...this.instanceAllowedRules,
|
|
380
|
+
...this.temporaryRules,
|
|
381
|
+
...this.allowedRules,
|
|
382
|
+
...DEFAULT_ALLOWED_RULES,
|
|
383
|
+
].some((rule) => {
|
|
384
|
+
if (rule.startsWith("Bash(") && rule.endsWith(")")) {
|
|
385
|
+
const pattern = rule.substring(5, rule.length - 1);
|
|
386
|
+
// If the pattern itself has write redirections, we check if it matches
|
|
387
|
+
if (hasWriteRedirections(pattern)) {
|
|
388
|
+
return this.matchesRule(context, rule);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return false;
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
if (!isExplicitlyAllowed) {
|
|
395
|
+
return {
|
|
396
|
+
behavior: "deny",
|
|
397
|
+
message:
|
|
398
|
+
"Bash-based file writing operations (e.g., using '>', '>>', or 'cat <<EOF > file') are not allowed. Please use the dedicated 'Write' or 'Edit' tools instead for file modifications.",
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
370
404
|
// 0. Check instance-specific denied rules first - Deny always takes precedence
|
|
371
405
|
for (const rule of this.instanceDeniedRules) {
|
|
372
406
|
if (this.matchesRule(context, rule)) {
|
|
@@ -816,7 +850,8 @@ export class PermissionManager {
|
|
|
816
850
|
cmd === "cat" ||
|
|
817
851
|
cmd === "head" ||
|
|
818
852
|
cmd === "tail" ||
|
|
819
|
-
cmd === "wc"
|
|
853
|
+
cmd === "wc" ||
|
|
854
|
+
cmd === "sleep"
|
|
820
855
|
) {
|
|
821
856
|
return true;
|
|
822
857
|
}
|
|
@@ -929,7 +964,8 @@ export class PermissionManager {
|
|
|
929
964
|
cmd === "cat" ||
|
|
930
965
|
cmd === "head" ||
|
|
931
966
|
cmd === "tail" ||
|
|
932
|
-
cmd === "wc"
|
|
967
|
+
cmd === "wc" ||
|
|
968
|
+
cmd === "sleep"
|
|
933
969
|
) {
|
|
934
970
|
isSafe = true;
|
|
935
971
|
} else {
|
package/src/utils/bashParser.ts
CHANGED
|
@@ -381,6 +381,12 @@ export function hasWriteRedirections(command: string): boolean {
|
|
|
381
381
|
continue;
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
+
// Ignore file descriptor redirections like 2>&1, >&2, etc.
|
|
385
|
+
if (target.startsWith("&") && /^\d+$/.test(target.substring(1))) {
|
|
386
|
+
i = k - 1;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
|
|
384
390
|
return true;
|
|
385
391
|
}
|
|
386
392
|
}
|
|
@@ -388,6 +394,13 @@ export function hasWriteRedirections(command: string): boolean {
|
|
|
388
394
|
return false;
|
|
389
395
|
}
|
|
390
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Alias for hasWriteRedirections, used for semantic clarity in permission checks.
|
|
399
|
+
*/
|
|
400
|
+
export function isBashWriteRedirect(command: string): boolean {
|
|
401
|
+
return hasWriteRedirections(command);
|
|
402
|
+
}
|
|
403
|
+
|
|
391
404
|
/**
|
|
392
405
|
* Blacklist of dangerous commands that should not be safely prefix-matched
|
|
393
406
|
* and should not have persistent permissions.
|