@wix/himalaya-cli 0.992.0 → 0.993.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.
@@ -0,0 +1,58 @@
1
+ /** Which hosts a WIX-SCOPED dev-server request may be sent to.
2
+ *
3
+ * The dev-server does not merely forward these requests — it ATTACHES CREDENTIALS to them:
4
+ * a minted anonymous visitor token, a signed-in member token, the developer's `wixSession2`
5
+ * cookie. So the destination is not a detail. A wix-scoped request to any other host is those
6
+ * credentials leaving for that host, from a server that binds `0.0.0.0` (it has to, to serve
7
+ * simulators and physical devices) with no auth in front of it.
8
+ *
9
+ * The serve plane states the same rule for its own bridge — `core/serve-runtime/src/wix-bridge.ts`
10
+ * is "host-locked to www.wixapis.com — absolute URLs are rejected (no open proxy)". The
11
+ * dev-server needs the ALLOWLIST form rather than that outright rejection, because absolute
12
+ * URLs to other first-party hosts are a real, tested capability here: `users.wix.com` for
13
+ * identity, `apps`/`bo`/`manage`/`support`/`www`.wix.com elsewhere in this package, and
14
+ * `chat.nownia.com` for the Orion/Genie assistant — `orion-chat`'s `createConversation` sends
15
+ * an absolute url there through `wixApi.call`, whose own comment records the contract
16
+ * ("forwards this absolute, non-*.wix.com URL untouched"). Rejecting every absolute URL the way
17
+ * the serve plane does would break both that and
18
+ * "webWixCall preserves absolute Wix URLs for users.wix.com calls".
19
+ *
20
+ * THE LIST IS NOT INVENTED HERE. It mirrors `WixRequestPolicyRules.isFirstPartyWixHost`, which
21
+ * both natives already implement identically — `core/runtime/ios/HimalayaCore/Sources/
22
+ * HimalayaCore/WixRequestPolicy.swift` and the Kotlin twin under `core/runtime/android`. That is
23
+ * the platform's definition of "first party", so the web/dev arm has no business holding a
24
+ * narrower one: a host the device may call is a host this bridge may call. `wix-hosts.test.ts`
25
+ * pins the two together, so a root added natively cannot silently leave this arm behind — which
26
+ * is exactly how `nownia.com` was missed the first time (codebot review on #3120).
27
+ *
28
+ * PARSED, NEVER STRING-MATCHED. `apps/himi-owner-lite/dev/net-mocks.ts` records three spellings
29
+ * that defeated an earlier host parse in this repo, and all three are why this reads
30
+ * `new URL(...).hostname` instead of testing the raw text:
31
+ *
32
+ * `https://www.wixapis.com@evil.example/x` userinfo — the host is `evil.example`, and a
33
+ * `startsWith`/`includes` test says "wixapis".
34
+ * `https://www.wix.com\@evil.example/x` a backslash ENDS THE AUTHORITY for http(s), so
35
+ * the host really is `www.wix.com` and that is
36
+ * really where the request goes — a hand-rolled
37
+ * parser that splits on `/` disagrees.
38
+ * `https:///x` an empty authority yields no host at all.
39
+ *
40
+ * Judging the PARSED host cannot disagree with the connection that gets made, because `fetch`
41
+ * resolves the same string through the same WHATWG parser. A regex over the text can, and that
42
+ * divergence is the whole bug class.
43
+ *
44
+ * This is a destination check, NOT an SSRF guard in the `net-guard.ts` sense: it does not
45
+ * resolve the name or pin the address, so it does not close DNS rebinding. It does not need to
46
+ * — the property wanted here is "these credentials only ever go to Wix", and a name that
47
+ * resolves somewhere unexpected is still a name Wix controls. */
48
+ /** Registrable suffixes whose hosts may receive wix-scoped, credential-bearing traffic.
49
+ *
50
+ * Kept in lockstep with the natives' `isFirstPartyWixHost` roots — see the note above. */
51
+ export declare const WIX_HOST_SUFFIXES: readonly ["wix.com", "wixapis.com", "nownia.com"];
52
+ /** The host this url would actually be dialled on, lowercased — or `""` when it has none. */
53
+ export declare function targetHost(raw: string): string;
54
+ /** May a wix-scoped, credential-bearing request go to this url?
55
+ *
56
+ * `https:` only: every legitimate target in this package is https, and a downgrade to `http:`
57
+ * would put the very credentials this exists to protect on the wire in clear. */
58
+ export declare function isWixTarget(raw: string): boolean;
package/dist/cli.mjs CHANGED
@@ -15163,6 +15163,33 @@ var init_derive_transport_capability = __esm({
15163
15163
  }
15164
15164
  });
