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.
- package/build/core/auto-installer.js +31 -31
- package/build/core/command-allowlist.js +1 -1
- package/build/core/dependency-validator.js +9 -9
- package/build/core/distro-adapter.d.ts +0 -5
- package/build/core/distro-adapter.d.ts.map +1 -1
- package/build/core/distro-adapter.js +0 -7
- package/build/core/distro.d.ts +0 -11
- package/build/core/distro.d.ts.map +1 -1
- package/build/core/distro.js +0 -48
- package/build/core/encrypted-state.d.ts +0 -7
- package/build/core/encrypted-state.d.ts.map +1 -1
- package/build/core/encrypted-state.js +0 -7
- package/build/core/logger.js +1 -1
- package/build/core/pam-utils.js +1 -1
- package/build/core/parsers.js +1 -1
- package/build/core/preflight.d.ts +4 -4
- package/build/core/preflight.js +13 -13
- package/build/core/progress.js +20 -20
- package/build/core/run-command.d.ts +14 -0
- package/build/core/run-command.d.ts.map +1 -0
- package/build/core/run-command.js +46 -0
- package/build/core/spawn-safe.d.ts +6 -6
- package/build/core/spawn-safe.d.ts.map +1 -1
- package/build/core/sudo-guard.js +4 -4
- package/build/core/third-party-installer.js +4 -4
- package/build/core/tool-wrapper.js +3 -3
- package/build/tools/access-control.js +6 -6
- package/build/tools/api-security.d.ts.map +1 -1
- package/build/tools/api-security.js +5 -51
- package/build/tools/app-hardening.d.ts.map +1 -1
- package/build/tools/app-hardening.js +23 -25
- package/build/tools/cloud-security.d.ts.map +1 -1
- package/build/tools/cloud-security.js +5 -51
- package/build/tools/compliance.d.ts.map +1 -1
- package/build/tools/compliance.js +9 -13
- package/build/tools/container-security.d.ts.map +1 -1
- package/build/tools/container-security.js +51 -52
- package/build/tools/deception.d.ts.map +1 -1
- package/build/tools/deception.js +8 -54
- package/build/tools/dns-security.d.ts.map +1 -1
- package/build/tools/dns-security.js +2 -48
- package/build/tools/encryption.d.ts.map +1 -1
- package/build/tools/encryption.js +86 -87
- package/build/tools/firewall.d.ts.map +1 -1
- package/build/tools/firewall.js +324 -30
- package/build/tools/hardening.d.ts.map +1 -1
- package/build/tools/hardening.js +12 -13
- package/build/tools/incident-response.d.ts.map +1 -1
- package/build/tools/incident-response.js +3 -3
- package/build/tools/logging.d.ts.map +1 -1
- package/build/tools/logging.js +17 -59
- package/build/tools/malware.js +2 -2
- package/build/tools/meta.d.ts.map +1 -1
- package/build/tools/meta.js +86 -165
- package/build/tools/network-defense.d.ts.map +1 -1
- package/build/tools/network-defense.js +3 -3
- package/build/tools/patch-management.js +8 -8
- package/build/tools/process-security.d.ts.map +1 -1
- package/build/tools/process-security.js +38 -92
- package/build/tools/sudo-management.js +36 -36
- package/build/tools/threat-intel.d.ts.map +1 -1
- package/build/tools/threat-intel.js +2 -48
- package/build/tools/vulnerability-management.d.ts.map +1 -1
- package/build/tools/vulnerability-management.js +3 -49
- package/build/tools/waf.d.ts.map +1 -1
- package/build/tools/waf.js +47 -93
- package/build/tools/wireless-security.d.ts.map +1 -1
- package/build/tools/wireless-security.js +9 -55
- 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("
|
|
90
|
-
sections.push("
|
|
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("
|
|
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
|
-
|
|
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 = ["
|
|
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("
|
|
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 }
|
|
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 = ["
|
|
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("
|
|
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 ? "
|
|
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
|
|
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(`
|
|
411
|
-
sections.push("\n
|
|
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" }
|
|
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 = ["
|
|
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 ? "
|
|
505
|
+
sections.push(`\n AppArmor enabled: ${aaEnabled ? "Yes" : "No"}`);
|
|
507
506
|
if (moduleResult.exitCode === 0)
|
|
508
|
-
sections.push(` Kernel module: ${kernelModuleLoaded ? "
|
|
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 ? "
|
|
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 = ["
|
|
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("\
|
|
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
|
|
539
|
+
sections.push("\n Enforce Mode:");
|
|
541
540
|
}
|
|
542
541
|
else if (trimmed.includes("complain mode")) {
|
|
543
542
|
currentSection = "complain";
|
|
544
|
-
sections.push("\n
|
|
543
|
+
sections.push("\n Complain Mode:");
|
|
545
544
|
}
|
|
546
545
|
else if (trimmed.includes("unconfined")) {
|
|
547
546
|
currentSection = "unconfined";
|
|
548
|
-
sections.push("\n
|
|
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\
|
|
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(
|
|
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
|
-
|
|
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(`
|
|
623
|
+
complainResults.push(` OK ${profileName} ā complain mode`);
|
|
625
624
|
}
|
|
626
625
|
else {
|
|
627
|
-
complainResults.push(`
|
|
626
|
+
complainResults.push(` FAIL ${profileName} ā failed: ${complainResult.stderr.trim()}`);
|
|
628
627
|
}
|
|
629
628
|
}
|
|
630
629
|
}
|
|
631
630
|
const complainSection = complainResults.length > 0
|
|
632
|
-
? `\n\
|
|
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(
|
|
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 = [
|
|
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("\
|
|
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 = [
|
|
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("\
|
|
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 = [
|
|
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("\
|
|
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(`\
|
|
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 = [
|
|
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(`\
|
|
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("\
|
|
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 = [
|
|
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("\
|
|
766
|
+
sections.push("\nNo recent SELinux AVC denials.");
|
|
768
767
|
else if (result.exitCode !== 0)
|
|
769
|
-
sections.push("\
|
|
768
|
+
sections.push("\nCould not search audit logs.");
|
|
770
769
|
else
|
|
771
|
-
sections.push(`\
|
|
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 = ["
|
|
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("\
|
|
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" ? "
|
|
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("\
|
|
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("\
|
|
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("
|
|
821
|
+
sections.push(" Cannot list namespaces.");
|
|
823
822
|
}
|
|
824
823
|
if (check_type === "mount" || check_type === "all") {
|
|
825
|
-
sections.push("\
|
|
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;
|
|
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"}
|
package/build/tools/deception.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import * as crypto from "node:crypto";
|
|
13
|
-
import {
|
|
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
|
|
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
|
|
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 += "
|
|
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
|
|
656
|
+
text += `File/Directory Removed: ${removeResult.fileRemoved ? "yes OK" : "no"}\n`;
|
|
703
657
|
if (removeResult.listenerKilled) {
|
|
704
|
-
text += `Listener Killed: yes
|
|
658
|
+
text += `Listener Killed: yes OK\n`;
|
|
705
659
|
}
|
|
706
660
|
if (removeResult.iptablesRemoved) {
|
|
707
|
-
text += `Iptables Rule Removed: yes
|
|
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;
|
|
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 {
|
|
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;
|
|
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"}
|