@openclaw/crabline 0.1.11 → 0.1.13

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 (214) hide show
  1. package/README.md +137 -23
  2. package/dist/src/bin/crabline.js +3 -1
  3. package/dist/src/bin/crabline.js.map +1 -1
  4. package/dist/src/cli/program.d.ts +5 -1
  5. package/dist/src/cli/program.js +281 -52
  6. package/dist/src/cli/program.js.map +1 -1
  7. package/dist/src/config/load.js +39 -3
  8. package/dist/src/config/load.js.map +1 -1
  9. package/dist/src/config/schema.d.ts +20 -0
  10. package/dist/src/config/schema.js +163 -35
  11. package/dist/src/config/schema.js.map +1 -1
  12. package/dist/src/core/errors.d.ts +1 -0
  13. package/dist/src/core/errors.js +6 -3
  14. package/dist/src/core/errors.js.map +1 -1
  15. package/dist/src/core/http-path.d.ts +1 -0
  16. package/dist/src/core/http-path.js +14 -0
  17. package/dist/src/core/http-path.js.map +1 -0
  18. package/dist/src/core/matcher.d.ts +3 -1
  19. package/dist/src/core/matcher.js +58 -2
  20. package/dist/src/core/matcher.js.map +1 -1
  21. package/dist/src/core/nonces.d.ts +2 -0
  22. package/dist/src/core/nonces.js +11 -1
  23. package/dist/src/core/nonces.js.map +1 -1
  24. package/dist/src/core/reporters.d.ts +5 -0
  25. package/dist/src/core/reporters.js +26 -2
  26. package/dist/src/core/reporters.js.map +1 -1
  27. package/dist/src/core/run.d.ts +3 -1
  28. package/dist/src/core/run.js +178 -66
  29. package/dist/src/core/run.js.map +1 -1
  30. package/dist/src/index.d.ts +12 -10
  31. package/dist/src/index.js +6 -5
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/matrix-ids.d.ts +5 -0
  34. package/dist/src/matrix-ids.js +83 -0
  35. package/dist/src/matrix-ids.js.map +1 -0
  36. package/dist/src/openclaw/artifact-generation.d.ts +7 -6
  37. package/dist/src/openclaw/artifact-generation.js +385 -41
  38. package/dist/src/openclaw/artifact-generation.js.map +1 -1
  39. package/dist/src/openclaw/bridges/discord.d.ts +1 -0
  40. package/dist/src/openclaw/bridges/discord.js +149 -0
  41. package/dist/src/openclaw/bridges/discord.js.map +1 -0
  42. package/dist/src/openclaw/bridges/matrix.js +22 -5
  43. package/dist/src/openclaw/bridges/matrix.js.map +1 -1
  44. package/dist/src/openclaw/bridges/mattermost.js +14 -9
  45. package/dist/src/openclaw/bridges/mattermost.js.map +1 -1
  46. package/dist/src/openclaw/bridges/probe-response.d.ts +1 -0
  47. package/dist/src/openclaw/bridges/probe-response.js +5 -0
  48. package/dist/src/openclaw/bridges/probe-response.js.map +1 -0
  49. package/dist/src/openclaw/bridges/signal.js +16 -7
  50. package/dist/src/openclaw/bridges/signal.js.map +1 -1
  51. package/dist/src/openclaw/bridges/slack.js +117 -24
  52. package/dist/src/openclaw/bridges/slack.js.map +1 -1
  53. package/dist/src/openclaw/bridges/telegram.js +46 -20
  54. package/dist/src/openclaw/bridges/telegram.js.map +1 -1
  55. package/dist/src/openclaw/bridges/whatsapp.js +33 -17
  56. package/dist/src/openclaw/bridges/whatsapp.js.map +1 -1
  57. package/dist/src/openclaw/bridges/zalo.js +9 -4
  58. package/dist/src/openclaw/bridges/zalo.js.map +1 -1
  59. package/dist/src/openclaw/outbound-contract.js +3 -0
  60. package/dist/src/openclaw/outbound-contract.js.map +1 -1
  61. package/dist/src/openclaw/private-file.d.ts +38 -3
  62. package/dist/src/openclaw/private-file.js +2008 -87
  63. package/dist/src/openclaw/private-file.js.map +1 -1
  64. package/dist/src/openclaw/{smoke-lock.d.ts → provider-readiness-lock.d.ts} +14 -5
  65. package/dist/src/openclaw/provider-readiness-lock.js +1320 -0
  66. package/dist/src/openclaw/provider-readiness-lock.js.map +1 -0
  67. package/dist/src/openclaw/shared.d.ts +10 -17
  68. package/dist/src/openclaw/shared.js +53 -19
  69. package/dist/src/openclaw/shared.js.map +1 -1
  70. package/dist/src/openclaw.d.ts +3 -5
  71. package/dist/src/openclaw.js +245 -60
  72. package/dist/src/openclaw.js.map +1 -1
  73. package/dist/src/platform/process-owned-lock.d.ts +11 -0
  74. package/dist/src/platform/process-owned-lock.js +1698 -0
  75. package/dist/src/platform/process-owned-lock.js.map +1 -0
  76. package/dist/src/platform/recorder-directory.d.ts +1 -0
  77. package/dist/src/platform/recorder-directory.js +14 -0
  78. package/dist/src/platform/recorder-directory.js.map +1 -0
  79. package/dist/src/platform/windows-acl.d.ts +18 -0
  80. package/dist/src/platform/windows-acl.js +1367 -0
  81. package/dist/src/platform/windows-acl.js.map +1 -0
  82. package/dist/src/platform/windows-lock-root.d.ts +13 -0
  83. package/dist/src/platform/windows-lock-root.js +98 -0
  84. package/dist/src/platform/windows-lock-root.js.map +1 -0
  85. package/dist/src/providers/builtin/discord.d.ts +4 -0
  86. package/dist/src/providers/builtin/discord.js +52 -9
  87. package/dist/src/providers/builtin/discord.js.map +1 -1
  88. package/dist/src/providers/builtin/external-webhook-auth.d.ts +1 -0
  89. package/dist/src/providers/builtin/external-webhook-auth.js +38 -1
  90. package/dist/src/providers/builtin/external-webhook-auth.js.map +1 -1
  91. package/dist/src/providers/builtin/feishu.d.ts +1 -0
  92. package/dist/src/providers/builtin/feishu.js +96 -26
  93. package/dist/src/providers/builtin/feishu.js.map +1 -1
  94. package/dist/src/providers/builtin/googlechat.d.ts +1 -0
  95. package/dist/src/providers/builtin/googlechat.js +95 -26
  96. package/dist/src/providers/builtin/googlechat.js.map +1 -1
  97. package/dist/src/providers/builtin/imessage.js +60 -17
  98. package/dist/src/providers/builtin/imessage.js.map +1 -1
  99. package/dist/src/providers/builtin/loopback.js +88 -35
  100. package/dist/src/providers/builtin/loopback.js.map +1 -1
  101. package/dist/src/providers/builtin/matrix.d.ts +2 -2
  102. package/dist/src/providers/builtin/matrix.js +78 -21
  103. package/dist/src/providers/builtin/matrix.js.map +1 -1
  104. package/dist/src/providers/builtin/mattermost.d.ts +11 -7
  105. package/dist/src/providers/builtin/mattermost.js +138 -24
  106. package/dist/src/providers/builtin/mattermost.js.map +1 -1
  107. package/dist/src/providers/builtin/msteams.d.ts +1 -0
  108. package/dist/src/providers/builtin/msteams.js +118 -38
  109. package/dist/src/providers/builtin/msteams.js.map +1 -1
  110. package/dist/src/providers/builtin/native-local-mock.js +4 -4
  111. package/dist/src/providers/builtin/native-local-mock.js.map +1 -1
  112. package/dist/src/providers/builtin/script.js +1057 -146
  113. package/dist/src/providers/builtin/script.js.map +1 -1
  114. package/dist/src/providers/builtin/slack.d.ts +8 -1
  115. package/dist/src/providers/builtin/slack.js +327 -24
  116. package/dist/src/providers/builtin/slack.js.map +1 -1
  117. package/dist/src/providers/builtin/telegram.d.ts +1 -1
  118. package/dist/src/providers/builtin/telegram.js +55 -14
  119. package/dist/src/providers/builtin/telegram.js.map +1 -1
  120. package/dist/src/providers/builtin/whatsapp.d.ts +4 -3
  121. package/dist/src/providers/builtin/whatsapp.js +157 -29
  122. package/dist/src/providers/builtin/whatsapp.js.map +1 -1
  123. package/dist/src/providers/builtin/zalo.d.ts +5 -2
  124. package/dist/src/providers/builtin/zalo.js +31 -7
  125. package/dist/src/providers/builtin/zalo.js.map +1 -1
  126. package/dist/src/providers/catalog.d.ts +1 -1
  127. package/dist/src/providers/catalog.js +1 -1
  128. package/dist/src/providers/catalog.js.map +1 -1
  129. package/dist/src/providers/local-mock.d.ts +4 -0
  130. package/dist/src/providers/local-mock.js +162 -35
  131. package/dist/src/providers/local-mock.js.map +1 -1
  132. package/dist/src/providers/native-ids.d.ts +2 -0
  133. package/dist/src/providers/native-ids.js +3 -0
  134. package/dist/src/providers/native-ids.js.map +1 -1
  135. package/dist/src/providers/recorder.d.ts +14 -3
  136. package/dist/src/providers/recorder.js +521 -41
  137. package/dist/src/providers/recorder.js.map +1 -1
  138. package/dist/src/providers/registry.d.ts +1 -0
  139. package/dist/src/providers/registry.js +65 -20
  140. package/dist/src/providers/registry.js.map +1 -1
  141. package/dist/src/providers/signed-jwt.d.ts +6 -1
  142. package/dist/src/providers/signed-jwt.js +241 -50
  143. package/dist/src/providers/signed-jwt.js.map +1 -1
  144. package/dist/src/providers/slack-ids.d.ts +1 -0
  145. package/dist/src/providers/slack-ids.js +5 -0
  146. package/dist/src/providers/slack-ids.js.map +1 -1
  147. package/dist/src/providers/target-normalizers.d.ts +9 -1
  148. package/dist/src/providers/target-normalizers.js +177 -58
  149. package/dist/src/providers/target-normalizers.js.map +1 -1
  150. package/dist/src/providers/webhook-server.d.ts +3 -0
  151. package/dist/src/providers/webhook-server.js +268 -47
  152. package/dist/src/providers/webhook-server.js.map +1 -1
  153. package/dist/src/servers/discord.d.ts +40 -0
  154. package/dist/src/servers/discord.js +842 -0
  155. package/dist/src/servers/discord.js.map +1 -0
  156. package/dist/src/servers/http.d.ts +14 -2
  157. package/dist/src/servers/http.js +229 -58
  158. package/dist/src/servers/http.js.map +1 -1
  159. package/dist/src/servers/index.d.ts +10 -4
  160. package/dist/src/servers/index.js +4 -0
  161. package/dist/src/servers/index.js.map +1 -1
  162. package/dist/src/servers/matrix.d.ts +3 -0
  163. package/dist/src/servers/matrix.js +268 -110
  164. package/dist/src/servers/matrix.js.map +1 -1
  165. package/dist/src/servers/mattermost.d.ts +5 -0
  166. package/dist/src/servers/mattermost.js +389 -91
  167. package/dist/src/servers/mattermost.js.map +1 -1
  168. package/dist/src/servers/recorder.d.ts +11 -0
  169. package/dist/src/servers/recorder.js +1072 -69
  170. package/dist/src/servers/recorder.js.map +1 -1
  171. package/dist/src/servers/signal.d.ts +1 -0
  172. package/dist/src/servers/signal.js +279 -44
  173. package/dist/src/servers/signal.js.map +1 -1
  174. package/dist/src/servers/slack.d.ts +15 -0
  175. package/dist/src/servers/slack.js +270 -54
  176. package/dist/src/servers/slack.js.map +1 -1
  177. package/dist/src/servers/telegram-identity.d.ts +6 -0
  178. package/dist/src/servers/telegram-identity.js +27 -0
  179. package/dist/src/servers/telegram-identity.js.map +1 -0
  180. package/dist/src/servers/telegram.d.ts +17 -6
  181. package/dist/src/servers/telegram.js +1218 -107
  182. package/dist/src/servers/telegram.js.map +1 -1
  183. package/dist/src/servers/webhook-target.d.ts +12 -4
  184. package/dist/src/servers/webhook-target.js +226 -28
  185. package/dist/src/servers/webhook-target.js.map +1 -1
  186. package/dist/src/servers/whatsapp-baileys-websocket.d.ts +60 -1
  187. package/dist/src/servers/whatsapp-baileys-websocket.js +782 -112
  188. package/dist/src/servers/whatsapp-baileys-websocket.js.map +1 -1
  189. package/dist/src/servers/whatsapp-jid.d.ts +1 -0
  190. package/dist/src/servers/whatsapp-jid.js +9 -5
  191. package/dist/src/servers/whatsapp-jid.js.map +1 -1
  192. package/dist/src/servers/whatsapp-wire/binary-node.d.ts +1 -0
  193. package/dist/src/servers/whatsapp-wire/binary-node.js +37 -13
  194. package/dist/src/servers/whatsapp-wire/binary-node.js.map +1 -1
  195. package/dist/src/servers/whatsapp-wire/crypto.d.ts +3 -0
  196. package/dist/src/servers/whatsapp-wire/crypto.js +23 -8
  197. package/dist/src/servers/whatsapp-wire/crypto.js.map +1 -1
  198. package/dist/src/servers/whatsapp-wire/handshake.d.ts +9 -3
  199. package/dist/src/servers/whatsapp-wire/handshake.js +173 -12
  200. package/dist/src/servers/whatsapp-wire/handshake.js.map +1 -1
  201. package/dist/src/servers/whatsapp-wire/signal.js +3 -3
  202. package/dist/src/servers/whatsapp-wire/signal.js.map +1 -1
  203. package/dist/src/servers/whatsapp.d.ts +7 -1
  204. package/dist/src/servers/whatsapp.js +149 -25
  205. package/dist/src/servers/whatsapp.js.map +1 -1
  206. package/dist/src/servers/zalo.d.ts +1 -0
  207. package/dist/src/servers/zalo.js +193 -82
  208. package/dist/src/servers/zalo.js.map +1 -1
  209. package/docs/channel-setup.md +251 -64
  210. package/fixtures/examples/crabline.example.yaml +3 -2
  211. package/package.json +18 -10
  212. package/dist/src/openclaw/smoke-lock.js +0 -1004
  213. package/dist/src/openclaw/smoke-lock.js.map +0 -1
  214. package/fixtures/examples/openclaw-bridge.yaml +0 -125
