defense-mcp-server 0.9.2 → 0.9.3

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 (69) hide show
  1. package/build/core/auto-installer.js +31 -31
  2. package/build/core/command-allowlist.js +1 -1
  3. package/build/core/dependency-validator.js +9 -9
  4. package/build/core/distro-adapter.d.ts +0 -5
  5. package/build/core/distro-adapter.d.ts.map +1 -1
  6. package/build/core/distro-adapter.js +0 -7
  7. package/build/core/distro.d.ts +0 -11
  8. package/build/core/distro.d.ts.map +1 -1
  9. package/build/core/distro.js +0 -48
  10. package/build/core/encrypted-state.d.ts +0 -7
  11. package/build/core/encrypted-state.d.ts.map +1 -1
  12. package/build/core/encrypted-state.js +0 -7
  13. package/build/core/logger.js +1 -1
  14. package/build/core/pam-utils.js +1 -1
  15. package/build/core/parsers.js +1 -1
  16. package/build/core/preflight.d.ts +4 -4
  17. package/build/core/preflight.js +13 -13
  18. package/build/core/progress.js +20 -20
  19. package/build/core/run-command.d.ts +14 -0
  20. package/build/core/run-command.d.ts.map +1 -0
  21. package/build/core/run-command.js +46 -0
  22. package/build/core/spawn-safe.d.ts +6 -6
  23. package/build/core/spawn-safe.d.ts.map +1 -1
  24. package/build/core/sudo-guard.js +4 -4
  25. package/build/core/third-party-installer.js +4 -4
  26. package/build/core/tool-wrapper.js +3 -3
  27. package/build/tools/access-control.js +6 -6
  28. package/build/tools/api-security.d.ts.map +1 -1
  29. package/build/tools/api-security.js +5 -51
  30. package/build/tools/app-hardening.d.ts.map +1 -1
  31. package/build/tools/app-hardening.js +23 -25
  32. package/build/tools/cloud-security.d.ts.map +1 -1
  33. package/build/tools/cloud-security.js +5 -51
  34. package/build/tools/compliance.d.ts.map +1 -1
  35. package/build/tools/compliance.js +9 -13
  36. package/build/tools/container-security.d.ts.map +1 -1
  37. package/build/tools/container-security.js +51 -52
  38. package/build/tools/deception.d.ts.map +1 -1
  39. package/build/tools/deception.js +8 -54
  40. package/build/tools/dns-security.d.ts.map +1 -1
  41. package/build/tools/dns-security.js +2 -48
  42. package/build/tools/encryption.d.ts.map +1 -1
  43. package/build/tools/encryption.js +86 -87
  44. package/build/tools/firewall.d.ts.map +1 -1
  45. package/build/tools/firewall.js +324 -30
  46. package/build/tools/hardening.d.ts.map +1 -1
  47. package/build/tools/hardening.js +12 -13
  48. package/build/tools/incident-response.d.ts.map +1 -1
  49. package/build/tools/incident-response.js +3 -3
  50. package/build/tools/logging.d.ts.map +1 -1
  51. package/build/tools/logging.js +17 -59
  52. package/build/tools/malware.js +2 -2
  53. package/build/tools/meta.d.ts.map +1 -1
  54. package/build/tools/meta.js +86 -165
  55. package/build/tools/network-defense.d.ts.map +1 -1
  56. package/build/tools/network-defense.js +3 -3
  57. package/build/tools/patch-management.js +8 -8
  58. package/build/tools/process-security.d.ts.map +1 -1
  59. package/build/tools/process-security.js +38 -92
  60. package/build/tools/sudo-management.js +36 -36
  61. package/build/tools/threat-intel.d.ts.map +1 -1
  62. package/build/tools/threat-intel.js +2 -48
  63. package/build/tools/vulnerability-management.d.ts.map +1 -1
  64. package/build/tools/vulnerability-management.js +3 -49
  65. package/build/tools/waf.d.ts.map +1 -1
  66. package/build/tools/waf.js +47 -93
  67. package/build/tools/wireless-security.d.ts.map +1 -1
  68. package/build/tools/wireless-security.js +9 -55
  69. package/package.json +4 -2
@@ -236,7 +236,7 @@ export class PreflightEngine {
236
236
  };
237
237
  result.summary = this.formatSummary(result);
238
238
  this.cacheResult(toolName, result);
