@antasphere/chassis-server 1.0.2 → 1.2.0

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 (189) hide show
  1. package/dist/api/apikeys.d.ts +4 -1
  2. package/dist/api/apikeys.d.ts.map +1 -1
  3. package/dist/api/apikeys.js +11 -1
  4. package/dist/api/apikeys.js.map +1 -1
  5. package/dist/api/cli-package.d.ts +58 -0
  6. package/dist/api/cli-package.d.ts.map +1 -0
  7. package/dist/api/cli-package.js +83 -0
  8. package/dist/api/cli-package.js.map +1 -0
  9. package/dist/api/create-api.d.ts +3 -0
  10. package/dist/api/create-api.d.ts.map +1 -1
  11. package/dist/api/create-api.js +33 -4
  12. package/dist/api/create-api.js.map +1 -1
  13. package/dist/api/index.d.ts +1 -0
  14. package/dist/api/index.d.ts.map +1 -1
  15. package/dist/api/index.js +1 -0
  16. package/dist/api/index.js.map +1 -1
  17. package/dist/api/invitations.d.ts.map +1 -1
  18. package/dist/api/invitations.js +6 -0
  19. package/dist/api/invitations.js.map +1 -1
  20. package/dist/api/members.d.ts +5 -2
  21. package/dist/api/members.d.ts.map +1 -1
  22. package/dist/api/members.js +1 -0
  23. package/dist/api/members.js.map +1 -1
  24. package/dist/api/projects.d.ts +3 -0
  25. package/dist/api/projects.d.ts.map +1 -1
  26. package/dist/api/projects.js +13 -1
  27. package/dist/api/projects.js.map +1 -1
  28. package/dist/api/webhooks.d.ts +24 -0
  29. package/dist/api/webhooks.d.ts.map +1 -0
  30. package/dist/api/webhooks.js +170 -0
  31. package/dist/api/webhooks.js.map +1 -0
  32. package/dist/boot.d.ts.map +1 -1
  33. package/dist/boot.js +47 -4
  34. package/dist/boot.js.map +1 -1
  35. package/dist/cli-package/index.d.ts +44 -0
  36. package/dist/cli-package/index.d.ts.map +1 -0
  37. package/dist/cli-package/index.js +60 -0
  38. package/dist/cli-package/index.js.map +1 -0
  39. package/dist/email/index.d.ts +1 -0
  40. package/dist/email/index.d.ts.map +1 -1
  41. package/dist/email/index.js +1 -0
  42. package/dist/email/index.js.map +1 -1
  43. package/dist/env.d.ts +11 -0
  44. package/dist/env.d.ts.map +1 -1
  45. package/dist/env.js +24 -0
  46. package/dist/env.js.map +1 -1
  47. package/dist/identity/demo-pass-rules.d.ts.map +1 -1
  48. package/dist/identity/demo-pass-rules.js +6 -1
  49. package/dist/identity/demo-pass-rules.js.map +1 -1
  50. package/dist/identity/hub-reconcile.d.ts +2 -2
  51. package/dist/identity/hub-reconcile.d.ts.map +1 -1
  52. package/dist/identity/hub-reconcile.js.map +1 -1
  53. package/dist/index.d.ts +2 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/jobs/index.d.ts +1 -1
  56. package/dist/jobs/index.d.ts.map +1 -1
  57. package/dist/jobs/index.js.map +1 -1
  58. package/dist/jobs/pgboss.d.ts +34 -0
  59. package/dist/jobs/pgboss.d.ts.map +1 -1
  60. package/dist/jobs/pgboss.js +32 -9
  61. package/dist/jobs/pgboss.js.map +1 -1
  62. package/dist/mcp/index.d.ts +1 -0
  63. package/dist/mcp/index.d.ts.map +1 -1
  64. package/dist/mcp/index.js +1 -0
  65. package/dist/mcp/index.js.map +1 -1
  66. package/dist/mcp/server.d.ts +1 -1
  67. package/dist/mcp/server.d.ts.map +1 -1
  68. package/dist/mcp/server.js +11 -2
  69. package/dist/mcp/server.js.map +1 -1
  70. package/dist/mcp/tool-kit.d.ts +2 -0
  71. package/dist/mcp/tool-kit.d.ts.map +1 -1
  72. package/dist/mcp/tool-kit.js +5 -1
  73. package/dist/mcp/tool-kit.js.map +1 -1
  74. package/dist/mcp/webhooks.d.ts +16 -0
  75. package/dist/mcp/webhooks.d.ts.map +1 -0
  76. package/dist/mcp/webhooks.js +226 -0
  77. package/dist/mcp/webhooks.js.map +1 -0
  78. package/dist/members/removal.d.ts +10 -2
  79. package/dist/members/removal.d.ts.map +1 -1
  80. package/dist/members/removal.js +3 -0
  81. package/dist/members/removal.js.map +1 -1
  82. package/dist/middleware/idempotency.d.ts.map +1 -1
  83. package/dist/middleware/idempotency.js +3 -1
  84. package/dist/middleware/idempotency.js.map +1 -1
  85. package/dist/middleware/scopes.d.ts +9 -1
  86. package/dist/middleware/scopes.d.ts.map +1 -1
  87. package/dist/middleware/scopes.js +8 -1
  88. package/dist/middleware/scopes.js.map +1 -1
  89. package/dist/secret-box.d.ts +10 -0
  90. package/dist/secret-box.d.ts.map +1 -0
  91. package/dist/secret-box.js +87 -0
  92. package/dist/secret-box.js.map +1 -0
  93. package/dist/testing/helpers.d.ts +8 -0
  94. package/dist/testing/helpers.d.ts.map +1 -1
  95. package/dist/testing/helpers.js.map +1 -1
  96. package/dist/tool-definition.d.ts +24 -0
  97. package/dist/tool-definition.d.ts.map +1 -1
  98. package/dist/tool-definition.js.map +1 -1
  99. package/dist/webhooks/catalog.d.ts +80 -0
  100. package/dist/webhooks/catalog.d.ts.map +1 -0
  101. package/dist/webhooks/catalog.js +134 -0
  102. package/dist/webhooks/catalog.js.map +1 -0
  103. package/dist/webhooks/index.d.ts +9 -0
  104. package/dist/webhooks/index.d.ts.map +1 -0
  105. package/dist/webhooks/index.js +9 -0
  106. package/dist/webhooks/index.js.map +1 -0
  107. package/dist/webhooks/jobs.d.ts +12 -0
  108. package/dist/webhooks/jobs.d.ts.map +1 -0
  109. package/dist/webhooks/jobs.js +77 -0
  110. package/dist/webhooks/jobs.js.map +1 -0
  111. package/dist/webhooks/mail.d.ts +24 -0
  112. package/dist/webhooks/mail.d.ts.map +1 -0
  113. package/dist/webhooks/mail.js +21 -0
  114. package/dist/webhooks/mail.js.map +1 -0
  115. package/dist/webhooks/match.d.ts +22 -0
  116. package/dist/webhooks/match.d.ts.map +1 -0
  117. package/dist/webhooks/match.js +40 -0
  118. package/dist/webhooks/match.js.map +1 -0
  119. package/dist/webhooks/safe-fetch.d.ts +94 -0
  120. package/dist/webhooks/safe-fetch.d.ts.map +1 -0
  121. package/dist/webhooks/safe-fetch.js +263 -0
  122. package/dist/webhooks/safe-fetch.js.map +1 -0
  123. package/dist/webhooks/service.d.ts +245 -0
  124. package/dist/webhooks/service.d.ts.map +1 -0
  125. package/dist/webhooks/service.js +861 -0
  126. package/dist/webhooks/service.js.map +1 -0
  127. package/dist/webhooks/signing.d.ts +40 -0
  128. package/dist/webhooks/signing.d.ts.map +1 -0
  129. package/dist/webhooks/signing.js +108 -0
  130. package/dist/webhooks/signing.js.map +1 -0
  131. package/dist/webhooks/tx-send.d.ts +31 -0
  132. package/dist/webhooks/tx-send.d.ts.map +1 -0
  133. package/dist/webhooks/tx-send.js +22 -0
  134. package/dist/webhooks/tx-send.js.map +1 -0
  135. package/package.json +11 -3
  136. package/src/api/apikeys.ts +12 -1
  137. package/src/api/cli-package.ts +151 -0
  138. package/src/api/create-api.ts +42 -4
  139. package/src/api/index.ts +1 -0
  140. package/src/api/invitations.ts +6 -0
  141. package/src/api/members.ts +6 -2
  142. package/src/api/projects.ts +16 -1
  143. package/src/api/webhooks.ts +205 -0
  144. package/src/boot.ts +48 -4
  145. package/src/cli-package/index.ts +88 -0
  146. package/src/email/index.ts +1 -0
  147. package/src/env.ts +25 -0
  148. package/src/identity/demo-pass-rules.ts +6 -1
  149. package/src/identity/hub-reconcile.ts +2 -2
  150. package/src/index.ts +13 -1
  151. package/src/jobs/index.ts +1 -0
  152. package/src/jobs/pgboss.ts +71 -9
  153. package/src/mcp/index.ts +1 -0
  154. package/src/mcp/server.ts +10 -2
  155. package/src/mcp/tool-kit.ts +7 -1
  156. package/src/mcp/webhooks.ts +370 -0
  157. package/src/members/removal.ts +21 -1
  158. package/src/middleware/idempotency.ts +3 -1
  159. package/src/middleware/scopes.ts +14 -0
  160. package/src/secret-box.ts +93 -0
  161. package/src/testing/helpers.ts +8 -0
  162. package/src/tool-definition.ts +29 -0
  163. package/src/webhooks/catalog.ts +205 -0
  164. package/src/webhooks/index.ts +58 -0
  165. package/src/webhooks/jobs.ts +92 -0
  166. package/src/webhooks/mail.ts +48 -0
  167. package/src/webhooks/match.ts +52 -0
  168. package/src/webhooks/safe-fetch.ts +335 -0
  169. package/src/webhooks/service.ts +1089 -0
  170. package/src/webhooks/signing.ts +132 -0
  171. package/src/webhooks/tx-send.ts +53 -0
  172. package/test/host/drizzle/0002_webhooks.sql +49 -0
  173. package/test/host/drizzle/meta/0002_snapshot.json +3952 -0
  174. package/test/host/drizzle/meta/_journal.json +8 -1
  175. package/test/host/identity.ts +6 -1
  176. package/test/host/minimal-host.ts +2 -1
  177. package/test/host/minimal-tool.ts +32 -4
  178. package/test/integration/cli-package.test.ts +403 -0
  179. package/test/integration/mcp-webhooks.test.ts +360 -0
  180. package/test/integration/webhooks.test.ts +1684 -0
  181. package/test/unit/cli-package.test.ts +93 -0
  182. package/test/unit/secret-box.test.ts +124 -0
  183. package/test/unit/tx-send.test.ts +23 -0
  184. package/test/unit/webhook-catalog.test.ts +151 -0
  185. package/test/unit/webhook-env.test.ts +60 -0
  186. package/test/unit/webhook-jobs.test.ts +92 -0
  187. package/test/unit/webhook-match.test.ts +81 -0
  188. package/test/unit/webhook-safe-fetch.test.ts +330 -0
  189. package/test/unit/webhook-signing.test.ts +169 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The subscription rule, pure and unit-tested (`webhook-match.test.ts`): an
