defense-mcp-server 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/CHANGELOG.md +471 -0
  2. package/LICENSE +21 -0
  3. package/README.md +242 -0
  4. package/build/core/auto-installer.d.ts +102 -0
  5. package/build/core/auto-installer.d.ts.map +1 -0
  6. package/build/core/auto-installer.js +833 -0
  7. package/build/core/backup-manager.d.ts +63 -0
  8. package/build/core/backup-manager.d.ts.map +1 -0
  9. package/build/core/backup-manager.js +189 -0
  10. package/build/core/changelog.d.ts +75 -0
  11. package/build/core/changelog.d.ts.map +1 -0
  12. package/build/core/changelog.js +123 -0
  13. package/build/core/command-allowlist.d.ts +129 -0
  14. package/build/core/command-allowlist.d.ts.map +1 -0
  15. package/build/core/command-allowlist.js +849 -0
  16. package/build/core/config.d.ts +79 -0
  17. package/build/core/config.d.ts.map +1 -0
  18. package/build/core/config.js +193 -0
  19. package/build/core/dependency-validator.d.ts +106 -0
  20. package/build/core/dependency-validator.d.ts.map +1 -0
  21. package/build/core/dependency-validator.js +405 -0
  22. package/build/core/distro-adapter.d.ts +177 -0
  23. package/build/core/distro-adapter.d.ts.map +1 -0
  24. package/build/core/distro-adapter.js +481 -0
  25. package/build/core/distro.d.ts +68 -0
  26. package/build/core/distro.d.ts.map +1 -0
  27. package/build/core/distro.js +457 -0
  28. package/build/core/encrypted-state.d.ts +76 -0
  29. package/build/core/encrypted-state.d.ts.map +1 -0
  30. package/build/core/encrypted-state.js +209 -0
  31. package/build/core/executor.d.ts +56 -0
  32. package/build/core/executor.d.ts.map +1 -0
  33. package/build/core/executor.js +350 -0
  34. package/build/core/installer.d.ts +92 -0
  35. package/build/core/installer.d.ts.map +1 -0
  36. package/build/core/installer.js +1072 -0
  37. package/build/core/logger.d.ts +102 -0
  38. package/build/core/logger.d.ts.map +1 -0
  39. package/build/core/logger.js +132 -0
  40. package/build/core/parsers.d.ts +151 -0
  41. package/build/core/parsers.d.ts.map +1 -0
  42. package/build/core/parsers.js +479 -0
  43. package/build/core/policy-engine.d.ts +170 -0
  44. package/build/core/policy-engine.d.ts.map +1 -0
  45. package/build/core/policy-engine.js +656 -0
  46. package/build/core/preflight.d.ts +157 -0
  47. package/build/core/preflight.d.ts.map +1 -0
  48. package/build/core/preflight.js +638 -0
  49. package/build/core/privilege-manager.d.ts +108 -0
  50. package/build/core/privilege-manager.d.ts.map +1 -0
  51. package/build/core/privilege-manager.js +363 -0
  52. package/build/core/rate-limiter.d.ts +67 -0
  53. package/build/core/rate-limiter.d.ts.map +1 -0
  54. package/build/core/rate-limiter.js +129 -0
  55. package/build/core/rollback.d.ts +73 -0
  56. package/build/core/rollback.d.ts.map +1 -0
  57. package/build/core/rollback.js +278 -0
  58. package/build/core/safeguards.d.ts +58 -0
  59. package/build/core/safeguards.d.ts.map +1 -0
  60. package/build/core/safeguards.js +448 -0
  61. package/build/core/sanitizer.d.ts +118 -0
  62. package/build/core/sanitizer.d.ts.map +1 -0
  63. package/build/core/sanitizer.js +459 -0
  64. package/build/core/secure-fs.d.ts +67 -0
  65. package/build/core/secure-fs.d.ts.map +1 -0
  66. package/build/core/secure-fs.js +143 -0
  67. package/build/core/spawn-safe.d.ts +55 -0
  68. package/build/core/spawn-safe.d.ts.map +1 -0
  69. package/build/core/spawn-safe.js +146 -0
  70. package/build/core/sudo-guard.d.ts +145 -0
  71. package/build/core/sudo-guard.d.ts.map +1 -0
  72. package/build/core/sudo-guard.js +349 -0
  73. package/build/core/sudo-session.d.ts +100 -0
  74. package/build/core/sudo-session.d.ts.map +1 -0
  75. package/build/core/sudo-session.js +319 -0
  76. package/build/core/tool-dependencies.d.ts +61 -0
  77. package/build/core/tool-dependencies.d.ts.map +1 -0
  78. package/build/core/tool-dependencies.js +571 -0
  79. package/build/core/tool-registry.d.ts +111 -0
  80. package/build/core/tool-registry.d.ts.map +1 -0
  81. package/build/core/tool-registry.js +656 -0
  82. package/build/core/tool-wrapper.d.ts +73 -0
  83. package/build/core/tool-wrapper.d.ts.map +1 -0
  84. package/build/core/tool-wrapper.js +296 -0
  85. package/build/index.d.ts +3 -0
  86. package/build/index.d.ts.map +1 -0
  87. package/build/index.js +247 -0
  88. package/build/tools/access-control.d.ts +9 -0
  89. package/build/tools/access-control.d.ts.map +1 -0
  90. package/build/tools/access-control.js +1818 -0
  91. package/build/tools/api-security.d.ts +12 -0
  92. package/build/tools/api-security.d.ts.map +1 -0
  93. package/build/tools/api-security.js +901 -0
  94. package/build/tools/app-hardening.d.ts +11 -0
  95. package/build/tools/app-hardening.d.ts.map +1 -0
  96. package/build/tools/app-hardening.js +768 -0
  97. package/build/tools/backup.d.ts +8 -0
  98. package/build/tools/backup.d.ts.map +1 -0
  99. package/build/tools/backup.js +381 -0
  100. package/build/tools/cloud-security.d.ts +17 -0
  101. package/build/tools/cloud-security.d.ts.map +1 -0
  102. package/build/tools/cloud-security.js +739 -0
  103. package/build/tools/compliance.d.ts +10 -0
  104. package/build/tools/compliance.d.ts.map +1 -0
  105. package/build/tools/compliance.js +1225 -0
  106. package/build/tools/container-security.d.ts +14 -0
  107. package/build/tools/container-security.d.ts.map +1 -0
  108. package/build/tools/container-security.js +788 -0
  109. package/build/tools/deception.d.ts +13 -0
  110. package/build/tools/deception.d.ts.map +1 -0
  111. package/build/tools/deception.js +763 -0
  112. package/build/tools/dns-security.d.ts +93 -0
  113. package/build/tools/dns-security.d.ts.map +1 -0
  114. package/build/tools/dns-security.js +745 -0
  115. package/build/tools/drift-detection.d.ts +8 -0
  116. package/build/tools/drift-detection.d.ts.map +1 -0
  117. package/build/tools/drift-detection.js +326 -0
  118. package/build/tools/ebpf-security.d.ts +15 -0
  119. package/build/tools/ebpf-security.d.ts.map +1 -0
  120. package/build/tools/ebpf-security.js +294 -0
  121. package/build/tools/encryption.d.ts +9 -0
  122. package/build/tools/encryption.d.ts.map +1 -0
  123. package/build/tools/encryption.js +1667 -0
  124. package/build/tools/firewall.d.ts +9 -0
  125. package/build/tools/firewall.d.ts.map +1 -0
  126. package/build/tools/firewall.js +1398 -0
  127. package/build/tools/hardening.d.ts +10 -0
  128. package/build/tools/hardening.d.ts.map +1 -0
  129. package/build/tools/hardening.js +2654 -0
  130. package/build/tools/ids.d.ts +9 -0
  131. package/build/tools/ids.d.ts.map +1 -0
  132. package/build/tools/ids.js +624 -0
  133. package/build/tools/incident-response.d.ts +10 -0
  134. package/build/tools/incident-response.d.ts.map +1 -0
  135. package/build/tools/incident-response.js +1180 -0
  136. package/build/tools/logging.d.ts +12 -0
  137. package/build/tools/logging.d.ts.map +1 -0
  138. package/build/tools/logging.js +454 -0
  139. package/build/tools/malware.d.ts +10 -0
  140. package/build/tools/malware.d.ts.map +1 -0
  141. package/build/tools/malware.js +532 -0
  142. package/build/tools/meta.d.ts +11 -0
  143. package/build/tools/meta.d.ts.map +1 -0
  144. package/build/tools/meta.js +2278 -0
  145. package/build/tools/network-defense.d.ts +12 -0
  146. package/build/tools/network-defense.d.ts.map +1 -0
  147. package/build/tools/network-defense.js +760 -0
  148. package/build/tools/patch-management.d.ts +3 -0
  149. package/build/tools/patch-management.d.ts.map +1 -0
  150. package/build/tools/patch-management.js +708 -0
  151. package/build/tools/process-security.d.ts +12 -0
  152. package/build/tools/process-security.d.ts.map +1 -0
  153. package/build/tools/process-security.js +784 -0
  154. package/build/tools/reporting.d.ts +11 -0
  155. package/build/tools/reporting.d.ts.map +1 -0
  156. package/build/tools/reporting.js +559 -0
  157. package/build/tools/secrets.d.ts +9 -0
  158. package/build/tools/secrets.d.ts.map +1 -0
  159. package/build/tools/secrets.js +596 -0
  160. package/build/tools/siem-integration.d.ts +18 -0
  161. package/build/tools/siem-integration.d.ts.map +1 -0
  162. package/build/tools/siem-integration.js +754 -0
  163. package/build/tools/sudo-management.d.ts +18 -0
  164. package/build/tools/sudo-management.d.ts.map +1 -0
  165. package/build/tools/sudo-management.js +737 -0
  166. package/build/tools/supply-chain-security.d.ts +8 -0
  167. package/build/tools/supply-chain-security.d.ts.map +1 -0
  168. package/build/tools/supply-chain-security.js +256 -0
  169. package/build/tools/threat-intel.d.ts +22 -0
  170. package/build/tools/threat-intel.d.ts.map +1 -0
  171. package/build/tools/threat-intel.js +749 -0
  172. package/build/tools/vulnerability-management.d.ts +11 -0
  173. package/build/tools/vulnerability-management.d.ts.map +1 -0
  174. package/build/tools/vulnerability-management.js +667 -0
  175. package/build/tools/waf.d.ts +12 -0
  176. package/build/tools/waf.d.ts.map +1 -0
  177. package/build/tools/waf.js +843 -0
  178. package/build/tools/wireless-security.d.ts +19 -0
  179. package/build/tools/wireless-security.d.ts.map +1 -0
  180. package/build/tools/wireless-security.js +826 -0
  181. package/build/tools/zero-trust-network.d.ts +8 -0
  182. package/build/tools/zero-trust-network.d.ts.map +1 -0
  183. package/build/tools/zero-trust-network.js +367 -0
  184. package/docs/SAFEGUARDS.md +518 -0
  185. package/docs/TOOLS-REFERENCE.md +665 -0
  186. package/package.json +87 -0