239
- console.error(`[preflight] No manifest for '${toolName}' — skipping (${Date.now() - startTime}ms)`);
239
+ console.error(`[preflight] WARNING: No manifest for '${toolName}' — skipping (${Date.now() - startTime}ms)`);
240
240
  return result;
241
241
  }
242
242
  // ── Step 3–4: Use cached dep results or run fresh checks ───────────
@@ -305,7 +305,7 @@ export class PreflightEngine {
305
305
  `${safeguardResult.warnings.length} warning(s)`);
306
306
  }
307
307
  catch (err) {
308
- console.error(`[preflight] Safeguard check failed: ${err instanceof Error ? err.message : String(err)}`);
308
+ console.error(`[preflight] WARNING: Safeguard check failed: ${err instanceof Error ? err.message : String(err)}`);
309
309
  // Safeguard failure is non-blocking — log and continue
310
310
  }
311
311
  }
@@ -339,11 +339,11 @@ export class PreflightEngine {
339
339
  console.error(`[preflight] Dependencies: ${depCount} checked, ${missingCount} missing`);
340
340
  if (manifest.sudo !== "never") {
341
341
  const sudoStatus = privileges.satisfied
342
- ? "session active "
342
+ ? "session active OK"
343
343
  : `${privileges.issues.length} issue(s)`;
344
344
  console.error(`[preflight] Privileges: sudo ${manifest.sudo} — ${sudoStatus}`);
345
345
  }
346
- console.error(`[preflight] ${passed ? "" : ""} Pre-flight ${passed ? "passed" : "FAILED"} (${duration}ms)`);
346
+ console.error(`[preflight] ${passed ? "OK" : "FAIL"} Pre-flight ${passed ? "passed" : "FAILED"} (${duration}ms)`);
347
347
  return result;
348
348
  }
349
349
  // ── Individual check phases ────────────────────────────────────────────
