@volter/world-core 2.0.20 → 2.0.21

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/inject.cjs CHANGED
@@ -728,8 +728,9 @@ function socketTargetHosts(args, isTls) {
728
728
  return hosts;
729
729
  }
730
730
  if (typeof first === 'string' && !/^\d+$/.test(first)) return hosts; // a unix socket path
731
- hosts.push(typeof second === 'string' ? second : 'localhost');
732
731
  const options = typeof second === 'object' && second !== null ? second : typeof third === 'object' && third !== null ? third : null;
732
+ // connect(port, host) names the host; connect(port, { host }) puts it in the options, which Node merges
733
+ hosts.push(typeof second === 'string' ? second : typeof options?.host === 'string' ? options.host : 'localhost');
733
734
  if (isTls && options && options.servername) hosts.push(options.servername);
734
735
  return hosts;
735
736
  }
@@ -811,7 +812,15 @@ function patchedPromisesLookup(original) {
811
812
  };
812
813
  }
813
814
 
814
- function socketRefusal(hostname) {
815
+ /** The port a net/tls connect names (`connect(port, host)`, `connect({ port, host })`), or undefined. */
816
+ function socketTargetPort(args) {
817
+ const [first] = args;
818
+ const raw = first !== null && typeof first === 'object' ? first.port : first;
819
+ const port = Number(raw);
820
+ return Number.isInteger(port) && port > 0 && port < 65536 ? port : undefined;
821
+ }
822
+
823
+ function socketRefusal(hostname, port, isTls) {
815
824
  const host = String(hostname || '').toLowerCase();
816
825
  if (host === WORLD_ADDRESS[4] || host === WORLD_ADDRESS[6]) {
817
826
  return `[twin-inject] refused a socket to ${host}, the address this World's DNS gives the names it routes: `
@@ -831,8 +840,11 @@ function socketRefusal(hostname) {
831
840
  return `[twin-inject] refused a raw socket to ${host}, which this world routes to the application (volter-world app-url --host): `
832
841
  + 'the client opened its own connection instead of going through http/https or fetch, so it could not be routed.';
833
842
  }
834
- const url = (() => { try { return new URL(`https://${isIP(host) === 6 ? `[${host}]` : host}/`); } catch { return null; } })();
835
- if (url && shouldBlockUntwinned(url)) return `[twin-inject] blocked untwinned external socket to ${host}`;
843
+ // the destination as an origin, its port and its protocol included: a policy that lists https://host allows TLS to
844
+ // host:443, never another port or a plaintext connection to it
845
+ const authority = `${isIP(host) === 6 ? `[${host}]` : host}${port !== undefined && !(isTls && port === 443) ? `:${port}` : ''}`;
846
+ const url = (() => { try { return new URL(`${isTls ? 'https' : 'http'}://${authority}/`); } catch { return null; } })();
847
+ if (url && shouldBlockUntwinned(url)) return `[twin-inject] blocked untwinned external socket to ${authority}`;
836
848
  return null;
837
849
  }
838
850
 
@@ -844,8 +856,9 @@ function refusedSocket(message) {
844
856
 
845
857
  function patchedConnect(originalConnect, isTls) {
846
858
  return function patchedSocketConnect(...args) {
859
+ const port = socketTargetPort(args);
847
860
  for (const host of socketTargetHosts(args, isTls)) {
848
- const refusal = socketRefusal(host);
861
+ const refusal = socketRefusal(host, port, isTls);
849
862
  if (refusal) return refusedSocket(refusal);
850
863
  }
851
864
  return originalConnect.apply(this, args);
@@ -66,9 +66,11 @@ function isWorldInternalHost(hostname) {
66
66
  const v4 = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(h);
67
67
  if (v4) {
68
68
  const a = Number(v4[1]); const b = Number(v4[2]);
69
- return a === 127 || a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || (a === 169 && b === 254);
69
+ // not link-local 169.254/16: it holds a cloud's instance metadata (169.254.169.254), the box's credentials
70
+ return a === 127 || a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168);
70
71
  }
71
- return h.includes(':') && (h.startsWith('fc') || h.startsWith('fd') || h.startsWith('fe80:'));
72
+ // nor fe80::/10, nor a cloud's IPv6 metadata address (fd00:ec2::254)
73
+ return h.includes(':') && (h.startsWith('fc') || h.startsWith('fd')) && h !== 'fd00:ec2::254';
72
74
  }
73
75
 
74
76
  /**
package/inject.cjs CHANGED
@@ -728,8 +728,9 @@ function socketTargetHosts(args, isTls) {
728
728
  return hosts;
729
729
  }
730
730
  if (typeof first === 'string' && !/^\d+$/.test(first)) return hosts; // a unix socket path
731
- hosts.push(typeof second === 'string' ? second : 'localhost');
732
731
  const options = typeof second === 'object' && second !== null ? second : typeof third === 'object' && third !== null ? third : null;
732
+ // connect(port, host) names the host; connect(port, { host }) puts it in the options, which Node merges
733
+ hosts.push(typeof second === 'string' ? second : typeof options?.host === 'string' ? options.host : 'localhost');
733
734
  if (isTls && options && options.servername) hosts.push(options.servername);
734
735
  return hosts;
735
736
  }
@@ -811,7 +812,15 @@ function patchedPromisesLookup(original) {
811
812
  };
812
813
  }
813
814
 
814
- function socketRefusal(hostname) {
815
+ /** The port a net/tls connect names (`connect(port, host)`, `connect({ port, host })`), or undefined. */
816
+ function socketTargetPort(args) {
817
+ const [first] = args;
818
+ const raw = first !== null && typeof first === 'object' ? first.port : first;
819
+ const port = Number(raw);
820
+ return Number.isInteger(port) && port > 0 && port < 65536 ? port : undefined;
821
+ }
822
+
823
+ function socketRefusal(hostname, port, isTls) {
815
824
  const host = String(hostname || '').toLowerCase();
816
825
  if (host === WORLD_ADDRESS[4] || host === WORLD_ADDRESS[6]) {
817
826
  return `[twin-inject] refused a socket to ${host}, the address this World's DNS gives the names it routes: `
@@ -831,8 +840,11 @@ function socketRefusal(hostname) {
831
840
  return `[twin-inject] refused a raw socket to ${host}, which this world routes to the application (volter-world app-url --host): `
832
841
  + 'the client opened its own connection instead of going through http/https or fetch, so it could not be routed.';
833
842
  }
834
- const url = (() => { try { return new URL(`https://${isIP(host) === 6 ? `[${host}]` : host}/`); } catch { return null; } })();
835
- if (url && shouldBlockUntwinned(url)) return `[twin-inject] blocked untwinned external socket to ${host}`;
843
+ // the destination as an origin, its port and its protocol included: a policy that lists https://host allows TLS to
844
+ // host:443, never another port or a plaintext connection to it
845
+ const authority = `${isIP(host) === 6 ? `[${host}]` : host}${port !== undefined && !(isTls && port === 443) ? `:${port}` : ''}`;
846
+ const url = (() => { try { return new URL(`${isTls ? 'https' : 'http'}://${authority}/`); } catch { return null; } })();
847
+ if (url && shouldBlockUntwinned(url)) return `[twin-inject] blocked untwinned external socket to ${authority}`;
836
848
  return null;
837
849
  }
838
850
 
@@ -844,8 +856,9 @@ function refusedSocket(message) {
844
856
 
845
857
  function patchedConnect(originalConnect, isTls) {
846
858
  return function patchedSocketConnect(...args) {
859
+ const port = socketTargetPort(args);
847
860
  for (const host of socketTargetHosts(args, isTls)) {
848
- const refusal = socketRefusal(host);
861
+ const refusal = socketRefusal(host, port, isTls);
849
862
  if (refusal) return refusedSocket(refusal);
850
863
  }
851
864
  return originalConnect.apply(this, args);
@@ -66,9 +66,11 @@ function isWorldInternalHost(hostname) {
66
66
  const v4 = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(h);
67
67
  if (v4) {
68
68
  const a = Number(v4[1]); const b = Number(v4[2]);
69
- return a === 127 || a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || (a === 169 && b === 254);
69
+ // not link-local 169.254/16: it holds a cloud's instance metadata (169.254.169.254), the box's credentials
70
+ return a === 127 || a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168);
70
71
  }
71
- return h.includes(':') && (h.startsWith('fc') || h.startsWith('fd') || h.startsWith('fe80:'));
72
+ // nor fe80::/10, nor a cloud's IPv6 metadata address (fd00:ec2::254)
73
+ return h.includes(':') && (h.startsWith('fc') || h.startsWith('fd')) && h !== 'fd00:ec2::254';
72
74
  }
73
75
 
74
76
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/world-core",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "description": "The kernel of Volter World: one log per twin, branches as pointers, checkpoints, the fold that keeps a twin current, the head that performs a write against the vendor, references, and the git plane. A twin package builds on it; the runtime serves it.",
5
5
  "keywords": [
6
6
  "twin",