defense-mcp-server 0.6.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.
Files changed (186) hide show
  1. package/CHANGELOG.md +471 -0
  2. package/LICENSE +21 -0
  3. package/README.md +242 -0
  4. package/build/core/auto-installer.d.ts +102 -0
  5. package/build/core/auto-installer.d.ts.map +1 -0
  6. package/build/core/auto-installer.js +833 -0
  7. package/build/core/backup-manager.d.ts +63 -0
  8. package/build/core/backup-manager.d.ts.map +1 -0
  9. package/build/core/backup-manager.js +189 -0
  10. package/build/core/changelog.d.ts +75 -0
  11. package/build/core/changelog.d.ts.map +1 -0
  12. package/build/core/changelog.js +123 -0
  13. package/build/core/command-allowlist.d.ts +129 -0
  14. package/build/core/command-allowlist.d.ts.map +1 -0
  15. package/build/core/command-allowlist.js +849 -0
  16. package/build/core/config.d.ts +79 -0
  17. package/build/core/config.d.ts.map +1 -0
  18. package/build/core/config.js +193 -0
  19. package/build/core/dependency-validator.d.ts +106 -0
  20. package/build/core/dependency-validator.d.ts.map +1 -0
  21. package/build/core/dependency-validator.js +405 -0
  22. package/build/core/distro-adapter.d.ts +177 -0
  23. package/build/core/distro-adapter.d.ts.map +1 -0
  24. package/build/core/distro-adapter.js +481 -0
  25. package/build/core/distro.d.ts +68 -0
  26. package/build/core/distro.d.ts.map +1 -0
  27. package/build/core/distro.js +457 -0
  28. package/build/core/encrypted-state.d.ts +76 -0
  29. package/build/core/encrypted-state.d.ts.map +1 -0
  30. package/build/core/encrypted-state.js +209 -0
  31. package/build/core/executor.d.ts +56 -0
  32. package/build/core/executor.d.ts.map +1 -0
  33. package/build/core/executor.js +350 -0
  34. package/build/core/installer.d.ts +92 -0
  35. package/build/core/installer.d.ts.map +1 -0
  36. package/build/core/installer.js +1072 -0
  37. package/build/core/logger.d.ts +102 -0
  38. package/build/core/logger.d.ts.map +1 -0
  39. package/build/core/logger.js +132 -0
  40. package/build/core/parsers.d.ts +151 -0
  41. package/build/core/parsers.d.ts.map +1 -0
  42. package/build/core/parsers.js +479 -0
  43. package/build/core/policy-engine.d.ts +170 -0
  44. package/build/core/policy-engine.d.ts.map +1 -0
  45. package/build/core/policy-engine.js +656 -0
  46. package/build/core/preflight.d.ts +157 -0
  47. package/build/core/preflight.d.ts.map +1 -0
  48. package/build/core/preflight.js +638 -0
  49. package/build/core/privilege-manager.d.ts +108 -0
  50. package/build/core/privilege-manager.d.ts.map +1 -0
  51. package/build/core/privilege-manager.js +363 -0
  52. package/build/core/rate-limiter.d.ts +67 -0
  53. package/build/core/rate-limiter.d.ts.map +1 -0
  54. package/build/core/rate-limiter.js +129 -0
  55. package/build/core/rollback.d.ts +73 -0
  56. package/build/core/rollback.d.ts.map +1 -0
  57. package/build/core/rollback.js +278 -0
  58. package/build/core/safeguards.d.ts +58 -0
  59. package/build/core/safeguards.d.ts.map +1 -0
  60. package/build/core/safeguards.js +448 -0
  61. package/build/core/sanitizer.d.ts +118 -0
  62. package/build/core/sanitizer.d.ts.map +1 -0
  63. package/build/core/sanitizer.js +459 -0
  64. package/build/core/secure-fs.d.ts +67 -0
  65. package/build/core/secure-fs.d.ts.map +1 -0
  66. package/build/core/secure-fs.js +143 -0
  67. package/build/core/spawn-safe.d.ts +55 -0
  68. package/build/core/spawn-safe.d.ts.map +1 -0
  69. package/build/core/spawn-safe.js +146 -0
  70. package/build/core/sudo-guard.d.ts +145 -0
  71. package/build/core/sudo-guard.d.ts.map +1 -0
  72. package/build/core/sudo-guard.js +349 -0
  73. package/build/core/sudo-session.d.ts +100 -0
  74. package/build/core/sudo-session.d.ts.map +1 -0
  75. package/build/core/sudo-session.js +319 -0
  76. package/build/core/tool-dependencies.d.ts +61 -0
  77. package/build/core/tool-dependencies.d.ts.map +1 -0
  78. package/build/core/tool-dependencies.js +571 -0
  79. package/build/core/tool-registry.d.ts +111 -0
  80. package/build/core/tool-registry.d.ts.map +1 -0
  81. package/build/core/tool-registry.js +656 -0
  82. package/build/core/tool-wrapper.d.ts +73 -0
  83. package/build/core/tool-wrapper.d.ts.map +1 -0
  84. package/build/core/tool-wrapper.js +296 -0
  85. package/build/index.d.ts +3 -0
  86. package/build/index.d.ts.map +1 -0
  87. package/build/index.js +247 -0
  88. package/build/tools/access-control.d.ts +9 -0
  89. package/build/tools/access-control.d.ts.map +1 -0
  90. package/build/tools/access-control.js +1818 -0
  91. package/build/tools/api-security.d.ts +12 -0
  92. package/build/tools/api-security.d.ts.map +1 -0
  93. package/build/tools/api-security.js +901 -0
  94. package/build/tools/app-hardening.d.ts +11 -0
  95. package/build/tools/app-hardening.d.ts.map +1 -0
  96. package/build/tools/app-hardening.js +768 -0
  97. package/build/tools/backup.d.ts +8 -0
  98. package/build/tools/backup.d.ts.map +1 -0
  99. package/build/tools/backup.js +381 -0
  100. package/build/tools/cloud-security.d.ts +17 -0
  101. package/build/tools/cloud-security.d.ts.map +1 -0
  102. package/build/tools/cloud-security.js +739 -0
  103. package/build/tools/compliance.d.ts +10 -0
  104. package/build/tools/compliance.d.ts.map +1 -0
  105. package/build/tools/compliance.js +1225 -0
  106. package/build/tools/container-security.d.ts +14 -0
  107. package/build/tools/container-security.d.ts.map +1 -0
  108. package/build/tools/container-security.js +788 -0
  109. package/build/tools/deception.d.ts +13 -0
  110. package/build/tools/deception.d.ts.map +1 -0
  111. package/build/tools/deception.js +763 -0
  112. package/build/tools/dns-security.d.ts +93 -0
  113. package/build/tools/dns-security.d.ts.map +1 -0
  114. package/build/tools/dns-security.js +745 -0
  115. package/build/tools/drift-detection.d.ts +8 -0
  116. package/build/tools/drift-detection.d.ts.map +1 -0
  117. package/build/tools/drift-detection.js +326 -0
  118. package/build/tools/ebpf-security.d.ts +15 -0
  119. package/build/tools/ebpf-security.d.ts.map +1 -0
  120. package/build/tools/ebpf-security.js +294 -0
  121. package/build/tools/encryption.d.ts +9 -0
  122. package/build/tools/encryption.d.ts.map +1 -0
  123. package/build/tools/encryption.js +1667 -0
  124. package/build/tools/firewall.d.ts +9 -0
  125. package/build/tools/firewall.d.ts.map +1 -0
  126. package/build/tools/firewall.js +1398 -0
  127. package/build/tools/hardening.d.ts +10 -0
  128. package/build/tools/hardening.d.ts.map +1 -0
  129. package/build/tools/hardening.js +2654 -0
  130. package/build/tools/ids.d.ts +9 -0
  131. package/build/tools/ids.d.ts.map +1 -0
  132. package/build/tools/ids.js +624 -0
  133. package/build/tools/incident-response.d.ts +10 -0
  134. package/build/tools/incident-response.d.ts.map +1 -0
  135. package/build/tools/incident-response.js +1180 -0
  136. package/build/tools/logging.d.ts +12 -0
  137. package/build/tools/logging.d.ts.map +1 -0
  138. package/build/tools/logging.js +454 -0
  139. package/build/tools/malware.d.ts +10 -0
  140. package/build/tools/malware.d.ts.map +1 -0
  141. package/build/tools/malware.js +532 -0
  142. package/build/tools/meta.d.ts +11 -0
  143. package/build/tools/meta.d.ts.map +1 -0
  144. package/build/tools/meta.js +2278 -0
  145. package/build/tools/network-defense.d.ts +12 -0
  146. package/build/tools/network-defense.d.ts.map +1 -0
  147. package/build/tools/network-defense.js +760 -0
  148. package/build/tools/patch-management.d.ts +3 -0
  149. package/build/tools/patch-management.d.ts.map +1 -0
  150. package/build/tools/patch-management.js +708 -0
  151. package/build/tools/process-security.d.ts +12 -0
  152. package/build/tools/process-security.d.ts.map +1 -0
  153. package/build/tools/process-security.js +784 -0
  154. package/build/tools/reporting.d.ts +11 -0
  155. package/build/tools/reporting.d.ts.map +1 -0
  156. package/build/tools/reporting.js +559 -0
  157. package/build/tools/secrets.d.ts +9 -0
  158. package/build/tools/secrets.d.ts.map +1 -0
  159. package/build/tools/secrets.js +596 -0
  160. package/build/tools/siem-integration.d.ts +18 -0
  161. package/build/tools/siem-integration.d.ts.map +1 -0
  162. package/build/tools/siem-integration.js +754 -0
  163. package/build/tools/sudo-management.d.ts +18 -0
  164. package/build/tools/sudo-management.d.ts.map +1 -0
  165. package/build/tools/sudo-management.js +737 -0
  166. package/build/tools/supply-chain-security.d.ts +8 -0
  167. package/build/tools/supply-chain-security.d.ts.map +1 -0
  168. package/build/tools/supply-chain-security.js +256 -0
  169. package/build/tools/threat-intel.d.ts +22 -0
  170. package/build/tools/threat-intel.d.ts.map +1 -0
  171. package/build/tools/threat-intel.js +749 -0
  172. package/build/tools/vulnerability-management.d.ts +11 -0
  173. package/build/tools/vulnerability-management.d.ts.map +1 -0
  174. package/build/tools/vulnerability-management.js +667 -0
  175. package/build/tools/waf.d.ts +12 -0
  176. package/build/tools/waf.d.ts.map +1 -0
  177. package/build/tools/waf.js +843 -0
  178. package/build/tools/wireless-security.d.ts +19 -0
  179. package/build/tools/wireless-security.d.ts.map +1 -0
  180. package/build/tools/wireless-security.js +826 -0
  181. package/build/tools/zero-trust-network.d.ts +8 -0
  182. package/build/tools/zero-trust-network.d.ts.map +1 -0
  183. package/build/tools/zero-trust-network.js +367 -0
  184. package/docs/SAFEGUARDS.md +518 -0
  185. package/docs/TOOLS-REFERENCE.md +665 -0
  186. package/package.json +87 -0
