@koda-sl/baker-bridge 0.122.0-dev.3613db443 → 0.122.0-dev.71ff4e44a

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 (146) hide show
  1. package/README.md +27 -4
  2. package/dist/broker/baker-api.d.ts +23 -0
  3. package/dist/broker/baker-api.d.ts.map +1 -0
  4. package/dist/broker/baker-api.js +55 -0
  5. package/dist/broker/baker-api.js.map +1 -0
  6. package/dist/broker/broker-client.d.ts +10 -0
  7. package/dist/broker/broker-client.d.ts.map +1 -0
  8. package/dist/broker/broker-client.js +60 -0
  9. package/dist/broker/broker-client.js.map +1 -0
  10. package/dist/broker/ca.d.ts +38 -0
  11. package/dist/broker/ca.d.ts.map +1 -0
  12. package/dist/broker/ca.js +80 -0
  13. package/dist/broker/ca.js.map +1 -0
  14. package/dist/broker/ca.test.d.ts +2 -0
  15. package/dist/broker/ca.test.d.ts.map +1 -0
  16. package/dist/broker/ca.test.js +58 -0
  17. package/dist/broker/ca.test.js.map +1 -0
  18. package/dist/broker/credential-cache.d.ts +24 -0
  19. package/dist/broker/credential-cache.d.ts.map +1 -0
  20. package/dist/broker/credential-cache.js +69 -0
  21. package/dist/broker/credential-cache.js.map +1 -0
  22. package/dist/broker/credential-cache.test.d.ts +2 -0
  23. package/dist/broker/credential-cache.test.d.ts.map +1 -0
  24. package/dist/broker/credential-cache.test.js +120 -0
  25. package/dist/broker/credential-cache.test.js.map +1 -0
  26. package/dist/broker/credential-text.d.ts +45 -0
  27. package/dist/broker/credential-text.d.ts.map +1 -0
  28. package/dist/broker/credential-text.js +103 -0
  29. package/dist/broker/credential-text.js.map +1 -0
  30. package/dist/broker/credential-text.test.d.ts +2 -0
  31. package/dist/broker/credential-text.test.d.ts.map +1 -0
  32. package/dist/broker/credential-text.test.js +54 -0
  33. package/dist/broker/credential-text.test.js.map +1 -0
  34. package/dist/broker/env.d.ts +19 -0
  35. package/dist/broker/env.d.ts.map +1 -0
  36. package/dist/broker/env.js +16 -0
  37. package/dist/broker/env.js.map +1 -0
  38. package/dist/broker/main.d.ts +3 -0
  39. package/dist/broker/main.d.ts.map +1 -0
  40. package/dist/broker/main.js +51 -0
  41. package/dist/broker/main.js.map +1 -0
  42. package/dist/broker/proxy.d.ts +24 -0
  43. package/dist/broker/proxy.d.ts.map +1 -0
  44. package/dist/broker/proxy.integration.test.d.ts +2 -0
  45. package/dist/broker/proxy.integration.test.d.ts.map +1 -0
  46. package/dist/broker/proxy.integration.test.js +201 -0
  47. package/dist/broker/proxy.integration.test.js.map +1 -0
  48. package/dist/broker/proxy.js +147 -0
  49. package/dist/broker/proxy.js.map +1 -0
  50. package/dist/broker/rewrite.d.ts +44 -0
  51. package/dist/broker/rewrite.d.ts.map +1 -0
  52. package/dist/broker/rewrite.js +291 -0
  53. package/dist/broker/rewrite.js.map +1 -0
  54. package/dist/broker/rewrite.test.d.ts +2 -0
  55. package/dist/broker/rewrite.test.d.ts.map +1 -0
  56. package/dist/broker/rewrite.test.js +171 -0
  57. package/dist/broker/rewrite.test.js.map +1 -0
  58. package/dist/broker/socket-service.d.ts +18 -0
  59. package/dist/broker/socket-service.d.ts.map +1 -0
  60. package/dist/broker/socket-service.js +103 -0
  61. package/dist/broker/socket-service.js.map +1 -0
  62. package/dist/broker/socket-service.test.d.ts +2 -0
  63. package/dist/broker/socket-service.test.d.ts.map +1 -0
  64. package/dist/broker/socket-service.test.js +73 -0
  65. package/dist/broker/socket-service.test.js.map +1 -0
  66. package/dist/broker/token-bucket.d.ts +9 -0
  67. package/dist/broker/token-bucket.d.ts.map +1 -0
  68. package/dist/broker/token-bucket.js +18 -0
  69. package/dist/broker/token-bucket.js.map +1 -0
  70. package/dist/broker/usage-reporter.d.ts +18 -0
  71. package/dist/broker/usage-reporter.d.ts.map +1 -0
  72. package/dist/broker/usage-reporter.js +31 -0
  73. package/dist/broker/usage-reporter.js.map +1 -0
  74. package/dist/broker/usage-reporter.test.d.ts +2 -0
  75. package/dist/broker/usage-reporter.test.d.ts.map +1 -0
  76. package/dist/broker/usage-reporter.test.js +20 -0
  77. package/dist/broker/usage-reporter.test.js.map +1 -0
  78. package/dist/hono/agent-session.d.ts +3 -0
  79. package/dist/hono/agent-session.d.ts.map +1 -1
  80. package/dist/hono/agent-session.js +15 -0
  81. package/dist/hono/agent-session.js.map +1 -1
  82. package/dist/hono/agent.d.ts +2 -0
  83. package/dist/hono/agent.d.ts.map +1 -1
  84. package/dist/hono/agent.js +10 -2
  85. package/dist/hono/agent.js.map +1 -1
  86. package/dist/hono/avatar-casting.d.ts +1 -0
  87. package/dist/hono/avatar-casting.d.ts.map +1 -1
  88. package/dist/hono/avatar-casting.js +15 -2
  89. package/dist/hono/avatar-casting.js.map +1 -1
  90. package/dist/hono/avatar-casting.test.js +5 -0
  91. package/dist/hono/avatar-casting.test.js.map +1 -1
  92. package/dist/hono/baker-ui-tool-schemas.test.js +6 -0
  93. package/dist/hono/baker-ui-tool-schemas.test.js.map +1 -1
  94. package/dist/hono/bridge-job.d.ts +15 -1
  95. package/dist/hono/bridge-job.d.ts.map +1 -1
  96. package/dist/hono/bridge-job.js +85 -9
  97. package/dist/hono/bridge-job.js.map +1 -1
  98. package/dist/hono/bridge-job.test.js +145 -1
  99. package/dist/hono/bridge-job.test.js.map +1 -1
  100. package/dist/hono/connection-input.d.ts +11 -1
  101. package/dist/hono/connection-input.d.ts.map +1 -1
  102. package/dist/hono/connection-input.js +30 -5
  103. package/dist/hono/connection-input.js.map +1 -1
  104. package/dist/hono/connection-input.test.js +28 -8
  105. package/dist/hono/connection-input.test.js.map +1 -1
  106. package/dist/hono/convex.d.ts +8 -0
  107. package/dist/hono/convex.d.ts.map +1 -1
  108. package/dist/hono/convex.js +8 -0
  109. package/dist/hono/convex.js.map +1 -1
  110. package/dist/hono/credential-input.d.ts +39 -0
  111. package/dist/hono/credential-input.d.ts.map +1 -0
  112. package/dist/hono/credential-input.js +78 -0
  113. package/dist/hono/credential-input.js.map +1 -0
  114. package/dist/hono/credential-scrub.d.ts +41 -0
  115. package/dist/hono/credential-scrub.d.ts.map +1 -0
  116. package/dist/hono/credential-scrub.js +152 -0
  117. package/dist/hono/credential-scrub.js.map +1 -0
  118. package/dist/hono/credential-scrub.test.d.ts +2 -0
  119. package/dist/hono/credential-scrub.test.d.ts.map +1 -0
  120. package/dist/hono/credential-scrub.test.js +105 -0
  121. package/dist/hono/credential-scrub.test.js.map +1 -0
  122. package/dist/hono/latitude-telemetry.d.ts.map +1 -1
  123. package/dist/hono/latitude-telemetry.js +8 -2
  124. package/dist/hono/latitude-telemetry.js.map +1 -1
  125. package/dist/hono/prompt-context.d.ts +8 -1
  126. package/dist/hono/prompt-context.d.ts.map +1 -1
  127. package/dist/hono/prompt-context.js +45 -7
  128. package/dist/hono/prompt-context.js.map +1 -1
  129. package/dist/hono/prompt-context.test.js +35 -1
  130. package/dist/hono/prompt-context.test.js.map +1 -1
  131. package/dist/hono/relay.d.ts +10 -1
  132. package/dist/hono/relay.d.ts.map +1 -1
  133. package/dist/hono/relay.js +6 -0
  134. package/dist/hono/relay.js.map +1 -1
  135. package/dist/hono/server.d.ts.map +1 -1
  136. package/dist/hono/server.js +22 -0
  137. package/dist/hono/server.js.map +1 -1
  138. package/dist/hono/tag-input.d.ts +106 -3
  139. package/dist/hono/tag-input.d.ts.map +1 -1
  140. package/dist/hono/tag-input.js +20 -9
  141. package/dist/hono/tag-input.js.map +1 -1
  142. package/dist/hono/tag-input.test.js +19 -1
  143. package/dist/hono/tag-input.test.js.map +1 -1
  144. package/dist/hono/types.d.ts +8 -0
  145. package/dist/hono/types.d.ts.map +1 -1
  146. package/package.json +5 -2
