@defai.digital/ax-cli 3.5.2 → 3.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/.ax-cli/memory.json +8 -8
- package/README.md +27 -1
- package/dist/agent/chat-history-manager.d.ts +56 -0
- package/dist/agent/chat-history-manager.js +150 -0
- package/dist/agent/chat-history-manager.js.map +1 -0
- package/dist/agent/llm-agent.js +1 -1
- package/dist/agent/llm-agent.js.map +1 -1
- package/dist/agent/tool-manager.d.ts +39 -0
- package/dist/agent/tool-manager.js +76 -0
- package/dist/agent/tool-manager.js.map +1 -0
- package/dist/analyzers/ast/index.d.ts +9 -0
- package/dist/analyzers/ast/index.js +10 -0
- package/dist/analyzers/ast/index.js.map +1 -0
- package/dist/analyzers/ast/node-helpers.d.ts +81 -0
- package/dist/analyzers/ast/node-helpers.js +128 -0
- package/dist/analyzers/ast/node-helpers.js.map +1 -0
- package/dist/analyzers/ast/traverser.d.ts +67 -0
- package/dist/analyzers/ast/traverser.js +156 -0
- package/dist/analyzers/ast/traverser.js.map +1 -0
- package/dist/analyzers/best-practices/index.d.ts +10 -0
- package/dist/analyzers/best-practices/index.js +11 -0
- package/dist/analyzers/best-practices/index.js.map +1 -0
- package/dist/commands/setup.js +13 -5
- package/dist/commands/setup.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/client.d.ts +1 -0
- package/dist/llm/client.js +44 -0
- package/dist/llm/client.js.map +1 -1
- package/dist/mcp/ssrf-protection.d.ts +86 -0
- package/dist/mcp/ssrf-protection.js +313 -0
- package/dist/mcp/ssrf-protection.js.map +1 -0
- package/dist/mcp/validation.d.ts +4 -0
- package/dist/mcp/validation.js +122 -11
- package/dist/mcp/validation.js.map +1 -1
- package/dist/schemas/settings-schemas.d.ts +30 -0
- package/dist/schemas/settings-schemas.js +30 -0
- package/dist/schemas/settings-schemas.js.map +1 -1
- package/dist/tools/bash.d.ts +3 -2
- package/dist/tools/bash.js +31 -2
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/search.js +121 -128
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/text-editor.js +52 -15
- package/dist/tools/text-editor.js.map +1 -1
- package/dist/ui/components/status-bar.js +2 -2
- package/dist/ui/components/status-bar.js.map +1 -1
- package/dist/ui/components/toast-notification.js +0 -1
- package/dist/ui/components/toast-notification.js.map +1 -1
- package/dist/utils/audit-logger.d.ts +247 -0
- package/dist/utils/audit-logger.js +374 -0
- package/dist/utils/audit-logger.js.map +1 -0
- package/dist/utils/command-security.d.ts +85 -0
- package/dist/utils/command-security.js +200 -0
- package/dist/utils/command-security.js.map +1 -0
- package/dist/utils/encryption.d.ts +78 -0
- package/dist/utils/encryption.js +216 -0
- package/dist/utils/encryption.js.map +1 -0
- package/dist/utils/error-sanitizer.d.ts +119 -0
- package/dist/utils/error-sanitizer.js +253 -0
- package/dist/utils/error-sanitizer.js.map +1 -0
- package/dist/utils/input-sanitizer.d.ts +210 -0
- package/dist/utils/input-sanitizer.js +362 -0
- package/dist/utils/input-sanitizer.js.map +1 -0
- package/dist/utils/json-utils.d.ts +13 -0
- package/dist/utils/json-utils.js +55 -1
- package/dist/utils/json-utils.js.map +1 -1
- package/dist/utils/parallel-analyzer.js +29 -12
- package/dist/utils/parallel-analyzer.js.map +1 -1
- package/dist/utils/path-security.d.ts +90 -0
- package/dist/utils/path-security.js +328 -0
- package/dist/utils/path-security.js.map +1 -0
- package/dist/utils/process-pool.d.ts +105 -0
- package/dist/utils/process-pool.js +326 -0
- package/dist/utils/process-pool.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +207 -0
- package/dist/utils/rate-limiter.js +303 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/settings-manager.js +83 -4
- package/dist/utils/settings-manager.js.map +1 -1
- package/eslint.config.js +3 -0
- package/package.json +1 -1
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-11e9e0ba-c39d-4fd2-aa77-bc818811c921.json +0 -69
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-2b260b98-b418-4c7c-9694-e2b94967e662.json +0 -24
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-7e03601e-e8ab-4cd7-9841-a74b66adf78f.json +0 -69
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-7f9c6562-771f-4fd0-adcf-9e7e9ac34ae8.json +0 -44
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-e1ebe666-4c3a-4367-ba5c-27fe512a9c70.json +0 -24
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-15743e7d-430c-4d76-b6fc-955d7a5c250c.json +0 -44
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-25cf7679-0b3f-4988-83d7-704548fbba91.json +0 -69
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-54aedbac-6db0-464e-8ebb-dbb3979e6dca.json +0 -24
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-7658aed8-fe5d-4222-903f-1a7c63717ea7.json +0 -24
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-c9c13497-40dc-4294-a327-6a5fc854eaa1.json +0 -69
- package/ax.config.json +0 -333
- package/dist/hooks/use-chat-reducer.d.ts +0 -61
- package/dist/hooks/use-chat-reducer.js +0 -118
- package/dist/hooks/use-chat-reducer.js.map +0 -1
- package/dist/hooks/use-enhanced-input.d.ts +0 -40
- package/dist/hooks/use-enhanced-input.js +0 -249
- package/dist/hooks/use-enhanced-input.js.map +0 -1
- package/dist/hooks/use-input-handler.d.ts +0 -46
- package/dist/hooks/use-input-handler.js +0 -1430
- package/dist/hooks/use-input-handler.js.map +0 -1
- package/dist/hooks/use-input-history.d.ts +0 -9
- package/dist/hooks/use-input-history.js +0 -112
- package/dist/hooks/use-input-history.js.map +0 -1
- package/dist/utils/paste-collapse.d.ts +0 -46
- package/dist/utils/paste-collapse.js +0 -77
- package/dist/utils/paste-collapse.js.map +0 -1
- package/packages/schemas/dist/index.d.ts +0 -14
- package/packages/schemas/dist/index.d.ts.map +0 -1
- package/packages/schemas/dist/index.js +0 -19
- package/packages/schemas/dist/index.js.map +0 -1
- package/packages/schemas/dist/public/core/brand-types.d.ts +0 -308
- package/packages/schemas/dist/public/core/brand-types.d.ts.map +0 -1
- package/packages/schemas/dist/public/core/brand-types.js +0 -243
- package/packages/schemas/dist/public/core/brand-types.js.map +0 -1
- package/packages/schemas/dist/public/core/enums.d.ts +0 -227
- package/packages/schemas/dist/public/core/enums.d.ts.map +0 -1
- package/packages/schemas/dist/public/core/enums.js +0 -222
- package/packages/schemas/dist/public/core/enums.js.map +0 -1
- package/packages/schemas/dist/public/core/id-types.d.ts +0 -286
- package/packages/schemas/dist/public/core/id-types.d.ts.map +0 -1
- package/packages/schemas/dist/public/core/id-types.js +0 -136
- package/packages/schemas/dist/public/core/id-types.js.map +0 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF Protection for HTTP/SSE Transports (REQ-SEC-011)
|
|
3
|
+
*
|
|
4
|
+
* Prevents Server-Side Request Forgery attacks by validating URLs
|
|
5
|
+
* Blocks:
|
|
6
|
+
* - Private IP addresses (RFC 1918, loopback, link-local)
|
|
7
|
+
* - DNS rebinding attacks
|
|
8
|
+
* - Cloud metadata endpoints
|
|
9
|
+
* - Dangerous protocols
|
|
10
|
+
*
|
|
11
|
+
* Security: CVSS 8.6 (High Priority)
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* URL validation result
|
|
15
|
+
*/
|
|
16
|
+
export interface SSRFValidationResult {
|
|
17
|
+
valid: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
category?: SSRFThreatCategory;
|
|
20
|
+
resolvedIp?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* SSRF threat categories
|
|
24
|
+
*/
|
|
25
|
+
export declare enum SSRFThreatCategory {
|
|
26
|
+
PRIVATE_IP = "PRIVATE_IP",
|
|
27
|
+
BLOCKED_HOSTNAME = "BLOCKED_HOSTNAME",
|
|
28
|
+
INVALID_PROTOCOL = "INVALID_PROTOCOL",
|
|
29
|
+
INVALID_URL = "INVALID_URL",
|
|
30
|
+
DNS_REBINDING = "DNS_REBINDING"
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate URL against SSRF threats
|
|
34
|
+
*
|
|
35
|
+
* @param urlString - URL to validate
|
|
36
|
+
* @returns Validation result
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const result = validateURL('https://api.example.com');
|
|
41
|
+
* if (!result.valid) {
|
|
42
|
+
* console.error(`SSRF threat detected: ${result.error}`);
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateURL(urlString: string): SSRFValidationResult;
|
|
47
|
+
/**
|
|
48
|
+
* Validate MCP HTTP/SSE transport URL
|
|
49
|
+
*
|
|
50
|
+
* @param url - Transport URL
|
|
51
|
+
* @param transportType - Transport type ('http' or 'sse')
|
|
52
|
+
* @returns Validation result
|
|
53
|
+
*/
|
|
54
|
+
export declare function validateTransportURL(url: string, transportType: 'http' | 'sse'): SSRFValidationResult;
|
|
55
|
+
/**
|
|
56
|
+
* Safe URL wrapper for HTTP requests
|
|
57
|
+
*
|
|
58
|
+
* @param url - URL to validate and wrap
|
|
59
|
+
* @returns Validated URL or throws error
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* try {
|
|
64
|
+
* const safeUrl = safeURL('https://api.example.com');
|
|
65
|
+
* await fetch(safeUrl);
|
|
66
|
+
* } catch (error) {
|
|
67
|
+
* console.error('SSRF protection blocked request:', error);
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function safeURL(url: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Batch validate multiple URLs
|
|
74
|
+
*
|
|
75
|
+
* @param urls - Array of URLs to validate
|
|
76
|
+
* @returns Array of validation results
|
|
77
|
+
*/
|
|
78
|
+
export declare function validateURLs(urls: string[]): SSRFValidationResult[];
|
|
79
|
+
/**
|
|
80
|
+
* Get SSRF protection statistics
|
|
81
|
+
*/
|
|
82
|
+
export declare function getSSRFStats(): {
|
|
83
|
+
blockedHostnames: number;
|
|
84
|
+
privateIPRanges: number;
|
|
85
|
+
allowedProtocols: number;
|
|
86
|
+
};
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF Protection for HTTP/SSE Transports (REQ-SEC-011)
|
|
3
|
+
*
|
|
4
|
+
* Prevents Server-Side Request Forgery attacks by validating URLs
|
|
5
|
+
* Blocks:
|
|
6
|
+
* - Private IP addresses (RFC 1918, loopback, link-local)
|
|
7
|
+
* - DNS rebinding attacks
|
|
8
|
+
* - Cloud metadata endpoints
|
|
9
|
+
* - Dangerous protocols
|
|
10
|
+
*
|
|
11
|
+
* Security: CVSS 8.6 (High Priority)
|
|
12
|
+
*/
|
|
13
|
+
import { getAuditLogger, AuditCategory } from '../utils/audit-logger.js';
|
|
14
|
+
/**
|
|
15
|
+
* Private IP ranges (RFC 1918 and special-use addresses)
|
|
16
|
+
*/
|
|
17
|
+
const PRIVATE_IP_RANGES = [
|
|
18
|
+
// IPv4 Private Ranges
|
|
19
|
+
{ start: '10.0.0.0', end: '10.255.255.255', name: 'RFC1918 Class A' },
|
|
20
|
+
{ start: '172.16.0.0', end: '172.31.255.255', name: 'RFC1918 Class B' },
|
|
21
|
+
{ start: '192.168.0.0', end: '192.168.255.255', name: 'RFC1918 Class C' },
|
|
22
|
+
// Loopback
|
|
23
|
+
{ start: '127.0.0.0', end: '127.255.255.255', name: 'Loopback' },
|
|
24
|
+
// Link-local
|
|
25
|
+
{ start: '169.254.0.0', end: '169.254.255.255', name: 'Link-local' },
|
|
26
|
+
// Multicast
|
|
27
|
+
{ start: '224.0.0.0', end: '239.255.255.255', name: 'Multicast' },
|
|
28
|
+
// Reserved
|
|
29
|
+
{ start: '240.0.0.0', end: '255.255.255.255', name: 'Reserved' },
|
|
30
|
+
// Broadcast
|
|
31
|
+
{ start: '255.255.255.255', end: '255.255.255.255', name: 'Broadcast' },
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Blocked hostnames (cloud metadata endpoints, etc.)
|
|
35
|
+
*/
|
|
36
|
+
const BLOCKED_HOSTNAMES = [
|
|
37
|
+
// AWS metadata
|
|
38
|
+
'169.254.169.254',
|
|
39
|
+
'metadata.google.internal',
|
|
40
|
+
// GCP metadata
|
|
41
|
+
'metadata.goog',
|
|
42
|
+
// Azure metadata
|
|
43
|
+
'169.254.169.254',
|
|
44
|
+
// Kubernetes metadata
|
|
45
|
+
'kubernetes.default.svc',
|
|
46
|
+
// Special addresses
|
|
47
|
+
'localhost',
|
|
48
|
+
'localhost.localdomain',
|
|
49
|
+
'0.0.0.0',
|
|
50
|
+
'::1',
|
|
51
|
+
'::',
|
|
52
|
+
// DNS rebinding targets
|
|
53
|
+
'0x7f000001', // 127.0.0.1 in hex
|
|
54
|
+
'2130706433', // 127.0.0.1 in decimal
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* Allowed protocols
|
|
58
|
+
*/
|
|
59
|
+
const ALLOWED_PROTOCOLS = ['http:', 'https:'];
|
|
60
|
+
/**
|
|
61
|
+
* SSRF threat categories
|
|
62
|
+
*/
|
|
63
|
+
export var SSRFThreatCategory;
|
|
64
|
+
(function (SSRFThreatCategory) {
|
|
65
|
+
SSRFThreatCategory["PRIVATE_IP"] = "PRIVATE_IP";
|
|
66
|
+
SSRFThreatCategory["BLOCKED_HOSTNAME"] = "BLOCKED_HOSTNAME";
|
|
67
|
+
SSRFThreatCategory["INVALID_PROTOCOL"] = "INVALID_PROTOCOL";
|
|
68
|
+
SSRFThreatCategory["INVALID_URL"] = "INVALID_URL";
|
|
69
|
+
SSRFThreatCategory["DNS_REBINDING"] = "DNS_REBINDING";
|
|
70
|
+
})(SSRFThreatCategory || (SSRFThreatCategory = {}));
|
|
71
|
+
/**
|
|
72
|
+
* Convert IP address string to number (unsigned 32-bit)
|
|
73
|
+
*/
|
|
74
|
+
function ipToNumber(ip) {
|
|
75
|
+
const parts = ip.split('.').map(p => parseInt(p, 10));
|
|
76
|
+
if (parts.length !== 4 || parts.some(p => isNaN(p) || p < 0 || p > 255)) {
|
|
77
|
+
return -1;
|
|
78
|
+
}
|
|
79
|
+
// Use unsigned 32-bit arithmetic (>>> 0 converts to unsigned)
|
|
80
|
+
return ((parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3]) >>> 0;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if IP is in private range
|
|
84
|
+
*/
|
|
85
|
+
function isPrivateIP(ip) {
|
|
86
|
+
const ipNum = ipToNumber(ip);
|
|
87
|
+
if (ipNum === -1) {
|
|
88
|
+
return { isPrivate: false };
|
|
89
|
+
}
|
|
90
|
+
for (const range of PRIVATE_IP_RANGES) {
|
|
91
|
+
const startNum = ipToNumber(range.start);
|
|
92
|
+
const endNum = ipToNumber(range.end);
|
|
93
|
+
if (ipNum >= startNum && ipNum <= endNum) {
|
|
94
|
+
return { isPrivate: true, range: range.name };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { isPrivate: false };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if hostname is blocked
|
|
101
|
+
*/
|
|
102
|
+
function isBlockedHostname(hostname) {
|
|
103
|
+
const normalized = hostname.toLowerCase().trim();
|
|
104
|
+
// Check exact matches
|
|
105
|
+
if (BLOCKED_HOSTNAMES.includes(normalized)) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
// Check for localhost variations
|
|
109
|
+
if (normalized.startsWith('localhost') ||
|
|
110
|
+
normalized.endsWith('.localhost') ||
|
|
111
|
+
normalized === '0.0.0.0' ||
|
|
112
|
+
normalized === '::1') {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Validate URL against SSRF threats
|
|
119
|
+
*
|
|
120
|
+
* @param urlString - URL to validate
|
|
121
|
+
* @returns Validation result
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* const result = validateURL('https://api.example.com');
|
|
126
|
+
* if (!result.valid) {
|
|
127
|
+
* console.error(`SSRF threat detected: ${result.error}`);
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export function validateURL(urlString) {
|
|
132
|
+
const auditLogger = getAuditLogger();
|
|
133
|
+
// Parse URL
|
|
134
|
+
let url;
|
|
135
|
+
try {
|
|
136
|
+
url = new URL(urlString);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
const result = {
|
|
140
|
+
valid: false,
|
|
141
|
+
error: 'Invalid URL format',
|
|
142
|
+
category: SSRFThreatCategory.INVALID_URL,
|
|
143
|
+
};
|
|
144
|
+
auditLogger.logWarning({
|
|
145
|
+
category: AuditCategory.INPUT_VALIDATION,
|
|
146
|
+
action: 'ssrf_validation_failed',
|
|
147
|
+
resource: urlString,
|
|
148
|
+
outcome: 'failure',
|
|
149
|
+
error: result.error,
|
|
150
|
+
details: { category: result.category },
|
|
151
|
+
});
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
// Validate protocol
|
|
155
|
+
if (!ALLOWED_PROTOCOLS.includes(url.protocol)) {
|
|
156
|
+
const result = {
|
|
157
|
+
valid: false,
|
|
158
|
+
error: `Protocol not allowed: ${url.protocol}. Only HTTP(S) allowed.`,
|
|
159
|
+
category: SSRFThreatCategory.INVALID_PROTOCOL,
|
|
160
|
+
};
|
|
161
|
+
auditLogger.logCritical({
|
|
162
|
+
category: AuditCategory.INPUT_VALIDATION,
|
|
163
|
+
action: 'ssrf_invalid_protocol',
|
|
164
|
+
resource: urlString,
|
|
165
|
+
outcome: 'failure',
|
|
166
|
+
error: result.error,
|
|
167
|
+
details: { protocol: url.protocol, category: result.category },
|
|
168
|
+
});
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
// Validate hostname
|
|
172
|
+
const hostname = url.hostname.toLowerCase();
|
|
173
|
+
// Check for IPv6 addresses (they include brackets in hostname)
|
|
174
|
+
if (hostname.startsWith('[') && hostname.endsWith(']')) {
|
|
175
|
+
const result = {
|
|
176
|
+
valid: false,
|
|
177
|
+
error: `IPv6 addresses are not allowed: ${hostname}`,
|
|
178
|
+
category: SSRFThreatCategory.BLOCKED_HOSTNAME,
|
|
179
|
+
};
|
|
180
|
+
auditLogger.logCritical({
|
|
181
|
+
category: AuditCategory.INPUT_VALIDATION,
|
|
182
|
+
action: 'ssrf_ipv6_blocked',
|
|
183
|
+
resource: urlString,
|
|
184
|
+
outcome: 'failure',
|
|
185
|
+
error: result.error,
|
|
186
|
+
details: { hostname, category: result.category },
|
|
187
|
+
});
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
// Check if hostname is an IPv4 address (to get proper category)
|
|
191
|
+
const ipMatch = hostname.match(/^(\d{1,3}\.){3}\d{1,3}$/);
|
|
192
|
+
if (ipMatch) {
|
|
193
|
+
const privateCheck = isPrivateIP(hostname);
|
|
194
|
+
if (privateCheck.isPrivate) {
|
|
195
|
+
const result = {
|
|
196
|
+
valid: false,
|
|
197
|
+
error: `Private IP address not allowed: ${hostname} (${privateCheck.range})`,
|
|
198
|
+
category: SSRFThreatCategory.PRIVATE_IP,
|
|
199
|
+
resolvedIp: hostname,
|
|
200
|
+
};
|
|
201
|
+
auditLogger.logCritical({
|
|
202
|
+
category: AuditCategory.INPUT_VALIDATION,
|
|
203
|
+
action: 'ssrf_private_ip',
|
|
204
|
+
resource: urlString,
|
|
205
|
+
outcome: 'failure',
|
|
206
|
+
error: result.error,
|
|
207
|
+
details: {
|
|
208
|
+
ip: hostname,
|
|
209
|
+
range: privateCheck.range,
|
|
210
|
+
category: result.category,
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Check blocked hostnames (non-IP addresses)
|
|
217
|
+
if (isBlockedHostname(hostname)) {
|
|
218
|
+
const result = {
|
|
219
|
+
valid: false,
|
|
220
|
+
error: `Blocked hostname: ${hostname}`,
|
|
221
|
+
category: SSRFThreatCategory.BLOCKED_HOSTNAME,
|
|
222
|
+
};
|
|
223
|
+
auditLogger.logCritical({
|
|
224
|
+
category: AuditCategory.INPUT_VALIDATION,
|
|
225
|
+
action: 'ssrf_blocked_hostname',
|
|
226
|
+
resource: urlString,
|
|
227
|
+
outcome: 'failure',
|
|
228
|
+
error: result.error,
|
|
229
|
+
details: { hostname, category: result.category },
|
|
230
|
+
});
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
// URL is valid
|
|
234
|
+
auditLogger.logInfo({
|
|
235
|
+
category: AuditCategory.INPUT_VALIDATION,
|
|
236
|
+
action: 'ssrf_validation_passed',
|
|
237
|
+
resource: urlString,
|
|
238
|
+
outcome: 'success',
|
|
239
|
+
details: {
|
|
240
|
+
hostname,
|
|
241
|
+
protocol: url.protocol,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
return { valid: true };
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Validate MCP HTTP/SSE transport URL
|
|
248
|
+
*
|
|
249
|
+
* @param url - Transport URL
|
|
250
|
+
* @param transportType - Transport type ('http' or 'sse')
|
|
251
|
+
* @returns Validation result
|
|
252
|
+
*/
|
|
253
|
+
export function validateTransportURL(url, transportType) {
|
|
254
|
+
const auditLogger = getAuditLogger();
|
|
255
|
+
const result = validateURL(url);
|
|
256
|
+
if (!result.valid) {
|
|
257
|
+
auditLogger.logCritical({
|
|
258
|
+
category: AuditCategory.MCP_OPERATION,
|
|
259
|
+
action: 'mcp_transport_ssrf_blocked',
|
|
260
|
+
resource: url,
|
|
261
|
+
outcome: 'failure',
|
|
262
|
+
error: result.error,
|
|
263
|
+
details: {
|
|
264
|
+
transportType,
|
|
265
|
+
category: result.category,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Safe URL wrapper for HTTP requests
|
|
273
|
+
*
|
|
274
|
+
* @param url - URL to validate and wrap
|
|
275
|
+
* @returns Validated URL or throws error
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* ```typescript
|
|
279
|
+
* try {
|
|
280
|
+
* const safeUrl = safeURL('https://api.example.com');
|
|
281
|
+
* await fetch(safeUrl);
|
|
282
|
+
* } catch (error) {
|
|
283
|
+
* console.error('SSRF protection blocked request:', error);
|
|
284
|
+
* }
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
export function safeURL(url) {
|
|
288
|
+
const result = validateURL(url);
|
|
289
|
+
if (!result.valid) {
|
|
290
|
+
throw new Error(`SSRF protection: ${result.error}`);
|
|
291
|
+
}
|
|
292
|
+
return url;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Batch validate multiple URLs
|
|
296
|
+
*
|
|
297
|
+
* @param urls - Array of URLs to validate
|
|
298
|
+
* @returns Array of validation results
|
|
299
|
+
*/
|
|
300
|
+
export function validateURLs(urls) {
|
|
301
|
+
return urls.map(url => validateURL(url));
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Get SSRF protection statistics
|
|
305
|
+
*/
|
|
306
|
+
export function getSSRFStats() {
|
|
307
|
+
return {
|
|
308
|
+
blockedHostnames: BLOCKED_HOSTNAMES.length,
|
|
309
|
+
privateIPRanges: PRIVATE_IP_RANGES.length,
|
|
310
|
+
allowedProtocols: ALLOWED_PROTOCOLS.length,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=ssrf-protection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssrf-protection.js","sourceRoot":"","sources":["../../src/mcp/ssrf-protection.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/validation.d.ts
CHANGED
|
@@ -23,3 +23,7 @@ export declare function validateServerConfig(config: MCPServerConfig): Promise<V
|
|
|
23
23
|
* Format validation result for display
|
|
24
24
|
*/
|
|
25
25
|
export declare function formatValidationResult(result: ValidationResult): string;
|
|
26
|
+
/**
|
|
27
|
+
* Get the list of safe MCP commands (for documentation/testing)
|
|
28
|
+
*/
|
|
29
|
+
export declare function getSafeMCPCommands(): readonly string[];
|
package/dist/mcp/validation.js
CHANGED
|
@@ -4,10 +4,38 @@
|
|
|
4
4
|
* Provides pre-flight checks to validate MCP server configurations
|
|
5
5
|
* before attempting connection, improving error messages and UX.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { execFile } from 'child_process';
|
|
8
8
|
import { promisify } from 'util';
|
|
9
9
|
import { getTemplate } from './templates.js';
|
|
10
|
-
|
|
10
|
+
import { getAuditLogger, AuditCategory } from '../utils/audit-logger.js';
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
/**
|
|
13
|
+
* Safe commands whitelist for MCP stdio transport
|
|
14
|
+
* Only these commands are allowed to prevent command injection
|
|
15
|
+
*/
|
|
16
|
+
const SAFE_MCP_COMMANDS = [
|
|
17
|
+
// Node.js package managers
|
|
18
|
+
'node',
|
|
19
|
+
'npm',
|
|
20
|
+
'npx',
|
|
21
|
+
'bun',
|
|
22
|
+
'deno',
|
|
23
|
+
'pnpm',
|
|
24
|
+
'yarn',
|
|
25
|
+
// Python
|
|
26
|
+
'python',
|
|
27
|
+
'python3',
|
|
28
|
+
'pip',
|
|
29
|
+
'pip3',
|
|
30
|
+
'uvx',
|
|
31
|
+
// Docker
|
|
32
|
+
'docker',
|
|
33
|
+
// Common shell commands (for legitimate MCP servers)
|
|
34
|
+
'bash',
|
|
35
|
+
'sh',
|
|
36
|
+
'zsh',
|
|
37
|
+
// Full paths allowed (validated separately)
|
|
38
|
+
];
|
|
11
39
|
/**
|
|
12
40
|
* Validate an MCP server configuration with pre-flight checks
|
|
13
41
|
*/
|
|
@@ -47,6 +75,59 @@ export async function validateServerConfig(config) {
|
|
|
47
75
|
errors
|
|
48
76
|
};
|
|
49
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Validate command against whitelist
|
|
80
|
+
* Prevents command injection by only allowing safe commands
|
|
81
|
+
*/
|
|
82
|
+
function validateCommandWhitelist(command) {
|
|
83
|
+
// Allow full paths (they will be validated separately)
|
|
84
|
+
if (command.includes('/') || command.includes('\\')) {
|
|
85
|
+
// Validate path doesn't contain shell metacharacters
|
|
86
|
+
const dangerousChars = /[;&|`$()<>]/;
|
|
87
|
+
if (dangerousChars.test(command)) {
|
|
88
|
+
return {
|
|
89
|
+
valid: false,
|
|
90
|
+
error: `Command path contains dangerous characters: ${command}`
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return { valid: true };
|
|
94
|
+
}
|
|
95
|
+
// Check against whitelist
|
|
96
|
+
const baseCommand = command.split(/\s+/)[0]; // Get command name without args
|
|
97
|
+
if (!SAFE_MCP_COMMANDS.includes(baseCommand)) {
|
|
98
|
+
return {
|
|
99
|
+
valid: false,
|
|
100
|
+
error: `Command "${baseCommand}" is not in the safe commands whitelist. ` +
|
|
101
|
+
`Allowed: ${SAFE_MCP_COMMANDS.join(', ')}. ` +
|
|
102
|
+
`Use full path for custom commands.`
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return { valid: true };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Validate command arguments for injection attempts
|
|
109
|
+
*/
|
|
110
|
+
function validateCommandArgs(args) {
|
|
111
|
+
const dangerousPatterns = /[;&|`$()<>]/;
|
|
112
|
+
for (let i = 0; i < args.length; i++) {
|
|
113
|
+
const arg = args[i];
|
|
114
|
+
// Check for shell metacharacters
|
|
115
|
+
if (dangerousPatterns.test(arg)) {
|
|
116
|
+
return {
|
|
117
|
+
valid: false,
|
|
118
|
+
error: `Argument ${i} contains dangerous shell metacharacters: "${arg}"`
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// Check for null bytes
|
|
122
|
+
if (arg.includes('\0')) {
|
|
123
|
+
return {
|
|
124
|
+
valid: false,
|
|
125
|
+
error: `Argument ${i} contains null byte`
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { valid: true };
|
|
130
|
+
}
|
|
50
131
|
/**
|
|
51
132
|
* Validate stdio transport configuration
|
|
52
133
|
*/
|
|
@@ -59,14 +140,39 @@ async function validateStdioTransport(config, errors, warnings) {
|
|
|
59
140
|
errors.push('Command is required for stdio transport');
|
|
60
141
|
return;
|
|
61
142
|
}
|
|
62
|
-
//
|
|
63
|
-
const
|
|
64
|
-
if (!
|
|
65
|
-
|
|
143
|
+
// SECURITY: Validate command against whitelist (REQ-SEC-004)
|
|
144
|
+
const whitelistResult = validateCommandWhitelist(command);
|
|
145
|
+
if (!whitelistResult.valid) {
|
|
146
|
+
// REQ-SEC-008: Audit log command injection attempt
|
|
147
|
+
const auditLogger = getAuditLogger();
|
|
148
|
+
auditLogger.logCritical({
|
|
149
|
+
category: AuditCategory.COMMAND_EXECUTION,
|
|
150
|
+
action: 'mcp_command_injection_attempt',
|
|
151
|
+
resource: config.name,
|
|
152
|
+
outcome: 'failure',
|
|
153
|
+
error: whitelistResult.error,
|
|
154
|
+
details: { command, configName: config.name },
|
|
155
|
+
});
|
|
156
|
+
errors.push(whitelistResult.error);
|
|
157
|
+
return; // Don't continue if command is not allowed
|
|
66
158
|
}
|
|
67
159
|
// Validate args
|
|
68
160
|
if (args && !Array.isArray(args)) {
|
|
69
161
|
errors.push('Arguments must be an array');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// SECURITY: Validate arguments for injection attempts (REQ-SEC-004)
|
|
165
|
+
if (args && args.length > 0) {
|
|
166
|
+
const argsResult = validateCommandArgs(args);
|
|
167
|
+
if (!argsResult.valid) {
|
|
168
|
+
errors.push(argsResult.error);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Check if command is executable (after whitelist check)
|
|
173
|
+
const commandExists = await checkCommandExists(command);
|
|
174
|
+
if (!commandExists) {
|
|
175
|
+
warnings.push(`Command "${command}" not found in PATH. Please install it or provide full path.`);
|
|
70
176
|
}
|
|
71
177
|
// Check for common npm package patterns
|
|
72
178
|
if (command === 'npx' && args && args.length > 0) {
|
|
@@ -147,6 +253,7 @@ function validateRequiredEnvVars(config, errors, _warnings) {
|
|
|
147
253
|
}
|
|
148
254
|
/**
|
|
149
255
|
* Check if a command exists in PATH
|
|
256
|
+
* Uses execFile to prevent command injection vulnerabilities
|
|
150
257
|
*/
|
|
151
258
|
async function checkCommandExists(command) {
|
|
152
259
|
try {
|
|
@@ -154,11 +261,9 @@ async function checkCommandExists(command) {
|
|
|
154
261
|
if (command.includes('/') || command.includes('\\')) {
|
|
155
262
|
return true; // Assume full paths are valid
|
|
156
263
|
}
|
|
157
|
-
// Check if command exists in PATH
|
|
158
|
-
const checkCommand = process.platform === 'win32'
|
|
159
|
-
|
|
160
|
-
: `which ${command}`;
|
|
161
|
-
await execAsync(checkCommand);
|
|
264
|
+
// Check if command exists in PATH using execFile (prevents command injection)
|
|
265
|
+
const checkCommand = process.platform === 'win32' ? 'where' : 'which';
|
|
266
|
+
await execFileAsync(checkCommand, [command]);
|
|
162
267
|
return true;
|
|
163
268
|
}
|
|
164
269
|
catch {
|
|
@@ -212,4 +317,10 @@ export function formatValidationResult(result) {
|
|
|
212
317
|
}
|
|
213
318
|
return lines.join('\n');
|
|
214
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Get the list of safe MCP commands (for documentation/testing)
|
|
322
|
+
*/
|
|
323
|
+
export function getSafeMCPCommands() {
|
|
324
|
+
return SAFE_MCP_COMMANDS;
|
|
325
|
+
}
|
|
215
326
|
//# sourceMappingURL=validation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/mcp/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/mcp/validation.ts"],"names":[],"mappings":"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"}
|