ssh-agent-workspace 1.0.5 → 1.0.6

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.
Files changed (158) hide show
  1. package/README.md +471 -471
  2. package/dist/__tests__/SSHManager.test.js +59 -37
  3. package/dist/__tests__/SSHManager.test.js.map +1 -1
  4. package/dist/__tests__/SessionManager.test.js +53 -51
  5. package/dist/__tests__/SessionManager.test.js.map +1 -1
  6. package/dist/__tests__/StorageManager.test.js +74 -74
  7. package/dist/__tests__/StorageManager.test.js.map +1 -1
  8. package/dist/__tests__/ansi.test.js +33 -33
  9. package/dist/__tests__/ansi.test.js.map +1 -1
  10. package/dist/__tests__/security.test.js +54 -54
  11. package/dist/__tests__/security.test.js.map +1 -1
  12. package/dist/__tests__/validation.test.js +17 -17
  13. package/dist/__tests__/validation.test.js.map +1 -1
  14. package/dist/core/HostSecurityManager.d.ts.map +1 -1
  15. package/dist/core/HostSecurityManager.js +9 -9
  16. package/dist/core/HostSecurityManager.js.map +1 -1
  17. package/dist/core/SSHManager.d.ts +1 -1
  18. package/dist/core/SSHManager.d.ts.map +1 -1
  19. package/dist/core/SSHManager.js +31 -31
  20. package/dist/core/SSHManager.js.map +1 -1
  21. package/dist/core/SessionManager.d.ts +4 -4
  22. package/dist/core/SessionManager.d.ts.map +1 -1
  23. package/dist/core/SessionManager.js +14 -14
  24. package/dist/core/SessionManager.js.map +1 -1
  25. package/dist/core/StorageManager.d.ts.map +1 -1
  26. package/dist/core/StorageManager.js +11 -11
  27. package/dist/core/StorageManager.js.map +1 -1
  28. package/dist/core/TmuxManager.d.ts +3 -3
  29. package/dist/core/TmuxManager.d.ts.map +1 -1
  30. package/dist/core/TmuxManager.js +16 -16
  31. package/dist/core/TmuxManager.js.map +1 -1
  32. package/dist/core/ToolConfigManager.d.ts.map +1 -1
  33. package/dist/core/ToolConfigManager.js +10 -10
  34. package/dist/core/ToolConfigManager.js.map +1 -1
  35. package/dist/index.js +35 -35
  36. package/dist/index.js.map +1 -1
  37. package/dist/server.d.ts +4 -4
  38. package/dist/server.d.ts.map +1 -1
  39. package/dist/server.js +41 -41
  40. package/dist/server.js.map +1 -1
  41. package/dist/tools/backup.d.ts +3 -3
  42. package/dist/tools/backup.d.ts.map +1 -1
  43. package/dist/tools/backup.js +86 -49
  44. package/dist/tools/backup.js.map +1 -1
  45. package/dist/tools/connect.d.ts +4 -4
  46. package/dist/tools/connect.d.ts.map +1 -1
  47. package/dist/tools/connect.js +54 -42
  48. package/dist/tools/connect.js.map +1 -1
  49. package/dist/tools/connection_status.d.ts +4 -4
  50. package/dist/tools/connection_status.d.ts.map +1 -1
  51. package/dist/tools/connection_status.js +23 -16
  52. package/dist/tools/connection_status.js.map +1 -1
  53. package/dist/tools/db_query.d.ts +3 -3
  54. package/dist/tools/db_query.d.ts.map +1 -1
  55. package/dist/tools/db_query.js +130 -78
  56. package/dist/tools/db_query.js.map +1 -1
  57. package/dist/tools/deploy.d.ts +3 -3
  58. package/dist/tools/deploy.d.ts.map +1 -1
  59. package/dist/tools/deploy.js +103 -53
  60. package/dist/tools/deploy.js.map +1 -1
  61. package/dist/tools/disconnect.d.ts +3 -3
  62. package/dist/tools/disconnect.d.ts.map +1 -1
  63. package/dist/tools/disconnect.js +16 -19
  64. package/dist/tools/disconnect.js.map +1 -1
  65. package/dist/tools/exec.d.ts +3 -3
  66. package/dist/tools/exec.d.ts.map +1 -1
  67. package/dist/tools/exec.js +40 -45
  68. package/dist/tools/exec.js.map +1 -1
  69. package/dist/tools/group_exec.d.ts +3 -3
  70. package/dist/tools/group_exec.d.ts.map +1 -1
  71. package/dist/tools/group_exec.js +60 -44
  72. package/dist/tools/group_exec.js.map +1 -1
  73. package/dist/tools/health_check.d.ts +3 -3
  74. package/dist/tools/health_check.d.ts.map +1 -1
  75. package/dist/tools/health_check.js +27 -23
  76. package/dist/tools/health_check.js.map +1 -1
  77. package/dist/tools/host_security.d.ts +1 -1
  78. package/dist/tools/host_security.d.ts.map +1 -1
  79. package/dist/tools/host_security.js +30 -36
  80. package/dist/tools/host_security.js.map +1 -1
  81. package/dist/tools/index.d.ts +23 -23
  82. package/dist/tools/index.d.ts.map +1 -1
  83. package/dist/tools/index.js +23 -23
  84. package/dist/tools/index.js.map +1 -1
  85. package/dist/tools/interrupt.d.ts +3 -3
  86. package/dist/tools/interrupt.d.ts.map +1 -1
  87. package/dist/tools/interrupt.js +18 -18
  88. package/dist/tools/interrupt.js.map +1 -1
  89. package/dist/tools/list_hosts.d.ts.map +1 -1
  90. package/dist/tools/list_hosts.js +5 -7
  91. package/dist/tools/list_hosts.js.map +1 -1
  92. package/dist/tools/list_sessions.d.ts +1 -1
  93. package/dist/tools/list_sessions.js +4 -4
  94. package/dist/tools/list_sessions.js.map +1 -1
  95. package/dist/tools/read_output.d.ts +3 -3
  96. package/dist/tools/read_output.d.ts.map +1 -1
  97. package/dist/tools/read_output.js +17 -17
  98. package/dist/tools/read_output.js.map +1 -1
  99. package/dist/tools/reconnect_to_tmux.d.ts +4 -4
  100. package/dist/tools/reconnect_to_tmux.d.ts.map +1 -1
  101. package/dist/tools/reconnect_to_tmux.js +44 -37
  102. package/dist/tools/reconnect_to_tmux.js.map +1 -1
  103. package/dist/tools/send_input.d.ts +3 -3
  104. package/dist/tools/send_input.d.ts.map +1 -1
  105. package/dist/tools/send_input.js +18 -18
  106. package/dist/tools/send_input.js.map +1 -1
  107. package/dist/tools/sftp_download.d.ts +3 -3
  108. package/dist/tools/sftp_download.d.ts.map +1 -1
  109. package/dist/tools/sftp_download.js +41 -27
  110. package/dist/tools/sftp_download.js.map +1 -1
  111. package/dist/tools/sftp_list.d.ts +3 -3
  112. package/dist/tools/sftp_list.d.ts.map +1 -1
  113. package/dist/tools/sftp_list.js +35 -26
  114. package/dist/tools/sftp_list.js.map +1 -1
  115. package/dist/tools/sftp_upload.d.ts +3 -3
  116. package/dist/tools/sftp_upload.d.ts.map +1 -1
  117. package/dist/tools/sftp_upload.js +43 -29
  118. package/dist/tools/sftp_upload.js.map +1 -1
  119. package/dist/tools/ssh_tunnel.d.ts +3 -3
  120. package/dist/tools/ssh_tunnel.d.ts.map +1 -1
  121. package/dist/tools/ssh_tunnel.js +130 -76
  122. package/dist/tools/ssh_tunnel.js.map +1 -1
  123. package/dist/tools/sync.d.ts +3 -3
  124. package/dist/tools/sync.d.ts.map +1 -1
  125. package/dist/tools/sync.js +103 -61
  126. package/dist/tools/sync.js.map +1 -1
  127. package/dist/tools/tail_log.d.ts +3 -3
  128. package/dist/tools/tail_log.d.ts.map +1 -1
  129. package/dist/tools/tail_log.js +38 -26
  130. package/dist/tools/tail_log.js.map +1 -1
  131. package/dist/tools/tools_config.d.ts +1 -1
  132. package/dist/tools/tools_config.d.ts.map +1 -1
  133. package/dist/tools/tools_config.js +25 -35
  134. package/dist/tools/tools_config.js.map +1 -1
  135. package/dist/types/index.d.ts +1 -1
  136. package/dist/types/index.d.ts.map +1 -1
  137. package/dist/utils/ansi.js +1 -1
  138. package/dist/utils/ansi.js.map +1 -1
  139. package/dist/utils/logger.d.ts +1 -1
  140. package/dist/utils/logger.js +2 -2
  141. package/dist/utils/logger.js.map +1 -1
  142. package/dist/utils/security.d.ts +1 -1
  143. package/dist/utils/security.js +3 -3
  144. package/dist/utils/security.js.map +1 -1
  145. package/dist/utils/ssh.d.ts +2 -2
  146. package/dist/utils/ssh.js +5 -5
  147. package/dist/utils/ssh.js.map +1 -1
  148. package/dist/utils/sshConfig.d.ts +1 -1
  149. package/dist/utils/sshConfig.d.ts.map +1 -1
  150. package/dist/utils/sshConfig.js +21 -21
  151. package/dist/utils/sshConfig.js.map +1 -1
  152. package/dist/utils/validation.d.ts +1 -1
  153. package/dist/utils/validation.js +3 -3
  154. package/dist/utils/validation.js.map +1 -1
  155. package/docs/SECURITY.md +213 -213
  156. package/docs/TOOLS.md +425 -425
  157. package/package.json +59 -48
  158. package/vitest.config.ts +10 -10
