@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 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.2.13] - 2026-04-23
13
-
14
- ### Fixed
15
-
16
- - Resolved the remaining MCP client tool discovery failure introduced after the SDK upgrade.
17
- - Tool schemas exposed through `tools/list` are now normalized for stricter MCP clients.
18
- - Removed client-hostile schema constructs from published tool input schemas, including `$schema`, `$ref`, `anyOf`, `oneOf`, and `allOf`.
19
- - Replaced the highest-risk wire schemas with explicit compatibility-first object schemas for `attach_image` and `make_directory`.
20
- - Preserved runtime backward compatibility while tightening the schemas advertised to clients.
21
- - Fixed residual MCP mode detection drift so stdio mode is triggered reliably in local and `npx` execution paths.
22
- - Added `zod` as a direct runtime dependency to avoid install-time fragility in clean environments and `npx` usage.
23
- - Synchronized package metadata so published registry metadata and local package metadata report the same release version.
24
- - Re-enabled and stabilized the previously skipped PDF-related Jest coverage.
25
- - Enabled the 10 skipped document tests for PDF read, HTML-to-PDF write, mixed document batches, and PDF round-trip paths.
26
- - Updated Jest PDF mocks so document tests exercise meaningful content paths instead of placeholder-only buffers.
27
- - Suppressed expected negative-path test diagnostics inside tests so successful runs no longer look like runtime failures.
28
- - Added a regression test to prevent incompatible tool schema shapes from reappearing in future releases.
29
-
30
- ### Changed
31
-
32
- - Documented the Codex-specific MCP configuration format in the README alongside the JSON-based client examples.
33
- - Updated `npx` examples to use `-y` explicitly for non-interactive client execution.
34
-
35
- ## [1.2.12] - 2026-02-21
36
-
37
- ### Fixed
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 14 or higher) must be installed on your system. This provides npm and npx, which are required to run this package.
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, WebP, BMP, SVG
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
- - All file/directory paths in command arguments are automatically extracted and validated against allowed directories
774
- - Commands referencing paths outside approved directories are blocked, preventing directory restriction bypasses
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 enterprise-grade security controls to protect against common filesystem vulnerabilities. All security measures are based on industry best practices and address known CVE patterns.
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**: Multi-layer validation including command substitution detection, root command extraction, and dangerous pattern matching
867
- - **Implementation**: Blocks `$()`, `` ` ` ``, `>()`, `<()` patterns; validates all commands in chains; requires approval for dangerous operations
868
- - **Example**: Prevents `echo "; malicious_cmd; echo"` injection attempts
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 absolute paths in shell commands
873
- - **Mitigation**: Path extraction and validation for all file/directory paths embedded in command arguments
874
- - **Implementation**: Extracts paths from command strings (handles Windows/Unix paths, quotes, relative paths, environment variables), validates each path against allowed directories before execution
875
- - **Example**: Blocks `type C:\Windows\System32\drivers\etc\hosts` and `cat /etc/passwd` when these paths are outside approved directories
876
- - **Scope**: Applies to all shell commands executed via `execute_shell` tool - paths in arguments are validated just like filesystem operations
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 Whitelisting**: Only pre-approved commands execute without confirmation
903
- - **Pattern Detection**: Blocks dangerous patterns (destructive, privilege escalation, network execution)
904
- - **Command Substitution Blocking**: Prevents `$()`, backticks, process substitution
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 Argument Validation**: Extracts and validates all file/directory paths in command arguments against allowed directories (prevents bypass via absolute paths in commands)
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 explicit registration via `register_directory` tool
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**: Pre-approve safe commands via `--approved-commands`; require approval for others
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** (November 2024)
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**: ✅ Fixed November 2024 (Retrospective documentation)
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 - Path extraction and validation implemented
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, BMP, SVG
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)