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
@@ -86,8 +86,8 @@ export function registerContainerSecurityTools(server) {
86
86
  const { check_type } = params;
87
87
  try {
88
88
  const sections = [];
89
- sections.push("🐳 Docker Security Audit");
90
- sections.push("=".repeat(50));
89
+ sections.push("Docker Security Audit");
90
+ sections.push("");
91
91
  const findings = [];
92
92
  const dockerCheck = await executeCommand({ command: "which", args: ["docker"], toolName: "container_docker", timeout: 5000 });
93
93
  if (dockerCheck.exitCode !== 0) {
@@ -199,14 +199,13 @@ export function registerContainerSecurityTools(server) {
199
199
  }
200
200
  sections.push("\n── Security Findings Summary ──");
201
201
  if (findings.length === 0) {
202
- sections.push(" āœ… No significant security issues found.");
202
+ sections.push(" No significant security issues found.");
203
203
  }
204
204
  else {
205
205
  for (const lvl of ["CRITICAL", "WARNING", "INFO"]) {
206
206
  const items = findings.filter((f) => f.level === lvl);
207
207
  if (items.length > 0) {
208
- const icon = lvl === "CRITICAL" ? "ā›”" : lvl === "WARNING" ? "āš ļø" : "ā„¹ļø";
209
- sections.push(`\n ${icon} ${lvl} (${items.length}):`);
208
+ sections.push(`\n ${lvl} (${items.length}):`);
210
209
  for (const f of items)
211
210
  sections.push(` - ${f.msg}`);
212
211
  }
@@ -222,7 +221,7 @@ export function registerContainerSecurityTools(server) {
222
221
  case "bench": {
223
222
  const { checks: benchChecks, log_level } = params;
224
223
  try {
225
- const sections = ["šŸ”’ Docker Bench for Security", "=".repeat(50)];
224
+ const sections = ["Docker Bench for Security", ""];
226
225
  const dockerCheck = await executeCommand({ command: "which", args: ["docker"], toolName: "container_docker", timeout: 5000 });
227
226
  if (dockerCheck.exitCode !== 0)
228
227
  return { content: [createTextContent("Docker is not installed.")] };
@@ -234,7 +233,7 @@ export function registerContainerSecurityTools(server) {
234
233
  const result = await executeCommand({ command: "docker", args: benchArgs, toolName: "container_docker", timeout: 300000 });
235
234
  const output = result.stdout || result.stderr;
236
235
  if (result.exitCode !== 0 && !output) {
237
- sections.push("āš ļø Docker Bench could not run.");
236
+ sections.push("Docker Bench could not run.");
238
237
  return { content: [createTextContent(sections.join("\n"))] };
239
238
  }
240
239
  const levelPriority = { PASS: 0, INFO: 1, NOTE: 2, WARN: 3 };
@@ -296,7 +295,7 @@ export function registerContainerSecurityTools(server) {
296
295
  note: unconfined ? "seccomp explicitly disabled — HIGH RISK" : !hasSeccomp && secOpt === "[]" ? "Using Docker default seccomp (acceptable)" : "seccomp configured",
297
296
  });
298
297
  }
299
- return { content: [createTextContent(JSON.stringify({ summary: { total: results.length, pass: results.filter(r => r.status === "PASS").length, warn: results.filter(r => r.status === "WARN").length, fail: results.filter(r => r.status === "FAIL").length }, containers: results }, null, 2))] };
298
+ return { content: [createTextContent(JSON.stringify({ summary: { total: results.length, pass: results.filter(r => r.status === "PASS").length, warn: results.filter(r => r.status === "WARN").length, fail: results.filter(r => r.status === "FAIL").length }, containers: results }))] };
300
299
  }
301
300
  catch (error) {
302
301
  return { content: [createErrorContent(error instanceof Error ? error.message : String(error))], isError: true };
@@ -308,13 +307,13 @@ export function registerContainerSecurityTools(server) {
308
307
  try {
309
308
  if (!daemon_action)
310
309
  return { content: [createErrorContent("daemon_action is required (audit or apply)")], isError: true };
311
- const sections = ["🐳 Docker Daemon Configuration", "=".repeat(50)];
310
+ const sections = ["Docker Daemon Configuration", ""];
312
311
  const daemonPath = "/etc/docker/daemon.json";
313
312
  const readResult = await executeCommand({ command: "cat", args: [daemonPath], toolName: "container_docker", timeout: 5000 });
314
313
  const existingConfig = readResult.exitCode === 0 ? parseJsonSafe(readResult.stdout) || {} : {};
315
314
  if (daemon_action === "audit") {
316
315
  if (readResult.exitCode !== 0)
317
- sections.push(" āš ļø No /etc/docker/daemon.json found");
316
+ sections.push(" No /etc/docker/daemon.json found");
318
317
  else
319
318
  sections.push(` ${JSON.stringify(existingConfig, null, 4).replace(/\n/g, "\n ")}`);
320
319
  sections.push("\n── Security Settings Audit ──");
@@ -331,7 +330,7 @@ export function registerContainerSecurityTools(server) {
331
330
  for (const c of checks) {
332
331
  if (!c.present)
333
332
  missingCount++;
334
- sections.push(` ${c.present ? "āœ… Present" : "āŒ Missing"}: ${c.key} [${c.severity}]`);
333
+ sections.push(` ${c.present ? "Present" : "Missing"}: ${c.key} [${c.severity}]`);
335
334
  }
336
335
  sections.push(`\n Summary: ${checks.length - missingCount}/${checks.length} configured`);
337
336
  return { content: [createTextContent(sections.join("\n"))] };
@@ -402,13 +401,13 @@ export function registerContainerSecurityTools(server) {
402
401
  else {
403
402
  if (readResult.exitCode === 0) {
404
403
  await backupFile(daemonPath);
405
- sections.push(`\n āœ… Backed up ${daemonPath}`);
404
+ sections.push(`\n Backed up ${daemonPath}`);
406
405
  }
407
406
  const writeResult = await executeCommand({ command: "sudo", args: ["tee", daemonPath], stdin: newJson, toolName: "container_docker", timeout: 10000 });
408
407
  if (writeResult.exitCode !== 0)
409
408
  return { content: [createErrorContent(`Failed to write ${daemonPath}: ${writeResult.stderr}`)], isError: true };
410
- sections.push(` āœ… Written to ${daemonPath}`);
411
- sections.push("\n āš ļø Restart Docker: sudo systemctl restart docker");
409
+ sections.push(` Written to ${daemonPath}`);
410
+ sections.push("\n Restart Docker: sudo systemctl restart docker");
412
411
  logChange(createChangeEntry({ tool: "container_docker", action: "apply daemon config", target: daemonPath, before: JSON.stringify(existingConfig), after: newJson, dryRun: false, success: true }));
413
412
  }
414
413
  return { content: [createTextContent(sections.join("\n"))] };
@@ -434,7 +433,7 @@ export function registerContainerSecurityTools(server) {
434
433
  const grypeResult = await executeCommand({ command: "grype", args: [image, "-o", "json"], timeout: 300000, toolName: "container_docker" });
435
434
  if (grypeResult.exitCode === 0)
436
435
  return { content: [createTextContent(`Grype scan results for ${image}:\n${grypeResult.stdout.substring(0, 8000)}`)] };
437
- return { content: [createTextContent(JSON.stringify({ error: "Neither Trivy nor Grype is installed", recommendation: "Install Trivy or Grype" }, null, 2))] };
436
+ return { content: [createTextContent(JSON.stringify({ error: "Neither Trivy nor Grype is installed", recommendation: "Install Trivy or Grype" }))] };
438
437
  }
439
438
  catch (error) {
440
439
  return { content: [createErrorContent(error instanceof Error ? error.message : String(error))], isError: true };
@@ -493,7 +492,7 @@ export function registerContainerSecurityTools(server) {
493
492
  // ── apparmor_status ─────────────────────────────────────────
494
493
  case "apparmor_status": {
495
494
  try {
496
- const sections = ["šŸ›”ļø AppArmor System Status", "=".repeat(40)];
495
+ const sections = ["AppArmor System Status", ""];
497
496
  const enabledResult = await executeCommand({ command: "aa-enabled", args: [], toolName: "container_isolation", timeout: 5000 });
498
497
  const aaEnabledBin = enabledResult.exitCode === 0 && enabledResult.stdout.trim() === "Yes";
499
498
  const moduleResult = await executeCommand({ command: "cat", args: ["/sys/module/apparmor/parameters/enabled"], toolName: "container_isolation", timeout: 5000 });
@@ -503,15 +502,15 @@ export function registerContainerSecurityTools(server) {
503
502
  const svcActive = svcResult.exitCode === 0 && svcResult.stdout.trim() === "active";
504
503
  // AppArmor is considered enabled if aa-enabled says "Yes", OR if kernel module is loaded AND service is active
505
504
  const aaEnabled = aaEnabledBin || (kernelModuleLoaded && svcActive);
506
- sections.push(`\n AppArmor enabled: ${aaEnabled ? "āœ… Yes" : "āŒ No"}`);
505
+ sections.push(`\n AppArmor enabled: ${aaEnabled ? "Yes" : "No"}`);
507
506
  if (moduleResult.exitCode === 0)
508
- sections.push(` Kernel module: ${kernelModuleLoaded ? "āœ… Loaded" : "āŒ Not loaded"}`);
507
+ sections.push(` Kernel module: ${kernelModuleLoaded ? "Loaded" : "Not loaded"}`);
509
508
  const pkgChecks = ["apparmor-profiles", "apparmor-profiles-extra", "apparmor-utils"];
510
509
  sections.push("\n Profile Packages:");
511
510
  for (const pkg of pkgChecks) {
512
511
  const dpkgResult = await executeCommand({ command: "dpkg", args: ["-s", pkg], toolName: "container_isolation", timeout: 5000 });
513
512
  const installed = dpkgResult.exitCode === 0 && dpkgResult.stdout.includes("Status: install ok installed");
514
- sections.push(` ${pkg}: ${installed ? "āœ… Installed" : "āŒ Not installed"}`);
513
+ sections.push(` ${pkg}: ${installed ? "Installed" : "Not installed"}`);
515
514
  }
516
515
  return { content: [createTextContent(sections.join("\n"))] };
517
516
  }
@@ -522,12 +521,12 @@ export function registerContainerSecurityTools(server) {
522
521
  // ── apparmor_list ───────────────────────────────────────────
523
522
  case "apparmor_list": {
524
523
  try {
525
- const sections = ["šŸ›”ļø AppArmor Profiles", "=".repeat(40)];
524
+ const sections = ["AppArmor Profiles", ""];
526
525
  let result = await executeCommand({ command: "sudo", args: ["aa-status"], toolName: "container_isolation", timeout: getToolTimeout("container_apparmor_manage") });
527
526
  if (result.exitCode !== 0)
528
527
  result = await executeCommand({ command: "sudo", args: ["apparmor_status"], toolName: "container_isolation", timeout: getToolTimeout("container_apparmor_manage") });
529
528
  if (result.exitCode !== 0) {
530
- sections.push("\nāš ļø Cannot list AppArmor profiles.");
529
+ sections.push("\nCannot list AppArmor profiles.");
531
530
  return { content: [createTextContent(sections.join("\n"))] };
532
531
  }
533
532
  const output = result.stdout;
@@ -537,15 +536,15 @@ export function registerContainerSecurityTools(server) {
537
536
  const trimmed = line.trim();
538
537
  if (trimmed.includes("enforce mode")) {
539
538
  currentSection = "enforce";
540
- sections.push("\n šŸ”’ Enforce Mode:");
539
+ sections.push("\n Enforce Mode:");
541
540
  }
542
541
  else if (trimmed.includes("complain mode")) {
543
542
  currentSection = "complain";
544
- sections.push("\n šŸ“ Complain Mode:");
543
+ sections.push("\n Complain Mode:");
545
544
  }
546
545
  else if (trimmed.includes("unconfined")) {
547
546
  currentSection = "unconfined";
548
- sections.push("\n āš ļø Unconfined:");
547
+ sections.push("\n Unconfined:");
549
548
  }
550
549
  else if (currentSection && trimmed && !trimmed.match(/^\d+\s+processes?/)) {
551
550
  sections.push(` ${trimmed}`);
@@ -574,7 +573,7 @@ export function registerContainerSecurityTools(server) {
574
573
  const isDesktopProfile = DESKTOP_BREAKING_PROFILES.has(profileBaseName);
575
574
  // SAFETY: Warn when enforcing profiles known to break desktop apps
576
575
  const desktopWarning = (baseAction === "enforce" && isDesktopProfile)
577
- ? `\n\nāš ļø WARNING: Profile '${profileBaseName}' is known to break desktop applications ` +
576
+ ? `\n\nWARNING: Profile '${profileBaseName}' is known to break desktop applications ` +
578
577
  `(flatpak, browsers, GUI apps) when enforced.\n` +
579
578
  `These profiles use ABI 4.0 default-deny and block shared library loading.\n` +
580
579
  `This may prevent Chromium, Firefox, Flatpak apps, and similar from launching.\n` +
@@ -587,7 +586,7 @@ export function registerContainerSecurityTools(server) {
587
586
  if (result.exitCode !== 0)
588
587
  return { content: [createErrorContent(`Failed to ${baseAction} profile '${profile}': ${result.stderr}`)], isError: true };
589
588
  logChange(createChangeEntry({ tool: "container_isolation", action: baseAction, target: profile, after: `${baseAction} mode`, dryRun: false, success: true, rollbackCommand: baseAction === "disable" ? `sudo aa-enforce ${profile}` : baseAction === "enforce" ? `sudo aa-complain ${profile}` : undefined }));
590
- return { content: [createTextContent(`āœ… Profile '${profile}' set to ${baseAction} mode.\n${result.stdout || result.stderr}${desktopWarning}`)] };
589
+ return { content: [createTextContent(`Profile '${profile}' set to ${baseAction} mode.\n${result.stdout || result.stderr}${desktopWarning}`)] };
591
590
  }
592
591
  catch (err) {
593
592
  return { content: [createErrorContent(err instanceof Error ? err.message : String(err))], isError: true };
@@ -602,7 +601,7 @@ export function registerContainerSecurityTools(server) {
602
601
  if (isDryRun) {
603
602
  return { content: [createTextContent(`[DRY RUN] Would install: ${packages.join(", ")}\n` +
604
603
  ` Command: sudo apt-get install -y ${packages.join(" ")}\n\n` +
605
- `āš ļø After installation, the following profiles will be set to COMPLAIN mode\n` +
604
+ `After installation, the following profiles will be set to COMPLAIN mode\n` +
606
605
  `to prevent breaking desktop applications (flatpak, browsers, etc.):\n` +
607
606
  ` ${[...DESKTOP_BREAKING_PROFILES].join(", ")}\n\n` +
608
607
  `Use apparmor_enforce to selectively enforce profiles after testing.`)] };
@@ -621,18 +620,18 @@ export function registerContainerSecurityTools(server) {
621
620
  if (checkResult.exitCode === 0) {
622
621
  const complainResult = await executeCommand({ command: "sudo", args: ["aa-complain", profilePath], toolName: "container_isolation", timeout: 10000 });
623
622
  if (complainResult.exitCode === 0) {
624
- complainResults.push(` āœ“ ${profileName} → complain mode`);
623
+ complainResults.push(` OK ${profileName} → complain mode`);
625
624
  }
626
625
  else {
627
- complainResults.push(` āœ— ${profileName} → failed: ${complainResult.stderr.trim()}`);
626
+ complainResults.push(` FAIL ${profileName} → failed: ${complainResult.stderr.trim()}`);
628
627
  }
629
628
  }
630
629
  }
631
630
  const complainSection = complainResults.length > 0
632
- ? `\n\nāš ļø Desktop-safe profiles set to complain mode (prevents breaking GUI apps):\n${complainResults.join("\n")}\n\nUse apparmor_enforce to selectively enforce profiles after testing.`
631
+ ? `\n\nDesktop-safe profiles set to complain mode (prevents breaking GUI apps):\n${complainResults.join("\n")}\n\nUse apparmor_enforce to selectively enforce profiles after testing.`
633
632
  : "";
634
633
  logChange(createChangeEntry({ tool: "container_isolation", action: "install_profiles", target: packages.join(", "), after: `installed; ${complainResults.length} profiles set to complain`, dryRun: false, success: true, rollbackCommand: `sudo apt-get remove -y ${packages.join(" ")}` }));
635
- return { content: [createTextContent(`āœ… Successfully installed: ${packages.join(", ")}${complainSection}`)] };
634
+ return { content: [createTextContent(`Successfully installed: ${packages.join(", ")}${complainSection}`)] };
636
635
  }
637
636
  catch (err) {
638
637
  return { content: [createErrorContent(err instanceof Error ? err.message : String(err))], isError: true };
@@ -665,10 +664,10 @@ export function registerContainerSecurityTools(server) {
665
664
  // ── selinux_status ──────────────────────────────────────────
666
665
  case "selinux_status": {
667
666
  try {
668
- const sections = [`šŸ›”ļø SELinux Management: status`, "=".repeat(40)];
667
+ const sections = [`SELinux Management: status`, ""];
669
668
  const result = await executeCommand({ command: "sestatus", args: [], toolName: "container_isolation", timeout: getToolTimeout("container_selinux_manage") });
670
669
  if (result.exitCode !== 0) {
671
- sections.push("\nāš ļø SELinux may not be installed.");
670
+ sections.push("\nSELinux may not be installed.");
672
671
  sections.push(result.stderr || result.stdout);
673
672
  }
674
673
  else
@@ -682,10 +681,10 @@ export function registerContainerSecurityTools(server) {
682
681
  // ── selinux_getenforce ──────────────────────────────────────
683
682
  case "selinux_getenforce": {
684
683
  try {
685
- const sections = [`šŸ›”ļø SELinux Management: getenforce`, "=".repeat(40)];
684
+ const sections = [`SELinux Management: getenforce`, ""];
686
685
  const result = await executeCommand({ command: "getenforce", args: [], toolName: "container_isolation", timeout: getToolTimeout("container_selinux_manage") });
687
686
  if (result.exitCode !== 0)
688
- sections.push("\nāš ļø getenforce not available.");
687
+ sections.push("\ngetenforce not available.");
689
688
  else
690
689
  sections.push(`\nCurrent SELinux mode: ${result.stdout.trim()}`);
691
690
  return { content: [createTextContent(sections.join("\n"))] };
@@ -698,11 +697,11 @@ export function registerContainerSecurityTools(server) {
698
697
  case "selinux_setenforce": {
699
698
  const { mode, dry_run } = params;
700
699
  try {
701
- const sections = [`šŸ›”ļø SELinux Management: setenforce`, "=".repeat(40)];
700
+ const sections = [`SELinux Management: setenforce`, ""];
702
701
  if (!mode)
703
702
  return { content: [createErrorContent("mode is required for selinux_setenforce")], isError: true };
704
703
  if (mode === "disabled") {
705
- sections.push("\nāš ļø Cannot disable SELinux at runtime.");
704
+ sections.push("\nCannot disable SELinux at runtime.");
706
705
  return { content: [createTextContent(sections.join("\n"))] };
707
706
  }
708
707
  const modeValue = mode === "enforcing" ? "1" : "0";
@@ -713,7 +712,7 @@ export function registerContainerSecurityTools(server) {
713
712
  const result = await executeCommand({ command: "sudo", args: ["setenforce", modeValue], toolName: "container_isolation", timeout: getToolTimeout("container_selinux_manage") });
714
713
  if (result.exitCode !== 0)
715
714
  return { content: [createErrorContent(`Failed: ${result.stderr}`)], isError: true };
716
- sections.push(`\nāœ… SELinux mode set to ${mode}.`);
715
+ sections.push(`\nSELinux mode set to ${mode}.`);
717
716
  logChange(createChangeEntry({ tool: "container_isolation", action: "setenforce", target: "SELinux", after: mode, dryRun: false, success: true, rollbackCommand: `sudo setenforce ${mode === "enforcing" ? "0" : "1"}` }));
718
717
  return { content: [createTextContent(sections.join("\n"))] };
719
718
  }
@@ -725,7 +724,7 @@ export function registerContainerSecurityTools(server) {
725
724
  case "selinux_booleans": {
726
725
  const { boolean_name, boolean_value, dry_run } = params;
727
726
  try {
728
- const sections = [`šŸ›”ļø SELinux Management: booleans`, "=".repeat(40)];
727
+ const sections = [`SELinux Management: booleans`, ""];
729
728
  if (boolean_name && boolean_value) {
730
729
  sanitizeArgs([boolean_name]);
731
730
  if (dry_run ?? getConfig().dryRun) {
@@ -735,7 +734,7 @@ export function registerContainerSecurityTools(server) {
735
734
  const result = await executeCommand({ command: "sudo", args: ["setsebool", "-P", boolean_name, boolean_value], toolName: "container_isolation", timeout: getToolTimeout("container_selinux_manage") });
736
735
  if (result.exitCode !== 0)
737
736
  return { content: [createErrorContent(`Failed: ${result.stderr}`)], isError: true };
738
- sections.push(`\nāœ… Boolean '${boolean_name}' set to ${boolean_value}.`);
737
+ sections.push(`\nBoolean '${boolean_name}' set to ${boolean_value}.`);
739
738
  logChange(createChangeEntry({ tool: "container_isolation", action: "set_boolean", target: boolean_name, after: boolean_value, dryRun: false, success: true, rollbackCommand: `sudo setsebool -P ${boolean_name} ${boolean_value === "on" ? "off" : "on"}` }));
740
739
  }
741
740
  else if (boolean_name) {
@@ -748,7 +747,7 @@ export function registerContainerSecurityTools(server) {
748
747
  else {
749
748
  const result = await executeCommand({ command: "getsebool", args: ["-a"], toolName: "container_isolation", timeout: getToolTimeout("container_selinux_manage") });
750
749
  if (result.exitCode !== 0)
751
- sections.push("\nāš ļø Cannot list booleans.");
750
+ sections.push("\nCannot list booleans.");
752
751
  else
753
752
  sections.push(result.stdout);
754
753
  }
@@ -761,14 +760,14 @@ export function registerContainerSecurityTools(server) {
761
760
  // ── selinux_audit ───────────────────────────────────────────
762
761
  case "selinux_audit": {
763
762
  try {
764
- const sections = [`šŸ›”ļø SELinux Management: audit`, "=".repeat(40)];
763
+ const sections = [`SELinux Management: audit`, ""];
765
764
  const result = await executeCommand({ command: "sudo", args: ["ausearch", "-m", "AVC", "-ts", "recent"], toolName: "container_isolation", timeout: getToolTimeout("container_selinux_manage") });
766
765
  if (result.exitCode !== 0 && (result.stderr.includes("no matches") || result.stdout.includes("no matches")))
767
- sections.push("\nāœ… No recent SELinux AVC denials.");
766
+ sections.push("\nNo recent SELinux AVC denials.");
768
767
  else if (result.exitCode !== 0)
769
- sections.push("\nāš ļø Could not search audit logs.");
768
+ sections.push("\nCould not search audit logs.");
770
769
  else
771
- sections.push(`\nāš ļø Recent SELinux AVC Denials:\n${result.stdout}`);
770
+ sections.push(`\nWARNING: Recent SELinux AVC Denials:\n${result.stdout}`);
772
771
  return { content: [createTextContent(sections.join("\n"))] };
773
772
  }
774
773
  catch (err) {
@@ -779,7 +778,7 @@ export function registerContainerSecurityTools(server) {
779
778
  case "namespace_check": {
780
779
  const { pid, check_type } = params;
781
780
  try {
782
- const sections = ["šŸ“¦ Namespace Isolation Check", "=".repeat(40)];
781
+ const sections = ["Namespace Isolation Check", ""];
783
782
  if (pid !== undefined) {
784
783
  sections.push(`\nProcess PID: ${pid}`);
785
784
  const nsResult = await executeCommand({ command: "ls", args: ["-la", `/proc/${pid}/ns/`], toolName: "container_isolation", timeout: getToolTimeout("container_namespace_check") });
@@ -791,16 +790,16 @@ export function registerContainerSecurityTools(server) {
791
790
  else {
792
791
  sections.push("\n── System Namespace Configuration ──");
793
792
  if (check_type === "user" || check_type === "all") {
794
- sections.push("\nšŸ”‘ User Namespaces:");
793
+ sections.push("\nUser Namespaces:");
795
794
  const maxNsResult = await executeCommand({ command: "cat", args: ["/proc/sys/user/max_user_namespaces"], toolName: "container_isolation", timeout: 5000 });
796
795
  if (maxNsResult.exitCode === 0) {
797
796
  const maxNs = maxNsResult.stdout.trim();
798
797
  sections.push(` max_user_namespaces: ${maxNs}`);
799
- sections.push(maxNs === "0" ? " āš ļø User namespaces are disabled" : " āœ… User namespaces are enabled");
798
+ sections.push(maxNs === "0" ? " WARNING: User namespaces are disabled" : " User namespaces are enabled");
800
799
  }
801
800
  }
802
801
  if (check_type === "network" || check_type === "all") {
803
- sections.push("\n🌐 Network Namespaces:");
802
+ sections.push("\nNetwork Namespaces:");
804
803
  const netnsResult = await executeCommand({ command: "ip", args: ["netns", "list"], toolName: "container_isolation", timeout: getToolTimeout("container_namespace_check") });
805
804
  if (netnsResult.exitCode === 0 && netnsResult.stdout.trim()) {
806
805
  const namespaces = netnsResult.stdout.trim().split("\n").filter((l) => l.trim());
@@ -812,17 +811,17 @@ export function registerContainerSecurityTools(server) {
812
811
  sections.push(" No named network namespaces found.");
813
812
  }
814
813
  if (check_type === "all" || check_type === "pid") {
815
- sections.push("\nšŸ“‹ All Active Namespaces (lsns):");
814
+ sections.push("\nAll Active Namespaces (lsns):");
816
815
  let lsnsResult = await executeCommand({ command: "lsns", args: [], toolName: "container_isolation", timeout: getToolTimeout("container_namespace_check") });
817
816
  if (lsnsResult.exitCode !== 0)
818
817
  lsnsResult = await executeCommand({ command: "sudo", args: ["lsns"], toolName: "container_isolation", timeout: getToolTimeout("container_namespace_check") });
819
818
  if (lsnsResult.exitCode === 0)
820
819
  sections.push(lsnsResult.stdout);
821
820
  else
822
- sections.push(" āš ļø Cannot list namespaces.");
821
+ sections.push(" Cannot list namespaces.");
823
822
  }
824
823
  if (check_type === "mount" || check_type === "all") {
825
- sections.push("\nšŸ“ Mount Namespace Info:");
824
+ sections.push("\nMount Namespace Info:");
826
825
  const mountInfoResult = await executeCommand({ command: "cat", args: ["/proc/self/mountinfo"], toolName: "container_isolation", timeout: 5000 });
827
826
  if (mountInfoResult.exitCode === 0)
828
827
  sections.push(` Current mount namespace has ${mountInfoResult.stdout.trim().split("\n").length} mount points`);
@@ -1 +1 @@
1
- {"version":3,"file":"deception.d.ts","sourceRoot":"","sources":["../../src/tools/deception.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAorBpE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA2R9D"}
1
+ {"version":3,"file":"deception.d.ts","sourceRoot":"","sources":["../../src/tools/deception.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsnBpE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA2R9D"}
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { z } from "zod";
12
12
  import * as crypto from "node:crypto";
13
- import { spawnSafe } from "../core/spawn-safe.js";
13
+ import { runCommand } from "../core/run-command.js";
14
14
  import { secureWriteFileSync } from "../core/secure-fs.js";
15
15
  import { createTextContent, createErrorContent, formatToolOutput, } from "../core/parsers.js";
16
16
  import { existsSync, readFileSync, unlinkSync, rmSync } from "node:fs";
@@ -19,53 +19,7 @@ import { existsSync, readFileSync, unlinkSync, rmSync } from "node:fs";
19
19
  const CANARY_BASE_DIR = "/var/lib/defense-mcp/canaries";
20
20
  /** Path to the canary registry file */
21
21
  const REGISTRY_PATH = `${CANARY_BASE_DIR}/registry.json`;
22
- /**
23
- * Run a command via spawnSafe and collect output as a promise.
24
- * Handles errors gracefully — returns error info instead of throwing.
25
- */
26
- async function runCommand(command, args, timeoutMs = 30_000) {
27
- return new Promise((resolve) => {
28
- let child;
29
- try {
30
- child = spawnSafe(command, args);
31
- }
32
- catch (err) {
33
- const msg = err instanceof Error ? err.message : String(err);
34
- resolve({ stdout: "", stderr: msg, exitCode: -1 });
35
- return;
36
- }
37
- let stdout = "";
38
- let stderr = "";
39
- let resolved = false;
40
- const timer = setTimeout(() => {
41
- if (!resolved) {
42
- resolved = true;
43
- child.kill("SIGTERM");
44
- resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
45
- }
46
- }, timeoutMs);
47
- child.stdout?.on("data", (data) => {
48
- stdout += data.toString();
49
- });
50
- child.stderr?.on("data", (data) => {
51
- stderr += data.toString();
52
- });
53
- child.on("close", (code) => {
54
- if (!resolved) {
55
- resolved = true;
56
- clearTimeout(timer);
57
- resolve({ stdout, stderr, exitCode: code ?? -1 });
58
- }
59
- });
60
- child.on("error", (err) => {
61
- if (!resolved) {
62
- resolved = true;
63
- clearTimeout(timer);
64
- resolve({ stdout, stderr: err.message, exitCode: -1 });
65
- }
66
- });
67
- });
68
- }
22
+ // ── Helpers ────────────────────────────────────────────────────────────────────
69
23
  /**
70
24
  * Generate a unique canary ID based on timestamp and cryptographic random suffix.
71
25
  */
@@ -572,7 +526,7 @@ export function registerDeceptionTools(server) {
572
526
  text += `Canary ID: ${deployResult.canaryId}\n`;
573
527
  text += `Type: ${deployResult.type}\n`;
574
528
  text += `Path: ${deployResult.path}\n`;
575
- text += `Monitoring: ${deployResult.monitoringSetup ? "active āœ“" : "not set up ⚠"}\n`;
529
+ text += `Monitoring: ${deployResult.monitoringSetup ? "active OK" : "not set up WARNING"}\n`;
576
530
  text += `Details: ${deployResult.monitoringDetails}\n`;
577
531
  text += `\n${deployResult.description}\n`;
578
532
  return { content: [createTextContent(text)] };
@@ -610,7 +564,7 @@ export function registerDeceptionTools(server) {
610
564
  text += `Port: ${honeyResult.port}\n`;
611
565
  text += `Listener PID: ${honeyResult.listenerPid ?? "unknown"}\n`;
612
566
  text += `Log Path: ${honeyResult.logPath}\n`;
613
- text += `Iptables LOG Rule: ${honeyResult.iptablesRuleAdded ? "added āœ“" : "not added ⚠"}\n`;
567
+ text += `Iptables LOG Rule: ${honeyResult.iptablesRuleAdded ? "added OK" : "not added WARNING"}\n`;
614
568
  text += `\n${honeyResult.description}\n`;
615
569
  return { content: [createTextContent(text)] };
616
570
  }
@@ -639,7 +593,7 @@ export function registerDeceptionTools(server) {
639
593
  text += `Triggered: ${triggerResult.triggeredCount}\n`;
640
594
  text += `Not Triggered: ${triggerResult.notTriggered.length}\n\n`;
641
595
  if (triggerResult.triggered.length > 0) {
642
- text += "⚠ TRIGGERED CANARIES:\n";
596
+ text += "WARNING: TRIGGERED CANARIES:\n";
643
597
  for (const t of triggerResult.triggered) {
644
598
  text += `\n • ${t.id} [${t.type}] — Severity: ${t.severity}\n`;
645
599
  if (t.path)
@@ -699,12 +653,12 @@ export function registerDeceptionTools(server) {
699
653
  }
700
654
  else {
701
655
  text += `Canary ID: ${removeResult.canaryId}\n`;
702
- text += `File/Directory Removed: ${removeResult.fileRemoved ? "yes āœ“" : "no"}\n`;
656
+ text += `File/Directory Removed: ${removeResult.fileRemoved ? "yes OK" : "no"}\n`;
703
657
  if (removeResult.listenerKilled) {
704
- text += `Listener Killed: yes āœ“\n`;
658
+ text += `Listener Killed: yes OK\n`;
705
659
  }
706
660
  if (removeResult.iptablesRemoved) {
707
- text += `Iptables Rule Removed: yes āœ“\n`;
661
+ text += `Iptables Rule Removed: yes OK\n`;
708
662
  }
709
663
  text += `\n${removeResult.description}\n`;
710
664
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dns-security.d.ts","sourceRoot":"","sources":["../../src/tools/dns-security.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmHpE;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAkB3D;AAID,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CA4DjF;AAID,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EACtB,gBAAgB,EAAE,MAAM,GACvB;IAAE,UAAU,EAAE,eAAe,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CA4DxI;AAID,UAAU,iBAAiB;IACzB,WAAW,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChG,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9F,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,iBAAiB,CAyEhG;AAID,UAAU,gBAAgB;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,oBAAoB,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAyFpE;AAID,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwZhE"}
1
+ {"version":3,"file":"dns-security.d.ts","sourceRoot":"","sources":["../../src/tools/dns-security.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqDpE;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAkB3D;AAID,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CA4DjF;AAID,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EACtB,gBAAgB,EAAE,MAAM,GACvB;IAAE,UAAU,EAAE,eAAe,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CA4DxI;AAID,UAAU,iBAAiB;IACzB,WAAW,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChG,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9F,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,iBAAiB,CAyEhG;AAID,UAAU,gBAAgB;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,oBAAoB,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAyFpE;AAID,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwZhE"}
@@ -8,7 +8,7 @@
8
8
  * DNS tunneling detection, domain blocking, and query log 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 { secureWriteFileSync, secureCopyFileSync } from "../core/secure-fs.js";
13
13
  import { createErrorContent, formatToolOutput, } from "../core/parsers.js";
14
14
  import { validateInterface } from "../core/sanitizer.js";
@@ -34,53 +34,7 @@ const SUSPICIOUS_TLDS = new Set([
34
34
  const MAX_CAPTURE_DURATION = 120;
35
35
  /** Default entropy threshold for tunneling detection */
36
36
  const DEFAULT_ENTROPY_THRESHOLD = 3.5;
37
- /**
38
- * Run a command via spawnSafe and collect output as a promise.
39
- * Handles errors gracefully — returns error info instead of throwing.
40
- */
41
- async function runCommand(command, args, timeoutMs = 30_000) {
42
- return new Promise((resolve) => {
43
- let child;
44
- try {
45
- child = spawnSafe(command, args);
46
- }
47
- catch (err) {
48
- const msg = err instanceof Error ? err.message : String(err);
49
- resolve({ stdout: "", stderr: msg, exitCode: -1 });
50
- return;
51
- }
52
- let stdout = "";
53
- let stderr = "";
54
- let resolved = false;
55
- const timer = setTimeout(() => {
56
- if (!resolved) {
57
- resolved = true;
58
- child.kill("SIGTERM");
59
- resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
60
- }
61
- }, timeoutMs);
62
- child.stdout?.on("data", (data) => {
63
- stdout += data.toString();
64
- });
65
- child.stderr?.on("data", (data) => {
66
- stderr += data.toString();
67
- });
68
- child.on("close", (code) => {
69
- if (!resolved) {
70
- resolved = true;
71
- clearTimeout(timer);
72
- resolve({ stdout, stderr, exitCode: code ?? -1 });
73
- }
74
- });
75
- child.on("error", (err) => {
76
- if (!resolved) {
77
- resolved = true;
78
- clearTimeout(timer);
79
- resolve({ stdout, stderr: err.message, exitCode: -1 });
80
- }
81
- });
82
- });
83
- }
37
+ // ── Helpers ────────────────────────────────────────────────────────────────────
84
38
  /**
85
39
  * Run a command via sudo through spawnSafe.
86
40
  */
@@ -1 +1 @@
1
- {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../src/tools/encryption.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqJpE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgnD/D"}
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../src/tools/encryption.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoJpE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+mD/D"}