autoclaw 1.3.3 → 1.3.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.
@@ -3,6 +3,8 @@ import * as path from 'path';
3
3
  import inquirer from 'inquirer';
4
4
  import chalk from 'chalk';
5
5
  import { execShellCommand } from '../shell.js';
6
+ import { resolveSandboxMode, sandboxBackend, sandboxedInvocation } from '../sandbox.js';
7
+ import * as os from 'os';
6
8
  const DEFAULT_SHELL_TIMEOUT_MS = 120000;
7
9
  const SHELL_MAX_BUFFER = 10 * 1024 * 1024;
8
10
  // Bounded reads keep a huge file from exhausting memory or the model context;
@@ -27,6 +29,24 @@ export function matchDangerousPattern(command) {
27
29
  const hit = DANGEROUS_PATTERNS.find(p => p.re.test(command));
28
30
  return hit ? hit.label : null;
29
31
  }
32
+ // An unattended agent reads web pages and repos; a prompt injection must not
33
+ // end up reading AutoClaw's own credential stores (or classic .env files)
34
+ // and exfiltrating them. Blocked for read_file/write_file unless
35
+ // --allow-dangerous. execute_shell_command is not pattern-restricted here.
36
+ export function isSensitivePath(p) {
37
+ const norm = path.normalize(String(p)).toLowerCase();
38
+ const protectedFiles = [
39
+ path.join(os.homedir(), '.autoclaw', 'setting.json').toLowerCase(),
40
+ path.join(os.homedir(), '.autoclaw', '.env').toLowerCase(),
41
+ path.join(process.cwd(), '.autoclaw', 'setting.json').toLowerCase()
42
+ ];
43
+ if (protectedFiles.includes(norm))
44
+ return true;
45
+ return path.basename(norm).startsWith('.env');
46
+ }
47
+ function sensitivePathBlock(p, verb) {
48
+ return `Error: ${p} is blocked by AutoClaw safety policy (credential/secret store). It was NOT ${verb === 'read' ? 'read' : 'written'}. If this task genuinely requires it, restart AutoClaw with --allow-dangerous.`;
49
+ }
30
50
  export const ShellTool = {
31
51
  name: "Shell Execution",
32
52
  definition: {
@@ -56,6 +76,21 @@ export const ShellTool = {
56
76
  }
57
77
  }
58
78
  const timeoutMs = Number(config?.shellTimeout || process.env.AUTOCLOW_SHELL_TIMEOUT || DEFAULT_SHELL_TIMEOUT_MS);
79
+ // Sandbox policy: non-default modes wrap the invocation in a platform
80
+ // backend, or refuse (fail-closed) when no backend exists.
81
+ const sandboxMode = resolveSandboxMode(config);
82
+ let sandboxed;
83
+ if (sandboxMode !== 'danger-full-access') {
84
+ const backend = sandboxBackend();
85
+ if (!backend.available) {
86
+ return `Error: sandbox mode "${sandboxMode}" is configured but no sandbox backend is available: ${backend.detail}`;
87
+ }
88
+ const inv = sandboxedInvocation(args.command, sandboxMode, process.cwd());
89
+ if ('blocked' in inv) {
90
+ return `Error: sandbox mode "${sandboxMode}" cannot wrap this command: ${inv.detail}`;
91
+ }
92
+ sandboxed = inv;
93
+ }
59
94
  // Check for auto-confirm flag
60
95
  if (!config?.autoConfirm) {
61
96
  if (!process.stdin.isTTY) {
@@ -78,7 +113,7 @@ export const ShellTool = {
78
113
  console.log(chalk.gray("(Auto-confirming command execution due to --yes flag)"));
79
114
  }
80
115
  try {
81
- const r = await execShellCommand(args.command, { timeoutMs, maxBuffer: SHELL_MAX_BUFFER });
116
+ const r = await execShellCommand(args.command, { timeoutMs, maxBuffer: SHELL_MAX_BUFFER, sandboxed });
82
117
  if (r.timedOut) {
83
118
  return `Command timed out after ${timeoutMs}ms and was terminated.\nStdout: ${r.stdout}\nStderr: ${r.stderr}`;
84
119
  }
@@ -107,7 +142,10 @@ export const ReadFileTool = {
107
142
  }
108
143
  }
109
144
  },
110
- handler: async (args) => {
145
+ handler: async (args, config) => {
146
+ if (!config?.allowDangerous && isSensitivePath(args.path)) {
147
+ return sensitivePathBlock(args.path, 'read');
148
+ }
111
149
  let fh;
112
150
  try {
113
151
  fh = await fs.open(args.path, 'r');
@@ -151,7 +189,10 @@ export const WriteFileTool = {
151
189
  }
152
190
  }
153
191
  },
154
- handler: async (args) => {
192
+ handler: async (args, config) => {
193
+ if (!config?.allowDangerous && isSensitivePath(args.path)) {
194
+ return sensitivePathBlock(args.path, 'write');
195
+ }
155
196
  try {
156
197
  await fs.mkdir(path.dirname(args.path), { recursive: true });
157
198
  await fs.writeFile(args.path, args.content, 'utf-8');
@@ -6,6 +6,7 @@ import { BrowserTool } from './browser.js';
6
6
  import { ScreenshotTool } from './screenshot.js';
7
7
  import { ImageTool } from './image.js';
8
8
  import { PromptOptimizerTool } from './prompt-optimizer.js';
9
+ import { CheckBackgroundProcessTool, StartBackgroundProcessTool, StopBackgroundProcessTool } from './background.js';
9
10
  // Central Registry of all available tools
10
11
  export const toolRegistry = [
11
12
  ShellTool,
@@ -18,7 +19,10 @@ export const toolRegistry = [
18
19
  NotifyTool,
19
20
  BrowserTool,
20
21
  ScreenshotTool,
21
- ImageTool
22
+ ImageTool,
23
+ StartBackgroundProcessTool,
24
+ CheckBackgroundProcessTool,
25
+ StopBackgroundProcessTool
22
26
  ];
23
27
  export function getToolDefinitions(config) {
24
28
  return toolRegistry
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autoclaw",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {