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.
- package/CHANGELOG.md +471 -0
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/build/core/auto-installer.d.ts +102 -0
- package/build/core/auto-installer.d.ts.map +1 -0
- package/build/core/auto-installer.js +833 -0
- package/build/core/backup-manager.d.ts +63 -0
- package/build/core/backup-manager.d.ts.map +1 -0
- package/build/core/backup-manager.js +189 -0
- package/build/core/changelog.d.ts +75 -0
- package/build/core/changelog.d.ts.map +1 -0
- package/build/core/changelog.js +123 -0
- package/build/core/command-allowlist.d.ts +129 -0
- package/build/core/command-allowlist.d.ts.map +1 -0
- package/build/core/command-allowlist.js +849 -0
- package/build/core/config.d.ts +79 -0
- package/build/core/config.d.ts.map +1 -0
- package/build/core/config.js +193 -0
- package/build/core/dependency-validator.d.ts +106 -0
- package/build/core/dependency-validator.d.ts.map +1 -0
- package/build/core/dependency-validator.js +405 -0
- package/build/core/distro-adapter.d.ts +177 -0
- package/build/core/distro-adapter.d.ts.map +1 -0
- package/build/core/distro-adapter.js +481 -0
- package/build/core/distro.d.ts +68 -0
- package/build/core/distro.d.ts.map +1 -0
- package/build/core/distro.js +457 -0
- package/build/core/encrypted-state.d.ts +76 -0
- package/build/core/encrypted-state.d.ts.map +1 -0
- package/build/core/encrypted-state.js +209 -0
- package/build/core/executor.d.ts +56 -0
- package/build/core/executor.d.ts.map +1 -0
- package/build/core/executor.js +350 -0
- package/build/core/installer.d.ts +92 -0
- package/build/core/installer.d.ts.map +1 -0
- package/build/core/installer.js +1072 -0
- package/build/core/logger.d.ts +102 -0
- package/build/core/logger.d.ts.map +1 -0
- package/build/core/logger.js +132 -0
- package/build/core/parsers.d.ts +151 -0
- package/build/core/parsers.d.ts.map +1 -0
- package/build/core/parsers.js +479 -0
- package/build/core/policy-engine.d.ts +170 -0
- package/build/core/policy-engine.d.ts.map +1 -0
- package/build/core/policy-engine.js +656 -0
- package/build/core/preflight.d.ts +157 -0
- package/build/core/preflight.d.ts.map +1 -0
- package/build/core/preflight.js +638 -0
- package/build/core/privilege-manager.d.ts +108 -0
- package/build/core/privilege-manager.d.ts.map +1 -0
- package/build/core/privilege-manager.js +363 -0
- package/build/core/rate-limiter.d.ts +67 -0
- package/build/core/rate-limiter.d.ts.map +1 -0
- package/build/core/rate-limiter.js +129 -0
- package/build/core/rollback.d.ts +73 -0
- package/build/core/rollback.d.ts.map +1 -0
- package/build/core/rollback.js +278 -0
- package/build/core/safeguards.d.ts +58 -0
- package/build/core/safeguards.d.ts.map +1 -0
- package/build/core/safeguards.js +448 -0
- package/build/core/sanitizer.d.ts +118 -0
- package/build/core/sanitizer.d.ts.map +1 -0
- package/build/core/sanitizer.js +459 -0
- package/build/core/secure-fs.d.ts +67 -0
- package/build/core/secure-fs.d.ts.map +1 -0
- package/build/core/secure-fs.js +143 -0
- package/build/core/spawn-safe.d.ts +55 -0
- package/build/core/spawn-safe.d.ts.map +1 -0
- package/build/core/spawn-safe.js +146 -0
- package/build/core/sudo-guard.d.ts +145 -0
- package/build/core/sudo-guard.d.ts.map +1 -0
- package/build/core/sudo-guard.js +349 -0
- package/build/core/sudo-session.d.ts +100 -0
- package/build/core/sudo-session.d.ts.map +1 -0
- package/build/core/sudo-session.js +319 -0
- package/build/core/tool-dependencies.d.ts +61 -0
- package/build/core/tool-dependencies.d.ts.map +1 -0
- package/build/core/tool-dependencies.js +571 -0
- package/build/core/tool-registry.d.ts +111 -0
- package/build/core/tool-registry.d.ts.map +1 -0
- package/build/core/tool-registry.js +656 -0
- package/build/core/tool-wrapper.d.ts +73 -0
- package/build/core/tool-wrapper.d.ts.map +1 -0
- package/build/core/tool-wrapper.js +296 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +247 -0
- package/build/tools/access-control.d.ts +9 -0
- package/build/tools/access-control.d.ts.map +1 -0
- package/build/tools/access-control.js +1818 -0
- package/build/tools/api-security.d.ts +12 -0
- package/build/tools/api-security.d.ts.map +1 -0
- package/build/tools/api-security.js +901 -0
- package/build/tools/app-hardening.d.ts +11 -0
- package/build/tools/app-hardening.d.ts.map +1 -0
- package/build/tools/app-hardening.js +768 -0
- package/build/tools/backup.d.ts +8 -0
- package/build/tools/backup.d.ts.map +1 -0
- package/build/tools/backup.js +381 -0
- package/build/tools/cloud-security.d.ts +17 -0
- package/build/tools/cloud-security.d.ts.map +1 -0
- package/build/tools/cloud-security.js +739 -0
- package/build/tools/compliance.d.ts +10 -0
- package/build/tools/compliance.d.ts.map +1 -0
- package/build/tools/compliance.js +1225 -0
- package/build/tools/container-security.d.ts +14 -0
- package/build/tools/container-security.d.ts.map +1 -0
- package/build/tools/container-security.js +788 -0
- package/build/tools/deception.d.ts +13 -0
- package/build/tools/deception.d.ts.map +1 -0
- package/build/tools/deception.js +763 -0
- package/build/tools/dns-security.d.ts +93 -0
- package/build/tools/dns-security.d.ts.map +1 -0
- package/build/tools/dns-security.js +745 -0
- package/build/tools/drift-detection.d.ts +8 -0
- package/build/tools/drift-detection.d.ts.map +1 -0
- package/build/tools/drift-detection.js +326 -0
- package/build/tools/ebpf-security.d.ts +15 -0
- package/build/tools/ebpf-security.d.ts.map +1 -0
- package/build/tools/ebpf-security.js +294 -0
- package/build/tools/encryption.d.ts +9 -0
- package/build/tools/encryption.d.ts.map +1 -0
- package/build/tools/encryption.js +1667 -0
- package/build/tools/firewall.d.ts +9 -0
- package/build/tools/firewall.d.ts.map +1 -0
- package/build/tools/firewall.js +1398 -0
- package/build/tools/hardening.d.ts +10 -0
- package/build/tools/hardening.d.ts.map +1 -0
- package/build/tools/hardening.js +2654 -0
- package/build/tools/ids.d.ts +9 -0
- package/build/tools/ids.d.ts.map +1 -0
- package/build/tools/ids.js +624 -0
- package/build/tools/incident-response.d.ts +10 -0
- package/build/tools/incident-response.d.ts.map +1 -0
- package/build/tools/incident-response.js +1180 -0
- package/build/tools/logging.d.ts +12 -0
- package/build/tools/logging.d.ts.map +1 -0
- package/build/tools/logging.js +454 -0
- package/build/tools/malware.d.ts +10 -0
- package/build/tools/malware.d.ts.map +1 -0
- package/build/tools/malware.js +532 -0
- package/build/tools/meta.d.ts +11 -0
- package/build/tools/meta.d.ts.map +1 -0
- package/build/tools/meta.js +2278 -0
- package/build/tools/network-defense.d.ts +12 -0
- package/build/tools/network-defense.d.ts.map +1 -0
- package/build/tools/network-defense.js +760 -0
- package/build/tools/patch-management.d.ts +3 -0
- package/build/tools/patch-management.d.ts.map +1 -0
- package/build/tools/patch-management.js +708 -0
- package/build/tools/process-security.d.ts +12 -0
- package/build/tools/process-security.d.ts.map +1 -0
- package/build/tools/process-security.js +784 -0
- package/build/tools/reporting.d.ts +11 -0
- package/build/tools/reporting.d.ts.map +1 -0
- package/build/tools/reporting.js +559 -0
- package/build/tools/secrets.d.ts +9 -0
- package/build/tools/secrets.d.ts.map +1 -0
- package/build/tools/secrets.js +596 -0
- package/build/tools/siem-integration.d.ts +18 -0
- package/build/tools/siem-integration.d.ts.map +1 -0
- package/build/tools/siem-integration.js +754 -0
- package/build/tools/sudo-management.d.ts +18 -0
- package/build/tools/sudo-management.d.ts.map +1 -0
- package/build/tools/sudo-management.js +737 -0
- package/build/tools/supply-chain-security.d.ts +8 -0
- package/build/tools/supply-chain-security.d.ts.map +1 -0
- package/build/tools/supply-chain-security.js +256 -0
- package/build/tools/threat-intel.d.ts +22 -0
- package/build/tools/threat-intel.d.ts.map +1 -0
- package/build/tools/threat-intel.js +749 -0
- package/build/tools/vulnerability-management.d.ts +11 -0
- package/build/tools/vulnerability-management.d.ts.map +1 -0
- package/build/tools/vulnerability-management.js +667 -0
- package/build/tools/waf.d.ts +12 -0
- package/build/tools/waf.d.ts.map +1 -0
- package/build/tools/waf.js +843 -0
- package/build/tools/wireless-security.d.ts +19 -0
- package/build/tools/wireless-security.d.ts.map +1 -0
- package/build/tools/wireless-security.js +826 -0
- package/build/tools/zero-trust-network.d.ts +8 -0
- package/build/tools/zero-trust-network.d.ts.map +1 -0
- package/build/tools/zero-trust-network.js +367 -0
- package/docs/SAFEGUARDS.md +518 -0
- package/docs/TOOLS-REFERENCE.md +665 -0
- package/package.json +87 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intrusion Detection System (IDS) tools for Kali Defense MCP Server.
|
|
3
|
+
*
|
|
4
|
+
* Registers 3 tools: ids_aide_manage, ids_rootkit_scan (actions: rkhunter, chkrootkit, all),
|
|
5
|
+
* ids_file_integrity_check.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
export declare function registerIdsTools(server: McpServer): void;
|
|
9
|
+
//# sourceMappingURL=ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/tools/ids.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoBpE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAyrBxD"}
|
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intrusion Detection System (IDS) tools for Kali Defense MCP Server.
|
|
3
|
+
*
|
|
4
|
+
* Registers 3 tools: ids_aide_manage, ids_rootkit_scan (actions: rkhunter, chkrootkit, all),
|
|
5
|
+
* ids_file_integrity_check.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { executeCommand } from "../core/executor.js";
|
|
9
|
+
import { getConfig, getToolTimeout } from "../core/config.js";
|
|
10
|
+
import { createTextContent, createErrorContent, formatToolOutput, } from "../core/parsers.js";
|
|
11
|
+
import { logChange, createChangeEntry, } from "../core/changelog.js";
|
|
12
|
+
import { sanitizeArgs, validateToolPath } from "../core/sanitizer.js";
|
|
13
|
+
// ── TOOL-016 remediation: allowed directories for IDS config/baseline paths ─
|
|
14
|
+
const ALLOWED_IDS_DIRS = ["/etc", "/var/lib", "/tmp", "/home", "/opt", "/usr"];
|
|
15
|
+
// ── Registration entry point ───────────────────────────────────────────────
|
|
16
|
+
export function registerIdsTools(server) {
|
|
17
|
+
// ── 1. ids_aide_manage ─────────────────────────────────────────────────
|
|
18
|
+
server.tool("ids_aide_manage", "Manage AIDE (Advanced Intrusion Detection Environment) file integrity database", {
|
|
19
|
+
action: z
|
|
20
|
+
.enum(["init", "check", "update", "compare"])
|
|
21
|
+
.describe("Action: init (create baseline), check (verify), update (update db), compare (compare dbs)"),
|
|
22
|
+
config: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Path to custom AIDE config file"),
|
|
26
|
+
dry_run: z
|
|
27
|
+
.boolean()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("Preview the command without executing (defaults to KALI_DEFENSE_DRY_RUN env var)"),
|
|
30
|
+
}, async ({ action, config, dry_run }) => {
|
|
31
|
+
try {
|
|
32
|
+
const args = [];
|
|
33
|
+
switch (action) {
|
|
34
|
+
case "init":
|
|
35
|
+
args.push("--init");
|
|
36
|
+
break;
|
|
37
|
+
case "check":
|
|
38
|
+
args.push("--check");
|
|
39
|
+
break;
|
|
40
|
+
case "update":
|
|
41
|
+
args.push("--update");
|
|
42
|
+
break;
|
|
43
|
+
case "compare":
|
|
44
|
+
args.push("--compare");
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
if (config) {
|
|
48
|
+
// TOOL-016: Validate config path against traversal and allowed dirs
|
|
49
|
+
const validatedConfig = validateToolPath(config, ALLOWED_IDS_DIRS, "AIDE config path");
|
|
50
|
+
args.push("--config", validatedConfig);
|
|
51
|
+
}
|
|
52
|
+
const fullCmd = `sudo aide ${args.join(" ")}`;
|
|
53
|
+
if (dry_run ?? getConfig().dryRun) {
|
|
54
|
+
let preview = `[DRY-RUN] Would execute:\n ${fullCmd}`;
|
|
55
|
+
if (action === "init") {
|
|
56
|
+
preview +=
|
|
57
|
+
"\n\nThis will create a new AIDE database. After init, the database at /var/lib/aide/aide.db.new will need to be moved to /var/lib/aide/aide.db.";
|
|
58
|
+
}
|
|
59
|
+
else if (action === "check") {
|
|
60
|
+
preview +=
|
|
61
|
+
"\n\nThis will check file integrity against the baseline database.";
|
|
62
|
+
}
|
|
63
|
+
else if (action === "update") {
|
|
64
|
+
preview +=
|
|
65
|
+
"\n\nThis will update the AIDE database with current file states.";
|
|
66
|
+
}
|
|
67
|
+
else if (action === "compare") {
|
|
68
|
+
preview +=
|
|
69
|
+
"\n\nThis will compare the baseline and new databases.";
|
|
70
|
+
}
|
|
71
|
+
const entry = createChangeEntry({
|
|
72
|
+
tool: "ids_aide_manage",
|
|
73
|
+
action: `[DRY-RUN] AIDE ${action}`,
|
|
74
|
+
target: "aide-database",
|
|
75
|
+
dryRun: true,
|
|
76
|
+
success: true,
|
|
77
|
+
});
|
|
78
|
+
logChange(entry);
|
|
79
|
+
return { content: [createTextContent(preview)] };
|
|
80
|
+
}
|
|
81
|
+
const result = await executeCommand({
|
|
82
|
+
command: "sudo",
|
|
83
|
+
args: ["aide", ...args],
|
|
84
|
+
toolName: "ids_aide_manage",
|
|
85
|
+
timeout: getToolTimeout("aide"),
|
|
86
|
+
});
|
|
87
|
+
// AIDE returns different exit codes:
|
|
88
|
+
// 0 = no changes, 1-7 = changes detected (not necessarily errors)
|
|
89
|
+
const isError = action === "init" && result.exitCode !== 0;
|
|
90
|
+
// For init, move the new database
|
|
91
|
+
let postAction = "";
|
|
92
|
+
if (action === "init" && result.exitCode === 0) {
|
|
93
|
+
const moveResult = await executeCommand({
|
|
94
|
+
command: "sudo",
|
|
95
|
+
args: [
|
|
96
|
+
"mv",
|
|
97
|
+
"/var/lib/aide/aide.db.new",
|
|
98
|
+
"/var/lib/aide/aide.db",
|
|
99
|
+
],
|
|
100
|
+
toolName: "ids_aide_manage",
|
|
101
|
+
});
|
|
102
|
+
if (moveResult.exitCode === 0) {
|
|
103
|
+
postAction =
|
|
104
|
+
"\n\nDatabase initialized and moved to /var/lib/aide/aide.db";
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
postAction = `\n\nWarning: Database created but failed to move: ${moveResult.stderr}`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const entry = createChangeEntry({
|
|
111
|
+
tool: "ids_aide_manage",
|
|
112
|
+
action: `AIDE ${action}`,
|
|
113
|
+
target: "aide-database",
|
|
114
|
+
dryRun: false,
|
|
115
|
+
success: !isError,
|
|
116
|
+
error: isError ? result.stderr : undefined,
|
|
117
|
+
});
|
|
118
|
+
logChange(entry);
|
|
119
|
+
if (isError) {
|
|
120
|
+
return {
|
|
121
|
+
content: [
|
|
122
|
+
createErrorContent(`AIDE ${action} failed (exit ${result.exitCode}): ${result.stderr}`),
|
|
123
|
+
],
|
|
124
|
+
isError: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Parse AIDE check output for summary
|
|
128
|
+
let summary = "";
|
|
129
|
+
if (action === "check") {
|
|
130
|
+
const lines = result.stdout.split("\n");
|
|
131
|
+
const summaryLines = lines.filter((l) => l.includes("added") ||
|
|
132
|
+
l.includes("removed") ||
|
|
133
|
+
l.includes("changed") ||
|
|
134
|
+
l.includes("Total"));
|
|
135
|
+
if (summaryLines.length > 0) {
|
|
136
|
+
summary = `\n\nSummary:\n${summaryLines.join("\n")}`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
content: [
|
|
141
|
+
createTextContent(`AIDE ${action} completed (exit code: ${result.exitCode}).${postAction}${summary}\n\n${result.stdout}${result.stderr ? `\n\nStderr:\n${result.stderr}` : ""}`),
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
147
|
+
return { content: [createErrorContent(msg)], isError: true };
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
// ── 2. ids_rootkit_scan (merged: rkhunter, chkrootkit, all) ────────────
|
|
151
|
+
server.tool("ids_rootkit_scan", "Rootkit detection: run rkhunter, chkrootkit, or combined scan using all available tools.", {
|
|
152
|
+
action: z.enum(["rkhunter", "chkrootkit", "all"]).describe("Action: rkhunter=rkhunter scan, chkrootkit=chkrootkit scan, all=combined summary"),
|
|
153
|
+
// rkhunter params
|
|
154
|
+
update_first: z.boolean().optional().default(true).describe("Update rkhunter database before scanning (rkhunter/all action)"),
|
|
155
|
+
skip_keypress: z.boolean().optional().default(true).describe("Skip keypress prompts during scan (rkhunter action)"),
|
|
156
|
+
report_warnings_only: z.boolean().optional().default(false).describe("Only report warnings, not OK results (rkhunter action)"),
|
|
157
|
+
// chkrootkit params
|
|
158
|
+
quiet: z.boolean().optional().default(false).describe("Quiet mode - only show infected findings (chkrootkit action)"),
|
|
159
|
+
expert: z.boolean().optional().default(false).describe("Expert mode - show additional diagnostic info (chkrootkit action)"),
|
|
160
|
+
// all params
|
|
161
|
+
quick: z.boolean().optional().default(true).describe("Quick scan mode - skip database updates (all action)"),
|
|
162
|
+
}, async (params) => {
|
|
163
|
+
const { action } = params;
|
|
164
|
+
switch (action) {
|
|
165
|
+
// ── rkhunter ────────────────────────────────────────────────
|
|
166
|
+
case "rkhunter": {
|
|
167
|
+
const { update_first, skip_keypress, report_warnings_only } = params;
|
|
168
|
+
try {
|
|
169
|
+
let updateOutput = "";
|
|
170
|
+
if (update_first) {
|
|
171
|
+
const updateResult = await executeCommand({
|
|
172
|
+
command: "sudo",
|
|
173
|
+
args: ["rkhunter", "--update"],
|
|
174
|
+
toolName: "ids_rootkit_scan",
|
|
175
|
+
timeout: getToolTimeout("rkhunter"),
|
|
176
|
+
});
|
|
177
|
+
updateOutput = `Database update (exit ${updateResult.exitCode}):\n${updateResult.stdout}\n\n`;
|
|
178
|
+
}
|
|
179
|
+
const args = ["rkhunter", "--check"];
|
|
180
|
+
if (skip_keypress)
|
|
181
|
+
args.push("--sk");
|
|
182
|
+
if (report_warnings_only)
|
|
183
|
+
args.push("--rwo");
|
|
184
|
+
const result = await executeCommand({
|
|
185
|
+
command: "sudo",
|
|
186
|
+
args,
|
|
187
|
+
toolName: "ids_rootkit_scan",
|
|
188
|
+
timeout: getToolTimeout("rkhunter"),
|
|
189
|
+
});
|
|
190
|
+
const warnings = [];
|
|
191
|
+
const infected = [];
|
|
192
|
+
for (const line of result.stdout.split("\n")) {
|
|
193
|
+
const trimmed = line.trim();
|
|
194
|
+
if (trimmed.includes("[ Warning ]") || trimmed.includes("WARNING")) {
|
|
195
|
+
warnings.push(trimmed);
|
|
196
|
+
}
|
|
197
|
+
if (trimmed.includes("[ Infected ]") || trimmed.includes("INFECTED")) {
|
|
198
|
+
infected.push(trimmed);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const output = {
|
|
202
|
+
exitCode: result.exitCode,
|
|
203
|
+
updateOutput: update_first ? updateOutput : undefined,
|
|
204
|
+
warningsCount: warnings.length,
|
|
205
|
+
infectedCount: infected.length,
|
|
206
|
+
warnings,
|
|
207
|
+
infected,
|
|
208
|
+
riskLevel: infected.length > 0 ? "CRITICAL" : warnings.length > 0 ? "WARNING" : "CLEAN",
|
|
209
|
+
raw: result.stdout,
|
|
210
|
+
};
|
|
211
|
+
logChange(createChangeEntry({
|
|
212
|
+
tool: "ids_rootkit_scan",
|
|
213
|
+
action: "Rootkit scan (rkhunter)",
|
|
214
|
+
target: "system",
|
|
215
|
+
after: `Warnings: ${warnings.length}, Infected: ${infected.length}`,
|
|
216
|
+
dryRun: false,
|
|
217
|
+
success: true,
|
|
218
|
+
}));
|
|
219
|
+
return { content: [formatToolOutput(output)] };
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
223
|
+
return { content: [createErrorContent(msg)], isError: true };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// ── chkrootkit ──────────────────────────────────────────────
|
|
227
|
+
case "chkrootkit": {
|
|
228
|
+
const { quiet, expert } = params;
|
|
229
|
+
try {
|
|
230
|
+
const args = ["chkrootkit"];
|
|
231
|
+
if (quiet)
|
|
232
|
+
args.push("-q");
|
|
233
|
+
if (expert)
|
|
234
|
+
args.push("-x");
|
|
235
|
+
const result = await executeCommand({
|
|
236
|
+
command: "sudo",
|
|
237
|
+
args,
|
|
238
|
+
toolName: "ids_rootkit_scan",
|
|
239
|
+
timeout: getToolTimeout("chkrootkit"),
|
|
240
|
+
});
|
|
241
|
+
const infected = [];
|
|
242
|
+
const suspicious = [];
|
|
243
|
+
const notInfected = [];
|
|
244
|
+
for (const line of result.stdout.split("\n")) {
|
|
245
|
+
const trimmed = line.trim();
|
|
246
|
+
if (!trimmed)
|
|
247
|
+
continue;
|
|
248
|
+
if (trimmed.includes("INFECTED")) {
|
|
249
|
+
infected.push(trimmed);
|
|
250
|
+
}
|
|
251
|
+
else if (trimmed.includes("Suspicious") || trimmed.includes("suspicious")) {
|
|
252
|
+
suspicious.push(trimmed);
|
|
253
|
+
}
|
|
254
|
+
else if (trimmed.includes("not infected") || trimmed.includes("not found") || trimmed.includes("nothing found")) {
|
|
255
|
+
notInfected.push(trimmed);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const output = {
|
|
259
|
+
exitCode: result.exitCode,
|
|
260
|
+
infectedCount: infected.length,
|
|
261
|
+
suspiciousCount: suspicious.length,
|
|
262
|
+
cleanCount: notInfected.length,
|
|
263
|
+
infected,
|
|
264
|
+
suspicious,
|
|
265
|
+
riskLevel: infected.length > 0 ? "CRITICAL" : suspicious.length > 0 ? "WARNING" : "CLEAN",
|
|
266
|
+
raw: result.stdout,
|
|
267
|
+
};
|
|
268
|
+
logChange(createChangeEntry({
|
|
269
|
+
tool: "ids_rootkit_scan",
|
|
270
|
+
action: "Rootkit scan (chkrootkit)",
|
|
271
|
+
target: "system",
|
|
272
|
+
after: `Infected: ${infected.length}, Suspicious: ${suspicious.length}`,
|
|
273
|
+
dryRun: false,
|
|
274
|
+
success: true,
|
|
275
|
+
}));
|
|
276
|
+
return { content: [formatToolOutput(output)] };
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
280
|
+
return { content: [createErrorContent(msg)], isError: true };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// ── all (combined summary) ──────────────────────────────────
|
|
284
|
+
case "all": {
|
|
285
|
+
const { quick } = params;
|
|
286
|
+
try {
|
|
287
|
+
const findings = [];
|
|
288
|
+
// Check if rkhunter is available
|
|
289
|
+
const rkhunterCheck = await executeCommand({
|
|
290
|
+
command: "which",
|
|
291
|
+
args: ["rkhunter"],
|
|
292
|
+
toolName: "ids_rootkit_scan",
|
|
293
|
+
});
|
|
294
|
+
if (rkhunterCheck.exitCode === 0) {
|
|
295
|
+
const rkhunterArgs = ["rkhunter", "--check", "--sk"];
|
|
296
|
+
if (!quick) {
|
|
297
|
+
await executeCommand({
|
|
298
|
+
command: "sudo",
|
|
299
|
+
args: ["rkhunter", "--update"],
|
|
300
|
+
toolName: "ids_rootkit_scan",
|
|
301
|
+
timeout: getToolTimeout("rkhunter"),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
const rkhunterResult = await executeCommand({
|
|
305
|
+
command: "sudo",
|
|
306
|
+
args: rkhunterArgs,
|
|
307
|
+
toolName: "ids_rootkit_scan",
|
|
308
|
+
timeout: getToolTimeout("rkhunter"),
|
|
309
|
+
});
|
|
310
|
+
const warnings = [];
|
|
311
|
+
const infected = [];
|
|
312
|
+
for (const line of rkhunterResult.stdout.split("\n")) {
|
|
313
|
+
const trimmed = line.trim();
|
|
314
|
+
if (trimmed.includes("[ Warning ]") || trimmed.includes("WARNING"))
|
|
315
|
+
warnings.push(trimmed);
|
|
316
|
+
if (trimmed.includes("[ Infected ]") || trimmed.includes("INFECTED"))
|
|
317
|
+
infected.push(trimmed);
|
|
318
|
+
}
|
|
319
|
+
findings.push({
|
|
320
|
+
tool: "rkhunter",
|
|
321
|
+
available: true,
|
|
322
|
+
exitCode: rkhunterResult.exitCode,
|
|
323
|
+
warnings,
|
|
324
|
+
infected,
|
|
325
|
+
riskLevel: infected.length > 0 ? "CRITICAL" : warnings.length > 0 ? "WARNING" : "CLEAN",
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
findings.push({
|
|
330
|
+
tool: "rkhunter",
|
|
331
|
+
available: false,
|
|
332
|
+
exitCode: -1,
|
|
333
|
+
warnings: [],
|
|
334
|
+
infected: [],
|
|
335
|
+
riskLevel: "UNKNOWN",
|
|
336
|
+
error: "rkhunter not installed. Install with: sudo apt install rkhunter",
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
// Check if chkrootkit is available
|
|
340
|
+
const chkrootkitCheck = await executeCommand({
|
|
341
|
+
command: "which",
|
|
342
|
+
args: ["chkrootkit"],
|
|
343
|
+
toolName: "ids_rootkit_scan",
|
|
344
|
+
});
|
|
345
|
+
if (chkrootkitCheck.exitCode === 0) {
|
|
346
|
+
const chkrootkitResult = await executeCommand({
|
|
347
|
+
command: "sudo",
|
|
348
|
+
args: ["chkrootkit", "-q"],
|
|
349
|
+
toolName: "ids_rootkit_scan",
|
|
350
|
+
timeout: getToolTimeout("chkrootkit"),
|
|
351
|
+
});
|
|
352
|
+
const warnings = [];
|
|
353
|
+
const infected = [];
|
|
354
|
+
for (const line of chkrootkitResult.stdout.split("\n")) {
|
|
355
|
+
const trimmed = line.trim();
|
|
356
|
+
if (!trimmed)
|
|
357
|
+
continue;
|
|
358
|
+
if (trimmed.includes("INFECTED"))
|
|
359
|
+
infected.push(trimmed);
|
|
360
|
+
else if (trimmed.includes("Suspicious") || trimmed.includes("suspicious"))
|
|
361
|
+
warnings.push(trimmed);
|
|
362
|
+
}
|
|
363
|
+
findings.push({
|
|
364
|
+
tool: "chkrootkit",
|
|
365
|
+
available: true,
|
|
366
|
+
exitCode: chkrootkitResult.exitCode,
|
|
367
|
+
warnings,
|
|
368
|
+
infected,
|
|
369
|
+
riskLevel: infected.length > 0 ? "CRITICAL" : warnings.length > 0 ? "WARNING" : "CLEAN",
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
findings.push({
|
|
374
|
+
tool: "chkrootkit",
|
|
375
|
+
available: false,
|
|
376
|
+
exitCode: -1,
|
|
377
|
+
warnings: [],
|
|
378
|
+
infected: [],
|
|
379
|
+
riskLevel: "UNKNOWN",
|
|
380
|
+
error: "chkrootkit not installed. Install with: sudo apt install chkrootkit",
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
const totalInfected = findings.reduce((sum, f) => sum + f.infected.length, 0);
|
|
384
|
+
const totalWarnings = findings.reduce((sum, f) => sum + f.warnings.length, 0);
|
|
385
|
+
const availableTools = findings.filter((f) => f.available).length;
|
|
386
|
+
let overallRisk = "CLEAN";
|
|
387
|
+
if (totalInfected > 0)
|
|
388
|
+
overallRisk = "CRITICAL";
|
|
389
|
+
else if (totalWarnings > 0)
|
|
390
|
+
overallRisk = "WARNING";
|
|
391
|
+
else if (availableTools === 0)
|
|
392
|
+
overallRisk = "UNKNOWN - No scanning tools available";
|
|
393
|
+
const output = {
|
|
394
|
+
overallRisk,
|
|
395
|
+
summary: { toolsAvailable: availableTools, toolsTotal: findings.length, totalInfected, totalWarnings },
|
|
396
|
+
findings,
|
|
397
|
+
recommendations: totalInfected > 0
|
|
398
|
+
? ["CRITICAL: Potential rootkit detected. Investigate immediately.", "Boot from clean media and verify system integrity.", "Compare findings against known false positives.", "Consider reimaging the system if infection confirmed."]
|
|
399
|
+
: totalWarnings > 0
|
|
400
|
+
? ["Review warnings and determine if they are false positives.", "Cross-reference with system changes and package updates.", "Run a full scan with verbose output for more details."]
|
|
401
|
+
: availableTools > 0
|
|
402
|
+
? ["System appears clean. Schedule regular scans."]
|
|
403
|
+
: ["Install rkhunter: sudo apt install rkhunter", "Install chkrootkit: sudo apt install chkrootkit"],
|
|
404
|
+
};
|
|
405
|
+
logChange(createChangeEntry({
|
|
406
|
+
tool: "ids_rootkit_scan",
|
|
407
|
+
action: "Combined rootkit scan",
|
|
408
|
+
target: "system",
|
|
409
|
+
after: `Risk: ${overallRisk}, Infected: ${totalInfected}, Warnings: ${totalWarnings}`,
|
|
410
|
+
dryRun: false,
|
|
411
|
+
success: true,
|
|
412
|
+
}));
|
|
413
|
+
return { content: [formatToolOutput(output)] };
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
417
|
+
return { content: [createErrorContent(msg)], isError: true };
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
default:
|
|
421
|
+
return { content: [createErrorContent(`Unknown action: ${action}`)], isError: true };
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
// ── 3. ids_file_integrity_check (kept as-is) ──────────────────────────
|
|
425
|
+
server.tool("ids_file_integrity_check", "Quick file integrity check using SHA-256 hashes. Create, verify against, or display file baselines.", {
|
|
426
|
+
paths: z
|
|
427
|
+
.union([z.string(), z.array(z.string())])
|
|
428
|
+
.describe("File path(s) to check — accepts a single comma-separated string or an array of paths"),
|
|
429
|
+
baseline_path: z
|
|
430
|
+
.string()
|
|
431
|
+
.optional()
|
|
432
|
+
.describe("Path to baseline hash file for comparison"),
|
|
433
|
+
create_baseline: z
|
|
434
|
+
.boolean()
|
|
435
|
+
.optional()
|
|
436
|
+
.default(false)
|
|
437
|
+
.describe("Create a new baseline hash file from the given paths"),
|
|
438
|
+
}, async ({ paths, baseline_path, create_baseline }) => {
|
|
439
|
+
try {
|
|
440
|
+
const filePaths = (Array.isArray(paths)
|
|
441
|
+
? paths
|
|
442
|
+
: paths.split(","))
|
|
443
|
+
.map((p) => p.trim())
|
|
444
|
+
.filter((p) => p.length > 0);
|
|
445
|
+
if (filePaths.length === 0) {
|
|
446
|
+
return {
|
|
447
|
+
content: [createErrorContent("No file paths provided")],
|
|
448
|
+
isError: true,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
// TOOL-016: Validate each file path against traversal
|
|
452
|
+
for (const fp of filePaths) {
|
|
453
|
+
validateToolPath(fp, ALLOWED_IDS_DIRS, "File integrity path");
|
|
454
|
+
}
|
|
455
|
+
sanitizeArgs(filePaths);
|
|
456
|
+
// Compute sha256sum for each file
|
|
457
|
+
const hashResult = await executeCommand({
|
|
458
|
+
command: "sha256sum",
|
|
459
|
+
args: filePaths,
|
|
460
|
+
toolName: "ids_file_integrity_check",
|
|
461
|
+
timeout: getToolTimeout("ids_file_integrity_check"),
|
|
462
|
+
});
|
|
463
|
+
if (hashResult.exitCode !== 0 && !hashResult.stdout) {
|
|
464
|
+
return {
|
|
465
|
+
content: [
|
|
466
|
+
createErrorContent(`sha256sum failed (exit ${hashResult.exitCode}): ${hashResult.stderr}`),
|
|
467
|
+
],
|
|
468
|
+
isError: true,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
// Parse current hashes
|
|
472
|
+
const currentHashes = [];
|
|
473
|
+
for (const line of hashResult.stdout.split("\n")) {
|
|
474
|
+
const trimmed = line.trim();
|
|
475
|
+
if (!trimmed)
|
|
476
|
+
continue;
|
|
477
|
+
const parts = trimmed.split(/\s+/);
|
|
478
|
+
if (parts.length >= 2) {
|
|
479
|
+
currentHashes.push({ hash: parts[0], file: parts.slice(1).join(" ") });
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// Create baseline mode
|
|
483
|
+
if (create_baseline) {
|
|
484
|
+
const baselineOutput = baseline_path ?? "/tmp/file-integrity-baseline.sha256";
|
|
485
|
+
// TOOL-016: Validate baseline output path
|
|
486
|
+
validateToolPath(baselineOutput, ALLOWED_IDS_DIRS, "Baseline output path");
|
|
487
|
+
const writeResult = await executeCommand({
|
|
488
|
+
command: "sudo",
|
|
489
|
+
args: ["tee", baselineOutput],
|
|
490
|
+
stdin: hashResult.stdout,
|
|
491
|
+
toolName: "ids_file_integrity_check",
|
|
492
|
+
});
|
|
493
|
+
const success = writeResult.exitCode === 0;
|
|
494
|
+
const entry = createChangeEntry({
|
|
495
|
+
tool: "ids_file_integrity_check",
|
|
496
|
+
action: "Create integrity baseline",
|
|
497
|
+
target: baselineOutput,
|
|
498
|
+
after: `${currentHashes.length} files hashed`,
|
|
499
|
+
dryRun: false,
|
|
500
|
+
success,
|
|
501
|
+
error: success ? undefined : writeResult.stderr,
|
|
502
|
+
});
|
|
503
|
+
logChange(entry);
|
|
504
|
+
if (!success) {
|
|
505
|
+
return {
|
|
506
|
+
content: [
|
|
507
|
+
createErrorContent(`Failed to write baseline: ${writeResult.stderr}`),
|
|
508
|
+
],
|
|
509
|
+
isError: true,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
content: [
|
|
514
|
+
formatToolOutput({
|
|
515
|
+
action: "baseline_created",
|
|
516
|
+
baselinePath: baselineOutput,
|
|
517
|
+
fileCount: currentHashes.length,
|
|
518
|
+
hashes: currentHashes,
|
|
519
|
+
}),
|
|
520
|
+
],
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
// Compare against baseline mode
|
|
524
|
+
if (baseline_path) {
|
|
525
|
+
// TOOL-016: Validate baseline path for traversal
|
|
526
|
+
validateToolPath(baseline_path, ALLOWED_IDS_DIRS, "Baseline path");
|
|
527
|
+
// Read baseline
|
|
528
|
+
const baselineResult = await executeCommand({
|
|
529
|
+
command: "cat",
|
|
530
|
+
args: [baseline_path],
|
|
531
|
+
toolName: "ids_file_integrity_check",
|
|
532
|
+
});
|
|
533
|
+
if (baselineResult.exitCode !== 0) {
|
|
534
|
+
return {
|
|
535
|
+
content: [
|
|
536
|
+
createErrorContent(`Cannot read baseline file ${baseline_path}: ${baselineResult.stderr}`),
|
|
537
|
+
],
|
|
538
|
+
isError: true,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
// Parse baseline hashes
|
|
542
|
+
const baselineHashes = new Map();
|
|
543
|
+
for (const line of baselineResult.stdout.split("\n")) {
|
|
544
|
+
const trimmed = line.trim();
|
|
545
|
+
if (!trimmed)
|
|
546
|
+
continue;
|
|
547
|
+
const parts = trimmed.split(/\s+/);
|
|
548
|
+
if (parts.length >= 2) {
|
|
549
|
+
baselineHashes.set(parts.slice(1).join(" "), parts[0]);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
// Compare
|
|
553
|
+
const changed = [];
|
|
554
|
+
const unchanged = [];
|
|
555
|
+
const newFiles = [];
|
|
556
|
+
const missingFiles = [];
|
|
557
|
+
for (const current of currentHashes) {
|
|
558
|
+
const baselineHash = baselineHashes.get(current.file);
|
|
559
|
+
if (baselineHash === undefined) {
|
|
560
|
+
newFiles.push({ file: current.file, hash: current.hash });
|
|
561
|
+
}
|
|
562
|
+
else if (baselineHash !== current.hash) {
|
|
563
|
+
changed.push({
|
|
564
|
+
file: current.file,
|
|
565
|
+
baselineHash,
|
|
566
|
+
currentHash: current.hash,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
unchanged.push(current.file);
|
|
571
|
+
}
|
|
572
|
+
baselineHashes.delete(current.file);
|
|
573
|
+
}
|
|
574
|
+
// Remaining entries in baseline are missing files
|
|
575
|
+
for (const [file] of baselineHashes) {
|
|
576
|
+
missingFiles.push(file);
|
|
577
|
+
}
|
|
578
|
+
const output = {
|
|
579
|
+
action: "comparison",
|
|
580
|
+
baselinePath: baseline_path,
|
|
581
|
+
summary: {
|
|
582
|
+
total: currentHashes.length,
|
|
583
|
+
unchanged: unchanged.length,
|
|
584
|
+
changed: changed.length,
|
|
585
|
+
new: newFiles.length,
|
|
586
|
+
missing: missingFiles.length,
|
|
587
|
+
},
|
|
588
|
+
integrityStatus: changed.length > 0 || missingFiles.length > 0
|
|
589
|
+
? "MODIFIED"
|
|
590
|
+
: "INTACT",
|
|
591
|
+
changed,
|
|
592
|
+
newFiles,
|
|
593
|
+
missingFiles,
|
|
594
|
+
unchanged,
|
|
595
|
+
};
|
|
596
|
+
const entry = createChangeEntry({
|
|
597
|
+
tool: "ids_file_integrity_check",
|
|
598
|
+
action: "File integrity comparison",
|
|
599
|
+
target: baseline_path,
|
|
600
|
+
after: `Changed: ${changed.length}, New: ${newFiles.length}, Missing: ${missingFiles.length}`,
|
|
601
|
+
dryRun: false,
|
|
602
|
+
success: true,
|
|
603
|
+
});
|
|
604
|
+
logChange(entry);
|
|
605
|
+
return { content: [formatToolOutput(output)] };
|
|
606
|
+
}
|
|
607
|
+
// Display mode - just show hashes
|
|
608
|
+
return {
|
|
609
|
+
content: [
|
|
610
|
+
formatToolOutput({
|
|
611
|
+
action: "display",
|
|
612
|
+
fileCount: currentHashes.length,
|
|
613
|
+
hashes: currentHashes,
|
|
614
|
+
errors: hashResult.stderr || undefined,
|
|
615
|
+
}),
|
|
616
|
+
],
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
catch (err) {
|
|
620
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
621
|
+
return { content: [createErrorContent(msg)], isError: true };
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incident response tools for Kali Defense MCP Server.
|
|
3
|
+
*
|
|
4
|
+
* Registers 2 tools:
|
|
5
|
+
* - incident_response (actions: collect, ioc_scan, timeline)
|
|
6
|
+
* - ir_forensics (actions: memory_dump, disk_image, network_capture_forensic, evidence_bag, chain_of_custody)
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
export declare function registerIncidentResponseTools(server: McpServer): void;
|
|
10
|
+
//# sourceMappingURL=incident-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"incident-response.d.ts","sourceRoot":"","sources":["../../src/tools/incident-response.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4FpE,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4xCrE"}
|