sliccy 6.87.2 → 6.87.3

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.
Files changed (102) hide show
  1. package/dist/node-server/_shared/index.d.ts +1 -0
  2. package/dist/node-server/_shared/index.d.ts.map +1 -1
  3. package/dist/node-server/_shared/index.js +1 -0
  4. package/dist/node-server/_shared/index.js.map +1 -1
  5. package/dist/node-server/_shared/loopback.d.ts +18 -0
  6. package/dist/node-server/_shared/loopback.d.ts.map +1 -0
  7. package/dist/node-server/_shared/loopback.js +41 -0
  8. package/dist/node-server/_shared/loopback.js.map +1 -0
  9. package/dist/node-server/bridge-security.d.ts +4 -1
  10. package/dist/node-server/bridge-security.js +6 -16
  11. package/dist/node-server/cloud-status.d.ts +3 -0
  12. package/dist/node-server/cloud-status.js +6 -2
  13. package/dist/node-server/routes/fetch-proxy.js +3 -18
  14. package/dist/ui/.vite/manifest.json +122 -132
  15. package/dist/ui/assets/{account-store-u5knhoCx.js → account-store-Cy3TJKnu.js} +1 -1
  16. package/dist/ui/assets/{account-store-D1f8dn0G.js → account-store-DysucxMw.js} +1 -1
  17. package/dist/ui/assets/{adobe-FLVM_96g.js → adobe-C19oqBaU.js} +1 -1
  18. package/dist/ui/assets/{adobe-B034kk8u.js → adobe-Dv-gMc09.js} +1 -1
  19. package/dist/ui/assets/{apply-layout-g9ha9fDb.js → apply-layout-TIXzvNTN.js} +1 -1
  20. package/dist/ui/assets/{apps-D2giAoeX.js → apps-e--FORGt.js} +1 -1
  21. package/dist/ui/assets/{azure-openai-DYlB7tTf.js → azure-openai-C5josrBb.js} +1 -1
  22. package/dist/ui/assets/{azure-openai-CB7Ox9_p.js → azure-openai-mDyNL8OK.js} +1 -1
  23. package/dist/ui/assets/{cerebras-DweKDbE1.js → cerebras-CDHmZz_Y.js} +1 -1
  24. package/dist/ui/assets/{cerebras-O8AuH6NY.js → cerebras-DPV0CO1I.js} +1 -1
  25. package/dist/ui/assets/{connect-surface-NkMUeXbQ.js → connect-surface-DqU2NC2c.js} +1 -1
  26. package/dist/ui/assets/{fs-Drpegy_X.js → fs-CIIVB1OK.js} +2 -2
  27. package/dist/ui/assets/{fs-Bov3sJ0L.js → fs-QyBiZMuq.js} +1 -1
  28. package/dist/ui/assets/{github-t3RMhkRE.js → github-Ctvr_Nd6.js} +1 -1
  29. package/dist/ui/assets/{github-CE8SlGEA.js → github-Dq-ryVOk.js} +2 -2
  30. package/dist/ui/assets/{github-copilot-ywjopCMn.js → github-copilot-DIk_soEH.js} +1 -1
  31. package/dist/ui/assets/{github-copilot-DsCq9ehC.js → github-copilot-Dm5mGSYk.js} +1 -1
  32. package/dist/ui/assets/{github-install-C7YXkDBT.js → github-install-CFJLPySf.js} +1 -1
  33. package/dist/ui/assets/{github-zip-DVWjYLIh.js → github-zip-C7dhlRG8.js} +1 -1
  34. package/dist/ui/assets/{js-realm-worker-Ymi_bwDP.js → js-realm-worker-BcijiBMH.js} +1 -1
  35. package/dist/ui/assets/{kernel-worker-CJu5HqlT.js → kernel-worker-BiaHa-Mz.js} +27 -27
  36. package/dist/ui/assets/loader-B23_5sFw.js +4 -0
  37. package/dist/ui/assets/{local-llm-CwFpBJ4H.js → local-llm-CQE3URQQ.js} +1 -1
  38. package/dist/ui/assets/{local-llm-C0c86vuh.js → local-llm-DVCKFUl_.js} +1 -1
  39. package/dist/ui/assets/loopback-2H1kMO8T.js +1 -0
  40. package/dist/ui/assets/main-DQS8QHXU.js +22 -0
  41. package/dist/ui/assets/{mount-CDH4T-Mp.js → mount-C20u28qg.js} +2 -2
  42. package/dist/ui/assets/{mount-CE7C-kQS.js → mount-DSlWOyRg.js} +1 -1
  43. package/dist/ui/assets/{mount-recovery-h9v3cRpd.js → mount-recovery-Dp2uzDT3.js} +1 -1
  44. package/dist/ui/assets/{new-session-CU8TGRAW.js → new-session-BmqHuPnd.js} +2 -2
  45. package/dist/ui/assets/{oauth-bootstrap-CRJLIz8V.js → oauth-bootstrap-CF5Iq7xU.js} +2 -2
  46. package/dist/ui/assets/{openai-codex-C2u_j09G.js → openai-codex-C2ilX51B.js} +1 -1
  47. package/dist/ui/assets/{openai-codex-DhoN0DsA.js → openai-codex-bhWf9evO.js} +1 -1
  48. package/dist/ui/assets/{openrouter-DvfkEaFA.js → openrouter-Bo62to17.js} +1 -1
  49. package/dist/ui/assets/{openrouter-DnjFjBY3.js → openrouter-C9_8OqEu.js} +1 -1
  50. package/dist/ui/assets/{openrouter-oauth-578SWfOt.js → openrouter-oauth-DvOb1QMV.js} +1 -1
  51. package/dist/ui/assets/{openrouter-oauth-BeOk-ba4.js → openrouter-oauth-Dy4Qqt06.js} +1 -1
  52. package/dist/ui/assets/{panel-rpc-handlers-CqcvBRTi.js → panel-rpc-handlers-Dbhx7m_G.js} +2 -2
  53. package/dist/ui/assets/{provider-B5MTJuhf.js → provider-Bk24TjUg.js} +1 -1
  54. package/dist/ui/assets/{provider-qCqf7_uc.js → provider-D2RXncrR.js} +2 -2
  55. package/dist/ui/assets/{provider-settings-NcM2Npzg.js → provider-settings-B5B0NtAl.js} +1 -1
  56. package/dist/ui/assets/{provider-settings-DcYK3m1J.js → provider-settings-BTpBo_o4.js} +1 -1
  57. package/dist/ui/assets/{provider-store-access-BlHUi5MH.js → provider-store-access-Cl5aZRx7.js} +2 -2
  58. package/dist/ui/assets/provider-store-access-CrgSlufJ.js +1 -0
  59. package/dist/ui/assets/providers-Crdj27DN.js +2 -0
  60. package/dist/ui/assets/{providers-BYRmRIj_.js → providers-DX7EJ4Fn.js} +1 -1
  61. package/dist/ui/assets/{quick-llm-CXBgg2d_.js → quick-llm-hL6bZGLe.js} +1 -1
  62. package/dist/ui/assets/{recovery-screen-CC3JYs-j.js → recovery-screen-MHXsY2RD.js} +1 -1
  63. package/dist/ui/assets/{redirect-uri-Cz0-DXMU.js → redirect-uri-B1SxiY-b.js} +1 -1
  64. package/dist/ui/assets/{session-freezer-DYVYVWZ3.js → session-freezer-CnFBCb6j.js} +3 -3
  65. package/dist/ui/assets/{setup-feature-flags-remote-DtLgZPds.js → setup-feature-flags-remote-Dlcwmn-_.js} +1 -1
  66. package/dist/ui/assets/setup-standalone-prelude-Btsjqjoe.js +2 -0
  67. package/dist/ui/assets/{setup-sudo-CGpFS_H9.js → setup-sudo-DrmeHwU_.js} +2 -2
  68. package/dist/ui/assets/silent-renew-backoff-C_Iv54ZG.js +1 -0
  69. package/dist/ui/assets/silent-renew-backoff-t_Cd4Zw7.js +1 -0
  70. package/dist/ui/assets/{skills-CfaCWaY6.js → skills-C90t15zW.js} +1 -1
  71. package/dist/ui/assets/slicc-version-tsUsi909.js +1 -0
  72. package/dist/ui/assets/{store-BQWc00Lo.js → store-BmhLnay6.js} +1 -1
  73. package/dist/ui/assets/{store-sdSLaoJp.js → store-Cvg04_Rz.js} +1 -1
  74. package/dist/ui/assets/{sudo-BTuTezs4.js → sudo-DYhM4jWf.js} +1 -1
  75. package/dist/ui/assets/{upgrade-detection-CmsEVCLH.js → upgrade-detection-Co2MZvYS.js} +1 -1
  76. package/dist/ui/assets/{wc-extension-wBWx11Lq.js → wc-extension-CL-LAE0W.js} +2 -2
  77. package/dist/ui/assets/wc-follower-troAtcMA.js +17 -0
  78. package/dist/ui/assets/wc-live-D1Mon0cw.js +8 -0
  79. package/dist/ui/assets/{wc-nav-B8ymo0Vu.js → wc-nav-BV6Jm98e.js} +2 -2
  80. package/dist/ui/assets/{wc-onboarding-CMJYjLlt.js → wc-onboarding-Dg4E9VDR.js} +2 -2
  81. package/dist/ui/assets/{wc-placeholder-DIOhu2Ac.js → wc-placeholder-_6RUOpYH.js} +2 -2
  82. package/dist/ui/assets/{wc-settings-D2oVzKVs.js → wc-settings-Dy5cPd8c.js} +2 -2
  83. package/dist/ui/assets/{wc-shell-DrXzWzBs.js → wc-shell-DC5bovWA.js} +2 -2
  84. package/dist/ui/assets/{wc-sprinkles-Hdy-5FTU.js → wc-sprinkles-CDP31nXS.js} +2 -2
  85. package/dist/ui/assets/{wc-tray-CfSIy5M0.js → wc-tray-MFZAPwt4.js} +2 -2
  86. package/dist/ui/assets/{xai-grok-B_POoF8K.js → xai-grok-C2GEvF8U.js} +1 -1
  87. package/dist/ui/assets/{xai-grok-D__MgEgm.js → xai-grok-CoSPbEFx.js} +1 -1
  88. package/dist/ui/index.html +8 -9
  89. package/dist/ui/llm-proxy-sw.js +1 -1
  90. package/dist/ui/packages/webapp/index.html +8 -9
  91. package/package.json +1 -1
  92. package/dist/ui/assets/bridge-launch-params-zrpWWxT3.js +0 -1
  93. package/dist/ui/assets/loader-sgPDMLWT.js +0 -4
  94. package/dist/ui/assets/main-BWIUe9mc.js +0 -22
  95. package/dist/ui/assets/provider-store-access-BN6c8KtH.js +0 -1
  96. package/dist/ui/assets/providers-BAjAn8LJ.js +0 -2
  97. package/dist/ui/assets/setup-standalone-prelude-B5yx5j_i.js +0 -2
  98. package/dist/ui/assets/silent-renew-backoff-BhyOB6Rq.js +0 -1
  99. package/dist/ui/assets/silent-renew-backoff-BrAZcbzt.js +0 -1
  100. package/dist/ui/assets/slicc-version-ClAVruxy.js +0 -1
  101. package/dist/ui/assets/wc-follower-Di7VVZ0z.js +0 -17
  102. package/dist/ui/assets/wc-live-1OpoOfN1.js +0 -8
