@openparachute/hub 0.5.7 → 0.5.10-rc.10

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 (85) hide show
  1. package/package.json +1 -1
  2. package/src/__tests__/admin-clients.test.ts +275 -0
  3. package/src/__tests__/admin-handlers.test.ts +70 -323
  4. package/src/__tests__/admin-host-admin-token.test.ts +52 -4
  5. package/src/__tests__/api-me.test.ts +149 -0
  6. package/src/__tests__/api-mint-token.test.ts +381 -0
  7. package/src/__tests__/api-modules-ops.test.ts +658 -0
  8. package/src/__tests__/api-modules.test.ts +426 -0
  9. package/src/__tests__/api-revocation-list.test.ts +198 -0
  10. package/src/__tests__/api-revoke-token.test.ts +320 -0
  11. package/src/__tests__/api-tokens.test.ts +629 -0
  12. package/src/__tests__/auth.test.ts +680 -16
  13. package/src/__tests__/csrf.test.ts +40 -1
  14. package/src/__tests__/expose-2fa-warning.test.ts +3 -5
  15. package/src/__tests__/expose-cloudflare.test.ts +1 -1
  16. package/src/__tests__/expose.test.ts +2 -2
  17. package/src/__tests__/hub-server.test.ts +584 -67
  18. package/src/__tests__/hub-settings.test.ts +377 -0
  19. package/src/__tests__/hub.test.ts +123 -53
  20. package/src/__tests__/install-source.test.ts +249 -0
  21. package/src/__tests__/jwt-sign.test.ts +205 -0
  22. package/src/__tests__/module-manifest.test.ts +48 -0
  23. package/src/__tests__/oauth-handlers.test.ts +522 -5
  24. package/src/__tests__/operator-token.test.ts +427 -3
  25. package/src/__tests__/origin-check.test.ts +220 -0
  26. package/src/__tests__/request-protocol.test.ts +54 -0
  27. package/src/__tests__/serve-boot.test.ts +193 -0
  28. package/src/__tests__/serve.test.ts +100 -0
  29. package/src/__tests__/sessions.test.ts +25 -2
  30. package/src/__tests__/setup-gate.test.ts +222 -0
  31. package/src/__tests__/setup-wizard.test.ts +2089 -0
  32. package/src/__tests__/status.test.ts +199 -0
  33. package/src/__tests__/supervisor.test.ts +482 -0
  34. package/src/__tests__/upgrade.test.ts +247 -4
  35. package/src/__tests__/vault-name.test.ts +79 -0
  36. package/src/__tests__/well-known.test.ts +69 -0
  37. package/src/admin-clients.ts +139 -0
  38. package/src/admin-handlers.ts +37 -254
  39. package/src/admin-host-admin-token.ts +25 -10
  40. package/src/admin-login-ui.ts +256 -0
  41. package/src/admin-vault-admin-token.ts +1 -1
  42. package/src/api-me.ts +124 -0
  43. package/src/api-mint-token.ts +239 -0
  44. package/src/api-modules-ops.ts +585 -0
  45. package/src/api-modules.ts +367 -0
  46. package/src/api-revocation-list.ts +59 -0
  47. package/src/api-revoke-token.ts +153 -0
  48. package/src/api-tokens.ts +224 -0
  49. package/src/cli.ts +28 -0
  50. package/src/commands/auth.ts +408 -51
  51. package/src/commands/expose-2fa-warning.ts +6 -6
  52. package/src/commands/serve-boot.ts +133 -0
  53. package/src/commands/serve.ts +214 -0
  54. package/src/commands/status.ts +74 -10
  55. package/src/commands/upgrade.ts +33 -6
  56. package/src/csrf.ts +34 -13
  57. package/src/help.ts +55 -5
  58. package/src/hub-control.ts +1 -0
  59. package/src/hub-db.ts +87 -0
  60. package/src/hub-server.ts +767 -136
  61. package/src/hub-settings.ts +259 -0
  62. package/src/hub.ts +298 -150
  63. package/src/install-source.ts +291 -0
  64. package/src/jwt-sign.ts +265 -5
  65. package/src/module-manifest.ts +48 -10
  66. package/src/oauth-handlers.ts +262 -56
  67. package/src/oauth-ui.ts +23 -2
  68. package/src/operator-token.ts +349 -18
  69. package/src/origin-check.ts +127 -0
  70. package/src/rate-limit.ts +5 -2
  71. package/src/request-protocol.ts +48 -0
  72. package/src/scope-explanations.ts +33 -2
  73. package/src/sessions.ts +30 -18
  74. package/src/setup-wizard.ts +2009 -0
  75. package/src/supervisor.ts +411 -0
  76. package/src/vault-name.ts +71 -0
  77. package/src/well-known.ts +54 -1
  78. package/web/ui/dist/assets/index-BDSEsaBY.css +1 -0
  79. package/web/ui/dist/assets/index-CP07NbdF.js +61 -0
  80. package/web/ui/dist/index.html +2 -2
  81. package/src/__tests__/admin-config.test.ts +0 -281
  82. package/src/admin-config-ui.ts +0 -534
  83. package/src/admin-config.ts +0 -226
  84. package/web/ui/dist/assets/index-BKzPDdB0.js +0 -60
  85. package/web/ui/dist/assets/index-Dyk6g7vT.css +0 -1
@@ -29,7 +29,7 @@ describe("generateCsrfToken", () => {
29
29
  });
30
30
 