@@ -0,0 +1,754 @@
1
+ /**
2
+ * SIEM integration tools for Kali Defense MCP Server.
3
+ *
4
+ * Registers 1 tool: siem_export (actions: configure_syslog_forward,
5
+ * configure_filebeat, audit_forwarding, test_connectivity)
6
+ *
7
+ * Provides syslog/rsyslog remote forwarding configuration, Filebeat
8
+ * auditing, comprehensive log forwarding audit with CIS benchmark
9
+ * references, and SIEM endpoint connectivity testing.
10
+ */
11
+ import { z } from "zod";
12
+ import { spawnSafe } from "../core/spawn-safe.js";
13
+ import { createTextContent, createErrorContent, formatToolOutput, } from "../core/parsers.js";
14
+ // ── Constants ──────────────────────────────────────────────────────────────────
15
+ /** Critical log sources that should be forwarded per CIS benchmarks */
16
+ const CRITICAL_LOG_SOURCES = ["auth", "syslog", "kern", "audit"];
17
+ /** Log file paths corresponding to critical sources */
18
+ const LOG_SOURCE_FILES = {
19
+ auth: "/var/log/auth.log",
20
+ syslog: "/var/log/syslog",
21
+ kern: "/var/log/kern.log",
22
+ audit: "/var/log/audit/audit.log",
23
+ };
24
+ /** Hostname/IP validation pattern */
25
+ const SIEM_HOST_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,253}[a-zA-Z0-9]$/;
26
+ /**
27
+ * Run a command via spawnSafe and collect output as a promise.
28
+ * Handles errors gracefully — returns error info instead of throwing.
29
+ */
30
+ async function runCommand(command, args, timeoutMs = 30_000) {
31
+ return new Promise((resolve) => {
32
+ let child;
33
+ try {
34
+ child = spawnSafe(command, args);
35
+ }
36
+ catch (err) {
37
+ const msg = err instanceof Error ? err.message : String(err);
38
+ resolve({ stdout: "", stderr: msg, exitCode: -1 });
39
+ return;
40
+ }
41
+ let stdout = "";
42
+ let stderr = "";
43
+ let resolved = false;
44
+ const timer = setTimeout(() => {
45
+ if (!resolved) {
46
+ resolved = true;
47
+ child.kill("SIGTERM");
48
+ resolve({ stdout, stderr: stderr + "\n[TIMEOUT]", exitCode: -1 });
49
+ }
50
+ }, timeoutMs);
51
+ child.stdout?.on("data", (data) => {
52
+ stdout += data.toString();
53
+ });
54
+ child.stderr?.on("data", (data) => {
55
+ stderr += data.toString();
56
+ });
57
+ child.on("close", (code) => {
58
+ if (!resolved) {
59
+ resolved = true;
60
+ clearTimeout(timer);
61
+ resolve({ stdout, stderr, exitCode: code ?? -1 });
62
+ }
63
+ });
64
+ child.on("error", (err) => {
65
+ if (!resolved) {
66
+ resolved = true;
67
+ clearTimeout(timer);
68
+ resolve({ stdout, stderr: err.message, exitCode: -1 });
69
+ }
70
+ });
71
+ });
72
+ }
73
+ // ── Host validation ────────────────────────────────────────────────────────────
74
+ /**
75
+ * Validate a SIEM host string (hostname or IP address).
76
+ * Returns true if the host looks reasonable.
77
+ */
78
+ export function validateSiemHost(host) {
79
+ if (!host || host.trim().length === 0)
80
+ return false;
81
+ const trimmed = host.trim();
82
+ // Allow IP addresses (v4)
83
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(trimmed))
84
+ return true;
85
+ // Allow hostnames
86
+ if (SIEM_HOST_PATTERN.test(trimmed))
87
+ return true;
88
+ return false;
89
+ }
90
+ async function configureSyslogForward(siemHost, siemPort, protocol, logSources) {
91
+ const result = {
92
+ syslogDaemon: "unknown",
93
+ daemonInstalled: false,
94
+ existingForwardingRules: [],
95
+ rsyslogModules: { imtcp: false, imudp: false },
96
+ tlsSupport: false,
97
+ currentConfig: "",
98
+ recommendedConfig: "",
99
+ recommendations: [],
100
+ };
101
+ const effectivePort = siemPort ?? 514;
102
+ const effectiveProtocol = protocol ?? "tcp";
103
+ // Check which syslog daemon is installed
104
+ const rsyslogCheck = await runCommand("dpkg", ["-l", "rsyslog"], 10_000);
105
+ const syslogNgCheck = await runCommand("dpkg", ["-l", "syslog-ng"], 10_000);
106
+ if (rsyslogCheck.exitCode === 0 && rsyslogCheck.stdout.includes("ii")) {
107
+ result.syslogDaemon = "rsyslog";
108
+ result.daemonInstalled = true;
109
+ }
110
+ else if (syslogNgCheck.exitCode === 0 && syslogNgCheck.stdout.includes("ii")) {
111
+ result.syslogDaemon = "syslog-ng";
112
+ result.daemonInstalled = true;
113
+ }
114
+ else {
115
+ result.syslogDaemon = "none";
116
+ result.daemonInstalled = false;
117
+ result.recommendations.push("No syslog daemon found — install rsyslog: apt-get install rsyslog");
118
+ return result;
119
+ }
120
+ // Read current rsyslog config
121
+ const configResult = await runCommand("cat", ["/etc/rsyslog.conf"], 10_000);
122
+ if (configResult.exitCode === 0) {
123
+ result.currentConfig = configResult.stdout;
124
+ // Check for existing remote forwarding rules
125
+ // rsyslog forwarding rules can be:
126
+ // *.* @@host:port (TCP)
127
+ // *.* @host:port (UDP)
128
+ // auth.* @@host:port
129
+ const lines = configResult.stdout.split("\n");
130
+ for (const line of lines) {
131
+ const trimmed = line.trim();
132
+ if (trimmed.startsWith("#") || trimmed.length === 0)
133
+ continue;
134
+ // Match lines with @@ (TCP forwarding) or @ (UDP forwarding) after facility
135
+ if (/\s@@[^\s]/.test(trimmed) || /\s@[^@\s]/.test(trimmed)) {
136
+ result.existingForwardingRules.push(trimmed);
137
+ }
138
+ }
139
+ // Check loaded modules
140
+ result.rsyslogModules.imtcp = configResult.stdout.includes("imtcp");
141
+ result.rsyslogModules.imudp = configResult.stdout.includes("imudp");
142
+ }
143
+ // Also check /etc/rsyslog.d/ for forwarding rules
144
+ const rsyslogDResult = await runCommand("cat", ["/etc/rsyslog.d/"], 10_000);
145
+ if (rsyslogDResult.exitCode === 0) {
146
+ const lines = rsyslogDResult.stdout.split("\n");
147
+ for (const line of lines) {
148
+ const trimmed = line.trim();
149
+ if (trimmed.startsWith("#") || trimmed.length === 0)
150
+ continue;
151
+ if (/\s@@[^\s]/.test(trimmed) || /\s@[^@\s]/.test(trimmed)) {
152
+ result.existingForwardingRules.push(trimmed);
153
+ }
154
+ }
155
+ }
156
+ // Check for TLS support (gtls driver)
157
+ const tlsCheck = await runCommand("dpkg", ["-l", "rsyslog-gnutls"], 10_000);
158
+ result.tlsSupport = tlsCheck.exitCode === 0 && tlsCheck.stdout.includes("ii");
159
+ // Generate recommended config if siem_host provided
160
+ if (siemHost) {
161
+ const forwardPrefix = effectiveProtocol === "udp" ? "@" : "@@";
162
+ const sources = logSources && logSources.length > 0 ? logSources : ["*.*"];
163
+ let config = "# SIEM forwarding configuration\n";
164
+ if (effectiveProtocol === "tls") {
165
+ config += '# TLS configuration\n';
166
+ config += '$DefaultNetstreamDriverCAFile /etc/rsyslog.d/ca.pem\n';
167
+ config += '$DefaultNetstreamDriver gtls\n';
168
+ config += '$ActionSendStreamDriverMode 1\n';
169
+ config += '$ActionSendStreamDriverAuthMode x509/name\n';
170
+ if (!result.tlsSupport) {
171
+ result.recommendations.push("TLS requested but rsyslog-gnutls not installed — install: apt-get install rsyslog-gnutls");
172
+ }
173
+ }
174
+ for (const source of sources) {
175
+ const facility = source === "*.*" ? "*.*" : `${source}.*`;
176
+ config += `${facility} ${forwardPrefix}${siemHost}:${effectivePort}\n`;
177
+ }
178
+ result.recommendedConfig = config;
179
+ }
180
+ // Recommendations
181
+ if (result.existingForwardingRules.length === 0) {
182
+ result.recommendations.push("No remote forwarding rules found — logs are not being forwarded to a SIEM");
183
+ }
184
+ else {
185
+ result.recommendations.push(`Found ${result.existingForwardingRules.length} forwarding rule(s) — verify they target the correct SIEM`);
186
+ }
187
+ if (!result.rsyslogModules.imtcp && effectiveProtocol === "tcp") {
188
+ result.recommendations.push("imtcp module not loaded — TCP reception may not work");
189
+ }
190
+ if (!result.tlsSupport) {
191
+ result.recommendations.push("TLS not available — consider installing rsyslog-gnutls for encrypted log forwarding");
192
+ }
193
+ return result;
194
+ }
195
+ async function configureFilebeat(siemHost, siemPort) {
196
+ const result = {
197
+ installed: false,
198
+ version: "unknown",
199
+ enabledModules: [],
200
+ disabledModules: [],
201
+ outputConfig: "",
202
+ serviceStatus: "unknown",
203
+ serviceRunning: false,
204
+ configPath: "/etc/filebeat/filebeat.yml",
205
+ recommendedConfig: "",
206
+ recommendations: [],
207
+ };
208
+ const effectivePort = siemPort ?? 5044;
209
+ // Check if filebeat is installed
210
+ const whichResult = await runCommand("which", ["filebeat"], 10_000);
211
+ if (whichResult.exitCode !== 0) {
212
+ const dpkgResult = await runCommand("dpkg", ["-l", "filebeat"], 10_000);
213
+ if (dpkgResult.exitCode !== 0 || !dpkgResult.stdout.includes("ii")) {
214
+ result.installed = false;
215
+ result.recommendations.push("Filebeat is not installed — install from Elastic repository for log shipping");
216
+ return result;
217
+ }
218
+ }
219
+ result.installed = true;
220
+ // Get version
221
+ const versionResult = await runCommand("filebeat", ["version"], 10_000);
222
+ if (versionResult.exitCode === 0) {
223
+ result.version = versionResult.stdout.trim();
224
+ }
225
+ // Read config
226
+ const configResult = await runCommand("cat", ["/etc/filebeat/filebeat.yml"], 10_000);
227
+ if (configResult.exitCode === 0) {
228
+ result.outputConfig = configResult.stdout;
229
+ // Parse output section
230
+ const lines = configResult.stdout.split("\n");
231
+ let inOutput = false;
232
+ for (const line of lines) {
233
+ if (line.match(/^output\./)) {
234
+ inOutput = true;
235
+ }
236
+ if (inOutput && line.trim().length > 0 && !line.startsWith(" ") && !line.startsWith("\t") && !line.match(/^output\./)) {
237
+ inOutput = false;
238
+ }
239
+ }
240
+ }
241
+ // Check enabled modules
242
+ const modulesResult = await runCommand("filebeat", ["modules", "list"], 15_000);
243
+ if (modulesResult.exitCode === 0) {
244
+ const lines = modulesResult.stdout.split("\n");
245
+ let inEnabled = false;
246
+ let inDisabled = false;
247
+ for (const line of lines) {
248
+ const trimmed = line.trim();
249
+ if (trimmed === "Enabled:") {
250
+ inEnabled = true;
251
+ inDisabled = false;
252
+ continue;
253
+ }
254
+ if (trimmed === "Disabled:") {
255
+ inEnabled = false;
256
+ inDisabled = true;
257
+ continue;
258
+ }
259
+ if (trimmed.length > 0) {
260
+ if (inEnabled)
261
+ result.enabledModules.push(trimmed);
262
+ if (inDisabled)
263
+ result.disabledModules.push(trimmed);
264
+ }
265
+ }
266
+ }
267
+ // Check service status
268
+ const serviceResult = await runCommand("systemctl", ["status", "filebeat"], 10_000);
269
+ if (serviceResult.exitCode === 0 || serviceResult.exitCode === 3) {
270
+ result.serviceStatus = serviceResult.stdout.trim();
271
+ result.serviceRunning = serviceResult.stdout.includes("active (running)");
272
+ }
273
+ // Generate recommended config if siem_host provided
274
+ if (siemHost) {
275
+ result.recommendedConfig =
276
+ "# Filebeat output configuration for Logstash\n" +
277
+ "output.logstash:\n" +
278
+ ` hosts: ["${siemHost}:${effectivePort}"]\n` +
279
+ " ssl.enabled: true\n" +
280
+ " ssl.certificate_authorities: ['/etc/filebeat/ca.pem']\n";
281
+ }
282
+ // Recommendations
283
+ if (!result.serviceRunning) {
284
+ result.recommendations.push("Filebeat service is not running — start with: systemctl start filebeat");
285
+ }
286
+ if (result.enabledModules.length === 0) {
287
+ result.recommendations.push("No Filebeat modules enabled — enable system module: filebeat modules enable system");
288
+ }
289
+ return result;
290
+ }
291
+ async function auditForwarding(logSources) {
292
+ const result = {
293
+ rsyslogForwarding: false,
294
+ rsyslogRules: [],
295
+ filebeatRunning: false,
296
+ filebeatStatus: "unknown",
297
+ criticalSourcesCovered: [],
298
+ missingSourcesCount: 0,
299
+ logRotationConfig: "",
300
+ logRotationInterferes: false,
301
+ cisBenchmark: "CIS Benchmark 4.2.1 — Ensure logging is configured to a remote log host",
302
+ cisCompliant: false,
303
+ recommendations: [],
304
+ };
305
+ const sourcesToCheck = logSources && logSources.length > 0 ? logSources : CRITICAL_LOG_SOURCES;
306
+ // Check rsyslog forwarding rules
307
+ const rsyslogConfig = await runCommand("cat", ["/etc/rsyslog.conf"], 10_000);
308
+ if (rsyslogConfig.exitCode === 0) {
309
+ const lines = rsyslogConfig.stdout.split("\n");
310
+ for (const line of lines) {
311
+ const trimmed = line.trim();
312
+ if (trimmed.startsWith("#") || trimmed.length === 0)
313
+ continue;
314
+ // Match lines with @@ (TCP forwarding) or @ (UDP forwarding) after facility
315
+ if (/\s@@[^\s]/.test(trimmed) || /\s@[^@\s]/.test(trimmed)) {
316
+ result.rsyslogRules.push(trimmed);
317
+ result.rsyslogForwarding = true;
318
+ }
319
+ // Also check for action-based forwarding
320
+ if (trimmed.includes("action(type=\"omfwd\"")) {
321
+ result.rsyslogRules.push(trimmed);
322
+ result.rsyslogForwarding = true;
323
+ }
324
+ }
325
+ }
326
+ // Check filebeat status
327
+ const filebeatResult = await runCommand("systemctl", ["status", "filebeat"], 10_000);
328
+ if (filebeatResult.exitCode === 0 || filebeatResult.exitCode === 3) {
329
+ result.filebeatStatus = filebeatResult.stdout.trim();
330
+ result.filebeatRunning = filebeatResult.stdout.includes("active (running)");
331
+ }
332
+ // Check coverage of critical log sources
333
+ const configContent = rsyslogConfig.exitCode === 0 ? rsyslogConfig.stdout : "";
334
+ for (const source of sourcesToCheck) {
335
+ const logPath = LOG_SOURCE_FILES[source] ?? `/var/log/${source}.log`;
336
+ let forwarded = false;
337
+ // Check if rsyslog has a forwarding rule that covers this source
338
+ if (result.rsyslogForwarding) {
339
+ // Wildcard rule covers everything
340
+ if (result.rsyslogRules.some((r) => r.includes("*.*"))) {
341
+ forwarded = true;
342
+ }
343
+ // Specific facility rule
344
+ if (result.rsyslogRules.some((r) => r.includes(`${source}.*`))) {
345
+ forwarded = true;
346
+ }
347
+ // Check if config mentions this source with forwarding
348
+ if (configContent.includes(source) && result.rsyslogForwarding) {
349
+ forwarded = true;
350
+ }
351
+ }
352
+ // Filebeat covers log sources via modules
353
+ if (result.filebeatRunning) {
354
+ forwarded = true; // Filebeat with system module covers standard logs
355
+ }
356
+ result.criticalSourcesCovered.push({ source, forwarded, path: logPath });
357
+ if (!forwarded) {
358
+ result.missingSourcesCount++;
359
+ }
360
+ }
361
+ // Check log rotation config
362
+ const logrotateResult = await runCommand("cat", ["/etc/logrotate.d/rsyslog"], 10_000);
363
+ if (logrotateResult.exitCode === 0) {
364
+ result.logRotationConfig = logrotateResult.stdout;
365
+ // Check for sharedscripts and postrotate that restarts rsyslog
366
+ if (!logrotateResult.stdout.includes("sharedscripts") ||
367
+ !logrotateResult.stdout.includes("postrotate")) {
368
+ result.logRotationInterferes = true;
369
+ result.recommendations.push("Log rotation may interfere with forwarding — ensure sharedscripts and postrotate with rsyslog reload are configured");
370
+ }
371
+ }
372
+ // CIS compliance check
373
+ result.cisCompliant = result.rsyslogForwarding || result.filebeatRunning;
374
+ // Recommendations
375
+ if (!result.rsyslogForwarding && !result.filebeatRunning) {
376
+ result.recommendations.push("CRITICAL: No log forwarding configured — logs are not being sent to a remote SIEM");
377
+ result.recommendations.push("Configure rsyslog forwarding or install Filebeat for centralized logging");
378
+ }
379
+ if (result.missingSourcesCount > 0) {
380
+ const missing = result.criticalSourcesCovered
381
+ .filter((s) => !s.forwarded)
382
+ .map((s) => s.source);
383
+ result.recommendations.push(`Missing forwarding for ${result.missingSourcesCount} critical source(s): ${missing.join(", ")}`);
384
+ }
385
+ if (!result.cisCompliant) {
386
+ result.recommendations.push(`Non-compliant with ${result.cisBenchmark}`);
387
+ }
388
+ return result;
389
+ }
390
+ async function testConnectivity(siemHost, siemPort, protocol) {
391
+ const effectivePort = siemPort ?? 514;
392
+ const effectiveProtocol = protocol ?? "tcp";
393
+ const result = {
394
+ siemHost,
395
+ siemPort: effectivePort,
396
+ protocol: effectiveProtocol,
397
+ tcpConnectivity: false,
398
+ tcpMessage: "not tested",
399
+ tlsVerification: false,
400
+ tlsMessage: "not tested",
401
+ dnsResolution: false,
402
+ dnsResult: "not tested",
403
+ firewallStatus: "not checked",
404
+ firewallBlocked: false,
405
+ testMessageSent: false,
406
+ testMessageResult: "not attempted",
407
+ recommendations: [],
408
+ };
409
+ // Test DNS resolution
410
+ const digResult = await runCommand("dig", [siemHost], 15_000);
411
+ if (digResult.exitCode === 0 && digResult.stdout.includes("ANSWER SECTION")) {
412
+ result.dnsResolution = true;
413
+ result.dnsResult = "resolved successfully";
414
+ }
415
+ else if (digResult.exitCode === 0) {
416
+ result.dnsResolution = false;
417
+ result.dnsResult = "DNS query returned no results";
418
+ result.recommendations.push(`DNS resolution failed for ${siemHost} — verify hostname is correct`);
419
+ }
420
+ else {
421
+ result.dnsResolution = false;
422
+ result.dnsResult = digResult.stderr || "dig command failed";
423
+ result.recommendations.push("dig command not available — install dnsutils for DNS testing");
424
+ }
425
+ // Test TCP connectivity
426
+ const ncResult = await runCommand("nc", ["-z", "-w", "5", siemHost, String(effectivePort)], 15_000);
427
+ if (ncResult.exitCode === 0) {
428
+ result.tcpConnectivity = true;
429
+ result.tcpMessage = `TCP connection to ${siemHost}:${effectivePort} successful`;
430
+ }
431
+ else {
432
+ result.tcpConnectivity = false;
433
+ result.tcpMessage = `TCP connection to ${siemHost}:${effectivePort} failed`;
434
+ result.recommendations.push(`Cannot reach ${siemHost}:${effectivePort} — check network connectivity and firewall rules`);
435
+ }
436
+ // Test TLS if protocol is TLS
437
+ if (effectiveProtocol === "tls") {
438
+ const opensslResult = await runCommand("openssl", ["s_client", "-connect", `${siemHost}:${effectivePort}`, "-brief"], 15_000);
439
+ if (opensslResult.exitCode === 0 && opensslResult.stdout.includes("Verification")) {
440
+ result.tlsVerification = true;
441
+ result.tlsMessage = "TLS handshake successful";
442
+ }
443
+ else if (opensslResult.exitCode === 0) {
444
+ result.tlsVerification = false;
445
+ result.tlsMessage = opensslResult.stderr || "TLS handshake completed with warnings";
446
+ result.recommendations.push("TLS certificate verification may have issues — check CA certificates");
447
+ }
448
+ else {
449
+ result.tlsVerification = false;
450
+ result.tlsMessage = opensslResult.stderr || "TLS handshake failed";
451
+ result.recommendations.push("TLS connection failed — verify the SIEM supports TLS on this port");
452
+ }
453
+ }
454
+ // Check firewall rules for the port
455
+ const iptablesResult = await runCommand("iptables", ["-L", "-n"], 10_000);
456
+ if (iptablesResult.exitCode === 0) {
457
+ const lines = iptablesResult.stdout.split("\n");
458
+ const portStr = String(effectivePort);
459
+ for (const line of lines) {
460
+ if (line.includes(portStr)) {
461
+ result.firewallStatus = line.trim();
462
+ if (line.includes("DROP") || line.includes("REJECT")) {
463
+ result.firewallBlocked = true;
464
+ result.recommendations.push(`Firewall rule blocking port ${effectivePort} detected — update iptables to allow SIEM traffic`);
465
+ }
466
+ }
467
+ }
468
+ if (result.firewallStatus === "not checked") {
469
+ result.firewallStatus = `No specific rules found for port ${effectivePort}`;
470
+ }
471
+ }
472
+ else {
473
+ result.firewallStatus = "iptables not accessible (may need root)";
474
+ }
475
+ // Send test syslog message
476
+ const loggerResult = await runCommand("logger", ["-n", siemHost, "-P", String(effectivePort), "--tcp", "kali-defense SIEM connectivity test"], 15_000);
477
+ if (loggerResult.exitCode === 0) {
478
+ result.testMessageSent = true;
479
+ result.testMessageResult = "Test syslog message sent successfully";
480
+ }
481
+ else {
482
+ result.testMessageSent = false;
483
+ result.testMessageResult = loggerResult.stderr || "Failed to send test message";
484
+ if (loggerResult.stderr?.includes("not found")) {
485
+ result.recommendations.push("logger command not available — install bsdutils for syslog testing");
486
+ }
487
+ }
488
+ // Overall connectivity assessment
489
+ if (!result.tcpConnectivity && !result.dnsResolution) {
490
+ result.recommendations.push("CRITICAL: SIEM endpoint is unreachable — verify host, port, and network path");
491
+ }
492
+ else if (!result.tcpConnectivity) {
493
+ result.recommendations.push("DNS resolves but TCP connection fails — check if SIEM is listening on the specified port");
494
+ }
495
+ return result;
496
+ }
497
+ // ── Registration entry point ───────────────────────────────────────────────
498
+ export function registerSiemIntegrationTools(server) {
499
+ server.tool("siem_export", "SIEM integration: configure syslog forwarding, audit Filebeat, comprehensive log forwarding audit with CIS benchmarks, and test SIEM endpoint connectivity.", {
500
+ action: z
501
+ .enum(["configure_syslog_forward", "configure_filebeat", "audit_forwarding", "test_connectivity"])
502
+ .describe("Action: configure_syslog_forward=audit/configure rsyslog remote forwarding, configure_filebeat=audit Filebeat configuration, audit_forwarding=comprehensive log forwarding audit, test_connectivity=test SIEM endpoint connectivity"),
503
+ siem_host: z
504
+ .string()
505
+ .optional()
506
+ .describe("SIEM server hostname or IP address"),
507
+ siem_port: z
508
+ .number()
509
+ .optional()
510
+ .describe("SIEM server port (default 514 for syslog, 5044 for filebeat)"),
511
+ protocol: z
512
+ .enum(["tcp", "udp", "tls"])
513
+ .optional()
514
+ .default("tcp")
515
+ .describe("Transport protocol (default tcp)"),
516
+ log_sources: z
517
+ .array(z.string())
518
+ .optional()
519
+ .describe("Log sources to forward (e.g., auth, syslog, kern, audit)"),
520
+ output_format: z
521
+ .enum(["text", "json"])
522
+ .optional()
523
+ .default("text")
524
+ .describe("Output format (default text)"),
525
+ }, async (params) => {
526
+ const { action } = params;
527
+ const outputFormat = params.output_format ?? "text";
528
+ switch (action) {
529
+ // ── configure_syslog_forward ─────────────────────────────────────
530
+ case "configure_syslog_forward": {
531
+ try {
532
+ const syslog = await configureSyslogForward(params.siem_host, params.siem_port, params.protocol, params.log_sources);
533
+ const output = {
534
+ action: "configure_syslog_forward",
535
+ syslogDaemon: syslog.syslogDaemon,
536
+ daemonInstalled: syslog.daemonInstalled,
537
+ existingForwardingRules: syslog.existingForwardingRules,
538
+ rsyslogModules: syslog.rsyslogModules,
539
+ tlsSupport: syslog.tlsSupport,
540
+ recommendedConfig: syslog.recommendedConfig,
541
+ recommendations: syslog.recommendations,
542
+ };
543
+ if (outputFormat === "json") {
544
+ return { content: [formatToolOutput(output)] };
545
+ }
546
+ let text = "SIEM Integration — Syslog Forwarding Configuration\n\n";
547
+ text += `Syslog Daemon: ${syslog.syslogDaemon}\n`;
548
+ text += `Daemon Installed: ${syslog.daemonInstalled ? "yes" : "no"}\n`;
549
+ text += `TLS Support: ${syslog.tlsSupport ? "available" : "not available"}\n`;
550
+ text += `Modules — imtcp: ${syslog.rsyslogModules.imtcp ? "loaded" : "not loaded"}, imudp: ${syslog.rsyslogModules.imudp ? "loaded" : "not loaded"}\n`;
551
+ if (syslog.existingForwardingRules.length > 0) {
552
+ text += `\nExisting Forwarding Rules (${syslog.existingForwardingRules.length}):\n`;
553
+ for (const rule of syslog.existingForwardingRules) {
554
+ text += ` • ${rule}\n`;
555
+ }
556
+ }
557
+ else {
558
+ text += "\nExisting Forwarding Rules: none\n";
559
+ }
560
+ if (syslog.recommendedConfig) {
561
+ text += `\nRecommended Configuration:\n${syslog.recommendedConfig}\n`;
562
+ }
563
+ if (syslog.recommendations.length > 0) {
564
+ text += "\nRecommendations:\n";
565
+ for (const rec of syslog.recommendations) {
566
+ text += ` • ${rec}\n`;
567
+ }
568
+ }
569
+ return { content: [createTextContent(text)] };
570
+ }
571
+ catch (err) {
572
+ const msg = err instanceof Error ? err.message : String(err);
573
+ return { content: [createErrorContent(`configure_syslog_forward failed: ${msg}`)], isError: true };
574
+ }
575
+ }
576
+ // ── configure_filebeat ───────────────────────────────────────────
577
+ case "configure_filebeat": {
578
+ try {
579
+ const filebeat = await configureFilebeat(params.siem_host, params.siem_port);
580
+ const output = {
581
+ action: "configure_filebeat",
582
+ installed: filebeat.installed,
583
+ version: filebeat.version,
584
+ enabledModules: filebeat.enabledModules,
585
+ disabledModules: filebeat.disabledModules,
586
+ serviceRunning: filebeat.serviceRunning,
587
+ serviceStatus: filebeat.serviceStatus,
588
+ configPath: filebeat.configPath,
589
+ recommendedConfig: filebeat.recommendedConfig,
590
+ recommendations: filebeat.recommendations,
591
+ };
592
+ if (outputFormat === "json") {
593
+ return { content: [formatToolOutput(output)] };
594
+ }
595
+ let text = "SIEM Integration — Filebeat Configuration\n\n";
596
+ text += `Installed: ${filebeat.installed ? "yes" : "no"}\n`;
597
+ if (!filebeat.installed) {
598
+ text += "\nFilebeat is not installed.\n";
599
+ }
600
+ else {
601
+ text += `Version: ${filebeat.version}\n`;
602
+ text += `Service Running: ${filebeat.serviceRunning ? "yes ✓" : "no ⚠"}\n`;
603
+ text += `Config Path: ${filebeat.configPath}\n`;
604
+ if (filebeat.enabledModules.length > 0) {
605
+ text += `\nEnabled Modules (${filebeat.enabledModules.length}):\n`;
606
+ for (const mod of filebeat.enabledModules) {
607
+ text += ` • ${mod}\n`;
608
+ }
609
+ }
610
+ else {
611
+ text += "\nEnabled Modules: none\n";
612
+ }
613
+ if (filebeat.disabledModules.length > 0) {
614
+ text += `\nDisabled Modules (${filebeat.disabledModules.length}):\n`;
615
+ for (const mod of filebeat.disabledModules.slice(0, 10)) {
616
+ text += ` • ${mod}\n`;
617
+ }
618
+ if (filebeat.disabledModules.length > 10) {
619
+ text += ` ... and ${filebeat.disabledModules.length - 10} more\n`;
620
+ }
621
+ }
622
+ }
623
+ if (filebeat.recommendedConfig) {
624
+ text += `\nRecommended Output Configuration:\n${filebeat.recommendedConfig}\n`;
625
+ }
626
+ if (filebeat.recommendations.length > 0) {
627
+ text += "\nRecommendations:\n";
628
+ for (const rec of filebeat.recommendations) {
629
+ text += ` • ${rec}\n`;
630
+ }
631
+ }
632
+ return { content: [createTextContent(text)] };
633
+ }
634
+ catch (err) {
635
+ const msg = err instanceof Error ? err.message : String(err);
636
+ return { content: [createErrorContent(`configure_filebeat failed: ${msg}`)], isError: true };
637
+ }
638
+ }
639
+ // ── audit_forwarding ─────────────────────────────────────────────
640
+ case "audit_forwarding": {
641
+ try {
642
+ const audit = await auditForwarding(params.log_sources);
643
+ const output = {
644
+ action: "audit_forwarding",
645
+ rsyslogForwarding: audit.rsyslogForwarding,
646
+ rsyslogRules: audit.rsyslogRules,
647
+ filebeatRunning: audit.filebeatRunning,
648
+ criticalSourcesCovered: audit.criticalSourcesCovered,
649
+ missingSourcesCount: audit.missingSourcesCount,
650
+ logRotationInterferes: audit.logRotationInterferes,
651
+ cisBenchmark: audit.cisBenchmark,
652
+ cisCompliant: audit.cisCompliant,
653
+ recommendations: audit.recommendations,
654
+ };
655
+ if (outputFormat === "json") {
656
+ return { content: [formatToolOutput(output)] };
657
+ }
658
+ let text = "SIEM Integration — Log Forwarding Audit\n\n";
659
+ text += `CIS Reference: ${audit.cisBenchmark}\n`;
660
+ text += `CIS Compliant: ${audit.cisCompliant ? "YES ✓" : "NO ⚠"}\n\n`;
661
+ text += `Rsyslog Forwarding: ${audit.rsyslogForwarding ? "configured" : "not configured"}\n`;
662
+ text += `Filebeat Running: ${audit.filebeatRunning ? "yes" : "no"}\n`;
663
+ if (audit.rsyslogRules.length > 0) {
664
+ text += `\nRsyslog Forwarding Rules (${audit.rsyslogRules.length}):\n`;
665
+ for (const rule of audit.rsyslogRules) {
666
+ text += ` • ${rule}\n`;
667
+ }
668
+ }
669
+ text += `\nCritical Log Source Coverage:\n`;
670
+ for (const source of audit.criticalSourcesCovered) {
671
+ text += ` • ${source.source} (${source.path}): ${source.forwarded ? "forwarded ✓" : "NOT forwarded ⚠"}\n`;
672
+ }
673
+ if (audit.missingSourcesCount > 0) {
674
+ text += `\n⚠ Missing Sources: ${audit.missingSourcesCount}\n`;
675
+ }
676
+ if (audit.logRotationInterferes) {
677
+ text += "\n⚠ Log rotation may interfere with forwarding\n";
678
+ }
679
+ if (audit.recommendations.length > 0) {
680
+ text += "\nRecommendations:\n";
681
+ for (const rec of audit.recommendations) {
682
+ text += ` • ${rec}\n`;
683
+ }
684
+ }
685
+ return { content: [createTextContent(text)] };
686
+ }
687
+ catch (err) {
688
+ const msg = err instanceof Error ? err.message : String(err);
689
+ return { content: [createErrorContent(`audit_forwarding failed: ${msg}`)], isError: true };
690
+ }
691
+ }
692
+ // ── test_connectivity ────────────────────────────────────────────
693
+ case "test_connectivity": {
694
+ try {
695
+ if (!params.siem_host) {
696
+ return {
697
+ content: [createErrorContent("test_connectivity requires siem_host parameter")],
698
+ isError: true,
699
+ };
700
+ }
701
+ if (!validateSiemHost(params.siem_host)) {
702
+ return {
703
+ content: [createErrorContent(`Invalid siem_host format: ${params.siem_host}`)],
704
+ isError: true,
705
+ };
706
+ }
707
+ const connectivity = await testConnectivity(params.siem_host, params.siem_port, params.protocol);
708
+ const output = {
709
+ action: "test_connectivity",
710
+ siemHost: connectivity.siemHost,
711
+ siemPort: connectivity.siemPort,
712
+ protocol: connectivity.protocol,
713
+ tcpConnectivity: connectivity.tcpConnectivity,
714
+ tcpMessage: connectivity.tcpMessage,
715
+ tlsVerification: connectivity.tlsVerification,
716
+ tlsMessage: connectivity.tlsMessage,
717
+ dnsResolution: connectivity.dnsResolution,
718
+ dnsResult: connectivity.dnsResult,
719
+ firewallStatus: connectivity.firewallStatus,
720
+ firewallBlocked: connectivity.firewallBlocked,
721
+ testMessageSent: connectivity.testMessageSent,
722
+ testMessageResult: connectivity.testMessageResult,
723
+ recommendations: connectivity.recommendations,
724
+ };
725
+ if (outputFormat === "json") {
726
+ return { content: [formatToolOutput(output)] };
727
+ }
728
+ let text = "SIEM Integration — Connectivity Test\n\n";
729
+ text += `Target: ${connectivity.siemHost}:${connectivity.siemPort} (${connectivity.protocol})\n\n`;
730
+ text += `DNS Resolution: ${connectivity.dnsResolution ? "✓ " : "✗ "}${connectivity.dnsResult}\n`;
731
+ text += `TCP Connectivity: ${connectivity.tcpConnectivity ? "✓ " : "✗ "}${connectivity.tcpMessage}\n`;
732
+ if (connectivity.protocol === "tls") {
733
+ text += `TLS Verification: ${connectivity.tlsVerification ? "✓ " : "✗ "}${connectivity.tlsMessage}\n`;
734
+ }
735
+ text += `Firewall: ${connectivity.firewallBlocked ? "⚠ BLOCKED" : connectivity.firewallStatus}\n`;
736
+ text += `Test Message: ${connectivity.testMessageSent ? "✓ " : "✗ "}${connectivity.testMessageResult}\n`;
737
+ if (connectivity.recommendations.length > 0) {
738
+ text += "\nRecommendations:\n";
739
+ for (const rec of connectivity.recommendations) {
740
+ text += ` • ${rec}\n`;
741
+ }
742
+ }
743
+ return { content: [createTextContent(text)] };
744
+ }
745
+ catch (err) {
746
+ const msg = err instanceof Error ? err.message : String(err);
747
+ return { content: [createErrorContent(`test_connectivity failed: ${msg}`)], isError: true };
748
+ }
749
+ }
750
+ default:
751
+ return { content: [createErrorContent(`Unknown action: ${action}`)], isError: true };
752
+ }
753
+ });
754
+ }