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,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift detection tools.
|
|
3
|
+
*
|
|
4
|
+
* Registers 1 tool: drift_baseline (actions: create, compare, list).
|
|
5
|
+
*/
|
|
6
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
export declare function registerDriftDetectionTools(server: McpServer): void;
|
|
8
|
+
//# sourceMappingURL=drift-detection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift-detection.d.ts","sourceRoot":"","sources":["../../src/tools/drift-detection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAwFpE,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkSnE"}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift detection tools.
|
|
3
|
+
*
|
|
4
|
+
* Registers 1 tool: drift_baseline (actions: create, compare, list).
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { executeCommand } from "../core/executor.js";
|
|
8
|
+
import { createErrorContent, formatToolOutput } from "../core/parsers.js";
|
|
9
|
+
import { existsSync, readFileSync, mkdirSync, readdirSync, statSync } from "node:fs";
|
|
10
|
+
import { join, extname, resolve } from "node:path";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { secureWriteFileSync } from "../core/secure-fs.js";
|
|
14
|
+
const BASELINE_DIR = join(homedir(), ".kali-mcp-baselines");
|
|
15
|
+
// ── TOOL-024 remediation: baseline path validation ─────────────────────────
|
|
16
|
+
/** Allowed directories for drift baseline files */
|
|
17
|
+
const ALLOWED_BASELINE_DIRS = [BASELINE_DIR, "/tmp", "/var/lib", "/home", "/root", "/opt"];
|
|
18
|
+
/** Allowed file extensions for baseline files */
|
|
19
|
+
const ALLOWED_BASELINE_EXTENSIONS = new Set([".json", ".yaml", ".yml"]);
|
|
20
|
+
/**
|
|
21
|
+
* Validate a baseline file path:
|
|
22
|
+
* 1. Reject `..` sequences
|
|
23
|
+
* 2. Resolve and verify within allowed directories
|
|
24
|
+
* 3. Validate file extension
|
|
25
|
+
*/
|
|
26
|
+
function validateBaselinePath(inputPath) {
|
|
27
|
+
if (!inputPath || typeof inputPath !== "string") {
|
|
28
|
+
throw new Error("Baseline path must be a non-empty string");
|
|
29
|
+
}
|
|
30
|
+
if (inputPath.includes("..")) {
|
|
31
|
+
throw new Error(`Baseline path contains forbidden traversal sequence (..): '${inputPath}'`);
|
|
32
|
+
}
|
|
33
|
+
const resolved = resolve(inputPath);
|
|
34
|
+
const isAllowed = ALLOWED_BASELINE_DIRS.some((dir) => resolved === dir || resolved.startsWith(dir + "/"));
|
|
35
|
+
if (!isAllowed) {
|
|
36
|
+
throw new Error(`Baseline path '${resolved}' is not within allowed directories: ${ALLOWED_BASELINE_DIRS.join(", ")}`);
|
|
37
|
+
}
|
|
38
|
+
const ext = extname(resolved).toLowerCase();
|
|
39
|
+
if (ext && !ALLOWED_BASELINE_EXTENSIONS.has(ext)) {
|
|
40
|
+
throw new Error(`Baseline file has invalid extension '${ext}'. Allowed: ${[...ALLOWED_BASELINE_EXTENSIONS].join(", ")}`);
|
|
41
|
+
}
|
|
42
|
+
return resolved;
|
|
43
|
+
}
|
|
44
|
+
function ensureBaselineDir() {
|
|
45
|
+
if (!existsSync(BASELINE_DIR)) {
|
|
46
|
+
mkdirSync(BASELINE_DIR, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function hashFile(filePath) {
|
|
50
|
+
try {
|
|
51
|
+
const content = readFileSync(filePath);
|
|
52
|
+
return createHash("sha256").update(content).digest("hex");
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return "unreadable";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function registerDriftDetectionTools(server) {
|
|
59
|
+
server.tool("drift_baseline", "Drift detection: create system baselines, compare current state against baselines, or list available baselines.", {
|
|
60
|
+
action: z.enum(["create", "compare", "list"]).describe("Action: create=create baseline, compare=compare against baseline, list=list baselines"),
|
|
61
|
+
// create/compare params
|
|
62
|
+
name: z.string().optional().default("default").describe("Baseline name"),
|
|
63
|
+
directories: z.array(z.string()).optional().default(["/etc"]).describe("Directories to hash (create action)"),
|
|
64
|
+
// shared
|
|
65
|
+
dryRun: z.boolean().optional().default(true).describe("Preview only"),
|
|
66
|
+
}, async (params) => {
|
|
67
|
+
const { action } = params;
|
|
68
|
+
switch (action) {
|
|
69
|
+
// ── create ──────────────────────────────────────────────────
|
|
70
|
+
case "create": {
|
|
71
|
+
const { directories, name, dryRun } = params;
|
|
72
|
+
try {
|
|
73
|
+
ensureBaselineDir();
|
|
74
|
+
if (dryRun) {
|
|
75
|
+
return {
|
|
76
|
+
content: [formatToolOutput({
|
|
77
|
+
dryRun: true,
|
|
78
|
+
directories,
|
|
79
|
+
baselineName: name,
|
|
80
|
+
storagePath: join(BASELINE_DIR, `${name}.json`),
|
|
81
|
+
})],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// Hash files in directories
|
|
85
|
+
const files = [];
|
|
86
|
+
for (const dir of directories) {
|
|
87
|
+
if (!existsSync(dir))
|
|
88
|
+
continue;
|
|
89
|
+
const findResult = await executeCommand({
|
|
90
|
+
command: "find",
|
|
91
|
+
args: [dir, "-maxdepth", "3", "-type", "f", "-not", "-path", "*/proc/*", "-not", "-path", "*/sys/*"],
|
|
92
|
+
timeout: 30000,
|
|
93
|
+
});
|
|
94
|
+
// find may return non-zero due to permission errors but still output valid paths
|
|
95
|
+
if (findResult.stdout.trim()) {
|
|
96
|
+
const paths = findResult.stdout.trim().split("\n").filter(Boolean).slice(0, 5000);
|
|
97
|
+
for (const p of paths) {
|
|
98
|
+
try {
|
|
99
|
+
const stat = statSync(p);
|
|
100
|
+
files.push({
|
|
101
|
+
path: p,
|
|
102
|
+
hash: hashFile(p),
|
|
103
|
+
size: stat.size,
|
|
104
|
+
mtime: stat.mtime.toISOString(),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch { /* skip unreadable */ }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Capture sysctl state
|
|
112
|
+
const sysctlState = {};
|
|
113
|
+
const sysctlResult = await executeCommand({
|
|
114
|
+
command: "sysctl",
|
|
115
|
+
args: ["-a"],
|
|
116
|
+
timeout: 10000,
|
|
117
|
+
});
|
|
118
|
+
if (sysctlResult.exitCode === 0 && sysctlResult.stdout.trim()) {
|
|
119
|
+
for (const line of sysctlResult.stdout.split("\n")) {
|
|
120
|
+
const idx = line.indexOf("=");
|
|
121
|
+
if (idx > 0) {
|
|
122
|
+
sysctlState[line.substring(0, idx).trim()] = line.substring(idx + 1).trim();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Fallback: read key sysctl values from /proc/sys/ if sysctl binary failed or returned nothing
|
|
127
|
+
if (Object.keys(sysctlState).length === 0) {
|
|
128
|
+
const procSysKeys = [
|
|
129
|
+
["net.ipv4.ip_forward", "/proc/sys/net/ipv4/ip_forward"],
|
|
130
|
+
["net.ipv4.conf.all.accept_redirects", "/proc/sys/net/ipv4/conf/all/accept_redirects"],
|
|
131
|
+
["net.ipv4.conf.all.send_redirects", "/proc/sys/net/ipv4/conf/all/send_redirects"],
|
|
132
|
+
["net.ipv4.conf.all.accept_source_route", "/proc/sys/net/ipv4/conf/all/accept_source_route"],
|
|
133
|
+
["net.ipv4.conf.all.log_martians", "/proc/sys/net/ipv4/conf/all/log_martians"],
|
|
134
|
+
["net.ipv4.icmp_echo_ignore_broadcasts", "/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts"],
|
|
135
|
+
["net.ipv4.tcp_syncookies", "/proc/sys/net/ipv4/tcp_syncookies"],
|
|
136
|
+
["net.ipv6.conf.all.accept_redirects", "/proc/sys/net/ipv6/conf/all/accept_redirects"],
|
|
137
|
+
["net.ipv6.conf.all.accept_source_route", "/proc/sys/net/ipv6/conf/all/accept_source_route"],
|
|
138
|
+
["kernel.randomize_va_space", "/proc/sys/kernel/randomize_va_space"],
|
|
139
|
+
["kernel.dmesg_restrict", "/proc/sys/kernel/dmesg_restrict"],
|
|
140
|
+
["kernel.kptr_restrict", "/proc/sys/kernel/kptr_restrict"],
|
|
141
|
+
["kernel.yama.ptrace_scope", "/proc/sys/kernel/yama/ptrace_scope"],
|
|
142
|
+
["kernel.sysrq", "/proc/sys/kernel/sysrq"],
|
|
143
|
+
["fs.protected_hardlinks", "/proc/sys/fs/protected_hardlinks"],
|
|
144
|
+
["fs.protected_symlinks", "/proc/sys/fs/protected_symlinks"],
|
|
145
|
+
["fs.suid_dumpable", "/proc/sys/fs/suid_dumpable"],
|
|
146
|
+
];
|
|
147
|
+
for (const [key, procPath] of procSysKeys) {
|
|
148
|
+
try {
|
|
149
|
+
if (existsSync(procPath)) {
|
|
150
|
+
const val = readFileSync(procPath, "utf-8").trim();
|
|
151
|
+
sysctlState[key] = val;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch { /* skip unreadable */ }
|
|
155
|
+
}
|
|
156
|
+
if (Object.keys(sysctlState).length > 0) {
|
|
157
|
+
console.error(`[drift-detection] sysctl binary unavailable; read ${Object.keys(sysctlState).length} keys from /proc/sys/`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
console.error("[drift-detection] Warning: could not capture any sysctl state (sysctl binary unavailable and /proc/sys/ read failed)");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Capture service states
|
|
164
|
+
const services = {};
|
|
165
|
+
const svcResult = await executeCommand({
|
|
166
|
+
command: "systemctl",
|
|
167
|
+
args: ["list-units", "--type=service", "--no-pager", "--plain", "--no-legend"],
|
|
168
|
+
timeout: 10000,
|
|
169
|
+
});
|
|
170
|
+
if (svcResult.exitCode === 0) {
|
|
171
|
+
for (const line of svcResult.stdout.split("\n")) {
|
|
172
|
+
const parts = line.trim().split(/\s+/);
|
|
173
|
+
if (parts.length >= 3) {
|
|
174
|
+
services[parts[0]] = parts[2]; // active/inactive/failed
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const baseline = {
|
|
179
|
+
id: name,
|
|
180
|
+
timestamp: new Date().toISOString(),
|
|
181
|
+
directories,
|
|
182
|
+
files,
|
|
183
|
+
sysctlState,
|
|
184
|
+
services,
|
|
185
|
+
};
|
|
186
|
+
const outPath = join(BASELINE_DIR, `${name}.json`);
|
|
187
|
+
// TOOL-024: Validate baseline output path and use secure write
|
|
188
|
+
validateBaselinePath(outPath);
|
|
189
|
+
secureWriteFileSync(outPath, JSON.stringify(baseline, null, 2), "utf-8");
|
|
190
|
+
return {
|
|
191
|
+
content: [formatToolOutput({
|
|
192
|
+
baselineName: name,
|
|
193
|
+
timestamp: baseline.timestamp,
|
|
194
|
+
filesHashed: files.length,
|
|
195
|
+
sysctlKeys: Object.keys(sysctlState).length,
|
|
196
|
+
servicesTracked: Object.keys(services).length,
|
|
197
|
+
savedTo: outPath,
|
|
198
|
+
})],
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
return { content: [createErrorContent(`Baseline creation failed: ${err instanceof Error ? err.message : String(err)}`)], isError: true };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// ── compare ─────────────────────────────────────────────────
|
|
206
|
+
case "compare": {
|
|
207
|
+
const { name, dryRun } = params;
|
|
208
|
+
try {
|
|
209
|
+
const baselinePath = join(BASELINE_DIR, `${name}.json`);
|
|
210
|
+
// TOOL-024: Validate baseline path before reading
|
|
211
|
+
validateBaselinePath(baselinePath);
|
|
212
|
+
if (!existsSync(baselinePath)) {
|
|
213
|
+
return { content: [createErrorContent(`Baseline '${name}' not found at ${baselinePath}`)], isError: true };
|
|
214
|
+
}
|
|
215
|
+
const baseline = JSON.parse(readFileSync(baselinePath, "utf-8"));
|
|
216
|
+
const fileChanges = [];
|
|
217
|
+
const sysctlChanges = [];
|
|
218
|
+
const serviceChanges = [];
|
|
219
|
+
// Compare files
|
|
220
|
+
for (const entry of baseline.files.slice(0, 2000)) {
|
|
221
|
+
if (!existsSync(entry.path)) {
|
|
222
|
+
fileChanges.push({ path: entry.path, type: "deleted", detail: "File no longer exists" });
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const currentHash = hashFile(entry.path);
|
|
226
|
+
if (currentHash !== entry.hash && currentHash !== "unreadable") {
|
|
227
|
+
fileChanges.push({ path: entry.path, type: "modified", detail: `hash changed: ${entry.hash.slice(0, 12)}... → ${currentHash.slice(0, 12)}...` });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Compare sysctl
|
|
231
|
+
const sysctlResult = await executeCommand({
|
|
232
|
+
command: "sysctl",
|
|
233
|
+
args: ["-a"],
|
|
234
|
+
timeout: 10000,
|
|
235
|
+
});
|
|
236
|
+
if (sysctlResult.exitCode === 0) {
|
|
237
|
+
const currentSysctl = {};
|
|
238
|
+
for (const line of sysctlResult.stdout.split("\n")) {
|
|
239
|
+
const idx = line.indexOf("=");
|
|
240
|
+
if (idx > 0) {
|
|
241
|
+
currentSysctl[line.substring(0, idx).trim()] = line.substring(idx + 1).trim();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
for (const [key, val] of Object.entries(baseline.sysctlState)) {
|
|
245
|
+
const current = currentSysctl[key];
|
|
246
|
+
if (current !== undefined && current !== val) {
|
|
247
|
+
sysctlChanges.push({ key, baseline: val, current });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Compare services
|
|
252
|
+
const svcResult = await executeCommand({
|
|
253
|
+
command: "systemctl",
|
|
254
|
+
args: ["list-units", "--type=service", "--no-pager", "--plain", "--no-legend"],
|
|
255
|
+
timeout: 10000,
|
|
256
|
+
});
|
|
257
|
+
if (svcResult.exitCode === 0) {
|
|
258
|
+
const currentServices = {};
|
|
259
|
+
for (const line of svcResult.stdout.split("\n")) {
|
|
260
|
+
const parts = line.trim().split(/\s+/);
|
|
261
|
+
if (parts.length >= 3) {
|
|
262
|
+
currentServices[parts[0]] = parts[2];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
for (const [svc, state] of Object.entries(baseline.services)) {
|
|
266
|
+
const current = currentServices[svc];
|
|
267
|
+
if (current !== undefined && current !== state) {
|
|
268
|
+
serviceChanges.push({ service: svc, baseline: state, current });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const totalDrifts = fileChanges.length + sysctlChanges.length + serviceChanges.length;
|
|
273
|
+
return {
|
|
274
|
+
content: [formatToolOutput({
|
|
275
|
+
baselineName: name,
|
|
276
|
+
baselineTimestamp: baseline.timestamp,
|
|
277
|
+
comparedAt: new Date().toISOString(),
|
|
278
|
+
totalDrifts,
|
|
279
|
+
fileChanges: fileChanges.slice(0, 50),
|
|
280
|
+
sysctlChanges: sysctlChanges.slice(0, 50),
|
|
281
|
+
serviceChanges: serviceChanges.slice(0, 50),
|
|
282
|
+
status: totalDrifts === 0 ? "NO_DRIFT" : "DRIFT_DETECTED",
|
|
283
|
+
})],
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
return { content: [createErrorContent(`Baseline comparison failed: ${err instanceof Error ? err.message : String(err)}`)], isError: true };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// ── list ────────────────────────────────────────────────────
|
|
291
|
+
case "list": {
|
|
292
|
+
try {
|
|
293
|
+
ensureBaselineDir();
|
|
294
|
+
const files = readdirSync(BASELINE_DIR).filter((f) => f.endsWith(".json") && f !== "manifest.json");
|
|
295
|
+
const baselines = files.map((f) => {
|
|
296
|
+
try {
|
|
297
|
+
const data = JSON.parse(readFileSync(join(BASELINE_DIR, f), "utf-8"));
|
|
298
|
+
return {
|
|
299
|
+
name: data.id,
|
|
300
|
+
timestamp: data.timestamp,
|
|
301
|
+
filesTracked: data.files.length,
|
|
302
|
+
sysctlKeys: Object.keys(data.sysctlState).length,
|
|
303
|
+
services: Object.keys(data.services).length,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
return { name: f, timestamp: "unknown", filesTracked: 0, sysctlKeys: 0, services: 0 };
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
return {
|
|
311
|
+
content: [formatToolOutput({
|
|
312
|
+
baselineDir: BASELINE_DIR,
|
|
313
|
+
totalBaselines: baselines.length,
|
|
314
|
+
baselines,
|
|
315
|
+
})],
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
return { content: [createErrorContent(`Drift listing failed: ${err instanceof Error ? err.message : String(err)}`)], isError: true };
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
default:
|
|
323
|
+
return { content: [createErrorContent(`Unknown action: ${action}`)], isError: true };
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eBPF and Falco security tools.
|
|
3
|
+
*
|
|
4
|
+
* Registers 2 tools: list_ebpf_programs, falco (actions: status, deploy_rules, events).
|
|
5
|
+
*/
|
|
6
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
/**
|
|
8
|
+
* Validate a BPF filter expression.
|
|
9
|
+
* Rejects shell metacharacters, enforces length limits, and allows only
|
|
10
|
+
* safe characters (alphanumeric, spaces, dots, colons, slashes, hyphens,
|
|
11
|
+
* underscores, brackets, comparison operators, and quotes).
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateBpfFilter(filter: string): string;
|
|
14
|
+
export declare function registerEbpfSecurityTools(server: McpServer): void;
|
|
15
|
+
//# sourceMappingURL=ebpf-security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ebpf-security.d.ts","sourceRoot":"","sources":["../../src/tools/ebpf-security.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqBpE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA0BxD;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwRjE"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eBPF and Falco security tools.
|
|
3
|
+
*
|
|
4
|
+
* Registers 2 tools: list_ebpf_programs, falco (actions: status, deploy_rules, events).
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { executeCommand } from "../core/executor.js";
|
|
8
|
+
import { createErrorContent, formatToolOutput } from "../core/parsers.js";
|
|
9
|
+
import { logChange, createChangeEntry } from "../core/changelog.js";
|
|
10
|
+
import { SafeguardRegistry } from "../core/safeguards.js";
|
|
11
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { secureWriteFileSync } from "../core/secure-fs.js";
|
|
13
|
+
// ── TOOL-018 remediation: BPF filter expression validation ─────────────────
|
|
14
|
+
/** Characters allowed in BPF filter expressions */
|
|
15
|
+
const BPF_FILTER_ALLOWED_RE = /^[a-zA-Z0-9\s.\/:_\-\[\]!=<>'"]+$/;
|
|
16
|
+
/** Shell metacharacters that must be rejected in BPF filters */
|
|
17
|
+
const BPF_SHELL_METACHAR_RE = /[;|&`$(){}<>]/;
|
|
18
|
+
/** Maximum BPF filter expression length */
|
|
19
|
+
const BPF_FILTER_MAX_LENGTH = 500;
|
|
20
|
+
/**
|
|
21
|
+
* Validate a BPF filter expression.
|
|
22
|
+
* Rejects shell metacharacters, enforces length limits, and allows only
|
|
23
|
+
* safe characters (alphanumeric, spaces, dots, colons, slashes, hyphens,
|
|
24
|
+
* underscores, brackets, comparison operators, and quotes).
|
|
25
|
+
*/
|
|
26
|
+
export function validateBpfFilter(filter) {
|
|
27
|
+
if (!filter || typeof filter !== "string") {
|
|
28
|
+
throw new Error("BPF filter must be a non-empty string");
|
|
29
|
+
}
|
|
30
|
+
const trimmed = filter.trim();
|
|
31
|
+
if (trimmed.length > BPF_FILTER_MAX_LENGTH) {
|
|
32
|
+
throw new Error(`BPF filter expression too long (${trimmed.length} chars). Maximum is ${BPF_FILTER_MAX_LENGTH} characters.`);
|
|
33
|
+
}
|
|
34
|
+
if (BPF_SHELL_METACHAR_RE.test(trimmed)) {
|
|
35
|
+
throw new Error(`BPF filter contains forbidden shell metacharacters: '${trimmed}'`);
|
|
36
|
+
}
|
|
37
|
+
if (!BPF_FILTER_ALLOWED_RE.test(trimmed)) {
|
|
38
|
+
throw new Error(`BPF filter contains invalid characters. Only alphanumeric, spaces, dots, colons, slashes, hyphens, underscores, brackets, comparison operators, and quotes are allowed.`);
|
|
39
|
+
}
|
|
40
|
+
return trimmed;
|
|
41
|
+
}
|
|
42
|
+
export function registerEbpfSecurityTools(server) {
|
|
43
|
+
// ── list_ebpf_programs (kept as-is) ────────────────────────────────────────
|
|
44
|
+
server.tool("ebpf_list_programs", "List loaded eBPF programs and pinned maps.", {
|
|
45
|
+
dryRun: z.boolean().optional().default(true).describe("Preview only"),
|
|
46
|
+
}, async ({ dryRun }) => {
|
|
47
|
+
try {
|
|
48
|
+
const results = {};
|
|
49
|
+
// List BPF filesystem
|
|
50
|
+
if (existsSync("/sys/fs/bpf")) {
|
|
51
|
+
const ls = await executeCommand({
|
|
52
|
+
command: "ls",
|
|
53
|
+
args: ["-la", "/sys/fs/bpf"],
|
|
54
|
+
timeout: 5000,
|
|
55
|
+
});
|
|
56
|
+
results.bpfFs = ls.stdout.trim().split("\n");
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
results.bpfFs = "Not mounted";
|
|
60
|
+
}
|
|
61
|
+
// Try bpftool
|
|
62
|
+
const bpftool = await executeCommand({
|
|
63
|
+
command: "bpftool",
|
|
64
|
+
args: ["prog", "list", "--json"],
|
|
65
|
+
timeout: 10000,
|
|
66
|
+
});
|
|
67
|
+
if (bpftool.exitCode === 0) {
|
|
68
|
+
try {
|
|
69
|
+
results.programs = JSON.parse(bpftool.stdout);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
results.programs = bpftool.stdout.split("\n").filter(Boolean);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Fallback to non-JSON
|
|
77
|
+
const fallback = await executeCommand({
|
|
78
|
+
command: "bpftool",
|
|
79
|
+
args: ["prog", "list"],
|
|
80
|
+
timeout: 10000,
|
|
81
|
+
});
|
|
82
|
+
results.programs = fallback.exitCode === 0
|
|
83
|
+
? fallback.stdout.split("\n").filter(Boolean)
|
|
84
|
+
: "bpftool not available or insufficient permissions";
|
|
85
|
+
}
|
|
86
|
+
// List maps
|
|
87
|
+
const maps = await executeCommand({
|
|
88
|
+
command: "bpftool",
|
|
89
|
+
args: ["map", "list", "--json"],
|
|
90
|
+
timeout: 10000,
|
|
91
|
+
});
|
|
92
|
+
if (maps.exitCode === 0) {
|
|
93
|
+
try {
|
|
94
|
+
results.maps = JSON.parse(maps.stdout);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
results.maps = maps.stdout.split("\n").filter(Boolean);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { content: [formatToolOutput(results)] };
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
return { content: [createErrorContent(`eBPF listing failed: ${err instanceof Error ? err.message : String(err)}`)], isError: true };
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// ── falco (merged: check_falco + deploy_falco_rules + get_ebpf_events) ────
|
|
107
|
+
server.tool("ebpf_falco", "Falco runtime security: check status, deploy custom rules, or read recent events.", {
|
|
108
|
+
action: z.enum(["status", "deploy_rules", "events"]).describe("Action: status=check Falco, deploy_rules=deploy custom rules, events=read recent events"),
|
|
109
|
+
// deploy_rules params
|
|
110
|
+
ruleName: z.string().optional().describe("Rule file name without .yaml extension (deploy_rules action)"),
|
|
111
|
+
ruleContent: z.string().optional().describe("YAML rule content (deploy_rules action)"),
|
|
112
|
+
// events params
|
|
113
|
+
lines: z.number().optional().default(50).describe("Number of recent events to return (events action)"),
|
|
114
|
+
priority: z.enum(["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"]).optional().describe("Filter by minimum priority (events action)"),
|
|
115
|
+
// shared
|
|
116
|
+
dryRun: z.boolean().optional().default(true).describe("Preview only"),
|
|
117
|
+
}, async (params) => {
|
|
118
|
+
const { action } = params;
|
|
119
|
+
switch (action) {
|
|
120
|
+
// ── status ──────────────────────────────────────────────────
|
|
121
|
+
case "status": {
|
|
122
|
+
try {
|
|
123
|
+
const info = {};
|
|
124
|
+
// Check if falco is installed
|
|
125
|
+
const which = await executeCommand({ command: "which", args: ["falco"], timeout: 5000 });
|
|
126
|
+
info.installed = which.exitCode === 0;
|
|
127
|
+
if (!info.installed) {
|
|
128
|
+
return {
|
|
129
|
+
content: [formatToolOutput({
|
|
130
|
+
installed: false,
|
|
131
|
+
message: "Falco not installed. Install from https://falco.org/docs/install-operate/installation/",
|
|
132
|
+
})],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Version
|
|
136
|
+
const version = await executeCommand({ command: "falco", args: ["--version"], timeout: 5000 });
|
|
137
|
+
info.version = version.stdout.trim();
|
|
138
|
+
// Service status
|
|
139
|
+
const status = await executeCommand({
|
|
140
|
+
command: "systemctl",
|
|
141
|
+
args: ["status", "falco", "--no-pager"],
|
|
142
|
+
timeout: 10000,
|
|
143
|
+
});
|
|
144
|
+
info.serviceStatus = status.stdout.trim().split("\n").slice(0, 10);
|
|
145
|
+
info.active = status.stdout.includes("active (running)");
|
|
146
|
+
// Check config
|
|
147
|
+
if (existsSync("/etc/falco/falco.yaml")) {
|
|
148
|
+
info.configExists = true;
|
|
149
|
+
}
|
|
150
|
+
// Check custom rules
|
|
151
|
+
if (existsSync("/etc/falco/rules.d")) {
|
|
152
|
+
const ls = await executeCommand({ command: "ls", args: ["/etc/falco/rules.d"], timeout: 5000 });
|
|
153
|
+
info.customRules = ls.stdout.trim().split("\n").filter(Boolean);
|
|
154
|
+
}
|
|
155
|
+
return { content: [formatToolOutput(info)] };
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
return { content: [createErrorContent(`Falco check failed: ${err instanceof Error ? err.message : String(err)}`)], isError: true };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// ── deploy_rules ────────────────────────────────────────────
|
|
162
|
+
case "deploy_rules": {
|
|
163
|
+
const { ruleName, ruleContent, dryRun } = params;
|
|
164
|
+
try {
|
|
165
|
+
if (!ruleName) {
|
|
166
|
+
return { content: [createErrorContent("ruleName is required for deploy_rules action")], isError: true };
|
|
167
|
+
}
|
|
168
|
+
if (!ruleContent) {
|
|
169
|
+
return { content: [createErrorContent("ruleContent is required for deploy_rules action")], isError: true };
|
|
170
|
+
}
|
|
171
|
+
const safety = await SafeguardRegistry.getInstance().checkSafety("deploy_falco_rules", { ruleName });
|
|
172
|
+
const rulesDir = "/etc/falco/rules.d";
|
|
173
|
+
const rulePath = `${rulesDir}/${ruleName}.yaml`;
|
|
174
|
+
if (dryRun) {
|
|
175
|
+
return {
|
|
176
|
+
content: [formatToolOutput({
|
|
177
|
+
dryRun: true,
|
|
178
|
+
rulePath,
|
|
179
|
+
ruleContent,
|
|
180
|
+
warnings: safety.warnings,
|
|
181
|
+
restartCommand: "systemctl restart falco",
|
|
182
|
+
})],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (!existsSync(rulesDir)) {
|
|
186
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
187
|
+
}
|
|
188
|
+
// TOOL-010: Use secure-fs instead of direct writeFileSync for audit trail
|
|
189
|
+
secureWriteFileSync(rulePath, ruleContent, "utf-8");
|
|
190
|
+
// Validate rules
|
|
191
|
+
const validate = await executeCommand({
|
|
192
|
+
command: "falco",
|
|
193
|
+
args: ["--validate", rulePath],
|
|
194
|
+
timeout: 15000,
|
|
195
|
+
});
|
|
196
|
+
const entry = createChangeEntry({
|
|
197
|
+
tool: "ebpf_falco",
|
|
198
|
+
action: `Deploy Falco rule ${ruleName}`,
|
|
199
|
+
target: rulePath,
|
|
200
|
+
dryRun: false,
|
|
201
|
+
success: validate.exitCode === 0,
|
|
202
|
+
rollbackCommand: `rm ${rulePath}`,
|
|
203
|
+
});
|
|
204
|
+
logChange(entry);
|
|
205
|
+
return {
|
|
206
|
+
content: [formatToolOutput({
|
|
207
|
+
success: validate.exitCode === 0,
|
|
208
|
+
rulePath,
|
|
209
|
+
validated: validate.exitCode === 0,
|
|
210
|
+
validationOutput: validate.stdout || validate.stderr,
|
|
211
|
+
nextStep: "systemctl restart falco",
|
|
212
|
+
})],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
return { content: [createErrorContent(`Falco rule deployment failed: ${err instanceof Error ? err.message : String(err)}`)], isError: true };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// ── events ──────────────────────────────────────────────────
|
|
220
|
+
case "events": {
|
|
221
|
+
const { lines, priority } = params;
|
|
222
|
+
try {
|
|
223
|
+
const logPaths = [
|
|
224
|
+
"/var/log/falco/falco_events.json",
|
|
225
|
+
"/var/log/falco/events.json",
|
|
226
|
+
"/var/log/falco.log",
|
|
227
|
+
];
|
|
228
|
+
let logPath = null;
|
|
229
|
+
for (const p of logPaths) {
|
|
230
|
+
if (existsSync(p)) {
|
|
231
|
+
logPath = p;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!logPath) {
|
|
236
|
+
// Try journalctl
|
|
237
|
+
const journalResult = await executeCommand({
|
|
238
|
+
command: "journalctl",
|
|
239
|
+
args: ["-u", "falco", "--no-pager", "-n", String(lines), "-o", "json"],
|
|
240
|
+
timeout: 10000,
|
|
241
|
+
});
|
|
242
|
+
if (journalResult.exitCode === 0) {
|
|
243
|
+
const events = journalResult.stdout.trim().split("\n").filter(Boolean).map((l) => {
|
|
244
|
+
try {
|
|
245
|
+
return JSON.parse(l);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
return { raw: l };
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
return { content: [formatToolOutput({ source: "journalctl", events: events.slice(-lines) })] };
|
|
252
|
+
}
|
|
253
|
+
return { content: [createErrorContent("No Falco log file found and journalctl query failed")], isError: true };
|
|
254
|
+
}
|
|
255
|
+
const result = await executeCommand({
|
|
256
|
+
command: "tail",
|
|
257
|
+
args: ["-n", String(lines), logPath],
|
|
258
|
+
timeout: 10000,
|
|
259
|
+
});
|
|
260
|
+
const events = result.stdout.trim().split("\n").filter(Boolean).map((l) => {
|
|
261
|
+
try {
|
|
262
|
+
return JSON.parse(l);
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return { raw: l };
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
let filtered = events;
|
|
269
|
+
if (priority) {
|
|
270
|
+
const priorities = ["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"];
|
|
271
|
+
const minIdx = priorities.indexOf(priority);
|
|
272
|
+
filtered = events.filter((e) => {
|
|
273
|
+
const p = typeof e.priority === "string" ? e.priority.toLowerCase() : "";
|
|
274
|
+
const idx = priorities.indexOf(p);
|
|
275
|
+
return idx >= 0 && idx <= minIdx;
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
content: [formatToolOutput({
|
|
280
|
+
source: logPath,
|
|
281
|
+
totalEvents: filtered.length,
|
|
282
|
+
events: filtered,
|
|
283
|
+
})],
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
return { content: [createErrorContent(`eBPF events retrieval failed: ${err instanceof Error ? err.message : String(err)}`)], isError: true };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
default:
|
|
291
|
+
return { content: [createErrorContent(`Unknown action: ${action}`)], isError: true };
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encryption and cryptography tools for Kali Defense MCP Server.
|
|
3
|
+
*
|
|
4
|
+
* Registers 4 tools: crypto_tls (actions: remote_audit, cert_expiry, config_audit),
|
|
5
|
+
* crypto_gpg_keys, crypto_luks_manage, crypto_file_hash.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
export declare function registerEncryptionTools(server: McpServer): void;
|
|
9
|
+
//# sourceMappingURL=encryption.d.ts.map
|