@n0zer0d4y/vulcan-file-ops 1.2.13 → 1.3.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 +99 -27
- package/README.md +153 -118
- package/dist/cli.js +16 -0
- package/dist/server/index.js +150 -39
- package/dist/tools/filesystem-tools.js +222 -39
- package/dist/tools/read-tools.js +89 -16
- package/dist/tools/shell-tool.js +48 -62
- package/dist/tools/write-tools.js +19 -17
- package/dist/types/index.js +6 -3
- package/dist/utils/command-path-extraction.js +169 -174
- package/dist/utils/command-validation.js +62 -10
- package/dist/utils/document-parser.js +43 -5
- package/dist/utils/html-image-sanitizer.js +478 -0
- package/dist/utils/html-to-document.js +75 -11
- package/dist/utils/lib.js +357 -80
- package/dist/utils/limits.js +47 -0
- package/dist/utils/regex-worker.js +262 -0
- package/dist/utils/shell-parser.js +225 -0
- package/dist/utils/zip-guard.js +266 -0
- package/package.json +11 -8
package/CHANGELOG.md
CHANGED
|
@@ -8,33 +8,105 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
8
8
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
## [1.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
11
|
+
|
|
12
|
+
## [1.3.0] - 2026-10-04
|
|
13
|
+
|
|
14
|
+
Security hardening release. Upgrading is strongly recommended. See **Breaking Changes** before upgrading.
|
|
15
|
+
|
|
16
|
+
### Security
|
|
17
|
+
|
|
18
|
+
- **execute_shell: commands could bypass the approved-command list.** Newline-separated commands were not seen by the allowlist check. Commands are now parsed with a conservative, quote-aware parser that accepts only a grammar bash and PowerShell interpret the same way; newlines and control characters, backticks, escaped quotes (`\"`, `\'`), a lone `&`, `( )`/`{ }` grouping and script blocks, heredocs, Unicode quotes and PowerShell's `--%` are rejected. Separators inside quotes are now correctly treated as data.
|
|
19
|
+
- **execute_shell: path validation gaps.** Slash-prefixed absolute paths (`/etc/passwd`, `/Windows/...`) were treated as command switches and never validated; redirection targets written without a space (`>C:\outside\file`) were not validated; paths through symlinks/junctions were checked only lexically (GitHub issue #3). Every operand, option value (`--out=...`, `-C:\x`), redirection target and `cd` target is now validated lexically, after `realpath`, and with physical resolution (symlinks followed before `..`). Relative operands are checked against every directory a `cd` could leave the command in. Arguments with unresolvable variables and PowerShell provider paths (`env:`, `HKLM:`, ...) are refused.
|
|
20
|
+
- **execute_shell: dangerous-command check could be self-approved.** The `requiresApproval` argument let the caller (the AI) bypass the dangerous-pattern check. It is now ignored; the server operator can allow specific commands with `--allow-dangerous-commands`. The over-broad `format` pattern no longer matches `Get-Date -Format`.
|
|
21
|
+
- **make_directory could create directories outside approved folders** through a symlink or junction (GitHub issue #3). Paths are now validated canonically and created segment by segment with a `realpath` check after each segment.
|
|
22
|
+
- **register_directory let the AI widen its own access without user involvement.** Registration now requires user confirmation through the MCP client by default (see `--runtime-registration`). The real path is registered; filesystem roots and the home directory require `allow`.
|
|
23
|
+
- **`.env` in the server's working directory controlled the shell allowlist** and was injected into the environment of executed commands. The working-directory `.env` is no longer read (see `--commands-env-file`).
|
|
24
|
+
- **PDF generation could crash the server.** Any `<img>` that was not an inline `data:` image caused an unhandled promise rejection that terminated the process; corrupt PNG data could also crash it. Images are now sanitized (only `data:image/...` images up to 10 MB are embedded; others are replaced by their alt text), PDF rendering errors are returned as tool errors with a 30 s timeout, and unhandled rejections are logged to stderr instead of terminating the server.
|
|
25
|
+
- **DOCX generation fetched remote image URLs** (server-side HTTP request with the response embedded in the document). Remote images are no longer fetched.
|
|
26
|
+
- **grep_files regular expressions could freeze the server** (catastrophic backtracking). Matching now runs in a worker thread with a 5 s per-file and 30 s per-search budget; regex and glob patterns are limited to 1,000 characters.
|
|
27
|
+
- **Unbounded reads and decompression.** Full-mode text reads are capped at 10 MB (use `head`/`tail`/`range` for larger files), `attach_image` at 10 MB per image and 20 MB per call, and Office/ODF documents are checked for zip bombs (size, entry count, compression ratio, ZIP64) before parsing.
|
|
28
|
+
- **Directory copies dereferenced symlinks**, copying files from outside approved folders into them. Copying a directory that contains symlinks or junctions is now refused.
|
|
29
|
+
- **PDF/DOCX output is written atomically** and never through a symlink, like text files.
|
|
30
|
+
- **Dependencies refreshed** with a 14-day release quarantine (`npm --before`), including `@modelcontextprotocol/sdk` 1.20.0 → 1.30.0 (fixes GHSA-345p-7cg4-v4c7, GHSA-w48q-cv73-mx4w, GHSA-8r9q-7v3j-jr4g), `axios` (via `@turbodocx/html-to-docx`), `@xmldom/xmldom`, `minimatch`, `mammoth` and `officeparser` 5.2 → 7.8 (fixes a `file-type` infinite loop on crafted files). Snyk Open Source runtime findings went from 89 to 0 open; one `pdfjs-dist` advisory is ignored with justification because it is unreachable (PDFs are never parsed with `officeparser`, and the issue requires browser rendering). All packages pass `npm audit signatures`.
|
|
31
|
+
- **Office/ODF spreadsheet expansion is bounded.** `officeparser` 7.x caps ODF "repeated cells" at 1,000,000 cells, so a tiny file that claims billions of cells can no longer expand without limit; its ZIP decompression limits are aligned with the zip-bomb pre-check (200 MB, 10,000 entries).
|
|
32
|
+
|
|
33
|
+
### Breaking Changes
|
|
34
|
+
|
|
35
|
+
- `register_directory` asks the user to confirm each directory by default. Clients without MCP elicitation support get a refusal; use `--approved-folders`, or start the server with `--runtime-registration allow` to restore the previous behavior.
|
|
36
|
+
- The server no longer reads `.env` from its working directory. Pass `--approved-commands`, or `--commands-env-file <path>` (only `APPROVED_COMMANDS` is read). `APPROVED_COMMANDS` set as a process environment variable is no longer used. `--env-file` is rejected because Node.js reserves it.
|
|
37
|
+
- `execute_shell` rejects commands outside the supported grammar (see Security above), and `requiresApproval` no longer bypasses dangerous-pattern blocking.
|
|
38
|
+
- Node.js 20.19+ (or 22.12+) is required (already required by `jsdom` 27; now declared in `engines`).
|
|
39
|
+
- Copying directories that contain symlinks or junctions is refused.
|
|
40
|
+
- Full-mode reads of text files over 10 MB are refused.
|
|
41
|
+
- `officeparser` 7.x (used for PPTX, XLSX and OpenDocument files) adds about 46 MB to the install, mostly the `tesseract.js` OCR engine it depends on. OCR is disabled and the engine is never loaded, but it is downloaded with the package.
|
|
42
|
+
- `move_file` refuses to overwrite an existing destination (previously it replaced it silently). Use `file_operations` with `onConflict: "overwrite"` to replace files.
|
|
43
|
+
- `attach_image` returns SVG files as markup text and rejects BMP files; only PNG, JPEG, GIF and WebP are returned as images.
|
|
44
|
+
- `grep_files` `glob` without a slash now matches at any depth (`*.md` also matches `docs/guide.md`).
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- `--runtime-registration <confirm|allow|deny>`, `--allow-dangerous-commands <cmds>`, `--commands-env-file <path>`.
|
|
49
|
+
- `SECURITY.md` with private vulnerability reporting instructions.
|
|
50
|
+
- GitHub Actions CI: runs the test suite and the build on Windows with Node 22.
|
|
51
|
+
- Dependabot version updates for npm and GitHub Actions with a 14-day cooldown.
|
|
52
|
+
- `engines` field in `package.json` (`^20.19.0 || >=22.12.0`).
|
|
53
|
+
- Regression test suites for the fixes above.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- Blocked `execute_shell` commands now report errors starting with `Access denied:` (validation, approval, dangerous-pattern and path checks).
|
|
58
|
+
- The `.snyk` policy, which was invalid and suppressed nothing, is replaced by a minimal policy with one justified, time-boxed ignore.
|
|
59
|
+
- Test tooling is pinned to `jest` / `@jest/globals` 30.2.0 and `ts-jest` 29.4.6; newer versions hang on several suites in this project.
|
|
60
|
+
- Documentation corrected: Node.js requirement (was "14 or higher"), README security claims that the 1.3.0 audit disproved, and dated notes on the earlier audit reports in `docs/`.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- DOCX generation no longer mangles quoted HTML attributes (inline images and `style` attributes work again).
|
|
65
|
+
- Shell path-validation tests that passed for the wrong reason (missing `workdir`) now assert the actual path denial.
|
|
66
|
+
- `move_file` no longer silently overwrites an existing destination, matching its description; case-only renames (e.g. `readme.md` → `README.md`) now take effect.
|
|
67
|
+
- `read_file` / `read_multiple_files` `tail` mode no longer counts the newline at the end of a file as an extra empty line.
|
|
68
|
+
- `grep_files` `glob` without a slash (e.g. `*.md`) now matches files at any depth, like ripgrep; patterns with a slash still match the path from the search root.
|
|
69
|
+
- `write_multiple_files` reports the size of the written file; for PDF/DOCX this was previously the length of the HTML input.
|
|
70
|
+
- `attach_image` no longer sends SVG or BMP as images (vision models reject them): SVG is returned as markup text and BMP returns an error asking for PNG/JPEG.
|
|
71
|
+
- A destination that resolves outside the allowed directories is reported as "Access denied" instead of "Parent directory does not exist".
|
|
72
|
+
- The `execute_shell` description recommends `;` on Windows, where Windows PowerShell 5.1 does not support `&&` or `||`.
|
|
73
|
+
|
|
74
|
+
## [1.2.14] - 2026-05-16
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- **CRITICAL**: Fixed initialization deadlock with `claude-ai` v0.1.0 client (Claude Desktop web client, protocol `2025-11-25`).
|
|
79
|
+
- **Root Cause**: A custom `setRequestHandler(InitializeRequestSchema, ...)` was overriding the SDK's internal `_oninitialize` handler. This broke the SDK's state machine: `_clientCapabilities` and `_clientVersion` were never set, and protocol version negotiation was bypassed — the server echoed back the client's requested version verbatim rather than responding with the highest version it actually supports. The new client's stricter handshake exposed this latent defect.
|
|
80
|
+
- **Fix**: Removed the custom initialize handler and restored the SDK's built-in `_oninitialize`. The server now correctly negotiates `2025-06-18` when a client requests `2025-11-25`, which the client accepts per the MCP spec.
|
|
81
|
+
- **Instructions**: Server instructions (`generateServerDescription()`) are now injected into the SDK's `_instructions` field after directory initialization in `runServer()`, so they remain present in the initialize response without requiring a custom handler.
|
|
82
|
+
- Fixed `oninitialized` callback blocking the MCP handshake. The `listRoots()` call inside `oninitialized` was previously awaited, creating a potential deadlock (client won't respond to `roots/list` until after `initialized` is acknowledged, but the callback blocked acknowledgement). Changed to a detached fire-and-forget promise.
|
|
83
|
+
|
|
84
|
+
## [1.2.13] - 2026-04-23
|
|
85
|
+
|
|
86
|
+
### Fixed
|
|
87
|
+
|
|
88
|
+
- Resolved the remaining MCP client tool discovery failure introduced after the SDK upgrade.
|
|
89
|
+
- Tool schemas exposed through `tools/list` are now normalized for stricter MCP clients.
|
|
90
|
+
- Removed client-hostile schema constructs from published tool input schemas, including `$schema`, `$ref`, `anyOf`, `oneOf`, and `allOf`.
|
|
91
|
+
- Replaced the highest-risk wire schemas with explicit compatibility-first object schemas for `attach_image` and `make_directory`.
|
|
92
|
+
- Preserved runtime backward compatibility while tightening the schemas advertised to clients.
|
|
93
|
+
- Fixed residual MCP mode detection drift so stdio mode is triggered reliably in local and `npx` execution paths.
|
|
94
|
+
- Added `zod` as a direct runtime dependency to avoid install-time fragility in clean environments and `npx` usage.
|
|
95
|
+
- Synchronized package metadata so published registry metadata and local package metadata report the same release version.
|
|
96
|
+
- Re-enabled and stabilized the previously skipped PDF-related Jest coverage.
|
|
97
|
+
- Enabled the 10 skipped document tests for PDF read, HTML-to-PDF write, mixed document batches, and PDF round-trip paths.
|
|
98
|
+
- Updated Jest PDF mocks so document tests exercise meaningful content paths instead of placeholder-only buffers.
|
|
99
|
+
- Suppressed expected negative-path test diagnostics inside tests so successful runs no longer look like runtime failures.
|
|
100
|
+
- Added a regression test to prevent incompatible tool schema shapes from reappearing in future releases.
|
|
101
|
+
|
|
102
|
+
### Changed
|
|
103
|
+
|
|
104
|
+
- Documented the Codex-specific MCP configuration format in the README alongside the JSON-based client examples.
|
|
105
|
+
- Updated `npx` examples to use `-y` explicitly for non-interactive client execution.
|
|
106
|
+
|
|
107
|
+
## [1.2.12] - 2026-02-21
|
|
108
|
+
|
|
109
|
+
### Fixed
|
|
38
110
|
|
|
39
111
|
- **CRITICAL**: Fixed MCP server toggle failure and "no tools configured" error caused by Node.js version incompatibility and restrictive MCP detection.
|
|
40
112
|
- **Node.js Compatibility**: Replaced `import ... with { type: "json" }` with manual `fs.readFileSync` for `package.json` to support Node.js versions earlier than 20.10.0 and 18.20.0 (including Node 14/16).
|
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ This enhanced implementation provides:
|
|
|
72
72
|
This server supports multiple flexible approaches to directory access:
|
|
73
73
|
|
|
74
74
|
1. **Pre-configured Access**: Use `--approved-folders` to specify directories on server start for immediate access
|
|
75
|
-
2. **Runtime Registration**: Users can instruct AI agents to register directories during conversation via `register_directory` tool
|
|
75
|
+
2. **Runtime Registration**: Users can instruct AI agents to register directories during conversation via `register_directory` tool. By default you are asked to confirm each new directory through your MCP client (see [Runtime Registration Policy](#runtime-registration-policy))
|
|
76
76
|
3. **MCP Roots Protocol**: Client applications can provide workspace directories dynamically
|
|
77
77
|
4. **Flexible Permissions**: Combine multiple approaches - start with approved folders, add more at runtime
|
|
78
78
|
5. **Secure Boundaries**: All operations validate against registered directories regardless of access method
|
|
@@ -109,7 +109,7 @@ npm install @n0zer0d4y/vulcan-file-ops
|
|
|
109
109
|
|
|
110
110
|
### Prerequisites
|
|
111
111
|
|
|
112
|
-
**Node.js** (version
|
|
112
|
+
**Node.js** (version 20.19 or higher, or 22.12 or higher) must be installed on your system. This provides npm and npx, which are required to run this package.
|
|
113
113
|
|
|
114
114
|
- **Download Node.js**: https://nodejs.org/
|
|
115
115
|
- **Check installation**: Run `node --version` and `npm --version`
|
|
@@ -124,10 +124,10 @@ This server can be used directly with npx (recommended) or installed globally/lo
|
|
|
124
124
|
|
|
125
125
|
### Basic Configuration
|
|
126
126
|
|
|
127
|
-
Add to your MCP client configuration.
|
|
128
|
-
|
|
129
|
-
For JSON-based clients such as Claude Desktop and Cursor, use their `mcpServers` JSON format.
|
|
130
|
-
For Codex, use `C:\Users\<username>\.codex\config.toml` and the `mcp_servers` TOML table format shown below.
|
|
127
|
+
Add to your MCP client configuration.
|
|
128
|
+
|
|
129
|
+
For JSON-based clients such as Claude Desktop and Cursor, use their `mcpServers` JSON format.
|
|
130
|
+
For Codex, use `C:\Users\<username>\.codex\config.toml` and the `mcp_servers` TOML table format shown below.
|
|
131
131
|
|
|
132
132
|
#### Option 1: Using npx (Recommended - No Installation Required)
|
|
133
133
|
|
|
@@ -135,22 +135,22 @@ For Codex, use `C:\Users\<username>\.codex\config.toml` and the `mcp_servers` TO
|
|
|
135
135
|
{
|
|
136
136
|
"mcpServers": {
|
|
137
137
|
"vulcan-file-ops": {
|
|
138
|
-
"command": "npx",
|
|
139
|
-
"args": ["-y", "@n0zer0d4y/vulcan-file-ops"]
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**Codex (`config.toml`)**
|
|
146
|
-
|
|
147
|
-
```toml
|
|
148
|
-
[mcp_servers.vulcan_file_ops]
|
|
149
|
-
command = "npx"
|
|
150
|
-
args = ["-y", "@n0zer0d4y/vulcan-file-ops"]
|
|
151
|
-
enabled = true
|
|
152
|
-
startup_timeout_sec = 120.0
|
|
153
|
-
```
|
|
138
|
+
"command": "npx",
|
|
139
|
+
"args": ["-y", "@n0zer0d4y/vulcan-file-ops"]
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Codex (`config.toml`)**
|
|
146
|
+
|
|
147
|
+
```toml
|
|
148
|
+
[mcp_servers.vulcan_file_ops]
|
|
149
|
+
command = "npx"
|
|
150
|
+
args = ["-y", "@n0zer0d4y/vulcan-file-ops"]
|
|
151
|
+
enabled = true
|
|
152
|
+
startup_timeout_sec = 120.0
|
|
153
|
+
```
|
|
154
154
|
|
|
155
155
|
#### Option 2: Using Global Installation
|
|
156
156
|
|
|
@@ -180,7 +180,7 @@ After running `npm install @n0zer0d4y/vulcan-file-ops` in your project:
|
|
|
180
180
|
}
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
-
#### Option 4: Local Repository Execution (For Developers)
|
|
183
|
+
#### Option 4: Local Repository Execution (For Developers)
|
|
184
184
|
|
|
185
185
|
If you've cloned this repository and want to run from source:
|
|
186
186
|
|
|
@@ -191,39 +191,39 @@ npm install
|
|
|
191
191
|
npm run build
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
-
Then configure your MCP client:
|
|
195
|
-
|
|
196
|
-
```json
|
|
197
|
-
{
|
|
198
|
-
"mcpServers": {
|
|
199
|
-
"vulcan-file-ops": {
|
|
200
|
-
"command": "node",
|
|
201
|
-
"args": [
|
|
202
|
-
"/absolute/path/to/vulcan-file-ops/dist/cli.js",
|
|
203
|
-
"--approved-folders",
|
|
204
|
-
"/path/to/your/allowed/directories"
|
|
205
|
-
]
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
**Codex (`config.toml`)**
|
|
212
|
-
|
|
213
|
-
```toml
|
|
214
|
-
[mcp_servers.vulcan_file_ops]
|
|
215
|
-
command = "node"
|
|
216
|
-
args = [
|
|
217
|
-
'C:\absolute\path\to\vulcan-file-ops\dist\cli.js',
|
|
218
|
-
"--approved-folders",
|
|
219
|
-
'C:\path\to\your\allowed\directories'
|
|
220
|
-
]
|
|
221
|
-
cwd = 'C:\absolute\path\to\vulcan-file-ops'
|
|
222
|
-
enabled = true
|
|
223
|
-
startup_timeout_sec = 120.0
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
**Note:** For local repository execution, prefer `node dist/cli.js` with an absolute path. This works reliably in Codex and avoids PATH ambiguity.
|
|
194
|
+
Then configure your MCP client:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"mcpServers": {
|
|
199
|
+
"vulcan-file-ops": {
|
|
200
|
+
"command": "node",
|
|
201
|
+
"args": [
|
|
202
|
+
"/absolute/path/to/vulcan-file-ops/dist/cli.js",
|
|
203
|
+
"--approved-folders",
|
|
204
|
+
"/path/to/your/allowed/directories"
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Codex (`config.toml`)**
|
|
212
|
+
|
|
213
|
+
```toml
|
|
214
|
+
[mcp_servers.vulcan_file_ops]
|
|
215
|
+
command = "node"
|
|
216
|
+
args = [
|
|
217
|
+
'C:\absolute\path\to\vulcan-file-ops\dist\cli.js',
|
|
218
|
+
"--approved-folders",
|
|
219
|
+
'C:\path\to\your\allowed\directories'
|
|
220
|
+
]
|
|
221
|
+
cwd = 'C:\absolute\path\to\vulcan-file-ops'
|
|
222
|
+
enabled = true
|
|
223
|
+
startup_timeout_sec = 120.0
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Note:** For local repository execution, prefer `node dist/cli.js` with an absolute path. This works reliably in Codex and avoids PATH ambiguity.
|
|
227
227
|
|
|
228
228
|
### Advanced Configuration
|
|
229
229
|
|
|
@@ -237,12 +237,12 @@ Pre-configure specific directories for immediate access on server start:
|
|
|
237
237
|
{
|
|
238
238
|
"mcpServers": {
|
|
239
239
|
"vulcan-file-ops": {
|
|
240
|
-
"command": "npx",
|
|
241
|
-
"args": [
|
|
242
|
-
"-y",
|
|
243
|
-
"@n0zer0d4y/vulcan-file-ops",
|
|
244
|
-
"--approved-folders",
|
|
245
|
-
"/Users/username/projects",
|
|
240
|
+
"command": "npx",
|
|
241
|
+
"args": [
|
|
242
|
+
"-y",
|
|
243
|
+
"@n0zer0d4y/vulcan-file-ops",
|
|
244
|
+
"--approved-folders",
|
|
245
|
+
"/Users/username/projects",
|
|
246
246
|
"/Users/username/documents"
|
|
247
247
|
]
|
|
248
248
|
}
|
|
@@ -256,12 +256,12 @@ Pre-configure specific directories for immediate access on server start:
|
|
|
256
256
|
{
|
|
257
257
|
"mcpServers": {
|
|
258
258
|
"vulcan-file-ops": {
|
|
259
|
-
"command": "npx",
|
|
260
|
-
"args": [
|
|
261
|
-
"-y",
|
|
262
|
-
"@n0zer0d4y/vulcan-file-ops",
|
|
263
|
-
"--approved-folders",
|
|
264
|
-
"C:/Users/username/projects",
|
|
259
|
+
"command": "npx",
|
|
260
|
+
"args": [
|
|
261
|
+
"-y",
|
|
262
|
+
"@n0zer0d4y/vulcan-file-ops",
|
|
263
|
+
"--approved-folders",
|
|
264
|
+
"C:/Users/username/projects",
|
|
265
265
|
"C:/Users/username/documents"
|
|
266
266
|
]
|
|
267
267
|
}
|
|
@@ -269,7 +269,7 @@ Pre-configure specific directories for immediate access on server start:
|
|
|
269
269
|
}
|
|
270
270
|
```
|
|
271
271
|
|
|
272
|
-
**Alternative: Local Repository Execution**
|
|
272
|
+
**Alternative: Local Repository Execution**
|
|
273
273
|
|
|
274
274
|
For users running from a cloned repository (after `npm run build`):
|
|
275
275
|
|
|
@@ -277,32 +277,32 @@ For users running from a cloned repository (after `npm run build`):
|
|
|
277
277
|
{
|
|
278
278
|
"mcpServers": {
|
|
279
279
|
"vulcan-file-ops": {
|
|
280
|
-
"command": "vulcan-file-ops",
|
|
281
|
-
"args": [
|
|
282
|
-
"--approved-folders",
|
|
283
|
-
"/Users/username/projects",
|
|
284
|
-
"/Users/username/documents"
|
|
280
|
+
"command": "vulcan-file-ops",
|
|
281
|
+
"args": [
|
|
282
|
+
"--approved-folders",
|
|
283
|
+
"/Users/username/projects",
|
|
284
|
+
"/Users/username/documents"
|
|
285
285
|
]
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
**Codex with Approved Folders (`config.toml`)**
|
|
292
|
-
|
|
293
|
-
```toml
|
|
294
|
-
[mcp_servers.vulcan_file_ops]
|
|
295
|
-
command = "node"
|
|
296
|
-
args = [
|
|
297
|
-
'C:\absolute\path\to\vulcan-file-ops\dist\cli.js',
|
|
298
|
-
"--approved-folders",
|
|
299
|
-
'C:\Users\username\projects',
|
|
300
|
-
'C:\Users\username\documents'
|
|
301
|
-
]
|
|
302
|
-
cwd = 'C:\absolute\path\to\vulcan-file-ops'
|
|
303
|
-
enabled = true
|
|
304
|
-
startup_timeout_sec = 120.0
|
|
305
|
-
```
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Codex with Approved Folders (`config.toml`)**
|
|
292
|
+
|
|
293
|
+
```toml
|
|
294
|
+
[mcp_servers.vulcan_file_ops]
|
|
295
|
+
command = "node"
|
|
296
|
+
args = [
|
|
297
|
+
'C:\absolute\path\to\vulcan-file-ops\dist\cli.js',
|
|
298
|
+
"--approved-folders",
|
|
299
|
+
'C:\Users\username\projects',
|
|
300
|
+
'C:\Users\username\documents'
|
|
301
|
+
]
|
|
302
|
+
cwd = 'C:\absolute\path\to\vulcan-file-ops'
|
|
303
|
+
enabled = true
|
|
304
|
+
startup_timeout_sec = 120.0
|
|
305
|
+
```
|
|
306
306
|
|
|
307
307
|
**Path Format Note:**
|
|
308
308
|
|
|
@@ -399,6 +399,28 @@ Or enable individual tools:
|
|
|
399
399
|
}
|
|
400
400
|
```
|
|
401
401
|
|
|
402
|
+
#### Shell Commands
|
|
403
|
+
|
|
404
|
+
`execute_shell` only runs commands whose root command is approved:
|
|
405
|
+
|
|
406
|
+
- `--approved-commands npm,node,git` — comma-separated allowlist of root commands. Every command in a chain (`;`, `&&`, `||`, `|`) must be approved.
|
|
407
|
+
- `--allow-dangerous-commands rm` — approved commands that may also run when they match a dangerous pattern (for example `rm -rf`, `del /s`, `Remove-Item -Recurse`, `sudo`). Without this flag such commands are always blocked; the AI cannot override it.
|
|
408
|
+
- `--commands-env-file <path>` — read `APPROVED_COMMANDS` from a `.env`-format file. Only that key is used and nothing is added to the server's environment. `--approved-commands` takes priority.
|
|
409
|
+
|
|
410
|
+
> **Changed in 1.3.0:** the server no longer reads a `.env` file from its working directory. Use `--approved-commands` or `--commands-env-file`. Do not use `--env-file`: Node.js reserves that flag and would apply the file to the server process itself.
|
|
411
|
+
|
|
412
|
+
> **Note:** `execute_shell` is not a sandbox. An approved interpreter or code-running tool (`node`, `python`, `bash`, `npm`, `git`, `find -exec`, ...) can do anything your user account can do. Only approve commands you are comfortable letting the AI run.
|
|
413
|
+
|
|
414
|
+
#### Runtime Registration Policy
|
|
415
|
+
|
|
416
|
+
`--runtime-registration <confirm|allow|deny>` controls the `register_directory` tool:
|
|
417
|
+
|
|
418
|
+
- `confirm` (default) — you are asked to approve each new directory through your MCP client's confirmation prompt (MCP elicitation). If the client does not support confirmation prompts, registration is refused; add the folder with `--approved-folders` instead, or use `allow`.
|
|
419
|
+
- `allow` — directories are registered without a prompt (the behavior before 1.3.0).
|
|
420
|
+
- `deny` — runtime registration is disabled; only `--approved-folders` and MCP Roots grant access.
|
|
421
|
+
|
|
422
|
+
Filesystem roots (such as `C:\` or `/`) and your home directory itself can only be registered with `allow`.
|
|
423
|
+
|
|
402
424
|
#### Combined Configuration
|
|
403
425
|
|
|
404
426
|
All configuration options can be combined:
|
|
@@ -490,7 +512,7 @@ To access a specific directory, instruct the AI agent:
|
|
|
490
512
|
"Please register the directory C:\path\to\your\folder for access, then list its contents."
|
|
491
513
|
```
|
|
492
514
|
|
|
493
|
-
The AI will use the `register_directory` tool to gain access, then perform operations within that directory.
|
|
515
|
+
The AI will use the `register_directory` tool to gain access, then perform operations within that directory. With the default `--runtime-registration confirm` policy your MCP client asks you to approve the directory first.
|
|
494
516
|
|
|
495
517
|
## API
|
|
496
518
|
|
|
@@ -526,7 +548,7 @@ Attach images for AI vision analysis
|
|
|
526
548
|
|
|
527
549
|
- `path` (string | string[]): Path to image file, or array of paths to attach multiple images at once
|
|
528
550
|
|
|
529
|
-
**Output:** Image content in MCP format for vision model processing. Supports PNG, JPEG, GIF
|
|
551
|
+
**Output:** Image content in MCP format for vision model processing. Supports PNG, JPEG, GIF and WebP (the formats vision models accept). SVG files are returned as their markup text; BMP is not supported (convert to PNG)
|
|
530
552
|
|
|
531
553
|
##### read_multiple_files
|
|
532
554
|
|
|
@@ -708,7 +730,7 @@ Enable runtime access to new directories
|
|
|
708
730
|
|
|
709
731
|
- `path` (string): Directory path to register
|
|
710
732
|
|
|
711
|
-
**Output:** Success confirmation. Directory becomes accessible for operations
|
|
733
|
+
**Output:** Success confirmation. Directory becomes accessible for operations. The directory's real path (symlinks resolved) is registered. Subject to `--runtime-registration` (default: user confirmation via the MCP client).
|
|
712
734
|
|
|
713
735
|
##### list_allowed_directories
|
|
714
736
|
|
|
@@ -730,7 +752,7 @@ Find files using glob pattern matching
|
|
|
730
752
|
- `pattern` (string): Glob pattern (e.g., `**/*.ts`)
|
|
731
753
|
- `excludePatterns` (array, optional): Patterns to exclude
|
|
732
754
|
|
|
733
|
-
**Output:** List of matching file paths
|
|
755
|
+
**Output:** List of matching file paths. Patterns are limited to 1,000 characters.
|
|
734
756
|
|
|
735
757
|
##### grep_files
|
|
736
758
|
|
|
@@ -751,6 +773,8 @@ Search for text patterns within files
|
|
|
751
773
|
|
|
752
774
|
**Output:** Matching lines with context, file paths, or match counts
|
|
753
775
|
|
|
776
|
+
Regex matching runs in a worker thread with a time budget (5 s per file, 30 s per search), so a pathological pattern fails with a timeout error instead of freezing the server. Patterns are limited to 1,000 characters.
|
|
777
|
+
|
|
754
778
|
#### Shell Operations
|
|
755
779
|
|
|
756
780
|
##### execute_shell
|
|
@@ -763,6 +787,7 @@ Execute shell commands with security controls
|
|
|
763
787
|
- `description` (string, optional): Command purpose
|
|
764
788
|
- `workdir` (string, optional): Working directory (must be within allowed directories). If not provided, process.cwd() is used and validated
|
|
765
789
|
- `timeout` (number, optional): Timeout in milliseconds (default: 30000)
|
|
790
|
+
- `requiresApproval` (boolean, optional): Deprecated and ignored (kept for compatibility)
|
|
766
791
|
|
|
767
792
|
**Output:** Exit code, stdout, stderr, and execution metadata
|
|
768
793
|
|
|
@@ -770,8 +795,11 @@ Execute shell commands with security controls
|
|
|
770
795
|
|
|
771
796
|
- At least one approved directory must be configured before executing shell commands
|
|
772
797
|
- Working directory (whether explicit or default process.cwd()) is always validated against allowed directories
|
|
773
|
-
-
|
|
774
|
-
-
|
|
798
|
+
- Every command in a chain (`;`, `&&`, `||`, `|`) must be in `--approved-commands`
|
|
799
|
+
- File and directory operands (arguments, option values such as `--out=...`, and redirection targets) are validated against allowed directories after resolving symlinks and junctions; operands with variables that cannot be resolved are refused
|
|
800
|
+
- Commands must be a single line. Not allowed: newlines, command substitution, backticks, a lone `&`, `( )`/`{ }` grouping or script blocks, heredocs, and escaped quotes (`\"`, `\'`)
|
|
801
|
+
- Commands matching dangerous patterns are blocked unless the operator allowed them with `--allow-dangerous-commands`
|
|
802
|
+
- `execute_shell` is not a sandbox: approved interpreters and code-running tools can still do anything your account can do
|
|
775
803
|
|
|
776
804
|
### Multi-File Edit Examples
|
|
777
805
|
|
|
@@ -849,7 +877,7 @@ For detailed usage examples, see [Tool Usage Guide](docs/TOOL_USAGE_GUIDE.md)
|
|
|
849
877
|
|
|
850
878
|
## Security
|
|
851
879
|
|
|
852
|
-
This MCP server implements
|
|
880
|
+
This MCP server implements security controls to protect against common filesystem vulnerabilities, based on known CVE patterns. To report a vulnerability, please follow [SECURITY.md](SECURITY.md) (private reporting; do not open a public issue).
|
|
853
881
|
|
|
854
882
|
### Protected Against
|
|
855
883
|
|
|
@@ -863,17 +891,17 @@ This MCP server implements enterprise-grade security controls to protect against
|
|
|
863
891
|
#### Command Injection (CWE-78)
|
|
864
892
|
|
|
865
893
|
- **Protected Pattern**: CVE-2025-54795
|
|
866
|
-
- **Mitigation**:
|
|
867
|
-
- **Implementation**:
|
|
868
|
-
- **Example**:
|
|
894
|
+
- **Mitigation**: Commands are parsed with a conservative, quote-aware parser; only a small grammar that bash and PowerShell interpret the same way is accepted, and every command in a chain must be approved
|
|
895
|
+
- **Implementation**: Rejects newlines and control characters, command substitution, backticks, escaped quotes, a lone `&`, grouping/script blocks and heredocs; dangerous patterns are blocked unless the operator allows them (`--allow-dangerous-commands`)
|
|
896
|
+
- **Example**: `echo "\"; malicious_cmd; echo \""` and newline-separated commands are rejected
|
|
869
897
|
|
|
870
898
|
#### Shell Command Directory Bypass (CWE-22)
|
|
871
899
|
|
|
872
|
-
- **Protected Pattern**: Path restriction bypass via
|
|
873
|
-
- **Mitigation**:
|
|
874
|
-
- **Implementation**:
|
|
875
|
-
- **Example**: Blocks `type C:\Windows\System32\drivers\etc\hosts` and `cat /
|
|
876
|
-
- **Scope**:
|
|
900
|
+
- **Protected Pattern**: Path restriction bypass via paths in shell commands
|
|
901
|
+
- **Mitigation**: Every file operand (arguments, option values such as `--out=...`, redirection targets, `cd` targets) is validated against allowed directories after resolving symlinks and junctions
|
|
902
|
+
- **Implementation**: Operands are checked lexically, after `realpath`, and with physical resolution (symlinks followed before `..`, as a POSIX kernel does); relative operands are checked against every directory a `cd` could leave the command in; unresolvable variables and PowerShell provider paths (`env:`, `HKLM:`, ...) are refused
|
|
903
|
+
- **Example**: Blocks `cat /etc/passwd`, `type C:\Windows\System32\drivers\etc\hosts`, `echo x >C:\outside\file` and `cat link/secret` (where `link` points outside) when the targets are outside approved directories
|
|
904
|
+
- **Scope**: `execute_shell` is not a sandbox. An approved interpreter or code-running tool can still do anything your account can do, so approve commands sparingly
|
|
877
905
|
|
|
878
906
|
#### Symlink Attacks (CWE-59 / CWE-61)
|
|
879
907
|
|
|
@@ -899,16 +927,17 @@ This MCP server implements enterprise-grade security controls to protect against
|
|
|
899
927
|
|
|
900
928
|
#### Command Execution
|
|
901
929
|
|
|
902
|
-
- **Command
|
|
903
|
-
- **Pattern Detection**: Blocks dangerous patterns (destructive, privilege escalation, network execution)
|
|
904
|
-
- **
|
|
930
|
+
- **Command Allowlist**: Only commands listed in `--approved-commands` execute; everything else is blocked
|
|
931
|
+
- **Pattern Detection**: Blocks dangerous patterns (destructive, privilege escalation, network execution) unless the operator allows the command with `--allow-dangerous-commands`
|
|
932
|
+
- **Structural Parsing**: Rejects command substitution, backticks, newlines, grouping and other constructs that could hide commands from validation
|
|
905
933
|
- **Root Command Extraction**: Analyzes all commands in chained operations for approval
|
|
906
|
-
- **Path
|
|
934
|
+
- **Path Operand Validation**: Validates all file/directory operands, including redirection targets, against allowed directories with symlink resolution
|
|
907
935
|
|
|
908
936
|
#### Access Controls
|
|
909
937
|
|
|
910
938
|
- **Directory Whitelisting**: Operations restricted to explicitly approved directories
|
|
911
|
-
- **Runtime Registration**: Additional directories require
|
|
939
|
+
- **Runtime Registration**: Additional directories require registration via `register_directory`, which asks the user to confirm by default (`--runtime-registration`)
|
|
940
|
+
- **Resource Limits**: Full-file text reads are capped at 10 MB, `attach_image` at 10 MB per image and 20 MB per call, and Office/ODF documents are checked for zip bombs before parsing
|
|
912
941
|
- **Atomic Validation**: Paths validated before any file operations begin
|
|
913
942
|
- **Cross-Platform Safety**: Proper handling of Windows/Unix path differences and UNC paths
|
|
914
943
|
|
|
@@ -917,7 +946,7 @@ This MCP server implements enterprise-grade security controls to protect against
|
|
|
917
946
|
1. **Minimize Approved Directories**: Only approve directories that require AI access
|
|
918
947
|
2. **Use Directory Filtering**: Exclude sensitive folders (e.g., `.git`, `node_modules`) from listings
|
|
919
948
|
3. **Limit Tool Access**: Enable only necessary tools via `--enabled-tools` or `--enabled-tool-categories`
|
|
920
|
-
4. **Command Approval**:
|
|
949
|
+
4. **Command Approval**: Approve only the commands you need via `--approved-commands`; avoid shells and interpreters (`bash`, `sh`, `powershell`, `node`, `python`, `eval`), which can run arbitrary code
|
|
921
950
|
5. **Monitor Operations**: Review MCP client logs for unexpected access attempts
|
|
922
951
|
6. **Regular Updates**: Keep the server updated to receive security patches
|
|
923
952
|
|
|
@@ -928,13 +957,19 @@ This server has been comprehensively audited against known vulnerabilities and s
|
|
|
928
957
|
**CVE Protection Status:**
|
|
929
958
|
|
|
930
959
|
- ✅ CVE-2025-54794 (Path Restriction Bypass) - **FIXED**
|
|
931
|
-
- ✅ CVE-2025-54795 (Command Injection) - **PROTECTED**
|
|
932
|
-
- ✅ CVE-2025-53109 (Symlink Attacks) - **PROTECTED**
|
|
960
|
+
- ✅ CVE-2025-54795 (Command Injection) - **PROTECTED** (escaped-quote and newline variants closed in 1.3.0)
|
|
961
|
+
- ✅ CVE-2025-53109 (Symlink Attacks) - **PROTECTED** (`make_directory` and `execute_shell` gaps closed in 1.3.0)
|
|
933
962
|
- ✅ CVE-2025-53110 (Directory Containment Bypass) - **PROTECTED**
|
|
934
|
-
- ✅ Shell Execution Directory Bypass - **FIXED**
|
|
963
|
+
- ✅ Shell Execution Directory Bypass - **FIXED** in 1.3.0. The November 2024 fix was incomplete: slash-prefixed paths, attached redirections and symlinked paths could still escape
|
|
935
964
|
|
|
936
965
|
**Latest Security Audits:**
|
|
937
966
|
|
|
967
|
+
- 📋 Security hardening release 1.3.0 - October 2026
|
|
968
|
+
- **Scope**: Independent audit of GitHub issue #3 plus a Snyk Open Source / Snyk Code / container scan and manual review
|
|
969
|
+
- **Fixed**: newline command chaining past the allowlist, shell path validation gaps (slash paths, redirections, symlinks), self-approved dangerous commands, `make_directory` symlink escape, AI-initiated directory registration without consent, `.env` loading from the working directory, PDF image handling that could crash the server or fetch remote URLs, unbounded regex/read/decompression resource use, symlink dereferencing in directory copies
|
|
970
|
+
- **Dependencies**: refreshed with a 14-day release quarantine; all packages pass `npm audit signatures`
|
|
971
|
+
- See [CHANGELOG.md](CHANGELOG.md) for details
|
|
972
|
+
|
|
938
973
|
- 📋 [Snyk Vulnerability Audit Report - November 2025](docs/SNYK_VULNERABILITY_AUDIT_2025.md)
|
|
939
974
|
- **Status**: 5/6 Snyk findings validated as false positives, 1 finding fixed
|
|
940
975
|
- **Risk Level**: LOW - Comprehensive path traversal protection verified
|
|
@@ -945,10 +980,10 @@ This server has been comprehensively audited against known vulnerabilities and s
|
|
|
945
980
|
- **Focus**: CVE-2025-54794/54795 pattern analysis and mitigation strategies
|
|
946
981
|
- **Date**: November 4, 2025 (Manual CVE Research)
|
|
947
982
|
- 📋 [Shell Command Directory Bypass Audit - November 2025](docs/SHELL_COMMAND_AUDIT_2025-11-04.md)
|
|
948
|
-
- **Status**:
|
|
983
|
+
- **Status**: Partially fixed November 2024; completed in 1.3.0 (October 2026)
|
|
949
984
|
- **Issue**: Shell commands previously could access files outside approved directories via absolute paths
|
|
950
985
|
- **Severity**: HIGH (CVSS ~7.5) - Path traversal via command arguments
|
|
951
|
-
- **Fix Status**: ✅ FIXED
|
|
986
|
+
- **Fix Status**: ✅ FIXED in 1.3.0 - symlink-aware validation of all operands, including redirections
|
|
952
987
|
- **Test Coverage**: 419 lines of comprehensive tests, all passing
|
|
953
988
|
- 📋 [Security Test Coverage Summary](docs/SECURITY_TEST_SUMMARY.md)
|
|
954
989
|
- **Test Suite**: 2000+ lines of security-focused tests in `src/tests/`
|
|
@@ -999,7 +1034,7 @@ This server has been comprehensively audited against known vulnerabilities and s
|
|
|
999
1034
|
**Attach Image Tool** (`attach_image`):
|
|
1000
1035
|
|
|
1001
1036
|
- Attaches images for AI vision analysis (requires vision-capable MCP client)
|
|
1002
|
-
- **Supported formats**: PNG, JPEG, GIF, WebP
|
|
1037
|
+
- **Supported formats**: PNG, JPEG, GIF, WebP (SVG is returned as markup text; BMP is not supported)
|
|
1003
1038
|
- **Batch support**: Can attach single image or multiple images in one call
|
|
1004
1039
|
- Images are presented to the AI as if uploaded directly by the user
|
|
1005
1040
|
- Enables visual analysis: reading text in images, analyzing diagrams, describing scenes
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,22 @@ if (isMCP) {
|
|
|
18
18
|
// as the MCP SDK uses those for JSON-RPC protocol communication
|
|
19
19
|
}
|
|
20
20
|
import { runServer } from "./server/index.js";
|
|
21
|
+
// Defense in depth (VFO-13): a promise rejection that a third-party library
|
|
22
|
+
// fails to handle must not terminate the stdio server (Node's default for
|
|
23
|
+
// unhandled rejections is to crash). Log one line to STDERR - console.* is
|
|
24
|
+
// a no-op in MCP mode and stdout carries the JSON-RPC stream - and keep
|
|
25
|
+
// running. uncaughtException keeps Node's default (fatal) behavior.
|
|
26
|
+
process.on("unhandledRejection", (reason) => {
|
|
27
|
+
try {
|
|
28
|
+
const detail = reason instanceof Error
|
|
29
|
+
? `${reason.name}: ${reason.message}`
|
|
30
|
+
: String(reason);
|
|
31
|
+
process.stderr.write(`[vulcan-file-ops] unhandledRejection (ignored): ${detail.replace(/\s+/g, " ").slice(0, 2000)}\n`);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Never let diagnostics throw.
|
|
35
|
+
}
|
|
36
|
+
});
|
|
21
37
|
// Run the server and handle any fatal errors
|
|
22
38
|
runServer().catch((error) => {
|
|
23
39
|
// Only show errors when not running under MCP (to avoid protocol corruption)
|