@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.
- package/README.md +137 -23
- package/dist/src/bin/crabline.js +3 -1
- package/dist/src/bin/crabline.js.map +1 -1
- package/dist/src/cli/program.d.ts +5 -1
- package/dist/src/cli/program.js +281 -52
- package/dist/src/cli/program.js.map +1 -1
- package/dist/src/config/load.js +39 -3
- package/dist/src/config/load.js.map +1 -1
- package/dist/src/config/schema.d.ts +20 -0
- package/dist/src/config/schema.js +163 -35
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/core/errors.d.ts +1 -0
- package/dist/src/core/errors.js +6 -3
- package/dist/src/core/errors.js.map +1 -1
- package/dist/src/core/http-path.d.ts +1 -0
- package/dist/src/core/http-path.js +14 -0
- package/dist/src/core/http-path.js.map +1 -0
- package/dist/src/core/matcher.d.ts +3 -1
- package/dist/src/core/matcher.js +58 -2
- package/dist/src/core/matcher.js.map +1 -1
- package/dist/src/core/nonces.d.ts +2 -0
- package/dist/src/core/nonces.js +11 -1
- package/dist/src/core/nonces.js.map +1 -1
- package/dist/src/core/reporters.d.ts +5 -0
- package/dist/src/core/reporters.js +26 -2
- package/dist/src/core/reporters.js.map +1 -1
- package/dist/src/core/run.d.ts +3 -1
- package/dist/src/core/run.js +178 -66
- package/dist/src/core/run.js.map +1 -1
- package/dist/src/index.d.ts +12 -10
- package/dist/src/index.js +6 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/matrix-ids.d.ts +5 -0
- package/dist/src/matrix-ids.js +83 -0
- package/dist/src/matrix-ids.js.map +1 -0
- package/dist/src/openclaw/artifact-generation.d.ts +7 -6
- package/dist/src/openclaw/artifact-generation.js +385 -41
- package/dist/src/openclaw/artifact-generation.js.map +1 -1
- package/dist/src/openclaw/bridges/discord.d.ts +1 -0
- package/dist/src/openclaw/bridges/discord.js +149 -0
- package/dist/src/openclaw/bridges/discord.js.map +1 -0
- package/dist/src/openclaw/bridges/matrix.js +22 -5
- package/dist/src/openclaw/bridges/matrix.js.map +1 -1
- package/dist/src/openclaw/bridges/mattermost.js +14 -9
- package/dist/src/openclaw/bridges/mattermost.js.map +1 -1
- package/dist/src/openclaw/bridges/probe-response.d.ts +1 -0
- package/dist/src/openclaw/bridges/probe-response.js +5 -0
- package/dist/src/openclaw/bridges/probe-response.js.map +1 -0
- package/dist/src/openclaw/bridges/signal.js +16 -7
- package/dist/src/openclaw/bridges/signal.js.map +1 -1
- package/dist/src/openclaw/bridges/slack.js +117 -24
- package/dist/src/openclaw/bridges/slack.js.map +1 -1
- package/dist/src/openclaw/bridges/telegram.js +46 -20
- package/dist/src/openclaw/bridges/telegram.js.map +1 -1
- package/dist/src/openclaw/bridges/whatsapp.js +33 -17
- package/dist/src/openclaw/bridges/whatsapp.js.map +1 -1
- package/dist/src/openclaw/bridges/zalo.js +9 -4
- package/dist/src/openclaw/bridges/zalo.js.map +1 -1
- package/dist/src/openclaw/outbound-contract.js +3 -0
- package/dist/src/openclaw/outbound-contract.js.map +1 -1
- package/dist/src/openclaw/private-file.d.ts +38 -3
- package/dist/src/openclaw/private-file.js +2008 -87
- package/dist/src/openclaw/private-file.js.map +1 -1
- package/dist/src/openclaw/{smoke-lock.d.ts → provider-readiness-lock.d.ts} +14 -5
- package/dist/src/openclaw/provider-readiness-lock.js +1320 -0
- package/dist/src/openclaw/provider-readiness-lock.js.map +1 -0
- package/dist/src/openclaw/shared.d.ts +10 -17
- package/dist/src/openclaw/shared.js +53 -19
- package/dist/src/openclaw/shared.js.map +1 -1
- package/dist/src/openclaw.d.ts +3 -5
- package/dist/src/openclaw.js +245 -60
- package/dist/src/openclaw.js.map +1 -1
- package/dist/src/platform/process-owned-lock.d.ts +11 -0
- package/dist/src/platform/process-owned-lock.js +1698 -0
- package/dist/src/platform/process-owned-lock.js.map +1 -0
- package/dist/src/platform/recorder-directory.d.ts +1 -0
- package/dist/src/platform/recorder-directory.js +14 -0
- package/dist/src/platform/recorder-directory.js.map +1 -0
- package/dist/src/platform/windows-acl.d.ts +18 -0
- package/dist/src/platform/windows-acl.js +1367 -0
- package/dist/src/platform/windows-acl.js.map +1 -0
- package/dist/src/platform/windows-lock-root.d.ts +13 -0
- package/dist/src/platform/windows-lock-root.js +98 -0
- package/dist/src/platform/windows-lock-root.js.map +1 -0
- package/dist/src/providers/builtin/discord.d.ts +4 -0
- package/dist/src/providers/builtin/discord.js +52 -9
- package/dist/src/providers/builtin/discord.js.map +1 -1
- package/dist/src/providers/builtin/external-webhook-auth.d.ts +1 -0
- package/dist/src/providers/builtin/external-webhook-auth.js +38 -1
- package/dist/src/providers/builtin/external-webhook-auth.js.map +1 -1
- package/dist/src/providers/builtin/feishu.d.ts +1 -0
- package/dist/src/providers/builtin/feishu.js +96 -26
- package/dist/src/providers/builtin/feishu.js.map +1 -1
- package/dist/src/providers/builtin/googlechat.d.ts +1 -0
- package/dist/src/providers/builtin/googlechat.js +95 -26
- package/dist/src/providers/builtin/googlechat.js.map +1 -1
- package/dist/src/providers/builtin/imessage.js +60 -17
- package/dist/src/providers/builtin/imessage.js.map +1 -1
- package/dist/src/providers/builtin/loopback.js +88 -35
- package/dist/src/providers/builtin/loopback.js.map +1 -1
- package/dist/src/providers/builtin/matrix.d.ts +2 -2
- package/dist/src/providers/builtin/matrix.js +78 -21
- package/dist/src/providers/builtin/matrix.js.map +1 -1
- package/dist/src/providers/builtin/mattermost.d.ts +11 -7
- package/dist/src/providers/builtin/mattermost.js +138 -24
- package/dist/src/providers/builtin/mattermost.js.map +1 -1
- package/dist/src/providers/builtin/msteams.d.ts +1 -0
- package/dist/src/providers/builtin/msteams.js +118 -38
- package/dist/src/providers/builtin/msteams.js.map +1 -1
- package/dist/src/providers/builtin/native-local-mock.js +4 -4
- package/dist/src/providers/builtin/native-local-mock.js.map +1 -1
- package/dist/src/providers/builtin/script.js +1057 -146
- package/dist/src/providers/builtin/script.js.map +1 -1
- package/dist/src/providers/builtin/slack.d.ts +8 -1
- package/dist/src/providers/builtin/slack.js +327 -24
- package/dist/src/providers/builtin/slack.js.map +1 -1
- package/dist/src/providers/builtin/telegram.d.ts +1 -1
- package/dist/src/providers/builtin/telegram.js +55 -14
- package/dist/src/providers/builtin/telegram.js.map +1 -1
- package/dist/src/providers/builtin/whatsapp.d.ts +4 -3
- package/dist/src/providers/builtin/whatsapp.js +157 -29
- package/dist/src/providers/builtin/whatsapp.js.map +1 -1
- package/dist/src/providers/builtin/zalo.d.ts +5 -2
- package/dist/src/providers/builtin/zalo.js +31 -7
- package/dist/src/providers/builtin/zalo.js.map +1 -1
- package/dist/src/providers/catalog.d.ts +1 -1
- package/dist/src/providers/catalog.js +1 -1
- package/dist/src/providers/catalog.js.map +1 -1
- package/dist/src/providers/local-mock.d.ts +4 -0
- package/dist/src/providers/local-mock.js +162 -35
- package/dist/src/providers/local-mock.js.map +1 -1
- package/dist/src/providers/native-ids.d.ts +2 -0
- package/dist/src/providers/native-ids.js +3 -0
- package/dist/src/providers/native-ids.js.map +1 -1
- package/dist/src/providers/recorder.d.ts +14 -3
- package/dist/src/providers/recorder.js +521 -41
- package/dist/src/providers/recorder.js.map +1 -1
- package/dist/src/providers/registry.d.ts +1 -0
- package/dist/src/providers/registry.js +65 -20
- package/dist/src/providers/registry.js.map +1 -1
- package/dist/src/providers/signed-jwt.d.ts +6 -1
- package/dist/src/providers/signed-jwt.js +241 -50
- package/dist/src/providers/signed-jwt.js.map +1 -1
- package/dist/src/providers/slack-ids.d.ts +1 -0
- package/dist/src/providers/slack-ids.js +5 -0
- package/dist/src/providers/slack-ids.js.map +1 -1
- package/dist/src/providers/target-normalizers.d.ts +9 -1
- package/dist/src/providers/target-normalizers.js +177 -58
- package/dist/src/providers/target-normalizers.js.map +1 -1
- package/dist/src/providers/webhook-server.d.ts +3 -0
- package/dist/src/providers/webhook-server.js +268 -47
- package/dist/src/providers/webhook-server.js.map +1 -1
- package/dist/src/servers/discord.d.ts +40 -0
- package/dist/src/servers/discord.js +842 -0
- package/dist/src/servers/discord.js.map +1 -0
- package/dist/src/servers/http.d.ts +14 -2
- package/dist/src/servers/http.js +229 -58
- package/dist/src/servers/http.js.map +1 -1
- package/dist/src/servers/index.d.ts +10 -4
- package/dist/src/servers/index.js +4 -0
- package/dist/src/servers/index.js.map +1 -1
- package/dist/src/servers/matrix.d.ts +3 -0
- package/dist/src/servers/matrix.js +268 -110
- package/dist/src/servers/matrix.js.map +1 -1
- package/dist/src/servers/mattermost.d.ts +5 -0
- package/dist/src/servers/mattermost.js +389 -91
- package/dist/src/servers/mattermost.js.map +1 -1
- package/dist/src/servers/recorder.d.ts +11 -0
- package/dist/src/servers/recorder.js +1072 -69
- package/dist/src/servers/recorder.js.map +1 -1
- package/dist/src/servers/signal.d.ts +1 -0
- package/dist/src/servers/signal.js +279 -44
- package/dist/src/servers/signal.js.map +1 -1
- package/dist/src/servers/slack.d.ts +15 -0
- package/dist/src/servers/slack.js +270 -54
- package/dist/src/servers/slack.js.map +1 -1
- package/dist/src/servers/telegram-identity.d.ts +6 -0
- package/dist/src/servers/telegram-identity.js +27 -0
- package/dist/src/servers/telegram-identity.js.map +1 -0
- package/dist/src/servers/telegram.d.ts +17 -6
- package/dist/src/servers/telegram.js +1218 -107
- package/dist/src/servers/telegram.js.map +1 -1
- package/dist/src/servers/webhook-target.d.ts +12 -4
- package/dist/src/servers/webhook-target.js +226 -28
- package/dist/src/servers/webhook-target.js.map +1 -1
- package/dist/src/servers/whatsapp-baileys-websocket.d.ts +60 -1
- package/dist/src/servers/whatsapp-baileys-websocket.js +782 -112
- package/dist/src/servers/whatsapp-baileys-websocket.js.map +1 -1
- package/dist/src/servers/whatsapp-jid.d.ts +1 -0
- package/dist/src/servers/whatsapp-jid.js +9 -5
- package/dist/src/servers/whatsapp-jid.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/binary-node.d.ts +1 -0
- package/dist/src/servers/whatsapp-wire/binary-node.js +37 -13
- package/dist/src/servers/whatsapp-wire/binary-node.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/crypto.d.ts +3 -0
- package/dist/src/servers/whatsapp-wire/crypto.js +23 -8
- package/dist/src/servers/whatsapp-wire/crypto.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/handshake.d.ts +9 -3
- package/dist/src/servers/whatsapp-wire/handshake.js +173 -12
- package/dist/src/servers/whatsapp-wire/handshake.js.map +1 -1
- package/dist/src/servers/whatsapp-wire/signal.js +3 -3
- package/dist/src/servers/whatsapp-wire/signal.js.map +1 -1
- package/dist/src/servers/whatsapp.d.ts +7 -1
- package/dist/src/servers/whatsapp.js +149 -25
- package/dist/src/servers/whatsapp.js.map +1 -1
- package/dist/src/servers/zalo.d.ts +1 -0
- package/dist/src/servers/zalo.js +193 -82
- package/dist/src/servers/zalo.js.map +1 -1
- package/docs/channel-setup.md +251 -64
- package/fixtures/examples/crabline.example.yaml +3 -2
- package/package.json +18 -10
- package/dist/src/openclaw/smoke-lock.js +0 -1004
- package/dist/src/openclaw/smoke-lock.js.map +0 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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"]}
|