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/dist/cli.mjs CHANGED
@@ -81569,6 +81569,217 @@ function watch2(paths, options) {
81569
81569
  import { createServer, request as httpRequest } from "http";
81570
81570
  import { request as httpsRequest } from "https";
81571
81571
  import { createServer as createNetServer } from "net";
81572
+ import { lookup as dnsLookup } from "dns/promises";
81573
+ var BLOCKED_IPV4_PREFIXES = [
81574
+ "0.",
81575
+ "10.",
81576
+ "100.64.",
81577
+ "100.65.",
81578
+ "100.66.",
81579
+ "100.67.",
81580
+ "100.68.",
81581
+ "100.69.",
81582
+ "100.70.",
81583
+ "100.71.",
81584
+ "100.72.",
81585
+ "100.73.",
81586
+ "100.74.",
81587
+ "100.75.",
81588
+ "100.76.",
81589
+ "100.77.",
81590
+ "100.78.",
81591
+ "100.79.",
81592
+ "100.80.",
81593
+ "100.81.",
81594
+ "100.82.",
81595
+ "100.83.",
81596
+ "100.84.",
81597
+ "100.85.",
81598
+ "100.86.",
81599
+ "100.87.",
81600
+ "100.88.",
81601
+ "100.89.",
81602
+ "100.90.",
81603
+ "100.91.",
81604
+ "100.92.",
81605
+ "100.93.",
81606
+ "100.94.",
81607
+ "100.95.",
81608
+ "100.96.",
81609
+ "100.97.",
81610
+ "100.98.",
81611
+ "100.99.",
81612
+ "100.100.",
81613
+ "100.101.",
81614
+ "100.102.",
81615
+ "100.103.",
81616
+ "100.104.",
81617
+ "100.105.",
81618
+ "100.106.",
81619
+ "100.107.",
81620
+ "100.108.",
81621
+ "100.109.",
81622
+ "100.110.",
81623
+ "100.111.",
81624
+ "100.112.",
81625
+ "100.113.",
81626
+ "100.114.",
81627
+ "100.115.",
81628
+ "100.116.",
81629
+ "100.117.",
81630
+ "100.118.",
81631
+ "100.119.",
81632
+ "100.120.",
81633
+ "100.121.",
81634
+ "100.122.",
81635
+ "100.123.",
81636
+ "100.124.",
81637
+ "100.125.",
81638
+ "100.126.",
81639
+ "100.127.",
81640
+ "127.",
81641
+ "169.254.",
81642
+ "172.16.",
81643
+ "172.17.",
81644
+ "172.18.",
81645
+ "172.19.",
81646
+ "172.20.",
81647
+ "172.21.",
81648
+ "172.22.",
81649
+ "172.23.",
81650
+ "172.24.",
81651
+ "172.25.",
81652
+ "172.26.",
81653
+ "172.27.",
81654
+ "172.28.",
81655
+ "172.29.",
81656
+ "172.30.",
81657
+ "172.31.",
81658
+ "192.0.2.",
81659
+ "192.88.99.",
81660
+ "192.168.",
81661
+ "198.18.",
81662
+ "198.19.",
81663
+ "198.51.100.",
81664
+ "203.0.113.",
81665
+ "224.",
81666
+ "225.",
81667
+ "226.",
81668
+ "227.",
81669
+ "228.",
81670
+ "229.",
81671
+ "230.",
81672
+ "231.",
81673
+ "232.",
81674
+ "233.",
81675
+ "234.",
81676
+ "235.",
81677
+ "236.",
81678
+ "237.",
81679
+ "238.",
81680
+ "239.",
81681
+ "240.",
81682
+ "241.",
81683
+ "242.",
81684
+ "243.",
81685
+ "244.",
81686
+ "245.",
81687
+ "246.",
81688
+ "247.",
81689
+ "248.",
81690
+ "249.",
81691
+ "250.",
81692
+ "251.",
81693
+ "252.",
81694
+ "253.",
81695
+ "254.",
81696
+ "255."
81697
+ ];
81698
+ var ALLOWED_PROXY_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
81699
+ function isBlockedIpv4(hostname) {
81700
+ const octets = hostname.split(".");
81701
+ if (octets.length !== 4) return false;
81702
+ const joined = hostname;
81703
+ return BLOCKED_IPV4_PREFIXES.some((prefix) => joined.startsWith(prefix));
81704
+ }
81705
+ function isBlockedIpv6(hostname) {
81706
+ const lower = hostname.toLowerCase();
81707
+ if (lower === "::1" || lower === "::" || lower === "0:0:0:0:0:0:0:1" || lower === "0:0:0:0:0:0:0:0") {
81708
+ return true;
81709
+ }
81710
+ const ffffMatch = lower.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
81711
+ if (ffffMatch) {
81712
+ return isBlockedIpv4(ffffMatch[1]);
81713
+ }
81714
+ const compatMatch = lower.match(/^::(\d+\.\d+\.\d+\.\d+)$/);
81715
+ if (compatMatch) {
81716
+ return isBlockedIpv4(compatMatch[1]);
81717
+ }
81718
+ return false;
81719
+ }
81720
+ function isSafeHostname(hostname) {
81721
+ if (!hostname) return false;
81722
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) return !isBlockedIpv4(hostname);
81723
+ if (/^\[.*\]$/.test(hostname)) return !isBlockedIpv6(hostname.slice(1, -1));
81724
+ if (hostname.includes(":")) return !isBlockedIpv6(hostname);
81725
+ return true;
81726
+ }
81727
+ async function safeResolveHostname(hostname) {
81728
+ try {
81729
+ const result2 = await dnsLookup(hostname);
81730
+ const ip = result2.address;
81731
+ if (isBlockedIpv4(ip) || isBlockedIpv6(ip)) {
81732
+ throw new Error(`PM proxy target resolved to a blocked address: ${ip}`);
81733
+ }
81734
+ return ip;
81735
+ } catch (error) {
81736
+ if (error instanceof Error && error.message.includes("blocked address")) {
81737
+ throw error;
81738
+ }
81739
+ throw new Error(`PM proxy failed to resolve target hostname "${hostname}": ${error instanceof Error ? error.message : String(error)}`);
81740
+ }
81741
+ }
81742
+ async function validateProxyTargetUrl(target) {
81743
+ if (!ALLOWED_PROXY_PROTOCOLS.has(target.protocol)) {
81744
+ throw new Error(`PM proxy target protocol "${target.protocol}" is not allowed. Only http: and https: are permitted.`);
81745
+ }
81746
+ const hostname = target.hostname;
81747
+ if (!isSafeHostname(hostname)) {
81748
+ throw new Error(`PM proxy target "${hostname}" resolves to a blocked address and is not allowed.`);
81749
+ }
81750
+ if (!/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname) && !/^\[.*\]$/.test(hostname)) {
81751
+ await safeResolveHostname(hostname);
81752
+ }
81753
+ }
81754
+ function sanitizeProxyRequestPath(requestUrl) {
81755
+ if (!requestUrl || requestUrl === "/") return "/";
81756
+ try {
81757
+ const normalizedInput = requestUrl.replace(/\\/g, "/");
81758
+ const parsed = new URL(normalizedInput, "http://placeholder");
81759
+ if (parsed.username || parsed.password || parsed.hostname !== "placeholder" || parsed.port) {
81760
+ return "/";
81761
+ }
81762
+ const pathname = parsed.pathname || "/";
81763
+ let decodedPathname = pathname;
81764
+ try {
81765
+ decodedPathname = decodeURIComponent(pathname);
81766
+ } catch {
81767
+ return "/";
81768
+ }
81769
+ const lowerPath = pathname.toLowerCase();
81770
+ if (lowerPath.includes("%2f") || lowerPath.includes("%5c") || lowerPath.includes("%40") || lowerPath.includes("%00")) {
81771
+ return "/";
81772
+ }
81773
+ const segments = decodedPathname.split("/");
81774
+ if (segments.some((segment) => segment === "." || segment === "..")) {
81775
+ return "/";
81776
+ }
81777
+ const sanitized = pathname + parsed.search;
81778
+ return sanitized.startsWith("/") ? sanitized || "/" : `/${sanitized}`;
81779
+ } catch {
81780
+ return "/";
81781
+ }
81782
+ }
81572
81783
  function resolvePmProxyHost(proxy) {
81573
81784
  return proxy.host?.trim() || "0.0.0.0";
81574
81785
  }
