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,596 @@
1
+ /**
2
+ * Secrets tools for Kali Defense MCP Server.
3
+ *
4
+ * Registers 4 tools: secrets_scan, secrets_env_audit,
5
+ * secrets_ssh_key_sprawl, scan_git_history.
6
+ */
7
+ import { z } from "zod";
8
+ import { executeCommand } from "../core/executor.js";
9
+ import { createTextContent, createErrorContent, formatToolOutput, } from "../core/parsers.js";
10
+ // ── TOOL-021 remediation: error message sanitization ───────────────────────
11
+ /** Patterns for sensitive environment variable names */
12
+ const SENSITIVE_ENV_PATTERNS = [
13
+ /SECRET/i,
14
+ /KEY/i,
15
+ /TOKEN/i,
16
+ /PASSWORD/i,
17
+ /PASSWD/i,
18
+ /CREDENTIAL/i,
19
+ /AUTH/i,
20
+ /API_KEY/i,
21
+ /PRIVATE/i,
22
+ ];
23
+ /**
24
+ * Sanitize an error message by redacting potential environment variable values
25
+ * and other sensitive data that might be exposed in error output.
26
+ * Never logs the full process.env object.
27
+ */
28
+ function sanitizeErrorMessage(message) {
29
+ let sanitized = message;
30
+ // Redact values of known-sensitive environment variables
31
+ for (const [key, value] of Object.entries(process.env)) {
32
+ if (!value || value.length < 4)
33
+ continue; // Skip very short values
34
+ const isSensitive = SENSITIVE_ENV_PATTERNS.some((pattern) => pattern.test(key));
35
+ if (isSensitive) {
36
+ // Replace all occurrences of the sensitive value
37
+ sanitized = sanitized.split(value).join("[REDACTED]");
38
+ }
39
+ }
40
+ // Redact common token-like patterns in the message itself
41
+ sanitized = sanitized.replace(/(ghp_|gho_|github_pat_|sk-|sk_live_|AKIA|glpat-|glrt-|hvs\.)[A-Za-z0-9_\-]{8,}/g, "$1[REDACTED]");
42
+ // Redact long base64-like strings that may be tokens/keys (40+ chars)
43
+ sanitized = sanitized.replace(/(?<==|:\s*)[A-Za-z0-9+/]{40,}={0,2}/g, "[REDACTED]");
44
+ return sanitized;
45
+ }
46
+ // ── Registration entry point ───────────────────────────────────────────────
47
+ export function registerSecretsTools(server) {
48
+ // ── 1. secrets_scan ────────────────────────────────────────────────────
49
+ server.tool("secrets_scan", "Scan filesystem for hardcoded secrets (API keys, passwords, private keys, tokens)", {
50
+ path: z
51
+ .string()
52
+ .default("/home")
53
+ .describe("Root path to scan for secrets"),
54
+ scan_type: z
55
+ .enum(["all", "api_keys", "private_keys", "passwords", "tokens"])
56
+ .default("all")
57
+ .describe("Type of secrets to scan for"),
58
+ max_depth: z
59
+ .number()
60
+ .optional()
61
+ .default(5)
62
+ .describe("Maximum directory depth to search"),
63
+ }, async ({ path: scanPath, scan_type, max_depth }) => {
64
+ try {
65
+ const findings = {};
66
+ // ── API Keys ───────────────────────────────────────────────────
67
+ if (scan_type === "all" || scan_type === "api_keys") {
68
+ const result = await executeCommand({
69
+ command: "grep",
70
+ args: [
71
+ "-rnl",
72
+ "--include=*.py", "--include=*.js", "--include=*.ts",
73
+ "--include=*.rb", "--include=*.go", "--include=*.java",
74
+ "--include=*.yaml", "--include=*.yml", "--include=*.json",
75
+ "--include=*.xml", "--include=*.conf", "--include=*.cfg",
76
+ "--include=*.env", "--include=*.ini", "--include=*.sh",
77
+ `-E`,
78
+ `(api[_-]?key|apikey|api[_-]?secret)\\s*[:=]`,
79
+ `--max-depth=${max_depth}`,
80
+ scanPath,
81
+ ],
82
+ toolName: "secrets_scan",
83
+ timeout: 60000,
84
+ });
85
+ const files = result.stdout.split("\n").filter((l) => l.trim());
86
+ if (files.length > 0) {
87
+ findings["api_keys"] = files;
88
+ }
89
+ }
90
+ // ── Private Keys ───────────────────────────────────────────────
91
+ if (scan_type === "all" || scan_type === "private_keys") {
92
+ const result = await executeCommand({
93
+ command: "find",
94
+ args: [
95
+ scanPath,
96
+ "-maxdepth", String(max_depth),
97
+ "(", "-name", "*.pem",
98
+ "-o", "-name", "*.key",
99
+ "-o", "-name", "id_rsa",
100
+ "-o", "-name", "id_ecdsa",
101
+ "-o", "-name", "id_ed25519",
102
+ "-o", "-name", "*.p12",
103
+ "-o", "-name", "*.pfx",
104
+ ")",
105
+ "-type", "f",
106
+ ],
107
+ toolName: "secrets_scan",
108
+ timeout: 60000,
109
+ });
110
+ const files = result.stdout.split("\n").filter((l) => l.trim());
111
+ if (files.length > 0) {
112
+ // Check permissions on each found key file
113
+ const keyDetails = [];
114
+ for (const file of files.slice(0, 50)) {
115
+ const statResult = await executeCommand({
116
+ command: "stat",
117
+ args: ["-c", "%a %U:%G %n", file],
118
+ toolName: "secrets_scan",
119
+ timeout: 5000,
120
+ });
121
+ if (statResult.exitCode === 0 && statResult.stdout.trim()) {
122
+ const perms = statResult.stdout.trim().split(" ")[0];
123
+ const permWarning = perms !== "600" && perms !== "400"
124
+ ? " [WARNING: insecure permissions]"
125
+ : " [OK]";
126
+ keyDetails.push(`${statResult.stdout.trim()}${permWarning}`);
127
+ }
128
+ }
129
+ findings["private_keys"] = keyDetails;
130
+ }
131
+ }
132
+ // ── Passwords ──────────────────────────────────────────────────
133
+ if (scan_type === "all" || scan_type === "passwords") {
134
+ const result = await executeCommand({
135
+ command: "grep",
136
+ args: [
137
+ "-rnl",
138
+ "--include=*.py", "--include=*.js", "--include=*.ts",
139
+ "--include=*.rb", "--include=*.conf", "--include=*.cfg",
140
+ "--include=*.env", "--include=*.ini", "--include=*.yaml",
141
+ "--include=*.yml", "--include=*.json",
142
+ `-E`,
143
+ `(password|passwd|secret|token)\\s*[:=]\\s*['"][^'"]{4,}`,
144
+ `--max-depth=${max_depth}`,
145
+ scanPath,
146
+ ],
147
+ toolName: "secrets_scan",
148
+ timeout: 60000,
149
+ });
150
+ const files = result.stdout.split("\n").filter((l) => l.trim());
151
+ if (files.length > 0) {
152
+ findings["passwords"] = files;
153
+ }
154
+ }
155
+ // ── Tokens ─────────────────────────────────────────────────────
156
+ if (scan_type === "all" || scan_type === "tokens") {
157
+ const tokenPatterns = [
158
+ "ghp_", // GitHub personal access token
159
+ "gho_", // GitHub OAuth token
160
+ "github_pat_", // GitHub fine-grained PAT
161
+ "sk-", // OpenAI API key
162
+ "sk_live_[A-Za-z0-9]{24,}", // Stripe live secret key
163
+ "rk_live_", // Stripe restricted key
164
+ "AKIA[0-9A-Z]", // AWS access key ID
165
+ "xox[bpors]-[A-Za-z0-9-]{10,}", // Slack tokens (bot/user/app)
166
+ "glpat-[A-Za-z0-9_-]{20,}", // GitLab personal access token
167
+ "glrt-[A-Za-z0-9_-]{20,}", // GitLab pipeline token
168
+ "hvs\\.[A-Za-z0-9_-]{24,}", // HashiCorp Vault token (new format)
169
+ "s\\.[A-Za-z0-9]{24}", // HashiCorp Vault token (legacy)
170
+ '"type":.*"service_account"', // GCP service account key
171
+ "AZURE_(CLIENT_SECRET|TENANT_ID|SUBSCRIPTION_KEY)", // Azure credentials
172
+ "[A-Za-z0-9+/]{40,}={0,2}", // Generic high-entropy base64 secrets
173
+ ];
174
+ const result = await executeCommand({
175
+ command: "grep",
176
+ args: [
177
+ "-rnl",
178
+ `-E`,
179
+ `(${tokenPatterns.join("|")})`,
180
+ `--max-depth=${max_depth}`,
181
+ scanPath,
182
+ ],
183
+ toolName: "secrets_scan",
184
+ timeout: 60000,
185
+ });
186
+ const files = result.stdout.split("\n").filter((l) => l.trim());
187
+ if (files.length > 0) {
188
+ findings["tokens"] = files;
189
+ }
190
+ }
191
+ // ── Build report ───────────────────────────────────────────────
192
+ const totalFindings = Object.values(findings).reduce((sum, arr) => sum + arr.length, 0);
193
+ const lines = [
194
+ `=== Secrets Scan Report ===`,
195
+ `Path: ${scanPath}`,
196
+ `Scan Type: ${scan_type}`,
197
+ `Max Depth: ${max_depth}`,
198
+ `Total Findings: ${totalFindings}`,
199
+ ``,
200
+ ];
201
+ for (const [category, files] of Object.entries(findings)) {
202
+ lines.push(`── ${category.toUpperCase()} (${files.length} found) ──`);
203
+ for (const file of files) {
204
+ lines.push(` ${file}`);
205
+ }
206
+ lines.push(``);
207
+ }
208
+ if (totalFindings === 0) {
209
+ lines.push(`No hardcoded secrets detected in the scanned path.`);
210
+ }
211
+ return { content: [createTextContent(lines.join("\n"))] };
212
+ }
213
+ catch (err) {
214
+ const msg = err instanceof Error ? err.message : String(err);
215
+ // TOOL-021: Sanitize error message to avoid leaking sensitive data
216
+ return { content: [createErrorContent(sanitizeErrorMessage(msg))], isError: true };
217
+ }
218
+ });
219
+ // ── 2. secrets_env_audit ───────────────────────────────────────────────
220
+ server.tool("secrets_env_audit", "Audit environment variable security and .env file exposure", {
221
+ check_env: z
222
+ .boolean()
223
+ .optional()
224
+ .default(true)
225
+ .describe("Check environment variables for sensitive names"),
226
+ check_files: z
227
+ .boolean()
228
+ .optional()
229
+ .default(true)
230
+ .describe("Check for exposed .env files on disk"),
231
+ }, async ({ check_env, check_files }) => {
232
+ try {
233
+ const lines = [
234
+ `=== Environment Security Audit ===`,
235
+ ``,
236
+ ];
237
+ // ── Check environment variables ────────────────────────────────
238
+ if (check_env) {
239
+ lines.push(`── SENSITIVE ENVIRONMENT VARIABLES ──`);
240
+ const envResult = await executeCommand({
241
+ command: "env",
242
+ args: [],
243
+ toolName: "secrets_env_audit",
244
+ timeout: 10000,
245
+ });
246
+ // Filter and redact sensitive variables in TypeScript
247
+ const sensitivePattern = /password|secret|token|key|api/i;
248
+ const envVars = envResult.stdout
249
+ .split("\n")
250
+ .filter((line) => sensitivePattern.test(line.split("=")[0] ?? ""))
251
+ .map((line) => line.replace(/=.*/, "=<REDACTED>"))
252
+ .sort();
253
+ if (envVars.length > 0) {
254
+ lines.push(`Found ${envVars.length} sensitive variable(s) (values redacted):`);
255
+ for (const v of envVars) {
256
+ lines.push(` ${v}`);
257
+ }
258
+ }
259
+ else {
260
+ lines.push(`No sensitive environment variables detected.`);
261
+ }
262
+ lines.push(``);
263
+ }
264
+ // ── Check .env files ───────────────────────────────────────────
265
+ if (check_files) {
266
+ lines.push(`── .ENV FILE EXPOSURE ──`);
267
+ const findResult = await executeCommand({
268
+ command: "find",
269
+ args: [
270
+ "/home", "/tmp", "/var",
271
+ "(", "-name", ".env",
272
+ "-o", "-name", ".env.local",
273
+ "-o", "-name", ".env.production",
274
+ ")",
275
+ "-type", "f",
276
+ ],
277
+ toolName: "secrets_env_audit",
278
+ timeout: 30000,
279
+ });
280
+ const envFiles = findResult.stdout.split("\n").filter((l) => l.trim());
281
+ if (envFiles.length > 0) {
282
+ lines.push(`Found ${envFiles.length} .env file(s):`);
283
+ for (const file of envFiles) {
284
+ const statResult = await executeCommand({
285
+ command: "stat",
286
+ args: ["-c", "%a %U:%G %n", file],
287
+ toolName: "secrets_env_audit",
288
+ timeout: 5000,
289
+ });
290
+ if (statResult.exitCode === 0 && statResult.stdout.trim()) {
291
+ const perms = statResult.stdout.trim().split(" ")[0];
292
+ let status = "[OK]";
293
+ if (perms !== "600") {
294
+ status = `[WARNING: permissions ${perms}, should be 600]`;
295
+ }
296
+ // Check world-readable
297
+ const worldReadable = (parseInt(perms, 8) & 0o004) !== 0;
298
+ if (worldReadable) {
299
+ status = `[CRITICAL: world-readable (${perms})]`;
300
+ }
301
+ lines.push(` ${statResult.stdout.trim()} ${status}`);
302
+ }
303
+ }
304
+ }
305
+ else {
306
+ lines.push(`No .env files found in /home, /tmp, /var.`);
307
+ }
308
+ lines.push(``);
309
+ // ── Check /proc/*/environ readability ────────────────────────
310
+ lines.push(`── /proc/*/environ ACCESSIBILITY ──`);
311
+ // Check /proc/1/environ permissions
312
+ const proc1Result = await executeCommand({
313
+ command: "ls",
314
+ args: ["-la", "/proc/1/environ"],
315
+ toolName: "secrets_env_audit",
316
+ timeout: 5000,
317
+ });
318
+ // Find readable environ files
319
+ const procFindResult = await executeCommand({
320
+ command: "find",
321
+ args: ["/proc", "-maxdepth", "2", "-name", "environ", "-readable"],
322
+ toolName: "secrets_env_audit",
323
+ timeout: 10000,
324
+ });
325
+ const procOutput = [
326
+ ...(proc1Result.exitCode === 0 ? [proc1Result.stdout.trim()] : []),
327
+ ...(procFindResult.exitCode === 0 ? procFindResult.stdout.trim().split("\n") : []),
328
+ ].filter((l) => l.trim());
329
+ if (procOutput.length > 0) {
330
+ const readableFiles = procOutput.filter((l) => l.startsWith("/proc"));
331
+ if (readableFiles.length > 0) {
332
+ lines.push(`WARNING: ${readableFiles.length} /proc/*/environ file(s) are readable by current user.`);
333
+ lines.push(`These should only be readable by root (permissions 400).`);
334
+ for (const f of readableFiles.slice(0, 10)) {
335
+ lines.push(` ${f}`);
336
+ }
337
+ if (readableFiles.length > 10) {
338
+ lines.push(` ... and ${readableFiles.length - 10} more`);
339
+ }
340
+ }
341
+ else {
342
+ lines.push(`/proc/*/environ files are properly restricted.`);
343
+ }
344
+ }
345
+ else {
346
+ lines.push(`/proc/*/environ files are properly restricted.`);
347
+ }
348
+ lines.push(``);
349
+ }
350
+ return { content: [createTextContent(lines.join("\n"))] };
351
+ }
352
+ catch (err) {
353
+ const msg = err instanceof Error ? err.message : String(err);
354
+ // TOOL-021: Sanitize error message to avoid leaking sensitive data
355
+ return { content: [createErrorContent(sanitizeErrorMessage(msg))], isError: true };
356
+ }
357
+ });
358
+ // ── 3. secrets_ssh_key_sprawl ──────────────────────────────────────────
359
+ server.tool("secrets_ssh_key_sprawl", "Detect SSH key sprawl — find all SSH keys, check their age, permissions, and authorized_keys files", {
360
+ search_path: z
361
+ .string()
362
+ .optional()
363
+ .default("/home")
364
+ .describe("Root path to search for SSH keys"),
365
+ check_authorized_keys: z
366
+ .boolean()
367
+ .optional()
368
+ .default(true)
369
+ .describe("Also audit authorized_keys files"),
370
+ }, async ({ search_path, check_authorized_keys }) => {
371
+ try {
372
+ const lines = [
373
+ `=== SSH Key Sprawl Report ===`,
374
+ `Search Path: ${search_path}`,
375
+ ``,
376
+ ];
377
+ // ── Find SSH private keys ──────────────────────────────────────
378
+ lines.push(`── SSH PRIVATE KEYS ──`);
379
+ const findResult = await executeCommand({
380
+ command: "find",
381
+ args: [
382
+ search_path,
383
+ "-name", "id_*",
384
+ "-not", "-name", "*.pub",
385
+ "-type", "f",
386
+ ],
387
+ toolName: "secrets_ssh_key_sprawl",
388
+ timeout: 30000,
389
+ });
390
+ const keyFiles = findResult.stdout.split("\n").filter((l) => l.trim());
391
+ if (keyFiles.length > 0) {
392
+ lines.push(`Found ${keyFiles.length} SSH private key(s):`);
393
+ lines.push(``);
394
+ for (const keyFile of keyFiles) {
395
+ lines.push(` Key: ${keyFile}`);
396
+ // Check permissions
397
+ const statResult = await executeCommand({
398
+ command: "stat",
399
+ args: ["-c", "%a %U:%G %Y", keyFile],
400
+ toolName: "secrets_ssh_key_sprawl",
401
+ timeout: 5000,
402
+ });
403
+ if (statResult.exitCode === 0 && statResult.stdout.trim()) {
404
+ const parts = statResult.stdout.trim().split(" ");
405
+ const perms = parts[0];
406
+ const owner = parts[1];
407
+ const mtime = parseInt(parts[2], 10);
408
+ // Permission check
409
+ const permStatus = perms === "600" || perms === "400"
410
+ ? "[OK]"
411
+ : `[WARNING: permissions ${perms}, should be 600]`;
412
+ lines.push(` Permissions: ${perms} ${permStatus}`);
413
+ lines.push(` Owner: ${owner}`);
414
+ // Age calculation
415
+ const ageMs = Date.now() - mtime * 1000;
416
+ const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));
417
+ const ageWarning = ageDays > 365
418
+ ? ` [WARNING: key is ${ageDays} days old, consider rotation]`
419
+ : "";
420
+ lines.push(` Age: ${ageDays} days${ageWarning}`);
421
+ }
422
+ // Check key type and bits
423
+ const keygenResult = await executeCommand({
424
+ command: "ssh-keygen",
425
+ args: ["-l", "-f", keyFile],
426
+ toolName: "secrets_ssh_key_sprawl",
427
+ timeout: 5000,
428
+ });
429
+ if (keygenResult.exitCode === 0 && keygenResult.stdout.trim()) {
430
+ lines.push(` Key Info: ${keygenResult.stdout.trim()}`);
431
+ }
432
+ lines.push(``);
433
+ }
434
+ }
435
+ else {
436
+ lines.push(`No SSH private keys found.`);
437
+ lines.push(``);
438
+ }
439
+ // ── Check authorized_keys files ────────────────────────────────
440
+ if (check_authorized_keys) {
441
+ lines.push(`── AUTHORIZED_KEYS FILES ──`);
442
+ const akResult = await executeCommand({
443
+ command: "find",
444
+ args: [
445
+ search_path,
446
+ "-name", "authorized_keys",
447
+ "-type", "f",
448
+ ],
449
+ toolName: "secrets_ssh_key_sprawl",
450
+ timeout: 30000,
451
+ });
452
+ const akFiles = akResult.stdout.split("\n").filter((l) => l.trim());
453
+ if (akFiles.length > 0) {
454
+ lines.push(`Found ${akFiles.length} authorized_keys file(s):`);
455
+ lines.push(``);
456
+ for (const akFile of akFiles) {
457
+ lines.push(` File: ${akFile}`);
458
+ // Check permissions
459
+ const statResult = await executeCommand({
460
+ command: "stat",
461
+ args: ["-c", "%a %U:%G", akFile],
462
+ toolName: "secrets_ssh_key_sprawl",
463
+ timeout: 5000,
464
+ });
465
+ if (statResult.exitCode === 0 && statResult.stdout.trim()) {
466
+ const parts = statResult.stdout.trim().split(" ");
467
+ const perms = parts[0];
468
+ const owner = parts[1];
469
+ const permStatus = perms === "600" || perms === "644"
470
+ ? "[OK]"
471
+ : `[WARNING: permissions ${perms}, should be 600]`;
472
+ lines.push(` Permissions: ${perms} ${permStatus}`);
473
+ lines.push(` Owner: ${owner}`);
474
+ }
475
+ // Count entries and check for command restrictions
476
+ const wcResult = await executeCommand({
477
+ command: "grep",
478
+ args: ["-c", "^[^#]", akFile],
479
+ toolName: "secrets_ssh_key_sprawl",
480
+ timeout: 5000,
481
+ });
482
+ const entryCount = parseInt(wcResult.stdout.trim(), 10) || 0;
483
+ lines.push(` Entries: ${entryCount}`);
484
+ // Check for command restrictions
485
+ const cmdResult = await executeCommand({
486
+ command: "grep",
487
+ args: ["-c", "^command=", akFile],
488
+ toolName: "secrets_ssh_key_sprawl",
489
+ timeout: 5000,
490
+ });
491
+ const cmdRestricted = parseInt(cmdResult.stdout.trim(), 10) || 0;
492
+ if (entryCount > 0) {
493
+ const unrestricted = entryCount - cmdRestricted;
494
+ if (unrestricted > 0) {
495
+ lines.push(` Command-restricted: ${cmdRestricted}/${entryCount} [WARNING: ${unrestricted} unrestricted key(s)]`);
496
+ }
497
+ else {
498
+ lines.push(` Command-restricted: ${cmdRestricted}/${entryCount} [OK: all keys restricted]`);
499
+ }
500
+ }
501
+ lines.push(``);
502
+ }
503
+ }
504
+ else {
505
+ lines.push(`No authorized_keys files found.`);
506
+ lines.push(``);
507
+ }
508
+ }
509
+ // ── Summary ────────────────────────────────────────────────────
510
+ lines.push(`── SUMMARY ──`);
511
+ lines.push(`Private keys found: ${keyFiles.length}`);
512
+ if (check_authorized_keys) {
513
+ const akFindResult = await executeCommand({
514
+ command: "find",
515
+ args: [search_path, "-name", "authorized_keys", "-type", "f"],
516
+ toolName: "secrets_ssh_key_sprawl",
517
+ timeout: 10000,
518
+ });
519
+ const akCount = akFindResult.stdout.trim().split("\n").filter((l) => l.trim()).length;
520
+ lines.push(`Authorized_keys files: ${akCount}`);
521
+ }
522
+ return { content: [createTextContent(lines.join("\n"))] };
523
+ }
524
+ catch (err) {
525
+ const msg = err instanceof Error ? err.message : String(err);
526
+ // TOOL-021: Sanitize error message to avoid leaking sensitive data
527
+ return { content: [createErrorContent(sanitizeErrorMessage(msg))], isError: true };
528
+ }
529
+ });
530
+ // ── 4. scan_git_history ───────────────────────────────────────────────
531
+ server.tool("secrets_git_history_scan", "Scan git repository history for leaked secrets using truffleHog or gitleaks.", {
532
+ repoPath: z.string().describe("Path to git repository"),
533
+ dryRun: z.boolean().optional().default(true).describe("Preview only"),
534
+ }, async ({ repoPath, dryRun }) => {
535
+ try {
536
+ if (dryRun) {
537
+ return {
538
+ content: [formatToolOutput({
539
+ dryRun: true,
540
+ repoPath,
541
+ methods: ["truffleHog git", "gitleaks detect"],
542
+ })],
543
+ };
544
+ }
545
+ // Try truffleHog
546
+ const thWhich = await executeCommand({ command: "which", args: ["trufflehog"], timeout: 5000 });
547
+ if (thWhich.exitCode === 0) {
548
+ const result = await executeCommand({
549
+ command: "trufflehog",
550
+ args: ["git", `file://${repoPath}`, "--json"],
551
+ timeout: 300000,
552
+ });
553
+ const findings = result.stdout.trim().split("\n").filter(Boolean).map((l) => {
554
+ try {
555
+ return JSON.parse(l);
556
+ }
557
+ catch {
558
+ return { raw: l };
559
+ }
560
+ });
561
+ return {
562
+ content: [formatToolOutput({
563
+ tool: "trufflehog",
564
+ repoPath,
565
+ totalFindings: findings.length,
566
+ findings: findings.slice(0, 50),
567
+ })],
568
+ };
569
+ }
570
+ // Try gitleaks
571
+ const glWhich = await executeCommand({ command: "which", args: ["gitleaks"], timeout: 5000 });
572
+ if (glWhich.exitCode === 0) {
573
+ const result = await executeCommand({
574
+ command: "gitleaks",
575
+ args: ["detect", "--source", repoPath, "--report-format", "json", "--report-path", "/dev/stdout"],
576
+ timeout: 300000,
577
+ });
578
+ const findings = result.stdout.trim() ? JSON.parse(result.stdout) : [];
579
+ return {
580
+ content: [formatToolOutput({
581
+ tool: "gitleaks",
582
+ repoPath,
583
+ totalFindings: Array.isArray(findings) ? findings.length : 0,
584
+ findings: Array.isArray(findings) ? findings.slice(0, 50) : findings,
585
+ })],
586
+ };
587
+ }
588
+ return { content: [createErrorContent("Neither truffleHog nor gitleaks found. Install one for git history scanning.")], isError: true };
589
+ }
590
+ catch (err) {
591
+ const msg = err instanceof Error ? err.message : String(err);
592
+ // TOOL-021: Sanitize error message to avoid leaking sensitive data
593
+ return { content: [createErrorContent(`Git history scan failed: ${sanitizeErrorMessage(msg)}`)], isError: true };
594
+ }
595
+ });
596
+ }
@@ -0,0 +1,18 @@
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 { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ /**
13
+ * Validate a SIEM host string (hostname or IP address).
14
+ * Returns true if the host looks reasonable.
15
+ */
16
+ export declare function validateSiemHost(host: string): boolean;
17
+ export declare function registerSiemIntegrationTools(server: McpServer): void;
18
+ //# sourceMappingURL=siem-integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"siem-integration.d.ts","sourceRoot":"","sources":["../../src/tools/siem-integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4FpE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQtD;AAkhBD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmTpE"}