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,349 @@
1
+ /**
2
+ * SudoGuard — Central module for detecting permission failures and generating
3
+ * structured elevation prompts that instruct the AI client to ask the user
4
+ * for their sudo password.
5
+ *
6
+ * This module ensures that no MCP tool ever silently fails due to missing
7
+ * sudo privileges. Instead, failures are intercepted and converted into
8
+ * clear, actionable elevation prompts.
9
+ *
10
+ * ## Three Interception Layers
11
+ *
12
+ * 1. **Pre-flight** (tool-wrapper.ts): Tools with `sudo: "always"` are blocked
13
+ * before execution if no session is active. SudoGuard generates the prompt.
14
+ *
15
+ * 2. **Executor** (executor.ts): After command execution, the `permissionDenied`
16
+ * flag on {@link CommandResult} is set when stderr/exit code match known
17
+ * permission-denied patterns.
18
+ *
19
+ * 3. **Post-execution** (tool-wrapper.ts): If a tool handler's response
20
+ * indicates a permission error (detected via output text analysis),
21
+ * SudoGuard wraps it with an elevation prompt.
22
+ *
23
+ * ## Usage
24
+ *
25
+ * ```typescript
26
+ * import { SudoGuard } from './sudo-guard.js';
27
+ *
28
+ * // Check if command output indicates permission denied
29
+ * if (SudoGuard.isPermissionError(result.stderr, result.exitCode)) {
30
+ * return SudoGuard.createElevationPrompt('firewall_iptables_add');
31
+ * }
32
+ * ```
33
+ *
34
+ * @module sudo-guard
35
+ */
36
+ import { SudoSession } from "./sudo-session.js";
37
+ import { lstatSync } from "node:fs";
38
+ // ── Permission Error Detection ───────────────────────────────────────────────
39
+ /**
40
+ * Patterns in stderr/stdout that indicate a permission/privilege failure.
41
+ * These are matched case-insensitively against combined output.
42
+ *
43
+ * Covers sudo, polkit, systemd, Docker, iptables, and general POSIX errors.
44
+ */
45
+ const PERMISSION_ERROR_PATTERNS = [
46
+ // sudo-specific
47
+ /sudo[:\s].*password/i,
48
+ /sudo[:\s].*required/i,
49
+ /a password is required/i,
50
+ /sorry,?\s+try again/i,
51
+ /\bsudo\b.*\bnot allowed\b/i,
52
+ /no password.*and.*not.*(sudoers|allowed)/i,
53
+ // General POSIX permission errors
54
+ /permission denied/i,
55
+ /operation not permitted/i,
56
+ /EACCES/,
57
+ /EPERM/,
58
+ /access denied/i,
59
+ // Specific binary errors
60
+ /must be run as root/i,
61
+ /must be root/i,
62
+ /requires? root/i,
63
+ /requires? superuser/i,
64
+ /run.*as.*root/i,
65
+ /need to be root/i,
66
+ /insufficient privileges?/i,
67
+ /not enough privileges?/i,
68
+ /only root can/i,
69
+ /you must be root/i,
70
+ // iptables / nftables
71
+ /can't initialize iptables/i,
72
+ /iptables.*Permission denied/i,
73
+ /nft.*Operation not permitted/i,
74
+ // systemd / service management
75
+ /polkit.*authorization/i,
76
+ /interactive authentication required/i,
77
+ /access denied by.*policy/i,
78
+ /not privileged/i,
79
+ // Docker
80
+ /docker.*permission denied/i,
81
+ /connect: permission denied/i,
82
+ /dial.*permission denied/i,
83
+ // Package management
84
+ /are you root\?/i,
85
+ /unable to lock/i,
86
+ /could not get lock/i,
87
+ // auditd
88
+ /audit.*permission/i,
89
+ // File system
90
+ /cannot open.*permission denied/i,
91
+ /cannot write.*permission denied/i,
92
+ /read-only file system/i,
93
+ ];
94
+ /**
95
+ * Exit codes that commonly indicate permission failures.
96
+ * Note: exit code alone is not sufficient — must be combined with pattern
97
+ * matching for reliable detection.
98
+ */
99
+ const PERMISSION_EXIT_CODES = new Set([
100
+ 1, // General error (common for sudo failures)
101
+ 126, // Command invoked cannot execute (permission issue)
102
+ 4, // iptables: resource problem (often permission)
103
+ 77, // BSD/systemd: noperm
104
+ ]);
105
+ // ── SudoGuard ────────────────────────────────────────────────────────────────
106
+ /**
107
+ * Static utility class for permission error detection and elevation prompt
108
+ * generation. All methods are stateless and can be called directly.
109
+ */
110
+ export class SudoGuard {
111
+ /**
112
+ * Check whether command output (stderr and/or stdout) indicates a
113
+ * permission/privilege failure.
114
+ *
115
+ * Uses a combination of pattern matching against known error messages
116
+ * and exit code analysis. Pattern matching alone is authoritative —
117
+ * exit codes are used as supporting evidence only.
118
+ *
119
+ * @param output Combined stderr + stdout text to analyze
120
+ * @param exitCode The process exit code (optional, for confidence)
121
+ * @returns `true` if the output indicates a permission error
122
+ */
123
+ static isPermissionError(output, exitCode) {
124
+ if (!output || output.length === 0) {
125
+ return false;
126
+ }
127
+ // Check patterns against combined output
128
+ for (const pattern of PERMISSION_ERROR_PATTERNS) {
129
+ if (pattern.test(output)) {
130
+ return true;
131
+ }
132
+ }
133
+ // Exit code alone is not sufficient (too many false positives),
134
+ // but exit code 126 is very specific to permission issues
135
+ if (exitCode === 126) {
136
+ return true;
137
+ }
138
+ return false;
139
+ }
140
+ /**
141
+ * Create a structured MCP elevation prompt response.
142
+ *
143
+ * The response includes:
144
+ * - A clear human-readable message explaining what happened
145
+ * - Instructions to call `sudo_elevate` with the user's password
146
+ * - Machine-readable `_meta` for client-side automation
147
+ *
148
+ * @param toolName The tool that requires elevation
149
+ * @param reason Optional specific reason (from manifest or error output)
150
+ * @param originalError The original error message to include for context
151
+ */
152
+ static createElevationPrompt(toolName, reason, originalError) {
153
+ const session = SudoSession.getInstance();
154
+ const status = session.getStatus();
155
+ const reasonText = reason ?? "This tool requires elevated (root) privileges to function.";
156
+ // Build the prompt message — designed to be assertive enough that
157
+ // AI clients will HALT their workflow and ask the user for credentials.
158
+ const lines = [];
159
+ lines.push("🛑 WORKFLOW HALTED — ELEVATED PRIVILEGES REQUIRED");
160
+ lines.push("═".repeat(50));
161
+ lines.push("");
162
+ lines.push(`Tool: ${toolName}`);
163
+ lines.push(`Reason: ${reasonText}`);
164
+ lines.push("");
165
+ if (status.elevated && status.remainingSeconds !== null && status.remainingSeconds <= 0) {
166
+ // Session expired
167
+ lines.push("⚠️ Your sudo session has expired.");
168
+ lines.push("");
169
+ }
170
+ lines.push("⚠️ DO NOT SKIP THIS TOOL OR CONTINUE WITHOUT ELEVATION.");
171
+ lines.push("⚠️ You MUST ask the user for their sudo password NOW.");
172
+ lines.push("");
173
+ lines.push("ACTION REQUIRED — BEFORE PROCEEDING:");
174
+ lines.push("─".repeat(50));
175
+ lines.push("");
176
+ lines.push("1. ASK the user for their sudo password");
177
+ lines.push("2. CALL sudo_elevate with the password");
178
+ lines.push("3. RETRY this tool (and any other tools that failed)");
179
+ lines.push("");
180
+ lines.push(" Tool: sudo_elevate");
181
+ lines.push(" Parameter: password = <user's sudo password>");
182
+ lines.push("");
183
+ lines.push("Once elevated, ALL privileged tools will work automatically");
184
+ lines.push("for the session duration (default: 15 minutes).");
185
+ lines.push("");
186
+ lines.push("Security guarantees for the user's password:");
187
+ lines.push(" • Stored securely in a zeroable memory buffer");
188
+ lines.push(" • Never logged or exposed in any output");
189
+ lines.push(" • Auto-expires after the configured timeout");
190
+ lines.push(" • Can be dropped at any time with sudo_drop");
191
+ if (originalError) {
192
+ lines.push("");
193
+ lines.push("─".repeat(50));
194
+ lines.push("Original error:");
195
+ lines.push(originalError.substring(0, 500));
196
+ }
197
+ return {
198
+ content: [
199
+ {
200
+ type: "text",
201
+ text: lines.join("\n"),
202
+ },
203
+ ],
204
+ isError: true,
205
+ _meta: {
206
+ elevationRequired: true,
207
+ haltWorkflow: true,
208
+ failedTool: toolName,
209
+ reason: reasonText,
210
+ elevationTool: "sudo_elevate",
211
+ },
212
+ };
213
+ }
214
+ /**
215
+ * Check if a tool handler's MCP response content indicates a permission
216
+ * error that occurred at runtime (after pre-flight passed).
217
+ *
218
+ * This catches `conditional` sudo tools and tools where the pre-flight
219
+ * check passed but the actual command still failed due to permissions.
220
+ *
221
+ * Examines the `content` array of the tool's response for text content
222
+ * matching permission error patterns.
223
+ */
224
+ static isResponsePermissionError(response) {
225
+ if (!response)
226
+ return false;
227
+ // Only check error responses
228
+ if (!response.isError)
229
+ return false;
230
+ const content = response.content;
231
+ if (!Array.isArray(content))
232
+ return false;
233
+ for (const item of content) {
234
+ if (typeof item === "object" &&
235
+ item !== null &&
236
+ "type" in item &&
237
+ item.type === "text" &&
238
+ "text" in item &&
239
+ typeof item.text === "string") {
240
+ const text = item.text;
241
+ if (SudoGuard.isPermissionError(text)) {
242
+ return true;
243
+ }
244
+ }
245
+ }
246
+ return false;
247
+ }
248
+ /**
249
+ * Extract the first text content string from an MCP response.
250
+ * Used to pass original error context to the elevation prompt.
251
+ */
252
+ static extractResponseText(response) {
253
+ if (!response)
254
+ return undefined;
255
+ const content = response.content;
256
+ if (!Array.isArray(content))
257
+ return undefined;
258
+ for (const item of content) {
259
+ if (typeof item === "object" &&
260
+ item !== null &&
261
+ "type" in item &&
262
+ item.type === "text" &&
263
+ "text" in item) {
264
+ return item.text;
265
+ }
266
+ }
267
+ return undefined;
268
+ }
269
+ /**
270
+ * Check if the current process has an active sudo session that can
271
+ * be used for privileged operations.
272
+ */
273
+ static hasActiveSession() {
274
+ return SudoSession.getInstance().isElevated();
275
+ }
276
+ /**
277
+ * SECURITY (CORE-006): Validate the SUDO_ASKPASS environment variable.
278
+ *
279
+ * Before trusting the SUDO_ASKPASS path, verify:
280
+ * 1. The file exists and is a regular file (not a symlink)
281
+ * 2. Ownership is root or the current user
282
+ * 3. Permissions are restrictive (0o700 or 0o500 — no world/group access)
283
+ *
284
+ * @returns `{ valid: true }` if safe, or `{ valid: false, reason: string }` if not
285
+ */
286
+ static validateAskpass() {
287
+ const askpassPath = process.env.SUDO_ASKPASS;
288
+ if (!askpassPath) {
289
+ return { valid: true }; // Not set — nothing to validate
290
+ }
291
+ return SudoGuard.validateAskpassPath(askpassPath);
292
+ }
293
+ /**
294
+ * SECURITY (CORE-016): Validate an askpass helper path.
295
+ *
296
+ * Before trusting any askpass candidate, verify:
297
+ * 1. The file exists and is a regular file (not a symlink)
298
+ * 2. Ownership is root or the current user
299
+ * 3. Permissions are restrictive (no group/world access)
300
+ *
301
+ * @param askpassPath Absolute path to the askpass candidate
302
+ * @returns `{ valid: true }` if safe, or `{ valid: false, reason: string }` if not
303
+ */
304
+ static validateAskpassPath(askpassPath) {
305
+ try {
306
+ // 1. Check with lstat (does NOT follow symlinks)
307
+ const lstats = lstatSync(askpassPath);
308
+ if (lstats.isSymbolicLink()) {
309
+ return {
310
+ valid: false,
311
+ reason: `Askpass path '${askpassPath}' is a symlink. Refusing to trust it.`,
312
+ };
313
+ }
314
+ if (!lstats.isFile()) {
315
+ return {
316
+ valid: false,
317
+ reason: `Askpass path '${askpassPath}' is not a regular file.`,
318
+ };
319
+ }
320
+ // 2. Verify ownership: must be root (uid 0) or the current user
321
+ const currentUid = process.getuid?.() ?? -1;
322
+ if (lstats.uid !== 0 && lstats.uid !== currentUid) {
323
+ return {
324
+ valid: false,
325
+ reason: `Askpass '${askpassPath}' is owned by uid ${lstats.uid}, expected root (0) or current user (${currentUid}).`,
326
+ };
327
+ }
328
+ // 3. Verify permissions: no group or world access (must be 0o700 or 0o500)
329
+ // Extract the permission bits (lower 9 bits of mode)
330
+ const perms = lstats.mode & 0o777;
331
+ const groupWorldBits = perms & 0o077;
332
+ if (groupWorldBits !== 0) {
333
+ return {
334
+ valid: false,
335
+ reason: `Askpass '${askpassPath}' has overly permissive mode 0o${perms.toString(8)}. ` +
336
+ `Expected no group/world access (e.g., 0700 or 0500).`,
337
+ };
338
+ }
339
+ return { valid: true };
340
+ }
341
+ catch (err) {
342
+ const msg = err instanceof Error ? err.message : String(err);
343
+ return {
344
+ valid: false,
345
+ reason: `Failed to verify askpass '${askpassPath}': ${msg}`,
346
+ };
347
+ }
348
+ }
349
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * SudoSession — singleton that manages elevated privilege credentials.
3
+ *
4
+ * The MCP server runs non-interactively via stdio transport, so `sudo`
5
+ * cannot prompt for a password through a TTY. This module stores the
6
+ * user's password in a zeroable Buffer and transparently provides it
7
+ * to `sudo -S` via stdin piping in the executor.
8
+ *
9
+ * Security features:
10
+ * - Password stored in a Buffer and remains as Buffer through the entire
11
+ * stdin pipeline (never converted to a V8 string, can be zeroed)
12
+ * - Auto-expires after a configurable timeout (default 15 minutes)
13
+ * - Explicit `drop()` zeroes the buffer immediately
14
+ * - Process exit handler zeroes the buffer on shutdown
15
+ * - Validates credentials before storing (test with `sudo -S -v`)
16
+ * - Never logs or exposes the password in any output
17
+ *
18
+ * Child process spawning goes through spawn-safe.ts which enforces the
19
+ * command allowlist and shell: false without creating circular dependencies.
20
+ */
21
+ export interface SudoSessionStatus {
22
+ elevated: boolean;
23
+ username: string | null;
24
+ expiresAt: string | null;
25
+ remainingSeconds: number | null;
26
+ }
27
+ export declare class SudoSession {
28
+ /** Password stored in a Buffer so we can zero it (not interned by V8). */
29
+ private passwordBuf;
30
+ /** Username that authenticated. */
31
+ private username;
32
+ /**
33
+ * SECURITY (CICD-028): OS-level user ID that created this session.
34
+ * Used for session isolation tracking. Defaults to process.getuid().
35
+ *
36
+ * NOTE: Concurrent multi-user sessions are NOT currently supported.
37
+ * This server should not be used in multi-tenant environments where
38
+ * multiple users share the same process. Each user should run their
39
+ * own server instance.
40
+ */
41
+ private sessionUserId;
42
+ /** Timestamp (epoch ms) when the session expires. */
43
+ private expiresAt;
44
+ /** Handle for the auto-expiry timer. */
45
+ private expiryTimer;
46
+ /** Default session timeout in milliseconds (15 min). */
47
+ private defaultTimeoutMs;
48
+ private constructor();
49
+ /** Get the singleton instance. */
50
+ static getInstance(): SudoSession;
51
+ /**
52
+ * Reset the singleton instance (for testing only).
53
+ * @internal
54
+ */
55
+ static resetInstance(): void;
56
+ /**
57
+ * Set the session timeout in milliseconds.
58
+ * Only affects future `elevate()` calls.
59
+ */
60
+ setDefaultTimeout(ms: number): void;
61
+ /**
62
+ * Attempt to elevate privileges by validating the given password.
63
+ *
64
+ * Runs `sudo -S -k -v` with the password piped on stdin.
65
+ * `-k` invalidates cached credentials so we always test our password.
66
+ * `-v` validates without running a command.
67
+ * `-S` reads password from stdin.
68
+ * `-p ""` suppresses the password prompt text.
69
+ *
70
+ * @returns result indicating success or failure with error message.
71
+ */
72
+ elevate(password: string | Buffer, timeoutMs?: number): Promise<{
73
+ success: boolean;
74
+ error?: string;
75
+ }>;
76
+ /**
77
+ * Returns a **copy** of the password Buffer for piping to sudo -S,
78
+ * or null if not elevated.
79
+ *
80
+ * The caller MUST zero the returned Buffer with `.fill(0)` after use.
81
+ * A copy is returned so the original can be zeroed independently via `drop()`.
82
+ */
83
+ getPassword(): Buffer | null;
84
+ /** Check whether we have an active elevated session. */
85
+ isElevated(): boolean;
86
+ /** Get current session status (safe to expose via MCP). */
87
+ getStatus(): SudoSessionStatus;
88
+ /**
89
+ * Drop elevated privileges immediately.
90
+ * Zeroes the password buffer and clears all session state.
91
+ */
92
+ drop(): void;
93
+ /**
94
+ * Extend the session timeout by the given milliseconds (or the default).
95
+ */
96
+ extend(extraMs?: number): boolean;
97
+ private storePassword;
98
+ private isExpired;
99
+ }
100
+ //# sourceMappingURL=sudo-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sudo-session.d.ts","sourceRoot":"","sources":["../../src/core/sudo-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAsED,qBAAa,WAAW;IACtB,0EAA0E;IAC1E,OAAO,CAAC,WAAW,CAAuB;IAE1C,mCAAmC;IACnC,OAAO,CAAC,QAAQ,CAAuB;IAEvC;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa,CAAuB;IAE5C,qDAAqD;IACrD,OAAO,CAAC,SAAS,CAAuB;IAExC,wCAAwC;IACxC,OAAO,CAAC,WAAW,CAA8C;IAEjE,wDAAwD;IACxD,OAAO,CAAC,gBAAgB,CAAkB;IAE1C,OAAO;IASP,kCAAkC;IAClC,MAAM,CAAC,WAAW,IAAI,WAAW;IAOjC;;;OAGG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAI5B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAMnC;;;;;;;;;;OAUG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAuE3G;;;;;;OAMG;IACH,WAAW,IAAI,MAAM,GAAG,IAAI;IAc5B,wDAAwD;IACxD,UAAU,IAAI,OAAO;IAUrB,2DAA2D;IAC3D,SAAS,IAAI,iBAAiB;IAsB9B;;;OAGG;IACH,IAAI,IAAI,IAAI;IAwBZ;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO;IAyBjC,OAAO,CAAC,aAAa;IA8BrB,OAAO,CAAC,SAAS;CAIlB"}