@@ -0,0 +1,120 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { CredentialsRefused } from "./baker-api.js";
3
+ import { createCredentialCache } from "./credential-cache.js";
4
+ function fetcher() {
5
+ let calls = 0;
6
+ return {
7
+ get calls() {
8
+ return calls;
9
+ },
10
+ fetchCredentials: async () => {
11
+ await Promise.resolve();
12
+ calls += 1;
13
+ return [{ name: `v${calls}`, value: "secret-value", allowedHosts: ["api.example.com"] }];
14
+ },
15
+ };
16
+ }
17
+ describe("createCredentialCache", () => {
18
+ it("reuses the table for 10 seconds, then fetches it again", async () => {
19
+ let clock = 0;
20
+ const source = fetcher();
21
+ const cache = createCredentialCache({ fetchCredentials: source.fetchCredentials, now: () => clock });
22
+ await cache.get();
23
+ clock = 9_999;
24
+ await cache.get();
25
+ clock = 10_000;
26
+ const table = await cache.get();
27
+ expect(source.calls).toBe(2);
28
+ expect(table?.map((credential) => credential.name)).toEqual(["v2"]);
29
+ });
30
+ it("keeps the last good table when a refresh fails, and has none when no fetch ever succeeded", async () => {
31
+ let clock = 0;
32
+ let failing = true;
33
+ const cache = createCredentialCache({
34
+ fetchCredentials: async () => {
35
+ await Promise.resolve();
36
+ if (failing)
37
+ throw new Error("502 upstream");
38
+ return [{ name: "shop", value: "secret-value", allowedHosts: ["api.example.com"] }];
39
+ },
40
+ now: () => clock,
41
+ });
42
+ expect(await cache.get()).toBeNull();
43
+ failing = false;
44
+ clock = 10_000;
45
+ await cache.get();
46
+ failing = true;
47
+ clock = 20_000;
48
+ expect((await cache.get())?.map((credential) => credential.name)).toEqual(["shop"]);
49
+ });
50
+ it("drops the table once Baker refuses the broker, so no value is sent after Credentials are switched off", async () => {
51
+ let clock = 0;
52
+ let refused = false;
53
+ const cache = createCredentialCache({
54
+ fetchCredentials: async () => {
55
+ await Promise.resolve();
56
+ if (refused)
57
+ throw new CredentialsRefused(401);
58
+ return [{ name: "shop", value: "secret-value", allowedHosts: ["api.example.com"] }];
59
+ },
60
+ now: () => clock,
61
+ });
62
+ await cache.get();
63
+ refused = true;
64
+ clock = 10_000;
65
+ expect(await cache.get()).toBeNull();
66
+ expect(cache.size()).toBe(0);
67
+ });
68
+ it("still scrubs a value it may have sent after Baker refuses the broker or the credential is removed", async () => {
69
+ let clock = 0;
70
+ let table = [
71
+ { name: "shop", value: "shop-secret-value", allowedHosts: ["api.example.com"] },
72
+ { name: "ads", value: "ads-secret-value", allowedHosts: ["api.example.com"] },
73
+ ];
74
+ let refused = false;
75
+ const cache = createCredentialCache({
76
+ fetchCredentials: async () => {
77
+ await Promise.resolve();
78
+ if (refused)
79
+ throw new CredentialsRefused(401);
80
+ return table;
81
+ },
82
+ now: () => clock,
83
+ });
84
+ await cache.get();
85
+ table = table.slice(0, 1);
86
+ clock = 10_000;
87
+ const afterRemoval = (await cache.scrubbable()).map((credential) => credential.value);
88
+ refused = true;
89
+ clock = 20_000;
90
+ const afterRefusal = (await cache.scrubbable()).map((credential) => credential.value);
91
+ expect(afterRemoval.sort()).toEqual(["ads-secret-value", "shop-secret-value"]);
92
+ expect(afterRefusal.sort()).toEqual(["ads-secret-value", "shop-secret-value"]);
93
+ expect(await cache.get()).toBeNull();
94
+ });
95
+ it("fetches again on the next read after a refresh", async () => {
96
+ const source = fetcher();
97
+ const cache = createCredentialCache({ fetchCredentials: source.fetchCredentials, now: () => 0 });
98
+ await cache.get();
99
+ cache.refresh();
100
+ await cache.get();
101
+ expect(source.calls).toBe(2);
102
+ });
103
+ it("refreshes early at most once every 5 seconds, so refused retries cannot make it fetch on every request", async () => {
104
+ let clock = 0;
105
+ const source = fetcher();
106
+ const cache = createCredentialCache({ fetchCredentials: source.fetchCredentials, now: () => clock });
107
+ await cache.get();
108
+ const refreshed = [];
109
+ for (clock = 0; clock < 5_000; clock += 500) {
110
+ refreshed.push(cache.refresh());
111
+ await cache.get();
112
+ }
113
+ clock = 5_000;
114
+ refreshed.push(cache.refresh());
115
+ await cache.get();
116
+ expect(refreshed.filter(Boolean)).toHaveLength(2);
117
+ expect(source.calls).toBe(3);
118
+ });
119
+ });
120
+ //# sourceMappingURL=credential-cache.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-cache.test.js","sourceRoot":"","sources":["../../src/broker/credential-cache.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,SAAS,OAAO;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO;QACL,IAAI,KAAK;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QACD,gBAAgB,EAAE,KAAK,IAAI,EAAE;YAC3B,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,KAAK,IAAI,CAAC,CAAC;YACX,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;QAC3F,CAAC;KACF,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,MAAM,MAAM,GAAG,OAAO,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;QAErG,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAClB,KAAK,GAAG,KAAK,CAAC;QACd,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAClB,KAAK,GAAG,MAAM,CAAC;QACf,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAEhC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2FAA2F,EAAE,KAAK,IAAI,EAAE;QACzG,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,OAAO,GAAG,IAAI,CAAC;QACnB,MAAM,KAAK,GAAG,qBAAqB,CAAC;YAClC,gBAAgB,EAAE,KAAK,IAAI,EAAE;gBAC3B,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;gBACxB,IAAI,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;gBAC7C,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;YACtF,CAAC;YACD,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;SACjB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QACrC,OAAO,GAAG,KAAK,CAAC;QAChB,KAAK,GAAG,MAAM,CAAC;QACf,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAClB,OAAO,GAAG,IAAI,CAAC;QACf,KAAK,GAAG,MAAM,CAAC;QAEf,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uGAAuG,EAAE,KAAK,IAAI,EAAE;QACrH,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,KAAK,GAAG,qBAAqB,CAAC;YAClC,gBAAgB,EAAE,KAAK,IAAI,EAAE;gBAC3B,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;gBACxB,IAAI,OAAO;oBAAE,MAAM,IAAI,kBAAkB,CAAC,GAAG,CAAC,CAAC;gBAC/C,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;YACtF,CAAC;YACD,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;SACjB,CAAC,CAAC;QACH,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAElB,OAAO,GAAG,IAAI,CAAC;QACf,KAAK,GAAG,MAAM,CAAC;QAEf,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mGAAmG,EAAE,KAAK,IAAI,EAAE;QACjH,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,KAAK,GAAG;YACV,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,CAAC,iBAAiB,CAAC,EAAE;YAC/E,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,CAAC,iBAAiB,CAAC,EAAE;SAC9E,CAAC;QACF,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,KAAK,GAAG,qBAAqB,CAAC;YAClC,gBAAgB,EAAE,KAAK,IAAI,EAAE;gBAC3B,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;gBACxB,IAAI,OAAO;oBAAE,MAAM,IAAI,kBAAkB,CAAC,GAAG,CAAC,CAAC;gBAC/C,OAAO,KAAK,CAAC;YACf,CAAC;YACD,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;SACjB,CAAC,CAAC;QACH,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAElB,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1B,KAAK,GAAG,MAAM,CAAC;QACf,MAAM,YAAY,GAAG,CAAC,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACtF,OAAO,GAAG,IAAI,CAAC;QACf,KAAK,GAAG,MAAM,CAAC;QACf,MAAM,YAAY,GAAG,CAAC,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAEtF,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC/E,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC/E,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,MAAM,GAAG,OAAO,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAEjG,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAClB,KAAK,CAAC,OAAO,EAAE,CAAC;QAChB,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAElB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wGAAwG,EAAE,KAAK,IAAI,EAAE;QACtH,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,MAAM,MAAM,GAAG,OAAO,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;QACrG,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAElB,MAAM,SAAS,GAAG,EAAE,CAAC;QACrB,KAAK,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,GAAG,EAAE,CAAC;YAC5C,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAChC,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QACpB,CAAC;QACD,KAAK,GAAG,KAAK,CAAC;QACd,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAChC,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;QAElB,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Hand-copy of the placeholder grammar, host check and scrub in
3
+ * `packages/api/src/credentials/{placeholder,hosts,scrub}.ts`. The bridge is
4
+ * published standalone to npm and cannot depend on the private `@baker/api`,
5
+ * so these are copied rather than imported; `credential-text.test.ts` runs both
6
+ * copies over the same inputs and fails when this one drifts.
7
+ */
8
+ export type CredentialPlaceholder = {
9
+ name: string;
10
+ start: number;
11
+ end: number;
12
+ };
13
+ export type ResolvedCredential = {
14
+ name: string;
15
+ value: string;
16
+ allowedHosts: readonly string[];
17
+ };
18
+ export type ScrubbableCredential = {
19
+ name: string;
20
+ value: string;
21
+ };
22
+ export type Substitution = {
23
+ out: string;
24
+ used: string[];
25
+ denied: {
26
+ name: string;
27
+ allowedHosts: readonly string[];
28
+ }[];
29
+ unknown: string[];
30
+ };
31
+ /** Below this length a value is too likely to occur in unrelated text to be scrubbed. */
32
+ export declare const MIN_SCRUB_LENGTH = 6;
33
+ export declare function renderPlaceholder(name: string): string;
34
+ export declare function parsePlaceholders(text: string): CredentialPlaceholder[];
35
+ /** The credential a value stands for when the whole value is one placeholder, as a tag's secret field holds it. */
36
+ export declare function credentialNamedBy(value: string): string | null;
37
+ export declare function hostAllowed(host: string, patterns: readonly string[]): boolean;
38
+ export declare function substitute(text: string, credentials: readonly ResolvedCredential[], host: string): Substitution;
39
+ /** Every form of a credential a scrub must catch, longest first, so a longer value is never half-replaced by a shorter one. */
40
+ export declare function scrubPatterns(credentials: readonly ScrubbableCredential[]): {
41
+ needle: string;
42
+ name: string;
43
+ }[];
44
+ export declare function scrub(text: string, credentials: readonly ScrubbableCredential[]): string;
45
+ //# sourceMappingURL=credential-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-text.d.ts","sourceRoot":"","sources":["../../src/broker/credential-text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,MAAM,MAAM,qBAAqB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AACjF,MAAM,MAAM,kBAAkB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAClG,MAAM,MAAM,oBAAoB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACnE,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IAC5D,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAMvE;AAID,mHAAmH;AACnH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE9D;AAMD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAM9E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,kBAAkB,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CA4B/G;AAuBD,+HAA+H;AAC/H,wBAAgB,aAAa,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAK9G;AAED,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,oBAAoB,EAAE,GAAG,MAAM,CAMxF"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Hand-copy of the placeholder grammar, host check and scrub in
3
+ * `packages/api/src/credentials/{placeholder,hosts,scrub}.ts`. The bridge is
4
+ * published standalone to npm and cannot depend on the private `@baker/api`,
5
+ * so these are copied rather than imported; `credential-text.test.ts` runs both
6
+ * copies over the same inputs and fails when this one drifts.
7
+ */
8
+ const CREDENTIAL_NAME_PATTERN = "[a-z0-9][a-z0-9_-]{0,63}";
9
+ const PLACEHOLDER_SOURCE = `\\{\\{credential:(${CREDENTIAL_NAME_PATTERN})\\}\\}`;
10
+ /** Below this length a value is too likely to occur in unrelated text to be scrubbed. */
11
+ export const MIN_SCRUB_LENGTH = 6;
12
+ export function renderPlaceholder(name) {
13
+ return `{{credential:${name}}}`;
14
+ }
15
+ export function parsePlaceholders(text) {
16
+ return [...text.matchAll(new RegExp(PLACEHOLDER_SOURCE, "g"))].map((match) => ({
17
+ name: match[1] ?? "",
18
+ start: match.index,
19
+ end: match.index + match[0].length,
20
+ }));
21
+ }
22
+ const WHOLE_PLACEHOLDER_RE = new RegExp(`^${PLACEHOLDER_SOURCE}$`);
23
+ /** The credential a value stands for when the whole value is one placeholder, as a tag's secret field holds it. */
24
+ export function credentialNamedBy(value) {
25
+ return WHOLE_PLACEHOLDER_RE.exec(value.trim())?.[1] ?? null;
26
+ }
27
+ function normaliseHost(host) {
28
+ return host.trim().toLowerCase().replace(/\.$/, "");
29
+ }
30
+ export function hostAllowed(host, patterns) {
31
+ const target = normaliseHost(host);
32
+ return patterns.some((raw) => {
33
+ const pattern = normaliseHost(raw);
34
+ return pattern.startsWith("*.") ? target.endsWith(pattern.slice(1)) : target === pattern;
35
+ });
36
+ }
37
+ export function substitute(text, credentials, host) {
38
+ const byName = new Map(credentials.map((credential) => [credential.name, credential]));
39
+ const used = new Set();
40
+ const denied = new Map();
41
+ const unknown = new Set();
42
+ let out = "";
43
+ let cursor = 0;
44
+ for (const placeholder of parsePlaceholders(text)) {
45
+ const credential = byName.get(placeholder.name);
46
+ let replacement = text.slice(placeholder.start, placeholder.end);
47
+ if (credential === undefined) {
48
+ unknown.add(placeholder.name);
49
+ }
50
+ else if (hostAllowed(host, credential.allowedHosts)) {
51
+ replacement = credential.value;
52
+ used.add(credential.name);
53
+ }
54
+ else {
55
+ denied.set(credential.name, credential.allowedHosts);
56
+ }
57
+ out += text.slice(cursor, placeholder.start) + replacement;
58
+ cursor = placeholder.end;
59
+ }
60
+ out += text.slice(cursor);
61
+ return {
62
+ out,
63
+ used: [...used],
64
+ denied: [...denied].map(([name, allowedHosts]) => ({ name, allowedHosts })),
65
+ unknown: [...unknown],
66
+ };
67
+ }
68
+ function base64(value) {
69
+ let binary = "";
70
+ for (const byte of new TextEncoder().encode(value))
71
+ binary += String.fromCharCode(byte);
72
+ return btoa(binary);
73
+ }
74
+ function encodings(value) {
75
+ const standard = base64(value);
76
+ const variants = [
77
+ value,
78
+ standard,
79
+ standard.replace(/=+$/, ""),
80
+ standard.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""),
81
+ encodeURIComponent(value),
82
+ // Inside a JSON string, and as PHP's json_encode writes it (`/` as `\/`), as WordPress echoes it.
83
+ JSON.stringify(value).slice(1, -1),
84
+ JSON.stringify(value).slice(1, -1).replaceAll("/", "\\/"),
85
+ ];
86
+ return [...new Set(variants)].filter((variant) => variant.length >= MIN_SCRUB_LENGTH);
87
+ }
88
+ /** Every form of a credential a scrub must catch, longest first, so a longer value is never half-replaced by a shorter one. */
89
+ export function scrubPatterns(credentials) {
90
+ return credentials
91
+ .filter((credential) => credential.value.length >= MIN_SCRUB_LENGTH)
92
+ .flatMap((credential) => encodings(credential.value).map((needle) => ({ needle, name: credential.name })))
93
+ .sort((a, b) => b.needle.length - a.needle.length);
94
+ }
95
+ export function scrub(text, credentials) {
96
+ let out = text;
97
+ for (const { needle, name } of scrubPatterns(credentials)) {
98
+ if (out.includes(needle))
99
+ out = out.split(needle).join(renderPlaceholder(name));
100
+ }
101
+ return out;
102
+ }
103
+ //# sourceMappingURL=credential-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-text.js","sourceRoot":"","sources":["../../src/broker/credential-text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,uBAAuB,GAAG,0BAA0B,CAAC;AAC3D,MAAM,kBAAkB,GAAG,qBAAqB,uBAAuB,SAAS,CAAC;AAYjF,yFAAyF;AACzF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,gBAAgB,IAAI,IAAI,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7E,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QACpB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,GAAG,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;KACnC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,oBAAoB,GAAG,IAAI,MAAM,CAAC,IAAI,kBAAkB,GAAG,CAAC,CAAC;AAEnE,mHAAmH;AACnH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,OAAO,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC9D,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAA2B;IACnE,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC;IAC3F,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,WAA0C,EAAE,IAAY;IAC/F,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACvF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6B,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,WAAW,IAAI,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;QACjE,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;aAAM,IAAI,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACtD,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;QACvD,CAAC;QACD,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC;QAC3D,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC;IAC3B,CAAC;IACD,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,OAAO;QACL,GAAG;QACH,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QACf,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;QAC3E,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC;KACtB,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,KAAa;IAC3B,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACxF,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG;QACf,KAAK;QACL,QAAQ;QACR,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QAC3B,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACnE,kBAAkB,CAAC,KAAK,CAAC;QACzB,kGAAkG;QAClG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC;KAC1D,CAAC;IACF,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,IAAI,gBAAgB,CAAC,CAAC;AACxF,CAAC;AAED,+HAA+H;AAC/H,MAAM,UAAU,aAAa,CAAC,WAA4C;IACxE,OAAO,WAAW;SACf,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,IAAI,gBAAgB,CAAC;SACnE,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;SACzG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,IAAY,EAAE,WAA4C;IAC9E,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1D,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=credential-text.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-text.test.d.ts","sourceRoot":"","sources":["../../src/broker/credential-text.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,54 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ /**
10
+ * `credential-text.ts` is a hand-copy of the placeholder grammar, host check
11
+ * and scrub in `packages/api/src/credentials/` — the bridge is published
12
+ * standalone and cannot depend on the private `@baker/api`. These run both
13
+ * copies over the same inputs so the copy fails loudly when it falls behind.
14
+ */
15
+ import { resolve } from "node:path";
16
+ import { describe, expect, it } from "vitest";
17
+ import * as local from "./credential-text.js";
18
+ const UPSTREAM = resolve(import.meta.dirname, "../../../api/src/credentials/index.ts");
19
+ async function upstream() {
20
+ const path = UPSTREAM;
21
+ return (await import(__rewriteRelativeImportExtension(path)));
22
+ }
23
+ const credentials = [
24
+ { name: "meta-token", value: "EAAGm0PX4ZCpsBAKZCZ+/x", allowedHosts: ["graph.facebook.com"] },
25
+ { name: "shop", value: "shpat_0a1b2c3d4e5f", allowedHosts: ["acme.myshopify.com"] },
26
+ { name: "short", value: "abc", allowedHosts: ["api.example.com"] },
27
+ { name: "unicode", value: "contraseña-segura-1", allowedHosts: ["api.example.com"] },
28
+ { name: "quoted", value: 'pa"ss/wo\\rd-9x', allowedHosts: ["api.example.com"] },
29
+ ];
30
+ const texts = [
31
+ " {{credential:meta-token}} ",
32
+ "Authorization: Bearer {{credential:meta-token}} and {{credential:shop}} and {{credential:missing}}",
33
+ "{{credential:}} {{credential:Upper}} {credential:x} {{credential:short}}",
34
+ "echo EAAGm0PX4ZCpsBAKZCZ+/x RUFBR20wUFg0WkNwc0JBS1pDWisveA== shpat_0a1b2c3d4e5f abc",
35
+ `encoded ${encodeURIComponent("contraseña-segura-1")} ${btoa("shpat_0a1b2c3d4e5f")}`,
36
+ String.raw `{"password":"pa"ss/wo\rd-9x","php":"pa"ss\/wo\rd-9x"}`,
37
+ ];
38
+ const hosts = ["graph.facebook.com", "GRAPH.facebook.com.", "acme.myshopify.com", "myshopify.com", "evil.io"];
39
+ describe("credential-text mirrors @baker/api/credentials", () => {
40
+ it("parses, substitutes and scrubs exactly like the contract", async () => {
41
+ const api = await upstream();
42
+ for (const text of texts) {
43
+ expect(local.parsePlaceholders(text)).toEqual(api.parsePlaceholders(text));
44
+ expect(local.credentialNamedBy(text)).toBe(api.credentialNamedBy(text));
45
+ expect(local.scrub(text, credentials)).toBe(api.scrub(text, credentials));
46
+ for (const host of hosts) {
47
+ expect(local.substitute(text, credentials, host)).toEqual(api.substitute(text, credentials, host));
48
+ }
49
+ }
50
+ expect(local.scrubPatterns(credentials)).toEqual(api.scrubPatterns(credentials));
51
+ expect(local.MIN_SCRUB_LENGTH).toBe(api.MIN_SCRUB_LENGTH);
52
+ });
53
+ });
54
+ //# sourceMappingURL=credential-text.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-text.test.js","sourceRoot":"","sources":["../../src/broker/credential-text.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /** The broker's environment, parsed once at startup. */
2
+ import { z } from "zod";
3
+ declare const envSchema: z.ZodObject<{
4
+ CREDENTIAL_BROKER_TOKEN: z.ZodString;
5
+ BAKER_CONVEX_SITE_URL: z.ZodURL;
6
+ BAKER_CREDENTIAL_PROXY_PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
7
+ BAKER_CA_DIR: z.ZodDefault<z.ZodString>;
8
+ BAKER_BROKER_SOCKET: z.ZodDefault<z.ZodString>;
9
+ }, z.core.$strip>;
10
+ export type BrokerEnv = z.infer<typeof envSchema>;
11
+ export declare function parseBrokerEnv(env?: NodeJS.ProcessEnv): z.ZodSafeParseResult<{
12
+ CREDENTIAL_BROKER_TOKEN: string;
13
+ BAKER_CONVEX_SITE_URL: string;
14
+ BAKER_CREDENTIAL_PROXY_PORT: number;
15
+ BAKER_CA_DIR: string;
16
+ BAKER_BROKER_SOCKET: string;
17
+ }>;
18
+ export {};
19
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/broker/env.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,QAAA,MAAM,SAAS;;;;;;iBAMb,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAElD,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB;;;;;;GAElE"}
@@ -0,0 +1,16 @@
1
+ /** The broker's environment, parsed once at startup. */
2
+ import { z } from "zod";
3
+ import { DEFAULT_BROKER_SOCKET } from "./broker-client.js";
4
+ import { DEFAULT_CA_DIR } from "./ca.js";
5
+ import { DEFAULT_PROXY_PORT } from "./proxy.js";
6
+ const envSchema = z.object({
7
+ CREDENTIAL_BROKER_TOKEN: z.string().min(1),
8
+ BAKER_CONVEX_SITE_URL: z.url(),
9
+ BAKER_CREDENTIAL_PROXY_PORT: z.coerce.number().int().min(1).max(65_535).default(DEFAULT_PROXY_PORT),
10
+ BAKER_CA_DIR: z.string().min(1).default(DEFAULT_CA_DIR),
11
+ BAKER_BROKER_SOCKET: z.string().min(1).default(DEFAULT_BROKER_SOCKET),
12
+ });
13
+ export function parseBrokerEnv(env = process.env) {
14
+ return envSchema.safeParse(env);
15
+ }
16
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/broker/env.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACzB,uBAAuB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,qBAAqB,EAAE,CAAC,CAAC,GAAG,EAAE;IAC9B,2BAA2B,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC;IACnG,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC;IACvD,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC;CACtE,CAAC,CAAC;AAIH,MAAM,UAAU,cAAc,CAAC,GAAG,GAAsB,OAAO,CAAC,GAAG;IACjE,OAAO,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/broker/main.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `baker-credential-broker` — runs as root inside the Runtime, before the
4
+ * agent starts. Holds the company's credentials so the agent never does:
5
+ * the agent writes `{{credential:NAME}}`, the broker swaps the value in for
6
+ * allowed hosts and swaps it back out of what the agent reads.
7
+ */
8
+ import { createBakerBrokerApi } from "./baker-api.js";
9
+ import { ensureCertificateAuthority } from "./ca.js";
10
+ import { parseBrokerEnv } from "./env.js";
11
+ import { createBroker } from "./proxy.js";
12
+ import { createSocketService } from "./socket-service.js";
13
+ function log(message) {
14
+ console.error(`[credential-broker] ${message}`);
15
+ }
16
+ async function main() {
17
+ const parsed = parseBrokerEnv();
18
+ if (!parsed.success) {
19
+ log(`invalid environment: ${parsed.error.issues.map((issue) => issue.path.join(".")).join(", ")}`);
20
+ process.exit(1);
21
+ }
22
+ const env = parsed.data;
23
+ const api = createBakerBrokerApi({ siteUrl: env.BAKER_CONVEX_SITE_URL, token: env.CREDENTIAL_BROKER_TOKEN });
24
+ const ca = await ensureCertificateAuthority({
25
+ dir: env.BAKER_CA_DIR,
26
+ installSystemTrust: process.getuid?.() === 0,
27
+ log,
28
+ });
29
+ const broker = createBroker({
30
+ ca,
31
+ port: env.BAKER_CREDENTIAL_PROXY_PORT,
32
+ fetchCredentials: api.fetchCredentials,
33
+ reportUsage: api.reportUsage,
34
+ log,
35
+ });
36
+ const socket = createSocketService({ cache: broker.cache, socketPath: env.BAKER_BROKER_SOCKET, log });
37
+ const port = await broker.start();
38
+ await socket.listen();
39
+ log(`listening on 127.0.0.1:${port}, socket ${env.BAKER_BROKER_SOCKET}`);
40
+ const shutdown = async () => {
41
+ await Promise.allSettled([socket.close(), broker.stop()]);
42
+ process.exit(0);
43
+ };
44
+ process.once("SIGTERM", () => void shutdown());
45
+ process.once("SIGINT", () => void shutdown());
46
+ }
47
+ main().catch((error) => {
48
+ log(`failed to start: ${error instanceof Error ? error.message : String(error)}`);
49
+ process.exit(1);
50
+ });
51
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/broker/main.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,SAAS,GAAG,CAAC,OAAe;IAC1B,OAAO,CAAC,KAAK,CAAC,uBAAuB,OAAO,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,GAAG,CAAC,wBAAwB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC;IACxB,MAAM,GAAG,GAAG,oBAAoB,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,qBAAqB,EAAE,KAAK,EAAE,GAAG,CAAC,uBAAuB,EAAE,CAAC,CAAC;IAC7G,MAAM,EAAE,GAAG,MAAM,0BAA0B,CAAC;QAC1C,GAAG,EAAE,GAAG,CAAC,YAAY;QACrB,kBAAkB,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC;QAC5C,GAAG;KACJ,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,YAAY,CAAC;QAC1B,EAAE;QACF,IAAI,EAAE,GAAG,CAAC,2BAA2B;QACrC,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;QACtC,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,GAAG;KACJ,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,mBAAmB,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;IACtG,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAClC,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IACtB,GAAG,CAAC,0BAA0B,IAAI,YAAY,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAEzE,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,GAAG,CAAC,oBAAoB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { CredentialUse } from "./baker-api.ts";
2
+ import { type CredentialCache, type FetchCredentials } from "./credential-cache.ts";
3
+ export declare const DEFAULT_PROXY_PORT = 8877;
4
+ export type BrokerOptions = {
5
+ ca: {
6
+ key: string;
7
+ cert: string;
8
+ };
9
+ fetchCredentials: FetchCredentials;
10
+ reportUsage: (uses: CredentialUse[]) => Promise<void>;
11
+ /** 0 picks a free port. */
12
+ port?: number;
13
+ /** Extra roots to trust upstream (tests); Node's built-in roots are always trusted. */
14
+ upstreamCAs?: readonly string[];
15
+ log?: (message: string) => void;
16
+ };
17
+ export type Broker = {
18
+ cache: CredentialCache;
19
+ /** Starts the proxy and returns the port it listens on. */
20
+ start(): Promise<number>;
21
+ stop(): Promise<void>;
22
+ };
23
+ export declare function createBroker(options: BrokerOptions): Broker;
24
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/broker/proxy.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,KAAK,eAAe,EAAyB,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAe3G,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,EAAE,eAAe,CAAC;IACvB,2DAA2D;IAC3D,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB,CAAC;AA6CF,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAkF3D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=proxy.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.integration.test.d.ts","sourceRoot":"","sources":["../../src/broker/proxy.integration.test.ts"],"names":[],"mappings":""}