3
+ * endpoint's `events` are matched against an event's type, its `filters`
4
+ * against the event's subjects. The chassis never knows what a subject IS,
5
+ * only that it is a key with one or several ids.
6
+ */
7
+ /** `*` matches every type; `family.*` matches the family; a type matches itself. */
8
+ export function matchesEventType(patterns, type) {
9
+ const family = type.slice(0, type.indexOf('.'));
10
+ for (const pattern of patterns) {
11
+ if (pattern === '*' || pattern === type)
12
+ return true;
13
+ if (pattern.endsWith('.*') && pattern.slice(0, -2) === family)
14
+ return true;
15
+ }
16
+ return false;
17
+ }
18
+ /**
19
+ * AND across the filter's keys, OR within a key's ids. An event that does not
20
+ * carry a filtered subject does not match (a filter on `project` excludes an
21
+ * event with no project). An empty filter matches everything.
22
+ */
23
+ export function matchesFilters(filters, subjects) {
24
+ for (const [key, ids] of Object.entries(filters)) {
25
+ if (ids.length === 0)
26
+ continue;
27
+ const value = subjects[key];
28
+ if (value === undefined)
29
+ return false;
30
+ const values = typeof value === 'string' ? [value] : value;
31
+ if (!values.some((v) => ids.includes(v)))
32
+ return false;
33
+ }
34
+ return true;
35
+ }
36
+ /** Whether an endpoint takes this event: enabled, the type subscribed, the filters satisfied. */
37
+ export function endpointWants(endpoint, type, subjects) {
38
+ return (endpoint.enabled && matchesEventType(endpoint.events, type) && matchesFilters(endpoint.filters, subjects));
39
+ }
40
+ //# sourceMappingURL=match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match.js","sourceRoot":"","sources":["../../src/webhooks/match.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAAC,QAA2B,EAAE,IAAY;IACxE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACrD,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;IAC7E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAoD,EACpD,QAAuD;IAEvD,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACjD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IACzD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,aAAa,CAC3B,QAIC,EACD,IAAY,EACZ,QAAuD;IAEvD,OAAO,CACL,QAAQ,CAAC,OAAO,IAAI,gBAAgB,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,cAAc,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAC1G,CAAC;AACJ,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { type Readable } from 'node:stream';
2
+ /**
3
+ * The guarded outbound POST the webhook delivery worker uses. A webhook's
4
+ * address is somebody else's choice, so the one rule that is not negotiable:
5
+ * a POST never reaches the instance's own network unless the operator opened
6
+ * it (`allowPrivateNetworks`, a self-hoster's LAN).
7
+ *
8
+ * - Up front, before any request (`checkWebhookUrl`): `http:`/`https:` only
9
+ * (`https:` alone under `httpsOnly`), the scheme's default port only, no
10
+ * userinfo, a hostname, and a literal IP host must be public.
11
+ * - A name is resolved with ALL its addresses, and the connection is refused
12
+ * (`WebhookUrlRefusedError`) when ANY of them is not public. The check is
13
+ * the socket's own `lookup`, so the address connected to IS the one
14
+ * checked: no gap for a name that answers differently the second time.
15
+ * - No redirect is ever followed: a 3xx comes back as its status.
16
+ * - One timeout over the whole call (resolution, connection, the body read).
17
+ * - The response body is read up to `maxResponseBytes`, then the stream is
18
+ * destroyed.
19
+ */
20
+ export type WebhookUrlRefusal = 'scheme' | 'port' | 'userinfo' | 'private_address' | 'https_required' | 'unresolvable' | 'malformed';
21
+ /** A webhook URL the guard refused (a scheme, a port, userinfo, an address). */
22
+ export declare class WebhookUrlRefusedError extends Error {
23
+ readonly code: WebhookUrlRefusal;
24
+ constructor(code: WebhookUrlRefusal, message?: string);
25
+ }
26
+ export interface SafeFetchPolicy {
27
+ /** Private, loopback, link-local and other non-public addresses are reachable (a self-hoster's LAN). Default false. */
28
+ allowPrivateNetworks: boolean;
29
+ /** Only https: URLs are accepted (the cloud edition). Default false. */
30
+ httpsOnly: boolean;
31
+ }
32
+ export interface LookupAddress {
33
+ address: string;
34
+ family: number;
35
+ }
36
+ /** Every address a name resolves to (`dns.lookup(host, { all: true })` by default). */
37
+ export type SafeLookup = (hostname: string) => Promise<LookupAddress[]>;
38
+ export interface SafeFetchResponse {
39
+ status: number;
40
+ headers: Record<string, string | string[] | undefined>;
41
+ body: Readable;
42
+ }
43
+ /**
44
+ * One POST, no redirect followed. The default speaks `node:http`/`node:https`
45
+ * with the guarded lookup in the socket; a test hands its own, and the
46
+ * address check then runs before it.
47
+ */
48
+ export type SafeTransport = (url: URL, init: {
49
+ method: 'POST';
50
+ headers: Record<string, string>;
51
+ body: string;
52
+ }, signal: AbortSignal) => Promise<SafeFetchResponse>;
53
+ export interface SafeFetchOptions {
54
+ lookup?: SafeLookup;
55
+ transport?: SafeTransport;
56
+ /** Over the whole call. Default 10 000. */
57
+ timeoutMs?: number;
58
+ /** The response body is cut there. Default 65 536. */
59
+ maxResponseBytes?: number;
60
+ }
61
+ export interface SafeFetchResult {
62
+ status: number;
63
+ /** the response body, utf8, cut at maxResponseBytes */
64
+ bodyText: string;
65
+ durationMs: number;
66
+ }
67
+ /**
68
+ * Whether an address is on the public internet: false for loopback, private,
69
+ * link-local (the metadata address), carrier-grade NAT, multicast, reserved,
70
+ * and every IPv6 outside global unicast (`2000::/3`): `::`, `::1`, `fc00::/7`,
71
+ * `fe80::/10`, `ff00::/8`. An IPv4-mapped (`::ffff:a.b.c.d`) or a 6to4
72
+ * (`2002::/16`) address is judged by the IPv4 inside it. Unparseable: false.
73
+ */
74
+ export declare function isPublicAddress(ip: string): boolean;
75
+ /**
76
+ * The checks that need no lookup: `http:`/`https:` only (`https:` alone under
77
+ * `httpsOnly`), the scheme's default port only (`url.port === ''`, so an
78
+ * explicit default port, which the URL parser drops, passes), no userinfo, a
79
+ * non-empty hostname, a literal IP public unless `allowPrivateNetworks`.
80
+ * Throws `WebhookUrlRefusedError`; returns the parsed URL.
81
+ */
82
+ export declare function checkWebhookUrl(url: string, policy: SafeFetchPolicy): URL;
83
+ /**
84
+ * One POST, no redirect followed (a 3xx comes back as its status), the address
85
+ * check inside the socket's own lookup (ALL resolved addresses must be public
86
+ * unless `allowPrivateNetworks`; an empty resolution is `unresolvable`),
87
+ * `AbortSignal.timeout(timeoutMs)` over the whole call, the response body read
88
+ * up to `maxResponseBytes` then the stream destroyed. Rejects with
89
+ * `WebhookUrlRefusedError` on a refused address, with the transport's error
90
+ * (ECONNREFUSED, the timeout's AbortError…) otherwise. Sets no `user-agent`:
91
+ * that is the caller's.
92
+ */
93
+ export declare function safePost(url: string, headers: Record<string, string>, body: string, policy: SafeFetchPolicy, options?: SafeFetchOptions): Promise<SafeFetchResult>;
94
+ //# sourceMappingURL=safe-fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-fetch.d.ts","sourceRoot":"","sources":["../../src/webhooks/safe-fetch.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5D;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,iBAAiB,GAC3B,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,cAAc,GAAG,WAAW,CAAC;AAYvG,gFAAgF;AAChF,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;gBACrB,IAAI,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,MAAM;CAKtD;AAED,MAAM,WAAW,eAAe;IAC9B,uHAAuH;IACvH,oBAAoB,EAAE,OAAO,CAAC;IAC9B,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,uFAAuF;AACvF,MAAM,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,IAAI,EAAE,QAAQ,CAAC;CAChB;AACD;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,GAAG,EACR,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACvE,MAAM,EAAE,WAAW,KAChB,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEhC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAkDD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAanD;AAOD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,GAAG,CAoBzE;AAuFD;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAmC1B"}
@@ -0,0 +1,263 @@
1
+ import { promises as dns } from 'node:dns';
2
+ import http from 'node:http';
3
+ import https from 'node:https';
4
+ import { isIP } from 'node:net';
5
+ import { addAbortSignal } from 'node:stream';
6
+ const REFUSAL_MESSAGES = {
7
+ scheme: 'the webhook URL must use http: or https:',
8
+ port: "the webhook URL must use its scheme's default port",
9
+ userinfo: 'the webhook URL must not carry a user name or a password',
10
+ private_address: 'the webhook URL resolves to an address that is not on the public internet',
11
+ https_required: 'the webhook URL must use https:',
12
+ unresolvable: 'the webhook URL names a host that resolves to no address',
13
+ malformed: 'the webhook URL is not a valid URL'
14
+ };
15
+ /** A webhook URL the guard refused (a scheme, a port, userinfo, an address). */
16
+ export class WebhookUrlRefusedError extends Error {
17
+ code;
18
+ constructor(code, message) {
19
+ super(message ?? REFUSAL_MESSAGES[code]);
20
+ this.name = 'WebhookUrlRefusedError';
21
+ this.code = code;
22
+ }
23
+ }
24
+ const DEFAULT_TIMEOUT_MS = 10_000;
25
+ const DEFAULT_MAX_RESPONSE_BYTES = 65_536;
26
+ // ── The address check ────────────────────────────────────────────────────────
27
+ function ipv4(ip) {
28
+ const parts = ip.split('.');
29
+ if (parts.length !== 4)
30
+ return null;
31
+ const bytes = parts.map((p) => (/^\d{1,3}$/.test(p) ? Number(p) : NaN));
32
+ return bytes.every((b) => b >= 0 && b <= 255) ? bytes : null;
33
+ }
34
+ function isPublicV4(b) {
35
+ const [a, c] = [b[0], b[1]];
36
+ if (a === 0 || a === 10 || a === 127)
37
+ return false; // this network, private, loopback
38
+ if (a === 100 && c >= 64 && c <= 127)
39
+ return false; // carrier-grade NAT
40
+ if (a === 169 && c === 254)
41
+ return false; // link-local, the cloud metadata address
42
+ if (a === 172 && c >= 16 && c <= 31)
43
+ return false; // private
44
+ if (a === 192 && c === 0 && b[2] === 0)
45
+ return false; // IETF protocol assignments
46
+ if (a === 192 && c === 0 && b[2] === 2)
47
+ return false; // documentation
48
+ if (a === 192 && c === 168)
49
+ return false; // private
50
+ if (a === 198 && (c === 18 || c === 19))
51
+ return false; // benchmarking
52
+ if (a === 198 && c === 51 && b[2] === 100)
53
+ return false; // documentation
54
+ if (a === 203 && c === 0 && b[2] === 113)
55
+ return false; // documentation
56
+ return a < 224; // multicast, reserved, broadcast
57
+ }
58
+ /** The eight 16-bit groups of an IPv6 address (a trailing dotted IPv4 folded in), or null. */
59
+ function ipv6(ip) {
60
+ let s = ip.toLowerCase().replace(/%.*$/, '');
61
+ const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(s);
62
+ if (dotted) {
63
+ const v4 = ipv4(dotted[1]);
64
+ if (!v4)
65
+ return null;
66
+ s = `${s.slice(0, -dotted[1].length)}${((v4[0] << 8) | v4[1]).toString(16)}:${((v4[2] << 8) | v4[3]).toString(16)}`;
67
+ }
68
+ const halves = s.split('::');
69
+ if (halves.length > 2)
70
+ return null;
71
+ const parse = (h) => (h === '' ? [] : h.split(':'));
72
+ const head = parse(halves[0]);
73
+ const tail = halves.length === 2 ? parse(halves[1]) : [];
74
+ const fill = 8 - head.length - tail.length;
75
+ if (halves.length === 2 ? fill < 1 : fill !== 0)
76
+ return null;
77
+ const groups = [...head, ...Array(halves.length === 2 ? fill : 0).fill('0'), ...tail];
78
+ if (!groups.every((g) => /^[0-9a-f]{1,4}$/.test(g)))
79
+ return null;
80
+ return groups.map((g) => Number.parseInt(g, 16));
81
+ }
82
+ /**
83
+ * Whether an address is on the public internet: false for loopback, private,
84
+ * link-local (the metadata address), carrier-grade NAT, multicast, reserved,
85
+ * and every IPv6 outside global unicast (`2000::/3`): `::`, `::1`, `fc00::/7`,
86
+ * `fe80::/10`, `ff00::/8`. An IPv4-mapped (`::ffff:a.b.c.d`) or a 6to4
87
+ * (`2002::/16`) address is judged by the IPv4 inside it. Unparseable: false.
88
+ */
89
+ export function isPublicAddress(ip) {
90
+ if (isIP(ip) === 4) {
91
+ const b = ipv4(ip);
92
+ return b !== null && isPublicV4(b);
93
+ }
94
+ const g = ipv6(ip);
95
+ if (!g)
96
+ return false;
97
+ const embedded = (hi, lo) => isPublicV4([hi >> 8, hi & 255, lo >> 8, lo & 255]);
98
+ if (g.slice(0, 5).every((x) => x === 0) && g[5] === 0xffff)
99
+ return embedded(g[6], g[7]);
100
+ if ((g[0] & 0xe000) !== 0x2000)
101
+ return false; // not global unicast
102
+ if (g[0] === 0x2002)
103
+ return embedded(g[1], g[2]); // 6to4
104
+ if (g[0] === 0x2001 && (g[1] === 0 || g[1] === 0xdb8))
105
+ return false; // Teredo, documentation
106
+ return true;
107
+ }
108
+ /** The URL's host without the brackets of an IPv6 literal. */
109
+ function bareHost(hostname) {
110
+ return hostname.replace(/^\[|\]$/g, '');
111
+ }
112
+ /**
113
+ * The checks that need no lookup: `http:`/`https:` only (`https:` alone under
114
+ * `httpsOnly`), the scheme's default port only (`url.port === ''`, so an
115
+ * explicit default port, which the URL parser drops, passes), no userinfo, a
116
+ * non-empty hostname, a literal IP public unless `allowPrivateNetworks`.
117
+ * Throws `WebhookUrlRefusedError`; returns the parsed URL.
118
+ */
119
+ export function checkWebhookUrl(url, policy) {
120
+ let parsed;
121
+ try {
122
+ parsed = new URL(url);
123
+ }
124
+ catch {
125
+ throw new WebhookUrlRefusedError('malformed');
126
+ }
127
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
128
+ throw new WebhookUrlRefusedError('scheme');
129
+ if (policy.httpsOnly && parsed.protocol !== 'https:')
130
+ throw new WebhookUrlRefusedError('https_required');
131
+ if (parsed.username || parsed.password)
132
+ throw new WebhookUrlRefusedError('userinfo');
133
+ // The scheme's default port only on the public internet; an operator who
134
+ // opened private networks addresses services on their own ports (an n8n on
135
+ // 5678), so the port rule lifts with the address rule.
136
+ if (parsed.port !== '' && !policy.allowPrivateNetworks)
137
+ throw new WebhookUrlRefusedError('port');
138
+ const host = bareHost(parsed.hostname);
139
+ if (host === '')
140
+ throw new WebhookUrlRefusedError('malformed');
141
+ if (isIP(host) && !policy.allowPrivateNetworks && !isPublicAddress(host)) {
142
+ throw new WebhookUrlRefusedError('private_address');
143
+ }
144
+ return parsed;
145
+ }
146
+ /**
147
+ * The address to connect to, once EVERY address the name resolves to is
148
+ * public (or the policy opens private networks); an empty resolution is
149
+ * `unresolvable`. A failing resolver's own error passes through.
150
+ */
151
+ async function checkedAddress(hostname, policy, lookup) {
152
+ const host = bareHost(hostname);
153
+ const family = isIP(host);
154
+ if (family) {
155
+ if (!policy.allowPrivateNetworks && !isPublicAddress(host)) {
156
+ throw new WebhookUrlRefusedError('private_address');
157
+ }
158
+ return { address: host, family };
159
+ }
160
+ const all = await lookup(host);
161
+ if (all.length === 0)
162
+ throw new WebhookUrlRefusedError('unresolvable');
163
+ if (!policy.allowPrivateNetworks && !all.every((a) => isPublicAddress(a.address))) {
164
+ throw new WebhookUrlRefusedError('private_address');
165
+ }
166
+ return all[0];
167
+ }
168
+ /** The promise, or the signal's reason once it aborts first. */
169
+ function abortable(promise, signal) {
170
+ if (signal.aborted)
171
+ return Promise.reject(signal.reason);
172
+ return new Promise((resolve, reject) => {
173
+ const onAbort = () => reject(signal.reason);
174
+ signal.addEventListener('abort', onAbort, { once: true });
175
+ promise.then((v) => {
176
+ signal.removeEventListener('abort', onAbort);
177
+ resolve(v);
178
+ }, (e) => {
179
+ signal.removeEventListener('abort', onAbort);
180
+ reject(e);
181
+ });
182
+ });
183
+ }
184
+ /** At most `max` bytes of the body; the rest is never read. */
185
+ async function readCapped(body, max) {
186
+ const chunks = [];
187
+ let size = 0;
188
+ for await (const chunk of body) {
189
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
190
+ if (size + buf.length >= max) {
191
+ chunks.push(buf.subarray(0, max - size));
192
+ break;
193
+ }
194
+ chunks.push(buf);
195
+ size += buf.length;
196
+ }
197
+ return Buffer.concat(chunks);
198
+ }
199
+ function nodeTransport(policy, resolve) {
200
+ return (url, init, signal) => {
201
+ const lookup = (hostname, options, callback) => {
202
+ checkedAddress(hostname, policy, resolve).then((first) => {
203
+ if (options.all)
204
+ callback(null, [first]);
205
+ else
206
+ callback(null, first.address, first.family);
207
+ }, (e) => callback(e, '', 0));
208
+ };
209
+ const request = url.protocol === 'https:' ? https.request : http.request;
210
+ return new Promise((done, fail) => {
211
+ const req = request(url, { method: init.method, headers: init.headers, lookup, signal, agent: false }, (res) => done({ status: res.statusCode ?? 0, headers: res.headers, body: res }));
212
+ req.on('error', fail);
213
+ req.end(init.body);
214
+ });
215
+ };
216
+ }
217
+ /**
218
+ * One POST, no redirect followed (a 3xx comes back as its status), the address
219
+ * check inside the socket's own lookup (ALL resolved addresses must be public
220
+ * unless `allowPrivateNetworks`; an empty resolution is `unresolvable`),
221
+ * `AbortSignal.timeout(timeoutMs)` over the whole call, the response body read
222
+ * up to `maxResponseBytes` then the stream destroyed. Rejects with
223
+ * `WebhookUrlRefusedError` on a refused address, with the transport's error
224
+ * (ECONNREFUSED, the timeout's AbortError…) otherwise. Sets no `user-agent`:
225
+ * that is the caller's.
226
+ */
227
+ export async function safePost(url, headers, body, policy, options = {}) {
228
+ const started = performance.now();
229
+ const target = checkWebhookUrl(url, policy);
230
+ const lookup = options.lookup ?? ((host) => dns.lookup(host, { all: true }));
231
+ const signal = AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
232
+ const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
233
+ const sent = {};
234
+ for (const [name, value] of Object.entries(headers)) {
235
+ if (name.toLowerCase() !== 'content-length')
236
+ sent[name] = value;
237
+ }
238
+ sent['content-length'] = String(Buffer.byteLength(body, 'utf8'));
239
+ const init = { method: 'POST', headers: sent, body };
240
+ let transport;
241
+ if (options.transport) {
242
+ // The test seam: the same check the socket's lookup makes, made first.
243
+ await abortable(checkedAddress(target.hostname, policy, lookup), signal);
244
+ transport = options.transport;
245
+ }
246
+ else {
247
+ transport = nodeTransport(policy, lookup);
248
+ }
249
+ const res = await abortable(transport(target, init, signal), signal);
250
+ try {
251
+ addAbortSignal(signal, res.body);
252
+ const bytes = await readCapped(res.body, maxBytes);
253
+ return {
254
+ status: res.status,
255
+ bodyText: bytes.toString('utf8'),
256
+ durationMs: Math.round(performance.now() - started)
257
+ };
258
+ }
259
+ finally {
260
+ res.body.destroy();
261
+ }
262
+ }
263
+ //# sourceMappingURL=safe-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-fetch.js","sourceRoot":"","sources":["../../src/webhooks/safe-fetch.ts"],"names":[],"mappings":"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"}