@@ -81658,6 +81869,9 @@ async function createPmProxyController(proxy) {
81658
81869
  nextTargetIndex = (nextTargetIndex + 1) % targets.length;
81659
81870
  return target;
81660
81871
  };
81872
+ const validateTarget = async (target) => {
81873
+ await validateProxyTargetUrl(target);
81874
+ };
81661
81875
  const server = createServer((req, res) => {
81662
81876
  const target = pickTarget();
81663
81877
  if (!target) {
@@ -81665,29 +81879,45 @@ async function createPmProxyController(proxy) {
81665
81879
  res.end("PM proxy target is not ready.");
81666
81880
  return;
81667
81881
  }
81882
+ const sanitizedPath = sanitizeProxyRequestPath(req.url || "/");
81668
81883
  const requestLib = target.protocol === "https:" ? httpsRequest : httpRequest;
81669
- const targetUrl = new URL(req.url || "/", target);
81670
81884
  const headers = buildPmProxyHeaders(req.headers, target.host);
81671
- const proxyReq = requestLib(targetUrl, {
81672
- method: req.method,
81673
- headers
81674
- }, (proxyRes) => {
81675
- const outgoingHeaders = {};
81676
- for (const [key, value] of Object.entries(proxyRes.headers)) {
81677
- if (value !== void 0) {
81678
- outgoingHeaders[key] = value;
81885
+ if (!ALLOWED_PROXY_PROTOCOLS.has(target.protocol)) {
81886
+ res.statusCode = 400;
81887
+ res.end("PM proxy rejected unsafe target protocol.");
81888
+ return;
81889
+ }
81890
+ validateTarget(target).then(() => {
81891
+ const proxyReq = requestLib({
81892
+ protocol: target.protocol,
81893
+ hostname: target.hostname,
81894
+ port: target.port || void 0,
81895
+ path: sanitizedPath,
81896
+ method: req.method,
81897
+ headers
81898
+ }, (proxyRes) => {
81899
+ const outgoingHeaders = {};
81900
+ for (const [key, value] of Object.entries(proxyRes.headers)) {
81901
+ if (value !== void 0) {
81902
+ outgoingHeaders[key] = value;
81903
+ }
81679
81904
  }
81680
- }
81681
- res.writeHead(proxyRes.statusCode || 200, outgoingHeaders);
81682
- proxyRes.pipe(res);
81683
- });
81684
- proxyReq.on("error", (error) => {
81905
+ res.writeHead(proxyRes.statusCode || 200, outgoingHeaders);
81906
+ proxyRes.pipe(res);
81907
+ });
81908
+ proxyReq.on("error", (error) => {
81909
+ if (!res.headersSent) {
81910
+ res.statusCode = 502;
81911
+ }
81912
+ res.end(`PM proxy error: ${error.message}`);
81913
+ });
81914
+ req.pipe(proxyReq);
81915
+ }).catch((error) => {
81685
81916
  if (!res.headersSent) {
81686
- res.statusCode = 502;
81917
+ res.statusCode = 403;
81687
81918
  }
81688
- res.end(`PM proxy error: ${error.message}`);
81919
+ res.end(`PM proxy blocked target: ${error instanceof Error ? error.message : String(error)}`);
81689
81920
  });
81690
- req.pipe(proxyReq);
81691
81921
  });
81692
81922
  server.on("upgrade", (req, socket, head) => {
81693
81923
  const target = pickTarget();
@@ -81699,38 +81929,56 @@ async function createPmProxyController(proxy) {
81699
81929
  socket.destroy();
81700
81930
  return;
81701
81931
  }
81932
+ const sanitizedPath = sanitizeProxyRequestPath(req.url || "/");
81702
81933
  const requestLib = target.protocol === "https:" ? httpsRequest : httpRequest;
81703
- const targetUrl = new URL(req.url || "/", target);
81704
- const proxyReq = requestLib(targetUrl, {
81705
- method: req.method,
81706
- headers: buildPmProxyHeaders(req.headers, target.host)
81707
- });
81708
- proxyReq.on("upgrade", (proxyRes, proxySocket, proxyHead) => {
81709
- writeRawHttpResponse(socket, proxyRes.statusCode || 101, proxyRes.statusMessage || "Switching Protocols", proxyRes.headers);
81710
- if (head.length > 0) {
81711
- proxySocket.write(head);
81712
- }
81713
- if (proxyHead.length > 0) {
81714
- socket.write(proxyHead);
81715
- }
81716
- socket.on("error", () => proxySocket.destroy());
81717
- proxySocket.on("error", () => socket.destroy());
81718
- proxySocket.pipe(socket);
81719
- socket.pipe(proxySocket);
81720
- });
81721
- proxyReq.on("response", (proxyRes) => {
81722
- writeRawHttpResponse(socket, proxyRes.statusCode || 502, proxyRes.statusMessage || "Bad Gateway", proxyRes.headers);
81723
- proxyRes.pipe(socket);
81724
- });
81725
- proxyReq.on("error", (error) => {
81726
- writeRawHttpResponse(socket, 502, "Bad Gateway", {
81934
+ const targetUrl = new URL(sanitizedPath, target);
81935
+ if (targetUrl.hostname !== target.hostname || targetUrl.port !== target.port || !ALLOWED_PROXY_PROTOCOLS.has(targetUrl.protocol)) {
81936
+ writeRawHttpResponse(socket, 400, "Bad Request", {
81937
+ connection: "close",
81938
+ "content-length": 0
81939
+ });
81940
+ socket.destroy();
81941
+ return;
81942
+ }
81943
+ validateTarget(target).then(() => {
81944
+ const proxyReq = requestLib(targetUrl, {
81945
+ method: req.method,
81946
+ headers: buildPmProxyHeaders(req.headers, target.host)
81947
+ });
81948
+ proxyReq.on("upgrade", (proxyRes, proxySocket, proxyHead) => {
81949
+ writeRawHttpResponse(socket, proxyRes.statusCode || 101, proxyRes.statusMessage || "Switching Protocols", proxyRes.headers);
81950
+ if (head.length > 0) {
81951
+ proxySocket.write(head);
81952
+ }
81953
+ if (proxyHead.length > 0) {
81954
+ socket.write(proxyHead);
81955
+ }
81956
+ socket.on("error", () => proxySocket.destroy());
81957
+ proxySocket.on("error", () => socket.destroy());
81958
+ proxySocket.pipe(socket);
81959
+ socket.pipe(proxySocket);
81960
+ });
81961
+ proxyReq.on("response", (proxyRes) => {
81962
+ writeRawHttpResponse(socket, proxyRes.statusCode || 502, proxyRes.statusMessage || "Bad Gateway", proxyRes.headers);
81963
+ proxyRes.pipe(socket);
81964
+ });
81965
+ proxyReq.on("error", (error) => {
81966
+ writeRawHttpResponse(socket, 502, "Bad Gateway", {
81967
+ connection: "close",
81968
+ "content-type": "text/plain; charset=utf-8",
81969
+ "content-length": Buffer.byteLength(`PM proxy error: ${error.message}`)
81970
+ });
81971
+ socket.end(`PM proxy error: ${error.message}`);
81972
+ });
81973
+ proxyReq.end();
81974
+ }).catch((error) => {
81975
+ writeRawHttpResponse(socket, 403, "Forbidden", {
81727
81976
  connection: "close",
81728
81977
  "content-type": "text/plain; charset=utf-8",
81729
- "content-length": Buffer.byteLength(`PM proxy error: ${error.message}`)
81978
+ "content-length": Buffer.byteLength(`PM proxy blocked target: ${error instanceof Error ? error.message : String(error)}`)
81730
81979
  });
81731
- socket.end(`PM proxy error: ${error.message}`);
81980
+ socket.end(`PM proxy blocked target: ${error instanceof Error ? error.message : String(error)}`);
81732
81981
  });
81733
- proxyReq.end();
81734
81982
  });
81735
81983
  await new Promise((resolve26, reject) => {
81736
81984
  server.once("error", reject);
@@ -81738,10 +81986,25 @@ async function createPmProxyController(proxy) {
81738
81986
  });
81739
81987
  return {
81740
81988
  setTarget(targetUrl) {
81741
- setResolvedTargets(targetUrl ? [new URL(targetUrl)] : []);
81989
+ if (targetUrl) {
81990
+ const parsed = new URL(targetUrl);
81991
+ validateProxyTargetUrl(parsed).then(() => {
81992
+ setResolvedTargets([parsed]);
81993
+ }).catch((error) => {
81994
+ console.error(`[PM proxy] Blocked setTarget: ${error instanceof Error ? error.message : String(error)}`);
81995
+ setResolvedTargets([]);
81996
+ });
81997
+ } else {
81998
+ setResolvedTargets([]);
81999
+ }
81742
82000
  },
81743
82001
  setTargets(targetUrls) {
81744
- setResolvedTargets(targetUrls.map((targetUrl) => new URL(targetUrl)));
82002
+ Promise.all(targetUrls.map((url) => validateProxyTargetUrl(new URL(url)))).then(() => {
82003
+ setResolvedTargets(targetUrls.map((targetUrl) => new URL(targetUrl)));
82004
+ }).catch((error) => {
82005
+ console.error(`[PM proxy] Blocked setTargets: ${error instanceof Error ? error.message : String(error)}`);
82006
+ setResolvedTargets([]);
82007
+ });
81745
82008
  },
81746
82009
  close() {
81747
82010
  return new Promise((resolve26, reject) => {
@@ -86160,7 +86423,7 @@ function parsePreviewArgs(args) {
86160
86423
  // package.json
86161
86424
  var package_default = {
86162
86425
  name: "elit",
86163
- version: "3.6.8",
86426
+ version: "3.6.9",
86164
86427
  description: "Optimized lightweight library for creating DOM elements with reactive state",
86165
86428
  main: "dist/index.js",
86166
86429
  module: "dist/index.mjs",