elit 3.6.8 → 3.6.9
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/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/dist/cli.cjs +328 -65
- package/dist/cli.mjs +310 -47
- package/dist/pm.cjs +309 -46
- package/dist/pm.js +309 -46
- package/dist/pm.mjs +309 -46
- package/package.json +1 -1
package/dist/pm.js
CHANGED
|
@@ -5203,6 +5203,217 @@ error: ${text}`);
|
|
|
5203
5203
|
var import_node_http = __require("http");
|
|
5204
5204
|
var import_node_https = __require("https");
|
|
5205
5205
|
var import_node_net = __require("net");
|
|
5206
|
+
var import_promises = __require("dns/promises");
|
|
5207
|
+
var BLOCKED_IPV4_PREFIXES = [
|
|
5208
|
+
"0.",
|
|
5209
|
+
"10.",
|
|
5210
|
+
"100.64.",
|
|
5211
|
+
"100.65.",
|
|
5212
|
+
"100.66.",
|
|
5213
|
+
"100.67.",
|
|
5214
|
+
"100.68.",
|
|
5215
|
+
"100.69.",
|
|
5216
|
+
"100.70.",
|
|
5217
|
+
"100.71.",
|
|
5218
|
+
"100.72.",
|
|
5219
|
+
"100.73.",
|
|
5220
|
+
"100.74.",
|
|
5221
|
+
"100.75.",
|
|
5222
|
+
"100.76.",
|
|
5223
|
+
"100.77.",
|
|
5224
|
+
"100.78.",
|
|
5225
|
+
"100.79.",
|
|
5226
|
+
"100.80.",
|
|
5227
|
+
"100.81.",
|
|
5228
|
+
"100.82.",
|
|
5229
|
+
"100.83.",
|
|
5230
|
+
"100.84.",
|
|
5231
|
+
"100.85.",
|
|
5232
|
+
"100.86.",
|
|
5233
|
+
"100.87.",
|
|
5234
|
+
"100.88.",
|
|
5235
|
+
"100.89.",
|
|
5236
|
+
"100.90.",
|
|
5237
|
+
"100.91.",
|
|
5238
|
+
"100.92.",
|
|
5239
|
+
"100.93.",
|
|
5240
|
+
"100.94.",
|
|
5241
|
+
"100.95.",
|
|
5242
|
+
"100.96.",
|
|
5243
|
+
"100.97.",
|
|
5244
|
+
"100.98.",
|
|
5245
|
+
"100.99.",
|
|
5246
|
+
"100.100.",
|
|
5247
|
+
"100.101.",
|
|
5248
|
+
"100.102.",
|
|
5249
|
+
"100.103.",
|
|
5250
|
+
"100.104.",
|
|
5251
|
+
"100.105.",
|
|
5252
|
+
"100.106.",
|
|
5253
|
+
"100.107.",
|
|
5254
|
+
"100.108.",
|
|
5255
|
+
"100.109.",
|
|
5256
|
+
"100.110.",
|
|
5257
|
+
"100.111.",
|
|
5258
|
+
"100.112.",
|
|
5259
|
+
"100.113.",
|
|
5260
|
+
"100.114.",
|
|
5261
|
+
"100.115.",
|
|
5262
|
+
"100.116.",
|
|
5263
|
+
"100.117.",
|
|
5264
|
+
"100.118.",
|
|
5265
|
+
"100.119.",
|
|
5266
|
+
"100.120.",
|
|
5267
|
+
"100.121.",
|
|
5268
|
+
"100.122.",
|
|
5269
|
+
"100.123.",
|
|
5270
|
+
"100.124.",
|
|
5271
|
+
"100.125.",
|
|
5272
|
+
"100.126.",
|
|
5273
|
+
"100.127.",
|
|
5274
|
+
"127.",
|
|
5275
|
+
"169.254.",
|
|
5276
|
+
"172.16.",
|
|
5277
|
+
"172.17.",
|
|
5278
|
+
"172.18.",
|
|
5279
|
+
"172.19.",
|
|
5280
|
+
"172.20.",
|
|
5281
|
+
"172.21.",
|
|
5282
|
+
"172.22.",
|
|
5283
|
+
"172.23.",
|
|
5284
|
+
"172.24.",
|
|
5285
|
+
"172.25.",
|
|
5286
|
+
"172.26.",
|
|
5287
|
+
"172.27.",
|
|
5288
|
+
"172.28.",
|
|
5289
|
+
"172.29.",
|
|
5290
|
+
"172.30.",
|
|
5291
|
+
"172.31.",
|
|
5292
|
+
"192.0.2.",
|
|
5293
|
+
"192.88.99.",
|
|
5294
|
+
"192.168.",
|
|
5295
|
+
"198.18.",
|
|
5296
|
+
"198.19.",
|
|
5297
|
+
"198.51.100.",
|
|
5298
|
+
"203.0.113.",
|
|
5299
|
+
"224.",
|
|
5300
|
+
"225.",
|
|
5301
|
+
"226.",
|
|
5302
|
+
"227.",
|
|
5303
|
+
"228.",
|
|
5304
|
+
"229.",
|
|
5305
|
+
"230.",
|
|
5306
|
+
"231.",
|
|
5307
|
+
"232.",
|
|
5308
|
+
"233.",
|
|
5309
|
+
"234.",
|
|
5310
|
+
"235.",
|
|
5311
|
+
"236.",
|
|
5312
|
+
"237.",
|
|
5313
|
+
"238.",
|
|
5314
|
+
"239.",
|
|
5315
|
+
"240.",
|
|
5316
|
+
"241.",
|
|
5317
|
+
"242.",
|
|
5318
|
+
"243.",
|
|
5319
|
+
"244.",
|
|
5320
|
+
"245.",
|
|
5321
|
+
"246.",
|
|
5322
|
+
"247.",
|
|
5323
|
+
"248.",
|
|
5324
|
+
"249.",
|
|
5325
|
+
"250.",
|
|
5326
|
+
"251.",
|
|
5327
|
+
"252.",
|
|
5328
|
+
"253.",
|
|
5329
|
+
"254.",
|
|
5330
|
+
"255."
|
|
5331
|
+
];
|
|
5332
|
+
var ALLOWED_PROXY_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
5333
|
+
function isBlockedIpv4(hostname) {
|
|
5334
|
+
const octets = hostname.split(".");
|
|
5335
|
+
if (octets.length !== 4) return false;
|
|
5336
|
+
const joined = hostname;
|
|
5337
|
+
return BLOCKED_IPV4_PREFIXES.some((prefix) => joined.startsWith(prefix));
|
|
5338
|
+
}
|
|
5339
|
+
function isBlockedIpv6(hostname) {
|
|
5340
|
+
const lower = hostname.toLowerCase();
|
|
5341
|
+
if (lower === "::1" || lower === "::" || lower === "0:0:0:0:0:0:0:1" || lower === "0:0:0:0:0:0:0:0") {
|
|
5342
|
+
return true;
|
|
5343
|
+
}
|
|
5344
|
+
const ffffMatch = lower.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
|
|
5345
|
+
if (ffffMatch) {
|
|
5346
|
+
return isBlockedIpv4(ffffMatch[1]);
|
|
5347
|
+
}
|
|
5348
|
+
const compatMatch = lower.match(/^::(\d+\.\d+\.\d+\.\d+)$/);
|
|
5349
|
+
if (compatMatch) {
|
|
5350
|
+
return isBlockedIpv4(compatMatch[1]);
|
|
5351
|
+
}
|
|
5352
|
+
return false;
|
|
5353
|
+
}
|
|
5354
|
+
function isSafeHostname(hostname) {
|
|
5355
|
+
if (!hostname) return false;
|
|
5356
|
+
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) return !isBlockedIpv4(hostname);
|
|
5357
|
+
if (/^\[.*\]$/.test(hostname)) return !isBlockedIpv6(hostname.slice(1, -1));
|
|
5358
|
+
if (hostname.includes(":")) return !isBlockedIpv6(hostname);
|
|
5359
|
+
return true;
|
|
5360
|
+
}
|
|
5361
|
+
async function safeResolveHostname(hostname) {
|
|
5362
|
+
try {
|
|
5363
|
+
const result = await (0, import_promises.lookup)(hostname);
|
|
5364
|
+
const ip = result.address;
|
|
5365
|
+
if (isBlockedIpv4(ip) || isBlockedIpv6(ip)) {
|
|
5366
|
+
throw new Error(`PM proxy target resolved to a blocked address: ${ip}`);
|
|
5367
|
+
}
|
|
5368
|
+
return ip;
|
|
5369
|
+
} catch (error) {
|
|
5370
|
+
if (error instanceof Error && error.message.includes("blocked address")) {
|
|
5371
|
+
throw error;
|
|
5372
|
+
}
|
|
5373
|
+
throw new Error(`PM proxy failed to resolve target hostname "${hostname}": ${error instanceof Error ? error.message : String(error)}`);
|
|
5374
|
+
}
|
|
5375
|
+
}
|
|
5376
|
+
async function validateProxyTargetUrl(target) {
|
|
5377
|
+
if (!ALLOWED_PROXY_PROTOCOLS.has(target.protocol)) {
|
|
5378
|
+
throw new Error(`PM proxy target protocol "${target.protocol}" is not allowed. Only http: and https: are permitted.`);
|
|
5379
|
+
}
|
|
5380
|
+
const hostname = target.hostname;
|
|
5381
|
+
if (!isSafeHostname(hostname)) {
|
|
5382
|
+
throw new Error(`PM proxy target "${hostname}" resolves to a blocked address and is not allowed.`);
|
|
5383
|
+
}
|
|
5384
|
+
if (!/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname) && !/^\[.*\]$/.test(hostname)) {
|
|
5385
|
+
await safeResolveHostname(hostname);
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
function sanitizeProxyRequestPath(requestUrl) {
|
|
5389
|
+
if (!requestUrl || requestUrl === "/") return "/";
|
|
5390
|
+
try {
|
|
5391
|
+
const normalizedInput = requestUrl.replace(/\\/g, "/");
|
|
5392
|
+
const parsed = new URL(normalizedInput, "http://placeholder");
|
|
5393
|
+
if (parsed.username || parsed.password || parsed.hostname !== "placeholder" || parsed.port) {
|
|
5394
|
+
return "/";
|
|
5395
|
+
}
|
|
5396
|
+
const pathname = parsed.pathname || "/";
|
|
5397
|
+
let decodedPathname = pathname;
|
|
5398
|
+
try {
|
|
5399
|
+
decodedPathname = decodeURIComponent(pathname);
|
|
5400
|
+
} catch {
|
|
5401
|
+
return "/";
|
|
5402
|
+
}
|
|
5403
|
+
const lowerPath = pathname.toLowerCase();
|
|
5404
|
+
if (lowerPath.includes("%2f") || lowerPath.includes("%5c") || lowerPath.includes("%40") || lowerPath.includes("%00")) {
|
|
5405
|
+
return "/";
|
|
5406
|
+
}
|
|
5407
|
+
const segments = decodedPathname.split("/");
|
|
5408
|
+
if (segments.some((segment) => segment === "." || segment === "..")) {
|
|
5409
|
+
return "/";
|
|
5410
|
+
}
|
|
5411
|
+
const sanitized = pathname + parsed.search;
|
|
5412
|
+
return sanitized.startsWith("/") ? sanitized || "/" : `/${sanitized}`;
|
|
5413
|
+
} catch {
|
|
5414
|
+
return "/";
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5206
5417
|
function resolvePmProxyHost(proxy) {
|
|
5207
5418
|
return proxy.host?.trim() || "0.0.0.0";
|
|
5208
5419
|
}
|
|
@@ -5292,6 +5503,9 @@ error: ${text}`);
|
|
|
5292
5503
|
nextTargetIndex = (nextTargetIndex + 1) % targets.length;
|
|
5293
5504
|
return target;
|
|
5294
5505
|
};
|
|
5506
|
+
const validateTarget = async (target) => {
|
|
5507
|
+
await validateProxyTargetUrl(target);
|
|
5508
|
+
};
|
|
5295
5509
|
const server = (0, import_node_http.createServer)((req, res) => {
|
|
5296
5510
|
const target = pickTarget();
|
|
5297
5511
|
if (!target) {
|
|
@@ -5299,29 +5513,45 @@ error: ${text}`);
|
|
|
5299
5513
|
res.end("PM proxy target is not ready.");
|
|
5300
5514
|
return;
|
|
5301
5515
|
}
|
|
5516
|
+
const sanitizedPath = sanitizeProxyRequestPath(req.url || "/");
|
|
5302
5517
|
const requestLib = target.protocol === "https:" ? import_node_https.request : import_node_http.request;
|
|
5303
|
-
const targetUrl = new URL(req.url || "/", target);
|
|
5304
5518
|
const headers = buildPmProxyHeaders(req.headers, target.host);
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5519
|
+
if (!ALLOWED_PROXY_PROTOCOLS.has(target.protocol)) {
|
|
5520
|
+
res.statusCode = 400;
|
|
5521
|
+
res.end("PM proxy rejected unsafe target protocol.");
|
|
5522
|
+
return;
|
|
5523
|
+
}
|
|
5524
|
+
validateTarget(target).then(() => {
|
|
5525
|
+
const proxyReq = requestLib({
|
|
5526
|
+
protocol: target.protocol,
|
|
5527
|
+
hostname: target.hostname,
|
|
5528
|
+
port: target.port || void 0,
|
|
5529
|
+
path: sanitizedPath,
|
|
5530
|
+
method: req.method,
|
|
5531
|
+
headers
|
|
5532
|
+
}, (proxyRes) => {
|
|
5533
|
+
const outgoingHeaders = {};
|
|
5534
|
+
for (const [key, value] of Object.entries(proxyRes.headers)) {
|
|
5535
|
+
if (value !== void 0) {
|
|
5536
|
+
outgoingHeaders[key] = value;
|
|
5537
|
+
}
|
|
5313
5538
|
}
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5539
|
+
res.writeHead(proxyRes.statusCode || 200, outgoingHeaders);
|
|
5540
|
+
proxyRes.pipe(res);
|
|
5541
|
+
});
|
|
5542
|
+
proxyReq.on("error", (error) => {
|
|
5543
|
+
if (!res.headersSent) {
|
|
5544
|
+
res.statusCode = 502;
|
|
5545
|
+
}
|
|
5546
|
+
res.end(`PM proxy error: ${error.message}`);
|
|
5547
|
+
});
|
|
5548
|
+
req.pipe(proxyReq);
|
|
5549
|
+
}).catch((error) => {
|
|
5319
5550
|
if (!res.headersSent) {
|
|
5320
|
-
res.statusCode =
|
|
5551
|
+
res.statusCode = 403;
|
|
5321
5552
|
}
|
|
5322
|
-
res.end(`PM proxy
|
|
5553
|
+
res.end(`PM proxy blocked target: ${error instanceof Error ? error.message : String(error)}`);
|
|
5323
5554
|
});
|
|
5324
|
-
req.pipe(proxyReq);
|
|
5325
5555
|
});
|
|
5326
5556
|
server.on("upgrade", (req, socket, head) => {
|
|
5327
5557
|
const target = pickTarget();
|
|
@@ -5333,38 +5563,56 @@ error: ${text}`);
|
|
|
5333
5563
|
socket.destroy();
|
|
5334
5564
|
return;
|
|
5335
5565
|
}
|
|
5566
|
+
const sanitizedPath = sanitizeProxyRequestPath(req.url || "/");
|
|
5336
5567
|
const requestLib = target.protocol === "https:" ? import_node_https.request : import_node_http.request;
|
|
5337
|
-
const targetUrl = new URL(
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5568
|
+
const targetUrl = new URL(sanitizedPath, target);
|
|
5569
|
+
if (targetUrl.hostname !== target.hostname || targetUrl.port !== target.port || !ALLOWED_PROXY_PROTOCOLS.has(targetUrl.protocol)) {
|
|
5570
|
+
writeRawHttpResponse(socket, 400, "Bad Request", {
|
|
5571
|
+
connection: "close",
|
|
5572
|
+
"content-length": 0
|
|
5573
|
+
});
|
|
5574
|
+
socket.destroy();
|
|
5575
|
+
return;
|
|
5576
|
+
}
|
|
5577
|
+
validateTarget(target).then(() => {
|
|
5578
|
+
const proxyReq = requestLib(targetUrl, {
|
|
5579
|
+
method: req.method,
|
|
5580
|
+
headers: buildPmProxyHeaders(req.headers, target.host)
|
|
5581
|
+
});
|
|
5582
|
+
proxyReq.on("upgrade", (proxyRes, proxySocket, proxyHead) => {
|
|
5583
|
+
writeRawHttpResponse(socket, proxyRes.statusCode || 101, proxyRes.statusMessage || "Switching Protocols", proxyRes.headers);
|
|
5584
|
+
if (head.length > 0) {
|
|
5585
|
+
proxySocket.write(head);
|
|
5586
|
+
}
|
|
5587
|
+
if (proxyHead.length > 0) {
|
|
5588
|
+
socket.write(proxyHead);
|
|
5589
|
+
}
|
|
5590
|
+
socket.on("error", () => proxySocket.destroy());
|
|
5591
|
+
proxySocket.on("error", () => socket.destroy());
|
|
5592
|
+
proxySocket.pipe(socket);
|
|
5593
|
+
socket.pipe(proxySocket);
|
|
5594
|
+
});
|
|
5595
|
+
proxyReq.on("response", (proxyRes) => {
|
|
5596
|
+
writeRawHttpResponse(socket, proxyRes.statusCode || 502, proxyRes.statusMessage || "Bad Gateway", proxyRes.headers);
|
|
5597
|
+
proxyRes.pipe(socket);
|
|
5598
|
+
});
|
|
5599
|
+
proxyReq.on("error", (error) => {
|
|
5600
|
+
writeRawHttpResponse(socket, 502, "Bad Gateway", {
|
|
5601
|
+
connection: "close",
|
|
5602
|
+
"content-type": "text/plain; charset=utf-8",
|
|
5603
|
+
"content-length": Buffer.byteLength(`PM proxy error: ${error.message}`)
|
|
5604
|
+
});
|
|
5605
|
+
socket.end(`PM proxy error: ${error.message}`);
|
|
5606
|
+
});
|
|
5607
|
+
proxyReq.end();
|
|
5608
|
+
}).catch((error) => {
|
|
5609
|
+
writeRawHttpResponse(socket, 403, "Forbidden", {
|
|
5361
5610
|
connection: "close",
|
|
5362
5611
|
"content-type": "text/plain; charset=utf-8",
|
|
5363
|
-
"content-length": Buffer.byteLength(`PM proxy
|
|
5612
|
+
"content-length": Buffer.byteLength(`PM proxy blocked target: ${error instanceof Error ? error.message : String(error)}`)
|
|
5364
5613
|
});
|
|
5365
|
-
socket.end(`PM proxy
|
|
5614
|
+
socket.end(`PM proxy blocked target: ${error instanceof Error ? error.message : String(error)}`);
|
|
5366
5615
|
});
|
|
5367
|
-
proxyReq.end();
|
|
5368
5616
|
});
|
|
5369
5617
|
await new Promise((resolve7, reject) => {
|
|
5370
5618
|
server.once("error", reject);
|
|
@@ -5372,10 +5620,25 @@ error: ${text}`);
|
|
|
5372
5620
|
});
|
|
5373
5621
|
return {
|
|
5374
5622
|
setTarget(targetUrl) {
|
|
5375
|
-
|
|
5623
|
+
if (targetUrl) {
|
|
5624
|
+
const parsed = new URL(targetUrl);
|
|
5625
|
+
validateProxyTargetUrl(parsed).then(() => {
|
|
5626
|
+
setResolvedTargets([parsed]);
|
|
5627
|
+
}).catch((error) => {
|
|
5628
|
+
console.error(`[PM proxy] Blocked setTarget: ${error instanceof Error ? error.message : String(error)}`);
|
|
5629
|
+
setResolvedTargets([]);
|
|
5630
|
+
});
|
|
5631
|
+
} else {
|
|
5632
|
+
setResolvedTargets([]);
|
|
5633
|
+
}
|
|
5376
5634
|
},
|
|
5377
5635
|
setTargets(targetUrls) {
|
|
5378
|
-
|
|
5636
|
+
Promise.all(targetUrls.map((url) => validateProxyTargetUrl(new URL(url)))).then(() => {
|
|
5637
|
+
setResolvedTargets(targetUrls.map((targetUrl) => new URL(targetUrl)));
|
|
5638
|
+
}).catch((error) => {
|
|
5639
|
+
console.error(`[PM proxy] Blocked setTargets: ${error instanceof Error ? error.message : String(error)}`);
|
|
5640
|
+
setResolvedTargets([]);
|
|
5641
|
+
});
|
|
5379
5642
|
},
|
|
5380
5643
|
close() {
|
|
5381
5644
|
return new Promise((resolve7, reject) => {
|