@@ -1,4 +1,4 @@
1
- import { type ClientRequest } from "node:http";
1
+ import { type ClientRequest, type IncomingHttpHeaders } from "node:http";
2
2
  export type WebhookAddress = {
3
3
  address: string;
4
4
  family: 4 | 6;
@@ -9,6 +9,10 @@ export type ValidatedWebhookTarget = {
9
9
  } | {
10
10
  error: WebhookTargetError;
11
11
  };
12
+ export type WebhookResponse = {
13
+ headers: IncomingHttpHeaders;
14
+ status: number;
15
+ };
12
16
  export declare const MAX_CONCURRENT_WEBHOOK_DNS_LOOKUPS = 8;
13
17
  type WebhookDnsLookupResult = ReadonlyArray<{
14
18
  address: string;
@@ -19,16 +23,18 @@ export declare class WebhookDnsLookupPool {
19
23
  #private;
20
24
  private readonly maxConcurrent;
21
25
  private readonly lookupHostname;
22
- constructor(maxConcurrent: number, lookupHostname?: WebhookDnsLookup);
26
+ private readonly abortedLookupRecoveryMs;
27
+ constructor(maxConcurrent: number, lookupHostname?: WebhookDnsLookup, abortedLookupRecoveryMs?: number);
23
28
  resolve(hostname: string, signal?: AbortSignal): Promise<WebhookDnsLookupResult>;
24
29
  }
25
30
  export declare function validateWebhookTarget(params: {
26
31
  allowLoopbackHttp: boolean;
32
+ dnsLookupPool?: Pick<WebhookDnsLookupPool, "resolve"> | undefined;
27
33
  restrictPrivateAddresses: boolean;
28
34
  signal?: AbortSignal | undefined;
29
35
  url: URL;
30
36
  }): Promise<ValidatedWebhookTarget>;
31
- export declare function postWebhookRequest(params: {
37
+ type PostWebhookRequestParams = {
32
38
  activeRequests?: Set<ClientRequest> | undefined;
33
39
  address?: WebhookAddress | undefined;
34
40
  body: string;
@@ -36,5 +42,7 @@ export declare function postWebhookRequest(params: {
36
42
  signal?: AbortSignal | undefined;
37
43
  timeoutMs: number;
38
44
  url: URL;
39
- }): Promise<number>;
45
+ };
46
+ export declare function postWebhookRequestWithResponse(params: PostWebhookRequestParams): Promise<WebhookResponse>;
47
+ export declare function postWebhookRequest(params: PostWebhookRequestParams): Promise<number>;
40
48
  export {};
@@ -1,5 +1,5 @@
1
1
  import { lookup } from "node:dns/promises";
2
- import { request as requestHttp } from "node:http";
2
+ import { request as requestHttp, } from "node:http";
3
3
  import { request as requestHttps } from "node:https";
4
4
  import { BlockList, isIP } from "node:net";
5
5
  import { isLoopbackHost } from "./http.js";
@@ -9,21 +9,43 @@ const BLOCKED_IPV6_ADDRESSES = createBlockedIpv6Addresses();
9
9
  const GLOBAL_IPV4_EXCEPTIONS = createGlobalIpv4Exceptions();
10
10
  const GLOBAL_IPV6_EXCEPTIONS = createGlobalIpv6Exceptions();
11
11
  const ALLOCATED_GLOBAL_IPV6_RANGES = createAllocatedGlobalIpv6Ranges();
12
+ const RFC6052_PREFIX_LENGTHS = [32, 40, 48, 56, 64, 96];
13
+ const IPV4ONLY_ADDRESSES = new Set(["192.0.0.170", "192.0.0.171"]);
14
+ const SENDER_CONTROLLED_WEBHOOK_HEADERS = new Set([
15
+ "connection",
16
+ "content-length",
17
+ "expect",
18
+ "host",
19
+ "keep-alive",
20
+ "proxy-connection",
21
+ "te",
22
+ "trailer",
23
+ "transfer-encoding",
24
+ "upgrade",
25
+ ]);
12
26
  export class WebhookDnsLookupPool {
13
27
  maxConcurrent;
14
28
  lookupHostname;
29
+ abortedLookupRecoveryMs;
15
30
  #pending = [];
31
+ #recoverable = new Set();
16
32
  #active = 0;
17
- constructor(maxConcurrent, lookupHostname = async (hostname) => await lookup(hostname, { all: true, verbatim: true })) {
33
+ #detached = 0;
34
+ constructor(maxConcurrent, lookupHostname = async (hostname) => await lookup(hostname, { all: true, verbatim: true }), abortedLookupRecoveryMs = 5_000) {
18
35
  this.maxConcurrent = maxConcurrent;
19
36
  this.lookupHostname = lookupHostname;
37
+ this.abortedLookupRecoveryMs = abortedLookupRecoveryMs;
20
38
  if (!Number.isSafeInteger(maxConcurrent) || maxConcurrent < 1) {
21
39
  throw new Error("Webhook DNS lookup concurrency must be a positive safe integer.");
22
40
  }
41
+ if (!Number.isSafeInteger(abortedLookupRecoveryMs) || abortedLookupRecoveryMs < 1) {
42
+ throw new Error("Webhook DNS lookup recovery must be a positive safe integer.");
43
+ }
23
44
  }
24
45
  resolve(hostname, signal) {
25
46
  return new Promise((resolve, reject) => {
26
47
  const pending = {
48
+ detached: false,
27
49
  hostname,
28
50
  onAbort: () => {
29
51
  if (pending.settled) {
@@ -36,14 +58,20 @@ export class WebhookDnsLookupPool {
36
58
  this.#pending.splice(index, 1);
37
59
  }
38
60
  }
61
+ else {
62
+ this.#scheduleRecovery(pending);
63
+ }
39
64
  signal?.removeEventListener("abort", pending.onAbort);
40
65
  reject(abortSignalError(signal));
41
66
  this.#pump();
42
67
  },
43
68
  reject,
69
+ recoveryReady: false,
70
+ recoveryTimer: undefined,
44
71
  resolve,
45
72
  settled: false,
46
73
  signal,
74
+ slotHeld: false,
47
75
  started: false,
48
76
  };
49
77
  if (signal?.aborted) {
@@ -65,8 +93,16 @@ export class WebhookDnsLookupPool {
65
93
  continue;
66
94
  }
67
95
  pending.started = true;
96
+ pending.slotHeld = true;
68
97
  this.#active += 1;
69
- void this.lookupHostname(pending.hostname)
98
+ let lookupPromise;
99
+ try {
100
+ lookupPromise = this.lookupHostname(pending.hostname);
101
+ }
102
+ catch (error) {
103
+ lookupPromise = Promise.reject(error);
104
+ }
105
+ void lookupPromise
70
106
  .then((addresses) => {
71
107
  if (!pending.settled) {
72
108
  pending.settled = true;
@@ -79,12 +115,61 @@ export class WebhookDnsLookupPool {
79
115
  }
80
116
  })
81
117
  .finally(() => {
118
+ if (pending.recoveryTimer) {
119
+ clearTimeout(pending.recoveryTimer);
120
+ pending.recoveryTimer = undefined;
121
+ }
122
+ this.#recoverable.delete(pending);
82
123
  pending.signal?.removeEventListener("abort", pending.onAbort);
83
- this.#active -= 1;
84
- this.#pump();
124
+ if (pending.detached) {
125
+ pending.detached = false;
126
+ this.#detached -= 1;
127
+ this.#recoverSlots();
128
+ this.#pump();
129
+ }
130
+ else {
131
+ this.#releaseSlot(pending);
132
+ }
85
133
  });
86
134
  }
87
135
  }
136
+ #scheduleRecovery(pending) {
137
+ if (pending.recoveryTimer || pending.detached || !pending.slotHeld) {
138
+ return;
139
+ }
140
+ pending.recoveryTimer = setTimeout(() => {
141
+ pending.recoveryTimer = undefined;
142
+ if (!pending.slotHeld) {
143
+ return;
144
+ }
145
+ pending.recoveryReady = true;
146
+ this.#recoverable.add(pending);
147
+ this.#recoverSlots();
148
+ }, this.abortedLookupRecoveryMs);
149
+ pending.recoveryTimer.unref();
150
+ }
151
+ #recoverSlots() {
152
+ for (const pending of this.#recoverable) {
153
+ if (this.#detached >= this.maxConcurrent) {
154
+ return;
155
+ }
156
+ this.#recoverable.delete(pending);
157
+ if (!pending.recoveryReady || !pending.slotHeld) {
158
+ continue;
159
+ }
160
+ pending.detached = true;
161
+ this.#detached += 1;
162
+ this.#releaseSlot(pending);
163
+ }
164
+ }
165
+ #releaseSlot(pending) {
166
+ if (!pending.slotHeld) {
167
+ return;
168
+ }
169
+ pending.slotHeld = false;
170
+ this.#active -= 1;
171
+ this.#pump();
172
+ }
88
173
  }
89
174
  const webhookDnsLookupPool = new WebhookDnsLookupPool(MAX_CONCURRENT_WEBHOOK_DNS_LOOKUPS);
90
175
  function abortSignalError(signal) {
@@ -227,17 +312,83 @@ function parseIpv6Hextets(address) {
227
312
  const hextets = [...leading, ...Array.from({ length: omitted }, () => 0), ...trailing];
228
313
  return hextets.length === 8 ? hextets : undefined;
229
314
  }
230
- function embeddedNat64Ipv4(address) {
315
+ function ipv6Bytes(address) {
316
+ if (isIP(address) !== 6) {
317
+ return undefined;
318
+ }
231
319
  const hextets = parseIpv6Hextets(address);
232
- if (!hextets ||
233
- hextets[0] !== 0x64 ||
234
- hextets[1] !== 0xff9b ||
235
- hextets.slice(2, 6).some((value) => value !== 0)) {
320
+ if (!hextets) {
236
321
  return undefined;
237
322
  }
238
- return [hextets[6] >> 8, hextets[6] & 0xff, hextets[7] >> 8, hextets[7] & 0xff].join(".");
323
+ return hextets.flatMap((value) => [value >> 8, value & 0xff]);
324
+ }
325
+ function extractRfc6052Ipv4(bytes, prefixLength) {
326
+ if (prefixLength === 96) {
327
+ return bytes.slice(12, 16).join(".");
328
+ }
329
+ const prefixBytes = prefixLength / 8;
330
+ const leadingIpv4Bytes = 8 - prefixBytes;
331
+ return [...bytes.slice(prefixBytes, 8), ...bytes.slice(9, 9 + (4 - leadingIpv4Bytes))].join(".");
239
332
  }
240
- function isBlockedWebhookAddress(address) {
333
+ function prefixMatches(bytes, prefix) {
334
+ const prefixBytes = prefix.length / 8;
335
+ return prefix.bytes.slice(0, prefixBytes).every((value, index) => bytes[index] === value);
336
+ }
337
+ function nat64PrefixKey(prefix) {
338
+ return `${prefix.length}:${prefix.bytes.slice(0, prefix.length / 8).join(".")}`;
339
+ }
340
+ function discoverNat64Prefixes(addresses) {
341
+ const candidates = new Map();
342
+ let ipv6Answers = 0;
343
+ for (const entry of addresses) {
344
+ if (entry.family !== 6) {
345
+ continue;
346
+ }
347
+ ipv6Answers++;
348
+ const bytes = ipv6Bytes(normalizeHostname(entry.address));
349
+ if (!bytes) {
350
+ continue;
351
+ }
352
+ for (const length of RFC6052_PREFIX_LENGTHS) {
353
+ if (length !== 96 && bytes[8] !== 0) {
354
+ continue;
355
+ }
356
+ if (!IPV4ONLY_ADDRESSES.has(extractRfc6052Ipv4(bytes, length))) {
357
+ continue;
358
+ }
359
+ const prefix = { bytes, length };
360
+ const key = nat64PrefixKey(prefix);
361
+ const candidate = candidates.get(key) ?? { addresses: new Set(), prefix };
362
+ candidate.addresses.add(extractRfc6052Ipv4(bytes, length));
363
+ candidates.set(key, candidate);
364
+ }
365
+ }
366
+ if (ipv6Answers === 0) {
367
+ return [];
368
+ }
369
+ const prefixes = [...candidates.values()]
370
+ .filter((candidate) => [...IPV4ONLY_ADDRESSES].every((address) => candidate.addresses.has(address)))
371
+ .map((candidate) => candidate.prefix);
372
+ return prefixes.length > 0 ? prefixes : undefined;
373
+ }
374
+ const WELL_KNOWN_NAT64_PREFIX = {
375
+ bytes: ipv6Bytes("64:ff9b::"),
376
+ length: 96,
377
+ };
378
+ function embeddedNat64Ipv4Addresses(address, nat64Prefixes) {
379
+ const bytes = ipv6Bytes(address);
380
+ if (!bytes) {
381
+ return [];
382
+ }
383
+ const addresses = [];
384
+ for (const prefix of [WELL_KNOWN_NAT64_PREFIX, ...nat64Prefixes]) {
385
+ if (prefixMatches(bytes, prefix)) {
386
+ addresses.push(extractRfc6052Ipv4(bytes, prefix.length));
387
+ }
388
+ }
389
+ return addresses;
390
+ }
391
+ function isBlockedWebhookAddress(address, nat64Prefixes = []) {
241
392
  const normalized = normalizeHostname(address);
242
393
  const family = isIP(normalized);
243
394
  if (family === 4) {
@@ -245,8 +396,7 @@ function isBlockedWebhookAddress(address) {
245
396
  BLOCKED_IPV4_ADDRESSES.check(normalized, "ipv4"));
246
397
  }
247
398
  if (family === 6) {
248
- const embeddedIpv4 = embeddedNat64Ipv4(normalized);
249
- if (embeddedIpv4 && isBlockedWebhookAddress(embeddedIpv4)) {
399
+ if (embeddedNat64Ipv4Addresses(normalized, nat64Prefixes).some((embeddedIpv4) => isBlockedWebhookAddress(embeddedIpv4))) {
250
400
  return true;
251
401
  }
252
402
  if (GLOBAL_IPV6_EXCEPTIONS.check(normalized, "ipv6")) {
@@ -257,13 +407,13 @@ function isBlockedWebhookAddress(address) {
257
407
  }
258
408
  return false;
259
409
  }
260
- async function resolveWebhookAddresses(hostname, signal) {
410
+ async function resolveWebhookAddresses(hostname, signal, dnsLookupPool = webhookDnsLookupPool) {
261
411
  const normalized = normalizeHostname(hostname);
262
412
  const family = isIP(normalized);
263
413
  if (family === 4 || family === 6) {
264
414
  return [{ address: normalized, family }];
265
415
  }
266
- return (await webhookDnsLookupPool.resolve(normalized, signal)).flatMap((entry) => entry.family === 4 || entry.family === 6
416
+ return (await dnsLookupPool.resolve(normalized, signal)).flatMap((entry) => entry.family === 4 || entry.family === 6
267
417
  ? [{ address: entry.address, family: entry.family }]
268
418
  : []);
269
419
  }
@@ -274,15 +424,12 @@ export async function validateWebhookTarget(params) {
274
424
  if (params.url.protocol !== "https:" && !allowThisLoopbackHttp) {
275
425
  return { error: "https-required" };
276
426
  }
277
- if (allowThisLoopbackHttp) {
278
- return { addresses: undefined };
279
- }
280
- if (!params.restrictPrivateAddresses) {
427
+ if (!params.restrictPrivateAddresses && !allowThisLoopbackHttp) {
281
428
  return { addresses: undefined };
282
429
  }
283
430
  let addresses;
284
431
  try {
285
- addresses = await resolveWebhookAddresses(params.url.hostname, params.signal);
432
+ addresses = await resolveWebhookAddresses(params.url.hostname, params.signal, params.dnsLookupPool);
286
433
  }
287
434
  catch (error) {
288
435
  if (params.signal?.aborted) {
@@ -293,12 +440,50 @@ export async function validateWebhookTarget(params) {
293
440
  if (addresses.length === 0) {
294
441
  return { error: "unresolvable" };
295
442
  }
443
+ if (allowThisLoopbackHttp) {
444
+ return addresses.every((entry) => isLoopbackHost(entry.address))
445
+ ? { addresses }
446
+ : { error: "private-address" };
447
+ }
296
448
  if (addresses.some((entry) => isBlockedWebhookAddress(entry.address))) {
297
449
  return { error: "private-address" };
298
450
  }
451
+ if (addresses.some((entry) => entry.family === 6)) {
452
+ let nat64Prefixes;
453
+ try {
454
+ nat64Prefixes = discoverNat64Prefixes(await (params.dnsLookupPool ?? webhookDnsLookupPool).resolve("ipv4only.arpa", params.signal));
455
+ }
456
+ catch (error) {
457
+ if (params.signal?.aborted) {
458
+ throw error;
459
+ }
460
+ return { error: "private-address" };
461
+ }
462
+ if (!nat64Prefixes) {
463
+ return { error: "private-address" };
464
+ }
465
+ if (addresses.some((entry) => isBlockedWebhookAddress(entry.address, nat64Prefixes))) {
466
+ return { error: "private-address" };
467
+ }
468
+ }
299
469
  return { addresses };
300
470
  }
301
- export async function postWebhookRequest(params) {
471
+ function webhookRequestHeaders(body, headerEntries) {
472
+ const headers = {
473
+ "content-length": String(Buffer.byteLength(body)),
474
+ "content-type": "application/json",
475
+ };
476
+ for (const [name, value] of headerEntries ?? []) {
477
+ const normalizedName = name.toLowerCase();
478
+ if (SENDER_CONTROLLED_WEBHOOK_HEADERS.has(normalizedName)) {
479
+ throw new Error(`Webhook header "${name}" is controlled by the sender.`);
480
+ }
481
+ headers[normalizedName] = value;
482
+ }
483
+ return headers;
484
+ }
485
+ async function sendWebhookRequest(params, resolveOnHeaders) {
486
+ const headers = webhookRequestHeaders(params.body, params.headerEntries);
302
487
  return await new Promise((resolve, reject) => {
303
488
  const address = params.address;
304
489
  let settled = false;
@@ -317,11 +502,7 @@ export async function postWebhookRequest(params) {
317
502
  const request = send(params.url, {
318
503
  agent: false,
319
504
  ...(address ? { family: address.family } : {}),
320
- headers: {
321
- "content-length": String(Buffer.byteLength(params.body)),
322
- "content-type": "application/json",
323
- ...Object.fromEntries(params.headerEntries ?? []),
324
- },
505
+ headers,
325
506
  ...(address
326
507
  ? {
327
508
  lookup: (_hostname, _options, callback) => callback(null, address.address, address.family),
@@ -331,9 +512,20 @@ export async function postWebhookRequest(params) {
331
512
  signal: params.signal,
332
513
  }, (incoming) => {
333
514
  response = incoming;
515
+ const result = {
516
+ headers: incoming.headers,
517
+ status: incoming.statusCode ?? 0,
518
+ };
519
+ if (resolveOnHeaders) {
520
+ finish(() => {
521
+ incoming.destroy();
522
+ resolve(result);
523
+ });
524
+ return;
525
+ }
334
526
  incoming.resume();
335
527
  incoming.once("error", (error) => finish(() => reject(error)));
336
- incoming.once("end", () => finish(() => resolve(incoming.statusCode ?? 0)));
528
+ incoming.once("end", () => finish(() => resolve(result)));
337
529
  });
338
530
  params.activeRequests?.add(request);
339
531
  deadline = setTimeout(() => {
@@ -346,4 +538,10 @@ export async function postWebhookRequest(params) {
346
538
  request.end(params.body);
347
539
  });
348
540
  }
541
+ export async function postWebhookRequestWithResponse(params) {
542
+ return await sendWebhookRequest(params, true);
543
+ }
544
+ export async function postWebhookRequest(params) {
545
+ return (await sendWebhookRequest(params, false)).status;
546
+ }
349
547
  //# sourceMappingURL=webhook-target.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-target.js","sourceRoot":"","sources":["../../../src/servers/webhook-target.ts"],"names":[],"mappings":"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","sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { request as requestHttp, type ClientRequest, type IncomingMessage } from \"node:http\";\nimport { request as requestHttps } from \"node:https\";\nimport { BlockList, isIP } from \"node:net\";\nimport { isLoopbackHost } from \"./http.js\";\n\nexport type WebhookAddress = {\n address: string;\n family: 4 | 6;\n};\n\nexport type WebhookTargetError = \"https-required\" | \"private-address\" | \"unresolvable\";\n\nexport type ValidatedWebhookTarget =\n | { addresses: WebhookAddress[] | undefined }\n | { error: WebhookTargetError };\n\nexport const MAX_CONCURRENT_WEBHOOK_DNS_LOOKUPS = 8;\n\nconst BLOCKED_IPV4_ADDRESSES = createBlockedIpv4Addresses();\nconst BLOCKED_IPV6_ADDRESSES = createBlockedIpv6Addresses();\nconst GLOBAL_IPV4_EXCEPTIONS = createGlobalIpv4Exceptions();\nconst GLOBAL_IPV6_EXCEPTIONS = createGlobalIpv6Exceptions();\nconst ALLOCATED_GLOBAL_IPV6_RANGES = createAllocatedGlobalIpv6Ranges();\n\ntype WebhookDnsLookupResult = ReadonlyArray<{ address: string; family: number }>;\ntype WebhookDnsLookup = (hostname: string) => Promise<WebhookDnsLookupResult>;\n\ntype PendingWebhookDnsLookup = {\n hostname: string;\n onAbort: () => void;\n reject(error: unknown): void;\n resolve(addresses: WebhookDnsLookupResult): void;\n settled: boolean;\n signal: AbortSignal | undefined;\n started: boolean;\n};\n\nexport class WebhookDnsLookupPool {\n readonly #pending: PendingWebhookDnsLookup[] = [];\n #active = 0;\n\n constructor(\n private readonly maxConcurrent: number,\n private readonly lookupHostname: WebhookDnsLookup = async (hostname) =>\n await lookup(hostname, { all: true, verbatim: true }),\n ) {\n if (!Number.isSafeInteger(maxConcurrent) || maxConcurrent < 1) {\n throw new Error(\"Webhook DNS lookup concurrency must be a positive safe integer.\");\n }\n }\n\n resolve(hostname: string, signal?: AbortSignal): Promise<WebhookDnsLookupResult> {\n return new Promise<WebhookDnsLookupResult>((resolve, reject) => {\n const pending: PendingWebhookDnsLookup = {\n hostname,\n onAbort: () => {\n if (pending.settled) {\n return;\n }\n pending.settled = true;\n if (!pending.started) {\n const index = this.#pending.indexOf(pending);\n if (index >= 0) {\n this.#pending.splice(index, 1);\n }\n }\n signal?.removeEventListener(\"abort\", pending.onAbort);\n reject(abortSignalError(signal));\n this.#pump();\n },\n reject,\n resolve,\n settled: false,\n signal,\n started: false,\n };\n if (signal?.aborted) {\n pending.onAbort();\n return;\n }\n signal?.addEventListener(\"abort\", pending.onAbort, { once: true });\n this.#pending.push(pending);\n this.#pump();\n });\n }\n\n #pump(): void {\n while (this.#active < this.maxConcurrent) {\n const pending = this.#pending.shift();\n if (!pending) {\n return;\n }\n if (pending.settled) {\n continue;\n }\n pending.started = true;\n this.#active += 1;\n void this.lookupHostname(pending.hostname)\n .then(\n (addresses) => {\n if (!pending.settled) {\n pending.settled = true;\n pending.resolve(addresses);\n }\n },\n (error: unknown) => {\n if (!pending.settled) {\n pending.settled = true;\n pending.reject(error);\n }\n },\n )\n .finally(() => {\n pending.signal?.removeEventListener(\"abort\", pending.onAbort);\n this.#active -= 1;\n this.#pump();\n });\n }\n }\n}\n\nconst webhookDnsLookupPool = new WebhookDnsLookupPool(MAX_CONCURRENT_WEBHOOK_DNS_LOOKUPS);\n\nfunction abortSignalError(signal: AbortSignal | undefined): Error {\n return signal?.reason instanceof Error\n ? signal.reason\n : new DOMException(\"Webhook DNS lookup aborted\", \"AbortError\");\n}\n\nfunction createBlockedIpv4Addresses(): BlockList {\n const blockList = new BlockList();\n for (const [address, prefix] of [\n [\"0.0.0.0\", 8],\n [\"10.0.0.0\", 8],\n [\"100.64.0.0\", 10],\n [\"127.0.0.0\", 8],\n [\"169.254.0.0\", 16],\n [\"172.16.0.0\", 12],\n [\"192.0.0.0\", 24],\n [\"192.0.2.0\", 24],\n [\"192.88.99.0\", 24],\n [\"192.168.0.0\", 16],\n [\"198.18.0.0\", 15],\n [\"198.51.100.0\", 24],\n [\"203.0.113.0\", 24],\n [\"224.0.0.0\", 4],\n [\"240.0.0.0\", 4],\n ] as const) {\n blockList.addSubnet(address, prefix, \"ipv4\");\n }\n return blockList;\n}\n\nfunction createBlockedIpv6Addresses(): BlockList {\n const blockList = new BlockList();\n for (const [address, prefix] of [\n [\"::\", 96],\n [\"::ffff:0:0\", 96],\n [\"64:ff9b:1::\", 48],\n [\"100::\", 64],\n [\"100:0:0:1::\", 64],\n [\"2001::\", 23],\n [\"2001:db8::\", 32],\n [\"2002::\", 16],\n [\"3fff::\", 20],\n [\"5f00::\", 16],\n [\"fc00::\", 7],\n [\"fe80::\", 10],\n [\"ff00::\", 8],\n ] as const) {\n blockList.addSubnet(address, prefix, \"ipv6\");\n }\n return blockList;\n}\n\nfunction createGlobalIpv4Exceptions(): BlockList {\n const blockList = new BlockList();\n // These are globally reachable assignments inside 192.0.0.0/24.\n for (const address of [\"192.0.0.9\", \"192.0.0.10\"] as const) {\n blockList.addAddress(address, \"ipv4\");\n }\n return blockList;\n}\n\nfunction createGlobalIpv6Exceptions(): BlockList {\n const blockList = new BlockList();\n // These are globally reachable assignments inside 2001::/23.\n for (const [address, prefix] of [\n [\"2001:1::1\", 128],\n [\"2001:1::2\", 128],\n [\"2001:1::3\", 128],\n [\"2001:3::\", 32],\n [\"2001:4:112::\", 48],\n [\"2001:20::\", 28],\n [\"2001:30::\", 28],\n ] as const) {\n blockList.addSubnet(address, prefix, \"ipv6\");\n }\n return blockList;\n}\n\nfunction createAllocatedGlobalIpv6Ranges(): BlockList {\n const blockList = new BlockList();\n // IANA reserves unlisted portions of 2000::/3 for future allocation.\n for (const [address, prefix] of [\n [\"2001:200::\", 23],\n [\"2001:400::\", 23],\n [\"2001:600::\", 23],\n [\"2001:800::\", 22],\n [\"2001:c00::\", 23],\n [\"2001:e00::\", 23],\n [\"2001:1200::\", 23],\n [\"2001:1400::\", 22],\n [\"2001:1800::\", 23],\n [\"2001:1a00::\", 23],\n [\"2001:1c00::\", 22],\n [\"2001:2000::\", 19],\n [\"2001:4000::\", 23],\n [\"2001:4200::\", 23],\n [\"2001:4400::\", 23],\n [\"2001:4600::\", 23],\n [\"2001:4800::\", 23],\n [\"2001:4a00::\", 23],\n [\"2001:4c00::\", 23],\n [\"2001:5000::\", 20],\n [\"2001:8000::\", 19],\n [\"2001:a000::\", 20],\n [\"2001:b000::\", 20],\n [\"2003::\", 18],\n [\"2400::\", 12],\n [\"2410::\", 12],\n [\"2600::\", 12],\n [\"2610::\", 23],\n [\"2620::\", 23],\n [\"2630::\", 12],\n [\"2800::\", 12],\n [\"2a00::\", 12],\n [\"2a10::\", 12],\n [\"2c00::\", 12],\n ] as const) {\n blockList.addSubnet(address, prefix, \"ipv6\");\n }\n blockList.addSubnet(\"64:ff9b::\", 96, \"ipv6\");\n return blockList;\n}\n\nfunction normalizeHostname(hostname: string): string {\n return hostname.replace(/^\\[(.*)\\]$/u, \"$1\").replace(/%25/gu, \"%\");\n}\n\nfunction parseIpv6Hextets(address: string): number[] | undefined {\n const sections = address.split(\"::\");\n if (sections.length > 2) {\n return undefined;\n }\n const parseSection = (section: string): number[] =>\n section\n ? section.split(\":\").flatMap((value) => {\n if (!value.includes(\".\")) {\n return [Number.parseInt(value, 16)];\n }\n const octets = value.split(\".\").map(Number);\n return [(octets[0]! << 8) | octets[1]!, (octets[2]! << 8) | octets[3]!];\n })\n : [];\n const leading = parseSection(sections[0]!);\n const trailing = parseSection(sections[1] ?? \"\");\n const omitted = sections.length === 2 ? 8 - leading.length - trailing.length : 0;\n const hextets = [...leading, ...Array.from({ length: omitted }, () => 0), ...trailing];\n return hextets.length === 8 ? hextets : undefined;\n}\n\nfunction embeddedNat64Ipv4(address: string): string | undefined {\n const hextets = parseIpv6Hextets(address);\n if (\n !hextets ||\n hextets[0] !== 0x64 ||\n hextets[1] !== 0xff9b ||\n hextets.slice(2, 6).some((value) => value !== 0)\n ) {\n return undefined;\n }\n return [hextets[6]! >> 8, hextets[6]! & 0xff, hextets[7]! >> 8, hextets[7]! & 0xff].join(\".\");\n}\n\nfunction isBlockedWebhookAddress(address: string): boolean {\n const normalized = normalizeHostname(address);\n const family = isIP(normalized);\n if (family === 4) {\n return (\n !GLOBAL_IPV4_EXCEPTIONS.check(normalized, \"ipv4\") &&\n BLOCKED_IPV4_ADDRESSES.check(normalized, \"ipv4\")\n );\n }\n if (family === 6) {\n const embeddedIpv4 = embeddedNat64Ipv4(normalized);\n if (embeddedIpv4 && isBlockedWebhookAddress(embeddedIpv4)) {\n return true;\n }\n if (GLOBAL_IPV6_EXCEPTIONS.check(normalized, \"ipv6\")) {\n return false;\n }\n return (\n BLOCKED_IPV6_ADDRESSES.check(normalized, \"ipv6\") ||\n !ALLOCATED_GLOBAL_IPV6_RANGES.check(normalized, \"ipv6\")\n );\n }\n return false;\n}\n\nasync function resolveWebhookAddresses(\n hostname: string,\n signal?: AbortSignal,\n): Promise<WebhookAddress[]> {\n const normalized = normalizeHostname(hostname);\n const family = isIP(normalized);\n if (family === 4 || family === 6) {\n return [{ address: normalized, family }];\n }\n return (await webhookDnsLookupPool.resolve(normalized, signal)).flatMap((entry) =>\n entry.family === 4 || entry.family === 6\n ? [{ address: entry.address, family: entry.family }]\n : [],\n );\n}\n\nexport async function validateWebhookTarget(params: {\n allowLoopbackHttp: boolean;\n restrictPrivateAddresses: boolean;\n signal?: AbortSignal | undefined;\n url: URL;\n}): Promise<ValidatedWebhookTarget> {\n const allowThisLoopbackHttp =\n params.url.protocol === \"http:\" &&\n params.allowLoopbackHttp &&\n isLoopbackHost(params.url.hostname);\n if (params.url.protocol !== \"https:\" && !allowThisLoopbackHttp) {\n return { error: \"https-required\" };\n }\n if (allowThisLoopbackHttp) {\n return { addresses: undefined };\n }\n if (!params.restrictPrivateAddresses) {\n return { addresses: undefined };\n }\n\n let addresses: WebhookAddress[];\n try {\n addresses = await resolveWebhookAddresses(params.url.hostname, params.signal);\n } catch (error) {\n if (params.signal?.aborted) {\n throw error;\n }\n return { error: \"unresolvable\" };\n }\n if (addresses.length === 0) {\n return { error: \"unresolvable\" };\n }\n if (addresses.some((entry) => isBlockedWebhookAddress(entry.address))) {\n return { error: \"private-address\" };\n }\n return { addresses };\n}\n\nexport async function postWebhookRequest(params: {\n activeRequests?: Set<ClientRequest> | undefined;\n address?: WebhookAddress | undefined;\n body: string;\n headerEntries?: ReadonlyArray<readonly [string, string]> | undefined;\n signal?: AbortSignal | undefined;\n timeoutMs: number;\n url: URL;\n}): Promise<number> {\n return await new Promise<number>((resolve, reject) => {\n const address = params.address;\n let settled = false;\n let response: IncomingMessage | undefined;\n let deadline: NodeJS.Timeout;\n const finish = (callback: () => void) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(deadline);\n params.activeRequests?.delete(request);\n callback();\n };\n const send = params.url.protocol === \"https:\" ? requestHttps : requestHttp;\n const request = send(\n params.url,\n {\n agent: false,\n ...(address ? { family: address.family } : {}),\n headers: {\n \"content-length\": String(Buffer.byteLength(params.body)),\n \"content-type\": \"application/json\",\n ...Object.fromEntries(params.headerEntries ?? []),\n },\n ...(address\n ? {\n lookup: (_hostname, _options, callback) =>\n callback(null, address.address, address.family),\n }\n : {}),\n method: \"POST\",\n signal: params.signal,\n },\n (incoming) => {\n response = incoming;\n incoming.resume();\n incoming.once(\"error\", (error) => finish(() => reject(error)));\n incoming.once(\"end\", () => finish(() => resolve(incoming.statusCode ?? 0)));\n },\n );\n params.activeRequests?.add(request);\n deadline = setTimeout(() => {\n const error = new DOMException(\n `Webhook delivery timed out after ${params.timeoutMs}ms`,\n \"TimeoutError\",\n );\n response?.destroy(error);\n request.destroy(error);\n }, params.timeoutMs);\n deadline.unref();\n request.once(\"error\", (error) => finish(() => reject(error)));\n request.end(params.body);\n });\n}\n"]}
1
+ {"version":3,"file":"webhook-target.js","sourceRoot":"","sources":["../../../src/servers/webhook-target.ts"],"names":[],"mappings":"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","sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport {\n request as requestHttp,\n type ClientRequest,\n type IncomingHttpHeaders,\n type IncomingMessage,\n} from \"node:http\";\nimport { request as requestHttps } from \"node:https\";\nimport { BlockList, isIP } from \"node:net\";\nimport { isLoopbackHost } from \"./http.js\";\n\nexport type WebhookAddress = {\n address: string;\n family: 4 | 6;\n};\n\nexport type WebhookTargetError = \"https-required\" | \"private-address\" | \"unresolvable\";\n\nexport type ValidatedWebhookTarget =\n | { addresses: WebhookAddress[] | undefined }\n | { error: WebhookTargetError };\n\nexport type WebhookResponse = {\n headers: IncomingHttpHeaders;\n status: number;\n};\n\nexport const MAX_CONCURRENT_WEBHOOK_DNS_LOOKUPS = 8;\n\nconst BLOCKED_IPV4_ADDRESSES = createBlockedIpv4Addresses();\nconst BLOCKED_IPV6_ADDRESSES = createBlockedIpv6Addresses();\nconst GLOBAL_IPV4_EXCEPTIONS = createGlobalIpv4Exceptions();\nconst GLOBAL_IPV6_EXCEPTIONS = createGlobalIpv6Exceptions();\nconst ALLOCATED_GLOBAL_IPV6_RANGES = createAllocatedGlobalIpv6Ranges();\nconst RFC6052_PREFIX_LENGTHS = [32, 40, 48, 56, 64, 96] as const;\nconst IPV4ONLY_ADDRESSES = new Set([\"192.0.0.170\", \"192.0.0.171\"]);\nconst SENDER_CONTROLLED_WEBHOOK_HEADERS = new Set([\n \"connection\",\n \"content-length\",\n \"expect\",\n \"host\",\n \"keep-alive\",\n \"proxy-connection\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n]);\n\ntype WebhookDnsLookupResult = ReadonlyArray<{ address: string; family: number }>;\ntype WebhookDnsLookup = (hostname: string) => Promise<WebhookDnsLookupResult>;\ntype Nat64PrefixLength = (typeof RFC6052_PREFIX_LENGTHS)[number];\ntype Nat64Prefix = {\n bytes: readonly number[];\n length: Nat64PrefixLength;\n};\n\ntype PendingWebhookDnsLookup = {\n detached: boolean;\n hostname: string;\n onAbort: () => void;\n recoveryReady: boolean;\n recoveryTimer: NodeJS.Timeout | undefined;\n reject(error: unknown): void;\n resolve(addresses: WebhookDnsLookupResult): void;\n settled: boolean;\n signal: AbortSignal | undefined;\n slotHeld: boolean;\n started: boolean;\n};\n\nexport class WebhookDnsLookupPool {\n readonly #pending: PendingWebhookDnsLookup[] = [];\n readonly #recoverable = new Set<PendingWebhookDnsLookup>();\n #active = 0;\n #detached = 0;\n\n constructor(\n private readonly maxConcurrent: number,\n private readonly lookupHostname: WebhookDnsLookup = async (hostname) =>\n await lookup(hostname, { all: true, verbatim: true }),\n private readonly abortedLookupRecoveryMs = 5_000,\n ) {\n if (!Number.isSafeInteger(maxConcurrent) || maxConcurrent < 1) {\n throw new Error(\"Webhook DNS lookup concurrency must be a positive safe integer.\");\n }\n if (!Number.isSafeInteger(abortedLookupRecoveryMs) || abortedLookupRecoveryMs < 1) {\n throw new Error(\"Webhook DNS lookup recovery must be a positive safe integer.\");\n }\n }\n\n resolve(hostname: string, signal?: AbortSignal): Promise<WebhookDnsLookupResult> {\n return new Promise<WebhookDnsLookupResult>((resolve, reject) => {\n const pending: PendingWebhookDnsLookup = {\n detached: false,\n hostname,\n onAbort: () => {\n if (pending.settled) {\n return;\n }\n pending.settled = true;\n if (!pending.started) {\n const index = this.#pending.indexOf(pending);\n if (index >= 0) {\n this.#pending.splice(index, 1);\n }\n } else {\n this.#scheduleRecovery(pending);\n }\n signal?.removeEventListener(\"abort\", pending.onAbort);\n reject(abortSignalError(signal));\n this.#pump();\n },\n reject,\n recoveryReady: false,\n recoveryTimer: undefined,\n resolve,\n settled: false,\n signal,\n slotHeld: false,\n started: false,\n };\n if (signal?.aborted) {\n pending.onAbort();\n return;\n }\n signal?.addEventListener(\"abort\", pending.onAbort, { once: true });\n this.#pending.push(pending);\n this.#pump();\n });\n }\n\n #pump(): void {\n while (this.#active < this.maxConcurrent) {\n const pending = this.#pending.shift();\n if (!pending) {\n return;\n }\n if (pending.settled) {\n continue;\n }\n pending.started = true;\n pending.slotHeld = true;\n this.#active += 1;\n let lookupPromise: Promise<WebhookDnsLookupResult>;\n try {\n lookupPromise = this.lookupHostname(pending.hostname);\n } catch (error) {\n lookupPromise = Promise.reject(error);\n }\n void lookupPromise\n .then(\n (addresses) => {\n if (!pending.settled) {\n pending.settled = true;\n pending.resolve(addresses);\n }\n },\n (error: unknown) => {\n if (!pending.settled) {\n pending.settled = true;\n pending.reject(error);\n }\n },\n )\n .finally(() => {\n if (pending.recoveryTimer) {\n clearTimeout(pending.recoveryTimer);\n pending.recoveryTimer = undefined;\n }\n this.#recoverable.delete(pending);\n pending.signal?.removeEventListener(\"abort\", pending.onAbort);\n if (pending.detached) {\n pending.detached = false;\n this.#detached -= 1;\n this.#recoverSlots();\n this.#pump();\n } else {\n this.#releaseSlot(pending);\n }\n });\n }\n }\n\n #scheduleRecovery(pending: PendingWebhookDnsLookup): void {\n if (pending.recoveryTimer || pending.detached || !pending.slotHeld) {\n return;\n }\n pending.recoveryTimer = setTimeout(() => {\n pending.recoveryTimer = undefined;\n if (!pending.slotHeld) {\n return;\n }\n pending.recoveryReady = true;\n this.#recoverable.add(pending);\n this.#recoverSlots();\n }, this.abortedLookupRecoveryMs);\n pending.recoveryTimer.unref();\n }\n\n #recoverSlots(): void {\n for (const pending of this.#recoverable) {\n if (this.#detached >= this.maxConcurrent) {\n return;\n }\n this.#recoverable.delete(pending);\n if (!pending.recoveryReady || !pending.slotHeld) {\n continue;\n }\n pending.detached = true;\n this.#detached += 1;\n this.#releaseSlot(pending);\n }\n }\n\n #releaseSlot(pending: PendingWebhookDnsLookup): void {\n if (!pending.slotHeld) {\n return;\n }\n pending.slotHeld = false;\n this.#active -= 1;\n this.#pump();\n }\n}\n\nconst webhookDnsLookupPool = new WebhookDnsLookupPool(MAX_CONCURRENT_WEBHOOK_DNS_LOOKUPS);\n\nfunction abortSignalError(signal: AbortSignal | undefined): Error {\n return signal?.reason instanceof Error\n ? signal.reason\n : new DOMException(\"Webhook DNS lookup aborted\", \"AbortError\");\n}\n\nfunction createBlockedIpv4Addresses(): BlockList {\n const blockList = new BlockList();\n for (const [address, prefix] of [\n [\"0.0.0.0\", 8],\n [\"10.0.0.0\", 8],\n [\"100.64.0.0\", 10],\n [\"127.0.0.0\", 8],\n [\"169.254.0.0\", 16],\n [\"172.16.0.0\", 12],\n [\"192.0.0.0\", 24],\n [\"192.0.2.0\", 24],\n [\"192.88.99.0\", 24],\n [\"192.168.0.0\", 16],\n [\"198.18.0.0\", 15],\n [\"198.51.100.0\", 24],\n [\"203.0.113.0\", 24],\n [\"224.0.0.0\", 4],\n [\"240.0.0.0\", 4],\n ] as const) {\n blockList.addSubnet(address, prefix, \"ipv4\");\n }\n return blockList;\n}\n\nfunction createBlockedIpv6Addresses(): BlockList {\n const blockList = new BlockList();\n for (const [address, prefix] of [\n [\"::\", 96],\n [\"::ffff:0:0\", 96],\n [\"64:ff9b:1::\", 48],\n [\"100::\", 64],\n [\"100:0:0:1::\", 64],\n [\"2001::\", 23],\n [\"2001:db8::\", 32],\n [\"2002::\", 16],\n [\"3fff::\", 20],\n [\"5f00::\", 16],\n [\"fc00::\", 7],\n [\"fe80::\", 10],\n [\"ff00::\", 8],\n ] as const) {\n blockList.addSubnet(address, prefix, \"ipv6\");\n }\n return blockList;\n}\n\nfunction createGlobalIpv4Exceptions(): BlockList {\n const blockList = new BlockList();\n // These are globally reachable assignments inside 192.0.0.0/24.\n for (const address of [\"192.0.0.9\", \"192.0.0.10\"] as const) {\n blockList.addAddress(address, \"ipv4\");\n }\n return blockList;\n}\n\nfunction createGlobalIpv6Exceptions(): BlockList {\n const blockList = new BlockList();\n // These are globally reachable assignments inside 2001::/23.\n for (const [address, prefix] of [\n [\"2001:1::1\", 128],\n [\"2001:1::2\", 128],\n [\"2001:1::3\", 128],\n [\"2001:3::\", 32],\n [\"2001:4:112::\", 48],\n [\"2001:20::\", 28],\n [\"2001:30::\", 28],\n ] as const) {\n blockList.addSubnet(address, prefix, \"ipv6\");\n }\n return blockList;\n}\n\nfunction createAllocatedGlobalIpv6Ranges(): BlockList {\n const blockList = new BlockList();\n // IANA reserves unlisted portions of 2000::/3 for future allocation.\n for (const [address, prefix] of [\n [\"2001:200::\", 23],\n [\"2001:400::\", 23],\n [\"2001:600::\", 23],\n [\"2001:800::\", 22],\n [\"2001:c00::\", 23],\n [\"2001:e00::\", 23],\n [\"2001:1200::\", 23],\n [\"2001:1400::\", 22],\n [\"2001:1800::\", 23],\n [\"2001:1a00::\", 23],\n [\"2001:1c00::\", 22],\n [\"2001:2000::\", 19],\n [\"2001:4000::\", 23],\n [\"2001:4200::\", 23],\n [\"2001:4400::\", 23],\n [\"2001:4600::\", 23],\n [\"2001:4800::\", 23],\n [\"2001:4a00::\", 23],\n [\"2001:4c00::\", 23],\n [\"2001:5000::\", 20],\n [\"2001:8000::\", 19],\n [\"2001:a000::\", 20],\n [\"2001:b000::\", 20],\n [\"2003::\", 18],\n [\"2400::\", 12],\n [\"2410::\", 12],\n [\"2600::\", 12],\n [\"2610::\", 23],\n [\"2620::\", 23],\n [\"2630::\", 12],\n [\"2800::\", 12],\n [\"2a00::\", 12],\n [\"2a10::\", 12],\n [\"2c00::\", 12],\n ] as const) {\n blockList.addSubnet(address, prefix, \"ipv6\");\n }\n blockList.addSubnet(\"64:ff9b::\", 96, \"ipv6\");\n return blockList;\n}\n\nfunction normalizeHostname(hostname: string): string {\n return hostname.replace(/^\\[(.*)\\]$/u, \"$1\").replace(/%25/gu, \"%\");\n}\n\nfunction parseIpv6Hextets(address: string): number[] | undefined {\n const sections = address.split(\"::\");\n if (sections.length > 2) {\n return undefined;\n }\n const parseSection = (section: string): number[] =>\n section\n ? section.split(\":\").flatMap((value) => {\n if (!value.includes(\".\")) {\n return [Number.parseInt(value, 16)];\n }\n const octets = value.split(\".\").map(Number);\n return [(octets[0]! << 8) | octets[1]!, (octets[2]! << 8) | octets[3]!];\n })\n : [];\n const leading = parseSection(sections[0]!);\n const trailing = parseSection(sections[1] ?? \"\");\n const omitted = sections.length === 2 ? 8 - leading.length - trailing.length : 0;\n const hextets = [...leading, ...Array.from({ length: omitted }, () => 0), ...trailing];\n return hextets.length === 8 ? hextets : undefined;\n}\n\nfunction ipv6Bytes(address: string): number[] | undefined {\n if (isIP(address) !== 6) {\n return undefined;\n }\n const hextets = parseIpv6Hextets(address);\n if (!hextets) {\n return undefined;\n }\n return hextets.flatMap((value) => [value >> 8, value & 0xff]);\n}\n\nfunction extractRfc6052Ipv4(bytes: readonly number[], prefixLength: Nat64PrefixLength): string {\n if (prefixLength === 96) {\n return bytes.slice(12, 16).join(\".\");\n }\n const prefixBytes = prefixLength / 8;\n const leadingIpv4Bytes = 8 - prefixBytes;\n return [...bytes.slice(prefixBytes, 8), ...bytes.slice(9, 9 + (4 - leadingIpv4Bytes))].join(\".\");\n}\n\nfunction prefixMatches(bytes: readonly number[], prefix: Nat64Prefix): boolean {\n const prefixBytes = prefix.length / 8;\n return prefix.bytes.slice(0, prefixBytes).every((value, index) => bytes[index] === value);\n}\n\nfunction nat64PrefixKey(prefix: Nat64Prefix): string {\n return `${prefix.length}:${prefix.bytes.slice(0, prefix.length / 8).join(\".\")}`;\n}\n\nfunction discoverNat64Prefixes(addresses: WebhookDnsLookupResult): Nat64Prefix[] | undefined {\n const candidates = new Map<string, { addresses: Set<string>; prefix: Nat64Prefix }>();\n let ipv6Answers = 0;\n for (const entry of addresses) {\n if (entry.family !== 6) {\n continue;\n }\n ipv6Answers++;\n const bytes = ipv6Bytes(normalizeHostname(entry.address));\n if (!bytes) {\n continue;\n }\n for (const length of RFC6052_PREFIX_LENGTHS) {\n if (length !== 96 && bytes[8] !== 0) {\n continue;\n }\n if (!IPV4ONLY_ADDRESSES.has(extractRfc6052Ipv4(bytes, length))) {\n continue;\n }\n const prefix = { bytes, length };\n const key = nat64PrefixKey(prefix);\n const candidate = candidates.get(key) ?? { addresses: new Set(), prefix };\n candidate.addresses.add(extractRfc6052Ipv4(bytes, length));\n candidates.set(key, candidate);\n }\n }\n if (ipv6Answers === 0) {\n return [];\n }\n const prefixes = [...candidates.values()]\n .filter((candidate) =>\n [...IPV4ONLY_ADDRESSES].every((address) => candidate.addresses.has(address)),\n )\n .map((candidate) => candidate.prefix);\n return prefixes.length > 0 ? prefixes : undefined;\n}\n\nconst WELL_KNOWN_NAT64_PREFIX: Nat64Prefix = {\n bytes: ipv6Bytes(\"64:ff9b::\")!,\n length: 96,\n};\n\nfunction embeddedNat64Ipv4Addresses(\n address: string,\n nat64Prefixes: readonly Nat64Prefix[],\n): string[] {\n const bytes = ipv6Bytes(address);\n if (!bytes) {\n return [];\n }\n const addresses: string[] = [];\n for (const prefix of [WELL_KNOWN_NAT64_PREFIX, ...nat64Prefixes]) {\n if (prefixMatches(bytes, prefix)) {\n addresses.push(extractRfc6052Ipv4(bytes, prefix.length));\n }\n }\n return addresses;\n}\n\nfunction isBlockedWebhookAddress(\n address: string,\n nat64Prefixes: readonly Nat64Prefix[] = [],\n): boolean {\n const normalized = normalizeHostname(address);\n const family = isIP(normalized);\n if (family === 4) {\n return (\n !GLOBAL_IPV4_EXCEPTIONS.check(normalized, \"ipv4\") &&\n BLOCKED_IPV4_ADDRESSES.check(normalized, \"ipv4\")\n );\n }\n if (family === 6) {\n if (\n embeddedNat64Ipv4Addresses(normalized, nat64Prefixes).some((embeddedIpv4) =>\n isBlockedWebhookAddress(embeddedIpv4),\n )\n ) {\n return true;\n }\n if (GLOBAL_IPV6_EXCEPTIONS.check(normalized, \"ipv6\")) {\n return false;\n }\n return (\n BLOCKED_IPV6_ADDRESSES.check(normalized, \"ipv6\") ||\n !ALLOCATED_GLOBAL_IPV6_RANGES.check(normalized, \"ipv6\")\n );\n }\n return false;\n}\n\nasync function resolveWebhookAddresses(\n hostname: string,\n signal?: AbortSignal,\n dnsLookupPool: Pick<WebhookDnsLookupPool, \"resolve\"> = webhookDnsLookupPool,\n): Promise<WebhookAddress[]> {\n const normalized = normalizeHostname(hostname);\n const family = isIP(normalized);\n if (family === 4 || family === 6) {\n return [{ address: normalized, family }];\n }\n return (await dnsLookupPool.resolve(normalized, signal)).flatMap((entry) =>\n entry.family === 4 || entry.family === 6\n ? [{ address: entry.address, family: entry.family }]\n : [],\n );\n}\n\nexport async function validateWebhookTarget(params: {\n allowLoopbackHttp: boolean;\n dnsLookupPool?: Pick<WebhookDnsLookupPool, \"resolve\"> | undefined;\n restrictPrivateAddresses: boolean;\n signal?: AbortSignal | undefined;\n url: URL;\n}): Promise<ValidatedWebhookTarget> {\n const allowThisLoopbackHttp =\n params.url.protocol === \"http:\" &&\n params.allowLoopbackHttp &&\n isLoopbackHost(params.url.hostname);\n if (params.url.protocol !== \"https:\" && !allowThisLoopbackHttp) {\n return { error: \"https-required\" };\n }\n if (!params.restrictPrivateAddresses && !allowThisLoopbackHttp) {\n return { addresses: undefined };\n }\n\n let addresses: WebhookAddress[];\n try {\n addresses = await resolveWebhookAddresses(\n params.url.hostname,\n params.signal,\n params.dnsLookupPool,\n );\n } catch (error) {\n if (params.signal?.aborted) {\n throw error;\n }\n return { error: \"unresolvable\" };\n }\n if (addresses.length === 0) {\n return { error: \"unresolvable\" };\n }\n if (allowThisLoopbackHttp) {\n return addresses.every((entry) => isLoopbackHost(entry.address))\n ? { addresses }\n : { error: \"private-address\" };\n }\n if (addresses.some((entry) => isBlockedWebhookAddress(entry.address))) {\n return { error: \"private-address\" };\n }\n if (addresses.some((entry) => entry.family === 6)) {\n let nat64Prefixes: Nat64Prefix[] | undefined;\n try {\n nat64Prefixes = discoverNat64Prefixes(\n await (params.dnsLookupPool ?? webhookDnsLookupPool).resolve(\n \"ipv4only.arpa\",\n params.signal,\n ),\n );\n } catch (error) {\n if (params.signal?.aborted) {\n throw error;\n }\n return { error: \"private-address\" };\n }\n if (!nat64Prefixes) {\n return { error: \"private-address\" };\n }\n if (addresses.some((entry) => isBlockedWebhookAddress(entry.address, nat64Prefixes))) {\n return { error: \"private-address\" };\n }\n }\n return { addresses };\n}\n\ntype PostWebhookRequestParams = {\n activeRequests?: Set<ClientRequest> | undefined;\n address?: WebhookAddress | undefined;\n body: string;\n headerEntries?: ReadonlyArray<readonly [string, string]> | undefined;\n signal?: AbortSignal | undefined;\n timeoutMs: number;\n url: URL;\n};\n\nfunction webhookRequestHeaders(\n body: string,\n headerEntries: PostWebhookRequestParams[\"headerEntries\"],\n): Record<string, string> {\n const headers: Record<string, string> = {\n \"content-length\": String(Buffer.byteLength(body)),\n \"content-type\": \"application/json\",\n };\n for (const [name, value] of headerEntries ?? []) {\n const normalizedName = name.toLowerCase();\n if (SENDER_CONTROLLED_WEBHOOK_HEADERS.has(normalizedName)) {\n throw new Error(`Webhook header \"${name}\" is controlled by the sender.`);\n }\n headers[normalizedName] = value;\n }\n return headers;\n}\n\nasync function sendWebhookRequest(\n params: PostWebhookRequestParams,\n resolveOnHeaders: boolean,\n): Promise<WebhookResponse> {\n const headers = webhookRequestHeaders(params.body, params.headerEntries);\n return await new Promise<WebhookResponse>((resolve, reject) => {\n const address = params.address;\n let settled = false;\n let response: IncomingMessage | undefined;\n let deadline: NodeJS.Timeout;\n const finish = (callback: () => void) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(deadline);\n params.activeRequests?.delete(request);\n callback();\n };\n const send = params.url.protocol === \"https:\" ? requestHttps : requestHttp;\n const request = send(\n params.url,\n {\n agent: false,\n ...(address ? { family: address.family } : {}),\n headers,\n ...(address\n ? {\n lookup: (_hostname, _options, callback) =>\n callback(null, address.address, address.family),\n }\n : {}),\n method: \"POST\",\n signal: params.signal,\n },\n (incoming) => {\n response = incoming;\n const result = {\n headers: incoming.headers,\n status: incoming.statusCode ?? 0,\n };\n if (resolveOnHeaders) {\n finish(() => {\n incoming.destroy();\n resolve(result);\n });\n return;\n }\n incoming.resume();\n incoming.once(\"error\", (error) => finish(() => reject(error)));\n incoming.once(\"end\", () => finish(() => resolve(result)));\n },\n );\n params.activeRequests?.add(request);\n deadline = setTimeout(() => {\n const error = new DOMException(\n `Webhook delivery timed out after ${params.timeoutMs}ms`,\n \"TimeoutError\",\n );\n response?.destroy(error);\n request.destroy(error);\n }, params.timeoutMs);\n deadline.unref();\n request.once(\"error\", (error) => finish(() => reject(error)));\n request.end(params.body);\n });\n}\n\nexport async function postWebhookRequestWithResponse(\n params: PostWebhookRequestParams,\n): Promise<WebhookResponse> {\n return await sendWebhookRequest(params, true);\n}\n\nexport async function postWebhookRequest(params: PostWebhookRequestParams): Promise<number> {\n return (await sendWebhookRequest(params, false)).status;\n}\n"]}