@mcp-guardian/server 0.4.0 → 0.5.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/README.md +105 -8
- package/dist/auth/auth-types.d.ts +40 -0
- package/dist/auth/auth-types.d.ts.map +1 -0
- package/dist/auth/auth-types.js +5 -0
- package/dist/auth/auth-types.js.map +1 -0
- package/dist/auth/dashboard-auth.d.ts +97 -0
- package/dist/auth/dashboard-auth.d.ts.map +1 -0
- package/dist/auth/dashboard-auth.js +319 -0
- package/dist/auth/dashboard-auth.js.map +1 -0
- package/dist/auth/dpop.d.ts +38 -0
- package/dist/auth/dpop.d.ts.map +1 -0
- package/dist/auth/dpop.js +72 -0
- package/dist/auth/dpop.js.map +1 -0
- package/dist/auth/oauth.d.ts +25 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +96 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/redis-session-cache.d.ts +21 -0
- package/dist/auth/redis-session-cache.d.ts.map +1 -0
- package/dist/auth/redis-session-cache.js +74 -0
- package/dist/auth/redis-session-cache.js.map +1 -0
- package/dist/auth/session-cache.d.ts +47 -0
- package/dist/auth/session-cache.d.ts.map +1 -0
- package/dist/auth/session-cache.js +91 -0
- package/dist/auth/session-cache.js.map +1 -0
- package/dist/cli.js +23 -5
- package/dist/cli.js.map +1 -1
- package/dist/database/database-interface.d.ts +17 -0
- package/dist/database/database-interface.d.ts.map +1 -0
- package/dist/database/database-interface.js +2 -0
- package/dist/database/database-interface.js.map +1 -0
- package/dist/database/postgres-db.d.ts +18 -0
- package/dist/database/postgres-db.d.ts.map +1 -0
- package/dist/database/postgres-db.js +118 -0
- package/dist/database/postgres-db.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/policy/policy-watcher.d.ts +24 -0
- package/dist/policy/policy-watcher.d.ts.map +1 -0
- package/dist/policy/policy-watcher.js +68 -0
- package/dist/policy/policy-watcher.js.map +1 -0
- package/dist/policy/shell-tokenizer.d.ts +92 -0
- package/dist/policy/shell-tokenizer.d.ts.map +1 -0
- package/dist/policy/shell-tokenizer.js +300 -0
- package/dist/policy/shell-tokenizer.js.map +1 -0
- package/dist/proxy/http-proxy-server.d.ts +26 -0
- package/dist/proxy/http-proxy-server.d.ts.map +1 -0
- package/dist/proxy/http-proxy-server.js +172 -0
- package/dist/proxy/http-proxy-server.js.map +1 -0
- package/dist/proxy/proxy-manager.d.ts +3 -1
- package/dist/proxy/proxy-manager.d.ts.map +1 -1
- package/dist/proxy/proxy-manager.js +10 -3
- package/dist/proxy/proxy-manager.js.map +1 -1
- package/dist/proxy/proxy-server.d.ts +15 -8
- package/dist/proxy/proxy-server.d.ts.map +1 -1
- package/dist/proxy/proxy-server.js +80 -26
- package/dist/proxy/proxy-server.js.map +1 -1
- package/dist/utils/circuit-breaker.d.ts +29 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +81 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/dashboard-server.d.ts +19 -0
- package/dist/utils/dashboard-server.d.ts.map +1 -0
- package/dist/utils/dashboard-server.js +258 -0
- package/dist/utils/dashboard-server.js.map +1 -0
- package/dist/utils/metrics.d.ts +17 -0
- package/dist/utils/metrics.d.ts.map +1 -0
- package/dist/utils/metrics.js +79 -0
- package/dist/utils/metrics.js.map +1 -0
- package/dist/utils/mtls-config.d.ts +27 -0
- package/dist/utils/mtls-config.d.ts.map +1 -0
- package/dist/utils/mtls-config.js +82 -0
- package/dist/utils/mtls-config.js.map +1 -0
- package/dist/utils/payload-normalizer.d.ts +62 -0
- package/dist/utils/payload-normalizer.d.ts.map +1 -0
- package/dist/utils/payload-normalizer.js +240 -0
- package/dist/utils/payload-normalizer.js.map +1 -0
- package/dist/utils/policy-auditor.d.ts +24 -0
- package/dist/utils/policy-auditor.d.ts.map +1 -0
- package/dist/utils/policy-auditor.js +58 -0
- package/dist/utils/policy-auditor.js.map +1 -0
- package/dist/utils/redis-rate-limiter.d.ts +22 -0
- package/dist/utils/redis-rate-limiter.d.ts.map +1 -0
- package/dist/utils/redis-rate-limiter.js +61 -0
- package/dist/utils/redis-rate-limiter.js.map +1 -0
- package/dist/utils/structured-logger.d.ts +1 -1
- package/dist/utils/structured-logger.d.ts.map +1 -1
- package/dist/utils/tracing.d.ts +7 -0
- package/dist/utils/tracing.d.ts.map +1 -0
- package/dist/utils/tracing.js +34 -0
- package/dist/utils/tracing.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Registry, Counter, Gauge, Histogram } from 'prom-client';
|
|
2
|
+
/**
|
|
3
|
+
* Prometheus metrics for MCP Guardian.
|
|
4
|
+
* Exposed at /metrics for scraping by Prometheus/Grafana.
|
|
5
|
+
*
|
|
6
|
+
* Enable with: METRICS_ENABLED=true METRICS_PORT=9090
|
|
7
|
+
*/
|
|
8
|
+
export declare const registry: Registry<"text/plain; version=0.0.4; charset=utf-8">;
|
|
9
|
+
export declare const requestsTotal: Counter<"server_name" | "decision" | "authn_success">;
|
|
10
|
+
export declare const blockedRequestsTotal: Counter<"rule" | "server_name" | "block_reason">;
|
|
11
|
+
export declare const authFailuresTotal: Counter<"reason" | "server_name">;
|
|
12
|
+
export declare const circuitBreakerState: Gauge<"server_name">;
|
|
13
|
+
export declare const activeSessions: Gauge<string>;
|
|
14
|
+
export declare const proxyLatencyMs: Histogram<"server_name">;
|
|
15
|
+
export declare const authLatencyMs: Histogram<"server_name">;
|
|
16
|
+
export declare function startMetricsServer(port?: number): Promise<Registry>;
|
|
17
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/utils/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAyB,MAAM,aAAa,CAAC;AAGzF;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,sDAAiB,CAAC;AAIvC,eAAO,MAAM,aAAa,uDAKxB,CAAC;AAEH,eAAO,MAAM,oBAAoB,kDAK/B,CAAC;AAEH,eAAO,MAAM,iBAAiB,mCAK5B,CAAC;AAGH,eAAO,MAAM,mBAAmB,sBAK9B,CAAC;AAEH,eAAO,MAAM,cAAc,eAIzB,CAAC;AAGH,eAAO,MAAM,cAAc,0BAMzB,CAAC;AAEH,eAAO,MAAM,aAAa,0BAMxB,CAAC;AAGH,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,MAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoB/E"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Registry, Counter, Gauge, Histogram, collectDefaultMetrics } from 'prom-client';
|
|
2
|
+
import { Logger } from './logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Prometheus metrics for MCP Guardian.
|
|
5
|
+
* Exposed at /metrics for scraping by Prometheus/Grafana.
|
|
6
|
+
*
|
|
7
|
+
* Enable with: METRICS_ENABLED=true METRICS_PORT=9090
|
|
8
|
+
*/
|
|
9
|
+
export const registry = new Registry();
|
|
10
|
+
collectDefaultMetrics({ register: registry, prefix: 'mcp_guardian_' });
|
|
11
|
+
// ── Counters ─────────────────────────────────────────────────────
|
|
12
|
+
export const requestsTotal = new Counter({
|
|
13
|
+
name: 'mcp_guardian_requests_total',
|
|
14
|
+
help: 'Total number of tools/call requests processed',
|
|
15
|
+
labelNames: ['server_name', 'decision', 'authn_success'],
|
|
16
|
+
registers: [registry],
|
|
17
|
+
});
|
|
18
|
+
export const blockedRequestsTotal = new Counter({
|
|
19
|
+
name: 'mcp_guardian_blocked_requests_total',
|
|
20
|
+
help: 'Total number of blocked tools/call requests',
|
|
21
|
+
labelNames: ['server_name', 'block_reason', 'rule'],
|
|
22
|
+
registers: [registry],
|
|
23
|
+
});
|
|
24
|
+
export const authFailuresTotal = new Counter({
|
|
25
|
+
name: 'mcp_guardian_auth_failures_total',
|
|
26
|
+
help: 'Total number of authentication failures',
|
|
27
|
+
labelNames: ['server_name', 'reason'],
|
|
28
|
+
registers: [registry],
|
|
29
|
+
});
|
|
30
|
+
// ── Gauges ────────────────────────────────────────────────────────
|
|
31
|
+
export const circuitBreakerState = new Gauge({
|
|
32
|
+
name: 'mcp_guardian_circuit_breaker_state',
|
|
33
|
+
help: 'Circuit breaker state: 0=CLOSED, 1=OPEN, 2=HALF_OPEN',
|
|
34
|
+
labelNames: ['server_name'],
|
|
35
|
+
registers: [registry],
|
|
36
|
+
});
|
|
37
|
+
export const activeSessions = new Gauge({
|
|
38
|
+
name: 'mcp_guardian_active_sessions',
|
|
39
|
+
help: 'Number of active session tokens',
|
|
40
|
+
registers: [registry],
|
|
41
|
+
});
|
|
42
|
+
// ── Histograms ────────────────────────────────────────────────────
|
|
43
|
+
export const proxyLatencyMs = new Histogram({
|
|
44
|
+
name: 'mcp_guardian_proxy_latency_ms',
|
|
45
|
+
help: 'Proxy processing latency in milliseconds',
|
|
46
|
+
labelNames: ['server_name'],
|
|
47
|
+
buckets: [1, 5, 10, 25, 50, 100, 250, 500, 1000],
|
|
48
|
+
registers: [registry],
|
|
49
|
+
});
|
|
50
|
+
export const authLatencyMs = new Histogram({
|
|
51
|
+
name: 'mcp_guardian_auth_latency_ms',
|
|
52
|
+
help: 'Authentication/JWT validation latency in milliseconds',
|
|
53
|
+
labelNames: ['server_name'],
|
|
54
|
+
buckets: [1, 5, 10, 25, 50, 100, 250, 500],
|
|
55
|
+
registers: [registry],
|
|
56
|
+
});
|
|
57
|
+
// ── Metrics server ────────────────────────────────────────────────
|
|
58
|
+
export async function startMetricsServer(port = 9090) {
|
|
59
|
+
if (process.env['METRICS_ENABLED'] !== 'true') {
|
|
60
|
+
Logger.debug('[metrics] Metrics server not enabled (set METRICS_ENABLED=true)');
|
|
61
|
+
return registry;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const { createServer } = await import('http');
|
|
65
|
+
const server = createServer(async (_req, res) => {
|
|
66
|
+
res.writeHead(200, { 'Content-Type': registry.contentType });
|
|
67
|
+
res.end(await registry.metrics());
|
|
68
|
+
});
|
|
69
|
+
server.listen(port, () => {
|
|
70
|
+
Logger.info(`[metrics] Prometheus metrics available at http://0.0.0.0:${port}/metrics`);
|
|
71
|
+
});
|
|
72
|
+
return registry;
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
Logger.error(`[metrics] Failed to start metrics server: ${err?.message}`);
|
|
76
|
+
return registry;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/utils/metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Agent as HttpsAgent } from 'https';
|
|
2
|
+
export interface MtlsConfig {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
ca?: Buffer;
|
|
5
|
+
cert?: Buffer;
|
|
6
|
+
key?: Buffer;
|
|
7
|
+
rejectUnauthorized: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Load mTLS configuration from environment variables.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadMtlsConfig(): MtlsConfig;
|
|
13
|
+
/**
|
|
14
|
+
* Create an HTTPS Agent configured with mTLS client certificate and CA.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createMtlsAgent(config: MtlsConfig): HttpsAgent | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* CLI flag names for mTLS configuration.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MTL_CLI_FLAGS: {
|
|
21
|
+
readonly tlsEnabled: "--mtls";
|
|
22
|
+
readonly tlsCa: "--mtls-ca <path>";
|
|
23
|
+
readonly tlsCert: "--mtls-cert <path>";
|
|
24
|
+
readonly tlsKey: "--mtls-key <path>";
|
|
25
|
+
readonly tlsInsecure: "--mtls-insecure";
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=mtls-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mtls-config.d.ts","sourceRoot":"","sources":["../../src/utils/mtls-config.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,OAAO,CAAC;AAG5C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAsC3C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAW1E;AAED;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mTLS Configuration for Zero-Trust Proxy ↔ Upstream Communication.
|
|
3
|
+
*
|
|
4
|
+
* When MCP_TLS_ENABLED=true, the HTTP/SSE proxy validates the upstream
|
|
5
|
+
* server's certificate AND presents a client certificate for mutual
|
|
6
|
+
* authentication. This prevents MITM attacks and ensures only authorized
|
|
7
|
+
* proxies can connect to upstream MCP servers.
|
|
8
|
+
*
|
|
9
|
+
* Configuration via environment variables:
|
|
10
|
+
* MCP_TLS_ENABLED=true|false
|
|
11
|
+
* MCP_TLS_CA=/path/to/ca-cert.pem (required — trusted CA bundle)
|
|
12
|
+
* MCP_TLS_CERT=/path/to/client-cert.pem (required — proxy's client cert)
|
|
13
|
+
* MCP_TLS_KEY=/path/to/client-key.pem (required — proxy's client key)
|
|
14
|
+
* MCP_TLS_REJECT_UNAUTHORIZED=true|false (default: true — strict mode)
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync } from 'fs';
|
|
17
|
+
import { Agent as HttpsAgent } from 'https';
|
|
18
|
+
import { Logger } from './logger.js';
|
|
19
|
+
/**
|
|
20
|
+
* Load mTLS configuration from environment variables.
|
|
21
|
+
*/
|
|
22
|
+
export function loadMtlsConfig() {
|
|
23
|
+
const enabled = process.env['MCP_TLS_ENABLED'] === 'true';
|
|
24
|
+
if (!enabled) {
|
|
25
|
+
return { enabled: false, rejectUnauthorized: true };
|
|
26
|
+
}
|
|
27
|
+
const caPath = process.env['MCP_TLS_CA'];
|
|
28
|
+
const certPath = process.env['MCP_TLS_CERT'];
|
|
29
|
+
const keyPath = process.env['MCP_TLS_KEY'];
|
|
30
|
+
const rejectUnauthorized = process.env['MCP_TLS_REJECT_UNAUTHORIZED'] !== 'false';
|
|
31
|
+
const missing = [];
|
|
32
|
+
if (!caPath)
|
|
33
|
+
missing.push('MCP_TLS_CA');
|
|
34
|
+
if (!certPath)
|
|
35
|
+
missing.push('MCP_TLS_CERT');
|
|
36
|
+
if (!keyPath)
|
|
37
|
+
missing.push('MCP_TLS_KEY');
|
|
38
|
+
if (missing.length > 0) {
|
|
39
|
+
Logger.error(`[mtls] mTLS enabled but missing env vars: ${missing.join(', ')}`);
|
|
40
|
+
throw new Error(`mTLS misconfigured: missing ${missing.join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
let ca;
|
|
43
|
+
let cert;
|
|
44
|
+
let key;
|
|
45
|
+
try {
|
|
46
|
+
ca = readFileSync(caPath);
|
|
47
|
+
cert = readFileSync(certPath);
|
|
48
|
+
key = readFileSync(keyPath);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
Logger.error(`[mtls] Failed to read TLS files: ${err?.message}`);
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
Logger.info(`[mtls] mTLS enabled (CA: ${caPath}, cert: ${certPath}, rejectUnauthorized: ${rejectUnauthorized})`);
|
|
55
|
+
return { enabled: true, ca, cert, key, rejectUnauthorized };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Create an HTTPS Agent configured with mTLS client certificate and CA.
|
|
59
|
+
*/
|
|
60
|
+
export function createMtlsAgent(config) {
|
|
61
|
+
if (!config.enabled)
|
|
62
|
+
return undefined;
|
|
63
|
+
return new HttpsAgent({
|
|
64
|
+
ca: config.ca,
|
|
65
|
+
cert: config.cert,
|
|
66
|
+
key: config.key,
|
|
67
|
+
rejectUnauthorized: config.rejectUnauthorized,
|
|
68
|
+
keepAlive: true,
|
|
69
|
+
keepAliveMsecs: 30000,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* CLI flag names for mTLS configuration.
|
|
74
|
+
*/
|
|
75
|
+
export const MTL_CLI_FLAGS = {
|
|
76
|
+
tlsEnabled: '--mtls',
|
|
77
|
+
tlsCa: '--mtls-ca <path>',
|
|
78
|
+
tlsCert: '--mtls-cert <path>',
|
|
79
|
+
tlsKey: '--mtls-key <path>',
|
|
80
|
+
tlsInsecure: '--mtls-insecure',
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=mtls-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mtls-config.js","sourceRoot":"","sources":["../../src/utils/mtls-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface NormalizationResult {
|
|
2
|
+
/** The fully normalized string ready for policy evaluation */
|
|
3
|
+
normalized: string;
|
|
4
|
+
/** Whether any normalization was applied */
|
|
5
|
+
wasModified: boolean;
|
|
6
|
+
/** What transformations were applied */
|
|
7
|
+
transformations: string[];
|
|
8
|
+
/** The original raw input */
|
|
9
|
+
original: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* PayloadNormalizer applies multi-stage normalization to defeat
|
|
13
|
+
* common evasion techniques targeting regex-based policy engines.
|
|
14
|
+
*/
|
|
15
|
+
export declare class PayloadNormalizer {
|
|
16
|
+
private readonly maxDepth;
|
|
17
|
+
private readonly maxLength;
|
|
18
|
+
constructor(maxDepth?: number, maxLength?: number);
|
|
19
|
+
/**
|
|
20
|
+
* Full normalization pipeline for policy evaluation input.
|
|
21
|
+
*/
|
|
22
|
+
normalize(input: string): NormalizationResult;
|
|
23
|
+
/**
|
|
24
|
+
* URL decode: %XX → character, handles malformed sequences.
|
|
25
|
+
*/
|
|
26
|
+
private urlDecode;
|
|
27
|
+
/**
|
|
28
|
+
* Decode hex escapes: \x41 → 'A', \x00 → null byte detection.
|
|
29
|
+
*/
|
|
30
|
+
private decodeHexEscapes;
|
|
31
|
+
/**
|
|
32
|
+
* Decode unicode escapes: \u0041 → 'A', \U00000041 → 'A'.
|
|
33
|
+
*/
|
|
34
|
+
private decodeUnicodeEscapes;
|
|
35
|
+
/**
|
|
36
|
+
* Decode HTML entities: < -> <, < -> <, < -> <.
|
|
37
|
+
* Entity map built at runtime to avoid source-level entity decoding issues.
|
|
38
|
+
*/
|
|
39
|
+
private static htmlEntityMap;
|
|
40
|
+
private static getHtmlEntityMap;
|
|
41
|
+
private decodeHtmlEntities;
|
|
42
|
+
/**
|
|
43
|
+
* Unwrap double escapes: \\. → literal character.
|
|
44
|
+
*/
|
|
45
|
+
private unwrapDoubleEscapes;
|
|
46
|
+
/**
|
|
47
|
+
* Shell normalize: collapse common shell obfuscation patterns.
|
|
48
|
+
*
|
|
49
|
+
* - $'cmd' → cmd (ANSI-C quoting)
|
|
50
|
+
* - "c"m"d" → cmd (quote splitting)
|
|
51
|
+
* - ''cmd'' → cmd (empty quote pairs)
|
|
52
|
+
* - c\md → cmd (backslash escapes)
|
|
53
|
+
*/
|
|
54
|
+
private shellNormalize;
|
|
55
|
+
/**
|
|
56
|
+
* Specifically normalize a JSON string value (tool argument).
|
|
57
|
+
* Handles nested JSON structures recursively.
|
|
58
|
+
*/
|
|
59
|
+
normalizeJsonValue(value: unknown, depth?: number): unknown;
|
|
60
|
+
}
|
|
61
|
+
export declare function getNormalizer(): PayloadNormalizer;
|
|
62
|
+
//# sourceMappingURL=payload-normalizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-normalizer.d.ts","sourceRoot":"","sources":["../../src/utils/payload-normalizer.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,WAAW,EAAE,OAAO,CAAC;IACrB,wCAAwC;IACxC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,QAAQ,SAAI,EAAE,SAAS,SAAY;IAK/C;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB;IAmE7C;;OAEG;IACH,OAAO,CAAC,SAAS;IAejB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IASxB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAoB5B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa,CAAwC;IAEpE,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAiC/B,OAAO,CAAC,kBAAkB;IAmB1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAQ3B;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;IAkBtB;;;OAGG;IACH,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO;CAqBvD;AAKD,wBAAgB,aAAa,IAAI,iBAAiB,CAKjD"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PayloadNormalizer applies multi-stage normalization to defeat
|
|
3
|
+
* common evasion techniques targeting regex-based policy engines.
|
|
4
|
+
*/
|
|
5
|
+
export class PayloadNormalizer {
|
|
6
|
+
maxDepth;
|
|
7
|
+
maxLength;
|
|
8
|
+
constructor(maxDepth = 5, maxLength = 1_000_000) {
|
|
9
|
+
this.maxDepth = maxDepth;
|
|
10
|
+
this.maxLength = maxLength;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Full normalization pipeline for policy evaluation input.
|
|
14
|
+
*/
|
|
15
|
+
normalize(input) {
|
|
16
|
+
const transformations = [];
|
|
17
|
+
let current = input;
|
|
18
|
+
let depth = 0;
|
|
19
|
+
// ── Step 0: Truncate oversized inputs (memory safety) ──
|
|
20
|
+
if (current.length > this.maxLength) {
|
|
21
|
+
current = current.slice(0, this.maxLength);
|
|
22
|
+
transformations.push('truncated');
|
|
23
|
+
}
|
|
24
|
+
// ── Step 1: Unicode normalization (NFKC) — collapses homoglyphs ──
|
|
25
|
+
const unicodeNormalized = current.normalize('NFKC');
|
|
26
|
+
if (unicodeNormalized !== current) {
|
|
27
|
+
transformations.push('unicode-nfkc');
|
|
28
|
+
current = unicodeNormalized;
|
|
29
|
+
}
|
|
30
|
+
// ── Step 2: Iterative decode loop (URL, hex, HTML entities) ──
|
|
31
|
+
while (depth < this.maxDepth) {
|
|
32
|
+
const before = current;
|
|
33
|
+
// URL decode (handles %20, %00 null bytes, %2F slashes)
|
|
34
|
+
current = this.urlDecode(current);
|
|
35
|
+
// Hex escape decode (\x41, \x00, \x2F)
|
|
36
|
+
current = this.decodeHexEscapes(current);
|
|
37
|
+
// Unicode escape decode (\u0041, \U00000041)
|
|
38
|
+
current = this.decodeUnicodeEscapes(current);
|
|
39
|
+
// HTML entity decode (<, <, <)
|
|
40
|
+
current = this.decodeHtmlEntities(current);
|
|
41
|
+
// Double-backslash unwrap (\\. → .)
|
|
42
|
+
current = this.unwrapDoubleEscapes(current);
|
|
43
|
+
if (current === before)
|
|
44
|
+
break;
|
|
45
|
+
depth++;
|
|
46
|
+
}
|
|
47
|
+
if (current !== unicodeNormalized) {
|
|
48
|
+
transformations.push('decode-loop');
|
|
49
|
+
}
|
|
50
|
+
// ── Step 3: Shell normalization ──
|
|
51
|
+
const shellNormalized = this.shellNormalize(current);
|
|
52
|
+
if (shellNormalized !== current) {
|
|
53
|
+
transformations.push('shell-normalize');
|
|
54
|
+
current = shellNormalized;
|
|
55
|
+
}
|
|
56
|
+
// ── Step 4: Whitespace normalization (collapse runs) ──
|
|
57
|
+
const whitespaceNormalized = current.replace(/\s+/g, ' ').trim();
|
|
58
|
+
if (whitespaceNormalized !== current) {
|
|
59
|
+
transformations.push('whitespace');
|
|
60
|
+
current = whitespaceNormalized;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
normalized: current,
|
|
64
|
+
wasModified: transformations.length > 0,
|
|
65
|
+
transformations,
|
|
66
|
+
original: input,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* URL decode: %XX → character, handles malformed sequences.
|
|
71
|
+
*/
|
|
72
|
+
urlDecode(input) {
|
|
73
|
+
try {
|
|
74
|
+
return decodeURIComponent(input.replace(/\+/g, ' '));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Gracefully handle malformed % sequences: replace only valid ones
|
|
78
|
+
return input.replace(/%([0-9A-Fa-f]{2})/g, (_match, hex) => {
|
|
79
|
+
try {
|
|
80
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return _match;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Decode hex escapes: \x41 → 'A', \x00 → null byte detection.
|
|
90
|
+
*/
|
|
91
|
+
decodeHexEscapes(input) {
|
|
92
|
+
return input.replace(/\\x([0-9A-Fa-f]{2})/g, (_match, hex) => {
|
|
93
|
+
const code = parseInt(hex, 16);
|
|
94
|
+
// Preserve null byte as marker for detection
|
|
95
|
+
if (code === 0)
|
|
96
|
+
return '\0';
|
|
97
|
+
return String.fromCharCode(code);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Decode unicode escapes: \u0041 → 'A', \U00000041 → 'A'.
|
|
102
|
+
*/
|
|
103
|
+
decodeUnicodeEscapes(input) {
|
|
104
|
+
return input
|
|
105
|
+
.replace(/\\u([0-9A-Fa-f]{4})/g, (_match, hex) => {
|
|
106
|
+
try {
|
|
107
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return _match;
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
.replace(/\\U([0-9A-Fa-f]{8})/g, (_match, hex) => {
|
|
114
|
+
try {
|
|
115
|
+
const code = parseInt(hex, 16);
|
|
116
|
+
if (code > 0x10ffff)
|
|
117
|
+
return _match; // Invalid unicode
|
|
118
|
+
return String.fromCodePoint(code);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return _match;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Decode HTML entities: < -> <, < -> <, < -> <.
|
|
127
|
+
* Entity map built at runtime to avoid source-level entity decoding issues.
|
|
128
|
+
*/
|
|
129
|
+
static htmlEntityMap = null;
|
|
130
|
+
static getHtmlEntityMap() {
|
|
131
|
+
if (PayloadNormalizer.htmlEntityMap)
|
|
132
|
+
return PayloadNormalizer.htmlEntityMap;
|
|
133
|
+
const a = String.fromCharCode(38); // ampersand char
|
|
134
|
+
const pairs = [
|
|
135
|
+
[a + 'lt;', '<'],
|
|
136
|
+
[a + 'gt;', '>'],
|
|
137
|
+
[a + 'amp;', a],
|
|
138
|
+
[a + 'quot;', '"'],
|
|
139
|
+
[a + '#39;', "'"],
|
|
140
|
+
[a + 'apos;', "'"],
|
|
141
|
+
[a + 'sol;', '/'],
|
|
142
|
+
[a + 'bsol;', '\\'],
|
|
143
|
+
[a + 'colon;', ':'],
|
|
144
|
+
[a + 'semi;', ';'],
|
|
145
|
+
[a + 'verbar;', '|'],
|
|
146
|
+
[a + 'dollar;', '$'],
|
|
147
|
+
[a + 'lpar;', '('],
|
|
148
|
+
[a + 'rpar;', ')'],
|
|
149
|
+
[a + 'lcub;', '{'],
|
|
150
|
+
[a + 'rcub;', '}'],
|
|
151
|
+
[a + 'lbrack;', '['],
|
|
152
|
+
[a + 'rbrack;', ']'],
|
|
153
|
+
];
|
|
154
|
+
PayloadNormalizer.htmlEntityMap = pairs.map(([entity, ch]) => {
|
|
155
|
+
const escaped = entity.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
156
|
+
return [new RegExp(escaped, 'g'), ch];
|
|
157
|
+
});
|
|
158
|
+
return PayloadNormalizer.htmlEntityMap;
|
|
159
|
+
}
|
|
160
|
+
decodeHtmlEntities(input) {
|
|
161
|
+
let result = input;
|
|
162
|
+
// Named entities
|
|
163
|
+
for (const [regex, ch] of PayloadNormalizer.getHtmlEntityMap()) {
|
|
164
|
+
result = result.replace(regex, ch);
|
|
165
|
+
}
|
|
166
|
+
// Numeric decimal entities: <
|
|
167
|
+
result = result.replace(/&#(\d+);/g, (_match, dec) => {
|
|
168
|
+
const code = parseInt(dec, 10);
|
|
169
|
+
return (code > 0 && code < 65536) ? String.fromCharCode(code) : _match;
|
|
170
|
+
});
|
|
171
|
+
// Numeric hex entities: <
|
|
172
|
+
result = result.replace(/&#x([0-9A-Fa-f]+);/g, (_match, hex) => {
|
|
173
|
+
const code = parseInt(hex, 16);
|
|
174
|
+
return (code > 0 && code < 65536) ? String.fromCharCode(code) : _match;
|
|
175
|
+
});
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Unwrap double escapes: \\. → literal character.
|
|
180
|
+
*/
|
|
181
|
+
unwrapDoubleEscapes(input) {
|
|
182
|
+
return input.replace(/\\(.)/g, (_match, char) => {
|
|
183
|
+
// Only unwrap if the backslash is escaping a non-special char
|
|
184
|
+
if ('\\$`"\''.includes(char))
|
|
185
|
+
return _match;
|
|
186
|
+
return char;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Shell normalize: collapse common shell obfuscation patterns.
|
|
191
|
+
*
|
|
192
|
+
* - $'cmd' → cmd (ANSI-C quoting)
|
|
193
|
+
* - "c"m"d" → cmd (quote splitting)
|
|
194
|
+
* - ''cmd'' → cmd (empty quote pairs)
|
|
195
|
+
* - c\md → cmd (backslash escapes)
|
|
196
|
+
*/
|
|
197
|
+
shellNormalize(input) {
|
|
198
|
+
let result = input;
|
|
199
|
+
// ANSI-C quoting: $'command' → command
|
|
200
|
+
result = result.replace(/\$'([^']*)'/g, '$1');
|
|
201
|
+
// Quote splitting: "a""b" → ab, 'a''b' → ab
|
|
202
|
+
result = result.replace(/["']\s*["']/g, '');
|
|
203
|
+
// Shell backslash escapes on non-special chars
|
|
204
|
+
result = result.replace(/\\([^\\$`"'|&;><~#%{}()\[\]])/g, '$1');
|
|
205
|
+
// Null byte detection (normalized → mark as NUL for policy patterns)
|
|
206
|
+
result = result.replace(/\0/g, '\\0');
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Specifically normalize a JSON string value (tool argument).
|
|
211
|
+
* Handles nested JSON structures recursively.
|
|
212
|
+
*/
|
|
213
|
+
normalizeJsonValue(value, depth = 0) {
|
|
214
|
+
if (depth > 10)
|
|
215
|
+
return value; // Recursion guard
|
|
216
|
+
if (typeof value === 'string') {
|
|
217
|
+
return this.normalize(value).normalized;
|
|
218
|
+
}
|
|
219
|
+
if (Array.isArray(value)) {
|
|
220
|
+
return value.map((item) => this.normalizeJsonValue(item, depth + 1));
|
|
221
|
+
}
|
|
222
|
+
if (value !== null && typeof value === 'object') {
|
|
223
|
+
const result = {};
|
|
224
|
+
for (const [key, val] of Object.entries(value)) {
|
|
225
|
+
result[key] = this.normalizeJsonValue(val, depth + 1);
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** Singleton instance for policy engine integration */
|
|
233
|
+
let defaultInstance = null;
|
|
234
|
+
export function getNormalizer() {
|
|
235
|
+
if (!defaultInstance) {
|
|
236
|
+
defaultInstance = new PayloadNormalizer();
|
|
237
|
+
}
|
|
238
|
+
return defaultInstance;
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=payload-normalizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-normalizer.js","sourceRoot":"","sources":["../../src/utils/payload-normalizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Audit Trail — records every policy change for compliance.
|
|
3
|
+
* Logs: who changed what, when, old/new values, and rollback info.
|
|
4
|
+
* Enable with: POLICY_AUDIT_ENABLED=true
|
|
5
|
+
*/
|
|
6
|
+
export interface PolicyChangeRecord {
|
|
7
|
+
timestamp: string;
|
|
8
|
+
actor: string;
|
|
9
|
+
change: string;
|
|
10
|
+
oldValue?: string;
|
|
11
|
+
newValue?: string;
|
|
12
|
+
sourceHash?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class PolicyAuditor {
|
|
15
|
+
private auditPath;
|
|
16
|
+
private enabled;
|
|
17
|
+
private lastHash;
|
|
18
|
+
constructor(auditPath?: string);
|
|
19
|
+
record(change: PolicyChangeRecord): void;
|
|
20
|
+
readAuditTrail(): PolicyChangeRecord[];
|
|
21
|
+
computeHash(content: string): string;
|
|
22
|
+
hasChanged(content: string): boolean;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=policy-auditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-auditor.d.ts","sourceRoot":"","sources":["../../src/utils/policy-auditor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAuB;gBAE3B,SAAS,CAAC,EAAE,MAAM;IAK9B,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAWxC,cAAc,IAAI,kBAAkB,EAAE;IAUtC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAUpC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;CASrC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Audit Trail — records every policy change for compliance.
|
|
3
|
+
* Logs: who changed what, when, old/new values, and rollback info.
|
|
4
|
+
* Enable with: POLICY_AUDIT_ENABLED=true
|
|
5
|
+
*/
|
|
6
|
+
import { writeFileSync, readFileSync, existsSync } from 'fs';
|
|
7
|
+
import { Logger } from './logger.js';
|
|
8
|
+
export class PolicyAuditor {
|
|
9
|
+
auditPath;
|
|
10
|
+
enabled;
|
|
11
|
+
lastHash = null;
|
|
12
|
+
constructor(auditPath) {
|
|
13
|
+
this.enabled = process.env['POLICY_AUDIT_ENABLED'] === 'true';
|
|
14
|
+
this.auditPath = auditPath || process.env['POLICY_AUDIT_LOG'] || './policy-audit.jsonl';
|
|
15
|
+
}
|
|
16
|
+
record(change) {
|
|
17
|
+
if (!this.enabled)
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
const line = JSON.stringify({ ...change, source: 'mcp-guardian-policy-auditor' }) + '\n';
|
|
21
|
+
writeFileSync(this.auditPath, line, { flag: 'a' });
|
|
22
|
+
Logger.debug(`[policy-auditor] Change recorded: ${change.change}`);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
Logger.error(`[policy-auditor] Failed to write audit log: ${err?.message}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
readAuditTrail() {
|
|
29
|
+
if (!existsSync(this.auditPath))
|
|
30
|
+
return [];
|
|
31
|
+
try {
|
|
32
|
+
const content = readFileSync(this.auditPath, 'utf-8');
|
|
33
|
+
return content.trim().split('\n').filter(Boolean).map(l => JSON.parse(l));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
computeHash(content) {
|
|
40
|
+
let hash = 0;
|
|
41
|
+
for (let i = 0; i < content.length; i++) {
|
|
42
|
+
const char = content.charCodeAt(i);
|
|
43
|
+
hash = ((hash << 5) - hash) + char;
|
|
44
|
+
hash |= 0;
|
|
45
|
+
}
|
|
46
|
+
return hash.toString(16);
|
|
47
|
+
}
|
|
48
|
+
hasChanged(content) {
|
|
49
|
+
const currentHash = this.computeHash(content);
|
|
50
|
+
if (this.lastHash && this.lastHash !== currentHash) {
|
|
51
|
+
this.lastHash = currentHash;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
this.lastHash = currentHash;
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=policy-auditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-auditor.js","sourceRoot":"","sources":["../../src/utils/policy-auditor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAWrC,MAAM,OAAO,aAAa;IAChB,SAAS,CAAS;IAClB,OAAO,CAAU;IACjB,QAAQ,GAAkB,IAAI,CAAC;IAEvC,YAAY,SAAkB;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,KAAK,MAAM,CAAC;QAC9D,IAAI,CAAC,SAAS,GAAG,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,sBAAsB,CAAC;IAC1F,CAAC;IAED,MAAM,CAAC,MAA0B;QAC/B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAC,GAAG,IAAI,CAAC;YACzF,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;YACnD,MAAM,CAAC,KAAK,CAAC,qCAAqC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACrE,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,MAAM,CAAC,KAAK,CAAC,+CAA+C,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED,cAAc;QACZ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACtD,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,WAAW,CAAC,OAAe;QACzB,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACnC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;YACnC,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,UAAU,CAAC,OAAe;QACxB,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;YACnD,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|