@@ -0,0 +1,448 @@
1
+ /**
2
+ * SafeguardRegistry — singleton that detects running applications and
3
+ * environmental state so defensive operations can be evaluated for safety.
4
+ *
5
+ * Detection domains:
6
+ * - VS Code (editor process, config dir, IPC sockets)
7
+ * - Docker (daemon socket, running containers)
8
+ * - MCP servers (workspace config + node processes)
9
+ * - Databases (TCP port probing: PostgreSQL, MySQL, MongoDB, Redis)
10
+ * - Web servers (nginx, apache2, httpd process detection)
11
+ */
12
+ import * as fs from "node:fs";
13
+ import * as net from "node:net";
14
+ import * as os from "node:os";
15
+ import * as path from "node:path";
16
+ import { z } from "zod";
17
+ import { executeCommand } from "./executor.js";
18
+ // ── Zod schemas ──────────────────────────────────────────────────────────────
19
+ const OperationSchema = z.string().min(1).max(256);
20
+ const ParamsSchema = z.record(z.string(), z.unknown());
21
+ // ── Operations that affect specific domains ──────────────────────────────────
22
+ const DOCKER_OPERATIONS = [
23
+ "container", "docker", "apparmor", "seccomp", "namespace",
24
+ "image", "rootless",
25
+ ];
26
+ const FIREWALL_OPERATIONS = [
27
+ "firewall", "iptables", "nftables", "ufw", "port", "chain",
28
+ "microsegmentation", "wireguard",
29
+ ];
30
+ const SERVICE_OPERATIONS = [
31
+ "service", "systemd", "systemctl", "enable", "disable",
32
+ "start", "stop", "restart", "daemon",
33
+ ];
34
+ const DATABASE_OPERATIONS = [
35
+ "database", "postgres", "mysql", "mongo", "redis", "port",
36
+ ];
37
+ const WEBSERVER_OPERATIONS = [
38
+ "nginx", "apache", "httpd", "webserver", "tls", "cert",
39
+ ];
40
+ const SSH_OPERATIONS = [
41
+ "ssh", "sshd", "access_ssh",
42
+ ];
43
+ // ── Helpers ──────────────────────────────────────────────────────────────────
44
+ /** Check if a TCP port accepts connections (timeout 1s). */
45
+ function probePort(port, host = "127.0.0.1") {
46
+ return new Promise((resolve) => {
47
+ const sock = net.createConnection({ port, host, timeout: 1000 });
48
+ sock.once("connect", () => { sock.destroy(); resolve(true); });
49
+ sock.once("error", () => { resolve(false); });
50
+ sock.once("timeout", () => { sock.destroy(); resolve(false); });
51
+ });
52
+ }
53
+ /** Run pgrep and return true if at least one process matched. */
54
+ async function pgrepExists(pattern) {
55
+ try {
56
+ const r = await executeCommand({ command: "pgrep", args: ["-f", pattern], timeout: 5000 });
57
+ return r.exitCode === 0 && r.stdout.trim().length > 0;
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ function matchesAny(operation, keywords) {
64
+ const lower = operation.toLowerCase();
65
+ return keywords.some((k) => lower.includes(k));
66
+ }
67
+ /** Check if the current session is over SSH. */
68
+ export function isSSHSession() {
69
+ return !!(process.env.SSH_CONNECTION || process.env.SSH_TTY);
70
+ }
71
+ /** Check if the current user has a non-empty authorized_keys file. */
72
+ export function hasAuthorizedKeys() {
73
+ try {
74
+ const authKeysPath = path.join(os.homedir(), ".ssh", "authorized_keys");
75
+ if (!fs.existsSync(authKeysPath))
76
+ return false;
77
+ const stat = fs.statSync(authKeysPath);
78
+ return stat.size > 0;
79
+ }
80
+ catch {
81
+ return false;
82
+ }
83
+ }
84
+ /** Safely extract a string parameter value. */
85
+ function getParam(params, key) {
86
+ const val = params[key];
87
+ return typeof val === "string" ? val : undefined;
88
+ }
89
+ /** Check if an SSH-related operation is a modification (not an audit/check). */
90
+ function isSSHModification(operation, params) {
91
+ if (operation.includes("audit") || operation.includes("check")) {
92
+ return false;
93
+ }
94
+ if (operation.includes("harden"))
95
+ return true;
96
+ if (getParam(params, "settings"))
97
+ return true;
98
+ if (params.apply_recommended === true)
99
+ return true;
100
+ if (params.restart_sshd === true)
101
+ return true;
102
+ return false;
103
+ }
104
+ // ── SafeguardRegistry ────────────────────────────────────────────────────────
105
+ // SECURITY (CORE-021): Module-scoped singleton variable prevents external
106
+ // mutation via (SafeguardRegistry as any).instance — inaccessible outside module.
107
+ let _safeguardInstance = null;
108
+ export class SafeguardRegistry {
109
+ /** Cached detection results with TTL to avoid re-running on every tool call */
110
+ detectionCache = { result: null, timestamp: 0 };
111
+ DETECTION_CACHE_TTL = 15_000; // 15 seconds
112
+ constructor() { }
113
+ /** Get the singleton instance. */
114
+ static getInstance() {
115
+ if (!_safeguardInstance) {
116
+ _safeguardInstance = new SafeguardRegistry();
117
+ }
118
+ return _safeguardInstance;
119
+ }
120
+ /** Detect VS Code editor presence. */
121
+ async detectVSCode() {
122
+ const details = [];
123
+ let detected = false;
124
+ try {
125
+ // Check for running VS Code process
126
+ if (await pgrepExists("code")) {
127
+ details.push("VS Code process running");
128
+ detected = true;
129
+ }
130
+ // Check config directory
131
+ const vscodeDir = path.join(os.homedir(), ".vscode");
132
+ if (fs.existsSync(vscodeDir)) {
133
+ details.push(".vscode config dir exists");
134
+ detected = true;
135
+ }
136
+ // Check for IPC sockets
137
+ const uid = process.getuid?.();
138
+ if (uid !== undefined) {
139
+ const runDir = `/run/user/${uid}`;
140
+ try {
141
+ if (fs.existsSync(runDir)) {
142
+ const entries = fs.readdirSync(runDir);
143
+ const ipc = entries.filter((e) => e.startsWith("vscode-ipc-"));
144
+ if (ipc.length > 0) {
145
+ details.push(`${ipc.length} IPC socket(s) found`);
146
+ detected = true;
147
+ }
148
+ }
149
+ }
150
+ catch { /* no access */ }
151
+ }
152
+ }
153
+ catch { /* detection failed gracefully */ }
154
+ return {
155
+ category: "VS Code",
156
+ detected,
157
+ detail: detected ? details.join("; ") : "Not detected",
158
+ };
159
+ }
160
+ /** Detect Docker daemon and running containers. */
161
+ async detectDocker() {
162
+ const details = [];
163
+ let detected = false;
164
+ try {
165
+ // Check for Docker socket
166
+ if (fs.existsSync("/var/run/docker.sock")) {
167
+ details.push("Docker socket exists");
168
+ detected = true;
169
+ // Try to list running containers
170
+ const r = await executeCommand({
171
+ command: "docker",
172
+ args: ["ps", "--format", "{{.Names}}"],
173
+ timeout: 5000,
174
+ });
175
+ if (r.exitCode === 0) {
176
+ const containers = r.stdout.trim().split("\n").filter(Boolean);
177
+ if (containers.length > 0) {
178
+ details.push(`${containers.length} container(s): ${containers.slice(0, 5).join(", ")}`);
179
+ }
180
+ else {
181
+ details.push("No running containers");
182
+ }
183
+ }
184
+ }
185
+ }
186
+ catch { /* detection failed gracefully */ }
187
+ return {
188
+ category: "Docker",
189
+ detected,
190
+ detail: detected ? details.join("; ") : "Not detected",
191
+ };
192
+ }
193
+ /** Detect configured MCP servers. */
194
+ async detectMCPServers() {
195
+ const details = [];
196
+ let detected = false;
197
+ try {
198
+ // SECURITY (CORE-010): Use environment-aware path instead of hardcoded /home/robert/...
199
+ const mcpConfigPath = path.join(os.homedir(), "kali-mcp-workspace", ".mcp.json");
200
+ if (fs.existsSync(mcpConfigPath)) {
201
+ const raw = fs.readFileSync(mcpConfigPath, "utf-8");
202
+ const config = JSON.parse(raw);
203
+ const servers = config.mcpServers ?? config.servers ?? {};
204
+ const names = Object.keys(servers);
205
+ if (names.length > 0) {
206
+ details.push(`${names.length} MCP server(s) configured: ${names.join(", ")}`);
207
+ detected = true;
208
+ }
209
+ }
210
+ // Check for node processes
211
+ const r = await executeCommand({
212
+ command: "pgrep",
213
+ args: ["-a", "node"],
214
+ timeout: 5000,
215
+ });
216
+ if (r.exitCode === 0) {
217
+ const mcpProcs = r.stdout.split("\n").filter((l) => l.includes("mcp"));
218
+ if (mcpProcs.length > 0) {
219
+ details.push(`${mcpProcs.length} MCP-related node process(es)`);
220
+ detected = true;
221
+ }
222
+ }
223
+ }
224
+ catch { /* detection failed gracefully */ }
225
+ return {
226
+ category: "MCP Servers",
227
+ detected,
228
+ detail: detected ? details.join("; ") : "Not detected",
229
+ };
230
+ }
231
+ /** Detect databases via TCP port probing. */
232
+ async detectDatabases() {
233
+ const portMap = [
234
+ [5432, "PostgreSQL"],
235
+ [3306, "MySQL"],
236
+ [27017, "MongoDB"],
237
+ [6379, "Redis"],
238
+ ];
239
+ const results = await Promise.all(portMap.map(async ([port, name]) => {
240
+ const open = await probePort(port);
241
+ return open ? name : null;
242
+ }));
243
+ const found = results.filter(Boolean);
244
+ return {
245
+ category: "Databases",
246
+ detected: found.length > 0,
247
+ detail: found.length > 0
248
+ ? `Active: ${found.join(", ")}`
249
+ : "No databases detected on standard ports",
250
+ };
251
+ }
252
+ /** Detect web server processes. */
253
+ async detectWebServers() {
254
+ const servers = ["nginx", "apache2", "httpd"];
255
+ const found = [];
256
+ for (const name of servers) {
257
+ if (await pgrepExists(name)) {
258
+ found.push(name);
259
+ }
260
+ }
261
+ return {
262
+ category: "Web Servers",
263
+ detected: found.length > 0,
264
+ detail: found.length > 0
265
+ ? `Running: ${found.join(", ")}`
266
+ : "No web servers detected",
267
+ };
268
+ }
269
+ /**
270
+ * Check whether an operation is safe given current system state.
271
+ * Returns warnings (non-blocking) and blockers (operation should not proceed).
272
+ */
273
+ async checkSafety(operation, params) {
274
+ const validOp = OperationSchema.safeParse(operation);
275
+ if (!validOp.success) {
276
+ return {
277
+ safe: false,
278
+ warnings: [],
279
+ blockers: ["Invalid operation name"],
280
+ impactedApps: [],
281
+ };
282
+ }
283
+ const warnings = [];
284
+ const blockers = [];
285
+ const impactedApps = [];
286
+ try {
287
+ // Use cached detections if fresh (system state rarely changes within 15s)
288
+ let vscode, docker, mcp, dbs, web;
289
+ const now = Date.now();
290
+ if (this.detectionCache.result && (now - this.detectionCache.timestamp) < this.DETECTION_CACHE_TTL) {
291
+ ({ vscode, docker, mcp, dbs, web } = this.detectionCache.result);
292
+ }
293
+ else {
294
+ [vscode, docker, mcp, dbs, web] = await Promise.all([
295
+ this.detectVSCode(),
296
+ this.detectDocker(),
297
+ this.detectMCPServers(),
298
+ this.detectDatabases(),
299
+ this.detectWebServers(),
300
+ ]);
301
+ this.detectionCache = { result: { vscode, docker, mcp, dbs, web }, timestamp: now };
302
+ }
303
+ const detections = [vscode, docker, mcp, dbs, web];
304
+ // Check Docker impact
305
+ if (docker.detected && matchesAny(operation, DOCKER_OPERATIONS)) {
306
+ warnings.push(`Docker is active (${docker.detail}) — operation may affect containers`);
307
+ impactedApps.push("Docker");
308
+ }
309
+ // Check firewall impact
310
+ if (matchesAny(operation, FIREWALL_OPERATIONS)) {
311
+ if (docker.detected) {
312
+ warnings.push("Firewall changes may disrupt Docker networking");
313
+ impactedApps.push("Docker");
314
+ }
315
+ if (dbs.detected) {
316
+ warnings.push(`Database connectivity may be affected (${dbs.detail})`);
317
+ impactedApps.push("Databases");
318
+ }
319
+ if (web.detected) {
320
+ warnings.push(`Web server traffic may be affected (${web.detail})`);
321
+ impactedApps.push("Web Servers");
322
+ }
323
+ if (mcp.detected) {
324
+ warnings.push("Firewall changes may affect MCP server communication");
325
+ impactedApps.push("MCP Servers");
326
+ }
327
+ }
328
+ // Check service impact
329
+ if (matchesAny(operation, SERVICE_OPERATIONS)) {
330
+ if (docker.detected) {
331
+ warnings.push("Service changes may affect Docker daemon");
332
+ impactedApps.push("Docker");
333
+ }
334
+ if (web.detected) {
335
+ warnings.push(`Web server services may be affected (${web.detail})`);
336
+ impactedApps.push("Web Servers");
337
+ }
338
+ }
339
+ // Check database impact
340
+ if (matchesAny(operation, DATABASE_OPERATIONS) && dbs.detected) {
341
+ warnings.push(`Active databases detected (${dbs.detail}) — proceed with caution`);
342
+ impactedApps.push("Databases");
343
+ }
344
+ // Check web server impact
345
+ if (matchesAny(operation, WEBSERVER_OPERATIONS) && web.detected) {
346
+ warnings.push(`Active web servers detected (${web.detail})`);
347
+ impactedApps.push("Web Servers");
348
+ }
349
+ // VS Code — informational only
350
+ if (vscode.detected) {
351
+ warnings.push(`VS Code is active (${vscode.detail})`);
352
+ }
353
+ // ── Blocker checks (prevent dangerous operations) ───────────────────
354
+ // A. SSH lockout prevention — block SSH config changes during SSH session
355
+ if (isSSHSession() &&
356
+ matchesAny(operation, SSH_OPERATIONS) &&
357
+ isSSHModification(operation, params)) {
358
+ blockers.push("BLOCKED: Cannot modify SSH configuration while connected via SSH. " +
359
+ "Changes could lock you out of this remote session. " +
360
+ "Use a local console or ensure alternative access before proceeding.");
361
+ }
362
+ // B. Firewall rules blocking active SSH connections
363
+ if (isSSHSession() && matchesAny(operation, FIREWALL_OPERATIONS)) {
364
+ const port = getParam(params, "port");
365
+ const fwAction = getParam(params, "action")?.toUpperCase();
366
+ const policy = getParam(params, "policy")?.toUpperCase();
367
+ const chain = getParam(params, "chain")?.toUpperCase();
368
+ // Block rules that would drop/reject traffic on SSH port
369
+ if (port === "22" &&
370
+ (fwAction === "DROP" || fwAction === "REJECT" || fwAction === "DENY")) {
371
+ blockers.push("BLOCKED: Firewall rule would block SSH port 22 while connected via SSH. " +
372
+ "This would immediately terminate your session.");
373
+ }
374
+ // Block default DROP policy on INPUT chain (would kill SSH)
375
+ if (policy === "DROP" && chain === "INPUT") {
376
+ blockers.push("BLOCKED: Setting INPUT chain default policy to DROP while connected via SSH. " +
377
+ "Ensure an explicit ACCEPT rule for SSH port exists before changing the default policy.");
378
+ }
379
+ }
380
+ // C. Disabling password auth without SSH key auth configured
381
+ if (matchesAny(operation, SSH_OPERATIONS)) {
382
+ const settings = getParam(params, "settings") ?? "";
383
+ const applyRecommended = params.apply_recommended === true;
384
+ if (settings.toLowerCase().includes("passwordauthentication=no") ||
385
+ applyRecommended) {
386
+ if (!hasAuthorizedKeys()) {
387
+ blockers.push("BLOCKED: Disabling password authentication without confirming SSH key-based " +
388
+ "access is configured. No authorized_keys file found for the current user. " +
389
+ "Set up SSH key authentication first, then retry.");
390
+ }
391
+ }
392
+ }
393
+ // D. Stopping critical database services with active connections
394
+ if (matchesAny(operation, SERVICE_OPERATIONS)) {
395
+ const svcAction = getParam(params, "action")?.toLowerCase();
396
+ const svcName = getParam(params, "service")?.toLowerCase() ?? "";
397
+ if (svcAction === "stop" || svcAction === "disable" || svcAction === "mask") {
398
+ const dbServiceNames = [
399
+ "postgresql", "postgres", "mysql", "mysqld", "mariadb",
400
+ "mongod", "mongodb", "redis", "redis-server",
401
+ ];
402
+ const isDatabase = dbServiceNames.some((db) => svcName.includes(db));
403
+ if (isDatabase && dbs.detected) {
404
+ blockers.push(`BLOCKED: Cannot ${svcAction} database service '${svcName}' while it has ` +
405
+ `active connections (${dbs.detail}). ` +
406
+ `Drain connections first or use '--force' parameter.`);
407
+ }
408
+ }
409
+ }
410
+ }
411
+ catch (err) {
412
+ warnings.push(`Safety check encountered an error: ${err instanceof Error ? err.message : String(err)}`);
413
+ }
414
+ return {
415
+ safe: blockers.length === 0,
416
+ warnings,
417
+ blockers,
418
+ impactedApps: [...new Set(impactedApps)],
419
+ };
420
+ }
421
+ /** Generate a full safety report of all detected applications. */
422
+ async appSafetyReport() {
423
+ // Reuse detection cache if fresh
424
+ let vscode, docker, mcp, dbs, web;
425
+ const now = Date.now();
426
+ if (this.detectionCache.result && (now - this.detectionCache.timestamp) < this.DETECTION_CACHE_TTL) {
427
+ ({ vscode, docker, mcp, dbs, web } = this.detectionCache.result);
428
+ }
429
+ else {
430
+ [vscode, docker, mcp, dbs, web] = await Promise.all([
431
+ this.detectVSCode(),
432
+ this.detectDocker(),
433
+ this.detectMCPServers(),
434
+ this.detectDatabases(),
435
+ this.detectWebServers(),
436
+ ]);
437
+ this.detectionCache = { result: { vscode, docker, mcp, dbs, web }, timestamp: now };
438
+ }
439
+ const detectedApps = [vscode, docker, mcp, dbs, web];
440
+ const activeCount = detectedApps.filter((a) => a.detected).length;
441
+ return {
442
+ timestamp: new Date().toISOString(),
443
+ detectedApps,
444
+ summary: `${activeCount} of ${detectedApps.length} application categories detected`,
445
+ overallSafe: true,
446
+ };
447
+ }
448
+ }
@@ -0,0 +1,118 @@
1
+ import { type DefenseConfig } from "./config.js";
2
+ /**
3
+ * Validates a target string as hostname, IPv4, IPv6, or CIDR notation.
4
+ * Throws on invalid input.
5
+ */
6
+ export declare function validateTarget(target: string, config?: DefenseConfig): string;
7
+ /**
8
+ * Validates a single port number (1-65535).
9
+ * Throws on invalid input.
10
+ */
11
+ export declare function validatePort(port: number | string): number;
12
+ /**
13
+ * Validates a port range specification (e.g., "80,443,1-1024").
14
+ * Throws on invalid input.
15
+ */
16
+ export declare function validatePortRange(range: string): string;
17
+ /**
18
+ * Validates a file path is within allowed directories,
19
+ * contains no traversal attacks, no null bytes, no shell metacharacters.
20
+ * Throws on invalid input.
21
+ */
22
+ export declare function validateFilePath(filePath: string, config?: DefenseConfig): string;
23
+ /**
24
+ * Validates an array of arguments for shell metacharacters.
25
+ * Throws on invalid input.
26
+ */
27
+ export declare function sanitizeArgs(args: string[]): string[];
28
+ /**
29
+ * Validates a systemd service name.
30
+ * Only allows `[a-zA-Z0-9._@-]+`.
31
+ * Throws on invalid input.
32
+ */
33
+ export declare function validateServiceName(name: string): string;
34
+ /**
35
+ * Validates a sysctl key (must be word.word.word... pattern).
36
+ * Throws on invalid input.
37
+ */
38
+ export declare function validateSysctlKey(key: string): string;
39
+ /**
40
+ * Validates a configuration key.
41
+ * Only allows `[a-zA-Z0-9._-]+`.
42
+ * Throws on invalid input.
43
+ */
44
+ export declare function validateConfigKey(key: string): string;
45
+ /**
46
+ * Validates a package name.
47
+ * Only allows `[a-zA-Z0-9._+:-]+`.
48
+ * Throws on invalid input.
49
+ */
50
+ export declare function validatePackageName(name: string): string;
51
+ /**
52
+ * Validates an iptables chain name.
53
+ * Allows built-in chains `[A-Z_]+` (e.g., INPUT, OUTPUT, FORWARD)
54
+ * and custom chains matching `[A-Za-z_][A-Za-z0-9_-]{0,28}`.
55
+ * Throws on invalid input.
56
+ */
57
+ export declare function validateIptablesChain(chain: string): string;
58
+ /**
59
+ * Validates a network interface name.
60
+ * Only allows `[a-zA-Z0-9._-]+`, max 16 characters.
61
+ * Throws on invalid input.
62
+ */
63
+ export declare function validateInterface(iface: string): string;
64
+ /**
65
+ * Validates a Unix username.
66
+ * Only allows `[a-zA-Z0-9._-]+`, max 32 characters.
67
+ * Throws on invalid input.
68
+ */
69
+ export declare function validateUsername(name: string): string;
70
+ /**
71
+ * Validates a YARA rule file path (must end in .yar or .yara).
72
+ * Throws on invalid input.
73
+ */
74
+ export declare function validateYaraRule(path: string): string;
75
+ /**
76
+ * Validates a certificate file path (must end in .pem, .crt, .key, .p12, or .pfx).
77
+ * Throws on invalid input.
78
+ */
79
+ export declare function validateCertPath(path: string): string;
80
+ /**
81
+ * Validates a firewalld zone name.
82
+ * Only allows `[a-zA-Z0-9_-]+`.
83
+ * Throws on invalid input.
84
+ */
85
+ export declare function validateFirewallZone(zone: string): string;
86
+ /**
87
+ * Validates an auditd key name.
88
+ * Only allows `[a-zA-Z0-9_-]+`.
89
+ * Throws on invalid input.
90
+ */
91
+ export declare function validateAuditdKey(key: string): string;
92
+ /**
93
+ * Validates a tool-supplied file path against traversal attacks and an explicit
94
+ * list of allowed root directories.
95
+ *
96
+ * 1. Rejects paths containing `..`
97
+ * 2. Uses `path.resolve()` to normalize
98
+ * 3. Verifies resolved path is within one of the allowed directories
99
+ *
100
+ * @param inputPath The user-supplied path
101
+ * @param allowedDirs Array of allowed root directories (e.g. ["/var/log", "/etc"])
102
+ * @param label Human-readable label for error messages (default: "Path")
103
+ * @returns The resolved, validated path
104
+ */
105
+ export declare function validateToolPath(inputPath: string, allowedDirs: string[], label?: string): string;
106
+ /**
107
+ * Sanitize error messages before returning them to MCP clients.
108
+ *
109
+ * Strips:
110
+ * 1. Absolute file paths (replaced with `[path]`)
111
+ * 2. Stack traces (removed entirely)
112
+ * 3. Overly long messages (truncated to 500 chars)
113
+ *
114
+ * @param error - The caught error (unknown type)
115
+ * @returns A sanitized error message string safe for external exposure
116
+ */
117
+ export declare function sanitizeToolError(error: unknown): string;
118
+ //# sourceMappingURL=sanitizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizer.d.ts","sourceRoot":"","sources":["../../src/core/sanitizer.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAmB5D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAkD7E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAM1D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAwCvD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,aAAa,GACrB,MAAM,CA4DR;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAuBrD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcxD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAcrD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAcrD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcxD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAe3D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAoBvD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkBrD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA8BrD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqCrD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAczD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAcrD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EAAE,EACrB,KAAK,SAAS,GACb,MAAM,CA0CR;AAgBD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAuBxD"}