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,8 @@
1
+ /**
2
+ * Backup and restore tools for Kali Defense MCP Server.
3
+ *
4
+ * Registers 1 tool: backup (actions: config, state, restore, verify, list).
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ export declare function registerBackupTools(server: McpServer): void;
8
+ //# sourceMappingURL=backup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/tools/backup.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoFpE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgW3D"}
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Backup and restore tools for Kali Defense MCP Server.
3
+ *
4
+ * Registers 1 tool: backup (actions: config, state, restore, verify, list).
5
+ */
6
+ import { z } from "zod";
7
+ import { executeCommand } from "../core/executor.js";
8
+ import { getConfig, getToolTimeout } from "../core/config.js";
9
+ import { createTextContent, createErrorContent, formatToolOutput, } from "../core/parsers.js";
10
+ import { logChange, createChangeEntry, backupFile, restoreFile, } from "../core/changelog.js";
11
+ import { validateFilePath } from "../core/sanitizer.js";
12
+ import { existsSync, readFileSync } from "node:fs";
13
+ import { createHash } from "node:crypto";
14
+ import { join, resolve } from "node:path";
15
+ import { homedir } from "node:os";
16
+ // ── TOOL-026 remediation: allowed backup directories ───────────────────────
17
+ const ALLOWED_BACKUP_DIRS = ["/tmp", "/var/backups", "/home", "/root", "/opt", "/var/lib"];
18
+ /**
19
+ * Validate a backup destination path:
20
+ * 1. Reject `..` sequences
21
+ * 2. Resolve and verify within allowed backup directories
22
+ * 3. Validate the destination exists or can be created
23
+ */
24
+ function validateBackupPath(inputPath) {
25
+ if (!inputPath || typeof inputPath !== "string") {
26
+ throw new Error("Backup path must be a non-empty string");
27
+ }
28
+ if (inputPath.includes("..")) {
29
+ throw new Error(`Backup path contains forbidden traversal sequence (..): '${inputPath}'`);
30
+ }
31
+ const resolved = resolve(inputPath);
32
+ // Check it's within allowed directories or the config backupDir
33
+ const configBackupDir = getConfig().backupDir;
34
+ const allAllowed = [...ALLOWED_BACKUP_DIRS, configBackupDir];
35
+ const isAllowed = allAllowed.some((dir) => resolved === dir || resolved.startsWith(dir + "/"));
36
+ if (!isAllowed) {
37
+ throw new Error(`Backup path '${resolved}' is not within allowed directories: ${allAllowed.join(", ")}`);
38
+ }
39
+ return resolved;
40
+ }
41
+ // ── Default critical configuration files ─────────────────────────────────
42
+ const DEFAULT_CRITICAL_FILES = [
43
+ "/etc/passwd",
44
+ "/etc/shadow",
45
+ "/etc/group",
46
+ "/etc/sudoers",
47
+ "/etc/ssh/sshd_config",
48
+ "/etc/fstab",
49
+ "/etc/hosts",
50
+ "/etc/sysctl.conf",
51
+ "/etc/iptables/rules.v4",
52
+ ];
53
+ // ── Helper ─────────────────────────────────────────────────────────────────
54
+ function formatSize(bytes) {
55
+ if (bytes < 1024)
56
+ return `${bytes} B`;
57
+ if (bytes < 1024 * 1024)
58
+ return `${(bytes / 1024).toFixed(1)} KB`;
59
+ if (bytes < 1024 * 1024 * 1024)
60
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
61
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
62
+ }
63
+ // ── Registration entry point ───────────────────────────────────────────────
64
+ export function registerBackupTools(server) {
65
+ server.tool("backup", "Backup management: backup config files, capture system state, restore from backup, verify integrity, or list backups.", {
66
+ action: z.enum(["config", "state", "restore", "verify", "list"]).describe("Action: config=backup config files, state=system state snapshot, restore=restore from backup, verify=verify integrity, list=list backups"),
67
+ // config params
68
+ files: z.string().optional().describe("Comma-separated file paths, or omit for default critical files (config action)"),
69
+ tag: z.string().optional().describe("Tag for this backup set (config action)"),
70
+ // state params
71
+ output_dir: z.string().optional().describe("Directory to save snapshot files (state action)"),
72
+ include_packages: z.boolean().optional().default(true).describe("Include installed packages list (state action)"),
73
+ include_services: z.boolean().optional().default(true).describe("Include service states (state action)"),
74
+ include_network: z.boolean().optional().default(true).describe("Include network configuration (state action)"),
75
+ include_firewall: z.boolean().optional().default(true).describe("Include firewall rules (state action)"),
76
+ include_users: z.boolean().optional().default(true).describe("Include user/group information (state action)"),
77
+ // restore params
78
+ backup_path: z.string().optional().describe("Path to the backup file (restore/verify action)"),
79
+ original_path: z.string().optional().describe("Original file path to restore to (restore action)"),
80
+ // verify params
81
+ check_integrity: z.boolean().optional().default(true).describe("Compute and verify SHA256 checksums (verify action)"),
82
+ // list params
83
+ filter: z.string().optional().describe("Filter by filename pattern (list action)"),
84
+ sort_by: z.enum(["date", "name", "size"]).optional().default("date").describe("Sort order (list action)"),
85
+ limit: z.number().optional().default(50).describe("Maximum number of results (list action)"),
86
+ // shared
87
+ dry_run: z.boolean().optional().describe("Preview without executing (defaults to KALI_DEFENSE_DRY_RUN env var)"),
88
+ }, async (params) => {
89
+ const { action } = params;
90
+ switch (action) {
91
+ // ── config ──────────────────────────────────────────────────
92
+ case "config": {
93
+ const { files, tag, dry_run } = params;
94
+ try {
95
+ const fileList = files
96
+ ? files.split(",").map((f) => f.trim()).filter((f) => f.length > 0)
97
+ : DEFAULT_CRITICAL_FILES;
98
+ if (dry_run ?? getConfig().dryRun) {
99
+ logChange(createChangeEntry({
100
+ tool: "backup",
101
+ action: `[DRY-RUN] Backup config files${tag ? ` (tag: ${tag})` : ""}`,
102
+ target: fileList.join(", "),
103
+ after: `Would backup ${fileList.length} files`,
104
+ dryRun: true,
105
+ success: true,
106
+ }));
107
+ return {
108
+ content: [createTextContent(`[DRY-RUN] Would backup ${fileList.length} files:\n${fileList.map((f) => ` - ${f}`).join("\n")}`)],
109
+ };
110
+ }
111
+ const results = [];
112
+ for (const filePath of fileList) {
113
+ try {
114
+ const bkPath = backupFile(filePath);
115
+ results.push({ file: filePath, backupPath: bkPath });
116
+ }
117
+ catch (err) {
118
+ const msg = err instanceof Error ? err.message : String(err);
119
+ results.push({ file: filePath, error: msg });
120
+ }
121
+ }
122
+ const succeeded = results.filter((r) => r.backupPath);
123
+ const failed = results.filter((r) => r.error);
124
+ logChange(createChangeEntry({
125
+ tool: "backup",
126
+ action: `Backup config files${tag ? ` (tag: ${tag})` : ""}`,
127
+ target: `${succeeded.length}/${fileList.length} files`,
128
+ after: JSON.stringify(results),
129
+ dryRun: false,
130
+ success: failed.length === 0,
131
+ error: failed.length > 0 ? `${failed.length} file(s) failed to backup` : undefined,
132
+ }));
133
+ return { content: [formatToolOutput({ tag: tag ?? null, totalFiles: fileList.length, succeeded: succeeded.length, failed: failed.length, results })] };
134
+ }
135
+ catch (err) {
136
+ const msg = err instanceof Error ? err.message : String(err);
137
+ return { content: [createErrorContent(msg)], isError: true };
138
+ }
139
+ }
140
+ // ── state ───────────────────────────────────────────────────
141
+ case "state": {
142
+ const { output_dir, include_packages, include_services, include_network, include_firewall, include_users, dry_run } = params;
143
+ try {
144
+ const config = getConfig();
145
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
146
+ const snapshotDir = output_dir ?? `${config.backupDir}/snapshot-${timestamp}`;
147
+ // TOOL-026: Validate snapshot output directory for traversal
148
+ validateBackupPath(snapshotDir);
149
+ const captureSteps = [
150
+ { name: "packages", enabled: include_packages, commands: [{ label: "dpkg-selections", cmd: "dpkg", args: ["--get-selections"] }] },
151
+ { name: "services", enabled: include_services, commands: [{ label: "systemctl-units", cmd: "systemctl", args: ["list-unit-files", "--type=service", "--no-pager"] }] },
152
+ { name: "network", enabled: include_network, commands: [{ label: "ip-addr", cmd: "ip", args: ["addr", "show"] }, { label: "ip-route", cmd: "ip", args: ["route", "show"] }, { label: "ss-listening", cmd: "ss", args: ["-tulnp"] }] },
153
+ { name: "firewall", enabled: include_firewall, commands: [{ label: "iptables-save", cmd: "sudo", args: ["iptables-save"] }, { label: "ufw-status", cmd: "sudo", args: ["ufw", "status", "verbose"] }] },
154
+ { name: "users", enabled: include_users, commands: [{ label: "passwd", cmd: "cat", args: ["/etc/passwd"] }, { label: "group", cmd: "cat", args: ["/etc/group"] }, { label: "lastlog", cmd: "lastlog", args: [] }] },
155
+ ];
156
+ const enabledSteps = captureSteps.filter((s) => s.enabled);
157
+ const allCommands = enabledSteps.flatMap((s) => s.commands.map((c) => `${c.cmd} ${c.args.join(" ")}`));
158
+ if (dry_run ?? getConfig().dryRun) {
159
+ logChange(createChangeEntry({ tool: "backup", action: "[DRY-RUN] Capture system state snapshot", target: snapshotDir, after: `Would capture ${enabledSteps.length} categories`, dryRun: true, success: true }));
160
+ return {
161
+ content: [createTextContent(`[DRY-RUN] Would capture system state to: ${snapshotDir}\n\nCategories: ${enabledSteps.map((s) => s.name).join(", ")}\n\nCommands:\n${allCommands.map((c) => ` ${c}`).join("\n")}`)],
162
+ };
163
+ }
164
+ await executeCommand({ command: "mkdir", args: ["-p", snapshotDir], toolName: "backup" });
165
+ const results = {};
166
+ for (const step of enabledSteps) {
167
+ results[step.name] = [];
168
+ for (const cmd of step.commands) {
169
+ const outputFile = `${snapshotDir}/${step.name}-${cmd.label}.txt`;
170
+ const result = await executeCommand({ command: cmd.cmd, args: cmd.args, toolName: "backup", timeout: getToolTimeout("backup_system_state") });
171
+ if (result.stdout) {
172
+ await executeCommand({ command: "sudo", args: ["tee", outputFile], stdin: result.stdout, toolName: "backup" });
173
+ results[step.name].push({ label: cmd.label, success: true, file: outputFile });
174
+ }
175
+ else {
176
+ results[step.name].push({ label: cmd.label, success: false, error: result.stderr || "No output" });
177
+ }
178
+ }
179
+ }
180
+ logChange(createChangeEntry({
181
+ tool: "backup",
182
+ action: "Capture system state snapshot",
183
+ target: snapshotDir,
184
+ after: JSON.stringify(Object.fromEntries(Object.entries(results).map(([k, v]) => [k, v.filter((r) => r.success).length + "/" + v.length]))),
185
+ dryRun: false,
186
+ success: true,
187
+ }));
188
+ return { content: [formatToolOutput({ snapshotDir, timestamp, categories: results })] };
189
+ }
190
+ catch (err) {
191
+ const msg = err instanceof Error ? err.message : String(err);
192
+ return { content: [createErrorContent(msg)], isError: true };
193
+ }
194
+ }
195
+ // ── restore ─────────────────────────────────────────────────
196
+ case "restore": {
197
+ const { backup_path, original_path, dry_run } = params;
198
+ try {
199
+ if (!backup_path)
200
+ return { content: [createErrorContent("backup_path is required for restore action")], isError: true };
201
+ if (!original_path)
202
+ return { content: [createErrorContent("original_path is required for restore action")], isError: true };
203
+ // TOOL-026: Validate backup paths for traversal
204
+ validateBackupPath(backup_path);
205
+ const validatedBackup = validateFilePath(backup_path);
206
+ validateBackupPath(original_path);
207
+ const validatedOriginal = validateFilePath(original_path);
208
+ const fullCmd = `cp ${validatedBackup} ${validatedOriginal}`;
209
+ if (dry_run ?? getConfig().dryRun) {
210
+ logChange(createChangeEntry({ tool: "backup", action: "[DRY-RUN] Restore file from backup", target: validatedOriginal, before: validatedOriginal, after: `Would restore from ${validatedBackup}`, dryRun: true, success: true }));
211
+ return {
212
+ content: [createTextContent(`[DRY-RUN] Would execute:\n ${fullCmd}\n\nSource backup: ${validatedBackup}\nRestore to: ${validatedOriginal}\n\nA backup of the current file will be created before overwriting.`)],
213
+ };
214
+ }
215
+ let currentBackupPath;
216
+ try {
217
+ currentBackupPath = backupFile(validatedOriginal);
218
+ }
219
+ catch { /* File may not exist */ }
220
+ restoreFile(validatedBackup, validatedOriginal);
221
+ logChange(createChangeEntry({
222
+ tool: "backup",
223
+ action: "Restore file from backup",
224
+ target: validatedOriginal,
225
+ before: currentBackupPath ?? "none",
226
+ after: `Restored from ${validatedBackup}`,
227
+ backupPath: currentBackupPath,
228
+ dryRun: false,
229
+ success: true,
230
+ rollbackCommand: currentBackupPath ? `cp ${currentBackupPath} ${validatedOriginal}` : undefined,
231
+ }));
232
+ return {
233
+ content: [createTextContent(`File restored successfully.\nSource: ${validatedBackup}\nDestination: ${validatedOriginal}\n${currentBackupPath ? `Previous file backed up to: ${currentBackupPath}` : "No previous file existed."}`)],
234
+ };
235
+ }
236
+ catch (err) {
237
+ const msg = err instanceof Error ? err.message : String(err);
238
+ return { content: [createErrorContent(msg)], isError: true };
239
+ }
240
+ }
241
+ // ── verify ──────────────────────────────────────────────────
242
+ case "verify": {
243
+ const { backup_path, check_integrity } = params;
244
+ try {
245
+ const config = getConfig();
246
+ // Load backup manifest for hash comparison if available
247
+ const manifestPath = join(homedir(), ".kali-mcp-backups", "manifest.json");
248
+ let manifestHashes = null;
249
+ if (check_integrity) {
250
+ try {
251
+ if (existsSync(manifestPath)) {
252
+ const raw = JSON.parse(readFileSync(manifestPath, "utf-8"));
253
+ if (raw && Array.isArray(raw.backups)) {
254
+ manifestHashes = new Map();
255
+ for (const entry of raw.backups) {
256
+ if (entry.backupPath && entry.sha256) {
257
+ manifestHashes.set(entry.backupPath, entry.sha256);
258
+ }
259
+ }
260
+ }
261
+ }
262
+ }
263
+ catch { /* manifest unreadable */ }
264
+ }
265
+ if (backup_path) {
266
+ const statResult = await executeCommand({ command: "stat", args: [backup_path], toolName: "backup", timeout: getToolTimeout("backup_verify") });
267
+ if (statResult.exitCode !== 0) {
268
+ return { content: [createErrorContent(`Backup file not found: ${backup_path}\n${statResult.stderr}`)], isError: true };
269
+ }
270
+ let sha256 = "";
271
+ let integrity = "not_checked";
272
+ if (check_integrity) {
273
+ try {
274
+ const content = readFileSync(backup_path);
275
+ sha256 = createHash("sha256").update(content).digest("hex");
276
+ }
277
+ catch {
278
+ const hashResult = await executeCommand({ command: "sha256sum", args: [backup_path], toolName: "backup", timeout: getToolTimeout("backup_verify") });
279
+ sha256 = hashResult.stdout.trim().split(/\s+/)[0] ?? "";
280
+ }
281
+ if (sha256 && manifestHashes) {
282
+ const stored = manifestHashes.get(backup_path);
283
+ integrity = stored ? (stored === sha256 ? "verified" : "corrupted") : "no_baseline";
284
+ }
285
+ else if (sha256) {
286
+ integrity = "no_baseline";
287
+ }
288
+ }
289
+ return { content: [formatToolOutput({ file: backup_path, exists: true, sha256: sha256 || undefined, integrity, stat: statResult.stdout })] };
290
+ }
291
+ // Verify all backups
292
+ const findResult = await executeCommand({ command: "find", args: [config.backupDir, "-type", "f", "-printf", "%T@ %s %p\\n"], toolName: "backup", timeout: getToolTimeout("backup_verify") });
293
+ if (findResult.exitCode !== 0) {
294
+ return { content: [createErrorContent(`Cannot list backups: ${findResult.stderr}`)], isError: true };
295
+ }
296
+ const backupEntries = [];
297
+ const fileLines = findResult.stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
298
+ for (const line of fileLines) {
299
+ const parts = line.split(/\s+/);
300
+ const timestamp = parseFloat(parts[0]) || 0;
301
+ const size = parseInt(parts[1], 10) || 0;
302
+ const filePath = parts.slice(2).join(" ");
303
+ const entry = {
304
+ path: filePath, size, modified: new Date(timestamp * 1000).toISOString(), ageHours: Math.round((Date.now() - timestamp * 1000) / (1000 * 60 * 60)),
305
+ };
306
+ if (check_integrity && filePath && !filePath.endsWith("manifest.json")) {
307
+ try {
308
+ const content = readFileSync(filePath);
309
+ const hash = createHash("sha256").update(content).digest("hex");
310
+ entry.sha256 = hash;
311
+ if (manifestHashes) {
312
+ const stored = manifestHashes.get(filePath);
313
+ entry.integrity = stored ? (stored === hash ? "verified" : "corrupted") : "no_baseline";
314
+ }
315
+ else {
316
+ entry.integrity = "no_baseline";
317
+ }
318
+ }
319
+ catch {
320
+ entry.integrity = "unreadable";
321
+ }
322
+ }
323
+ backupEntries.push(entry);
324
+ }
325
+ const verified = backupEntries.filter((b) => b.integrity === "verified").length;
326
+ const corrupted = backupEntries.filter((b) => b.integrity === "corrupted").length;
327
+ const noBaseline = backupEntries.filter((b) => b.integrity === "no_baseline").length;
328
+ return { content: [formatToolOutput({ backupDir: config.backupDir, totalBackups: backupEntries.length, integrityChecked: check_integrity, summary: check_integrity ? { verified, corrupted, noBaseline } : undefined, backups: backupEntries })] };
329
+ }
330
+ catch (err) {
331
+ const msg = err instanceof Error ? err.message : String(err);
332
+ return { content: [createErrorContent(msg)], isError: true };
333
+ }
334
+ }
335
+ // ── list ────────────────────────────────────────────────────
336
+ case "list": {
337
+ const { filter: filterPattern, sort_by, limit: maxLimit } = params;
338
+ try {
339
+ const config = getConfig();
340
+ const backupDir = config.backupDir;
341
+ const args = [backupDir, "-type", "f"];
342
+ if (filterPattern)
343
+ args.push("-name", filterPattern);
344
+ args.push("-printf", "%T@ %s %p\\n");
345
+ const result = await executeCommand({ command: "find", args, toolName: "backup", timeout: getToolTimeout("backup_list") });
346
+ if (result.exitCode !== 0) {
347
+ return { content: [createErrorContent(`Cannot list backups: ${result.stderr}`)], isError: true };
348
+ }
349
+ let files = result.stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((line) => {
350
+ const parts = line.split(/\s+/);
351
+ const timestamp = parseFloat(parts[0]) || 0;
352
+ const size = parseInt(parts[1], 10) || 0;
353
+ const filePath = parts.slice(2).join(" ");
354
+ const name = filePath.split("/").pop() ?? filePath;
355
+ return { name, path: filePath, size, sizeHuman: formatSize(size), modified: new Date(timestamp * 1000).toISOString(), timestampEpoch: timestamp };
356
+ });
357
+ switch (sort_by) {
358
+ case "date":
359
+ files.sort((a, b) => b.timestampEpoch - a.timestampEpoch);
360
+ break;
361
+ case "name":
362
+ files.sort((a, b) => a.name.localeCompare(b.name));
363
+ break;
364
+ case "size":
365
+ files.sort((a, b) => b.size - a.size);
366
+ break;
367
+ }
368
+ if (maxLimit > 0)
369
+ files = files.slice(0, maxLimit);
370
+ return { content: [formatToolOutput({ backupDir, totalFiles: files.length, sortedBy: sort_by, filter: filterPattern ?? null, files })] };
371
+ }
372
+ catch (err) {
373
+ const msg = err instanceof Error ? err.message : String(err);
374
+ return { content: [createErrorContent(msg)], isError: true };
375
+ }
376
+ }
377
+ default:
378
+ return { content: [createErrorContent(`Unknown action: ${action}`)], isError: true };
379
+ }
380
+ });
381
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Cloud security tools for Kali Defense MCP Server.
3
+ *
4
+ * Registers 1 tool: cloud_security (actions: detect_environment, audit_metadata,
5
+ * check_iam_creds, audit_storage, check_imds)
6
+ *
7
+ * Provides cloud environment detection, metadata service auditing, credential
8
+ * exposure checking, storage audit, and IMDS security assessment for AWS/GCP/Azure.
9
+ */
10
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
+ /**
12
+ * Mask a credential value, showing first 4 chars + asterisks.
13
+ * Returns "(empty)" for empty strings.
14
+ */
15
+ export declare function maskCredential(value: string): string;
16
+ export declare function registerCloudSecurityTools(server: McpServer): void;
17
+ //# sourceMappingURL=cloud-security.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-security.d.ts","sourceRoot":"","sources":["../../src/tools/cloud-security.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAuGpE;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKpD;AA4fD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+RlE"}