defense-mcp-server 0.9.3 → 0.9.5
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/build/index.js +98 -76
- package/package.json +2 -2
- package/build/core/auto-installer.d.ts +0 -102
- package/build/core/auto-installer.d.ts.map +0 -1
- package/build/core/backup-manager.d.ts +0 -63
- package/build/core/backup-manager.d.ts.map +0 -1
- package/build/core/changelog.d.ts +0 -119
- package/build/core/changelog.d.ts.map +0 -1
- package/build/core/command-allowlist.d.ts +0 -129
- package/build/core/command-allowlist.d.ts.map +0 -1
- package/build/core/config.d.ts +0 -107
- package/build/core/config.d.ts.map +0 -1
- package/build/core/dependency-validator.d.ts +0 -106
- package/build/core/dependency-validator.d.ts.map +0 -1
- package/build/core/distro-adapter.d.ts +0 -172
- package/build/core/distro-adapter.d.ts.map +0 -1
- package/build/core/distro.d.ts +0 -57
- package/build/core/distro.d.ts.map +0 -1
- package/build/core/encrypted-state.d.ts +0 -69
- package/build/core/encrypted-state.d.ts.map +0 -1
- package/build/core/executor.d.ts +0 -65
- package/build/core/executor.d.ts.map +0 -1
- package/build/core/installer.d.ts +0 -129
- package/build/core/installer.d.ts.map +0 -1
- package/build/core/logger.d.ts +0 -118
- package/build/core/logger.d.ts.map +0 -1
- package/build/core/metrics.d.ts +0 -74
- package/build/core/metrics.d.ts.map +0 -1
- package/build/core/metrics.js +0 -97
- package/build/core/output-redactor.d.ts +0 -26
- package/build/core/output-redactor.d.ts.map +0 -1
- package/build/core/pam-utils.d.ts +0 -356
- package/build/core/pam-utils.d.ts.map +0 -1
- package/build/core/parsers.d.ts +0 -191
- package/build/core/parsers.d.ts.map +0 -1
- package/build/core/policy-engine.d.ts +0 -170
- package/build/core/policy-engine.d.ts.map +0 -1
- package/build/core/preflight.d.ts +0 -157
- package/build/core/preflight.d.ts.map +0 -1
- package/build/core/privilege-manager.d.ts +0 -108
- package/build/core/privilege-manager.d.ts.map +0 -1
- package/build/core/progress.d.ts +0 -99
- package/build/core/progress.d.ts.map +0 -1
- package/build/core/rate-limiter.d.ts +0 -101
- package/build/core/rate-limiter.d.ts.map +0 -1
- package/build/core/rollback.d.ts +0 -73
- package/build/core/rollback.d.ts.map +0 -1
- package/build/core/run-command.d.ts +0 -14
- package/build/core/run-command.d.ts.map +0 -1
- package/build/core/safeguards.d.ts +0 -58
- package/build/core/safeguards.d.ts.map +0 -1
- package/build/core/sanitizer.d.ts +0 -118
- package/build/core/sanitizer.d.ts.map +0 -1
- package/build/core/secure-fs.d.ts +0 -67
- package/build/core/secure-fs.d.ts.map +0 -1
- package/build/core/spawn-safe.d.ts +0 -55
- package/build/core/spawn-safe.d.ts.map +0 -1
- package/build/core/sudo-guard.d.ts +0 -167
- package/build/core/sudo-guard.d.ts.map +0 -1
- package/build/core/sudo-session.d.ts +0 -143
- package/build/core/sudo-session.d.ts.map +0 -1
- package/build/core/third-party-installer.d.ts +0 -58
- package/build/core/third-party-installer.d.ts.map +0 -1
- package/build/core/third-party-manifest.d.ts +0 -48
- package/build/core/third-party-manifest.d.ts.map +0 -1
- package/build/core/tool-annotations.d.ts +0 -13
- package/build/core/tool-annotations.d.ts.map +0 -1
- package/build/core/tool-dependencies.d.ts +0 -60
- package/build/core/tool-dependencies.d.ts.map +0 -1
- package/build/core/tool-durations.d.ts +0 -71
- package/build/core/tool-durations.d.ts.map +0 -1
- package/build/core/tool-registry.d.ts +0 -112
- package/build/core/tool-registry.d.ts.map +0 -1
- package/build/core/tool-wrapper.d.ts +0 -73
- package/build/core/tool-wrapper.d.ts.map +0 -1
- package/build/index.d.ts +0 -3
- package/build/index.d.ts.map +0 -1
- package/build/tools/access-control.d.ts +0 -11
- package/build/tools/access-control.d.ts.map +0 -1
- package/build/tools/api-security.d.ts +0 -12
- package/build/tools/api-security.d.ts.map +0 -1
- package/build/tools/app-hardening.d.ts +0 -11
- package/build/tools/app-hardening.d.ts.map +0 -1
- package/build/tools/backup.d.ts +0 -8
- package/build/tools/backup.d.ts.map +0 -1
- package/build/tools/cloud-security.d.ts +0 -17
- package/build/tools/cloud-security.d.ts.map +0 -1
- package/build/tools/compliance.d.ts +0 -11
- package/build/tools/compliance.d.ts.map +0 -1
- package/build/tools/container-security.d.ts +0 -14
- package/build/tools/container-security.d.ts.map +0 -1
- package/build/tools/deception.d.ts +0 -13
- package/build/tools/deception.d.ts.map +0 -1
- package/build/tools/dns-security.d.ts +0 -93
- package/build/tools/dns-security.d.ts.map +0 -1
- package/build/tools/ebpf-security.d.ts +0 -15
- package/build/tools/ebpf-security.d.ts.map +0 -1
- package/build/tools/encryption.d.ts +0 -12
- package/build/tools/encryption.d.ts.map +0 -1
- package/build/tools/firewall.d.ts +0 -9
- package/build/tools/firewall.d.ts.map +0 -1
- package/build/tools/hardening.d.ts +0 -8
- package/build/tools/hardening.d.ts.map +0 -1
- package/build/tools/incident-response.d.ts +0 -11
- package/build/tools/incident-response.d.ts.map +0 -1
- package/build/tools/integrity.d.ts +0 -15
- package/build/tools/integrity.d.ts.map +0 -1
- package/build/tools/logging.d.ts +0 -21
- package/build/tools/logging.d.ts.map +0 -1
- package/build/tools/malware.d.ts +0 -10
- package/build/tools/malware.d.ts.map +0 -1
- package/build/tools/meta.d.ts +0 -13
- package/build/tools/meta.d.ts.map +0 -1
- package/build/tools/network-defense.d.ts +0 -11
- package/build/tools/network-defense.d.ts.map +0 -1
- package/build/tools/patch-management.d.ts +0 -3
- package/build/tools/patch-management.d.ts.map +0 -1
- package/build/tools/process-security.d.ts +0 -12
- package/build/tools/process-security.d.ts.map +0 -1
- package/build/tools/secrets.d.ts +0 -8
- package/build/tools/secrets.d.ts.map +0 -1
- package/build/tools/sudo-management.d.ts +0 -17
- package/build/tools/sudo-management.d.ts.map +0 -1
- package/build/tools/supply-chain-security.d.ts +0 -8
- package/build/tools/supply-chain-security.d.ts.map +0 -1
- package/build/tools/threat-intel.d.ts +0 -22
- package/build/tools/threat-intel.d.ts.map +0 -1
- package/build/tools/vulnerability-management.d.ts +0 -11
- package/build/tools/vulnerability-management.d.ts.map +0 -1
- package/build/tools/waf.d.ts +0 -12
- package/build/tools/waf.d.ts.map +0 -1
- package/build/tools/wireless-security.d.ts +0 -19
- package/build/tools/wireless-security.d.ts.map +0 -1
- package/build/tools/zero-trust-network.d.ts +0 -8
- package/build/tools/zero-trust-network.d.ts.map +0 -1
package/build/index.js
CHANGED
|
@@ -104,10 +104,6 @@ process.on("unhandledRejection", (reason) => {
|
|
|
104
104
|
});
|
|
105
105
|
// ── Main entry point ─────────────────────────────────────────────────────────
|
|
106
106
|
async function main() {
|
|
107
|
-
const server = new McpServer({
|
|
108
|
-
name: "defense-mcp-server",
|
|
109
|
-
version: VERSION,
|
|
110
|
-
});
|
|
111
107
|
// ── Phase 1: Dependency Validation & Auto-Install ────────────────────────
|
|
112
108
|
//
|
|
113
109
|
// Before registering tools, validate that all required system binaries
|
|
@@ -216,66 +212,79 @@ async function main() {
|
|
|
216
212
|
catch {
|
|
217
213
|
// Non-fatal — third-party check failure must not block startup
|
|
218
214
|
}
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
function
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
215
|
+
// ── Phase 2: Tool registration ────────────────────────────────────────────
|
|
216
|
+
//
|
|
217
|
+
// registerAllTools() creates a fresh McpServer, wraps it with pre-flight
|
|
218
|
+
// middleware, and registers every tool module. For stdio we call it once;
|
|
219
|
+
// for HTTP we call it per session so each client gets its own instance
|
|
220
|
+
// (the MCP SDK only allows one transport per McpServer).
|
|
221
|
+
function registerAllTools() {
|
|
222
|
+
const srv = new McpServer({
|
|
223
|
+
name: "defense-mcp-server",
|
|
224
|
+
version: VERSION,
|
|
225
|
+
});
|
|
226
|
+
const wrapped = createPreflightServer(srv);
|
|
227
|
+
let registered = 0;
|
|
228
|
+
let failed = 0;
|
|
229
|
+
const failedModules = [];
|
|
230
|
+
function safeRegister(name, fn) {
|
|
231
|
+
try {
|
|
232
|
+
fn(wrapped);
|
|
233
|
+
registered++;
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
failed++;
|
|
237
|
+
failedModules.push(name);
|
|
238
|
+
console.error(`[startup] ⚠ Failed to register ${name} tools: ${err instanceof Error ? err.message : err}`);
|
|
239
|
+
}
|
|
234
240
|
}
|
|
241
|
+
// Sudo privilege management (must be registered first — prerequisite for other tools)
|
|
242
|
+
safeRegister("sudo-management", registerSudoManagementTools);
|
|
243
|
+
// Original tool modules
|
|
244
|
+
safeRegister("firewall", registerFirewallTools);
|
|
245
|
+
safeRegister("hardening", registerHardeningTools);
|
|
246
|
+
safeRegister("integrity", registerIntegrityTools);
|
|
247
|
+
safeRegister("logging", registerLoggingTools);
|
|
248
|
+
safeRegister("network-defense", registerNetworkDefenseTools);
|
|
249
|
+
safeRegister("compliance", registerComplianceTools);
|
|
250
|
+
safeRegister("malware", registerMalwareTools);
|
|
251
|
+
safeRegister("backup", registerBackupTools);
|
|
252
|
+
safeRegister("access-control", registerAccessControlTools);
|
|
253
|
+
safeRegister("encryption", registerEncryptionTools);
|
|
254
|
+
safeRegister("container-security", registerContainerSecurityTools);
|
|
255
|
+
safeRegister("meta", registerMetaTools);
|
|
256
|
+
safeRegister("patch-management", registerPatchManagementTools);
|
|
257
|
+
safeRegister("secrets", registerSecretsTools);
|
|
258
|
+
safeRegister("incident-response", registerIncidentResponseTools);
|
|
259
|
+
// New tool modules
|
|
260
|
+
safeRegister("supply-chain-security", registerSupplyChainSecurityTools);
|
|
261
|
+
safeRegister("zero-trust-network", registerZeroTrustNetworkTools);
|
|
262
|
+
safeRegister("ebpf-security", registerEbpfSecurityTools);
|
|
263
|
+
safeRegister("app-hardening", registerAppHardeningTools);
|
|
264
|
+
// v0.6.0 tool modules
|
|
265
|
+
safeRegister("api-security", registerApiSecurityTools);
|
|
266
|
+
safeRegister("cloud-security", registerCloudSecurityTools);
|
|
267
|
+
safeRegister("deception", registerDeceptionTools);
|
|
268
|
+
safeRegister("dns-security", registerDnsSecurityTools);
|
|
269
|
+
safeRegister("process-security", registerProcessSecurityTools);
|
|
270
|
+
safeRegister("threat-intel", registerThreatIntelTools);
|
|
271
|
+
safeRegister("vulnerability-management", registerVulnerabilityManagementTools);
|
|
272
|
+
safeRegister("waf", registerWafTools);
|
|
273
|
+
safeRegister("wireless-security", registerWirelessSecurityTools);
|
|
274
|
+
return { server: srv, registered, failed, failedModules };
|
|
235
275
|
}
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
safeRegister("firewall", registerFirewallTools);
|
|
240
|
-
safeRegister("hardening", registerHardeningTools);
|
|
241
|
-
safeRegister("integrity", registerIntegrityTools);
|
|
242
|
-
safeRegister("logging", registerLoggingTools);
|
|
243
|
-
safeRegister("network-defense", registerNetworkDefenseTools);
|
|
244
|
-
safeRegister("compliance", registerComplianceTools);
|
|
245
|
-
safeRegister("malware", registerMalwareTools);
|
|
246
|
-
safeRegister("backup", registerBackupTools);
|
|
247
|
-
safeRegister("access-control", registerAccessControlTools);
|
|
248
|
-
safeRegister("encryption", registerEncryptionTools);
|
|
249
|
-
safeRegister("container-security", registerContainerSecurityTools);
|
|
250
|
-
safeRegister("meta", registerMetaTools);
|
|
251
|
-
safeRegister("patch-management", registerPatchManagementTools);
|
|
252
|
-
safeRegister("secrets", registerSecretsTools);
|
|
253
|
-
safeRegister("incident-response", registerIncidentResponseTools);
|
|
254
|
-
// New tool modules
|
|
255
|
-
safeRegister("supply-chain-security", registerSupplyChainSecurityTools);
|
|
256
|
-
safeRegister("zero-trust-network", registerZeroTrustNetworkTools);
|
|
257
|
-
safeRegister("ebpf-security", registerEbpfSecurityTools);
|
|
258
|
-
safeRegister("app-hardening", registerAppHardeningTools);
|
|
259
|
-
// v0.6.0 tool modules
|
|
260
|
-
safeRegister("api-security", registerApiSecurityTools);
|
|
261
|
-
safeRegister("cloud-security", registerCloudSecurityTools);
|
|
262
|
-
safeRegister("deception", registerDeceptionTools);
|
|
263
|
-
safeRegister("dns-security", registerDnsSecurityTools);
|
|
264
|
-
safeRegister("process-security", registerProcessSecurityTools);
|
|
265
|
-
safeRegister("threat-intel", registerThreatIntelTools);
|
|
266
|
-
safeRegister("vulnerability-management", registerVulnerabilityManagementTools);
|
|
267
|
-
safeRegister("waf", registerWafTools);
|
|
268
|
-
safeRegister("wireless-security", registerWirelessSecurityTools);
|
|
269
|
-
// Fail hard if no modules loaded at all
|
|
270
|
-
if (registered === 0) {
|
|
276
|
+
// Validate that tool registration works (fail-fast on startup)
|
|
277
|
+
const initial = registerAllTools();
|
|
278
|
+
if (initial.registered === 0) {
|
|
271
279
|
throw new Error("No tool modules loaded — server cannot start");
|
|
272
280
|
}
|
|
273
281
|
// ── Phase 3: Connect transport ───────────────────────────────────────────
|
|
274
282
|
const transportMode = process.env.MCP_TRANSPORT ?? "stdio";
|
|
275
|
-
const registrationMsg = `Registered ${registered} tool modules with consolidated defensive security tools` +
|
|
276
|
-
`${failed > 0 ? ` (${failed} failed: ${failedModules.join(", ")})` : ""}`;
|
|
283
|
+
const registrationMsg = `Registered ${initial.registered} tool modules with consolidated defensive security tools` +
|
|
284
|
+
`${initial.failed > 0 ? ` (${initial.failed} failed: ${initial.failedModules.join(", ")})` : ""}`;
|
|
277
285
|
if (transportMode === "http") {
|
|
278
286
|
const port = parseInt(process.env.MCP_PORT ?? "3100", 10);
|
|
287
|
+
const apiKey = process.env.MCP_API_KEY ?? "";
|
|
279
288
|
// Server card for Smithery discovery
|
|
280
289
|
const serverCard = {
|
|
281
290
|
serverInfo: {
|
|
@@ -283,7 +292,7 @@ async function main() {
|
|
|
283
292
|
version: VERSION,
|
|
284
293
|
description: "31 defensive security tools (250+ actions) for Linux system hardening, compliance, and threat detection",
|
|
285
294
|
},
|
|
286
|
-
authentication: { required:
|
|
295
|
+
authentication: { required: !!apiKey },
|
|
287
296
|
tools: [
|
|
288
297
|
{ name: "access_control", description: "Access control: SSH, PAM, sudo, user audit, password policy, shell restriction" },
|
|
289
298
|
{ name: "api_security", description: "API security: local API discovery, auth audit, rate limiting, TLS verify, CORS check" },
|
|
@@ -320,63 +329,74 @@ async function main() {
|
|
|
320
329
|
resources: [],
|
|
321
330
|
prompts: [],
|
|
322
331
|
};
|
|
323
|
-
// Track active
|
|
332
|
+
// Track active sessions: each gets its own McpServer + transport pair
|
|
324
333
|
const sessions = new Map();
|
|
325
334
|
const httpServer = createServer(async (req, res) => {
|
|
326
335
|
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
327
336
|
const pathname = url.pathname;
|
|
328
|
-
// CORS
|
|
337
|
+
// CORS headers
|
|
329
338
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
330
339
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
331
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, mcp-session-id, smithery-*");
|
|
340
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, mcp-session-id, smithery-*");
|
|
332
341
|
res.setHeader("Access-Control-Expose-Headers", "mcp-session-id");
|
|
333
342
|
if (req.method === "OPTIONS") {
|
|
334
343
|
res.writeHead(204).end();
|
|
335
344
|
return;
|
|
336
345
|
}
|
|
337
|
-
// Health check
|
|
346
|
+
// Health check (unauthenticated)
|
|
338
347
|
if (req.method === "GET" && pathname === "/health") {
|
|
339
348
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
340
|
-
res.end(JSON.stringify({ status: "ok", version: VERSION, tools: registered }));
|
|
349
|
+
res.end(JSON.stringify({ status: "ok", version: VERSION, tools: initial.registered }));
|
|
341
350
|
return;
|
|
342
351
|
}
|
|
343
|
-
// Smithery server card
|
|
352
|
+
// Smithery server card (unauthenticated)
|
|
344
353
|
if (req.method === "GET" && pathname === "/.well-known/mcp/server-card.json") {
|
|
345
354
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
346
355
|
res.end(JSON.stringify(serverCard));
|
|
347
356
|
return;
|
|
348
357
|
}
|
|
358
|
+
// API key authentication (when MCP_API_KEY is set)
|
|
359
|
+
if (apiKey) {
|
|
360
|
+
const authHeader = req.headers["authorization"] ?? "";
|
|
361
|
+
const provided = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
362
|
+
if (provided !== apiKey) {
|
|
363
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
364
|
+
res.end(JSON.stringify({ error: "Unauthorized", message: "Valid Bearer token required" }));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
349
368
|
// MCP Streamable HTTP endpoint
|
|
350
369
|
if (pathname === "/mcp" || pathname === "/") {
|
|
351
370
|
const sessionId = req.headers["mcp-session-id"];
|
|
352
371
|
if (req.method === "GET" || req.method === "POST") {
|
|
353
|
-
// Reuse existing session or create new
|
|
354
|
-
let
|
|
355
|
-
if (!
|
|
356
|
-
|
|
372
|
+
// Reuse existing session or create a new McpServer + transport pair
|
|
373
|
+
let session = sessionId ? sessions.get(sessionId) : undefined;
|
|
374
|
+
if (!session) {
|
|
375
|
+
const { server: sessionServer } = registerAllTools();
|
|
376
|
+
const transport = new StreamableHTTPServerTransport({
|
|
357
377
|
sessionIdGenerator: () => crypto.randomUUID(),
|
|
358
378
|
});
|
|
359
|
-
await
|
|
360
|
-
|
|
379
|
+
await sessionServer.connect(transport);
|
|
380
|
+
session = { server: sessionServer, transport };
|
|
361
381
|
transport.onclose = () => {
|
|
362
|
-
const sid = [...sessions.entries()].find(([,
|
|
382
|
+
const sid = [...sessions.entries()].find(([, s]) => s.transport === transport)?.[0];
|
|
363
383
|
if (sid)
|
|
364
384
|
sessions.delete(sid);
|
|
365
385
|
};
|
|
366
386
|
}
|
|
367
|
-
await transport.handleRequest(req, res);
|
|
387
|
+
await session.transport.handleRequest(req, res);
|
|
368
388
|
// Capture session ID from response headers if new session
|
|
369
389
|
if (!sessionId) {
|
|
370
390
|
const newSid = res.getHeader("mcp-session-id");
|
|
371
|
-
if (newSid &&
|
|
372
|
-
sessions.set(newSid,
|
|
391
|
+
if (newSid && session)
|
|
392
|
+
sessions.set(newSid, session);
|
|
373
393
|
}
|
|
374
394
|
return;
|
|
375
395
|
}
|
|
376
396
|
if (req.method === "DELETE") {
|
|
377
397
|
if (sessionId && sessions.has(sessionId)) {
|
|
378
|
-
const
|
|
379
|
-
await transport.handleRequest(req, res);
|
|
398
|
+
const session = sessions.get(sessionId);
|
|
399
|
+
await session.transport.handleRequest(req, res);
|
|
380
400
|
sessions.delete(sessionId);
|
|
381
401
|
}
|
|
382
402
|
else {
|
|
@@ -390,14 +410,16 @@ async function main() {
|
|
|
390
410
|
httpServer.listen(port, () => {
|
|
391
411
|
console.error(`Defense MCP Server v${VERSION} running on HTTP port ${port}`);
|
|
392
412
|
console.error(registrationMsg);
|
|
413
|
+
console.error(`[startup] Authentication: ${apiKey ? "ENABLED (MCP_API_KEY)" : "DISABLED — set MCP_API_KEY to require Bearer auth"}`);
|
|
393
414
|
console.error("[startup] MCP endpoint: http://0.0.0.0:" + port + "/mcp");
|
|
394
415
|
console.error("[startup] Health check: http://0.0.0.0:" + port + "/health");
|
|
395
416
|
console.error("[startup] Server card: http://0.0.0.0:" + port + "/.well-known/mcp/server-card.json");
|
|
396
417
|
});
|
|
397
418
|
}
|
|
398
419
|
else {
|
|
420
|
+
// stdio mode: single server instance, single transport
|
|
399
421
|
const transport = new StdioServerTransport();
|
|
400
|
-
await server.connect(transport);
|
|
422
|
+
await initial.server.connect(transport);
|
|
401
423
|
console.error(`Defense MCP Server v${VERSION} running on stdio`);
|
|
402
424
|
console.error(registrationMsg);
|
|
403
425
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "defense-mcp-server",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "Defense MCP Server — 31 defensive security tools with 250+ actions for system hardening, compliance, and threat detection on Linux",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"defense-mcp-server": "build/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"build
|
|
11
|
+
"build/**/*.js",
|
|
12
12
|
"README.md",
|
|
13
13
|
"CHANGELOG.md",
|
|
14
14
|
"LICENSE",
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AutoInstaller — multi-package-manager automatic dependency resolver.
|
|
3
|
-
*
|
|
4
|
-
* Handles installation of missing dependencies across system package managers
|
|
5
|
-
* (apt, dnf, yum, pacman, apk, zypper, brew), pip, and npm. This module is
|
|
6
|
-
* part of the pre-flight validation pipeline and is invoked when
|
|
7
|
-
* `DEFENSE_MCP_AUTO_INSTALL=true`.
|
|
8
|
-
*
|
|
9
|
-
* Design constraints:
|
|
10
|
-
* - Uses `execFileSafe` from `spawn-safe.ts` (NOT the executor) to avoid
|
|
11
|
-
* circular dependencies with `sudo-session`. spawn-safe enforces the
|
|
12
|
-
* command allowlist and `shell: false` automatically.
|
|
13
|
-
* - Every `execFileSafe` call is wrapped in try/catch — install failures
|
|
14
|
-
* must NEVER crash the server.
|
|
15
|
-
* - Logs exclusively to stderr (`console.error`) because the MCP server
|
|
16
|
-
* uses stdio for JSON-RPC transport.
|
|
17
|
-
*
|
|
18
|
-
* @module auto-installer
|
|
19
|
-
*/
|
|
20
|
-
import type { ToolManifest } from "./tool-registry.js";
|
|
21
|
-
export interface InstallAttempt {
|
|
22
|
-
dependency: string;
|
|
23
|
-
type: "binary" | "python-module" | "npm-package" | "library" | "file";
|
|
24
|
-
method: "system-package" | "pip" | "npm" | "cargo" | "go-install" | "binary-download" | "build-from-source" | "vendored" | "skipped";
|
|
25
|
-
success: boolean;
|
|
26
|
-
message: string;
|
|
27
|
-
duration?: number;
|
|
28
|
-
}
|
|
29
|
-
export interface AutoInstallResult {
|
|
30
|
-
attempted: InstallAttempt[];
|
|
31
|
-
allResolved: boolean;
|
|
32
|
-
unresolvedDependencies: string[];
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Validate that a package name contains only safe characters.
|
|
36
|
-
* Allowed: alphanumeric, hyphens, dots, plus signs, colons (for arch qualifiers).
|
|
37
|
-
* No shell metacharacters, no path separators, no spaces.
|
|
38
|
-
* Max length: 128 characters.
|
|
39
|
-
*/
|
|
40
|
-
export declare function validatePackageName(name: string): boolean;
|
|
41
|
-
export declare class AutoInstaller {
|
|
42
|
-
private static _instance;
|
|
43
|
-
private distroCache;
|
|
44
|
-
/** Get or create the singleton instance. */
|
|
45
|
-
static instance(): AutoInstaller;
|
|
46
|
-
/**
|
|
47
|
-
* Reset the singleton (for testing).
|
|
48
|
-
* @internal
|
|
49
|
-
*/
|
|
50
|
-
static resetInstance(): void;
|
|
51
|
-
/** Check if auto-install is enabled via config. */
|
|
52
|
-
isEnabled(): boolean;
|
|
53
|
-
/**
|
|
54
|
-
* Resolve all missing dependencies for a tool manifest.
|
|
55
|
-
*
|
|
56
|
-
* If auto-install is disabled, returns all dependencies as unresolved
|
|
57
|
-
* with method `'skipped'`.
|
|
58
|
-
*/
|
|
59
|
-
resolveAll(manifest: ToolManifest, missingBinaries: string[], missingPython?: string[], missingNpm?: string[], missingLibraries?: string[]): Promise<AutoInstallResult>;
|
|
60
|
-
/**
|
|
61
|
-
* Install a system binary via the detected package manager.
|
|
62
|
-
*
|
|
63
|
-
* 1. Look up binary in DEFENSIVE_TOOLS for distro-specific package name
|
|
64
|
-
* 2. If not found, try binary name directly as package name
|
|
65
|
-
* 3. Verify with `which <binary>` after install
|
|
66
|
-
*/
|
|
67
|
-
installBinary(binary: string): Promise<InstallAttempt>;
|
|
68
|
-
/**
|
|
69
|
-
* Install a Python module via pip.
|
|
70
|
-
*
|
|
71
|
-
* 1. Check if pip3 or pip exists
|
|
72
|
-
* 2. Try user-site install first (no sudo)
|
|
73
|
-
* 3. If that fails, try with sudo
|
|
74
|
-
* 4. Verify with `python3 -c "import <module>"`
|
|
75
|
-
*/
|
|
76
|
-
installPythonModule(module: string): Promise<InstallAttempt>;
|
|
77
|
-
/**
|
|
78
|
-
* Install an npm package globally.
|
|
79
|
-
*
|
|
80
|
-
* 1. Check if npm exists
|
|
81
|
-
* 2. Run `npm install -g <package>` with sudo if needed
|
|
82
|
-
* 3. Verify by checking if the package provides an expected binary
|
|
83
|
-
*/
|
|
84
|
-
installNpmPackage(pkg: string): Promise<InstallAttempt>;
|
|
85
|
-
/**
|
|
86
|
-
* Install a system library (development headers).
|
|
87
|
-
*
|
|
88
|
-
* 1. Determine dev package name based on distro family
|
|
89
|
-
* 2. Try installing the first candidate that works
|
|
90
|
-
* 3. Verify with `ldconfig -p | grep <lib>` or `pkg-config --exists <lib>`
|
|
91
|
-
*/
|
|
92
|
-
installLibrary(lib: string): Promise<InstallAttempt>;
|
|
93
|
-
/**
|
|
94
|
-
* Get (and cache) the detected distro info.
|
|
95
|
-
*/
|
|
96
|
-
private getDistro;
|
|
97
|
-
/**
|
|
98
|
-
* Verify a library is available via ldconfig or pkg-config.
|
|
99
|
-
*/
|
|
100
|
-
private verifyLibrary;
|
|
101
|
-
}
|
|
102
|
-
//# sourceMappingURL=auto-installer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auto-installer.d.ts","sourceRoot":"","sources":["../../src/core/auto-installer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AASH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AASvD,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,QAAQ,GAAG,eAAe,GAAG,aAAa,GAAG,SAAS,GAAG,MAAM,CAAC;IACtE,MAAM,EACF,gBAAgB,GAChB,KAAK,GACL,KAAK,GACL,OAAO,GACP,YAAY,GACZ,iBAAiB,GACjB,mBAAmB,GACnB,UAAU,GACV,SAAS,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC;AA0FD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAoOD,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,SAAS,CAA8B;IACtD,OAAO,CAAC,WAAW,CAA2B;IAE9C,4CAA4C;IAC5C,MAAM,CAAC,QAAQ,IAAI,aAAa;IAahC;;;OAGG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAI5B,mDAAmD;IACnD,SAAS,IAAI,OAAO;IAIpB;;;;;OAKG;IACG,UAAU,CACd,QAAQ,EAAE,YAAY,EACtB,eAAe,EAAE,MAAM,EAAE,EACzB,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,UAAU,CAAC,EAAE,MAAM,EAAE,EACrB,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAC1B,OAAO,CAAC,iBAAiB,CAAC;IA6G7B;;;;;;OAMG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAsP5D;;;;;;;OAOG;IACG,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAgHlE;;;;;;OAMG;IACG,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA8G7D;;;;;;OAMG;IACG,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAwG1D;;OAEG;YACW,SAAS;IAOvB;;OAEG;IACH,OAAO,CAAC,aAAa;CAmBtB"}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BackupManager — manages file backups with manifest tracking.
|
|
3
|
-
*
|
|
4
|
-
* Backups are stored under ~/.defense-mcp/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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"backup-manager.d.ts","sourceRoot":"","sources":["../../src/core/backup-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH,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,CAiC1E;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;IAepB,8BAA8B;IAC9B,OAAO,CAAC,aAAa;IAKrB;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW;IAyCzC;;;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;IAyB9C;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAO3C;;OAEG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAiCtD"}
|
|
@@ -1,119 +0,0 @@
|
|
|
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
|
-
* SHA-256 hash-chain value.
|
|
35
|
-
*
|
|
36
|
-
* For the first entry: SHA-256 of the entry's core fields with previousHash="genesis".
|
|
37
|
-
* For subsequent entries: SHA-256 of the entry's core fields concatenated with
|
|
38
|
-
* the previous entry's hash. This creates a tamper-evident chain — modifying or
|
|
39
|
-
* deleting any entry breaks the chain, which is detectable via `verifyChangelog()`.
|
|
40
|
-
*
|
|
41
|
-
* Auto-populated by `logChange()`. Not present in legacy entries.
|
|
42
|
-
*/
|
|
43
|
-
hash?: string;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Versioned changelog state file format.
|
|
47
|
-
* Old files stored a bare array; new files use this envelope.
|
|
48
|
-
*/
|
|
49
|
-
export interface ChangelogState {
|
|
50
|
-
version: 1;
|
|
51
|
-
entries: ChangeEntry[];
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Compute the SHA-256 hash-chain value for a changelog entry.
|
|
55
|
-
*
|
|
56
|
-
* The hash is computed over the entry's immutable fields (id, timestamp, tool,
|
|
57
|
-
* action, target, dryRun, success) concatenated with the previous entry's hash.
|
|
58
|
-
* This creates a tamper-evident chain.
|
|
59
|
-
*
|
|
60
|
-
* @param entry - The entry to hash (hash field is excluded from the computation)
|
|
61
|
-
* @param previousHash - Hash of the previous entry, or "genesis" for the first entry
|
|
62
|
-
* @returns SHA-256 hex digest
|
|
63
|
-
*/
|
|
64
|
-
export declare function computeEntryHash(entry: ChangeEntry, previousHash: string): string;
|
|
65
|
-
/**
|
|
66
|
-
* Verify the integrity of the changelog hash chain.
|
|
67
|
-
*
|
|
68
|
-
* Walks all entries with `hash` fields and checks that each hash matches
|
|
69
|
-
* the recomputed value from the previous entry's hash. Legacy entries
|
|
70
|
-
* without `hash` fields are skipped.
|
|
71
|
-
*
|
|
72
|
-
* @returns Object with `valid` boolean and details of any broken links
|
|
73
|
-
*/
|
|
74
|
-
export declare function verifyChangelog(): {
|
|
75
|
-
valid: boolean;
|
|
76
|
-
totalEntries: number;
|
|
77
|
-
hashedEntries: number;
|
|
78
|
-
brokenLinks: Array<{
|
|
79
|
-
index: number;
|
|
80
|
-
entryId: string;
|
|
81
|
-
expected: string;
|
|
82
|
-
actual: string;
|
|
83
|
-
}>;
|
|
84
|
-
};
|
|
85
|
-
/**
|
|
86
|
-
* Creates a new ChangeEntry with auto-generated id and timestamp.
|
|
87
|
-
*/
|
|
88
|
-
export declare function createChangeEntry(partial: Omit<ChangeEntry, "id" | "timestamp" | "user">): ChangeEntry;
|
|
89
|
-
/**
|
|
90
|
-
* Appends a change entry to the changelog JSON file.
|
|
91
|
-
* Creates the file and parent directories if they don't exist.
|
|
92
|
-
* Rotates old entries when the file exceeds MAX_CHANGELOG_ENTRIES.
|
|
93
|
-
* Computes and attaches a hash-chain value for tamper evidence.
|
|
94
|
-
* Fails silently (logs to stderr) to avoid disrupting tool execution.
|
|
95
|
-
*/
|
|
96
|
-
export declare function logChange(entry: ChangeEntry): void;
|
|
97
|
-
/**
|
|
98
|
-
* Reads changelog entries, newest first.
|
|
99
|
-
* Returns empty array on any error.
|
|
100
|
-
*
|
|
101
|
-
* @param limit Maximum number of entries to return (default: all)
|
|
102
|
-
*/
|
|
103
|
-
export declare function getChangelog(limit?: number): ChangeEntry[];
|
|
104
|
-
/**
|
|
105
|
-
* Creates a backup copy of a file using the unified BackupManager.
|
|
106
|
-
* The backup is tracked in the manifest at ~/.defense-mcp/backups/manifest.json.
|
|
107
|
-
*
|
|
108
|
-
* @param filePath Absolute path to the file to back up
|
|
109
|
-
* @returns Path to the backup file
|
|
110
|
-
*/
|
|
111
|
-
export declare function backupFile(filePath: string): string;
|
|
112
|
-
/**
|
|
113
|
-
* Restores a file from a backup.
|
|
114
|
-
*
|
|
115
|
-
* @param backupPath Path to the backup file
|
|
116
|
-
* @param originalPath Path to restore the file to
|
|
117
|
-
*/
|
|
118
|
-
export declare function restoreFile(backupPath: string, originalPath: string): void;
|
|
119
|
-
//# sourceMappingURL=changelog.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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;IACnB;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAcjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,IAAI;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1F,CAuCA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,WAAW,GAAG,MAAM,CAAC,GACtD,WAAW,CAOb;AA2BD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAiClD;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"}
|