just-bash-mcp 2.7.0 → 2.9.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/README.md +58 -4
- package/package.json +24 -22
- package/src/config/index.ts +402 -0
- package/{build/index.js → src/index.ts} +12 -6
- package/src/just-bash-security.d.ts +705 -0
- package/src/tools/bash-instance.ts +212 -0
- package/src/tools/exec-tools.ts +165 -0
- package/src/tools/file-tools.ts +206 -0
- package/src/tools/index.ts +45 -0
- package/src/tools/info-tools.ts +175 -0
- package/src/tools/sandbox-tools.ts +217 -0
- package/src/types.ts +111 -0
- package/src/utils/index.ts +84 -0
- package/tsconfig.json +18 -0
- package/build/config/index.d.ts +0 -79
- package/build/config/index.d.ts.map +0 -1
- package/build/config/index.js +0 -178
- package/build/config/index.js.map +0 -1
- package/build/index.d.ts +0 -12
- package/build/index.d.ts.map +0 -1
- package/build/index.js.map +0 -1
- package/build/tools/bash-instance.d.ts +0 -26
- package/build/tools/bash-instance.d.ts.map +0 -1
- package/build/tools/bash-instance.js +0 -106
- package/build/tools/bash-instance.js.map +0 -1
- package/build/tools/exec-tools.d.ts +0 -10
- package/build/tools/exec-tools.d.ts.map +0 -1
- package/build/tools/exec-tools.js +0 -98
- package/build/tools/exec-tools.js.map +0 -1
- package/build/tools/file-tools.d.ts +0 -10
- package/build/tools/file-tools.d.ts.map +0 -1
- package/build/tools/file-tools.js +0 -171
- package/build/tools/file-tools.js.map +0 -1
- package/build/tools/index.d.ts +0 -15
- package/build/tools/index.d.ts.map +0 -1
- package/build/tools/index.js +0 -29
- package/build/tools/index.js.map +0 -1
- package/build/tools/info-tools.d.ts +0 -10
- package/build/tools/info-tools.d.ts.map +0 -1
- package/build/tools/info-tools.js +0 -95
- package/build/tools/info-tools.js.map +0 -1
- package/build/tools/sandbox-tools.d.ts +0 -10
- package/build/tools/sandbox-tools.d.ts.map +0 -1
- package/build/tools/sandbox-tools.js +0 -124
- package/build/tools/sandbox-tools.js.map +0 -1
- package/build/utils/index.d.ts +0 -52
- package/build/utils/index.d.ts.map +0 -1
- package/build/utils/index.js +0 -56
- package/build/utils/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,15 +7,20 @@ An MCP (Model Context Protocol) server that provides a sandboxed bash environmen
|
|
|
7
7
|
|
|
8
8
|
Execute bash commands in a secure, isolated environment with an in-memory virtual filesystem.
|
|
9
9
|
|
|
10
|
-
Built on top of [`just-bash`](https://github.com/vercel-labs/just-bash) v2.
|
|
10
|
+
Built on top of [`just-bash`](https://github.com/vercel-labs/just-bash) v2.9.6.
|
|
11
11
|
|
|
12
|
-
## What's New in v2.
|
|
12
|
+
## What's New in v2.8.0
|
|
13
13
|
|
|
14
|
+
- **Synced with upstream `just-bash` v2.9.6** - Latest security hardening, defense-in-depth, fuzzing, jq fixes, and large file support
|
|
15
|
+
- **Defense-in-depth mode** - Opt-in monkey-patching of dangerous JS globals (`JUST_BASH_DEFENSE_IN_DEPTH=true`)
|
|
16
|
+
- **Python support** - Python3 via Pyodide (`JUST_BASH_ENABLE_PYTHON=true`)
|
|
17
|
+
- **Vercel Sandbox API** - Compatible `bash_sandbox_*` tools for isolated execution
|
|
18
|
+
- **oxlint/oxfmt toolchain** - Replaced tsc/biome with faster oxlint and oxfmt
|
|
19
|
+
- **Configurable limits** - Fine-grained control over glob ops, string length, array size, heredoc size, and more
|
|
14
20
|
- **`rg` (ripgrep)** - Fast regex search with `--files`, `-d`, `--stats`, `-t markdown`
|
|
15
21
|
- **`tar`** - Archive support with compression
|
|
16
22
|
- **MountableFS** - Mount multiple filesystems at different paths
|
|
17
23
|
- **ReadWriteFS** - Direct read-write access to real directories
|
|
18
|
-
- **Multi-level glob patterns** - Improved `**/*.ts` style matching
|
|
19
24
|
|
|
20
25
|
## Features
|
|
21
26
|
|
|
@@ -120,6 +125,16 @@ Add to your MCP settings:
|
|
|
120
125
|
| `JUST_BASH_MAX_CALL_DEPTH` | Maximum function recursion depth | `100` |
|
|
121
126
|
| `JUST_BASH_MAX_COMMAND_COUNT` | Maximum total commands per execution | `10000` |
|
|
122
127
|
| `JUST_BASH_MAX_LOOP_ITERATIONS` | Maximum iterations per loop | `10000` |
|
|
128
|
+
| `JUST_BASH_ENABLE_PYTHON` | Enable Python3 via Pyodide (`true`/`false`) | `false` |
|
|
129
|
+
| `JUST_BASH_DEFENSE_IN_DEPTH` | Enable defense-in-depth mode (`true`/`false`) | `false` |
|
|
130
|
+
| `JUST_BASH_DEFENSE_IN_DEPTH_AUDIT` | Audit mode: log violations but don't block | `false` |
|
|
131
|
+
| `JUST_BASH_DEFENSE_IN_DEPTH_LOG` | Log violations to console | `false` |
|
|
132
|
+
| `JUST_BASH_OVERLAY_READ_ONLY` | OverlayFS read-only mode | `false` |
|
|
133
|
+
| `JUST_BASH_MAX_RESPONSE_SIZE` | Max network response body size (bytes) | `10485760` |
|
|
134
|
+
| `JUST_BASH_MAX_FILE_READ_SIZE` | Max file read size for OverlayFs/ReadWriteFs | `10485760` |
|
|
135
|
+
| `JUST_BASH_ALLOWED_COMMANDS` | Comma-separated command allow-list | all |
|
|
136
|
+
| `JUST_BASH_ENABLE_LOGGING` | Enable execution logging | `false` |
|
|
137
|
+
| `JUST_BASH_ENABLE_TRACING` | Enable performance tracing | `false` |
|
|
123
138
|
|
|
124
139
|
## Tools
|
|
125
140
|
|
|
@@ -149,9 +164,28 @@ Reset the persistent bash environment, clearing all files and state.
|
|
|
149
164
|
|
|
150
165
|
File operations in the persistent environment.
|
|
151
166
|
|
|
167
|
+
### `bash_direct_read` / `bash_direct_write`
|
|
168
|
+
|
|
169
|
+
Direct filesystem read/write operations (bypass shell execution).
|
|
170
|
+
|
|
152
171
|
### `bash_info`
|
|
153
172
|
|
|
154
|
-
Get information about the bash environment configuration.
|
|
173
|
+
Get information about the bash environment configuration, including defense-in-depth violation stats.
|
|
174
|
+
|
|
175
|
+
### `bash_get_cwd` / `bash_get_env`
|
|
176
|
+
|
|
177
|
+
Get current working directory or environment variables.
|
|
178
|
+
|
|
179
|
+
### Vercel Sandbox API
|
|
180
|
+
|
|
181
|
+
Compatible with the Vercel Sandbox API:
|
|
182
|
+
|
|
183
|
+
- `bash_sandbox_run` - Run a command in the sandbox
|
|
184
|
+
- `bash_sandbox_write_files` - Write multiple files at once
|
|
185
|
+
- `bash_sandbox_read_file` - Read a file (supports base64 encoding)
|
|
186
|
+
- `bash_sandbox_mkdir` - Create a directory
|
|
187
|
+
- `bash_sandbox_stop` - Stop and clean up the sandbox
|
|
188
|
+
- `bash_sandbox_reset` - Reset the sandbox state
|
|
155
189
|
|
|
156
190
|
## Supported Commands
|
|
157
191
|
|
|
@@ -235,6 +269,26 @@ Get information about the bash environment configuration.
|
|
|
235
269
|
- Execution limits protect against infinite loops and recursion
|
|
236
270
|
- No binary/WASM execution
|
|
237
271
|
- Network disabled by default; when enabled, URL and method allow-lists enforced
|
|
272
|
+
- **Defense-in-depth mode** (opt-in): Monkey-patches dangerous JS globals (`Function`, `eval`, `setTimeout`, `process`, etc.) during script execution to block escape vectors
|
|
273
|
+
- **SecurityViolationLogger**: Tracks all defense-in-depth violations with full stats accessible via `bash_info`
|
|
274
|
+
- **Rich network error classification**: `NetworkAccessDeniedError`, `TooManyRedirectsError`, `RedirectNotAllowedError` for precise error messages
|
|
275
|
+
|
|
276
|
+
## Upstream API Coverage
|
|
277
|
+
|
|
278
|
+
This wrapper integrates the full public API surface of `just-bash` v2.9.6:
|
|
279
|
+
|
|
280
|
+
| Category | Exports Used |
|
|
281
|
+
|----------|-------------|
|
|
282
|
+
| Core | `Bash`, `BashOptions`, `ExecOptions`, `BashExecResult` |
|
|
283
|
+
| Commands | `CommandName`, `AllCommandName`, `getCommandNames`, `getNetworkCommandNames`, `getPythonCommandNames` |
|
|
284
|
+
| Custom Commands | `defineCommand`, `CustomCommand`, `LazyCommand` |
|
|
285
|
+
| Filesystem | `InMemoryFs`, `OverlayFs`, `ReadWriteFs`, `MountableFs`, `IFileSystem` |
|
|
286
|
+
| Network | `NetworkConfig`, `NetworkAccessDeniedError`, `TooManyRedirectsError`, `RedirectNotAllowedError` |
|
|
287
|
+
| Sandbox | `Sandbox`, `SandboxCommand`, `SandboxOptions`, `OutputMessage` |
|
|
288
|
+
| Security | `DefenseInDepthBox`, `SecurityViolationLogger`, `SecurityViolationError`, `createConsoleViolationCallback` |
|
|
289
|
+
| Trace | `TraceCallback`, `TraceEvent` |
|
|
290
|
+
|
|
291
|
+
All types are re-exported from `src/types.ts` for downstream consumers.
|
|
238
292
|
|
|
239
293
|
## License
|
|
240
294
|
|
package/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "just-bash-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "MCP server providing a sandboxed bash environment using just-bash",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./
|
|
7
|
-
"types": "./build/index.d.ts",
|
|
6
|
+
"main": "./src/index.ts",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
10
|
-
"
|
|
11
|
-
"import": "./build/index.js"
|
|
9
|
+
"import": "./src/index.ts"
|
|
12
10
|
}
|
|
13
11
|
},
|
|
14
12
|
"bin": {
|
|
15
|
-
"just-bash-mcp": "
|
|
13
|
+
"just-bash-mcp": "src/index.ts"
|
|
16
14
|
},
|
|
17
15
|
"files": [
|
|
18
|
-
"
|
|
16
|
+
"src",
|
|
17
|
+
"tsconfig.json",
|
|
19
18
|
"README.md",
|
|
20
19
|
"LICENSE"
|
|
21
20
|
],
|
|
22
21
|
"scripts": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"format": "
|
|
29
|
-
"check": "
|
|
30
|
-
"
|
|
22
|
+
"start": "node src/index.ts",
|
|
23
|
+
"dev": "node --watch src/index.ts",
|
|
24
|
+
"lint": "oxlint --type-aware",
|
|
25
|
+
"lint:fix": "oxlint --type-aware --fix",
|
|
26
|
+
"typecheck": "oxlint --type-aware --type-check",
|
|
27
|
+
"format": "oxfmt --write src/",
|
|
28
|
+
"format:check": "oxfmt --check src/",
|
|
29
|
+
"check": "oxlint --type-aware --type-check && oxfmt --check src/",
|
|
30
|
+
"ci": "oxlint --type-aware --type-check && oxfmt --check src/"
|
|
31
31
|
},
|
|
32
32
|
"keywords": [
|
|
33
33
|
"mcp",
|
|
@@ -56,16 +56,18 @@
|
|
|
56
56
|
},
|
|
57
57
|
"homepage": "https://github.com/dalist1/just-bash-mcp#readme",
|
|
58
58
|
"engines": {
|
|
59
|
-
"node": ">=
|
|
59
|
+
"node": ">=22.0.0"
|
|
60
60
|
},
|
|
61
|
+
"packageManager": "bun@1.3.8",
|
|
61
62
|
"dependencies": {
|
|
62
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
63
|
-
"just-bash": "^2.
|
|
64
|
-
"zod": "^4.3.
|
|
63
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
64
|
+
"just-bash": "^2.9.6",
|
|
65
|
+
"zod": "^4.3.6"
|
|
65
66
|
},
|
|
66
67
|
"devDependencies": {
|
|
67
|
-
"@
|
|
68
|
-
"
|
|
69
|
-
"
|
|
68
|
+
"@types/node": "^25.2.2",
|
|
69
|
+
"oxfmt": "^0.28.0",
|
|
70
|
+
"oxlint": "^1.43.0",
|
|
71
|
+
"oxlint-tsgolint": "^0.11.5"
|
|
70
72
|
}
|
|
71
73
|
}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration module for just-bash-mcp
|
|
3
|
+
* Handles environment variable parsing and configuration building
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type BashLogger,
|
|
8
|
+
type BashOptions,
|
|
9
|
+
type CommandName,
|
|
10
|
+
type DefenseInDepthConfig,
|
|
11
|
+
type MountConfig,
|
|
12
|
+
type NetworkConfig,
|
|
13
|
+
type TraceCallback,
|
|
14
|
+
type TraceEvent,
|
|
15
|
+
OverlayFs,
|
|
16
|
+
ReadWriteFs,
|
|
17
|
+
SecurityViolationLogger,
|
|
18
|
+
createConsoleViolationCallback,
|
|
19
|
+
} from "just-bash";
|
|
20
|
+
|
|
21
|
+
// Re-export upstream types used by other modules
|
|
22
|
+
export type { DefenseInDepthConfig, TraceCallback, TraceEvent };
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Types
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
export type HttpMethod = "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Environment Variable Parsing
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
function parseEnvString(key: string, defaultValue: string): string {
|
|
35
|
+
return process.env[key] || defaultValue;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseEnvBoolean(key: string, defaultValue: boolean): boolean {
|
|
39
|
+
return process.env[key] === "true" ? true : defaultValue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseEnvInt(key: string, defaultValue: number): number {
|
|
43
|
+
const value = process.env[key];
|
|
44
|
+
if (!value) return defaultValue;
|
|
45
|
+
const parsed = Number.parseInt(value, 10);
|
|
46
|
+
return Number.isNaN(parsed) ? defaultValue : parsed;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseEnvStringArray(key: string): string[] {
|
|
50
|
+
return process.env[key]?.split(",").filter(Boolean) || [];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Configuration Constants
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
export interface Config {
|
|
58
|
+
readonly VERSION: string;
|
|
59
|
+
readonly SERVER_NAME: string;
|
|
60
|
+
readonly OVERLAY_ROOT: string | undefined;
|
|
61
|
+
readonly READ_WRITE_ROOT: string | undefined;
|
|
62
|
+
readonly MOUNTS_CONFIG: string | undefined;
|
|
63
|
+
readonly INITIAL_CWD: string;
|
|
64
|
+
readonly ALLOW_NETWORK: boolean;
|
|
65
|
+
readonly ALLOWED_URL_PREFIXES: string[];
|
|
66
|
+
readonly ALLOWED_METHODS: HttpMethod[];
|
|
67
|
+
readonly MAX_REDIRECTS: number;
|
|
68
|
+
readonly NETWORK_TIMEOUT_MS: number;
|
|
69
|
+
readonly MAX_RESPONSE_SIZE: number | undefined;
|
|
70
|
+
readonly MAX_CALL_DEPTH: number;
|
|
71
|
+
readonly MAX_COMMAND_COUNT: number;
|
|
72
|
+
readonly MAX_LOOP_ITERATIONS: number;
|
|
73
|
+
readonly MAX_AWK_ITERATIONS: number;
|
|
74
|
+
readonly MAX_SED_ITERATIONS: number;
|
|
75
|
+
readonly MAX_JQ_ITERATIONS: number;
|
|
76
|
+
readonly MAX_GLOB_OPERATIONS: number;
|
|
77
|
+
readonly MAX_STRING_LENGTH: number;
|
|
78
|
+
readonly MAX_ARRAY_ELEMENTS: number;
|
|
79
|
+
readonly MAX_HEREDOC_SIZE: number;
|
|
80
|
+
readonly MAX_SUBSTITUTION_DEPTH: number;
|
|
81
|
+
readonly MAX_SQLITE_TIMEOUT_MS: number;
|
|
82
|
+
readonly MAX_PYTHON_TIMEOUT_MS: number;
|
|
83
|
+
readonly MAX_OUTPUT_LENGTH: number;
|
|
84
|
+
readonly MAX_FILE_READ_SIZE: number | undefined;
|
|
85
|
+
readonly ENABLE_LOGGING: boolean;
|
|
86
|
+
readonly ENABLE_TRACING: boolean;
|
|
87
|
+
readonly ENABLE_PYTHON: boolean;
|
|
88
|
+
readonly ENABLE_DEFENSE_IN_DEPTH: boolean;
|
|
89
|
+
readonly DEFENSE_IN_DEPTH_AUDIT: boolean;
|
|
90
|
+
readonly DEFENSE_IN_DEPTH_LOG: boolean;
|
|
91
|
+
readonly OVERLAY_READ_ONLY: boolean;
|
|
92
|
+
readonly ALLOWED_COMMANDS: CommandName[] | undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function getAllowedMethods(): HttpMethod[] {
|
|
96
|
+
const methods = parseEnvStringArray("JUST_BASH_ALLOWED_METHODS");
|
|
97
|
+
return methods.length > 0 ? (methods as HttpMethod[]) : ["GET", "HEAD"];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getAllowedCommands(): CommandName[] | undefined {
|
|
101
|
+
const commands = parseEnvStringArray("JUST_BASH_ALLOWED_COMMANDS");
|
|
102
|
+
return commands.length > 0 ? (commands as CommandName[]) : undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function parseEnvOptionalInt(key: string): number | undefined {
|
|
106
|
+
const value = process.env[key];
|
|
107
|
+
if (!value) return undefined;
|
|
108
|
+
const parsed = Number.parseInt(value, 10);
|
|
109
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const config: Config = {
|
|
113
|
+
// Server info
|
|
114
|
+
VERSION: "2.8.0",
|
|
115
|
+
SERVER_NAME: "just-bash-mcp",
|
|
116
|
+
|
|
117
|
+
// Filesystem configuration
|
|
118
|
+
OVERLAY_ROOT: process.env.JUST_BASH_OVERLAY_ROOT,
|
|
119
|
+
READ_WRITE_ROOT: process.env.JUST_BASH_READ_WRITE_ROOT,
|
|
120
|
+
MOUNTS_CONFIG: process.env.JUST_BASH_MOUNTS,
|
|
121
|
+
INITIAL_CWD: parseEnvString("JUST_BASH_CWD", "/home/user"),
|
|
122
|
+
|
|
123
|
+
// Network configuration
|
|
124
|
+
ALLOW_NETWORK: parseEnvBoolean("JUST_BASH_ALLOW_NETWORK", false),
|
|
125
|
+
ALLOWED_URL_PREFIXES: parseEnvStringArray("JUST_BASH_ALLOWED_URLS"),
|
|
126
|
+
ALLOWED_METHODS: getAllowedMethods(),
|
|
127
|
+
MAX_REDIRECTS: parseEnvInt("JUST_BASH_MAX_REDIRECTS", 20),
|
|
128
|
+
NETWORK_TIMEOUT_MS: parseEnvInt("JUST_BASH_NETWORK_TIMEOUT_MS", 30000),
|
|
129
|
+
MAX_RESPONSE_SIZE: parseEnvOptionalInt("JUST_BASH_MAX_RESPONSE_SIZE"),
|
|
130
|
+
|
|
131
|
+
// Execution limits
|
|
132
|
+
MAX_CALL_DEPTH: parseEnvInt("JUST_BASH_MAX_CALL_DEPTH", 100),
|
|
133
|
+
MAX_COMMAND_COUNT: parseEnvInt("JUST_BASH_MAX_COMMAND_COUNT", 10000),
|
|
134
|
+
MAX_LOOP_ITERATIONS: parseEnvInt("JUST_BASH_MAX_LOOP_ITERATIONS", 10000),
|
|
135
|
+
MAX_AWK_ITERATIONS: parseEnvInt("JUST_BASH_MAX_AWK_ITERATIONS", 10000),
|
|
136
|
+
MAX_SED_ITERATIONS: parseEnvInt("JUST_BASH_MAX_SED_ITERATIONS", 10000),
|
|
137
|
+
MAX_JQ_ITERATIONS: parseEnvInt("JUST_BASH_MAX_JQ_ITERATIONS", 10000),
|
|
138
|
+
MAX_GLOB_OPERATIONS: parseEnvInt("JUST_BASH_MAX_GLOB_OPERATIONS", 100000),
|
|
139
|
+
MAX_STRING_LENGTH: parseEnvInt("JUST_BASH_MAX_STRING_LENGTH", 10485760),
|
|
140
|
+
MAX_ARRAY_ELEMENTS: parseEnvInt("JUST_BASH_MAX_ARRAY_ELEMENTS", 100000),
|
|
141
|
+
MAX_HEREDOC_SIZE: parseEnvInt("JUST_BASH_MAX_HEREDOC_SIZE", 10485760),
|
|
142
|
+
MAX_SUBSTITUTION_DEPTH: parseEnvInt("JUST_BASH_MAX_SUBSTITUTION_DEPTH", 50),
|
|
143
|
+
MAX_SQLITE_TIMEOUT_MS: parseEnvInt("JUST_BASH_MAX_SQLITE_TIMEOUT_MS", 5000),
|
|
144
|
+
MAX_PYTHON_TIMEOUT_MS: parseEnvInt("JUST_BASH_MAX_PYTHON_TIMEOUT_MS", 30000),
|
|
145
|
+
|
|
146
|
+
// Output limits
|
|
147
|
+
MAX_OUTPUT_LENGTH: parseEnvInt("JUST_BASH_MAX_OUTPUT_LENGTH", 30000),
|
|
148
|
+
|
|
149
|
+
// Filesystem limits
|
|
150
|
+
MAX_FILE_READ_SIZE: parseEnvOptionalInt("JUST_BASH_MAX_FILE_READ_SIZE"),
|
|
151
|
+
|
|
152
|
+
// Debugging
|
|
153
|
+
ENABLE_LOGGING: parseEnvBoolean("JUST_BASH_ENABLE_LOGGING", false),
|
|
154
|
+
ENABLE_TRACING: parseEnvBoolean("JUST_BASH_ENABLE_TRACING", false),
|
|
155
|
+
|
|
156
|
+
// Feature flags
|
|
157
|
+
ENABLE_PYTHON: parseEnvBoolean("JUST_BASH_ENABLE_PYTHON", false),
|
|
158
|
+
ENABLE_DEFENSE_IN_DEPTH: parseEnvBoolean("JUST_BASH_DEFENSE_IN_DEPTH", false),
|
|
159
|
+
DEFENSE_IN_DEPTH_AUDIT: parseEnvBoolean("JUST_BASH_DEFENSE_IN_DEPTH_AUDIT", false),
|
|
160
|
+
DEFENSE_IN_DEPTH_LOG: parseEnvBoolean("JUST_BASH_DEFENSE_IN_DEPTH_LOG", false),
|
|
161
|
+
OVERLAY_READ_ONLY: parseEnvBoolean("JUST_BASH_OVERLAY_READ_ONLY", false),
|
|
162
|
+
|
|
163
|
+
// Command filtering
|
|
164
|
+
ALLOWED_COMMANDS: getAllowedCommands(),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// ============================================================================
|
|
168
|
+
// Logger and Trace Callback
|
|
169
|
+
// ============================================================================
|
|
170
|
+
|
|
171
|
+
export const bashLogger: BashLogger | undefined = config.ENABLE_LOGGING
|
|
172
|
+
? {
|
|
173
|
+
info(message: string, data?: Record<string, unknown>): void {
|
|
174
|
+
if (data) {
|
|
175
|
+
console.error(`[just-bash] INFO: ${message}`, data);
|
|
176
|
+
} else {
|
|
177
|
+
console.error(`[just-bash] INFO: ${message}`);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
debug(message: string, data?: Record<string, unknown>): void {
|
|
181
|
+
if (data) {
|
|
182
|
+
console.error(`[just-bash] DEBUG: ${message}`, data);
|
|
183
|
+
} else {
|
|
184
|
+
console.error(`[just-bash] DEBUG: ${message}`);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
: undefined;
|
|
189
|
+
|
|
190
|
+
export const traceCallback: TraceCallback | undefined = config.ENABLE_TRACING
|
|
191
|
+
? (event: TraceEvent) => {
|
|
192
|
+
if (event.details) {
|
|
193
|
+
console.error(
|
|
194
|
+
`[just-bash] TRACE: ${event.category}/${event.name} ${event.durationMs}ms`,
|
|
195
|
+
JSON.stringify(event.details),
|
|
196
|
+
);
|
|
197
|
+
} else {
|
|
198
|
+
console.error(`[just-bash] TRACE: ${event.category}/${event.name} ${event.durationMs}ms`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
: undefined;
|
|
202
|
+
|
|
203
|
+
// ============================================================================
|
|
204
|
+
// Defense-in-Depth Configuration
|
|
205
|
+
// ============================================================================
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Shared SecurityViolationLogger instance for tracking violations across
|
|
209
|
+
* all Bash instances. Exposed so info-tools can report violation stats.
|
|
210
|
+
*/
|
|
211
|
+
export const violationLogger = new SecurityViolationLogger();
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Build the defense-in-depth configuration from environment variables.
|
|
215
|
+
* Returns `false` when disabled, or a full DefenseInDepthConfig object.
|
|
216
|
+
*/
|
|
217
|
+
export function buildDefenseInDepthConfig(): DefenseInDepthConfig | false {
|
|
218
|
+
if (!config.ENABLE_DEFENSE_IN_DEPTH) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const consoleCallback = config.DEFENSE_IN_DEPTH_LOG
|
|
223
|
+
? createConsoleViolationCallback()
|
|
224
|
+
: undefined;
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
enabled: true,
|
|
228
|
+
auditMode: config.DEFENSE_IN_DEPTH_AUDIT,
|
|
229
|
+
onViolation: (violation) => {
|
|
230
|
+
violationLogger.record(violation);
|
|
231
|
+
consoleCallback?.(violation);
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// Configuration Builders
|
|
238
|
+
// ============================================================================
|
|
239
|
+
|
|
240
|
+
export function buildNetworkConfig(): NetworkConfig | undefined {
|
|
241
|
+
if (!config.ALLOW_NETWORK) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (config.ALLOWED_URL_PREFIXES.length > 0) {
|
|
246
|
+
return {
|
|
247
|
+
allowedUrlPrefixes: config.ALLOWED_URL_PREFIXES,
|
|
248
|
+
allowedMethods: config.ALLOWED_METHODS,
|
|
249
|
+
maxRedirects: config.MAX_REDIRECTS,
|
|
250
|
+
timeoutMs: config.NETWORK_TIMEOUT_MS,
|
|
251
|
+
...(config.MAX_RESPONSE_SIZE !== undefined && {
|
|
252
|
+
maxResponseSize: config.MAX_RESPONSE_SIZE,
|
|
253
|
+
}),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
dangerouslyAllowFullInternetAccess: true,
|
|
259
|
+
maxRedirects: config.MAX_REDIRECTS,
|
|
260
|
+
timeoutMs: config.NETWORK_TIMEOUT_MS,
|
|
261
|
+
...(config.MAX_RESPONSE_SIZE !== undefined && {
|
|
262
|
+
maxResponseSize: config.MAX_RESPONSE_SIZE,
|
|
263
|
+
}),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function buildExecutionLimits(): NonNullable<BashOptions["executionLimits"]> {
|
|
268
|
+
return {
|
|
269
|
+
maxCallDepth: config.MAX_CALL_DEPTH,
|
|
270
|
+
maxCommandCount: config.MAX_COMMAND_COUNT,
|
|
271
|
+
maxLoopIterations: config.MAX_LOOP_ITERATIONS,
|
|
272
|
+
maxAwkIterations: config.MAX_AWK_ITERATIONS,
|
|
273
|
+
maxSedIterations: config.MAX_SED_ITERATIONS,
|
|
274
|
+
maxJqIterations: config.MAX_JQ_ITERATIONS,
|
|
275
|
+
maxGlobOperations: config.MAX_GLOB_OPERATIONS,
|
|
276
|
+
maxStringLength: config.MAX_STRING_LENGTH,
|
|
277
|
+
maxArrayElements: config.MAX_ARRAY_ELEMENTS,
|
|
278
|
+
maxHeredocSize: config.MAX_HEREDOC_SIZE,
|
|
279
|
+
maxSubstitutionDepth: config.MAX_SUBSTITUTION_DEPTH,
|
|
280
|
+
maxSqliteTimeoutMs: config.MAX_SQLITE_TIMEOUT_MS,
|
|
281
|
+
maxPythonTimeoutMs: config.MAX_PYTHON_TIMEOUT_MS,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function parseMountsConfig(): MountConfig[] {
|
|
286
|
+
if (!config.MOUNTS_CONFIG) return [];
|
|
287
|
+
try {
|
|
288
|
+
const parsed: unknown = JSON.parse(config.MOUNTS_CONFIG);
|
|
289
|
+
if (!Array.isArray(parsed)) return [];
|
|
290
|
+
return parsed.map(
|
|
291
|
+
(mount: { mountPoint: string; root: string; type?: string; readOnly?: boolean }) => {
|
|
292
|
+
const fsType = mount.type || "overlay";
|
|
293
|
+
const maxFileReadSize = config.MAX_FILE_READ_SIZE;
|
|
294
|
+
const filesystem =
|
|
295
|
+
fsType === "readwrite"
|
|
296
|
+
? new ReadWriteFs({
|
|
297
|
+
root: mount.root,
|
|
298
|
+
...(maxFileReadSize !== undefined && { maxFileReadSize }),
|
|
299
|
+
})
|
|
300
|
+
: new OverlayFs({
|
|
301
|
+
root: mount.root,
|
|
302
|
+
readOnly: mount.readOnly,
|
|
303
|
+
...(maxFileReadSize !== undefined && { maxFileReadSize }),
|
|
304
|
+
});
|
|
305
|
+
return { mountPoint: mount.mountPoint, filesystem };
|
|
306
|
+
},
|
|
307
|
+
);
|
|
308
|
+
} catch {
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ============================================================================
|
|
314
|
+
// Environment Variables Documentation
|
|
315
|
+
// ============================================================================
|
|
316
|
+
|
|
317
|
+
export const ENVIRONMENT_VARIABLES = {
|
|
318
|
+
JUST_BASH_OVERLAY_ROOT: "Real directory to mount as overlay (read from disk, write to memory)",
|
|
319
|
+
JUST_BASH_OVERLAY_READ_ONLY:
|
|
320
|
+
"If true, all writes to overlay filesystem throw errors (default: false)",
|
|
321
|
+
JUST_BASH_READ_WRITE_ROOT: "Real directory with read-write access",
|
|
322
|
+
JUST_BASH_MOUNTS: "JSON array of mount configurations (supports type, readOnly fields)",
|
|
323
|
+
JUST_BASH_CWD: "Initial working directory (default: /home/user)",
|
|
324
|
+
JUST_BASH_ALLOW_NETWORK: "Enable network access (default: false)",
|
|
325
|
+
JUST_BASH_ALLOWED_URLS: "Comma-separated URL prefixes to allow",
|
|
326
|
+
JUST_BASH_ALLOWED_METHODS: "Comma-separated HTTP methods (default: GET,HEAD)",
|
|
327
|
+
JUST_BASH_ALLOWED_COMMANDS: "Comma-separated list of allowed commands",
|
|
328
|
+
JUST_BASH_MAX_REDIRECTS: "Max HTTP redirects (default: 20)",
|
|
329
|
+
JUST_BASH_NETWORK_TIMEOUT_MS: "Network timeout in ms (default: 30000)",
|
|
330
|
+
JUST_BASH_MAX_RESPONSE_SIZE: "Max network response body size in bytes (default: 10MB)",
|
|
331
|
+
JUST_BASH_MAX_CALL_DEPTH: "Max recursion depth (default: 100)",
|
|
332
|
+
JUST_BASH_MAX_COMMAND_COUNT: "Max commands per execution (default: 10000)",
|
|
333
|
+
JUST_BASH_MAX_LOOP_ITERATIONS: "Max bash loop iterations (default: 10000)",
|
|
334
|
+
JUST_BASH_MAX_AWK_ITERATIONS: "Max AWK while/for loop iterations (default: 10000)",
|
|
335
|
+
JUST_BASH_MAX_SED_ITERATIONS: "Max sed branch loop iterations (default: 10000)",
|
|
336
|
+
JUST_BASH_MAX_JQ_ITERATIONS: "Max jq loop iterations (default: 10000)",
|
|
337
|
+
JUST_BASH_MAX_GLOB_OPERATIONS: "Max glob filesystem operations (default: 100000)",
|
|
338
|
+
JUST_BASH_MAX_STRING_LENGTH: "Max string length in bytes (default: 10485760 = 10MB)",
|
|
339
|
+
JUST_BASH_MAX_ARRAY_ELEMENTS: "Max array elements (default: 100000)",
|
|
340
|
+
JUST_BASH_MAX_HEREDOC_SIZE: "Max heredoc size in bytes (default: 10485760 = 10MB)",
|
|
341
|
+
JUST_BASH_MAX_SUBSTITUTION_DEPTH: "Max command substitution nesting depth (default: 50)",
|
|
342
|
+
JUST_BASH_MAX_SQLITE_TIMEOUT_MS: "SQLite timeout in ms (default: 5000)",
|
|
343
|
+
JUST_BASH_MAX_PYTHON_TIMEOUT_MS: "Python timeout in ms (default: 30000)",
|
|
344
|
+
JUST_BASH_MAX_OUTPUT_LENGTH: "Max output length (default: 30000)",
|
|
345
|
+
JUST_BASH_MAX_FILE_READ_SIZE:
|
|
346
|
+
"Max file read size in bytes for OverlayFs/ReadWriteFs (default: 10MB)",
|
|
347
|
+
JUST_BASH_ENABLE_LOGGING: "Enable debug logging (default: false)",
|
|
348
|
+
JUST_BASH_ENABLE_TRACING: "Enable performance tracing (default: false)",
|
|
349
|
+
JUST_BASH_ENABLE_PYTHON: "Enable python3/python commands via Pyodide (default: false)",
|
|
350
|
+
JUST_BASH_DEFENSE_IN_DEPTH:
|
|
351
|
+
"Enable defense-in-depth mode that patches dangerous JS globals (default: false)",
|
|
352
|
+
JUST_BASH_DEFENSE_IN_DEPTH_AUDIT:
|
|
353
|
+
"Audit mode: log violations but don't block them (default: false, requires DEFENSE_IN_DEPTH=true)",
|
|
354
|
+
JUST_BASH_DEFENSE_IN_DEPTH_LOG:
|
|
355
|
+
"Log violations to console via createConsoleViolationCallback (default: false)",
|
|
356
|
+
} as const;
|
|
357
|
+
|
|
358
|
+
// ============================================================================
|
|
359
|
+
// Command Categories Documentation
|
|
360
|
+
// ============================================================================
|
|
361
|
+
|
|
362
|
+
export const COMMAND_CATEGORIES = {
|
|
363
|
+
fileOperations: "cat, cp, file, ln, ls, mkdir, mv, readlink, rm, rmdir, split, stat, touch, tree",
|
|
364
|
+
textProcessing:
|
|
365
|
+
"awk, base64, column, comm, cut, diff, expand, fold, grep (egrep, fgrep), head, join, md5sum, nl, od, paste, printf, rev, rg (ripgrep), sed, sha1sum, sha256sum, sort, strings, tac, tail, tr, unexpand, uniq, wc, xargs",
|
|
366
|
+
dataProcessing:
|
|
367
|
+
"jq (JSON), python3/python (Python via Pyodide), sqlite3 (SQLite), xan (CSV), yq (YAML/XML/TOML/CSV)",
|
|
368
|
+
compression: "gzip (gunzip, zcat), tar",
|
|
369
|
+
navigation:
|
|
370
|
+
"basename, cd, dirname, du, echo, env, export, find, hostname, printenv, pwd, tee, whoami",
|
|
371
|
+
shellUtilities:
|
|
372
|
+
"alias, bash, chmod, clear, date, expr, false, help, history, seq, sh, sleep, time, timeout, true, unalias, which",
|
|
373
|
+
network: "curl, html-to-markdown (when network enabled)",
|
|
374
|
+
} as const;
|
|
375
|
+
|
|
376
|
+
// ============================================================================
|
|
377
|
+
// Features Documentation
|
|
378
|
+
// ============================================================================
|
|
379
|
+
|
|
380
|
+
export const FEATURES = {
|
|
381
|
+
customCommands:
|
|
382
|
+
"Define custom TypeScript commands using defineCommand() from just-bash, supports lazy-loading via LazyCommand",
|
|
383
|
+
rawScript: "Preserve leading whitespace in scripts (useful for here-docs)",
|
|
384
|
+
logger: "Optional execution logging via BashLogger interface",
|
|
385
|
+
trace: "Performance profiling via TraceCallback (upstream type)",
|
|
386
|
+
commandFilter: "Restrict available commands via JUST_BASH_ALLOWED_COMMANDS env var",
|
|
387
|
+
sandboxApi:
|
|
388
|
+
"Vercel Sandbox compatible API via bash_sandbox_* tools (run, write, read, mkdir, stop, reset)",
|
|
389
|
+
python: "Python support via Pyodide (opt-in via JUST_BASH_ENABLE_PYTHON=true)",
|
|
390
|
+
defenseInDepth:
|
|
391
|
+
"Defense-in-depth with SecurityViolationLogger, audit mode, and console logging (opt-in via JUST_BASH_DEFENSE_IN_DEPTH=true)",
|
|
392
|
+
overlayReadOnly:
|
|
393
|
+
"Read-only overlay filesystem mode (opt-in via JUST_BASH_OVERLAY_READ_ONLY=true)",
|
|
394
|
+
networkResponseSize:
|
|
395
|
+
"Configurable max network response body size via JUST_BASH_MAX_RESPONSE_SIZE",
|
|
396
|
+
fileReadSizeLimit:
|
|
397
|
+
"Configurable max file read size for OverlayFs/ReadWriteFs via JUST_BASH_MAX_FILE_READ_SIZE",
|
|
398
|
+
networkErrorHandling:
|
|
399
|
+
"Rich network error classification: NetworkAccessDeniedError, TooManyRedirectsError, RedirectNotAllowedError",
|
|
400
|
+
securityViolationTracking:
|
|
401
|
+
"SecurityViolationLogger tracks all defense-in-depth violations with stats via bash_info",
|
|
402
|
+
} as const;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
4
|
* just-bash-mcp - MCP Server for sandboxed bash execution
|
|
4
5
|
*
|
|
@@ -8,23 +9,28 @@
|
|
|
8
9
|
* @see https://github.com/vercel-labs/just-bash
|
|
9
10
|
* @see https://modelcontextprotocol.io
|
|
10
11
|
*/
|
|
12
|
+
|
|
11
13
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
14
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
+
|
|
16
|
+
import { config } from "./config/index.ts";
|
|
17
|
+
import { registerAllTools } from "./tools/index.ts";
|
|
18
|
+
|
|
15
19
|
// ============================================================================
|
|
16
20
|
// Server Initialization
|
|
17
21
|
// ============================================================================
|
|
22
|
+
|
|
18
23
|
const server = new McpServer({
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
name: config.SERVER_NAME,
|
|
25
|
+
version: config.VERSION,
|
|
21
26
|
});
|
|
27
|
+
|
|
22
28
|
// Register all tools with the server
|
|
23
29
|
registerAllTools(server);
|
|
30
|
+
|
|
24
31
|
// ============================================================================
|
|
25
32
|
// Start Server
|
|
26
33
|
// ============================================================================
|
|
34
|
+
|
|
27
35
|
const transport = new StdioServerTransport();
|
|
28
36
|
await server.connect(transport);
|
|
29
|
-
console.error(`${config.SERVER_NAME} server v${config.VERSION} running on stdio`);
|
|
30
|
-
//# sourceMappingURL=index.js.map
|