package/docs/SECURITY.md CHANGED
@@ -1,213 +1,213 @@
1
- # Security Model
2
-
3
- `ssh-agent-workspace` implements a **three-layer defense** architecture. Each layer adds granularity without breaking the layers above it.
4
-
5
- ---
6
-
7
- ## Layer 1: Global (Environment Variables)
8
-
9
- Set at server startup via environment variables. Applies to **all hosts**.
10
-
11
- | Variable | Effect |
12
- |---|---|
13
- | `MCP_SSH_READONLY=true` | Blocks all write operations globally |
14
- | `MCP_SSH_ALLOWED_HOSTS=prod,staging` | Only these host aliases can be connected to |
15
- | `MCP_SSH_DENYLIST_COMMANDS=rm,shutdown,dd` | Blocks commands containing these substrings (case-insensitive) |
16
-
17
- ### Read-Only Mode: Blocked Tools
18
-
19
- When `MCP_SSH_READONLY=true`, the following tools return errors:
20
-
21
- - `exec` — Command execution disabled
22
- - `send_input` — Input sending disabled
23
- - `sftp_upload` — SFTP upload disabled
24
- - `sftp_download` — SFTP download disabled
25
- - `deploy` — Deploy disabled
26
- - `backup` — Backup disabled
27
- - `sync` — Sync disabled
28
- - `group_exec` — Group exec disabled
29
- - `db_query` — DB query disabled
30
- - `ssh_tunnel_open` — Tunnels disabled
31
-
32
- ### Tools Always Allowed (Read Operations)
33
-
34
- `list_hosts`, `connect`, `reconnect_to_tmux`, `read_output`, `interrupt`, `disconnect`, `list_sessions`, `sftp_list`, `connection_status`, `health_check`, `tail_log`, `ssh_tunnel_close`, `ssh_tunnel_list`, `tools_config`, `host_security`
35
-
36
- ---
37
-
38
- ## Layer 2: Per-Host (Host Security)
39
-
40
- Managed via the `host_security` tool and persisted in `~/.dynamic-ssh-mcp/host_security.json`.
41
-
42
- Per-host settings **override** global settings for that specific host only.
43
-
44
- ### Configuration
45
-
46
- ```json
47
- {
48
- "prod": {
49
- "readonly": true,
50
- "deny_commands": ["shutdown", "reboot", "dd if="]
51
- },
52
- "staging": {
53
- "deny_commands": ["rm -rf /", "shutdown"]
54
- },
55
- "dev": {
56
- "allow_commands": [
57
- "ls", "cat", "echo", "ps aux",
58
- "docker ps", "docker logs",
59
- "git status", "git diff", "git log",
60
- "npm run", "node"
61
- ]
62
- }
63
- }
64
- ```
65
-
66
- ### Rules
67
-
68
- | Field | Behavior |
69
- |---|---|
70
- | `readonly: true` | Host locked to read-only, regardless of global `MCP_SSH_READONLY` |
71
- | `readonly: false` | Host explicitly writeable (does **not** override global readonly) |
72
- | `allow_commands: [...]` | If set, **only** commands matching these patterns are allowed (case-insensitive substring match) |
73
- | `deny_commands: [...]` | Commands matching these patterns are blocked (case-insensitive substring match) |
74
-
75
- **Precedence:** `deny_commands` wins over `allow_commands`. If a command matches both, it's blocked.
76
-
77
- ### Enforcement Points
78
-
79
- Per-host read-only is checked **after session lookup** in every write tool:
80
-
81
- ```
82
- exec, send_input, sftp_upload, sftp_download,
83
- deploy, backup, sync, group_exec, db_query, ssh_tunnel_open
84
- ```
85
-
86
- If a session's host has `readonly: true`, the tool returns:
87
-
88
- ```
89
- Error: Host 'prod' is in read-only mode. <operation> is disabled.
90
- ```
91
-
92
- ### Per-Host Command Filtering
93
-
94
- `isHostCommandDenied(host, command)` checks:
95
- 1. Global `MCP_SSH_DENYLIST_COMMANDS`
96
- 2. Per-host `allow_commands` (if set, command must match at least one)
97
- 3. Per-host `deny_commands`
98
-
99
- Per-host command filtering is available to all tools via the shared security utility.
100
-
101
- ---
102
-
103
- ## Layer 3: Per-Operation (Tool-Level)
104
-
105
- ### Path Sanitization
106
-
107
- All file paths used in SFTP, backup, deploy, sync, and tail_log are validated:
108
-
109
- - Rejects paths containing `;`, `&&`, `||`, `|`
110
- - Rejects paths starting with `-` (option injection)
111
- - Paths are shell-escaped before passing to exec commands
112
-
113
- ### Command Denylist (Global)
114
-
115
- Commands containing these substrings are blocked (case-insensitive):
116
-
117
- ```
118
- rm -rf, shutdown, reboot, dd, mkfs, fdisk, :(){ :|:& };: (fork bomb)
119
- ```
120
-
121
- Configured via `MCP_SSH_DENYLIST_COMMANDS` — comma-separated:
122
- ```
123
- MCP_SSH_DENYLIST_COMMANDS=rm -rf,shutdown,dd if=,mkfs,chmod 777
124
- ```
125
-
126
- ### SQL/MongoDB Query Security
127
-
128
- The `db_query` tool enforces read-only queries:
129
-
130
- **SQL (MySQL/PostgreSQL):**
131
- - Blocked keywords: `DROP`, `DELETE`, `INSERT`, `UPDATE`, `ALTER`, `CREATE`, `TRUNCATE`, `GRANT`, `REVOKE`, `RENAME`, `REPLACE`, `MERGE`, `UPSERT`, `LOAD`
132
- - Only allowed prefixes: `SELECT`, `SHOW`, `EXPLAIN`, `DESCRIBE`, `WITH`
133
-
134
- **MongoDB:**
135
- - Blocked methods: `deleteOne`, `deleteMany`, `insertOne`, `insertMany`, `updateOne`, `updateMany`, `replaceOne`, `drop`, `dropDatabase`, `createIndex`, `createCollection`
136
-
137
- ### Shell Injection Protection
138
-
139
- All arguments passed to tmux commands are:
140
- 1. Validated via Zod schemas before use
141
- 2. Passed through `sanitizeTmuxSessionName()` for session names
142
- 3. Base64-encoded buffer pipeline for command input (not raw string interpolation)
143
- 4. Escaped via `escapeShellArg()` for exec channel commands
144
-
145
- ### SFTP Security
146
-
147
- - `sftp_upload` / `sftp_download`: paths validated before transfer
148
- - `sftp_list`: read-only, allowed even in read-only mode
149
- - `deploy`: validates all paths in the files array, plus exec commands for chmod/chown/restart
150
-
151
- ---
152
-
153
- ## Proxy Jump / Bastion Security
154
-
155
- - Both the bastion host AND the target host must pass `isHostAllowed()` checks
156
- - Bastion resolved from `~/.ssh/config` (`ProxyJump` directive) or explicit `proxy_jump` parameter
157
- - Each hop uses its own SSH key from the corresponding `Host` config block
158
- - The bastion connection is established first, then `forwardOut` is used to reach the target
159
-
160
- ---
161
-
162
- ## Session Isolation
163
-
164
- - Each session = **one dedicated SSH connection** + **one dedicated tmux session**
165
- - Sessions cannot see each other's tmux panes
166
- - Session metadata stored locally at `~/.dynamic-ssh-mcp/sessions.json`
167
- - No passwords are stored — auth is key-based via `~/.ssh/config`
168
- - SSH key passphrases not stored; use `ssh-agent` for passphrase-protected keys
169
-
170
- ---
171
-
172
- ## Hardening Recommendations
173
-
174
- ### Production
175
-
176
- ```bash
177
- # Global read-only on production
178
- export MCP_SSH_READONLY=true
179
-
180
- # Only prod and staging hosts
181
- export MCP_SSH_ALLOWED_HOSTS=prod,staging
182
-
183
- # Block dangerous operations
184
- export MCP_SSH_DENYLIST_COMMANDS="rm -rf,shutdown,reboot,dd if=,chmod 777,fdisk,mkfs"
185
-
186
- # Only restore known sessions (don't discover new ones)
187
- export MCP_SSH_RESTORE_SESSIONS=true
188
- ```
189
-
190
- ### Per-Host Fine-Tuning (via tools_config / host_security)
191
-
192
- ```
193
- # Lock production to read-only
194
- host_security set host=prod readonly=true
195
-
196
- # Limit dev commands
197
- host_security set host=dev deny_commands=["rm -rf", "shutdown", "reboot"]
198
-
199
- # Disable unused tools to reduce attack surface
200
- tools_config disable db_query
201
- tools_config disable backup
202
- tools_config disable sync
203
- tools_config disable deploy
204
- ```
205
-
206
- ---
207
-
208
- ## Audit & Monitoring
209
-
210
- - All tool invocations logged to stderr via pino (configurable `LOG_LEVEL`)
211
- - Session create/remove/restore events logged with session IDs
212
- - Failed connections, auth rejections, and denied commands logged at `warn` level
213
- - Log format: structured JSON with timestamps
1
+ # Security Model
2
+
3
+ `ssh-agent-workspace` implements a **three-layer defense** architecture. Each layer adds granularity without breaking the layers above it.
4
+
5
+ ---
6
+
7
+ ## Layer 1: Global (Environment Variables)
8
+
9
+ Set at server startup via environment variables. Applies to **all hosts**.
10
+
11
+ | Variable | Effect |
12
+ |---|---|
13
+ | `MCP_SSH_READONLY=true` | Blocks all write operations globally |
14
+ | `MCP_SSH_ALLOWED_HOSTS=prod,staging` | Only these host aliases can be connected to |
15
+ | `MCP_SSH_DENYLIST_COMMANDS=rm,shutdown,dd` | Blocks commands containing these substrings (case-insensitive) |
16
+
17
+ ### Read-Only Mode: Blocked Tools
18
+
19
+ When `MCP_SSH_READONLY=true`, the following tools return errors:
20
+
21
+ - `exec` — Command execution disabled
22
+ - `send_input` — Input sending disabled
23
+ - `sftp_upload` — SFTP upload disabled
24
+ - `sftp_download` — SFTP download disabled
25
+ - `deploy` — Deploy disabled
26
+ - `backup` — Backup disabled
27
+ - `sync` — Sync disabled
28
+ - `group_exec` — Group exec disabled
29
+ - `db_query` — DB query disabled
30
+ - `ssh_tunnel_open` — Tunnels disabled
31
+
32
+ ### Tools Always Allowed (Read Operations)
33
+
34
+ `list_hosts`, `connect`, `reconnect_to_tmux`, `read_output`, `interrupt`, `disconnect`, `list_sessions`, `sftp_list`, `connection_status`, `health_check`, `tail_log`, `ssh_tunnel_close`, `ssh_tunnel_list`, `tools_config`, `host_security`
35
+
36
+ ---
37
+
38
+ ## Layer 2: Per-Host (Host Security)
39
+
40
+ Managed via the `host_security` tool and persisted in `~/.dynamic-ssh-mcp/host_security.json`.
41
+
42
+ Per-host settings **override** global settings for that specific host only.
43
+
44
+ ### Configuration
45
+
46
+ ```json
47
+ {
48
+ "prod": {
49
+ "readonly": true,
50
+ "deny_commands": ["shutdown", "reboot", "dd if="]
51
+ },
52
+ "staging": {
53
+ "deny_commands": ["rm -rf /", "shutdown"]
54
+ },
55
+ "dev": {
56
+ "allow_commands": [
57
+ "ls", "cat", "echo", "ps aux",
58
+ "docker ps", "docker logs",
59
+ "git status", "git diff", "git log",
60
+ "npm run", "node"
61
+ ]
62
+ }
63
+ }
64
+ ```
65
+
66
+ ### Rules
67
+
68
+ | Field | Behavior |
69
+ |---|---|
70
+ | `readonly: true` | Host locked to read-only, regardless of global `MCP_SSH_READONLY` |
71
+ | `readonly: false` | Host explicitly writeable (does **not** override global readonly) |
72
+ | `allow_commands: [...]` | If set, **only** commands matching these patterns are allowed (case-insensitive substring match) |
73
+ | `deny_commands: [...]` | Commands matching these patterns are blocked (case-insensitive substring match) |
74
+
75
+ **Precedence:** `deny_commands` wins over `allow_commands`. If a command matches both, it's blocked.
76
+
77
+ ### Enforcement Points
78
+
79
+ Per-host read-only is checked **after session lookup** in every write tool:
80
+
81
+ ```
82
+ exec, send_input, sftp_upload, sftp_download,
83
+ deploy, backup, sync, group_exec, db_query, ssh_tunnel_open
84
+ ```
85
+
86
+ If a session's host has `readonly: true`, the tool returns:
87
+
88
+ ```
89
+ Error: Host 'prod' is in read-only mode. <operation> is disabled.
90
+ ```
91
+
92
+ ### Per-Host Command Filtering
93
+
94
+ `isHostCommandDenied(host, command)` checks:
95
+ 1. Global `MCP_SSH_DENYLIST_COMMANDS`
96
+ 2. Per-host `allow_commands` (if set, command must match at least one)
97
+ 3. Per-host `deny_commands`
98
+
99
+ Per-host command filtering is available to all tools via the shared security utility.
100
+
101
+ ---
102
+
103
+ ## Layer 3: Per-Operation (Tool-Level)
104
+
105
+ ### Path Sanitization
106
+
107
+ All file paths used in SFTP, backup, deploy, sync, and tail_log are validated:
108
+
109
+ - Rejects paths containing `;`, `&&`, `||`, `|`
110
+ - Rejects paths starting with `-` (option injection)
111
+ - Paths are shell-escaped before passing to exec commands
112
+
113
+ ### Command Denylist (Global)
114
+
115
+ Commands containing these substrings are blocked (case-insensitive):
116
+
117
+ ```
118
+ rm -rf, shutdown, reboot, dd, mkfs, fdisk, :(){ :|:& };: (fork bomb)
119
+ ```
120
+
121
+ Configured via `MCP_SSH_DENYLIST_COMMANDS` — comma-separated:
122
+ ```
123
+ MCP_SSH_DENYLIST_COMMANDS=rm -rf,shutdown,dd if=,mkfs,chmod 777
124
+ ```
125
+
126
+ ### SQL/MongoDB Query Security
127
+
128
+ The `db_query` tool enforces read-only queries:
129
+
130
+ **SQL (MySQL/PostgreSQL):**
131
+ - Blocked keywords: `DROP`, `DELETE`, `INSERT`, `UPDATE`, `ALTER`, `CREATE`, `TRUNCATE`, `GRANT`, `REVOKE`, `RENAME`, `REPLACE`, `MERGE`, `UPSERT`, `LOAD`
132
+ - Only allowed prefixes: `SELECT`, `SHOW`, `EXPLAIN`, `DESCRIBE`, `WITH`
133
+
134
+ **MongoDB:**
135
+ - Blocked methods: `deleteOne`, `deleteMany`, `insertOne`, `insertMany`, `updateOne`, `updateMany`, `replaceOne`, `drop`, `dropDatabase`, `createIndex`, `createCollection`
136
+
137
+ ### Shell Injection Protection
138
+
139
+ All arguments passed to tmux commands are:
140
+ 1. Validated via Zod schemas before use
141
+ 2. Passed through `sanitizeTmuxSessionName()` for session names
142
+ 3. Base64-encoded buffer pipeline for command input (not raw string interpolation)
143
+ 4. Escaped via `escapeShellArg()` for exec channel commands
144
+
145
+ ### SFTP Security
146
+
147
+ - `sftp_upload` / `sftp_download`: paths validated before transfer
148
+ - `sftp_list`: read-only, allowed even in read-only mode
149
+ - `deploy`: validates all paths in the files array, plus exec commands for chmod/chown/restart
150
+
151
+ ---
152
+
153
+ ## Proxy Jump / Bastion Security
154
+
155
+ - Both the bastion host AND the target host must pass `isHostAllowed()` checks
156
+ - Bastion resolved from `~/.ssh/config` (`ProxyJump` directive) or explicit `proxy_jump` parameter
157
+ - Each hop uses its own SSH key from the corresponding `Host` config block
158
+ - The bastion connection is established first, then `forwardOut` is used to reach the target
159
+
160
+ ---
161
+
162
+ ## Session Isolation
163
+
164
+ - Each session = **one dedicated SSH connection** + **one dedicated tmux session**
165
+ - Sessions cannot see each other's tmux panes
166
+ - Session metadata stored locally at `~/.dynamic-ssh-mcp/sessions.json`
167
+ - No passwords are stored — auth is key-based via `~/.ssh/config`
168
+ - SSH key passphrases not stored; use `ssh-agent` for passphrase-protected keys
169
+
170
+ ---
171
+
172
+ ## Hardening Recommendations
173
+
174
+ ### Production
175
+
176
+ ```bash
177
+ # Global read-only on production
178
+ export MCP_SSH_READONLY=true
179
+
180
+ # Only prod and staging hosts
181
+ export MCP_SSH_ALLOWED_HOSTS=prod,staging
182
+
183
+ # Block dangerous operations
184
+ export MCP_SSH_DENYLIST_COMMANDS="rm -rf,shutdown,reboot,dd if=,chmod 777,fdisk,mkfs"
185
+
186
+ # Only restore known sessions (don't discover new ones)
187
+ export MCP_SSH_RESTORE_SESSIONS=true
188
+ ```
189
+
190
+ ### Per-Host Fine-Tuning (via tools_config / host_security)
191
+
192
+ ```
193
+ # Lock production to read-only
194
+ host_security set host=prod readonly=true
195
+
196
+ # Limit dev commands
197
+ host_security set host=dev deny_commands=["rm -rf", "shutdown", "reboot"]
198
+
199
+ # Disable unused tools to reduce attack surface
200
+ tools_config disable db_query
201
+ tools_config disable backup
202
+ tools_config disable sync
203
+ tools_config disable deploy
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Audit & Monitoring
209
+
210
+ - All tool invocations logged to stderr via pino (configurable `LOG_LEVEL`)
211
+ - Session create/remove/restore events logged with session IDs
212
+ - Failed connections, auth rejections, and denied commands logged at `warn` level
213
+ - Log format: structured JSON with timestamps