@@ -5,6 +5,7 @@ export * from './cdp-frame-unmask.js';
5
5
  export * from './content-type.js';
6
6
  export * from './electron-overlay-route.js';
7
7
  export * from './fetch-proxy-protocol.js';
8
+ export * from './loopback.js';
8
9
  export * from './oauth-extra-domains-storage.js';
9
10
  export * from './preview-url.js';
10
11
  export * from './runtime-env.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,kCAAkC,CAAC;AACjD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC"}
@@ -6,6 +6,7 @@ export * from './cdp-frame-unmask.js';
6
6
  export * from './content-type.js';
7
7
  export * from './electron-overlay-route.js';
8
8
  export * from './fetch-proxy-protocol.js';
9
+ export * from './loopback.js';
9
10
  export * from './oauth-extra-domains-storage.js';
10
11
  export * from './preview-url.js';
11
12
  export * from './runtime-env.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AAEjF,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AAEjF,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,kCAAkC,CAAC;AACjD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Canonical loopback-host detection shared across security gates
3
+ * (bridge-token exemption, localhost-only endpoints, http: allowlists,
4
+ * OAuth redirect branching). One accepted set — do not reimplement.
5
+ *
6
+ * Accepts:
7
+ * - `localhost`
8
+ * - the entire `127.0.0.0/8` block (e.g. `127.0.0.1`, `127.0.0.2`)
9
+ * - IPv6 loopback, bracketed or bare (`[::1]`, `::1`)
10
+ */
11
+ /** True when `hostname` is a loopback name (no URL parse — host only). */
12
+ export declare function isLoopbackHostname(hostname: string): boolean;
13
+ /**
14
+ * True when `origin` parses as a URL whose hostname is loopback.
15
+ * Returns false for missing / empty / unparseable values.
16
+ */
17
+ export declare function isLoopbackOrigin(origin: string | undefined | null): boolean;
18
+ //# sourceMappingURL=loopback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loopback.d.ts","sourceRoot":"","sources":["../src/loopback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAW5D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAO3E"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Canonical loopback-host detection shared across security gates
3
+ * (bridge-token exemption, localhost-only endpoints, http: allowlists,
4
+ * OAuth redirect branching). One accepted set — do not reimplement.
5
+ *
6
+ * Accepts:
7
+ * - `localhost`
8
+ * - the entire `127.0.0.0/8` block (e.g. `127.0.0.1`, `127.0.0.2`)
9
+ * - IPv6 loopback, bracketed or bare (`[::1]`, `::1`)
10
+ */
11
+ /** True when `hostname` is a loopback name (no URL parse — host only). */
12
+ export function isLoopbackHostname(hostname) {
13
+ if (!hostname)
14
+ return false;
15
+ // WHATWG URL keeps brackets on IPv6 hostnames (`http://[::1]` → `[::1]`);
16
+ // socket addresses and some callers pass bare `::1`. Accept both.
17
+ const host = hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;
18
+ if (host === 'localhost')
19
+ return true;
20
+ if (host === '::1')
21
+ return true;
22
+ // 127.0.0.0/8 — octet shape only (same as the former webapp plugin gate).
23
+ if (/^127(\.\d{1,3}){3}$/.test(host))
24
+ return true;
25
+ return false;
26
+ }
27
+ /**
28
+ * True when `origin` parses as a URL whose hostname is loopback.
29
+ * Returns false for missing / empty / unparseable values.
30
+ */
31
+ export function isLoopbackOrigin(origin) {
32
+ if (!origin)
33
+ return false;
34
+ try {
35
+ return isLoopbackHostname(new URL(origin).hostname);
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ //# sourceMappingURL=loopback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loopback.js","sourceRoot":"","sources":["../src/loopback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,0EAA0E;AAC1E,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,0EAA0E;IAC1E,kEAAkE;IAClE,MAAM,IAAI,GACR,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACxF,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAChC,0EAA0E;IAC1E,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAiC;IAChE,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -42,12 +42,15 @@ export declare const BRIDGE_ALLOWED_ORIGINS: readonly string[];
42
42
  */
43
43
  export declare function isAllowedBridgeOrigin(origin: string | undefined | null): boolean;
44
44
  /**
45
- * True iff `origin` is a loopback host (localhost / 127.0.0.1 / ::1).
45
+ * True iff `origin` is a loopback host.
46
46
  * Loopback allowlisted origins (e.g. the locally-served OAuth callback
47
47
  * page at `http://localhost:5710/auth/callback` posting to
48
48
  * `/api/oauth-result`) are exempt from the bridge-token requirement —
49
49
  * the token's threat model is "remote allowlisted origin (sliccy.ai)
50
50
  * with a hostile script", not "local server talking to itself".
51
+ *
52
+ * Canonical set: `@slicc/shared-ts` `isLoopbackOrigin` (localhost,
53
+ * 127.0.0.0/8, bracketed/bare ::1).
51
54
  */
52
55
  export declare function isLoopbackBridgeOrigin(origin: string | undefined | null): boolean;
53
56
  /**
@@ -19,7 +19,7 @@
19
19
  * testable from `tests/bridge-security.test.ts`.
20
20
  */
21
21
  import { randomUUID, timingSafeEqual } from 'node:crypto';
22
- import { BRIDGE_SUBPROTOCOL_PREFIX, BRIDGE_TOKEN_HEADER, BRIDGE_TOKEN_QUERY_PARAM, BRIDGE_WS_QUERY_PARAM, SLICC_HOSTED_ORIGIN, SLICC_STAGING_HUB_ORIGIN, } from './_shared/index.js';
22
+ import { BRIDGE_SUBPROTOCOL_PREFIX, BRIDGE_TOKEN_HEADER, BRIDGE_TOKEN_QUERY_PARAM, BRIDGE_WS_QUERY_PARAM, isLoopbackOrigin, SLICC_HOSTED_ORIGIN, SLICC_STAGING_HUB_ORIGIN, } from './_shared/index.js';
23
23
  // Re-exported so the many existing bridge-security importers (index.ts,
24
24
  // launch-url.ts, electron-controller.ts, tests) keep their import surface.
25
25
  // Canonical definitions: packages/shared-ts/src/bridge-protocol.ts.
@@ -144,28 +144,18 @@ export function isAllowedBridgeOrigin(origin) {
144
144
  return BRIDGE_DEV_ALLOWED_ORIGINS.has(normalized);
145
145
  }
146
146
  /**
147
- * True iff `origin` is a loopback host (localhost / 127.0.0.1 / ::1).
147
+ * True iff `origin` is a loopback host.
148
148
  * Loopback allowlisted origins (e.g. the locally-served OAuth callback
149
149
  * page at `http://localhost:5710/auth/callback` posting to
150
150
  * `/api/oauth-result`) are exempt from the bridge-token requirement —
151
151
  * the token's threat model is "remote allowlisted origin (sliccy.ai)
152
152
  * with a hostile script", not "local server talking to itself".
153
+ *
154
+ * Canonical set: `@slicc/shared-ts` `isLoopbackOrigin` (localhost,
155
+ * 127.0.0.0/8, bracketed/bare ::1).
153
156
  */
154
157
  export function isLoopbackBridgeOrigin(origin) {
155
- if (!origin)
156
- return false;
157
- try {
158
- const url = new URL(origin);
159
- // Node's WHATWG URL parser keeps the brackets on IPv6 hostnames
160
- // (`http://[::1]:5710` → `[::1]`); accept both bracketed and bare.
161
- return (url.hostname === 'localhost' ||
162
- url.hostname === '127.0.0.1' ||
163
- url.hostname === '::1' ||
164
- url.hostname === '[::1]');
165
- }
166
- catch {
167
- return false;
168
- }
158
+ return isLoopbackOrigin(origin);
169
159
  }
170
160
  /**
171
161
  * Constant-time compare for the bridge token. `presented` may be missing
@@ -15,6 +15,9 @@ export interface CloudStatusPayload {
15
15
  * Reject non-loopback requests. The sandbox is a private execution boundary,
16
16
  * but defense in depth: someone might wire a port-forward and we want this
17
17
  * endpoint to be unreachable from the outside.
18
+ *
19
+ * Socket addresses may be IPv4-mapped IPv6 (`::ffff:127.0.0.1`); strip that
20
+ * prefix then use the shared loopback hostname check.
18
21
  */
19
22
  export declare function requireLoopback(req: Request, res: Response, next: NextFunction): void;
20
23
  export declare function registerCloudStatusEndpoint(app: Express, options: CloudStatusEndpointOptions): void;
@@ -1,4 +1,5 @@
1
1
  import { promises as fs } from 'node:fs';
2
+ import { isLoopbackHostname } from './_shared/index.js';
2
3
  import express from 'express';
3
4
  /** The optional string fields validated when present but never required. */
4
5
  const OPTIONAL_STRING_FIELDS = [
@@ -25,11 +26,14 @@ function isCloudStatusPayload(x) {
25
26
  * Reject non-loopback requests. The sandbox is a private execution boundary,
26
27
  * but defense in depth: someone might wire a port-forward and we want this
27
28
  * endpoint to be unreachable from the outside.
29
+ *
30
+ * Socket addresses may be IPv4-mapped IPv6 (`::ffff:127.0.0.1`); strip that
31
+ * prefix then use the shared loopback hostname check.
28
32
  */
29
33
  export function requireLoopback(req, res, next) {
30
34
  const addr = req.socket.remoteAddress ?? '';
31
- const isLoopback = addr === '127.0.0.1' || addr === '::1' || addr === '::ffff:127.0.0.1' || addr === 'localhost';
32
- if (!isLoopback) {
35
+ const normalized = addr.toLowerCase().startsWith('::ffff:') ? addr.slice('::ffff:'.length) : addr;
36
+ if (!isLoopbackHostname(normalized)) {
33
37
  res.status(403).json({ error: 'localhost only' });
34
38
  return;
35
39
  }
@@ -1,6 +1,6 @@
1
1
  import { Readable, Transform } from 'node:stream';
2
2
  import { StringDecoder } from 'node:string_decoder';
3
- import { HMAC_SIGN_HEADER, isTextContentType } from '../_shared/index.js';
3
+ import { HMAC_SIGN_HEADER, isLoopbackOrigin, isTextContentType } from '../_shared/index.js';
4
4
  import { FETCH_PROXY_CONTENT_LENGTH_HEADER, FETCH_PROXY_SKIP_HEADERS, FETCH_PROXY_SKIP_RESPONSE_HEADERS, FETCH_PROXY_SKIP_RESPONSE_PREFIXES, } from '../fetch-proxy-headers.js';
5
5
  import { AgentActivityTracker, registerAgentActivityRoute } from './agent-activity.js';
6
6
  /** Pick the first value of a possibly-multi-valued request header. */
@@ -9,21 +9,6 @@ function firstHeaderValue(value) {
9
9
  return undefined;
10
10
  return Array.isArray(value) ? value[0] : value;
11
11
  }
12
- /** True when an origin/referer value points at localhost (any family). */
13
- function isLocalhostOrigin(origin) {
14
- if (!origin)
15
- return false;
16
- try {
17
- const url = new URL(origin);
18
- return (url.hostname === 'localhost' ||
19
- url.hostname === '127.0.0.1' ||
20
- url.hostname === '::1' ||
21
- url.hostname === '[::1]');
22
- }
23
- catch {
24
- return false;
25
- }
26
- }
27
12
  /** Get the body — either from express.json()'s parsed body or raw chunks. */
28
13
  async function collectRawBody(req) {
29
14
  if (req.body && typeof req.body === 'object' && Object.keys(req.body).length > 0) {
@@ -58,7 +43,7 @@ function buildForwardHeaders(req, targetUrl) {
58
43
  if (proxyOrigin) {
59
44
  headers.origin = proxyOrigin;
60
45
  }
61
- else if (isLocalhostOrigin(headers.origin)) {
46
+ else if (isLoopbackOrigin(headers.origin)) {
62
47
  // Only strip the browser's auto-added localhost origin; preserve legitimate origins.
63
48
  delete headers.origin;
64
49
  }
@@ -77,7 +62,7 @@ function buildForwardHeaders(req, targetUrl) {
77
62
  if (proxyReferer) {
78
63
  headers.referer = proxyReferer;
79
64
  }
80
- else if (isLocalhostOrigin(headers.referer)) {
65
+ else if (isLoopbackOrigin(headers.referer)) {
81
66
  delete headers.referer;
82
67
  }
83
68
  // Restore any X-Proxy-Proxy-* transport headers as Proxy-* headers