@easynet/agent-tool 1.0.61 → 1.0.63
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/dist/api/adapters/LangChainToolsHub.d.ts.map +1 -1
- package/dist/api/expose/extension-init/initExtension.d.ts.map +1 -1
- package/dist/api/main.cjs +29 -13
- package/dist/api/main.d.ts +34 -0
- package/dist/api/main.d.ts.map +1 -1
- package/dist/api/main.js +2 -2
- package/dist/api/runtimeFromConfig.d.ts +2 -5
- package/dist/api/runtimeFromConfig.d.ts.map +1 -1
- package/dist/api/runtimeFromConfig.helpers.d.ts +12 -0
- package/dist/api/runtimeFromConfig.helpers.d.ts.map +1 -0
- package/dist/build.cjs +1 -0
- package/dist/build.js +1 -0
- package/dist/chunk-DEDDPMBU.js +3 -0
- package/dist/chunk-DEDDPMBU.js.map +1 -0
- package/dist/chunk-FWWN4D2F.js +3 -0
- package/dist/chunk-FWWN4D2F.js.map +1 -0
- package/dist/chunk-ICHSEIZN.cjs +4 -0
- package/dist/chunk-ICHSEIZN.cjs.map +1 -0
- package/dist/{chunk-HK4GTFTQ.cjs → chunk-M2GEHWPN.cjs} +41 -40
- package/dist/chunk-M2GEHWPN.cjs.map +1 -0
- package/dist/chunk-NKYFYALQ.js +181 -0
- package/dist/chunk-NKYFYALQ.js.map +1 -0
- package/dist/chunk-NOGGIM7B.cjs +4 -0
- package/dist/chunk-NOGGIM7B.cjs.map +1 -0
- package/dist/chunk-R55NXJIH.cjs +184 -0
- package/dist/chunk-R55NXJIH.cjs.map +1 -0
- package/dist/{chunk-NVT4X4CB.js → chunk-RJAF5XY6.js} +40 -39
- package/dist/chunk-RJAF5XY6.js.map +1 -0
- package/dist/{chunk-ZH5MH3AK.cjs → chunk-U67QDQFQ.cjs} +73 -41
- package/dist/chunk-U67QDQFQ.cjs.map +1 -0
- package/dist/chunk-WO4LZKPQ.cjs +359 -0
- package/dist/chunk-WO4LZKPQ.cjs.map +1 -0
- package/dist/chunk-YL6RC7HQ.cjs +4 -0
- package/dist/chunk-YL6RC7HQ.cjs.map +1 -0
- package/dist/chunk-YLWTSNTT.js +3 -0
- package/dist/chunk-YLWTSNTT.js.map +1 -0
- package/dist/{chunk-QPKBEU64.js → chunk-YMHUDRYE.js} +59 -31
- package/dist/chunk-YMHUDRYE.js.map +1 -0
- package/dist/chunk-YPGF5Y2Y.js +341 -0
- package/dist/chunk-YPGF5Y2Y.js.map +1 -0
- package/dist/core/index.cjs +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/runtime.cjs +1 -0
- package/dist/core/runtime.js +1 -0
- package/dist/extension.cjs +54 -355
- package/dist/extension.cjs.map +1 -1
- package/dist/extension.js +3 -339
- package/dist/extension.js.map +1 -1
- package/dist/index.cjs +96 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -7
- package/dist/index.js.map +1 -1
- package/dist/security.cjs +11 -178
- package/dist/security.cjs.map +1 -1
- package/dist/security.js +2 -179
- package/dist/security.js.map +1 -1
- package/dist/utils/cli/help.d.ts +2 -0
- package/dist/utils/cli/help.d.ts.map +1 -0
- package/dist/utils/cli/index.cjs +95 -73
- package/dist/utils/cli/index.cjs.map +1 -1
- package/dist/utils/cli/index.d.ts.map +1 -1
- package/dist/utils/cli/index.js +84 -62
- package/dist/utils/cli/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-HK4GTFTQ.cjs.map +0 -1
- package/dist/chunk-NVT4X4CB.js.map +0 -1
- package/dist/chunk-QPKBEU64.js.map +0 -1
- package/dist/chunk-ZH5MH3AK.cjs.map +0 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { createTaggedError } from './chunk-RZTTO5MQ.js';
|
|
2
|
+
import { lookup } from 'dns/promises';
|
|
3
|
+
|
|
4
|
+
async function validateUrl(url, options) {
|
|
5
|
+
let parsed;
|
|
6
|
+
try {
|
|
7
|
+
parsed = new URL(url);
|
|
8
|
+
} catch {
|
|
9
|
+
throw createTaggedError(
|
|
10
|
+
"HTTP_DISALLOWED_HOST",
|
|
11
|
+
`Invalid URL: ${url}`,
|
|
12
|
+
{ url }
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
16
|
+
throw createTaggedError(
|
|
17
|
+
"HTTP_DISALLOWED_HOST",
|
|
18
|
+
`Protocol not allowed: ${parsed.protocol}. Only http: and https: are supported.`,
|
|
19
|
+
{ url, protocol: parsed.protocol }
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const hostname = parsed.hostname;
|
|
23
|
+
if (!isHostAllowed(hostname, options.allowedHosts)) {
|
|
24
|
+
throw createTaggedError(
|
|
25
|
+
"HTTP_DISALLOWED_HOST",
|
|
26
|
+
`Host "${hostname}" is not in the allowed hosts list`,
|
|
27
|
+
{ url, hostname, allowedHosts: options.allowedHosts }
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
if (isHostBlocked(hostname, options.blockedHosts)) {
|
|
31
|
+
throw createTaggedError(
|
|
32
|
+
"HTTP_DISALLOWED_HOST",
|
|
33
|
+
`Host "${hostname}" is in the blocked hosts list`,
|
|
34
|
+
{ url, hostname, blockedHosts: options.blockedHosts }
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const { address } = await lookup(hostname);
|
|
39
|
+
if (isIpInBlockedCidrs(address, options.blockedCidrs)) {
|
|
40
|
+
throw createTaggedError(
|
|
41
|
+
"HTTP_DISALLOWED_HOST",
|
|
42
|
+
`Host "${hostname}" resolves to blocked IP: ${address}`,
|
|
43
|
+
{ url, hostname, resolvedIp: address }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (err instanceof Error && err.kind === "HTTP_DISALLOWED_HOST") {
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
throw createTaggedError(
|
|
51
|
+
"HTTP_DISALLOWED_HOST",
|
|
52
|
+
`DNS resolution failed for host "${hostname}": ${err instanceof Error ? err.message : String(err)}`,
|
|
53
|
+
{ url, hostname }
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
function isHostAllowed(hostname, allowedHosts) {
|
|
59
|
+
for (const pattern of allowedHosts) {
|
|
60
|
+
if (pattern === "*") {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (pattern.startsWith("*.")) {
|
|
64
|
+
const suffix = pattern.slice(1);
|
|
65
|
+
if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
} else if (hostname === pattern) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
function isHostBlocked(hostname, blockedHosts) {
|
|
75
|
+
for (const pattern of blockedHosts) {
|
|
76
|
+
if (pattern === "*") {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
if (pattern.startsWith("*.")) {
|
|
80
|
+
const suffix = pattern.slice(1);
|
|
81
|
+
if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
} else if (hostname === pattern) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
function isIpInBlockedCidrs(ip, cidrs) {
|
|
91
|
+
const normalizedIp = normalizeIp(ip);
|
|
92
|
+
if (!normalizedIp) return false;
|
|
93
|
+
for (const cidr of cidrs) {
|
|
94
|
+
if (cidr.includes(":")) {
|
|
95
|
+
if (!ip.includes(":")) continue;
|
|
96
|
+
if (isIpv6InCidr(ip, cidr)) return true;
|
|
97
|
+
} else {
|
|
98
|
+
if (isIpv4InCidr(normalizedIp, cidr)) return true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
function normalizeIp(ip) {
|
|
104
|
+
if (ip.startsWith("::ffff:")) {
|
|
105
|
+
return ip.slice(7);
|
|
106
|
+
}
|
|
107
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(ip)) {
|
|
108
|
+
return ip;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
function isIpv4InCidr(ip, cidr) {
|
|
113
|
+
const [cidrIp, prefixStr] = cidr.split("/");
|
|
114
|
+
if (!cidrIp || !prefixStr) return false;
|
|
115
|
+
const prefix = parseInt(prefixStr, 10);
|
|
116
|
+
if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;
|
|
117
|
+
const ipNum = ipv4ToNum(ip);
|
|
118
|
+
const cidrNum = ipv4ToNum(cidrIp);
|
|
119
|
+
if (ipNum === null || cidrNum === null) return false;
|
|
120
|
+
const mask = prefix === 0 ? 0 : -1 << 32 - prefix >>> 0;
|
|
121
|
+
return (ipNum & mask) === (cidrNum & mask);
|
|
122
|
+
}
|
|
123
|
+
function ipv4ToNum(ip) {
|
|
124
|
+
const parts = ip.split(".");
|
|
125
|
+
if (parts.length !== 4) return null;
|
|
126
|
+
let num = 0;
|
|
127
|
+
for (const part of parts) {
|
|
128
|
+
const n = parseInt(part, 10);
|
|
129
|
+
if (isNaN(n) || n < 0 || n > 255) return null;
|
|
130
|
+
num = num << 8 | n;
|
|
131
|
+
}
|
|
132
|
+
return num >>> 0;
|
|
133
|
+
}
|
|
134
|
+
function isIpv6InCidr(ip, cidr) {
|
|
135
|
+
const [cidrIp, prefixStr] = cidr.split("/");
|
|
136
|
+
if (!cidrIp || !prefixStr) return false;
|
|
137
|
+
const prefix = parseInt(prefixStr, 10);
|
|
138
|
+
if (isNaN(prefix)) return false;
|
|
139
|
+
const ipBytes = expandIpv6(ip);
|
|
140
|
+
const cidrBytes = expandIpv6(cidrIp);
|
|
141
|
+
if (!ipBytes || !cidrBytes) return false;
|
|
142
|
+
const fullBytes = Math.floor(prefix / 8);
|
|
143
|
+
for (let i = 0; i < fullBytes && i < 16; i++) {
|
|
144
|
+
if (ipBytes[i] !== cidrBytes[i]) return false;
|
|
145
|
+
}
|
|
146
|
+
const remainingBits = prefix % 8;
|
|
147
|
+
if (remainingBits > 0 && fullBytes < 16) {
|
|
148
|
+
const mask = -1 << 8 - remainingBits & 255;
|
|
149
|
+
if ((ipBytes[fullBytes] & mask) !== (cidrBytes[fullBytes] & mask)) return false;
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
function expandIpv6(ip) {
|
|
154
|
+
const zoneIdx = ip.indexOf("%");
|
|
155
|
+
if (zoneIdx !== -1) ip = ip.slice(0, zoneIdx);
|
|
156
|
+
const parts = ip.split("::");
|
|
157
|
+
if (parts.length > 2) return null;
|
|
158
|
+
const bytes = new Array(16).fill(0);
|
|
159
|
+
const expandGroup = (group) => {
|
|
160
|
+
if (!group) return [];
|
|
161
|
+
return group.split(":").flatMap((hex) => {
|
|
162
|
+
const val = parseInt(hex || "0", 16);
|
|
163
|
+
return [val >> 8 & 255, val & 255];
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
if (parts.length === 1) {
|
|
167
|
+
const expanded = expandGroup(parts[0]);
|
|
168
|
+
if (expanded.length !== 16) return null;
|
|
169
|
+
return expanded;
|
|
170
|
+
}
|
|
171
|
+
const left = expandGroup(parts[0]);
|
|
172
|
+
const right = expandGroup(parts[1]);
|
|
173
|
+
if (left.length + right.length > 16) return null;
|
|
174
|
+
for (let i = 0; i < left.length; i++) bytes[i] = left[i];
|
|
175
|
+
for (let i = 0; i < right.length; i++) bytes[16 - right.length + i] = right[i];
|
|
176
|
+
return bytes;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export { isIpInBlockedCidrs, validateUrl };
|
|
180
|
+
//# sourceMappingURL=chunk-NKYFYALQ.js.map
|
|
181
|
+
//# sourceMappingURL=chunk-NKYFYALQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/security/ssrf.ts"],"names":[],"mappings":"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","file":"chunk-NKYFYALQ.js","sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { createTaggedError } from \"../core/runtime/Retry.js\";\n\n/**\n * Options for validateUrl. Unified rule: allow iff host is in allowedHosts AND not in blockedHosts.\n * - \"Default allow all + blocklist\": allowedHosts: [\"*\"], blockedHosts: [\"*.internal\", ...]\n * - \"Default disallow all + allowlist\": allowedHosts: [\"api.github.com\", ...], blockedHosts: []\n */\nexport interface ValidateUrlOptions {\n /** Allow only these hosts. Use [\"*\"] for allow-all. Supports \"*.example.com\", exact host. */\n allowedHosts: string[];\n /** Block these hosts even if allowed. Supports \"*.internal\", exact host. Merged with allowlist. */\n blockedHosts: string[];\n /** CIDR ranges to block (resolved IP). */\n blockedCidrs: string[];\n}\n\n/**\n * Validate a URL: allow iff (host in allowedHosts) AND (host not in blockedHosts). Then check blockedCidrs on resolved IP.\n *\n * @throws HTTP_DISALLOWED_HOST if the URL is blocked\n */\nexport async function validateUrl(url: string, options: ValidateUrlOptions): Promise<URL> {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Invalid URL: ${url}`,\n { url },\n );\n }\n\n // Only allow http/https\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Protocol not allowed: ${parsed.protocol}. Only http: and https: are supported.`,\n { url, protocol: parsed.protocol },\n );\n }\n\n const hostname = parsed.hostname;\n\n if (!isHostAllowed(hostname, options.allowedHosts)) {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Host \"${hostname}\" is not in the allowed hosts list`,\n { url, hostname, allowedHosts: options.allowedHosts },\n );\n }\n if (isHostBlocked(hostname, options.blockedHosts)) {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Host \"${hostname}\" is in the blocked hosts list`,\n { url, hostname, blockedHosts: options.blockedHosts },\n );\n }\n\n // DNS resolve and check against blocked CIDRs\n try {\n const { address } = await lookup(hostname);\n if (isIpInBlockedCidrs(address, options.blockedCidrs)) {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Host \"${hostname}\" resolves to blocked IP: ${address}`,\n { url, hostname, resolvedIp: address },\n );\n }\n } catch (err) {\n // Re-throw our tagged errors\n if (err instanceof Error && (err as any).kind === \"HTTP_DISALLOWED_HOST\") {\n throw err;\n }\n // DNS resolution failure — block by default\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `DNS resolution failed for host \"${hostname}\": ${err instanceof Error ? err.message : String(err)}`,\n { url, hostname },\n );\n }\n\n return parsed;\n}\n\n/**\n * Check if a hostname matches any entry in the allowed hosts list.\n * Supports: exact \"*\" (allow any host), wildcard prefix (e.g. \"*.github.com\"), or exact host.\n */\nfunction isHostAllowed(hostname: string, allowedHosts: string[]): boolean {\n for (const pattern of allowedHosts) {\n if (pattern === \"*\") {\n return true;\n }\n if (pattern.startsWith(\"*.\")) {\n const suffix = pattern.slice(1); // \".github.com\"\n if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {\n return true;\n }\n } else if (hostname === pattern) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a hostname matches any entry in the blocked hosts list (same pattern rules as allowlist).\n */\nfunction isHostBlocked(hostname: string, blockedHosts: string[]): boolean {\n for (const pattern of blockedHosts) {\n if (pattern === \"*\") {\n return true;\n }\n if (pattern.startsWith(\"*.\")) {\n const suffix = pattern.slice(1);\n if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {\n return true;\n }\n } else if (hostname === pattern) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if an IPv4 address falls within any blocked CIDR range.\n */\nexport function isIpInBlockedCidrs(ip: string, cidrs: string[]): boolean {\n // Handle IPv4-mapped IPv6\n const normalizedIp = normalizeIp(ip);\n if (!normalizedIp) return false;\n\n for (const cidr of cidrs) {\n if (cidr.includes(\":\")) {\n // IPv6 CIDR — skip for IPv4 addresses\n if (!ip.includes(\":\")) continue;\n if (isIpv6InCidr(ip, cidr)) return true;\n } else {\n if (isIpv4InCidr(normalizedIp, cidr)) return true;\n }\n }\n return false;\n}\n\nfunction normalizeIp(ip: string): string | null {\n // Handle IPv4-mapped IPv6 (e.g. \"::ffff:127.0.0.1\")\n if (ip.startsWith(\"::ffff:\")) {\n return ip.slice(7);\n }\n // Pure IPv4\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(ip)) {\n return ip;\n }\n return null;\n}\n\nfunction isIpv4InCidr(ip: string, cidr: string): boolean {\n const [cidrIp, prefixStr] = cidr.split(\"/\");\n if (!cidrIp || !prefixStr) return false;\n\n const prefix = parseInt(prefixStr, 10);\n if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;\n\n const ipNum = ipv4ToNum(ip);\n const cidrNum = ipv4ToNum(cidrIp);\n if (ipNum === null || cidrNum === null) return false;\n\n const mask = prefix === 0 ? 0 : (~0 << (32 - prefix)) >>> 0;\n return (ipNum & mask) === (cidrNum & mask);\n}\n\nfunction ipv4ToNum(ip: string): number | null {\n const parts = ip.split(\".\");\n if (parts.length !== 4) return null;\n let num = 0;\n for (const part of parts) {\n const n = parseInt(part, 10);\n if (isNaN(n) || n < 0 || n > 255) return null;\n num = (num << 8) | n;\n }\n return num >>> 0;\n}\n\nfunction isIpv6InCidr(ip: string, cidr: string): boolean {\n // Simplified IPv6 CIDR matching for common cases (::1, fc00::, fe80::)\n const [cidrIp, prefixStr] = cidr.split(\"/\");\n if (!cidrIp || !prefixStr) return false;\n\n const prefix = parseInt(prefixStr, 10);\n if (isNaN(prefix)) return false;\n\n const ipBytes = expandIpv6(ip);\n const cidrBytes = expandIpv6(cidrIp);\n if (!ipBytes || !cidrBytes) return false;\n\n // Compare prefix bits\n const fullBytes = Math.floor(prefix / 8);\n for (let i = 0; i < fullBytes && i < 16; i++) {\n if (ipBytes[i] !== cidrBytes[i]) return false;\n }\n\n const remainingBits = prefix % 8;\n if (remainingBits > 0 && fullBytes < 16) {\n const mask = (~0 << (8 - remainingBits)) & 0xff;\n if ((ipBytes[fullBytes]! & mask) !== (cidrBytes[fullBytes]! & mask)) return false;\n }\n\n return true;\n}\n\nfunction expandIpv6(ip: string): number[] | null {\n // Remove zone ID\n const zoneIdx = ip.indexOf(\"%\");\n if (zoneIdx !== -1) ip = ip.slice(0, zoneIdx);\n\n const parts = ip.split(\"::\");\n if (parts.length > 2) return null;\n\n const bytes: number[] = new Array(16).fill(0);\n\n const expandGroup = (group: string): number[] => {\n if (!group) return [];\n return group.split(\":\").flatMap((hex) => {\n const val = parseInt(hex || \"0\", 16);\n return [(val >> 8) & 0xff, val & 0xff];\n });\n };\n\n if (parts.length === 1) {\n const expanded = expandGroup(parts[0]!);\n if (expanded.length !== 16) return null;\n return expanded;\n }\n\n const left = expandGroup(parts[0]!);\n const right = expandGroup(parts[1]!);\n\n if (left.length + right.length > 16) return null;\n\n for (let i = 0; i < left.length; i++) bytes[i] = left[i]!;\n for (let i = 0; i < right.length; i++) bytes[16 - right.length + i] = right[i]!;\n\n return bytes;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-NOGGIM7B.cjs"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkXPGHS4W7_cjs = require('./chunk-XPGHS4W7.cjs');
|
|
4
|
+
var promises = require('dns/promises');
|
|
5
|
+
|
|
6
|
+
async function validateUrl(url, options) {
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = new URL(url);
|
|
10
|
+
} catch {
|
|
11
|
+
throw chunkXPGHS4W7_cjs.createTaggedError(
|
|
12
|
+
"HTTP_DISALLOWED_HOST",
|
|
13
|
+
`Invalid URL: ${url}`,
|
|
14
|
+
{ url }
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
18
|
+
throw chunkXPGHS4W7_cjs.createTaggedError(
|
|
19
|
+
"HTTP_DISALLOWED_HOST",
|
|
20
|
+
`Protocol not allowed: ${parsed.protocol}. Only http: and https: are supported.`,
|
|
21
|
+
{ url, protocol: parsed.protocol }
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
const hostname = parsed.hostname;
|
|
25
|
+
if (!isHostAllowed(hostname, options.allowedHosts)) {
|
|
26
|
+
throw chunkXPGHS4W7_cjs.createTaggedError(
|
|
27
|
+
"HTTP_DISALLOWED_HOST",
|
|
28
|
+
`Host "${hostname}" is not in the allowed hosts list`,
|
|
29
|
+
{ url, hostname, allowedHosts: options.allowedHosts }
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (isHostBlocked(hostname, options.blockedHosts)) {
|
|
33
|
+
throw chunkXPGHS4W7_cjs.createTaggedError(
|
|
34
|
+
"HTTP_DISALLOWED_HOST",
|
|
35
|
+
`Host "${hostname}" is in the blocked hosts list`,
|
|
36
|
+
{ url, hostname, blockedHosts: options.blockedHosts }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const { address } = await promises.lookup(hostname);
|
|
41
|
+
if (isIpInBlockedCidrs(address, options.blockedCidrs)) {
|
|
42
|
+
throw chunkXPGHS4W7_cjs.createTaggedError(
|
|
43
|
+
"HTTP_DISALLOWED_HOST",
|
|
44
|
+
`Host "${hostname}" resolves to blocked IP: ${address}`,
|
|
45
|
+
{ url, hostname, resolvedIp: address }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (err instanceof Error && err.kind === "HTTP_DISALLOWED_HOST") {
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
throw chunkXPGHS4W7_cjs.createTaggedError(
|
|
53
|
+
"HTTP_DISALLOWED_HOST",
|
|
54
|
+
`DNS resolution failed for host "${hostname}": ${err instanceof Error ? err.message : String(err)}`,
|
|
55
|
+
{ url, hostname }
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return parsed;
|
|
59
|
+
}
|
|
60
|
+
function isHostAllowed(hostname, allowedHosts) {
|
|
61
|
+
for (const pattern of allowedHosts) {
|
|
62
|
+
if (pattern === "*") {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (pattern.startsWith("*.")) {
|
|
66
|
+
const suffix = pattern.slice(1);
|
|
67
|
+
if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
} else if (hostname === pattern) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
function isHostBlocked(hostname, blockedHosts) {
|
|
77
|
+
for (const pattern of blockedHosts) {
|
|
78
|
+
if (pattern === "*") {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
if (pattern.startsWith("*.")) {
|
|
82
|
+
const suffix = pattern.slice(1);
|
|
83
|
+
if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
} else if (hostname === pattern) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
function isIpInBlockedCidrs(ip, cidrs) {
|
|
93
|
+
const normalizedIp = normalizeIp(ip);
|
|
94
|
+
if (!normalizedIp) return false;
|
|
95
|
+
for (const cidr of cidrs) {
|
|
96
|
+
if (cidr.includes(":")) {
|
|
97
|
+
if (!ip.includes(":")) continue;
|
|
98
|
+
if (isIpv6InCidr(ip, cidr)) return true;
|
|
99
|
+
} else {
|
|
100
|
+
if (isIpv4InCidr(normalizedIp, cidr)) return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
function normalizeIp(ip) {
|
|
106
|
+
if (ip.startsWith("::ffff:")) {
|
|
107
|
+
return ip.slice(7);
|
|
108
|
+
}
|
|
109
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(ip)) {
|
|
110
|
+
return ip;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
function isIpv4InCidr(ip, cidr) {
|
|
115
|
+
const [cidrIp, prefixStr] = cidr.split("/");
|
|
116
|
+
if (!cidrIp || !prefixStr) return false;
|
|
117
|
+
const prefix = parseInt(prefixStr, 10);
|
|
118
|
+
if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;
|
|
119
|
+
const ipNum = ipv4ToNum(ip);
|
|
120
|
+
const cidrNum = ipv4ToNum(cidrIp);
|
|
121
|
+
if (ipNum === null || cidrNum === null) return false;
|
|
122
|
+
const mask = prefix === 0 ? 0 : -1 << 32 - prefix >>> 0;
|
|
123
|
+
return (ipNum & mask) === (cidrNum & mask);
|
|
124
|
+
}
|
|
125
|
+
function ipv4ToNum(ip) {
|
|
126
|
+
const parts = ip.split(".");
|
|
127
|
+
if (parts.length !== 4) return null;
|
|
128
|
+
let num = 0;
|
|
129
|
+
for (const part of parts) {
|
|
130
|
+
const n = parseInt(part, 10);
|
|
131
|
+
if (isNaN(n) || n < 0 || n > 255) return null;
|
|
132
|
+
num = num << 8 | n;
|
|
133
|
+
}
|
|
134
|
+
return num >>> 0;
|
|
135
|
+
}
|
|
136
|
+
function isIpv6InCidr(ip, cidr) {
|
|
137
|
+
const [cidrIp, prefixStr] = cidr.split("/");
|
|
138
|
+
if (!cidrIp || !prefixStr) return false;
|
|
139
|
+
const prefix = parseInt(prefixStr, 10);
|
|
140
|
+
if (isNaN(prefix)) return false;
|
|
141
|
+
const ipBytes = expandIpv6(ip);
|
|
142
|
+
const cidrBytes = expandIpv6(cidrIp);
|
|
143
|
+
if (!ipBytes || !cidrBytes) return false;
|
|
144
|
+
const fullBytes = Math.floor(prefix / 8);
|
|
145
|
+
for (let i = 0; i < fullBytes && i < 16; i++) {
|
|
146
|
+
if (ipBytes[i] !== cidrBytes[i]) return false;
|
|
147
|
+
}
|
|
148
|
+
const remainingBits = prefix % 8;
|
|
149
|
+
if (remainingBits > 0 && fullBytes < 16) {
|
|
150
|
+
const mask = -1 << 8 - remainingBits & 255;
|
|
151
|
+
if ((ipBytes[fullBytes] & mask) !== (cidrBytes[fullBytes] & mask)) return false;
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
function expandIpv6(ip) {
|
|
156
|
+
const zoneIdx = ip.indexOf("%");
|
|
157
|
+
if (zoneIdx !== -1) ip = ip.slice(0, zoneIdx);
|
|
158
|
+
const parts = ip.split("::");
|
|
159
|
+
if (parts.length > 2) return null;
|
|
160
|
+
const bytes = new Array(16).fill(0);
|
|
161
|
+
const expandGroup = (group) => {
|
|
162
|
+
if (!group) return [];
|
|
163
|
+
return group.split(":").flatMap((hex) => {
|
|
164
|
+
const val = parseInt(hex || "0", 16);
|
|
165
|
+
return [val >> 8 & 255, val & 255];
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
if (parts.length === 1) {
|
|
169
|
+
const expanded = expandGroup(parts[0]);
|
|
170
|
+
if (expanded.length !== 16) return null;
|
|
171
|
+
return expanded;
|
|
172
|
+
}
|
|
173
|
+
const left = expandGroup(parts[0]);
|
|
174
|
+
const right = expandGroup(parts[1]);
|
|
175
|
+
if (left.length + right.length > 16) return null;
|
|
176
|
+
for (let i = 0; i < left.length; i++) bytes[i] = left[i];
|
|
177
|
+
for (let i = 0; i < right.length; i++) bytes[16 - right.length + i] = right[i];
|
|
178
|
+
return bytes;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
exports.isIpInBlockedCidrs = isIpInBlockedCidrs;
|
|
182
|
+
exports.validateUrl = validateUrl;
|
|
183
|
+
//# sourceMappingURL=chunk-R55NXJIH.cjs.map
|
|
184
|
+
//# sourceMappingURL=chunk-R55NXJIH.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/security/ssrf.ts"],"names":["createTaggedError","lookup"],"mappings":";;;;;AAsBA,eAAsB,WAAA,CAAY,KAAa,OAAA,EAA2C;AACxF,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAA,GAAS,IAAI,IAAI,GAAG,CAAA;AAAA,EACtB,CAAA,CAAA,MAAQ;AACN,IAAA,MAAMA,mCAAA;AAAA,MACJ,sBAAA;AAAA,MACA,gBAAgB,GAAG,CAAA,CAAA;AAAA,MACnB,EAAE,GAAA;AAAI,KACR;AAAA,EACF;AAGA,EAAA,IAAI,MAAA,CAAO,QAAA,KAAa,OAAA,IAAW,MAAA,CAAO,aAAa,QAAA,EAAU;AAC/D,IAAA,MAAMA,mCAAA;AAAA,MACJ,sBAAA;AAAA,MACA,CAAA,sBAAA,EAAyB,OAAO,QAAQ,CAAA,sCAAA,CAAA;AAAA,MACxC,EAAE,GAAA,EAAK,QAAA,EAAU,MAAA,CAAO,QAAA;AAAS,KACnC;AAAA,EACF;AAEA,EAAA,MAAM,WAAW,MAAA,CAAO,QAAA;AAExB,EAAA,IAAI,CAAC,aAAA,CAAc,QAAA,EAAU,OAAA,CAAQ,YAAY,CAAA,EAAG;AAClD,IAAA,MAAMA,mCAAA;AAAA,MACJ,sBAAA;AAAA,MACA,SAAS,QAAQ,CAAA,kCAAA,CAAA;AAAA,MACjB,EAAE,GAAA,EAAK,QAAA,EAAU,YAAA,EAAc,QAAQ,YAAA;AAAa,KACtD;AAAA,EACF;AACA,EAAA,IAAI,aAAA,CAAc,QAAA,EAAU,OAAA,CAAQ,YAAY,CAAA,EAAG;AACjD,IAAA,MAAMA,mCAAA;AAAA,MACJ,sBAAA;AAAA,MACA,SAAS,QAAQ,CAAA,8BAAA,CAAA;AAAA,MACjB,EAAE,GAAA,EAAK,QAAA,EAAU,YAAA,EAAc,QAAQ,YAAA;AAAa,KACtD;AAAA,EACF;AAGA,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,MAAMC,gBAAO,QAAQ,CAAA;AACzC,IAAA,IAAI,kBAAA,CAAmB,OAAA,EAAS,OAAA,CAAQ,YAAY,CAAA,EAAG;AACrD,MAAA,MAAMD,mCAAA;AAAA,QACJ,sBAAA;AAAA,QACA,CAAA,MAAA,EAAS,QAAQ,CAAA,0BAAA,EAA6B,OAAO,CAAA,CAAA;AAAA,QACrD,EAAE,GAAA,EAAK,QAAA,EAAU,UAAA,EAAY,OAAA;AAAQ,OACvC;AAAA,IACF;AAAA,EACF,SAAS,GAAA,EAAK;AAEZ,IAAA,IAAI,GAAA,YAAe,KAAA,IAAU,GAAA,CAAY,IAAA,KAAS,sBAAA,EAAwB;AACxE,MAAA,MAAM,GAAA;AAAA,IACR;AAEA,IAAA,MAAMA,mCAAA;AAAA,MACJ,sBAAA;AAAA,MACA,CAAA,gCAAA,EAAmC,QAAQ,CAAA,GAAA,EAAM,GAAA,YAAe,QAAQ,GAAA,CAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAC,CAAA,CAAA;AAAA,MACjG,EAAE,KAAK,QAAA;AAAS,KAClB;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAMA,SAAS,aAAA,CAAc,UAAkB,YAAA,EAAiC;AACxE,EAAA,KAAA,MAAW,WAAW,YAAA,EAAc;AAClC,IAAA,IAAI,YAAY,GAAA,EAAK;AACnB,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,IAAI,CAAA,EAAG;AAC5B,MAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA;AAC9B,MAAA,IAAI,QAAA,CAAS,SAAS,MAAM,CAAA,IAAK,aAAa,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA,EAAG;AAC9D,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,IACF,CAAA,MAAA,IAAW,aAAa,OAAA,EAAS;AAC/B,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAKA,SAAS,aAAA,CAAc,UAAkB,YAAA,EAAiC;AACxE,EAAA,KAAA,MAAW,WAAW,YAAA,EAAc;AAClC,IAAA,IAAI,YAAY,GAAA,EAAK;AACnB,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,IAAI,CAAA,EAAG;AAC5B,MAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA;AAC9B,MAAA,IAAI,QAAA,CAAS,SAAS,MAAM,CAAA,IAAK,aAAa,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA,EAAG;AAC9D,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,IACF,CAAA,MAAA,IAAW,aAAa,OAAA,EAAS;AAC/B,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAKO,SAAS,kBAAA,CAAmB,IAAY,KAAA,EAA0B;AAEvE,EAAA,MAAM,YAAA,GAAe,YAAY,EAAE,CAAA;AACnC,EAAA,IAAI,CAAC,cAAc,OAAO,KAAA;AAE1B,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AAEtB,MAAA,IAAI,CAAC,EAAA,CAAG,QAAA,CAAS,GAAG,CAAA,EAAG;AACvB,MAAA,IAAI,YAAA,CAAa,EAAA,EAAI,IAAI,CAAA,EAAG,OAAO,IAAA;AAAA,IACrC,CAAA,MAAO;AACL,MAAA,IAAI,YAAA,CAAa,YAAA,EAAc,IAAI,CAAA,EAAG,OAAO,IAAA;AAAA,IAC/C;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,YAAY,EAAA,EAA2B;AAE9C,EAAA,IAAI,EAAA,CAAG,UAAA,CAAW,SAAS,CAAA,EAAG;AAC5B,IAAA,OAAO,EAAA,CAAG,MAAM,CAAC,CAAA;AAAA,EACnB;AAEA,EAAA,IAAI,sBAAA,CAAuB,IAAA,CAAK,EAAE,CAAA,EAAG;AACnC,IAAA,OAAO,EAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,YAAA,CAAa,IAAY,IAAA,EAAuB;AACvD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AAC1C,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,SAAA,EAAW,OAAO,KAAA;AAElC,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,SAAA,EAAW,EAAE,CAAA;AACrC,EAAA,IAAI,MAAM,MAAM,CAAA,IAAK,SAAS,CAAA,IAAK,MAAA,GAAS,IAAI,OAAO,KAAA;AAEvD,EAAA,MAAM,KAAA,GAAQ,UAAU,EAAE,CAAA;AAC1B,EAAA,MAAM,OAAA,GAAU,UAAU,MAAM,CAAA;AAChC,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,OAAA,KAAY,IAAA,EAAM,OAAO,KAAA;AAE/C,EAAA,MAAM,OAAO,MAAA,KAAW,CAAA,GAAI,IAAK,EAAC,IAAM,KAAK,MAAA,KAAa,CAAA;AAC1D,EAAA,OAAA,CAAQ,KAAA,GAAQ,WAAW,OAAA,GAAU,IAAA,CAAA;AACvC;AAEA,SAAS,UAAU,EAAA,EAA2B;AAC5C,EAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,KAAA,CAAM,GAAG,CAAA;AAC1B,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAC/B,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,CAAA,GAAI,QAAA,CAAS,IAAA,EAAM,EAAE,CAAA;AAC3B,IAAA,IAAI,MAAM,CAAC,CAAA,IAAK,IAAI,CAAA,IAAK,CAAA,GAAI,KAAK,OAAO,IAAA;AACzC,IAAA,GAAA,GAAO,OAAO,CAAA,GAAK,CAAA;AAAA,EACrB;AACA,EAAA,OAAO,GAAA,KAAQ,CAAA;AACjB;AAEA,SAAS,YAAA,CAAa,IAAY,IAAA,EAAuB;AAEvD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AAC1C,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,SAAA,EAAW,OAAO,KAAA;AAElC,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,SAAA,EAAW,EAAE,CAAA;AACrC,EAAA,IAAI,KAAA,CAAM,MAAM,CAAA,EAAG,OAAO,KAAA;AAE1B,EAAA,MAAM,OAAA,GAAU,WAAW,EAAE,CAAA;AAC7B,EAAA,MAAM,SAAA,GAAY,WAAW,MAAM,CAAA;AACnC,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,SAAA,EAAW,OAAO,KAAA;AAGnC,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA;AACvC,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,SAAA,IAAa,CAAA,GAAI,IAAI,CAAA,EAAA,EAAK;AAC5C,IAAA,IAAI,QAAQ,CAAC,CAAA,KAAM,SAAA,CAAU,CAAC,GAAG,OAAO,KAAA;AAAA,EAC1C;AAEA,EAAA,MAAM,gBAAgB,MAAA,GAAS,CAAA;AAC/B,EAAA,IAAI,aAAA,GAAgB,CAAA,IAAK,SAAA,GAAY,EAAA,EAAI;AACvC,IAAA,MAAM,IAAA,GAAQ,EAAC,IAAM,CAAA,GAAI,aAAA,GAAkB,GAAA;AAC3C,IAAA,IAAA,CAAK,OAAA,CAAQ,SAAS,CAAA,GAAK,IAAA,OAAW,UAAU,SAAS,CAAA,GAAK,OAAO,OAAO,KAAA;AAAA,EAC9E;AAEA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,WAAW,EAAA,EAA6B;AAE/C,EAAA,MAAM,OAAA,GAAU,EAAA,CAAG,OAAA,CAAQ,GAAG,CAAA;AAC9B,EAAA,IAAI,YAAY,EAAA,EAAI,EAAA,GAAK,EAAA,CAAG,KAAA,CAAM,GAAG,OAAO,CAAA;AAE5C,EAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,KAAA,CAAM,IAAI,CAAA;AAC3B,EAAA,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,OAAO,IAAA;AAE7B,EAAA,MAAM,QAAkB,IAAI,KAAA,CAAM,EAAE,CAAA,CAAE,KAAK,CAAC,CAAA;AAE5C,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAA4B;AAC/C,IAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAC;AACpB,IAAA,OAAO,MAAM,KAAA,CAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,CAAC,GAAA,KAAQ;AACvC,MAAA,MAAM,GAAA,GAAM,QAAA,CAAS,GAAA,IAAO,GAAA,EAAK,EAAE,CAAA;AACnC,MAAA,OAAO,CAAE,GAAA,IAAO,CAAA,GAAK,GAAA,EAAM,MAAM,GAAI,CAAA;AAAA,IACvC,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,IAAA,MAAM,QAAA,GAAW,WAAA,CAAY,KAAA,CAAM,CAAC,CAAE,CAAA;AACtC,IAAA,IAAI,QAAA,CAAS,MAAA,KAAW,EAAA,EAAI,OAAO,IAAA;AACnC,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,KAAA,CAAM,CAAC,CAAE,CAAA;AAClC,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,KAAA,CAAM,CAAC,CAAE,CAAA;AAEnC,EAAA,IAAI,IAAA,CAAK,MAAA,GAAS,KAAA,CAAM,MAAA,GAAS,IAAI,OAAO,IAAA;AAE5C,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,MAAA,EAAQ,KAAK,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA;AACvD,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,EAAQ,CAAA,EAAA,EAAK,KAAA,CAAM,EAAA,GAAK,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,GAAI,MAAM,CAAC,CAAA;AAE7E,EAAA,OAAO,KAAA;AACT","file":"chunk-R55NXJIH.cjs","sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { createTaggedError } from \"../core/runtime/Retry.js\";\n\n/**\n * Options for validateUrl. Unified rule: allow iff host is in allowedHosts AND not in blockedHosts.\n * - \"Default allow all + blocklist\": allowedHosts: [\"*\"], blockedHosts: [\"*.internal\", ...]\n * - \"Default disallow all + allowlist\": allowedHosts: [\"api.github.com\", ...], blockedHosts: []\n */\nexport interface ValidateUrlOptions {\n /** Allow only these hosts. Use [\"*\"] for allow-all. Supports \"*.example.com\", exact host. */\n allowedHosts: string[];\n /** Block these hosts even if allowed. Supports \"*.internal\", exact host. Merged with allowlist. */\n blockedHosts: string[];\n /** CIDR ranges to block (resolved IP). */\n blockedCidrs: string[];\n}\n\n/**\n * Validate a URL: allow iff (host in allowedHosts) AND (host not in blockedHosts). Then check blockedCidrs on resolved IP.\n *\n * @throws HTTP_DISALLOWED_HOST if the URL is blocked\n */\nexport async function validateUrl(url: string, options: ValidateUrlOptions): Promise<URL> {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Invalid URL: ${url}`,\n { url },\n );\n }\n\n // Only allow http/https\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Protocol not allowed: ${parsed.protocol}. Only http: and https: are supported.`,\n { url, protocol: parsed.protocol },\n );\n }\n\n const hostname = parsed.hostname;\n\n if (!isHostAllowed(hostname, options.allowedHosts)) {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Host \"${hostname}\" is not in the allowed hosts list`,\n { url, hostname, allowedHosts: options.allowedHosts },\n );\n }\n if (isHostBlocked(hostname, options.blockedHosts)) {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Host \"${hostname}\" is in the blocked hosts list`,\n { url, hostname, blockedHosts: options.blockedHosts },\n );\n }\n\n // DNS resolve and check against blocked CIDRs\n try {\n const { address } = await lookup(hostname);\n if (isIpInBlockedCidrs(address, options.blockedCidrs)) {\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `Host \"${hostname}\" resolves to blocked IP: ${address}`,\n { url, hostname, resolvedIp: address },\n );\n }\n } catch (err) {\n // Re-throw our tagged errors\n if (err instanceof Error && (err as any).kind === \"HTTP_DISALLOWED_HOST\") {\n throw err;\n }\n // DNS resolution failure — block by default\n throw createTaggedError(\n \"HTTP_DISALLOWED_HOST\",\n `DNS resolution failed for host \"${hostname}\": ${err instanceof Error ? err.message : String(err)}`,\n { url, hostname },\n );\n }\n\n return parsed;\n}\n\n/**\n * Check if a hostname matches any entry in the allowed hosts list.\n * Supports: exact \"*\" (allow any host), wildcard prefix (e.g. \"*.github.com\"), or exact host.\n */\nfunction isHostAllowed(hostname: string, allowedHosts: string[]): boolean {\n for (const pattern of allowedHosts) {\n if (pattern === \"*\") {\n return true;\n }\n if (pattern.startsWith(\"*.\")) {\n const suffix = pattern.slice(1); // \".github.com\"\n if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {\n return true;\n }\n } else if (hostname === pattern) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a hostname matches any entry in the blocked hosts list (same pattern rules as allowlist).\n */\nfunction isHostBlocked(hostname: string, blockedHosts: string[]): boolean {\n for (const pattern of blockedHosts) {\n if (pattern === \"*\") {\n return true;\n }\n if (pattern.startsWith(\"*.\")) {\n const suffix = pattern.slice(1);\n if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {\n return true;\n }\n } else if (hostname === pattern) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if an IPv4 address falls within any blocked CIDR range.\n */\nexport function isIpInBlockedCidrs(ip: string, cidrs: string[]): boolean {\n // Handle IPv4-mapped IPv6\n const normalizedIp = normalizeIp(ip);\n if (!normalizedIp) return false;\n\n for (const cidr of cidrs) {\n if (cidr.includes(\":\")) {\n // IPv6 CIDR — skip for IPv4 addresses\n if (!ip.includes(\":\")) continue;\n if (isIpv6InCidr(ip, cidr)) return true;\n } else {\n if (isIpv4InCidr(normalizedIp, cidr)) return true;\n }\n }\n return false;\n}\n\nfunction normalizeIp(ip: string): string | null {\n // Handle IPv4-mapped IPv6 (e.g. \"::ffff:127.0.0.1\")\n if (ip.startsWith(\"::ffff:\")) {\n return ip.slice(7);\n }\n // Pure IPv4\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(ip)) {\n return ip;\n }\n return null;\n}\n\nfunction isIpv4InCidr(ip: string, cidr: string): boolean {\n const [cidrIp, prefixStr] = cidr.split(\"/\");\n if (!cidrIp || !prefixStr) return false;\n\n const prefix = parseInt(prefixStr, 10);\n if (isNaN(prefix) || prefix < 0 || prefix > 32) return false;\n\n const ipNum = ipv4ToNum(ip);\n const cidrNum = ipv4ToNum(cidrIp);\n if (ipNum === null || cidrNum === null) return false;\n\n const mask = prefix === 0 ? 0 : (~0 << (32 - prefix)) >>> 0;\n return (ipNum & mask) === (cidrNum & mask);\n}\n\nfunction ipv4ToNum(ip: string): number | null {\n const parts = ip.split(\".\");\n if (parts.length !== 4) return null;\n let num = 0;\n for (const part of parts) {\n const n = parseInt(part, 10);\n if (isNaN(n) || n < 0 || n > 255) return null;\n num = (num << 8) | n;\n }\n return num >>> 0;\n}\n\nfunction isIpv6InCidr(ip: string, cidr: string): boolean {\n // Simplified IPv6 CIDR matching for common cases (::1, fc00::, fe80::)\n const [cidrIp, prefixStr] = cidr.split(\"/\");\n if (!cidrIp || !prefixStr) return false;\n\n const prefix = parseInt(prefixStr, 10);\n if (isNaN(prefix)) return false;\n\n const ipBytes = expandIpv6(ip);\n const cidrBytes = expandIpv6(cidrIp);\n if (!ipBytes || !cidrBytes) return false;\n\n // Compare prefix bits\n const fullBytes = Math.floor(prefix / 8);\n for (let i = 0; i < fullBytes && i < 16; i++) {\n if (ipBytes[i] !== cidrBytes[i]) return false;\n }\n\n const remainingBits = prefix % 8;\n if (remainingBits > 0 && fullBytes < 16) {\n const mask = (~0 << (8 - remainingBits)) & 0xff;\n if ((ipBytes[fullBytes]! & mask) !== (cidrBytes[fullBytes]! & mask)) return false;\n }\n\n return true;\n}\n\nfunction expandIpv6(ip: string): number[] | null {\n // Remove zone ID\n const zoneIdx = ip.indexOf(\"%\");\n if (zoneIdx !== -1) ip = ip.slice(0, zoneIdx);\n\n const parts = ip.split(\"::\");\n if (parts.length > 2) return null;\n\n const bytes: number[] = new Array(16).fill(0);\n\n const expandGroup = (group: string): number[] => {\n if (!group) return [];\n return group.split(\":\").flatMap((hex) => {\n const val = parseInt(hex || \"0\", 16);\n return [(val >> 8) & 0xff, val & 0xff];\n });\n };\n\n if (parts.length === 1) {\n const expanded = expandGroup(parts[0]!);\n if (expanded.length !== 16) return null;\n return expanded;\n }\n\n const left = expandGroup(parts[0]!);\n const right = expandGroup(parts[1]!);\n\n if (left.length + right.length > 16) return null;\n\n for (let i = 0; i < left.length; i++) bytes[i] = left[i]!;\n for (let i = 0; i < right.length; i++) bytes[16 - right.length + i] = right[i]!;\n\n return bytes;\n}\n"]}
|
|
@@ -1717,8 +1717,6 @@ function buildInputSchemaHint(inputSchema) {
|
|
|
1717
1717
|
if (names.length === 0) return null;
|
|
1718
1718
|
return `This tool expects input property ${names.length === 1 ? `'${names[0]}'` : `one of [${names.map((n) => `'${n}'`).join(", ")}]`}. Use the exact property names from the tool schema.`;
|
|
1719
1719
|
}
|
|
1720
|
-
|
|
1721
|
-
// src/api/runtimeFromConfig.ts
|
|
1722
1720
|
var requireFromPackage = createRequire(import.meta.url);
|
|
1723
1721
|
function getProjectRequire() {
|
|
1724
1722
|
const cwd = process.cwd();
|
|
@@ -1736,7 +1734,6 @@ function findNearestPackageJson(startDir) {
|
|
|
1736
1734
|
dir = parent;
|
|
1737
1735
|
}
|
|
1738
1736
|
}
|
|
1739
|
-
var DEFAULT_EXTENSION_PACKAGES = [];
|
|
1740
1737
|
function getInstalledPackageVersion(packageName) {
|
|
1741
1738
|
const projectRequire = getProjectRequire();
|
|
1742
1739
|
const requirers = [requireFromPackage];
|
|
@@ -1838,33 +1835,6 @@ function createLocalDirectoryAdapter(kind) {
|
|
|
1838
1835
|
}
|
|
1839
1836
|
};
|
|
1840
1837
|
}
|
|
1841
|
-
function resolveFileDescriptorPath(descriptor, configFilePath) {
|
|
1842
|
-
const parsed = parseToolPath(descriptor.trim());
|
|
1843
|
-
if (!parsed || parsed.protocol !== "file") return null;
|
|
1844
|
-
const localPath = isAbsolute(configFilePath) ? configFilePath : resolve(process.cwd(), configFilePath);
|
|
1845
|
-
const configDir = dirname(localPath);
|
|
1846
|
-
return resolve(configDir, `${parsed.scope}/${parsed.packageWithVersion}`);
|
|
1847
|
-
}
|
|
1848
|
-
function getRegisterFn(mod) {
|
|
1849
|
-
return mod?.register ?? mod?.registerCoreTools;
|
|
1850
|
-
}
|
|
1851
|
-
function loadExtensionFromNodeModules() {
|
|
1852
|
-
const projectRequire = getProjectRequire();
|
|
1853
|
-
const requirers = [requireFromPackage];
|
|
1854
|
-
if (projectRequire) requirers.push(projectRequire);
|
|
1855
|
-
for (const req of requirers) {
|
|
1856
|
-
for (const pkg of DEFAULT_EXTENSION_PACKAGES) {
|
|
1857
|
-
try {
|
|
1858
|
-
const mod = req(pkg);
|
|
1859
|
-
const fn = getRegisterFn(mod);
|
|
1860
|
-
if (typeof fn === "function") return { register: fn, packageName: pkg };
|
|
1861
|
-
} catch {
|
|
1862
|
-
continue;
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
}
|
|
1866
|
-
return null;
|
|
1867
|
-
}
|
|
1868
1838
|
function createPrefixingRegistry(registry, prefix) {
|
|
1869
1839
|
return {
|
|
1870
1840
|
register(spec) {
|
|
@@ -1911,6 +1881,37 @@ function parseNpmDescriptor(entry) {
|
|
|
1911
1881
|
const version = rest.slice(at + 1).split("#")[0]?.trim() || "latest";
|
|
1912
1882
|
return { packageName, version };
|
|
1913
1883
|
}
|
|
1884
|
+
|
|
1885
|
+
// src/api/runtimeFromConfig.ts
|
|
1886
|
+
var requireFromPackage2 = createRequire(import.meta.url);
|
|
1887
|
+
var DEFAULT_EXTENSION_PACKAGES = [];
|
|
1888
|
+
function resolveFileDescriptorPath(descriptor, configFilePath) {
|
|
1889
|
+
const parsed = parseToolPath(descriptor.trim());
|
|
1890
|
+
if (!parsed || parsed.protocol !== "file") return null;
|
|
1891
|
+
const localPath = isAbsolute(configFilePath) ? configFilePath : resolve(process.cwd(), configFilePath);
|
|
1892
|
+
const configDir = dirname(localPath);
|
|
1893
|
+
return resolve(configDir, `${parsed.scope}/${parsed.packageWithVersion}`);
|
|
1894
|
+
}
|
|
1895
|
+
function getRegisterFn(mod) {
|
|
1896
|
+
return mod?.register ?? mod?.registerCoreTools;
|
|
1897
|
+
}
|
|
1898
|
+
function loadExtensionFromNodeModules() {
|
|
1899
|
+
const projectRequire = getProjectRequire();
|
|
1900
|
+
const requirers = [requireFromPackage2];
|
|
1901
|
+
if (projectRequire) requirers.push(projectRequire);
|
|
1902
|
+
for (const req of requirers) {
|
|
1903
|
+
for (const pkg of DEFAULT_EXTENSION_PACKAGES) {
|
|
1904
|
+
try {
|
|
1905
|
+
const mod = req(pkg);
|
|
1906
|
+
const fn = getRegisterFn(mod);
|
|
1907
|
+
if (typeof fn === "function") return { register: fn, packageName: pkg };
|
|
1908
|
+
} catch {
|
|
1909
|
+
continue;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
return null;
|
|
1914
|
+
}
|
|
1914
1915
|
function loadExtensionFromFileDescriptorSync(descriptor, configFilePath, stepLog) {
|
|
1915
1916
|
const entryStr = descriptor.trim();
|
|
1916
1917
|
const path = parseToolPath(entryStr);
|
|
@@ -2384,18 +2385,18 @@ var BodyParseError = class extends Error {
|
|
|
2384
2385
|
}
|
|
2385
2386
|
};
|
|
2386
2387
|
function parseBody(req) {
|
|
2387
|
-
return new Promise((
|
|
2388
|
+
return new Promise((resolve4, reject) => {
|
|
2388
2389
|
const chunks = [];
|
|
2389
2390
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
2390
2391
|
req.on("end", () => {
|
|
2391
2392
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
2392
2393
|
if (!raw.trim()) {
|
|
2393
|
-
|
|
2394
|
+
resolve4({});
|
|
2394
2395
|
return;
|
|
2395
2396
|
}
|
|
2396
2397
|
const parsed = safeParseToolArgs(raw);
|
|
2397
2398
|
if (parsed.ok) {
|
|
2398
|
-
|
|
2399
|
+
resolve4(parsed.value);
|
|
2399
2400
|
return;
|
|
2400
2401
|
}
|
|
2401
2402
|
reject(new BodyParseError("Invalid JSON body", parsed.hint));
|
|
@@ -2553,13 +2554,13 @@ function createOpenAPIHttpServer(runtime, options = {}) {
|
|
|
2553
2554
|
return server;
|
|
2554
2555
|
}
|
|
2555
2556
|
function listenOpenAPIHttpServer(server, options = {}) {
|
|
2556
|
-
return new Promise((
|
|
2557
|
+
return new Promise((resolve4, reject) => {
|
|
2557
2558
|
const port = options.port ?? 0;
|
|
2558
2559
|
const host = options.host ?? "localhost";
|
|
2559
2560
|
server.listen(port, host, () => {
|
|
2560
2561
|
const addr = server.address();
|
|
2561
2562
|
const actualPort = typeof addr === "object" && addr?.port != null ? addr.port : port;
|
|
2562
|
-
|
|
2563
|
+
resolve4({ port: actualPort, host });
|
|
2563
2564
|
});
|
|
2564
2565
|
server.on("error", reject);
|
|
2565
2566
|
});
|
|
@@ -2694,11 +2695,11 @@ async function createMCPServerStreamableHttp(runtimeOrConfig, options = {}) {
|
|
|
2694
2695
|
async listen(listenPort, listenHost) {
|
|
2695
2696
|
const p = listenPort ?? port;
|
|
2696
2697
|
const h = listenHost ?? host;
|
|
2697
|
-
return new Promise((
|
|
2698
|
+
return new Promise((resolve4, reject) => {
|
|
2698
2699
|
const server = app.listen(p, h, () => {
|
|
2699
2700
|
const addr = server.address();
|
|
2700
2701
|
const actualPort = typeof addr === "object" && addr !== null && "port" in addr ? addr.port : p;
|
|
2701
|
-
|
|
2702
|
+
resolve4({ url: `http://${h}:${actualPort}${path}`, port: actualPort });
|
|
2702
2703
|
});
|
|
2703
2704
|
});
|
|
2704
2705
|
}
|
|
@@ -2711,5 +2712,5 @@ async function runMCPServerOverStdio(runtime, options = {}) {
|
|
|
2711
2712
|
}
|
|
2712
2713
|
|
|
2713
2714
|
export { PTCRuntime, createHttpService, createMCPServer, createMCPServerStreamableHttp, createMCPStreamableHttpHandler, createRuntimeFromConfig, createRuntimeFromConfigSync, expandToolDescriptorsToRegistryNames, fileDescriptorToPackagePrefix, findAndLoadToolConfig, getDisplayScope, isBarePackageDescriptor, loadToolConfig, npmDescriptorToPackagePrefixWithVersion, resolveSandboxedPath, resolveToolDescriptor, runMCPServerOverStdio };
|
|
2714
|
-
//# sourceMappingURL=chunk-
|
|
2715
|
-
//# sourceMappingURL=chunk-
|
|
2715
|
+
//# sourceMappingURL=chunk-RJAF5XY6.js.map
|
|
2716
|
+
//# sourceMappingURL=chunk-RJAF5XY6.js.map
|