@koda-sl/baker-bridge 0.117.0-dev.933ebc23d → 0.118.0-dev.fc1af4da8

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 (143) hide show
  1. package/README.md +17 -2
  2. package/dist/broker/baker-api.d.ts +14 -0
  3. package/dist/broker/baker-api.d.ts.map +1 -0
  4. package/dist/broker/baker-api.js +41 -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 +30 -0
  11. package/dist/broker/ca.d.ts.map +1 -0
  12. package/dist/broker/ca.js +69 -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 +42 -0
  17. package/dist/broker/ca.test.js.map +1 -0
  18. package/dist/broker/credential-cache.d.ts +29 -0
  19. package/dist/broker/credential-cache.d.ts.map +1 -0
  20. package/dist/broker/credential-cache.js +36 -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 +74 -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 +100 -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 +52 -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 +128 -0
  47. package/dist/broker/proxy.integration.test.js.map +1 -0
  48. package/dist/broker/proxy.js +137 -0
  49. package/dist/broker/proxy.js.map +1 -0
  50. package/dist/broker/rewrite.d.ts +38 -0
  51. package/dist/broker/rewrite.d.ts.map +1 -0
  52. package/dist/broker/rewrite.js +275 -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 +84 -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 +62 -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 -3
  85. package/dist/hono/agent.js.map +1 -1
  86. package/dist/hono/baker-ui-tool-schemas.test.js +5 -0
  87. package/dist/hono/baker-ui-tool-schemas.test.js.map +1 -1
  88. package/dist/hono/bridge-job.d.ts +14 -29
  89. package/dist/hono/bridge-job.d.ts.map +1 -1
  90. package/dist/hono/bridge-job.js +71 -71
  91. package/dist/hono/bridge-job.js.map +1 -1
  92. package/dist/hono/bridge-job.test.js +79 -67
  93. package/dist/hono/bridge-job.test.js.map +1 -1
  94. package/dist/hono/connection-input.d.ts +11 -140
  95. package/dist/hono/connection-input.d.ts.map +1 -1
  96. package/dist/hono/connection-input.js +12 -109
  97. package/dist/hono/connection-input.js.map +1 -1
  98. package/dist/hono/connection-input.test.js +1 -61
  99. package/dist/hono/connection-input.test.js.map +1 -1
  100. package/dist/hono/convex.d.ts +8 -0
  101. package/dist/hono/convex.d.ts.map +1 -1
  102. package/dist/hono/convex.js +8 -0
  103. package/dist/hono/convex.js.map +1 -1
  104. package/dist/hono/credential-input.d.ts +39 -0
  105. package/dist/hono/credential-input.d.ts.map +1 -0
  106. package/dist/hono/credential-input.js +77 -0
  107. package/dist/hono/credential-input.js.map +1 -0
  108. package/dist/hono/credential-scrub.d.ts +41 -0
  109. package/dist/hono/credential-scrub.d.ts.map +1 -0
  110. package/dist/hono/credential-scrub.js +152 -0
  111. package/dist/hono/credential-scrub.js.map +1 -0
  112. package/dist/hono/credential-scrub.test.d.ts +2 -0
  113. package/dist/hono/credential-scrub.test.d.ts.map +1 -0
  114. package/dist/hono/credential-scrub.test.js +105 -0
  115. package/dist/hono/credential-scrub.test.js.map +1 -0
  116. package/dist/hono/mcp-servers.d.ts +1 -8
  117. package/dist/hono/mcp-servers.d.ts.map +1 -1
  118. package/dist/hono/mcp-servers.js +6 -17
  119. package/dist/hono/mcp-servers.js.map +1 -1
  120. package/dist/hono/mcp-servers.test.js +0 -25
  121. package/dist/hono/mcp-servers.test.js.map +1 -1
  122. package/dist/hono/prompt-context.d.ts +8 -1
  123. package/dist/hono/prompt-context.d.ts.map +1 -1
  124. package/dist/hono/prompt-context.js +45 -7
  125. package/dist/hono/prompt-context.js.map +1 -1
  126. package/dist/hono/prompt-context.test.js +32 -1
  127. package/dist/hono/prompt-context.test.js.map +1 -1
  128. package/dist/hono/relay.d.ts +10 -1
  129. package/dist/hono/relay.d.ts.map +1 -1
  130. package/dist/hono/relay.js +6 -0
  131. package/dist/hono/relay.js.map +1 -1
  132. package/dist/hono/server.d.ts.map +1 -1
  133. package/dist/hono/server.js +22 -0
  134. package/dist/hono/server.js.map +1 -1
  135. package/dist/hono/tag-input.d.ts +83 -3
  136. package/dist/hono/tag-input.d.ts.map +1 -1
  137. package/dist/hono/tag-input.js +23 -17
  138. package/dist/hono/tag-input.js.map +1 -1
  139. package/dist/hono/tag-input.test.js +15 -1
  140. package/dist/hono/tag-input.test.js.map +1 -1
  141. package/dist/hono/types.d.ts +8 -0
  142. package/dist/hono/types.d.ts.map +1 -1
  143. package/package.json +5 -2