31
31
  describe("buildCsrfCookie", () => {
32
- test("emits the expected attributes", () => {
32
+ test("emits the expected attributes (default secure)", () => {
33
33
  const v = buildCsrfCookie("abc");
34
34
  expect(v).toContain(`${CSRF_COOKIE_NAME}=abc`);
35
35
  expect(v).toContain("HttpOnly");
@@ -38,6 +38,19 @@ describe("buildCsrfCookie", () => {
38
38
  expect(v).toContain("Path=/");
39
39
  expect(v).toContain("Max-Age=");
40
40
  });
41
+
42
+ test("omits Secure when secure: false (HTTP localhost)", () => {
43
+ const v = buildCsrfCookie("abc", { secure: false });
44
+ expect(v).toContain(`${CSRF_COOKIE_NAME}=abc`);
45
+ expect(v).toContain("HttpOnly");
46
+ expect(v).not.toContain("Secure");
47
+ expect(v).toContain("SameSite=Lax");
48
+ });
49
+
50
+ test("keeps Secure when secure: true (explicit)", () => {
51
+ const v = buildCsrfCookie("abc", { secure: true });
52
+ expect(v).toContain("Secure");
53
+ });
41
54
  });
42
55
 
43
56
  describe("parseCsrfCookie", () => {
@@ -71,6 +84,32 @@ describe("ensureCsrfToken", () => {
71
84
  expect(result.token.length).toBeGreaterThan(0);
72
85
  expect(result.setCookie).toBeDefined();
73
86
  });
87
+
88
+ // Bug 1 (rc.5 → rc.6) regression: cookies minted over plain HTTP must
89
+ // NOT carry the Secure attribute or browsers silently drop them on
90
+ // http://localhost:1939, breaking the very next double-submit POST.
91
+ test("sets Secure when the request URL is https://", () => {
92
+ const req = new Request("https://hub.example/admin/setup");
93
+ const result = ensureCsrfToken(req);
94
+ expect(result.setCookie).toContain("Secure");
95
+ });
96
+
97
+ test("omits Secure when the request URL is http://localhost", () => {
98
+ const req = new Request("http://localhost:1939/admin/setup");
99
+ const result = ensureCsrfToken(req);
100
+ expect(result.setCookie).not.toContain("Secure");
101
+ // The rest of the cookie shape stays intact — only Secure flips.
102
+ expect(result.setCookie).toContain("HttpOnly");
103
+ expect(result.setCookie).toContain("SameSite=Lax");
104
+ });
105
+
106
+ test("sets Secure when http:// request carries X-Forwarded-Proto: https", () => {
107
+ const req = new Request("http://hub.internal/admin/setup", {
108
+ headers: { "x-forwarded-proto": "https" },
109
+ });
110
+ const result = ensureCsrfToken(req);
111
+ expect(result.setCookie).toContain("Secure");
112
+ });
74
113
  });
75
114
 
76
115
  describe("verifyCsrfToken", () => {
@@ -81,7 +81,7 @@ describe("printPublic2FAWarning", () => {
81
81
  expect(fired).toBe(true);
82
82
  const joined = logs.join("\n");
83
83
  expect(joined).toContain("2FA is not enrolled");
84
- expect(joined).toContain("https://vault.example.com/admin/login");
84
+ expect(joined).toContain("https://vault.example.com/login");
85
85
  expect(joined).toContain("parachute auth 2fa enroll");
86
86
  });
87
87
 
@@ -111,15 +111,13 @@ describe("printPublic2FAWarning", () => {
111
111
  expect(logs.some((l) => l.includes("2FA is not enrolled"))).toBe(true);
112
112
  });
113
113
 
114
- test("embeds the supplied publicUrl into the /admin/login pointer", () => {
114
+ test("embeds the supplied publicUrl into the /login pointer", () => {
115
115
  const logs: string[] = [];
116
116
  printPublic2FAWarning({
117
117
  status: status({ hasTotp: false }),
118
118
  log: (l) => logs.push(l),
119
119
  publicUrl: "https://parachute.taildf9ce2.ts.net",
120
120
  });
121
- expect(logs.some((l) => l.includes("https://parachute.taildf9ce2.ts.net/admin/login"))).toBe(
122
- true,
123
- );
121
+ expect(logs.some((l) => l.includes("https://parachute.taildf9ce2.ts.net/login"))).toBe(true);
124
122
  });
125
123
  });
@@ -557,7 +557,7 @@ describe("exposeCloudflareUp", () => {
557
557
  expect(code).toBe(0);
558
558
  const joined = logs.join("\n");
559
559
  expect(joined).toContain("2FA is not enrolled");
560
- expect(joined).toContain("https://vault.example.com/admin/login");
560
+ expect(joined).toContain("https://vault.example.com/login");
561
561
  expect(joined).toContain("parachute auth 2fa enroll");
562
562
  } finally {
563
563
  env.cleanup();
@@ -963,8 +963,8 @@ describe("expose public up", () => {
963
963
  const joined = logs.join("\n");
964
964
  expect(joined).toContain("2FA is not enrolled");
965
965
  expect(joined).toContain("parachute auth 2fa enroll");
966
- // /admin/login pointer uses the canonical https://<fqdn> origin.
967
- expect(joined).toContain("https://parachute.taildf9ce2.ts.net/admin/login");
966
+ // /login pointer uses the canonical https://<fqdn> origin.
967
+ expect(joined).toContain("https://parachute.taildf9ce2.ts.net/login");
968
968
  } finally {
969
969
  h.cleanup();
970
970
  }