@@ -497,7 +497,7 @@ export class PreflightEngine {
497
497
  *
498
498
  * @example Passing
499
499
  * ```
500
- * Pre-flight passed for 'firewall_iptables_list'
500
+ * PASS: Pre-flight passed for 'firewall_iptables_list'
501
501
  * Dependencies: 2/2 available (iptables, ip6tables)
502
502
  * Privileges: sudo session active
503
503
  * Ready to execute.
@@ -505,7 +505,7 @@ export class PreflightEngine {
505
505
  *
506
506
  * @example Failing
507
507
  * ```
508
- * Pre-flight FAILED for 'compliance_oscap_scan'
508
+ * Pre-flight FAILED for 'compliance_oscap_scan'
509
509
  * Missing dependencies:
510
510
  * • oscap (binary) — Install with: sudo apt-get install -y libopenscap8
511
511
  * Privilege issues:
@@ -522,7 +522,7 @@ export class PreflightEngine {
522
522
  const autoNote = autoInstalledCount > 0
523
523
  ? ` (auto-installed ${autoInstalledCount} ${autoInstalledCount === 1 ? "dependency" : "dependencies"})`
524
524
  : "";
525
- lines.push(`✅ Pre-flight passed for '${result.toolName}'${autoNote}`);
525
+ lines.push(`Pre-flight passed for '${result.toolName}'${autoNote}`);
526
526
  // Dependencies line
527
527
  const requiredChecks = result.dependencies.checked.filter((c) => c.required);
528
528
  const requiredFound = requiredChecks.filter((c) => c.found);
@@ -559,7 +559,7 @@ export class PreflightEngine {
559
559
  }
560
560
  else {
561
561
  // ── Failing summary ──────────────────────────────────────────────
562
- lines.push(`❌ Pre-flight FAILED for '${result.toolName}'`);
562
+ lines.push(`Pre-flight FAILED for '${result.toolName}'`);
563
563
  // Missing dependencies
564
564
  if (result.dependencies.missing.length > 0) {
565
565
  lines.push(" Missing dependencies:");
@@ -583,7 +583,7 @@ export class PreflightEngine {
583
583
  result.safeguards.blockers.length > 0) {
584
584
  lines.push(" Safety blockers:");
585
585
  for (const blocker of result.safeguards.blockers) {
586
- lines.push(` 🛑 ${blocker}`);
586
+ lines.push(` ${blocker}`);
587
587
  }
588
588
  }
589
589
  lines.push(" Cannot proceed until issues are resolved.");
@@ -593,8 +593,8 @@ export class PreflightEngine {
593
593
  /**
594
594
  * Generate a shorter status message for prepending to tool output.
595
595
  *
596
- * - Passed (no issues): `"[pre-flight ] All checks passed (2 deps, sudo active)"`
597
- * - Passed (warnings): `"[pre-flight ] Passed with warnings: optional dep 'nmap' not found"`
596
+ * - Passed (no issues): `"[pre-flight OK] All checks passed (2 deps, sudo active)"`
597
+ * - Passed (warnings): `"[pre-flight OK] Passed with warnings: optional dep 'nmap' not found"`
598
598
  * - Failed: returns the full error summary from {@link formatSummary}
599
599
  */
600
600
  formatStatusMessage(result) {
@@ -614,9 +614,9 @@ export class PreflightEngine {
614
614
  const missingNames = optionalMissing
615
615
  .map((c) => c.name)
616
616
  .join("', '");
617
- return `[pre-flight ] Passed with warnings: optional dep '${missingNames}' not found`;
617
+ return `[pre-flight OK] Passed with warnings: optional dep '${missingNames}' not found`;
618
618
  }
619
- return `[pre-flight ] All checks passed (${depCount} deps${privStatus})`;
619
+ return `[pre-flight OK] All checks passed (${depCount} deps${privStatus})`;
620
620
  }
621
621
  // ── Cache management ───────────────────────────────────────────────────
622
622
  /**
@@ -36,10 +36,10 @@ export function renderProgressBar(percent, options) {
36
36
  }
37
37
  // ── Complexity Badges ────────────────────────────────────────────────────────
38
38
  const COMPLEXITY_BADGES = {
39
- low: "LOW",
40
- medium: "📊 MEDIUM",
41
- high: "🔥 HIGH",
42
- critical: "⚠️ CRITICAL",
39
+ low: "LOW",
40
+ medium: "MEDIUM",
41
+ high: "HIGH",
42
+ critical: "WARNING: CRITICAL",
43
43
  };
44
44
  // ── Pre-Execution Banner ─────────────────────────────────────────────────────
45
45
  /**
@@ -58,28 +58,28 @@ export function generateDurationBanner(toolName, action, timeoutMs) {
58
58
  const estimate = getDurationEstimate(toolName, action);
59
59
  if (!estimate) {
60
60
  // No estimate available — return minimal info
61
- return `⏱️ Timeout: ${formatElapsed(timeoutMs)}\n`;
61
+ return `Timeout: ${formatElapsed(timeoutMs)}\n`;
62
62
  }
63
63
  const durationStr = formatDurationEstimate(estimate);
64
64
  const complexityBadge = COMPLEXITY_BADGES[estimate.complexity];
65
65
  const longRunning = isLongRunning(toolName, action);
66
66
  if (!longRunning) {
67
67
  // Quick tool — compact one-liner
68
- return `⏱️ Est: ${durationStr} | ${complexityBadge}\n`;
68
+ return `Est: ${durationStr} | ${complexityBadge}\n`;
69
69
  }
70
70
  // Long-running tool — detailed banner
71
71
  const lines = [];
72
72
  lines.push("┌─────────────────────────────────────────────────────────┐");
73
- lines.push(`│ 📋 ${estimate.description.padEnd(54)}│`);
73
+ lines.push(`│ ${estimate.description.padEnd(54)}│`);
74
74
  lines.push("├─────────────────────────────────────────────────────────┤");
75
- lines.push(`│ ⏱️ Duration estimate: ${durationStr.padEnd(36)}│`);
76
- lines.push(`│ 📊 Complexity: ${complexityBadge.padEnd(42)}│`);
77
- lines.push(`│ 🔧 Timeout: ${formatElapsed(timeoutMs).padEnd(45)}│`);
75
+ lines.push(`│ Duration estimate: ${durationStr.padEnd(36)}│`);
76
+ lines.push(`│ Complexity: ${complexityBadge.padEnd(42)}│`);
77
+ lines.push(`│ Timeout: ${formatElapsed(timeoutMs).padEnd(45)}│`);
78
78
  if (estimate.supportsProgress) {
79
- lines.push(`│ 📈 Progress tracking: enabled${" ".repeat(28)}│`);
79
+ lines.push(`│ Progress tracking: enabled${" ".repeat(28)}│`);
80
80
  }
81
81
  if (estimate.durationFactors.length > 0) {
82
- lines.push(`│ 📎 Factors: ${estimate.durationFactors.slice(0, 2).join(", ").padEnd(45)}│`);
82
+ lines.push(`│ Factors: ${estimate.durationFactors.slice(0, 2).join(", ").padEnd(45)}│`);
83
83
  }
84
84
  lines.push("└─────────────────────────────────────────────────────────┘");
85
85
  return lines.join("\n") + "\n\n";
@@ -100,22 +100,22 @@ export function generateTimingSummary(toolName, action, actualMs) {
100
100
  const estimate = getDurationEstimate(toolName, action);
101
101
  const actualStr = formatElapsed(actualMs);
102
102
  if (!estimate) {
103
- return `\n⏱️ Completed in ${actualStr}`;
103
+ return `\nCompleted in ${actualStr}`;
104
104
  }
105
105
  const minMs = estimate.minSeconds * 1000;
106
106
  const maxMs = estimate.maxSeconds * 1000;
107
107
  const estimatedStr = formatDurationEstimate(estimate);
108
108
  let indicator;
109
109
  if (actualMs < minMs) {
110
- indicator = "Faster than expected";
110
+ indicator = "Faster than expected";
111
111
  }
112
112
  else if (actualMs > maxMs) {
113
- indicator = "🐢 Slower than expected";
113
+ indicator = "Slower than expected";
114
114
  }
115
115
  else {
116
- indicator = " Within estimate";
116
+ indicator = "PASS: Within estimate";
117
117
  }
118
- return `\n⏱️ Completed in ${actualStr} (est: ${estimatedStr}) — ${indicator}`;
118
+ return `\nCompleted in ${actualStr} (est: ${estimatedStr}) — ${indicator}`;
119
119
  }
120
120
  // ── Timing Context Helpers ───────────────────────────────────────────────────
121
121
  /**
@@ -181,12 +181,12 @@ export function generatePhaseBanner(phaseName, phaseNumber, tools) {
181
181
  const totalMinMin = Math.ceil(totalMinSec / 60);
182
182
  const totalMaxMin = Math.ceil(totalMaxSec / 60);
183
183
  const avgMin = Math.ceil((totalMinMin + totalMaxMin) / 2);
184
- lines.push(`║ 📊 TOTAL ESTIMATE: ${totalMinMin}-${totalMaxMin} min (avg: ~${avgMin} min)${" ".repeat(Math.max(0, 20 - String(totalMaxMin).length - String(avgMin).length))}║`);
184
+ lines.push(`║ TOTAL ESTIMATE: ${totalMinMin}-${totalMaxMin} min (avg: ~${avgMin} min)${" ".repeat(Math.max(0, 20 - String(totalMaxMin).length - String(avgMin).length))}║`);
185
185
  }
186
186
  if (hasLongRunning) {
187
- lines.push("║ ⚠️ Long-running phase — progress updates included ║");
187
+ lines.push("║ WARNING: Long-running phase — progress updates included ║");
188
188
  }
189
- lines.push("║ All tools will run to completion (no timeouts) ║");
189
+ lines.push("║ OK All tools will run to completion (no timeouts) ║");
190
190
  lines.push("║ ║");
191
191
  lines.push("╚════════════════════════════════════════════════════════════╝");
192
192
  return lines.join("\n");
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Shared async command runner using spawnSafe.
3
+ *
4
+ * Replaces the duplicate runCommand() implementations found in 11 tool files.
5
+ * Wraps spawnSafe in a Promise that collects stdout/stderr and handles
6
+ * timeouts, spawn errors, and process errors gracefully.
7
+ */
8
+ export interface CommandResult {
9
+ stdout: string;
10
+ stderr: string;
11
+ exitCode: number;
12
+ }
13
+ export declare function runCommand(command: string, args: string[], timeoutMs?: number): Promise<CommandResult>;
14
+ //# sourceMappingURL=run-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-command.d.ts","sourceRoot":"","sources":["../../src/core/run-command.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,SAAS,GACjB,OAAO,CAAC,aAAa,CAAC,CAyCxB"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Shared async command runner using spawnSafe.
3
+ *
4
+ * Replaces the duplicate runCommand() implementations found in 11 tool files.
5
+ * Wraps spawnSafe in a Promise that collects stdout/stderr and handles
6
+ * timeouts, spawn errors, and process errors gracefully.
7
+ */
8
+ import { spawnSafe } from "./spawn-safe.js";
9
+ export async function runCommand(command, args, timeoutMs = 30_000) {
10
+ return new Promise((resolve) => {
11
+ let child;
12
+ try {
13
+ child = spawnSafe(command, args);
14
+ }
15
+ catch (err) {
16
+ resolve({ stdout: "", stderr: err instanceof Error ? err.message : String(err), exitCode: -1 });
17
+ return;
18
+ }
19
+ let stdout = "";
20
+ let stderr = "";
21
+ let resolved = false;
22
+ const timer = setTimeout(() => {
23
+ if (!resolved) {
24
+ resolved = true;
25
+ child.kill("SIGTERM");
26
+ resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
27
+ }
28
+ }, timeoutMs);
29
+ child.stdout?.on("data", (data) => { stdout += data.toString(); });
30
+ child.stderr?.on("data", (data) => { stderr += data.toString(); });
31
+ child.on("close", (code) => {
32
+ if (!resolved) {
33
+ resolved = true;
34
+ clearTimeout(timer);
35
+ resolve({ stdout, stderr, exitCode: code ?? -1 });
36
+ }
37
+ });
38
+ child.on("error", (err) => {
39
+ if (!resolved) {
40
+ resolved = true;
41
+ clearTimeout(timer);
42
+ resolve({ stdout, stderr: err.message, exitCode: -1 });
43
+ }
44
+ });
45
+ });
46
+ }
@@ -13,11 +13,11 @@
13
13
  * 2. shell: false always
14
14
  * 3. Audit logging to stderr
15
15
  */
16
- import { type SpawnOptions, type ExecFileSyncOptions, type ChildProcess } from "node:child_process";
17
- export interface SpawnSafeOptions extends SpawnOptions {
18
- }
19
- export interface ExecFileSafeOptions extends ExecFileSyncOptions {
20
- }
16
+ import { type SpawnOptions, type ExecFileSyncOptions, type ChildProcess as NodeChildProcess } from "node:child_process";
17
+ /** Re-export ChildProcess type so tool files can import it without touching node:child_process directly. */
18
+ export type { NodeChildProcess as ChildProcess };
19
+ export type SpawnSafeOptions = SpawnOptions;
20
+ export type ExecFileSafeOptions = ExecFileSyncOptions;
21
21
  /**
22
22
  * Redact sensitive arguments from a command's argument array for safe logging.
23
23
  *
@@ -41,7 +41,7 @@ export declare function redactArgs(command: string, args: readonly string[]): st
41
41
  * @param options - SpawnOptions (shell is always forced to false)
42
42
  * @throws {Error} If the command is not in the allowlist
43
43
  */
44
- export declare function spawnSafe(command: string, args: string[], options?: SpawnSafeOptions): ChildProcess;
44
+ export declare function spawnSafe(command: string, args: string[], options?: SpawnSafeOptions): NodeChildProcess;
45
45
  /**
46
46
  * Execute a file synchronously with allowlist enforcement and shell: false.
47
47
  *
@@ -1 +1 @@
1
- {"version":3,"file":"spawn-safe.d.ts","sourceRoot":"","sources":["../../src/core/spawn-safe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAK5B,MAAM,WAAW,gBAAiB,SAAQ,YAAY;CAAG;AAEzD,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB;CAAG;AAUnE;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAgC7E;AAID;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,gBAAgB,GACzB,YAAY,CAUd;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,GAAG,MAAM,CAiCjB"}
1
+ {"version":3,"file":"spawn-safe.d.ts","sourceRoot":"","sources":["../../src/core/spawn-safe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,YAAY,IAAI,gBAAgB,EACtC,MAAM,oBAAoB,CAAC;AAE5B,4GAA4G;AAC5G,YAAY,EAAE,gBAAgB,IAAI,YAAY,EAAE,CAAC;AAKjD,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAE5C,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AAUtD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAgC7E;AAID;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CAUlB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,GAAG,MAAM,CAiCjB"}
@@ -188,14 +188,14 @@ export class SudoGuard {
188
188
  const reasonText = reason ?? "This tool requires elevated (root) privileges to function.";
189
189
  // Build the prompt message for AI clients.
190
190
  const lines = [];
191
- lines.push("🔒 Sudo session required");
192
- lines.push("".repeat(50));
191
+ lines.push("Sudo session required");
192
+ lines.push("");
193
193
  lines.push("");
194
194
  lines.push(`Tool: ${toolName}`);
195
195
  lines.push(`Reason: ${reasonText}`);
196
196
  lines.push("");
197
197
  if (status.elevated && status.remainingSeconds !== null && status.remainingSeconds <= 0) {
198
- lines.push("⚠️ Your sudo session has expired.");
198
+ lines.push("WARNING: Your sudo session has expired.");
199
199
  lines.push("");
200
200
  lines.push("ACTION: Call sudo_session with action=elevate_gui to re-authenticate,");
201
201
  lines.push("or action=extend to extend an active session.");
@@ -210,7 +210,7 @@ export class SudoGuard {
210
210
  lines.push("stored in a zeroable memory buffer, and never visible to the AI.");
211
211
  if (originalError) {
212
212
  lines.push("");
213
- lines.push("".repeat(50));
213
+ lines.push("");
214
214
  lines.push("Original error:");
215
215
  lines.push(originalError.substring(0, 500));
216
216
  }
@@ -471,7 +471,7 @@ async function installGithubRelease(entry) {
471
471
  dryRun: false,
472
472
  success: true,
473
473
  }));
474
- console.error(`[third-party-installer] ${entry.name} v${entry.version} installed successfully`);
474
+ console.error(`[third-party-installer] OK ${entry.name} v${entry.version} installed successfully`);
475
475
  return {
476
476
  binary: entry.binary,
477
477
  success: true,
@@ -585,7 +585,7 @@ async function installAptRepo(entry) {
585
585
  console.error(`[third-party-installer] Running apt-get update...`);
586
586
  const updateResult = execWithSudo(["apt-get", "update"], { timeoutMs: 120_000 });
587
587
  if (!updateResult.success) {
588
- console.error(`[third-party-installer] apt-get update had issues: ${updateResult.stderr.slice(0, 200)}`);
588
+ console.error(`[third-party-installer] WARNING: apt-get update had issues: ${updateResult.stderr.slice(0, 200)}`);
589
589
  }
590
590
  const packages = entry.aptPinnedPackages ?? [entry.binary];
591
591
  for (const pkg of packages) {
@@ -609,7 +609,7 @@ async function installAptRepo(entry) {
609
609
  dryRun: false,
610
610
  success: true,
611
611
  }));
612
- console.error(`[third-party-installer] ${entry.name} v${entry.version} installed successfully via APT`);
612
+ console.error(`[third-party-installer] OK ${entry.name} v${entry.version} installed successfully via APT`);
613
613
  return {
614
614
  binary: entry.binary,
615
615
  success: true,
@@ -658,7 +658,7 @@ async function installNpmLocal(entry) {
658
658
  dryRun: false,
659
659
  success: true,
660
660
  }));
661
- console.error(`[third-party-installer] ${entry.name} v${entry.version} installed via npm`);
661
+ console.error(`[third-party-installer] OK ${entry.name} v${entry.version} installed via npm`);
662
662
  return {
663
663
  binary: entry.binary,
664
664
  success: true,
@@ -195,7 +195,7 @@ function createWrappedHandler(toolName, originalHandler, ctx) {
195
195
  content: [
196
196
  {
197
197
  type: "text",
198
- text: `⚠ Rate limit exceeded\n\n${rateLimitResult.reason}`,
198
+ text: `WARNING: Rate limit exceeded\n\n${rateLimitResult.reason}`,
199
199
  },
200
200
  ],
201
201
  isError: true,
@@ -269,12 +269,12 @@ function createWrappedHandler(toolName, originalHandler, ctx) {
269
269
  catch (err) {
270
270
  // Pre-flight itself threw — return error instead of running without dependency checking
271
271
  const errMsg = err instanceof Error ? err.message : String(err);
272
- console.error(`[preflight] Pre-flight failed unexpectedly for '${toolName}': ${errMsg}`);
272
+ console.error(`[preflight] WARNING: Pre-flight failed unexpectedly for '${toolName}': ${errMsg}`);
273
273
  return {
274
274
  content: [
275
275
  {
276
276
  type: "text",
277
- text: `⚠ Pre-flight internal error for '${toolName}'\n\nThe pre-flight dependency checking system encountered an unexpected error and the tool was not executed.\n\nError: ${errMsg}\n\nPlease retry the operation or check the pre-flight configuration. If this persists, the tool's dependency manifest may need attention.`,
277
+ text: `WARNING: Pre-flight internal error for '${toolName}'\n\nThe pre-flight dependency checking system encountered an unexpected error and the tool was not executed.\n\nError: ${errMsg}\n\nPlease retry the operation or check the pre-flight configuration. If this persists, the tool's dependency manifest may need attention.`,
278
278
  },
279
279
  ],
280
280
  isError: true,
@@ -879,8 +879,8 @@ export function registerAccessControlTools(server) {
879
879
  ? "CRITICAL: Weak SSH algorithms detected. Apply Mozilla Modern SSH configuration immediately."
880
880
  : warnCount > 0
881
881
  ? "WARNING: SSH algorithms not explicitly configured. Set explicit algorithms in sshd_config."
882
- : "PASS: SSH cryptographic configuration meets modern standards.",
883
- }, null, 2))],
882
+ : "SSH cryptographic configuration meets modern standards.",
883
+ }))],
884
884
  };
885
885
  }
886
886
  catch (error) {
@@ -1154,7 +1154,7 @@ export function registerAccessControlTools(server) {
1154
1154
  content: [
1155
1155
  createTextContent(`[DRY-RUN] Would write the following to ${targetFile}:\n\n` +
1156
1156
  configLines.map((l) => ` ${l}`).join("\n") +
1157
- (pwqSanityWarnings ? `\n\n⚠️ Sanity warnings:\n${pwqSanityWarnings}` : "")),
1157
+ (pwqSanityWarnings ? `\n\nWARNING: Sanity warnings:\n${pwqSanityWarnings}` : "")),
1158
1158
  ],
1159
1159
  };
1160
1160
  }
@@ -1200,7 +1200,7 @@ export function registerAccessControlTools(server) {
1200
1200
  content: [
1201
1201
  createTextContent(`pam_pwquality configured in ${targetFile}:\n\n` +
1202
1202
  configLines.map((l) => ` ${l}`).join("\n") +
1203
- (pwqSanityWarnings ? `\n\n⚠️ Sanity warnings:\n${pwqSanityWarnings}` : "")),
1203
+ (pwqSanityWarnings ? `\n\nWARNING: Sanity warnings:\n${pwqSanityWarnings}` : "")),
1204
1204
  ],
1205
1205
  };
1206
1206
  }
@@ -1276,7 +1276,7 @@ export function registerAccessControlTools(server) {
1276
1276
  createTextContent(`[DRY-RUN] Would add/update pam_faillock.so in ${targetFile}:\n\n` +
1277
1277
  ` ${preLine}\n ${authLine}\n\n` +
1278
1278
  `Settings: ${JSON.stringify(merged)}` +
1279
- (flSanityWarnings ? `\n\n⚠️ Sanity warnings:\n${flSanityWarnings}` : "")),
1279
+ (flSanityWarnings ? `\n\nWARNING: Sanity warnings:\n${flSanityWarnings}` : "")),
1280
1280
  ],
1281
1281
  };
1282
1282
  }
@@ -1332,7 +1332,7 @@ export function registerAccessControlTools(server) {
1332
1332
  ` ${preLine}\n ${authLine}\n\n` +
1333
1333
  `Settings: ${JSON.stringify(merged)}\n` +
1334
1334
  `Backup: ${backupEntry.backupPath}` +
1335
- (flSanityWarnings ? `\n\n⚠️ Sanity warnings:\n${flSanityWarnings}` : "")),
1335
+ (flSanityWarnings ? `\n\nWARNING: Sanity warnings:\n${flSanityWarnings}` : "")),
1336
1336
  ],
1337
1337
  };
1338
1338
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api-security.d.ts","sourceRoot":"","sources":["../../src/tools/api-security.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAy1BpE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6WhE"}
1
+ {"version":3,"file":"api-security.d.ts","sourceRoot":"","sources":["../../src/tools/api-security.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2xBpE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6WhE"}
@@ -8,7 +8,7 @@
8
8
  * verification, TLS configuration checking, and CORS policy analysis.
9
9
  */
10
10
  import { z } from "zod";
11
- import { spawnSafe } from "../core/spawn-safe.js";
11
+ import { runCommand } from "../core/run-command.js";
12
12
  import { createTextContent, createErrorContent, formatToolOutput, } from "../core/parsers.js";
13
13
  // ── Constants ──────────────────────────────────────────────────────────────────
14
14
  const DEFAULT_PORT_RANGE = "80,443,3000,4000,5000,8000,8080,8443,9000";
@@ -21,53 +21,7 @@ const COMMON_API_PATHS = [
21
21
  "/openapi.json",
22
22
  "/.well-known/openid-configuration",
23
23
  ];
24
- /**
25
- * Run a command via spawnSafe and collect output as a promise.
26
- * Handles errors gracefully — returns error info instead of throwing.
27
- */
28
- async function runCommand(command, args, timeoutMs = 30_000) {
29
- return new Promise((resolve) => {
30
- let child;
31
- try {
32
- child = spawnSafe(command, args);
33
- }
34
- catch (err) {
35
- const msg = err instanceof Error ? err.message : String(err);
36
- resolve({ stdout: "", stderr: msg, exitCode: -1 });
37
- return;
38
- }
39
- let stdout = "";
40
- let stderr = "";
41
- let resolved = false;
42
- const timer = setTimeout(() => {
43
- if (!resolved) {
44
- resolved = true;
45
- child.kill("SIGTERM");
46
- resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
47
- }
48
- }, timeoutMs);
49
- child.stdout?.on("data", (data) => {
50
- stdout += data.toString();
51
- });
52
- child.stderr?.on("data", (data) => {
53
- stderr += data.toString();
54
- });
55
- child.on("close", (code) => {
56
- if (!resolved) {
57
- resolved = true;
58
- clearTimeout(timer);
59
- resolve({ stdout, stderr, exitCode: code ?? -1 });
60
- }
61
- });
62
- child.on("error", (err) => {
63
- if (!resolved) {
64
- resolved = true;
65
- clearTimeout(timer);
66
- resolve({ stdout, stderr: err.message, exitCode: -1 });
67
- }
68
- });
69
- });
70
- }
24
+ // ── Helpers ────────────────────────────────────────────────────────────────────
71
25
  /**
72
26
  * Validate and normalize a target URL.
73
27
  * Returns the normalized URL or null if invalid.
@@ -692,7 +646,7 @@ export function registerApiSecurityTools(server) {
692
646
  text += `Auth Type: ${authResult.authType}\n`;
693
647
  text += `Status without auth: ${authResult.statusWithoutAuth}\n`;
694
648
  text += `Status with auth: ${authResult.statusWithAuth}\n`;
695
- text += `Verbose Errors: ${authResult.verboseErrors ? "YES " : "no"}\n`;
649
+ text += `Verbose Errors: ${authResult.verboseErrors ? "YES WARNING" : "no"}\n`;
696
650
  if (authResult.errorDetails.length > 0) {
697
651
  text += `\nError Details:\n`;
698
652
  for (const detail of authResult.errorDetails) {
@@ -864,8 +818,8 @@ export function registerApiSecurityTools(server) {
864
818
  text += `Allow-Origin: ${corsResult.allowOrigin}\n`;
865
819
  text += `Allow-Credentials: ${corsResult.allowCredentials}\n`;
866
820
  text += `Allow-Methods: ${corsResult.allowMethods || "not specified"}\n`;
867
- text += `Wildcard Origin: ${corsResult.wildcardOrigin ? "YES " : "no"}\n`;
868
- text += `Origin Reflection: ${corsResult.originReflection ? "YES " : "no"}\n`;
821
+ text += `Wildcard Origin: ${corsResult.wildcardOrigin ? "YES WARNING" : "no"}\n`;
822
+ text += `Origin Reflection: ${corsResult.originReflection ? "YES WARNING" : "no"}\n`;
869
823
  }
870
824
  if (corsResult.criticalIssues.length > 0) {
871
825
  text += `\nCritical Issues:\n`;
@@ -1 +1 @@
1
- {"version":3,"file":"app-hardening.d.ts","sourceRoot":"","sources":["../../src/tools/app-hardening.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6dpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgYjE"}
1
+ {"version":3,"file":"app-hardening.d.ts","sourceRoot":"","sources":["../../src/tools/app-hardening.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6dpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6XjE"}