@@ -0,0 +1,100 @@
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
+ ];
83
+ return [...new Set(variants)].filter((variant) => variant.length >= MIN_SCRUB_LENGTH);
84
+ }
85
+ /** Every form of a credential a scrub must catch, longest first, so a longer value is never half-replaced by a shorter one. */
86
+ export function scrubPatterns(credentials) {
87
+ return credentials
88
+ .filter((credential) => credential.value.length >= MIN_SCRUB_LENGTH)
89
+ .flatMap((credential) => encodings(credential.value).map((needle) => ({ needle, name: credential.name })))
90
+ .sort((a, b) => b.needle.length - a.needle.length);
91
+ }
92
+ export function scrub(text, credentials) {
93
+ let out = text;
94
+ for (const { needle, name } of scrubPatterns(credentials)) {
95
+ if (out.includes(needle))
96
+ out = out.split(needle).join(renderPlaceholder(name));
97
+ }
98
+ return out;
99
+ }
100
+ //# sourceMappingURL=credential-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-text.js","sourceRoot":"","sources":["../../src/broker/credential-text.ts"],"names":[],"mappings":"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"}
@@ -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,52 @@
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
+ ];
29
+ const texts = [
30
+ " {{credential:meta-token}} ",
31
+ "Authorization: Bearer {{credential:meta-token}} and {{credential:shop}} and {{credential:missing}}",
32
+ "{{credential:}} {{credential:Upper}} {credential:x} {{credential:short}}",
33
+ "echo EAAGm0PX4ZCpsBAKZCZ+/x RUFBR20wUFg0WkNwc0JBS1pDWisveA== shpat_0a1b2c3d4e5f abc",
34
+ `encoded ${encodeURIComponent("contraseña-segura-1")} ${btoa("shpat_0a1b2c3d4e5f")}`,
35
+ ];
36
+ const hosts = ["graph.facebook.com", "GRAPH.facebook.com.", "acme.myshopify.com", "myshopify.com", "evil.io"];
37
+ describe("credential-text mirrors @baker/api/credentials", () => {
38
+ it("parses, substitutes and scrubs exactly like the contract", async () => {
39
+ const api = await upstream();
40
+ for (const text of texts) {
41
+ expect(local.parsePlaceholders(text)).toEqual(api.parsePlaceholders(text));
42
+ expect(local.credentialNamedBy(text)).toBe(api.credentialNamedBy(text));
43
+ expect(local.scrub(text, credentials)).toBe(api.scrub(text, credentials));
44
+ for (const host of hosts) {
45
+ expect(local.substitute(text, credentials, host)).toEqual(api.substitute(text, credentials, host));
46
+ }
47
+ }
48
+ expect(local.scrubPatterns(credentials)).toEqual(api.scrubPatterns(credentials));
49
+ expect(local.MIN_SCRUB_LENGTH).toBe(api.MIN_SCRUB_LENGTH);
50
+ });
51
+ });
52
+ //# 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":";;;;;;;;AAAA;;;;;GAKG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,KAAK,MAAM,sBAAsB,CAAC;AAG9C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,uCAAuC,CAAC,CAAC;AAEvF,KAAK,UAAU,QAAQ;IACrB,MAAM,IAAI,GAAW,QAAQ,CAAC;IAC9B,OAAO,CAAC,MAAM,MAAM,kCAAC,IAAI,EAAC,CAAa,CAAC;AAC1C,CAAC;AAED,MAAM,WAAW,GAAG;IAClB,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,wBAAwB,EAAE,YAAY,EAAE,CAAC,oBAAoB,CAAC,EAAE;IAC7F,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,CAAC,oBAAoB,CAAC,EAAE;IACnF,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,iBAAiB,CAAC,EAAE;IAClE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,CAAC,iBAAiB,CAAC,EAAE;CACrF,CAAC;AAEF,MAAM,KAAK,GAAG;IACZ,6BAA6B;IAC7B,oGAAoG;IACpG,0EAA0E;IAC1E,qFAAqF;IACrF,WAAW,kBAAkB,CAAC,qBAAqB,CAAC,IAAI,IAAI,CAAC,oBAAoB,CAAC,EAAE;CACrF,CAAC;AACF,MAAM,KAAK,GAAG,CAAC,oBAAoB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;AAE9G,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC9D,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,GAAG,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;YAC3E,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;YACxE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;YAC1E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC;YACrG,CAAC;QACH,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;QACjF,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -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;AAuCF,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAgF3D"}
@@ -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":""}
@@ -0,0 +1,128 @@
1
+ import { request as httpRequest } from "node:http";
2
+ import { connect as http2Connect } from "node:http2";
3
+ import { connect as tlsConnect } from "node:tls";
4
+ import { generateCACertificate, getLocal } from "mockttp";
5
+ import { ProxyAgent, request } from "undici";
6
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
+ import { createBroker } from "./proxy.js";
8
+ const VALUE = "shpat_0a1b2c3d4e5f6a7b";
9
+ let upstream;
10
+ let broker;
11
+ let agent;
12
+ let metaHosts;
13
+ let brokerPort;
14
+ let brokerCaCert;
15
+ beforeEach(async () => {
16
+ const upstreamCa = await generateCACertificate();
17
+ upstream = getLocal({ https: upstreamCa });
18
+ await upstream.start();
19
+ await upstream.forAnyRequest().thenCallback((req) => ({
20
+ statusCode: 200,
21
+ json: { sawAuthorization: req.headers.authorization ?? null, bodyBytes: req.body.buffer.byteLength },
22
+ }));
23
+ metaHosts = ["graph.facebook.com"];
24
+ const brokerCa = await generateCACertificate();
25
+ broker = createBroker({
26
+ ca: brokerCa,
27
+ port: 0,
28
+ upstreamCAs: [upstreamCa.cert],
29
+ fetchCredentials: () => Promise.resolve([
30
+ { name: "shop", value: VALUE, allowedHosts: ["localhost"] },
31
+ { name: "meta-token", value: "EAAG-meta-value-1", allowedHosts: [...metaHosts] },
32
+ ]),
33
+ reportUsage: () => Promise.resolve(),
34
+ });
35
+ const port = await broker.start();
36
+ brokerPort = port;
37
+ brokerCaCert = brokerCa.cert;
38
+ agent = new ProxyAgent({ uri: `http://127.0.0.1:${port}`, requestTls: { ca: brokerCa.cert } });
39
+ });
40
+ afterEach(async () => {
41
+ await agent.close();
42
+ await broker.stop();
43
+ await upstream.stop();
44
+ });
45
+ /** A GET over HTTP/2 through the broker, as curl makes one: CONNECT, then TLS with h2 negotiated. */
46
+ function http2GetThroughBroker(path) {
47
+ const authority = `localhost:${upstream.port}`;
48
+ return new Promise((resolve, reject) => {
49
+ const connect = httpRequest({ host: "127.0.0.1", port: brokerPort, method: "CONNECT", path: authority });
50
+ connect.on("error", reject);
51
+ connect.on("connect", (_res, socket) => {
52
+ const session = http2Connect(`https://${authority}`, {
53
+ createConnection: () => tlsConnect({ socket, servername: "localhost", ALPNProtocols: ["h2"], ca: brokerCaCert }),
54
+ });
55
+ session.on("error", reject);
56
+ const stream = session.request({ ":method": "GET", ":path": path });
57
+ let status = 0;
58
+ let body = "";
59
+ stream.on("response", (headers) => {
60
+ status = Number(headers[":status"]);
61
+ });
62
+ stream.setEncoding("utf8");
63
+ stream.on("data", (chunk) => {
64
+ body += chunk;
65
+ });
66
+ stream.on("end", () => {
67
+ session.close();
68
+ resolve({ status, body });
69
+ });
70
+ stream.on("error", reject);
71
+ stream.end();
72
+ });
73
+ connect.end();
74
+ });
75
+ }
76
+ describe("credential broker proxy", () => {
77
+ it("sends a placeholder written in the URL over HTTP/2", async () => {
78
+ const response = await http2GetThroughBroker("/echo?key={{credential:shop}}");
79
+ expect(response.status).toBe(200);
80
+ const [seen] = await upstream.getMockedEndpoints().then((endpoints) => endpoints[0]?.getSeenRequests() ?? []);
81
+ expect(seen?.url).toContain(`key=${VALUE}`);
82
+ });
83
+ it("sends the value to an allowed host and hands the agent back only the placeholder", async () => {
84
+ const response = await request(`https://localhost:${upstream.port}/echo`, {
85
+ dispatcher: agent,
86
+ headers: { authorization: "Bearer {{credential:shop}}" },
87
+ });
88
+ const body = await response.body.text();
89
+ const [seen] = await upstream.getMockedEndpoints().then((endpoints) => endpoints[0]?.getSeenRequests() ?? []);
90
+ expect(seen?.headers.authorization).toBe(`Bearer ${VALUE}`);
91
+ expect(JSON.parse(body)).toEqual({ sawAuthorization: "Bearer {{credential:shop}}", bodyBytes: 0 });
92
+ });
93
+ it("refuses a credential bound for a host it is not allowed for, before it leaves the sandbox", async () => {
94
+ const response = await request(`https://localhost:${upstream.port}/echo`, {
95
+ dispatcher: agent,
96
+ headers: { authorization: "Bearer {{credential:meta-token}}" },
97
+ });
98
+ const body = await response.body.text();
99
+ expect(response.statusCode).toBe(403);
100
+ expect(body).toContain('Baker credential "meta-token" is not allowed for host "localhost"');
101
+ const seen = await upstream.getMockedEndpoints().then((endpoints) => endpoints[0]?.getSeenRequests() ?? []);
102
+ expect(seen).toEqual([]);
103
+ });
104
+ it("sends a credential to a host the user allowed since the broker last fetched the table", async () => {
105
+ await request(`https://localhost:${upstream.port}/warm`, {
106
+ dispatcher: agent,
107
+ headers: { authorization: "Bearer {{credential:shop}}" },
108
+ }).then((response) => response.body.text());
109
+ metaHosts = ["graph.facebook.com", "localhost"];
110
+ const response = await request(`https://localhost:${upstream.port}/echo`, {
111
+ dispatcher: agent,
112
+ headers: { authorization: "Bearer {{credential:meta-token}}" },
113
+ });
114
+ expect(response.statusCode).toBe(200);
115
+ expect(await response.body.json()).toMatchObject({ sawAuthorization: "Bearer {{credential:meta-token}}" });
116
+ });
117
+ it("streams an upload larger than the broker reads through intact", async () => {
118
+ const upload = Buffer.alloc(12 * 1024 * 1024, "a");
119
+ const response = await request(`https://localhost:${upstream.port}/upload`, {
120
+ dispatcher: agent,
121
+ method: "POST",
122
+ headers: { "content-type": "text/plain" },
123
+ body: upload,
124
+ });
125
+ expect(await response.body.json()).toEqual({ sawAuthorization: null, bodyBytes: upload.byteLength });
126
+ });
127
+ });
128
+ //# sourceMappingURL=proxy.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.integration.test.js","sourceRoot":"","sources":["../../src/broker/proxy.integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * The credential broker: an explicit HTTP(S) proxy (mockttp, TLS intercepted
3
+ * with the broker's own CA) that swaps `{{credential:NAME}}` for the value on
4
+ * the way out and swaps the value back to the placeholder on the way in.
5
+ *
6
+ * Two rules, in order:
7
+ * - A request carrying a placeholder is rewritten by `rewriteRequest` and its
8
+ * response is scrubbed. mockttp's `beforeResponse` buffers the whole
9
+ * response, so these responses — event streams included — arrive in one
10
+ * piece. Headers are always scrubbed; a text body is scrubbed up to 10 MB
11
+ * and anything larger or binary passes through as-is.
12
+ * - Every other request passes straight through, streamed and untouched: no
13
+ * value was put into it, so there is nothing of ours to take out.
14
+ *
15
+ * Memory: the placeholder check reads each request body in full, and a
16
+ * credential-carrying request's response is buffered in full by mockttp
17
+ * before it is scrubbed (over 10 MB it then passes through unscrubbed).
18
+ * mockttp's `maxBodySize` cannot bound this: in 4.6.3 a body over the limit
19
+ * that a callback matcher has read is forwarded empty, so a large upload would
20
+ * hang or arrive truncated (see the upload test in `proxy.integration.test.ts`).
21
+ */
22
+ import { getLocal } from "mockttp";
23
+ import { createCredentialCache } from "./credential-cache.js";
24
+ import { scrub } from "./credential-text.js";
25
+ import { carriesPlaceholder, isTextBody, MAX_REWRITE_BODY_BYTES, rewriteRequest, } from "./rewrite.js";
26
+ import { createUsageReporter } from "./usage-reporter.js";
27
+ export const DEFAULT_PROXY_PORT = 8877;
28
+ const LOOPBACK_ADDRESSES = new Set(["127.0.0.1", "::1", "::ffff:127.0.0.1"]);
29
+ async function outgoing(req) {
30
+ return { url: req.url, headers: req.headers, body: (await req.body.getDecodedBuffer()) ?? null };
31
+ }
32
+ function first(value) {
33
+ return Array.isArray(value) ? value[0] : value;
34
+ }
35
+ function scrubHeaders(headers, credentials) {
36
+ let changed = false;
37
+ const out = {};
38
+ for (const [name, value] of Object.entries(headers)) {
39
+ const scrubbed = Array.isArray(value)
40
+ ? value.map((item) => scrub(item, credentials))
41
+ : value === undefined
42
+ ? undefined
43
+ : scrub(value, credentials);
44
+ if (JSON.stringify(scrubbed) !== JSON.stringify(value))
45
+ changed = true;
46
+ out[name] = scrubbed;
47
+ }
48
+ return changed ? out : undefined;
49
+ }
50
+ /** The scrubbed body, or undefined when it is binary, over 10 MB, or holds no value. */
51
+ function scrubBody(raw, contentType, credentials) {
52
+ if (raw === undefined || raw.byteLength > MAX_REWRITE_BODY_BYTES || !isTextBody(contentType, raw))
53
+ return undefined;
54
+ const text = raw.toString("utf8");
55
+ const scrubbed = scrub(text, credentials);
56
+ return scrubbed === text ? undefined : scrubbed;
57
+ }
58
+ export function createBroker(options) {
59
+ const log = options.log ?? (() => undefined);
60
+ const cache = createCredentialCache({ fetchCredentials: options.fetchCredentials, log });
61
+ const usage = createUsageReporter({ reportUsage: options.reportUsage, log });
62
+ const server = getLocal({
63
+ https: { key: options.ca.key, cert: options.ca.cert },
64
+ recordTraffic: false,
65
+ });
66
+ const upstreamTrust = { additionalTrustedCAs: (options.upstreamCAs ?? []).map((cert) => ({ cert })) };
67
+ let stopUsage = () => undefined;
68
+ async function decide(req) {
69
+ const request = await outgoing(req);
70
+ let decision = rewriteRequest(request, await cache.get());
71
+ if (decision.kind === "reject" && decision.stale && cache.refresh()) {
72
+ decision = rewriteRequest(request, await cache.get());
73
+ }
74
+ return decision;
75
+ }
76
+ async function beforeRequest(req) {
77
+ const decision = await decide(req);
78
+ const host = new URL(req.url).hostname;
79
+ if (decision.kind === "pass")
80
+ return undefined;
81
+ if (decision.kind === "reject") {
82
+ log(`refused ${decision.status} for ${host}`);
83
+ return {
84
+ response: {
85
+ statusCode: decision.status,
86
+ headers: { "content-type": "text/plain; charset=utf-8", "x-baker-credential-broker": "refused" },
87
+ body: decision.message,
88
+ },
89
+ };
90
+ }
91
+ for (const name of decision.used)
92
+ usage.record(name, host);
93
+ log(`sent ${decision.used.join(", ")} to ${host}`);
94
+ return {
95
+ url: decision.url,
96
+ headers: decision.headers,
97
+ ...(decision.body === undefined ? {} : { body: decision.body }),
98
+ };
99
+ }
100
+ async function beforeResponse(res) {
101
+ const credentials = (await cache.get()) ?? [];
102
+ const headers = scrubHeaders(res.headers, credentials);
103
+ const body = scrubBody(await res.body.getDecodedBuffer(), first(res.headers["content-type"]), credentials);
104
+ if (headers === undefined && body === undefined)
105
+ return undefined;
106
+ return { ...(headers === undefined ? {} : { headers }), ...(body === undefined ? {} : { body }) };
107
+ }
108
+ async function registerRules() {
109
+ await server
110
+ .forAnyRequest()
111
+ .matching((req) => !LOOPBACK_ADDRESSES.has(req.remoteIpAddress ?? ""))
112
+ .always()
113
+ .thenCloseConnection();
114
+ await server
115
+ .forAnyRequest()
116
+ .matching(async (req) => carriesPlaceholder(await outgoing(req)))
117
+ .always()
118
+ .thenPassThrough({ ...upstreamTrust, beforeRequest, beforeResponse });
119
+ await server.forAnyRequest().always().thenPassThrough(upstreamTrust);
120
+ await server.forAnyWebSocket().always().thenPassThrough(upstreamTrust);
121
+ }
122
+ return {
123
+ cache,
124
+ async start() {
125
+ await server.start(options.port ?? DEFAULT_PROXY_PORT);
126
+ await registerRules();
127
+ stopUsage = usage.start();
128
+ return server.port;
129
+ },
130
+ async stop() {
131
+ stopUsage();
132
+ await usage.flush();
133
+ await server.stop();
134
+ },
135
+ };
136
+ }
137
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/broker/proxy.ts"],"names":[],"mappings":"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"}