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
@@ -22,7 +22,7 @@ import { THIRD_PARTY_MANIFEST } from "../core/third-party-manifest.js";
22
22
  import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, statSync, renameSync } from "node:fs";
23
23
  import { join } from "node:path";
24
24
  import { homedir } from "node:os";
25
- import { spawnSafe } from "../core/spawn-safe.js";
25
+ import { runCommand } from "../core/run-command.js";
26
26
  import { secureWriteFileSync, verifySecurePermissions } from "../core/secure-fs.js";
27
27
  import { verifyAllBinaries } from "../core/command-allowlist.js";
28
28
  import { RateLimiter } from "../core/rate-limiter.js";
@@ -452,50 +452,11 @@ function riskSortValue(risk) {
452
452
  default: return 3;
453
453
  }
454
454
  }
455
- async function runRemediateCmd(command, args, timeoutMs = 30_000) {
456
- return new Promise((resolve) => {
457
- let child;
458
- try {
459
- child = spawnSafe(command, args);
460
- }
461
- catch (err) {
462
- const msg = err instanceof Error ? err.message : String(err);
463
- resolve({ stdout: "", stderr: msg, exitCode: -1 });
464
- return;
465
- }
466
- let stdout = "";
467
- let stderr = "";
468
- let resolved = false;
469
- const timer = setTimeout(() => {
470
- if (!resolved) {
471
- resolved = true;
472
- child.kill("SIGTERM");
473
- resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
474
- }
475
- }, timeoutMs);
476
- child.stdout?.on("data", (data) => { stdout += data.toString(); });
477
- child.stderr?.on("data", (data) => { stderr += data.toString(); });
478
- child.on("close", (code) => {
479
- if (!resolved) {
480
- resolved = true;
481
- clearTimeout(timer);
482
- resolve({ stdout, stderr, exitCode: code ?? -1 });
483
- }
484
- });
485
- child.on("error", (err) => {
486
- if (!resolved) {
487
- resolved = true;
488
- clearTimeout(timer);
489
- resolve({ stdout, stderr: err.message, exitCode: -1 });
490
- }
491
- });
492
- });
493
- }
494
455
  async function gatherRemediationFindings(source, severityFilter) {
495
456
  const findings = [];
496
457
  const shouldInclude = (cat) => source === "all" || source === cat;
497
458
  if (shouldInclude("hardening")) {
498
- const sysctlResult = await runRemediateCmd("sysctl", ["-a"]);
459
+ const sysctlResult = await runCommand("sysctl", ["-a"]);
499
460
  if (sysctlResult.exitCode === 0) {
500
461
  const sysctlValues = new Map();
501
462
  for (const line of sysctlResult.stdout.split("\n")) {
@@ -517,7 +478,7 @@ async function gatherRemediationFindings(source, severityFilter) {
517
478
  }
518
479
  }
519
480
  if (shouldInclude("access_control")) {
520
- const sshResult = await runRemediateCmd("grep", ["-E", "^PermitRootLogin|^PermitEmptyPasswords", "/etc/ssh/sshd_config"]);
481
+ const sshResult = await runCommand("grep", ["-E", "^PermitRootLogin|^PermitEmptyPasswords", "/etc/ssh/sshd_config"]);
521
482
  if (sshResult.exitCode === 0 || sshResult.stdout.length > 0) {
522
483
  if (sshResult.stdout.includes("PermitRootLogin yes")) {
523
484
  const f = KNOWN_REMEDIATIONS.find(r => r.finding_id === "ACCESS-001");
@@ -532,7 +493,7 @@ async function gatherRemediationFindings(source, severityFilter) {
532
493
  }
533
494
  }
534
495
  if (shouldInclude("firewall")) {
535
- const fwResult = await runRemediateCmd("iptables", ["-L", "-n"]);
496
+ const fwResult = await runCommand("iptables", ["-L", "-n"]);
536
497
  if (fwResult.exitCode === 0) {
537
498
  if (fwResult.stdout.includes("Chain INPUT (policy ACCEPT)")) {
538
499
  const f = KNOWN_REMEDIATIONS.find(r => r.finding_id === "FW-001");
@@ -547,7 +508,7 @@ async function gatherRemediationFindings(source, severityFilter) {
547
508
  }
548
509
  }
549
510
  if (shouldInclude("compliance")) {
550
- const lynisResult = await runRemediateCmd("lynis", ["audit", "system", "--quick", "--no-colors"], 120_000);
511
+ const lynisResult = await runCommand("lynis", ["audit", "system", "--quick", "--no-colors"], 120_000);
551
512
  if (lynisResult.exitCode === 0 || lynisResult.stdout.length > 0) {
552
513
  for (const f of KNOWN_REMEDIATIONS) {
553
514
  if (findings.some(e => e.finding_id === f.finding_id))
@@ -594,52 +555,13 @@ const ALL_SECTIONS = [
594
555
  "compliance_summary",
595
556
  "recommendations",
596
557
  ];
597
- async function runReportCommand(command, args, timeoutMs = 30_000) {
598
- return new Promise((resolve) => {
599
- let child;
600
- try {
601
- child = spawnSafe(command, args);
602
- }
603
- catch (err) {
604
- const msg = err instanceof Error ? err.message : String(err);
605
- resolve({ stdout: "", stderr: msg, exitCode: -1 });
606
- return;
607
- }
608
- let stdout = "";
609
- let stderr = "";
610
- let resolved = false;
611
- const timer = setTimeout(() => {
612
- if (!resolved) {
613
- resolved = true;
614
- child.kill("SIGTERM");
615
- resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
616
- }
617
- }, timeoutMs);
618
- child.stdout?.on("data", (data) => { stdout += data.toString(); });
619
- child.stderr?.on("data", (data) => { stderr += data.toString(); });
620
- child.on("close", (code) => {
621
- if (!resolved) {
622
- resolved = true;
623
- clearTimeout(timer);
624
- resolve({ stdout, stderr, exitCode: code ?? -1 });
625
- }
626
- });
627
- child.on("error", (err) => {
628
- if (!resolved) {
629
- resolved = true;
630
- clearTimeout(timer);
631
- resolve({ stdout, stderr: err.message, exitCode: -1 });
632
- }
633
- });
634
- });
635
- }
636
558
  async function runSudoReportCommand(command, args, timeoutMs = 30_000) {
637
- return runReportCommand("sudo", [command, ...args], timeoutMs);
559
+ return runCommand("sudo", [command, ...args], timeoutMs);
638
560
  }
639
561
  async function gatherSystemOverview() {
640
- const uname = await runReportCommand("uname", ["-a"]);
641
- const hostname = await runReportCommand("hostname", []);
642
- const uptime = await runReportCommand("uptime", []);
562
+ const uname = await runCommand("uname", ["-a"]);
563
+ const hostname = await runCommand("hostname", []);
564
+ const uptime = await runCommand("uptime", []);
643
565
  let data = "";
644
566
  if (uname.exitCode === 0)
645
567
  data += `Kernel: ${uname.stdout.trim()}\n`;
@@ -657,12 +579,12 @@ async function gatherFirewallStatus() {
657
579
  return { name: "Firewall Status", key: "firewall_status", data, error: iptables.exitCode !== 0 ? iptables.stderr.trim() : undefined };
658
580
  }
659
581
  async function gatherServiceAudit() {
660
- const services = await runReportCommand("systemctl", ["list-units", "--type=service", "--state=running", "--no-pager", "--no-legend"]);
582
+ const services = await runCommand("systemctl", ["list-units", "--type=service", "--state=running", "--no-pager", "--no-legend"]);
661
583
  const data = services.exitCode === 0 ? services.stdout.trim() : `[Error listing services: ${services.stderr.trim()}]`;
662
584
  return { name: "Service Audit", key: "service_audit", data, error: services.exitCode !== 0 ? services.stderr.trim() : undefined };
663
585
  }
664
586
  async function gatherActiveConnections() {
665
- const ss = await runReportCommand("ss", ["-tulnp"]);
587
+ const ss = await runCommand("ss", ["-tulnp"]);
666
588
  const data = ss.exitCode === 0 ? ss.stdout.trim() : `[Error listing connections: ${ss.stderr.trim()}]`;
667
589
  return { name: "Active Connections", key: "active_connections", data, error: ss.exitCode !== 0 ? ss.stderr.trim() : undefined };
668
590
  }
@@ -670,7 +592,7 @@ async function gatherRecentLogins(since) {
670
592
  const args = ["_COMM=sshd", "-n", "50", "--no-pager"];
671
593
  if (since)
672
594
  args.push("--since", since);
673
- const logins = await runReportCommand("journalctl", args);
595
+ const logins = await runCommand("journalctl", args);
674
596
  let data = "";
675
597
  if (logins.exitCode === 0 && logins.stdout.trim().length > 0) {
676
598
  data = logins.stdout.trim();
@@ -751,7 +673,7 @@ function formatAsMarkdown(sections, reportType, timestamp) {
751
673
  for (const section of sections) {
752
674
  md += `## ${section.name}\n\n`;
753
675
  if (section.error)
754
- md += `> ⚠️ Error: ${section.error}\n\n`;
676
+ md += `> Error: ${section.error}\n\n`;
755
677
  md += `\`\`\`\n${section.data}\n\`\`\`\n\n`;
756
678
  }
757
679
  return md;
@@ -764,14 +686,14 @@ function formatAsHtml(sections, reportType, timestamp) {
764
686
  for (const section of sections) {
765
687
  html += `<h2>${escapeHtml(section.name)}</h2>\n`;
766
688
  if (section.error)
767
- html += `<p class="warning">⚠ Error: ${escapeHtml(section.error)}</p>\n`;
689
+ html += `<p class="warning">WARNING: Error: ${escapeHtml(section.error)}</p>\n`;
768
690
  html += `<pre>${escapeHtml(section.data)}</pre>\n`;
769
691
  }
770
692
  html += `</body></html>`;
771
693
  return html;
772
694
  }
773
695
  function formatAsJson(sections, reportType, timestamp) {
774
- return JSON.stringify({ reportType, generatedAt: timestamp, sections: sections.map((s) => ({ name: s.name, key: s.key, data: s.data, error: s.error || null })) }, null, 2);
696
+ return JSON.stringify({ reportType, generatedAt: timestamp, sections: sections.map((s) => ({ name: s.name, key: s.key, data: s.data, error: s.error || null })) });
775
697
  }
776
698
  function formatAsCsv(sections, _reportType, _timestamp) {
777
699
  const lines = ["Section,Status,Summary"];
@@ -851,12 +773,12 @@ export function registerMetaTools(server) {
851
773
  const { category, install_missing, dry_run, gate } = params;
852
774
  try {
853
775
  const sections = [];
854
- sections.push("🔧 Defensive Tool Availability Check");
855
- sections.push("=".repeat(50));
776
+ sections.push("Defensive Tool Availability Check");
777
+ sections.push("");
856
778
  const validCategories = ["hardening", "firewall", "monitoring", "assessment", "network", "access", "encryption", "container", "malware", "forensics"];
857
779
  const filterCategory = category && validCategories.includes(category) ? category : undefined;
858
780
  if (category && !filterCategory) {
859
- sections.push(`\n⚠️ Unknown category '${category}'. Valid: ${validCategories.join(", ")}`);
781
+ sections.push(`\nWARNING: Unknown category '${category}'. Valid: ${validCategories.join(", ")}`);
860
782
  sections.push("Showing all categories.\n");
861
783
  }
862
784
  const results = await checkAllTools(filterCategory);
@@ -875,14 +797,14 @@ export function registerMetaTools(server) {
875
797
  if (t.installed) {
876
798
  installed++;
877
799
  const version = t.version ? ` (${t.version.substring(0, 60)})` : "";
878
- sections.push(` ${t.tool.name}${version}`);
800
+ sections.push(` ${t.tool.name}${version}`);
879
801
  if (t.path)
880
802
  sections.push(` Path: ${t.path}`);
881
803
  }
882
804
  else {
883
805
  missing++;
884
806
  const req = t.tool.required ? " [REQUIRED]" : " [optional]";
885
- sections.push(` ${t.tool.name}${req}`);
807
+ sections.push(` ${t.tool.name}${req}`);
886
808
  }
887
809
  }
888
810
  }
@@ -899,7 +821,7 @@ export function registerMetaTools(server) {
899
821
  sections.push(" Installing missing tools...\n");
900
822
  const installResults = await installMissing(filterCategory, false);
901
823
  for (const r of installResults) {
902
- const icon = r.success ? "" : "";
824
+ const icon = r.success ? "PASS" : "FAIL";
903
825
  sections.push(` ${icon} ${r.message}`);
904
826
  }
905
827
  logChange(createChangeEntry({ tool: "defense_mgmt", action: "install_missing", target: filterCategory || "all", after: `Attempted to install ${installResults.length} tools`, dryRun: false, success: installResults.every((r) => r.success) }));
@@ -919,8 +841,8 @@ export function registerMetaTools(server) {
919
841
  const optionalMissing = allMissing.filter(r => !r.tool.required);
920
842
  if (requiredMissing.length > 0) {
921
843
  sections.push("");
922
- sections.push("🚫 AUDIT GATE: BLOCKED");
923
- sections.push("=".repeat(50));
844
+ sections.push("AUDIT GATE: BLOCKED");
845
+ sections.push("");
924
846
  sections.push(`${requiredMissing.length} required tool(s) missing. Audit cannot proceed.\n`);
925
847
  sections.push("Required (must fix):");
926
848
  for (const r of requiredMissing) {
@@ -955,7 +877,7 @@ export function registerMetaTools(server) {
955
877
  }
956
878
  if (optionalMissing.length > 0) {
957
879
  sections.push("");
958
- sections.push("AUDIT GATE: PASSED (with warnings)");
880
+ sections.push("AUDIT GATE: PASSED (with warnings)");
959
881
  sections.push(`${optionalMissing.length} optional tool(s) missing — audit can proceed but some checks may be skipped.`);
960
882
  for (const r of optionalMissing) {
961
883
  const hint = await getInstallHint(r.tool);
@@ -974,7 +896,7 @@ export function registerMetaTools(server) {
974
896
  };
975
897
  }
976
898
  // All tools present
977
- sections.push("\n✅ AUDIT GATE: PASSED — all tools available.");
899
+ sections.push("\nAUDIT GATE: PASSED — all tools available.");
978
900
  return {
979
901
  content: [createTextContent(sections.join("\n"))],
980
902
  _meta: {
@@ -1001,9 +923,9 @@ export function registerMetaTools(server) {
1001
923
  if (!objective)
1002
924
  return { content: [createErrorContent("objective is required for workflow_suggest action")], isError: true };
1003
925
  const sections = [];
1004
- sections.push(`📋 Recommended Workflow: ${objective.replace(/_/g, " ").toUpperCase()}`);
926
+ sections.push(`Recommended Workflow: ${objective.replace(/_/g, " ").toUpperCase()}`);
1005
927
  sections.push(`System type: ${system_type}`);
1006
- sections.push("=".repeat(50));
928
+ sections.push("");
1007
929
  const suggestions = WORKFLOW_SUGGESTIONS[objective]?.[system_type] || [];
1008
930
  if (suggestions.length === 0) {
1009
931
  sections.push("\nNo specific workflow available for this combination.");
@@ -1035,8 +957,8 @@ export function registerMetaTools(server) {
1035
957
  if (!workflow)
1036
958
  return { content: [createErrorContent("workflow is required for workflow_run action")], isError: true };
1037
959
  const sections = [];
1038
- sections.push(`🚀 Workflow: ${workflow.replace(/_/g, " ").toUpperCase()}`);
1039
- sections.push("=".repeat(50));
960
+ sections.push(`Workflow: ${workflow.replace(/_/g, " ").toUpperCase()}`);
961
+ sections.push("");
1040
962
  const steps = WORKFLOWS[workflow];
1041
963
  if (!steps || steps.length === 0)
1042
964
  return { content: [createErrorContent(`Unknown workflow: ${workflow}`)], isError: true };
@@ -1077,9 +999,9 @@ export function registerMetaTools(server) {
1077
999
  sections.push(`── Step ${i + 1}/${steps.length}: ${step.description} ──`);
1078
1000
  const stepSafety = await SafeguardRegistry.getInstance().checkSafety(`defense_mgmt_${workflow}_step_${i + 1}`, { command: step.command, args: step.args, description: step.description });
1079
1001
  if (stepSafety.warnings.length > 0)
1080
- sections.push(` ⚠️ Safety warnings: ${stepSafety.warnings.join("; ")}`);
1002
+ sections.push(` WARNING: Safety warnings: ${stepSafety.warnings.join("; ")}`);
1081
1003
  if (!stepSafety.safe) {
1082
- sections.push(` 🛑 Step blocked by safeguards: ${stepSafety.blockers.join("; ")}`);
1004
+ sections.push(` Step blocked by safeguards: ${stepSafety.blockers.join("; ")}`);
1083
1005
  sections.push(` Impacted: ${stepSafety.impactedApps.join(", ")}`);
1084
1006
  failCount++;
1085
1007
  logChange(createChangeEntry({ tool: "defense_mgmt", action: `${workflow}_step_${i + 1}`, target: step.description, after: `blocked by safeguards: ${stepSafety.blockers.join("; ")}`, dryRun: false, success: false, error: "Blocked by safeguard checks" }));
@@ -1091,7 +1013,7 @@ export function registerMetaTools(server) {
1091
1013
  const duration = Math.round((Date.now() - startTime) / 1000);
1092
1014
  if (result.exitCode === 0) {
1093
1015
  successCount++;
1094
- sections.push(` Completed in ${duration}s`);
1016
+ sections.push(` Completed in ${duration}s`);
1095
1017
  const output = result.stdout.trim();
1096
1018
  if (output) {
1097
1019
  const outputLines = output.split("\n");
@@ -1110,7 +1032,7 @@ export function registerMetaTools(server) {
1110
1032
  }
1111
1033
  else {
1112
1034
  failCount++;
1113
- sections.push(` Failed (exit ${result.exitCode}) in ${duration}s`);
1035
+ sections.push(` Failed (exit ${result.exitCode}) in ${duration}s`);
1114
1036
  if (result.stderr)
1115
1037
  sections.push(` Error: ${result.stderr.substring(0, 200)}`);
1116
1038
  }
@@ -1120,7 +1042,7 @@ export function registerMetaTools(server) {
1120
1042
  sections.push("── Workflow Summary ──");
1121
1043
  sections.push(` Completed: ${successCount}/${steps.length}`);
1122
1044
  sections.push(` Failed: ${failCount}/${steps.length}`);
1123
- sections.push(failCount === 0 ? " All steps completed successfully" : " ⚠️ Some steps failed");
1045
+ sections.push(failCount === 0 ? " All steps completed successfully" : " WARNING: Some steps failed");
1124
1046
  }
1125
1047
  return { content: [createTextContent(sections.join("\n"))] };
1126
1048
  }
@@ -1133,8 +1055,8 @@ export function registerMetaTools(server) {
1133
1055
  const { limit, tool, since } = params;
1134
1056
  try {
1135
1057
  const sections = [];
1136
- sections.push("📜 Defense Change History");
1137
- sections.push("=".repeat(50));
1058
+ sections.push("Defense Change History");
1059
+ sections.push("");
1138
1060
  let entries = getChangelog(limit * 5);
1139
1061
  if (tool)
1140
1062
  entries = entries.filter((e) => e.tool.toLowerCase().includes(tool.toLowerCase()));
@@ -1165,14 +1087,14 @@ export function registerMetaTools(server) {
1165
1087
  if (since)
1166
1088
  sections.push(` Filter: since '${since}'`);
1167
1089
  for (const entry of entries) {
1168
- sections.push("\n " + "".repeat(40));
1090
+ sections.push("\n " + "");
1169
1091
  sections.push(` ID: ${entry.id}`);
1170
1092
  sections.push(` Time: ${entry.timestamp}`);
1171
1093
  sections.push(` Tool: ${entry.tool}`);
1172
1094
  sections.push(` Action: ${entry.action}`);
1173
1095
  sections.push(` Target: ${entry.target}`);
1174
1096
  sections.push(` Dry Run: ${entry.dryRun ? "Yes" : "No"}`);
1175
- sections.push(` Success: ${entry.success ? "" : ""}`);
1097
+ sections.push(` Success: ${entry.success ? "PASS" : "FAIL"}`);
1176
1098
  if (entry.error)
1177
1099
  sections.push(` Error: ${entry.error}`);
1178
1100
  if (entry.before)
@@ -1546,12 +1468,12 @@ export function registerMetaTools(server) {
1546
1468
  return { content: [formatToolOutput({ action: "plan", source: effectiveSource, severity_filter: effectiveSeverity, total_findings: findings.length, findings: findings.map(f => ({ finding_id: f.finding_id, description: f.description, severity: f.severity, remediation_command: `${f.remediation_command} ${f.remediation_args.join(" ")}`, risk_level: f.risk_level, category: f.category })) })] };
1547
1469
  }
1548
1470
  const sections = [];
1549
- sections.push("🔍 Auto-Remediation Plan");
1550
- sections.push("=".repeat(50));
1471
+ sections.push("Auto-Remediation Plan");
1472
+ sections.push("");
1551
1473
  sections.push(`Source: ${effectiveSource} | Severity filter: >= ${effectiveSeverity}`);
1552
1474
  sections.push(`Total findings: ${findings.length}`);
1553
1475
  if (findings.length === 0) {
1554
- sections.push("\n✅ No findings match the current filters. System looks good!");
1476
+ sections.push("\nNo findings match the current filters. System looks good!");
1555
1477
  return { content: [createTextContent(sections.join("\n"))] };
1556
1478
  }
1557
1479
  for (const f of findings) {
@@ -1585,29 +1507,29 @@ export function registerMetaTools(server) {
1585
1507
  const msg = "No findings match the current filters. Nothing to remediate.";
1586
1508
  if (params.output_format === "json")
1587
1509
  return { content: [formatToolOutput({ action: "apply", dry_run: effectiveDryRun, message: msg, actions_taken: 0 })] };
1588
- return { content: [createTextContent(`✅ ${msg}`)] };
1510
+ return { content: [createTextContent(msg)] };
1589
1511
  }
1590
1512
  if (effectiveDryRun) {
1591
1513
  if (params.output_format === "json") {
1592
1514
  return { content: [formatToolOutput({ action: "apply", dry_run: true, total_findings: findings.length, would_execute: findings.filter(f => f.risk_level === "safe").map(f => ({ finding_id: f.finding_id, description: f.description, command: `${f.remediation_command} ${f.remediation_args.join(" ")}`, risk_level: f.risk_level })), would_skip: findings.filter(f => f.risk_level !== "safe").map(f => ({ finding_id: f.finding_id, description: f.description, risk_level: f.risk_level, reason: `risk_level is ${f.risk_level} (only safe actions auto-executed)` })) })] };
1593
1515
  }
1594
1516
  const sections = [];
1595
- sections.push("🔒 Auto-Remediation — DRY RUN");
1596
- sections.push("=".repeat(50));
1517
+ sections.push("Auto-Remediation — DRY RUN");
1518
+ sections.push("");
1597
1519
  sections.push("[DRY RUN] No changes will be made.\n");
1598
1520
  const safeFindings = findings.filter(f => f.risk_level === "safe");
1599
1521
  const skippedFindings = findings.filter(f => f.risk_level !== "safe");
1600
1522
  if (safeFindings.length > 0) {
1601
1523
  sections.push("Would execute:");
1602
1524
  for (const f of safeFindings) {
1603
- sections.push(` ${f.finding_id}: ${f.remediation_command} ${f.remediation_args.join(" ")}`);
1525
+ sections.push(` ${f.finding_id}: ${f.remediation_command} ${f.remediation_args.join(" ")}`);
1604
1526
  sections.push(` ${f.description}`);
1605
1527
  }
1606
1528
  }
1607
1529
  if (skippedFindings.length > 0) {
1608
1530
  sections.push("\nWould skip (too risky for auto-execution):");
1609
1531
  for (const f of skippedFindings)
1610
- sections.push(` ⏭️ ${f.finding_id}: ${f.description} [${f.risk_level}]`);
1532
+ sections.push(` SKIPPED: ${f.finding_id}: ${f.description} [${f.risk_level}]`);
1611
1533
  }
1612
1534
  sections.push("\nSet dry_run=false to execute safe remediations.");
1613
1535
  return { content: [createTextContent(sections.join("\n"))] };
@@ -1621,46 +1543,46 @@ export function registerMetaTools(server) {
1621
1543
  summary: { total: 0, successful: 0, failed: 0, skipped: 0, rolled_back: 0 },
1622
1544
  };
1623
1545
  const sections = [];
1624
- sections.push("🔧 Auto-Remediation — LIVE EXECUTION");
1625
- sections.push("=".repeat(50));
1546
+ sections.push("Auto-Remediation — LIVE EXECUTION");
1547
+ sections.push("");
1626
1548
  sections.push(`Session ID: ${sessionId}\n`);
1627
1549
  for (const f of findings) {
1628
1550
  session.summary.total++;
1629
1551
  if (f.risk_level !== "safe") {
1630
1552
  session.actions.push({ finding_id: f.finding_id, description: f.description, remediation_command: f.remediation_command, remediation_args: f.remediation_args, rollback_command: f.rollback_command, rollback_args: f.rollback_args, before_state: "", after_state: "", status: "skipped", error: `risk_level is ${f.risk_level} (only safe actions auto-executed)`, timestamp: new Date().toISOString() });
1631
1553
  session.summary.skipped++;
1632
- sections.push(` ⏭️ ${f.finding_id}: SKIPPED (${f.risk_level} risk)`);
1554
+ sections.push(` SKIPPED: ${f.finding_id}: SKIPPED (${f.risk_level} risk)`);
1633
1555
  continue;
1634
1556
  }
1635
1557
  if (!REMEDIATION_ALLOWLIST.has(f.remediation_command)) {
1636
1558
  session.actions.push({ finding_id: f.finding_id, description: f.description, remediation_command: f.remediation_command, remediation_args: f.remediation_args, rollback_command: f.rollback_command, rollback_args: f.rollback_args, before_state: "", after_state: "", status: "skipped", error: `Command '${f.remediation_command}' not in remediation allowlist`, timestamp: new Date().toISOString() });
1637
1559
  session.summary.skipped++;
1638
- sections.push(` ⏭️ ${f.finding_id}: SKIPPED (command not in remediation allowlist)`);
1560
+ sections.push(` SKIPPED: ${f.finding_id}: SKIPPED (command not in remediation allowlist)`);
1639
1561
  continue;
1640
1562
  }
1641
1563
  let beforeState = "";
1642
1564
  const setArg = f.remediation_args.find(a => a.includes("="));
1643
1565
  if (setArg && f.remediation_command === "sysctl") {
1644
1566
  const key = setArg.substring(0, setArg.indexOf("="));
1645
- const beforeResult = await runRemediateCmd("sysctl", ["-n", key]);
1567
+ const beforeResult = await runCommand("sysctl", ["-n", key]);
1646
1568
  beforeState = beforeResult.stdout.trim();
1647
1569
  }
1648
- const result = await runRemediateCmd(f.remediation_command, f.remediation_args);
1570
+ const result = await runCommand(f.remediation_command, f.remediation_args);
1649
1571
  let afterState = "";
1650
1572
  if (setArg && f.remediation_command === "sysctl") {
1651
1573
  const key = setArg.substring(0, setArg.indexOf("="));
1652
- const afterResult = await runRemediateCmd("sysctl", ["-n", key]);
1574
+ const afterResult = await runCommand("sysctl", ["-n", key]);
1653
1575
  afterState = afterResult.stdout.trim();
1654
1576
  }
1655
1577
  if (result.exitCode === 0) {
1656
1578
  session.actions.push({ finding_id: f.finding_id, description: f.description, remediation_command: f.remediation_command, remediation_args: f.remediation_args, rollback_command: f.rollback_command, rollback_args: f.rollback_args, before_state: beforeState, after_state: afterState, status: "success", timestamp: new Date().toISOString() });
1657
1579
  session.summary.successful++;
1658
- sections.push(` ${f.finding_id}: ${f.description}`);
1580
+ sections.push(` ${f.finding_id}: ${f.description}`);
1659
1581
  }
1660
1582
  else {
1661
1583
  session.actions.push({ finding_id: f.finding_id, description: f.description, remediation_command: f.remediation_command, remediation_args: f.remediation_args, rollback_command: f.rollback_command, rollback_args: f.rollback_args, before_state: beforeState, after_state: afterState, status: "failed", error: result.stderr.substring(0, 200), timestamp: new Date().toISOString() });
1662
1584
  session.summary.failed++;
1663
- sections.push(` ${f.finding_id}: FAILED — ${result.stderr.substring(0, 100)}`);
1585
+ sections.push(` ${f.finding_id}: FAILED — ${result.stderr.substring(0, 100)}`);
1664
1586
  }
1665
1587
  }
1666
1588
  session.status = session.summary.failed === 0 && session.summary.skipped === 0 ? "completed" : session.summary.successful > 0 ? "partial" : "completed";
@@ -1670,7 +1592,7 @@ export function registerMetaTools(server) {
1670
1592
  sections.push(`\nSession saved: ${sessionPath}`);
1671
1593
  }
1672
1594
  catch (writeErr) {
1673
- sections.push(`\n⚠️ Failed to save session: ${writeErr instanceof Error ? writeErr.message : String(writeErr)}`);
1595
+ sections.push(`\nWARNING: Failed to save session: ${writeErr instanceof Error ? writeErr.message : String(writeErr)}`);
1674
1596
  }
1675
1597
  sections.push("\n── Summary ──");
1676
1598
  sections.push(` Total: ${session.summary.total} | Success: ${session.summary.successful} | Failed: ${session.summary.failed} | Skipped: ${session.summary.skipped}`);
@@ -1701,8 +1623,8 @@ export function registerMetaTools(server) {
1701
1623
  return { content: [createErrorContent(`Failed to parse session file: ${sessionPath}`)], isError: true };
1702
1624
  }
1703
1625
  const sections = [];
1704
- sections.push("Rollback Session");
1705
- sections.push("=".repeat(50));
1626
+ sections.push("Rollback Session");
1627
+ sections.push("");
1706
1628
  sections.push(`Session: ${session_id}`);
1707
1629
  const actionsToRollback = session.actions.filter(a => a.status === "success").reverse();
1708
1630
  if (actionsToRollback.length === 0) {
@@ -1713,15 +1635,15 @@ export function registerMetaTools(server) {
1713
1635
  }
1714
1636
  let rolledBack = 0, errors = 0;
1715
1637
  for (const action of actionsToRollback) {
1716
- const result = await runRemediateCmd(action.rollback_command, action.rollback_args);
1638
+ const result = await runCommand(action.rollback_command, action.rollback_args);
1717
1639
  if (result.exitCode === 0) {
1718
1640
  action.status = "rolled_back";
1719
1641
  rolledBack++;
1720
- sections.push(` Rolled back: ${action.finding_id} — ${action.description}`);
1642
+ sections.push(` Rolled back: ${action.finding_id} — ${action.description}`);
1721
1643
  }
1722
1644
  else {
1723
1645
  errors++;
1724
- sections.push(` Rollback failed: ${action.finding_id} — ${result.stderr.substring(0, 100)}`);
1646
+ sections.push(` Rollback failed: ${action.finding_id} — ${result.stderr.substring(0, 100)}`);
1725
1647
  }
1726
1648
  }
1727
1649
  session.status = "rolled_back";
@@ -1759,8 +1681,8 @@ export function registerMetaTools(server) {
1759
1681
  if (output_format === "json")
1760
1682
  return { content: [formatToolOutput(session)] };
1761
1683
  const sections = [];
1762
- sections.push("📊 Remediation Session Detail");
1763
- sections.push("=".repeat(50));
1684
+ sections.push("Remediation Session Detail");
1685
+ sections.push("");
1764
1686
  sections.push(`Session: ${session.session_id}`);
1765
1687
  sections.push(`Created: ${session.created_at}`);
1766
1688
  sections.push(`Status: ${session.status}`);
@@ -1801,8 +1723,8 @@ export function registerMetaTools(server) {
1801
1723
  if (output_format === "json")
1802
1724
  return { content: [formatToolOutput({ total_sessions: sessionSummaries.length, sessions: sessionSummaries })] };
1803
1725
  const sections = [];
1804
- sections.push("📊 Remediation Sessions");
1805
- sections.push("=".repeat(50));
1726
+ sections.push("Remediation Sessions");
1727
+ sections.push("");
1806
1728
  sections.push(`Total sessions: ${sessionSummaries.length}\n`);
1807
1729
  for (const s of sessionSummaries) {
1808
1730
  sections.push(` ${s.session_id}`);
@@ -1909,8 +1831,8 @@ export function registerMetaTools(server) {
1909
1831
  try {
1910
1832
  const statuses = await listThirdPartyTools();
1911
1833
  const sections = [];
1912
- sections.push("📦 Optional Third-Party Tool Status");
1913
- sections.push("=".repeat(50));
1834
+ sections.push("Optional Third-Party Tool Status");
1835
+ sections.push("");
1914
1836
  sections.push("");
1915
1837
  // Table header
1916
1838
  sections.push(" " +
@@ -1920,12 +1842,12 @@ export function registerMetaTools(server) {
1920
1842
  "Target".padEnd(12) +
1921
1843
  "Method".padEnd(16) +
1922
1844
  "Verification");
1923
- sections.push(" " + "".repeat(80));
1845
+ sections.push(" " + "");
1924
1846
  let installedCount = 0;
1925
1847
  let missingCount = 0;
1926
1848
  for (const status of statuses) {
1927
1849
  const entry = THIRD_PARTY_MANIFEST.find(e => e.binary === status.binary);
1928
- const installedStr = status.installed ? "yes" : "no";
1850
+ const installedStr = status.installed ? "yes" : "no";
1929
1851
  const currentVer = status.currentVersion ?? "—";
1930
1852
  const targetVer = status.manifestVersion;
1931
1853
  const method = entry?.installMethod ?? "unknown";
@@ -1956,7 +1878,7 @@ export function registerMetaTools(server) {
1956
1878
  }
1957
1879
  }
1958
1880
  sections.push("");
1959
- sections.push(" 💡 Run: defense_mgmt → install_optional_deps (dry_run=true) to see full install plan");
1881
+ sections.push(" Run: defense_mgmt → install_optional_deps (dry_run=true) to see full install plan");
1960
1882
  }
1961
1883
  return { content: [createTextContent(sections.join("\n"))] };
1962
1884
  }
@@ -1974,8 +1896,8 @@ export function registerMetaTools(server) {
1974
1896
  // Gate 1: Check DEFENSE_MCP_THIRD_PARTY_INSTALL env var
1975
1897
  if (!isThirdPartyInstallEnabled()) {
1976
1898
  const sections = [];
1977
- sections.push("🔒 Third-Party Installation Not Enabled");
1978
- sections.push("=".repeat(50));
1899
+ sections.push("Third-Party Installation Not Enabled");
1900
+ sections.push("");
1979
1901
  sections.push("");
1980
1902
  sections.push("SECURITY: Third-party tool installation requires explicit opt-in.");
1981
1903
  sections.push("Set the following environment variable in your MCP server configuration:");
@@ -1992,7 +1914,7 @@ export function registerMetaTools(server) {
1992
1914
  if (missing.length > 0) {
1993
1915
  sections.push("── Missing Optional Tools ──");
1994
1916
  for (const status of missing) {
1995
- sections.push(`\n 📦 ${status.name} (v${status.manifestVersion})`);
1917
+ sections.push(`\n ${status.name} (v${status.manifestVersion})`);
1996
1918
  const instructions = getVerifiedInstallInstructions(status.binary);
1997
1919
  // Show first few lines of instructions
1998
1920
  const instrLines = instructions.split("\n").slice(0, 3);
@@ -2002,7 +1924,7 @@ export function registerMetaTools(server) {
2002
1924
  }
2003
1925
  }
2004
1926
  else {
2005
- sections.push(" All optional tools are already installed.");
1927
+ sections.push(" All optional tools are already installed.");
2006
1928
  }
2007
1929
  return { content: [createTextContent(sections.join("\n"))] };
2008
1930
  }
@@ -2010,8 +1932,8 @@ export function registerMetaTools(server) {
2010
1932
  if (effectiveDryRun) {
2011
1933
  const statuses = await listThirdPartyTools();
2012
1934
  const sections = [];
2013
- sections.push("📋 Third-Party Tool Installation Plan [DRY RUN]");
2014
- sections.push("=".repeat(50));
1935
+ sections.push("Third-Party Tool Installation Plan [DRY RUN]");
1936
+ sections.push("");
2015
1937
  sections.push("");
2016
1938
  let planCount = 0;
2017
1939
  for (const status of statuses) {
@@ -2032,11 +1954,11 @@ export function registerMetaTools(server) {
2032
1954
  }
2033
1955
  }
2034
1956
  if (planCount === 0) {
2035
- sections.push(" All optional tools are already installed. Nothing to do.");
1957
+ sections.push(" All optional tools are already installed. Nothing to do.");
2036
1958
  if (toolName) {
2037
1959
  const found = statuses.find(s => s.binary === toolName);
2038
1960
  if (!found) {
2039
- sections.push(` ⚠️ Tool '${toolName}' is not in the third-party manifest.`);
1961
+ sections.push(` WARNING: Tool '${toolName}' is not in the third-party manifest.`);
2040
1962
  }
2041
1963
  }
2042
1964
  }
@@ -2050,11 +1972,11 @@ export function registerMetaTools(server) {
2050
1972
  // Gate 3: Live installation (dry_run=false AND DEFENSE_MCP_THIRD_PARTY_INSTALL=true)
2051
1973
  const statuses = await listThirdPartyTools();
2052
1974
  const sections = [];
2053
- sections.push("🔧 Third-Party Tool Installation [LIVE]");
2054
- sections.push("=".repeat(50));
1975
+ sections.push("Third-Party Tool Installation [LIVE]");
1976
+ sections.push("");
2055
1977
  sections.push("");
2056
1978
  if (!forceInstall) {
2057
- sections.push("⚠️ Note: The caller is responsible for confirming intent.");
1979
+ sections.push("WARNING: Note: The caller is responsible for confirming intent.");
2058
1980
  sections.push(" Use force=true to skip this notice for automation.");
2059
1981
  sections.push("");
2060
1982
  }
@@ -2067,24 +1989,24 @@ export function registerMetaTools(server) {
2067
1989
  continue;
2068
1990
  if (status.installed && !forceInstall) {
2069
1991
  skipCount++;
2070
- sections.push(` ⏭️ ${status.name} (${status.binary}) — already installed (v${status.currentVersion ?? "unknown"})`);
1992
+ sections.push(` SKIPPED: ${status.name} (${status.binary}) — already installed (v${status.currentVersion ?? "unknown"})`);
2071
1993
  continue;
2072
1994
  }
2073
- sections.push(` Installing ${status.name} v${status.manifestVersion}...`);
1995
+ sections.push(` Installing ${status.name} v${status.manifestVersion}...`);
2074
1996
  const result = await installThirdPartyTool(status.binary, { force: forceInstall });
2075
1997
  if (result.success) {
2076
1998
  successCount++;
2077
- sections.push(` ${result.message}`);
1999
+ sections.push(` ${result.message}`);
2078
2000
  }
2079
2001
  else {
2080
2002
  failCount++;
2081
- sections.push(` ${result.message}`);
2003
+ sections.push(` ${result.message}`);
2082
2004
  }
2083
2005
  }
2084
2006
  if (toolName) {
2085
2007
  const found = statuses.find(s => s.binary === toolName);
2086
2008
  if (!found) {
2087
- sections.push(` ⚠️ Tool '${toolName}' is not in the third-party manifest.`);
2009
+ sections.push(` WARNING: Tool '${toolName}' is not in the third-party manifest.`);
2088
2010
  sections.push(` Available tools: ${statuses.map(s => s.binary).join(", ")}`);
2089
2011
  }
2090
2012
  }
@@ -2171,7 +2093,6 @@ export function registerMetaTools(server) {
2171
2093
  const binResults = await verifyAllBinaries();
2172
2094
  const verified = binResults.filter(r => r.verified).length;
2173
2095
  const warnBins = binResults.filter(r => !r.verified).length;
2174
- const skipped = binResults.length === 0 ? 0 : 0;
2175
2096
  sections.push(` Verified: ${verified}, Warnings: ${warnBins}, Total: ${binResults.length}`);
2176
2097
  if (warnBins === 0) {
2177
2098
  score++;