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,63 @@
1
+ /**
2
+ * BackupManager — manages file backups with manifest tracking.
3
+ *
4
+ * Backups are stored under ~/.kali-defense/backups/ with timestamped filenames.
5
+ * A manifest.json tracks all backups for listing and restore operations.
6
+ */
7
+ export interface BackupEntry {
8
+ id: string;
9
+ originalPath: string;
10
+ backupPath: string;
11
+ timestamp: string;
12
+ sizeBytes: number;
13
+ }
14
+ export interface BackupManifest {
15
+ version: 1;
16
+ backups: BackupEntry[];
17
+ }
18
+ /**
19
+ * Validate that a backup path is safe:
20
+ * 1. No `..` traversal sequences
21
+ * 2. Normalized via path.resolve()
22
+ * 3. Resolved path is within the backup base directory
23
+ * 4. Not a symlink (prevent symlink attacks)
24
+ *
25
+ * @param filePath The path to validate
26
+ * @param baseDir The backup base directory that paths must stay within
27
+ * @throws {Error} If the path fails validation
28
+ */
29
+ export declare function validateBackupPath(filePath: string, baseDir: string): void;
30
+ export declare class BackupManager {
31
+ private readonly backupDir;
32
+ private readonly manifestPath;
33
+ constructor(backupDir?: string);
34
+ /** Ensure backup directory exists. */
35
+ private ensureDir;
36
+ /** Read manifest from disk with migration from old format. */
37
+ private readManifest;
38
+ /** Write manifest to disk. */
39
+ private writeManifest;
40
+ /**
41
+ * Create a backup of a file (synchronous).
42
+ * @returns The BackupEntry with id and backupPath.
43
+ */
44
+ backupSync(filePath: string): BackupEntry;
45
+ /**
46
+ * Create a backup of a file.
47
+ * @returns The backup ID.
48
+ */
49
+ backup(filePath: string): Promise<string>;
50
+ /**
51
+ * Restore a file from backup by ID.
52
+ */
53
+ restore(backupId: string): Promise<void>;
54
+ /**
55
+ * List all backup entries.
56
+ */
57
+ listBackups(): Promise<BackupEntry[]>;
58
+ /**
59
+ * Remove backups older than the specified number of days.
60
+ */
61
+ pruneOldBackups(daysOld: number): Promise<void>;
62
+ }
63
+ //# sourceMappingURL=backup-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-manager.d.ts","sourceRoot":"","sources":["../../src/core/backup-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkBH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAmC1E;AAID,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,SAAS,CAAC,EAAE,MAAM;IAK9B,sCAAsC;IACtC,OAAO,CAAC,SAAS;IAIjB,8DAA8D;IAC9D,OAAO,CAAC,YAAY;IAgBpB,8BAA8B;IAC9B,OAAO,CAAC,aAAa;IAKrB;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW;IAqCzC;;;OAGG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/C;;OAEG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB9C;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAO3C;;OAEG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA+BtD"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * BackupManager — manages file backups with manifest tracking.
3
+ *
4
+ * Backups are stored under ~/.kali-defense/backups/ with timestamped filenames.
5
+ * A manifest.json tracks all backups for listing and restore operations.
6
+ */
7
+ import { existsSync, readFileSync, unlinkSync, statSync, lstatSync, } from "node:fs";
8
+ import { join, basename, dirname, resolve as pathResolve } from "node:path";
9
+ import { secureWriteFileSync, secureMkdirSync, secureCopyFileSync } from "./secure-fs.js";
10
+ import { homedir } from "node:os";
11
+ import { randomUUID } from "node:crypto";
12
+ import { z } from "zod";
13
+ // ── Zod validators ───────────────────────────────────────────────────────────
14
+ const FilePathSchema = z.string().min(1).max(4096);
15
+ const BackupIdSchema = z.string().uuid();
16
+ const DaysOldSchema = z.number().int().min(1).max(3650);
17
+ // ── SECURITY (CORE-015): Path validation helper ──────────────────────────────
18
+ /**
19
+ * Validate that a backup path is safe:
20
+ * 1. No `..` traversal sequences
21
+ * 2. Normalized via path.resolve()
22
+ * 3. Resolved path is within the backup base directory
23
+ * 4. Not a symlink (prevent symlink attacks)
24
+ *
25
+ * @param filePath The path to validate
26
+ * @param baseDir The backup base directory that paths must stay within
27
+ * @throws {Error} If the path fails validation
28
+ */
29
+ export function validateBackupPath(filePath, baseDir) {
30
+ // 1. Reject paths containing '..' traversal sequences
31
+ if (filePath.includes("..")) {
32
+ throw new Error(`SECURITY: Backup path contains '..' traversal sequence: ${filePath}`);
33
+ }
34
+ // 2. Normalize with path.resolve()
35
+ const resolved = pathResolve(filePath);
36
+ const resolvedBase = pathResolve(baseDir);
37
+ // 3. Verify the resolved path is within the backup base directory
38
+ if (!resolved.startsWith(resolvedBase + "/") && resolved !== resolvedBase) {
39
+ throw new Error(`SECURITY: Backup path '${resolved}' escapes base directory '${resolvedBase}'`);
40
+ }
41
+ // 4. Reject symlinks (if the path exists)
42
+ if (existsSync(filePath)) {
43
+ try {
44
+ const lstats = lstatSync(filePath);
45
+ if (lstats.isSymbolicLink()) {
46
+ throw new Error(`SECURITY: Backup path '${filePath}' is a symlink. Refusing to use.`);
47
+ }
48
+ }
49
+ catch (err) {
50
+ if (err instanceof Error && err.message.startsWith("SECURITY:")) {
51
+ throw err;
52
+ }
53
+ // lstat failure on existing path is suspicious but non-fatal for validation
54
+ }
55
+ }
56
+ }
57
+ // ── BackupManager ────────────────────────────────────────────────────────────
58
+ export class BackupManager {
59
+ backupDir;
60
+ manifestPath;
61
+ constructor(backupDir) {
62
+ this.backupDir = backupDir ?? join(homedir(), ".kali-defense", "backups");
63
+ this.manifestPath = join(this.backupDir, "manifest.json");
64
+ }
65
+ /** Ensure backup directory exists. */
66
+ ensureDir() {
67
+ secureMkdirSync(this.backupDir);
68
+ }
69
+ /** Read manifest from disk with migration from old format. */
70
+ readManifest() {
71
+ try {
72
+ if (existsSync(this.manifestPath)) {
73
+ const raw = readFileSync(this.manifestPath, "utf-8");
74
+ const parsed = JSON.parse(raw);
75
+ if (parsed && Array.isArray(parsed.backups)) {
76
+ // Migrate: ensure version field is present (old format may lack it)
77
+ return { version: 1, backups: parsed.backups };
78
+ }
79
+ }
80
+ }
81
+ catch {
82
+ // Corrupt or missing manifest — start fresh
83
+ }
84
+ return { version: 1, backups: [] };
85
+ }
86
+ /** Write manifest to disk. */
87
+ writeManifest(manifest) {
88
+ this.ensureDir();
89
+ secureWriteFileSync(this.manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
90
+ }
91
+ /**
92
+ * Create a backup of a file (synchronous).
93
+ * @returns The BackupEntry with id and backupPath.
94
+ */
95
+ backupSync(filePath) {
96
+ const validated = FilePathSchema.parse(filePath);
97
+ this.ensureDir();
98
+ if (!existsSync(validated)) {
99
+ throw new Error(`Source file does not exist: ${validated}`);
100
+ }
101
+ const id = randomUUID();
102
+ const now = new Date();
103
+ const ts = now.toISOString().replace(/[:.]/g, "-");
104
+ const name = basename(validated);
105
+ const backupName = `${ts}_${name}`;
106
+ const backupPath = join(this.backupDir, backupName);
107
+ // SECURITY (CORE-015): Validate the backup destination path
108
+ validateBackupPath(backupPath, this.backupDir);
109
+ secureCopyFileSync(validated, backupPath);
110
+ const stat = statSync(backupPath);
111
+ const entry = {
112
+ id,
113
+ originalPath: validated,
114
+ backupPath,
115
+ timestamp: now.toISOString(),
116
+ sizeBytes: stat.size,
117
+ };
118
+ const manifest = this.readManifest();
119
+ manifest.backups.push(entry);
120
+ this.writeManifest(manifest);
121
+ console.error(`[backup-manager] Backed up ${validated} → ${backupPath} (id: ${id})`);
122
+ return entry;
123
+ }
124
+ /**
125
+ * Create a backup of a file.
126
+ * @returns The backup ID.
127
+ */
128
+ async backup(filePath) {
129
+ const entry = this.backupSync(filePath);
130
+ return entry.id;
131
+ }
132
+ /**
133
+ * Restore a file from backup by ID.
134
+ */
135
+ async restore(backupId) {
136
+ const validated = BackupIdSchema.parse(backupId);
137
+ const manifest = this.readManifest();
138
+ const entry = manifest.backups.find((b) => b.id === validated);
139
+ if (!entry) {
140
+ throw new Error(`Backup not found: ${validated}`);
141
+ }
142
+ if (!existsSync(entry.backupPath)) {
143
+ throw new Error(`Backup file missing on disk: ${entry.backupPath}`);
144
+ }
145
+ // SECURITY (CORE-015): Validate the backup source path before restore
146
+ validateBackupPath(entry.backupPath, this.backupDir);
147
+ secureMkdirSync(dirname(entry.originalPath));
148
+ secureCopyFileSync(entry.backupPath, entry.originalPath);
149
+ console.error(`[backup-manager] Restored ${entry.backupPath} → ${entry.originalPath}`);
150
+ }
151
+ /**
152
+ * List all backup entries.
153
+ */
154
+ async listBackups() {
155
+ const manifest = this.readManifest();
156
+ return manifest.backups.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
157
+ }
158
+ /**
159
+ * Remove backups older than the specified number of days.
160
+ */
161
+ async pruneOldBackups(daysOld) {
162
+ const days = DaysOldSchema.parse(daysOld);
163
+ const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
164
+ const manifest = this.readManifest();
165
+ const keep = [];
166
+ const remove = [];
167
+ for (const entry of manifest.backups) {
168
+ if (new Date(entry.timestamp).getTime() < cutoff) {
169
+ remove.push(entry);
170
+ }
171
+ else {
172
+ keep.push(entry);
173
+ }
174
+ }
175
+ for (const entry of remove) {
176
+ try {
177
+ if (existsSync(entry.backupPath)) {
178
+ unlinkSync(entry.backupPath);
179
+ }
180
+ }
181
+ catch (err) {
182
+ console.error(`[backup-manager] Failed to delete ${entry.backupPath}: ${err instanceof Error ? err.message : String(err)}`);
183
+ }
184
+ }
185
+ manifest.backups = keep;
186
+ this.writeManifest(manifest);
187
+ console.error(`[backup-manager] Pruned ${remove.length} backup(s) older than ${days} day(s)`);
188
+ }
189
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * A single changelog entry recording a defensive action taken.
3
+ */
4
+ export interface ChangeEntry {
5
+ /** Unique identifier (UUID v4) */
6
+ id: string;
7
+ /** ISO 8601 timestamp */
8
+ timestamp: string;
9
+ /** Tool that performed the action */
10
+ tool: string;
11
+ /** Description of the action */
12
+ action: string;
13
+ /** Target of the action (file, service, etc.) */
14
+ target: string;
15
+ /** State before the change */
16
+ before?: string;
17
+ /** State after the change */
18
+ after?: string;
19
+ /** Path to backup file if one was created */
20
+ backupPath?: string;
21
+ /** Whether this was a dry-run (no actual changes) */
22
+ dryRun: boolean;
23
+ /** Whether the action succeeded */
24
+ success: boolean;
25
+ /** Error message if the action failed */
26
+ error?: string;
27
+ /** Command to undo this change */
28
+ rollbackCommand?: string;
29
+ /** OS username who made the change (auto-populated) */
30
+ user?: string;
31
+ /** MCP session identifier (if available) */
32
+ sessionId?: string;
33
+ }
34
+ /**
35
+ * Versioned changelog state file format.
36
+ * Old files stored a bare array; new files use this envelope.
37
+ */
38
+ export interface ChangelogState {
39
+ version: 1;
40
+ entries: ChangeEntry[];
41
+ }
42
+ /**
43
+ * Creates a new ChangeEntry with auto-generated id and timestamp.
44
+ */
45
+ export declare function createChangeEntry(partial: Omit<ChangeEntry, "id" | "timestamp" | "user">): ChangeEntry;
46
+ /**
47
+ * Appends a change entry to the changelog JSON file.
48
+ * Creates the file and parent directories if they don't exist.
49
+ * Rotates old entries when the file exceeds MAX_CHANGELOG_ENTRIES.
50
+ * Fails silently (logs to stderr) to avoid disrupting tool execution.
51
+ */
52
+ export declare function logChange(entry: ChangeEntry): void;
53
+ /**
54
+ * Reads changelog entries, newest first.
55
+ * Returns empty array on any error.
56
+ *
57
+ * @param limit Maximum number of entries to return (default: all)
58
+ */
59
+ export declare function getChangelog(limit?: number): ChangeEntry[];
60
+ /**
61
+ * Creates a backup copy of a file using the unified BackupManager.
62
+ * The backup is tracked in the manifest at ~/.kali-defense/backups/manifest.json.
63
+ *
64
+ * @param filePath Absolute path to the file to back up
65
+ * @returns Path to the backup file
66
+ */
67
+ export declare function backupFile(filePath: string): string;
68
+ /**
69
+ * Restores a file from a backup.
70
+ *
71
+ * @param backupPath Path to the backup file
72
+ * @param originalPath Path to restore the file to
73
+ */
74
+ export declare function restoreFile(backupPath: string, originalPath: string): void;
75
+ //# sourceMappingURL=changelog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/core/changelog.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,MAAM,EAAE,OAAO,CAAC;IAChB,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,WAAW,GAAG,MAAM,CAAC,GACtD,WAAW,CAOb;AA2BD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CA0BlD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAmB1D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAU1E"}
@@ -0,0 +1,123 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { userInfo } from "node:os";
4
+ import { secureWriteFileSync, secureMkdirSync, secureCopyFileSync } from "./secure-fs.js";
5
+ import { randomUUID } from "node:crypto";
6
+ import { getConfig } from "./config.js";
7
+ import { BackupManager } from "./backup-manager.js";
8
+ /**
9
+ * Creates a new ChangeEntry with auto-generated id and timestamp.
10
+ */
11
+ export function createChangeEntry(partial) {
12
+ return {
13
+ id: randomUUID(),
14
+ timestamp: new Date().toISOString(),
15
+ user: (() => { try {
16
+ return userInfo().username;
17
+ }
18
+ catch {
19
+ return undefined;
20
+ } })(),
21
+ ...partial,
22
+ };
23
+ }
24
+ /** Maximum number of changelog entries to retain. Older entries beyond this
25
+ * limit are discarded during writes to prevent unbounded file growth. */
26
+ const MAX_CHANGELOG_ENTRIES = 10_000;
27
+ /**
28
+ * Load changelog state from disk, migrating old bare-array format if needed.
29
+ */
30
+ function loadChangelogState(changelogPath) {
31
+ try {
32
+ const raw = readFileSync(changelogPath, "utf-8");
33
+ const parsed = JSON.parse(raw);
34
+ // Handle old format (bare array)
35
+ if (Array.isArray(parsed)) {
36
+ return { version: 1, entries: parsed };
37
+ }
38
+ // Handle versioned format
39
+ if (parsed && typeof parsed === "object" && Array.isArray(parsed.entries)) {
40
+ return { version: 1, entries: parsed.entries };
41
+ }
42
+ }
43
+ catch {
44
+ // File doesn't exist or is invalid - start fresh
45
+ }
46
+ return { version: 1, entries: [] };
47
+ }
48
+ /**
49
+ * Appends a change entry to the changelog JSON file.
50
+ * Creates the file and parent directories if they don't exist.
51
+ * Rotates old entries when the file exceeds MAX_CHANGELOG_ENTRIES.
52
+ * Fails silently (logs to stderr) to avoid disrupting tool execution.
53
+ */
54
+ export function logChange(entry) {
55
+ try {
56
+ const config = getConfig();
57
+ const changelogPath = config.changelogPath;
58
+ const dir = dirname(changelogPath);
59
+ // Ensure directory exists
60
+ secureMkdirSync(dir);
61
+ // Read existing entries (with migration)
62
+ const state = loadChangelogState(changelogPath);
63
+ // Append new entry
64
+ state.entries.push(entry);
65
+ // Rotate: keep only the most recent entries to prevent unbounded growth
66
+ if (state.entries.length > MAX_CHANGELOG_ENTRIES) {
67
+ state.entries = state.entries.slice(-MAX_CHANGELOG_ENTRIES);
68
+ }
69
+ // Write back in versioned format
70
+ secureWriteFileSync(changelogPath, JSON.stringify(state, null, 2), "utf-8");
71
+ }
72
+ catch (err) {
73
+ const message = err instanceof Error ? err.message : String(err);
74
+ console.error(`[changelog] Failed to log change: ${message}`);
75
+ }
76
+ }
77
+ /**
78
+ * Reads changelog entries, newest first.
79
+ * Returns empty array on any error.
80
+ *
81
+ * @param limit Maximum number of entries to return (default: all)
82
+ */
83
+ export function getChangelog(limit) {
84
+ try {
85
+ const config = getConfig();
86
+ const state = loadChangelogState(config.changelogPath);
87
+ // Sort newest first
88
+ const sorted = state.entries.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
89
+ if (limit !== undefined && limit > 0) {
90
+ return sorted.slice(0, limit);
91
+ }
92
+ return sorted;
93
+ }
94
+ catch {
95
+ return [];
96
+ }
97
+ }
98
+ /**
99
+ * Creates a backup copy of a file using the unified BackupManager.
100
+ * The backup is tracked in the manifest at ~/.kali-defense/backups/manifest.json.
101
+ *
102
+ * @param filePath Absolute path to the file to back up
103
+ * @returns Path to the backup file
104
+ */
105
+ export function backupFile(filePath) {
106
+ const config = getConfig();
107
+ const manager = new BackupManager(config.backupDir);
108
+ const entry = manager.backupSync(filePath);
109
+ return entry.backupPath;
110
+ }
111
+ /**
112
+ * Restores a file from a backup.
113
+ *
114
+ * @param backupPath Path to the backup file
115
+ * @param originalPath Path to restore the file to
116
+ */
117
+ export function restoreFile(backupPath, originalPath) {
118
+ // Ensure target directory exists
119
+ const targetDir = dirname(originalPath);
120
+ secureMkdirSync(targetDir);
121
+ secureCopyFileSync(backupPath, originalPath);
122
+ console.error(`[changelog] Restored ${backupPath} → ${originalPath}`);
123
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Command Allowlist — security control that restricts which binaries the
3
+ * MCP server may execute.
4
+ *
5
+ * Every command passed to `executeCommand()` (and the bypass modules that
6
+ * use `execFileSync` / `spawn` directly) MUST be present in this allowlist.
7
+ * Bare command names are resolved to absolute paths at startup, eliminating
8
+ * PATH-manipulation attacks when running under sudo.
9
+ *
10
+ * Design constraints:
11
+ * - **No circular dependencies**: only imports from `node:fs` (no executor,
12
+ * no sudo-session, no tool-registry).
13
+ * - Uses `fs.existsSync` for path resolution — never shells out to `which`.
14
+ * - Candidate paths are checked in order; the first match wins.
15
+ * - Unresolvable binaries are logged as warnings but don't block startup
16
+ * (not every system has every tool installed).
17
+ *
18
+ * @module command-allowlist
19
+ */
20
+ export interface AllowlistEntry {
21
+ /** Bare binary name, e.g. "iptables" */
22
+ binary: string;
23
+ /** Ordered candidate absolute paths on Linux */
24
+ candidates: string[];
25
+ /** Filled at startup after resolution; `undefined` if not found on disk */
26
+ resolvedPath?: string;
27
+ /** Which distro package should own this binary (for integrity verification) */
28
+ expectedPackage?: string;
29
+ /** Inode number recorded at startup for TOCTOU detection (CORE-007) */
30
+ resolvedInode?: number;
31
+ }
32
+ /** Result of a binary ownership verification check */
33
+ export interface BinaryVerificationResult {
34
+ binary: string;
35
+ path: string;
36
+ verified: boolean;
37
+ owner?: string;
38
+ message: string;
39
+ }
40
+ /**
41
+ * Initialize the allowlist by resolving candidate paths on the current system.
42
+ *
43
+ * For each allowlisted binary, checks which candidate paths actually exist
44
+ * on disk and caches the first match. This should be called once at server
45
+ * startup, before any tool registration.
46
+ *
47
+ * Binaries that cannot be found are logged as warnings but do not prevent
48
+ * startup — not every system has every tool installed.
49
+ */
50
+ export declare function initializeAllowlist(): void;
51
+ /**
52
+ * Resolve a bare command name to its absolute path via the allowlist.
53
+ *
54
+ * @param command - Bare binary name (e.g. `"iptables"`)
55
+ * @returns The absolute path to the binary (e.g. `"/usr/sbin/iptables"`)
56
+ * @throws {Error} If the command is not in the allowlist or cannot be found
57
+ */
58
+ export declare function resolveCommand(command: string): string;
59
+ /**
60
+ * Check whether a bare command name is in the allowlist (without resolving).
61
+ *
62
+ * @param command - Bare binary name or absolute path
63
+ * @returns `true` if the command is allowlisted
64
+ */
65
+ export declare function isAllowlisted(command: string): boolean;
66
+ /**
67
+ * Resolve a sudo command and its target binary.
68
+ *
69
+ * When `command` is `"sudo"`, this function:
70
+ * 1. Resolves `sudo` itself to its absolute path
71
+ * 2. Finds the actual binary in the args array (skipping sudo flags like `-S`, `-p`, `-A`, `-k`, `-n`, `-v`)
72
+ * 3. Resolves that binary against the allowlist
73
+ * 4. Returns the resolved sudo path, the index of the target binary in args, and its resolved path
74
+ *
75
+ * @param args - The args array passed to sudo
76
+ * @returns Object with resolved paths and the index of the target command in args
77
+ * @throws {Error} If sudo or the target command is not allowlisted
78
+ */
79
+ export declare function resolveSudoCommand(args: string[]): {
80
+ sudoPath: string;
81
+ targetIndex: number;
82
+ targetPath: string;
83
+ };
84
+ /**
85
+ * Returns the full allowlist for inspection/debugging.
86
+ * Each entry includes resolution status.
87
+ */
88
+ export declare function getAllowlistEntries(): ReadonlyArray<Readonly<AllowlistEntry>>;
89
+ /**
90
+ * Returns whether the allowlist has been initialized.
91
+ */
92
+ export declare function isInitialized(): boolean;
93
+ /**
94
+ * Returns whether runtime path verification is currently enabled.
95
+ */
96
+ export declare function isRuntimePathVerificationEnabled(): boolean;
97
+ /**
98
+ * Enable or disable runtime path verification.
99
+ * Useful for testing or performance-sensitive environments.
100
+ */
101
+ export declare function setRuntimePathVerification(enabled: boolean): void;
102
+ /**
103
+ * Returns the critical binary package mappings for inspection/testing.
104
+ */
105
+ export declare function getCriticalBinaryPackages(): Readonly<Record<string, string[]>>;
106
+ /**
107
+ * Verify that a resolved binary is owned by its expected system package.
108
+ *
109
+ * Uses `dpkg -S` on Debian/Ubuntu, `rpm -qf` on RHEL/Fedora,
110
+ * or `pacman -Qo` on Arch to determine the owning package.
111
+ *
112
+ * @param binaryPath - Absolute path to the binary
113
+ * @param expectedPackage - Optional expected package name; if omitted, only ownership is checked
114
+ * @returns Verification result with owner info and status
115
+ */
116
+ export declare function verifyBinaryOwnership(binaryPath: string, expectedPackage?: string): BinaryVerificationResult;
117
+ /**
118
+ * Verify all resolved critical binaries against their expected packages.
119
+ *
120
+ * Runs after `initializeAllowlist()` and logs warnings for any binaries
121
+ * that can't be verified or are owned by unexpected packages.
122
+ *
123
+ * All verifications run in parallel for faster startup.
124
+ * This is best-effort — it never throws or blocks startup.
125
+ *
126
+ * @returns Array of verification results for all critical binaries that were resolved
127
+ */
128
+ export declare function verifyAllBinaries(): Promise<BinaryVerificationResult[]>;
129
+ //# sourceMappingURL=command-allowlist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-allowlist.d.ts","sourceRoot":"","sources":["../../src/core/command-allowlist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAeH,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,sDAAsD;AACtD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AA8WD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAkD1C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAsDtD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CActD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CA6CA;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAE7E;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;GAEG;AACH,wBAAgB,gCAAgC,IAAI,OAAO,CAE1D;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEjE;AA8CD;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAE9E;AAeD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,MAAM,GACvB,wBAAwB,CA6G1B;AAiHD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,wBAAwB,EAAE,CAAC,CA6C7E"}