@probelabs/probe 0.6.0-rc105 → 0.6.0-rc106

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.
@@ -92,6 +92,10 @@ export class ProbeAgent {
92
92
  this.maxResponseTokens = options.maxResponseTokens || parseInt(process.env.MAX_RESPONSE_TOKENS || '0', 10) || null;
93
93
  this.disableMermaidValidation = !!options.disableMermaidValidation;
94
94
 
95
+ // Bash configuration
96
+ this.enableBash = !!options.enableBash;
97
+ this.bashConfig = options.bashConfig || {};
98
+
95
99
  // Search configuration - support both path (single) and allowedFolders (array)
96
100
  if (options.allowedFolders && Array.isArray(options.allowedFolders)) {
97
101
  this.allowedFolders = options.allowedFolders;
@@ -156,7 +160,9 @@ export class ProbeAgent {
156
160
  debug: this.debug,
157
161
  defaultPath: this.allowedFolders.length > 0 ? this.allowedFolders[0] : process.cwd(),
158
162
  allowedFolders: this.allowedFolders,
159
- outline: this.outline
163
+ outline: this.outline,
164
+ enableBash: this.enableBash,
165
+ bashConfig: this.bashConfig
160
166
  };
161
167
 
162
168
  // Create base tools
@@ -174,6 +180,11 @@ export class ProbeAgent {
174
180
  listFiles: listFilesToolInstance,
175
181
  searchFiles: searchFilesToolInstance,
176
182
  };
183
+
184
+ // Add bash tool if enabled
185
+ if (this.enableBash && wrappedTools.bashToolInstance) {
186
+ this.toolImplementations.bash = wrappedTools.bashToolInstance;
187
+ }
177
188
 
178
189
  // Store wrapped tools for ACP system
179
190
  this.wrappedTools = wrappedTools;
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Unified command parsing utilities for bash tool
3
+ *
4
+ * This module provides a single source of truth for parsing shell commands.
5
+ * It supports only simple commands (no pipes, operators, or substitutions)
6
+ * to align with the executor's capabilities.
7
+ *
8
+ * @module bashCommandUtils
9
+ */
10
+
11
+ /**
12
+ * Parse a simple shell command into command and arguments
13
+ * Properly handles quoted arguments and strips quotes
14
+ * Rejects complex shell constructs for security and consistency
15
+ *
16
+ * @param {string} command - Command string to parse
17
+ * @returns {Object} Parse result with command, args, and validation info
18
+ */
19
+ export function parseSimpleCommand(command) {
20
+ if (!command || typeof command !== 'string') {
21
+ return {
22
+ success: false,
23
+ error: 'Command must be a non-empty string',
24
+ command: null,
25
+ args: [],
26
+ isComplex: false
27
+ };
28
+ }
29
+
30
+ const trimmed = command.trim();
31
+ if (!trimmed) {
32
+ return {
33
+ success: false,
34
+ error: 'Command cannot be empty',
35
+ command: null,
36
+ args: [],
37
+ isComplex: false
38
+ };
39
+ }
40
+
41
+ // Check for complex shell constructs that we don't support
42
+ const complexPatterns = [
43
+ /\|/, // Pipes
44
+ /&&/, // Logical AND
45
+ /\|\|/, // Logical OR
46
+ /(?<!\\);/, // Command separator (but not escaped \;)
47
+ /&$/, // Background execution
48
+ /\$\(/, // Command substitution $()
49
+ /`/, // Command substitution ``
50
+ />/, // Redirection >
51
+ /</, // Redirection <
52
+ /\*\*/, // Glob patterns (potentially dangerous)
53
+ /^\s*\{.*,.*\}|\{.*\.\.\.*\}/, // Brace expansion like {a,b} or {1..10} (but not find {} placeholders)
54
+ ];
55
+
56
+ for (const pattern of complexPatterns) {
57
+ if (pattern.test(trimmed)) {
58
+ return {
59
+ success: false,
60
+ error: 'Complex shell commands with pipes, operators, or redirections are not supported for security reasons',
61
+ command: null,
62
+ args: [],
63
+ isComplex: true,
64
+ detected: pattern.toString()
65
+ };
66
+ }
67
+ }
68
+
69
+ // Parse simple command with proper quote handling
70
+ const args = [];
71
+ let current = '';
72
+ let inQuotes = false;
73
+ let quoteChar = '';
74
+ let escaped = false;
75
+
76
+ for (let i = 0; i < trimmed.length; i++) {
77
+ const char = trimmed[i];
78
+ const nextChar = i + 1 < trimmed.length ? trimmed[i + 1] : '';
79
+
80
+ if (escaped) {
81
+ // Handle escaped characters
82
+ current += char;
83
+ escaped = false;
84
+ continue;
85
+ }
86
+
87
+ if (char === '\\' && !inQuotes) {
88
+ // Escape next character
89
+ escaped = true;
90
+ continue;
91
+ }
92
+
93
+ if (!inQuotes && (char === '"' || char === "'")) {
94
+ // Start quoted section - don't include quote char
95
+ inQuotes = true;
96
+ quoteChar = char;
97
+ } else if (inQuotes && char === quoteChar) {
98
+ // End quoted section - don't include quote char
99
+ inQuotes = false;
100
+ quoteChar = '';
101
+ } else if (!inQuotes && char === ' ') {
102
+ // Space outside quotes - end current argument
103
+ if (current.trim()) {
104
+ args.push(current.trim());
105
+ current = '';
106
+ }
107
+ } else {
108
+ // Regular character - add to current argument
109
+ current += char;
110
+ }
111
+ }
112
+
113
+ // Add final argument if exists
114
+ if (current.trim()) {
115
+ args.push(current.trim());
116
+ }
117
+
118
+ // Check for unclosed quotes
119
+ if (inQuotes) {
120
+ return {
121
+ success: false,
122
+ error: `Unclosed quote in command: ${quoteChar}`,
123
+ command: null,
124
+ args: [],
125
+ isComplex: false
126
+ };
127
+ }
128
+
129
+ if (args.length === 0) {
130
+ return {
131
+ success: false,
132
+ error: 'No command found after parsing',
133
+ command: null,
134
+ args: [],
135
+ isComplex: false
136
+ };
137
+ }
138
+
139
+ const [baseCommand, ...commandArgs] = args;
140
+
141
+ return {
142
+ success: true,
143
+ error: null,
144
+ command: baseCommand,
145
+ args: commandArgs,
146
+ fullArgs: args,
147
+ isComplex: false,
148
+ original: command
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Check if a command contains complex shell constructs
154
+ * @param {string} command - Command to check
155
+ * @returns {boolean} True if command is complex
156
+ */
157
+ export function isComplexCommand(command) {
158
+ const result = parseSimpleCommand(command);
159
+ return result.isComplex;
160
+ }
161
+
162
+ /**
163
+ * Legacy compatibility function - parses command for permission checking
164
+ * @param {string} command - Command to parse
165
+ * @returns {Object} Parse result compatible with existing permission checker
166
+ */
167
+ export function parseCommand(command) {
168
+ const result = parseSimpleCommand(command);
169
+
170
+ if (!result.success) {
171
+ return {
172
+ command: '',
173
+ args: [],
174
+ error: result.error,
175
+ isComplex: result.isComplex
176
+ };
177
+ }
178
+
179
+ return {
180
+ command: result.command,
181
+ args: result.args,
182
+ error: null,
183
+ isComplex: result.isComplex
184
+ };
185
+ }
186
+
187
+ /**
188
+ * Parse command for execution - returns array format expected by spawn()
189
+ * @param {string} command - Command to parse
190
+ * @returns {string[]|null} Array of [command, ...args] or null if invalid
191
+ */
192
+ export function parseCommandForExecution(command) {
193
+ const result = parseSimpleCommand(command);
194
+
195
+ if (!result.success) {
196
+ return null;
197
+ }
198
+
199
+ return result.fullArgs;
200
+ }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Default allow and deny patterns for bash command execution
3
+ * @module agent/bashDefaults
4
+ */
5
+
6
+ /**
7
+ * Default allow patterns for safe, read-only commands useful for code exploration
8
+ */
9
+ export const DEFAULT_ALLOW_PATTERNS = [
10
+ // Basic navigation and listing
11
+ 'ls', 'dir', 'pwd', 'cd', 'cd:*',
12
+
13
+ // File reading commands
14
+ 'cat', 'cat:*', 'head', 'head:*', 'tail', 'tail:*',
15
+ 'less', 'more', 'view',
16
+
17
+ // File information and metadata
18
+ 'file', 'file:*', 'stat', 'stat:*', 'wc', 'wc:*',
19
+ 'du', 'du:*', 'df', 'df:*', 'realpath', 'realpath:*',
20
+
21
+ // Search and find commands (read-only) - find restricted to safe operations
22
+ 'find', 'find:-name:*', 'find:-type:*', 'find:-size:*', 'find:-mtime:*', 'find:-newer:*',
23
+ 'find:-path:*', 'find:-iname:*', 'find:-maxdepth:*', 'find:-mindepth:*', 'find:-print',
24
+ 'grep', 'grep:*', 'egrep', 'egrep:*', 'fgrep', 'fgrep:*',
25
+ 'rg', 'rg:*', 'ag', 'ag:*', 'ack', 'ack:*',
26
+ 'which', 'which:*', 'whereis', 'whereis:*', 'locate', 'locate:*',
27
+ 'type', 'type:*', 'command', 'command:*',
28
+
29
+ // Tree and structure visualization
30
+ 'tree', 'tree:*',
31
+
32
+ // Git read-only operations
33
+ 'git:status', 'git:log', 'git:log:*', 'git:diff', 'git:diff:*',
34
+ 'git:show', 'git:show:*', 'git:branch', 'git:branch:*',
35
+ 'git:tag', 'git:tag:*', 'git:describe', 'git:describe:*',
36
+ 'git:remote', 'git:remote:*', 'git:config:*',
37
+ 'git:blame', 'git:blame:*', 'git:shortlog', 'git:reflog',
38
+ 'git:ls-files', 'git:ls-tree', 'git:rev-parse', 'git:rev-list',
39
+ 'git:--version', 'git:help', 'git:help:*',
40
+
41
+ // Package managers (information only)
42
+ 'npm:list', 'npm:ls', 'npm:view', 'npm:info', 'npm:show',
43
+ 'npm:outdated', 'npm:audit', 'npm:--version',
44
+ 'yarn:list', 'yarn:info', 'yarn:--version',
45
+ 'pnpm:list', 'pnpm:--version',
46
+ 'pip:list', 'pip:show', 'pip:--version',
47
+ 'pip3:list', 'pip3:show', 'pip3:--version',
48
+ 'gem:list', 'gem:--version',
49
+ 'bundle:list', 'bundle:show', 'bundle:--version',
50
+ 'composer:show', 'composer:--version',
51
+
52
+ // Language and runtime versions
53
+ 'node:--version', 'node:-v',
54
+ 'python:--version', 'python:-V', 'python3:--version', 'python3:-V',
55
+ 'ruby:--version', 'ruby:-v',
56
+ 'go:version', 'go:env', 'go:list', 'go:mod:graph',
57
+ 'rustc:--version', 'cargo:--version', 'cargo:tree', 'cargo:metadata',
58
+ 'java:--version', 'java:-version', 'javac:--version',
59
+ 'mvn:--version', 'gradle:--version',
60
+ 'php:--version', 'dotnet:--version', 'dotnet:list',
61
+
62
+ // Database client versions (connection info only)
63
+ 'psql:--version', 'mysql:--version', 'redis-cli:--version',
64
+ 'mongo:--version', 'sqlite3:--version',
65
+
66
+ // System information
67
+ 'uname', 'uname:*', 'hostname', 'whoami', 'id', 'groups',
68
+ 'date', 'cal', 'uptime', 'w', 'users', 'sleep', 'sleep:*',
69
+
70
+ // Environment and shell
71
+ 'env', 'printenv', 'echo', 'echo:*', 'printf', 'printf:*',
72
+ 'export', 'export:*', 'set', 'unset',
73
+
74
+ // Process information (read-only)
75
+ 'ps', 'ps:*', 'pgrep', 'pgrep:*', 'jobs', 'top:-n:1',
76
+
77
+ // Network information (read-only)
78
+ 'ifconfig', 'ip:addr', 'ip:link', 'hostname:-I',
79
+ 'ping:-c:*', 'traceroute', 'nslookup', 'dig',
80
+
81
+ // Text processing and utilities (awk removed - too powerful)
82
+ 'sed:-n:*', 'cut', 'cut:*', 'sort', 'sort:*',
83
+ 'uniq', 'uniq:*', 'tr', 'tr:*', 'column', 'column:*',
84
+ 'paste', 'paste:*', 'join', 'join:*', 'comm', 'comm:*',
85
+ 'diff', 'diff:*', 'cmp', 'cmp:*', 'patch:--dry-run:*',
86
+
87
+ // Hashing and encoding (read-only)
88
+ 'md5sum', 'md5sum:*', 'sha1sum', 'sha1sum:*', 'sha256sum', 'sha256sum:*',
89
+ 'base64', 'base64:-d', 'od', 'od:*', 'hexdump', 'hexdump:*',
90
+
91
+ // Archive and compression (list/view only)
92
+ 'tar:-tf:*', 'tar:-tzf:*', 'unzip:-l:*', 'zip:-l:*',
93
+ 'gzip:-l:*', 'gunzip:-l:*',
94
+
95
+ // Help and documentation
96
+ 'man', 'man:*', '--help', 'help', 'info', 'info:*',
97
+ 'whatis', 'whatis:*', 'apropos', 'apropos:*',
98
+
99
+ // Make (dry run and info)
100
+ 'make:-n', 'make:--dry-run', 'make:-p', 'make:--print-data-base',
101
+
102
+ // Docker (read-only operations)
103
+ 'docker:ps', 'docker:images', 'docker:version', 'docker:info',
104
+ 'docker:logs:*', 'docker:inspect:*',
105
+
106
+ // Test runners (list/info only)
107
+ 'jest:--listTests', 'mocha:--help', 'pytest:--collect-only'
108
+ ];
109
+
110
+ /**
111
+ * Default deny patterns for potentially dangerous or destructive commands
112
+ */
113
+ export const DEFAULT_DENY_PATTERNS = [
114
+ // Dangerous file operations
115
+ 'rm:-rf', 'rm:-f:/', 'rm:/', 'rm:-rf:*', 'rmdir',
116
+ 'chmod:777', 'chmod:-R:777', 'chown', 'chgrp',
117
+ 'dd', 'dd:*', 'shred', 'shred:*',
118
+
119
+ // Dangerous find operations that can execute arbitrary commands
120
+ 'find:-exec:*', 'find:*:-exec:*', 'find:-execdir:*', 'find:*:-execdir:*',
121
+ 'find:-ok:*', 'find:*:-ok:*', 'find:-okdir:*', 'find:*:-okdir:*',
122
+
123
+ // Powerful scripting tools that can execute arbitrary commands
124
+ 'awk', 'awk:*', 'perl', 'perl:*', 'python:-c:*', 'node:-e:*',
125
+
126
+ // System administration and modification
127
+ 'sudo:*', 'su', 'su:*', 'passwd', 'adduser', 'useradd',
128
+ 'userdel', 'usermod', 'groupadd', 'groupdel', 'visudo',
129
+
130
+ // Package installation and removal
131
+ 'npm:install', 'npm:i', 'npm:uninstall', 'npm:publish',
132
+ 'npm:unpublish', 'npm:link', 'npm:update',
133
+ 'yarn:install', 'yarn:add', 'yarn:remove', 'yarn:upgrade',
134
+ 'pnpm:install', 'pnpm:add', 'pnpm:remove',
135
+ 'pip:install', 'pip:uninstall', 'pip:upgrade',
136
+ 'pip3:install', 'pip3:uninstall', 'pip3:upgrade',
137
+ 'gem:install', 'gem:uninstall', 'gem:update',
138
+ 'bundle:install', 'bundle:update',
139
+ 'composer:install', 'composer:update', 'composer:remove',
140
+ 'apt:*', 'apt-get:*', 'yum:*', 'dnf:*', 'zypper:*',
141
+ 'brew:install', 'brew:uninstall', 'brew:upgrade',
142
+ 'conda:install', 'conda:remove', 'conda:update',
143
+
144
+ // Service and system control
145
+ 'systemctl:*', 'service:*', 'chkconfig:*',
146
+ 'initctl:*', 'upstart:*',
147
+
148
+ // Network operations that could be dangerous
149
+ 'curl:-d:*', 'curl:--data:*', 'curl:-X:POST:*', 'curl:-X:PUT:*',
150
+ 'wget:-O:/', 'wget:--post-data:*',
151
+ 'ssh', 'ssh:*', 'scp', 'scp:*', 'sftp', 'sftp:*', 'rsync:*',
152
+ 'nc', 'nc:*', 'netcat', 'netcat:*', 'telnet', 'telnet:*',
153
+ 'ftp', 'ftp:*',
154
+
155
+ // Process control and termination
156
+ 'kill', 'kill:*', 'killall', 'killall:*', 'pkill', 'pkill:*',
157
+ 'nohup:*', 'disown:*',
158
+
159
+ // System control and shutdown
160
+ 'shutdown', 'shutdown:*', 'reboot', 'halt', 'poweroff',
161
+ 'init', 'telinit',
162
+
163
+ // Kernel and module operations
164
+ 'insmod', 'insmod:*', 'rmmod', 'rmmod:*', 'modprobe', 'modprobe:*',
165
+ 'sysctl:-w:*',
166
+
167
+ // Dangerous git operations
168
+ 'git:push', 'git:push:*', 'git:force', 'git:reset:--hard:*',
169
+ 'git:clean:-fd', 'git:rm:*', 'git:commit', 'git:merge',
170
+ 'git:rebase', 'git:cherry-pick', 'git:stash:drop',
171
+
172
+ // File system mounting and partitioning
173
+ 'mount', 'mount:*', 'umount', 'umount:*', 'fdisk', 'fdisk:*',
174
+ 'parted', 'parted:*', 'mkfs', 'mkfs:*', 'fsck', 'fsck:*',
175
+
176
+ // Cron and scheduling
177
+ 'crontab', 'crontab:*', 'at', 'at:*', 'batch', 'batch:*',
178
+
179
+ // Compression with potential overwrite
180
+ 'tar:-xf:*', 'unzip', 'unzip:*', 'gzip:*', 'gunzip:*',
181
+
182
+ // Build and compilation that might modify files
183
+ 'make', 'make:install', 'make:clean', 'cargo:build', 'cargo:install',
184
+ 'npm:run:build', 'yarn:build', 'mvn:install', 'gradle:build',
185
+
186
+ // Docker operations that could modify state
187
+ 'docker:run', 'docker:run:*', 'docker:exec', 'docker:exec:*',
188
+ 'docker:build', 'docker:build:*', 'docker:pull', 'docker:push',
189
+ 'docker:rm', 'docker:rmi', 'docker:stop', 'docker:start',
190
+
191
+ // Database operations
192
+ 'mysql:-e:DROP', 'psql:-c:DROP', 'redis-cli:FLUSHALL',
193
+ 'mongo:--eval:*',
194
+
195
+ // Text editors that could modify files
196
+ 'vi', 'vi:*', 'vim', 'vim:*', 'nano', 'nano:*', 'emacs', 'emacs:*',
197
+ 'sed:-i:*', 'perl:-i:*',
198
+
199
+ // Potentially dangerous utilities
200
+ 'eval', 'eval:*', 'exec', 'exec:*', 'source', 'source:*',
201
+ 'bash:-c:*', 'sh:-c:*', 'zsh:-c:*'
202
+ ];