15165
15165
 
15166
+ // ../../core/dev-server/src/wix-hosts.ts
15167
+ function targetHost(raw) {
15168
+ try {
15169
+ return new URL(raw).hostname.toLowerCase();
15170
+ } catch {
15171
+ return "";
15172
+ }
15173
+ }
15174
+ function isWixTarget(raw) {
15175
+ let url;
15176
+ try {
15177
+ url = new URL(raw);
15178
+ } catch {
15179
+ return false;
15180
+ }
15181
+ if (url.protocol !== "https:") return false;
15182
+ const host = url.hostname.toLowerCase();
15183
+ if (!host) return false;
15184
+ return WIX_HOST_SUFFIXES.some((suffix) => host === suffix || host.endsWith(`.${suffix}`));
15185
+ }
15186
+ var WIX_HOST_SUFFIXES;
15187
+ var init_wix_hosts = __esm({
15188
+ "../../core/dev-server/src/wix-hosts.ts"() {
15189
+ WIX_HOST_SUFFIXES = ["wix.com", "wixapis.com", "nownia.com"];
15190
+ }
15191
+ });
15192
+
15166
15193
  // ../../core/dev-server/src/wix-member-login.ts
15167
15194
  var wix_member_login_exports = {};
15168
15195
  __export(wix_member_login_exports, {
@@ -15287,6 +15314,9 @@ async function memberDirectRegister(clientId, redirectUri, email, password, firs
15287
15314
  }
15288
15315
  async function webWixCall(method, path, body, token, cookie, forwardedHeaders = {}) {
15289
15316
  const url = /^https?:\/\//i.test(path) ? path : `${API}${path.startsWith("/") ? path : "/" + path}`;
15317
+ if (!isWixTarget(url)) {
15318
+ throw new Error(`refusing to send Wix credentials to a non-Wix host (${targetHost(url) || "no host"})`);
15319
+ }
15290
15320
  const res = await fetch(url, {
15291
15321
  method,
15292
15322
  headers: {
@@ -15302,6 +15332,7 @@ async function webWixCall(method, path, body, token, cookie, forwardedHeaders =
15302
15332
  var API;
15303
15333
  var init_wix_member_login = __esm({
15304
15334
  "../../core/dev-server/src/wix-member-login.ts"() {
15335
+ init_wix_hosts();
15305
15336
  API = "https://www.wixapis.com";
15306
15337
  }
15307
15338
  });
@@ -18867,6 +18898,7 @@ function shouldRevealError(args) {
18867
18898
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
18868
18899
  var init_validation = __esm({
18869
18900
  "../../core/ts/src/validation.ts"() {
18901
+ "use strict";
18870
18902
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
18871
18903
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
18872
18904
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -22291,6 +22323,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
22291
22323
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
22292
22324
  var init_worker_subtree = __esm({
22293
22325
  "../../core/ts/src/worker-subtree.ts"() {
22326
+ "use strict";
22294
22327
  init_worker_subtree_safe_types_generated();
22295
22328
  WORKER_SUBTREE_CEILINGS = Object.freeze({
22296
22329
  maxEncodedBytes: 65536,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.992.0",
3
+ "version": "0.993.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -65,5 +65,5 @@
65
65
  "artifactId": "himalaya-cli"
66
66
  }
67
67
  },
68
- "falconPackageHash": "cbd96ed65b504cab0c2d276b2a961b73f9bf6730d07259f3d334a43e"
68
+ "falconPackageHash": "65760e4588c44ee36e6538fcc1bf8802e544b4045157544b3f4d0d94"
69
69
  }