patchwork-os 1.2.0-beta.3.canary.875 → 1.2.0-beta.3.canary.876

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.
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Lexical private-range checks for hostnames and IP literals — pure string
3
+ * work, no DNS and no network.
4
+ *
5
+ * Split out of `ssrfGuard.ts` (2026-10-02) so code that only needs the range
6
+ * decision does not inherit ssrfGuard's I/O dependencies. The trigger: the
7
+ * dashboard's push-endpoint validator imports `isPrivateHost`, and once
8
+ * ssrfGuard imported `undici` (to pin connections) the dashboard package —
9
+ * which does not depend on undici — failed to typecheck. `ssrfGuard.ts`
10
+ * re-exports everything here, so every existing importer is unchanged.
11
+ */
12
+ /**
13
+ * Block requests to private/loopback addresses. Lexical-only check.
14
+ *
15
+ * Mirrors the predicate previously inlined in `tools/httpClient.ts`. Updates
16
+ * here MUST stay in sync with the test fixtures in
17
+ * `src/tools/__tests__/httpClient.test.ts`.
18
+ */
19
+ export declare function isPrivateHost(hostname: string): boolean;
20
+ /**
21
+ * Loopback-only check (127.0.0.0/8, ::1, localhost). Lexical-only.
22
+ *
23
+ * Used by `isPrivateNonLoopbackHost` and by automation webhook fan-out, which
24
+ * intentionally ALLOWS loopback (sidecars) but blocks every other private
25
+ * range (RFC 1918, link-local, ULA, IMDS, 6to4-wrapped private, etc.).
26
+ */
27
+ export declare function isLoopbackHost(hostname: string): boolean;
28
+ /**
29
+ * Private host MINUS loopback. Lexical-only.
30
+ *
31
+ * Use when a sink intentionally allows loopback (e.g. webhook fan-out to local
32
+ * sidecars) but must still block RFC 1918, link-local, IMDS, ULA, 6to4-wrapped
33
+ * private, and IPv4-mapped/translated private addresses.
34
+ */
35
+ export declare function isPrivateNonLoopbackHost(hostname: string): boolean;
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Lexical private-range checks for hostnames and IP literals — pure string
3
+ * work, no DNS and no network.
4
+ *
5
+ * Split out of `ssrfGuard.ts` (2026-10-02) so code that only needs the range
6
+ * decision does not inherit ssrfGuard's I/O dependencies. The trigger: the
7
+ * dashboard's push-endpoint validator imports `isPrivateHost`, and once
8
+ * ssrfGuard imported `undici` (to pin connections) the dashboard package —
9
+ * which does not depend on undici — failed to typecheck. `ssrfGuard.ts`
10
+ * re-exports everything here, so every existing importer is unchanged.
11
+ */
12
+ /**
13
+ * Convert the hex-compressed form that `new URL()` may return for an
14
+ * IPv4-mapped/translated IPv6 address back to dotted-decimal so the existing
15
+ * `isPrivateHost` checks apply.
16
+ *
17
+ * Handles:
18
+ * "xxxx:yyyy" — two 16-bit groups (up to 4 hex digits each, no leading zeros)
19
+ * "xxxxxxxx" — a single 32-bit group
20
+ *
21
+ * Returns null when the input is not recognisable as 32-bit hex-IPv4.
22
+ */
23
+ function hexIpv4ToDotted(s) {
24
+ const colon = s.indexOf(":");
25
+ if (colon !== -1) {
26
+ const hi = s.slice(0, colon);
27
+ const lo = s.slice(colon + 1);
28
+ if (!/^[0-9a-f]{1,4}$/i.test(hi) || !/^[0-9a-f]{1,4}$/i.test(lo))
29
+ return null;
30
+ const hiN = parseInt(hi, 16);
31
+ const loN = parseInt(lo, 16);
32
+ return `${(hiN >>> 8) & 0xff}.${hiN & 0xff}.${(loN >>> 8) & 0xff}.${loN & 0xff}`;
33
+ }
34
+ if (!/^[0-9a-f]{1,8}$/i.test(s))
35
+ return null;
36
+ const n = parseInt(s, 16);
37
+ return `${(n >>> 24) & 0xff}.${(n >>> 16) & 0xff}.${(n >>> 8) & 0xff}.${n & 0xff}`;
38
+ }
39
+ /**
40
+ * inet_aton-style loose IPv4 parse: 1-4 parts, each decimal, octal (leading
41
+ * 0) or hex (0x). Returns canonical dotted-quad or null when the string is
42
+ * not an all-numeric IPv4 form. `new URL()` already canonicalises these, but
43
+ * callers that hand `isPrivateHost` a raw string (a DNS answer, a Location
44
+ * header hostname) must not be bypassable by `0x7f000001`, `2130706433`,
45
+ * `0177.0.0.1` or `127.1`.
46
+ */
47
+ function looseIpv4ToDotted(host) {
48
+ if (!/^[0-9a-fx.]+$/i.test(host) ||
49
+ host.startsWith(".") ||
50
+ host.endsWith("."))
51
+ return null;
52
+ const parts = host.split(".");
53
+ if (parts.length < 1 || parts.length > 4)
54
+ return null;
55
+ const nums = [];
56
+ for (const part of parts) {
57
+ let n;
58
+ if (/^0x[0-9a-f]+$/i.test(part))
59
+ n = parseInt(part.slice(2), 16);
60
+ else if (/^0[0-7]+$/.test(part))
61
+ n = parseInt(part, 8);
62
+ else if (/^\d+$/.test(part))
63
+ n = parseInt(part, 10);
64
+ else
65
+ return null;
66
+ if (!Number.isFinite(n))
67
+ return null;
68
+ nums.push(n);
69
+ }
70
+ // Last part fills the remaining bytes (inet_aton semantics).
71
+ const last = nums[nums.length - 1];
72
+ const remainingBytes = 4 - (nums.length - 1);
73
+ if (last >= 2 ** (8 * remainingBytes))
74
+ return null;
75
+ for (let i = 0; i < nums.length - 1; i++) {
76
+ if (nums[i] > 255)
77
+ return null;
78
+ }
79
+ let value = 0;
80
+ for (let i = 0; i < nums.length - 1; i++) {
81
+ value = value * 256 + nums[i];
82
+ }
83
+ value = value * 2 ** (8 * remainingBytes) + last;
84
+ return `${(value >>> 24) & 0xff}.${(value >>> 16) & 0xff}.${(value >>> 8) & 0xff}.${value & 0xff}`;
85
+ }
86
+ /**
87
+ * Block requests to private/loopback addresses. Lexical-only check.
88
+ *
89
+ * Mirrors the predicate previously inlined in `tools/httpClient.ts`. Updates
90
+ * here MUST stay in sync with the test fixtures in
91
+ * `src/tools/__tests__/httpClient.test.ts`.
92
+ */
93
+ export function isPrivateHost(hostname) {
94
+ const host = hostname.startsWith("[") && hostname.endsWith("]")
95
+ ? hostname.slice(1, -1).toLowerCase()
96
+ : hostname.toLowerCase();
97
+ if (host === "localhost" || host.endsWith(".localhost"))
98
+ return true;
99
+ if (host === "0.0.0.0")
100
+ return true;
101
+ // Reject non-decimal IPv4 notations (hex/octal) that bypass the dotted-quad
102
+ // regex below. Node's URL parser may normalize them on some platforms.
103
+ if (/^0x[0-9a-f]+$/i.test(host) || /^0[0-7]{7,}$/.test(host))
104
+ return true;
105
+ // Unusual IPv4 notations (decimal integer, short-form "127.1", zero-padded
106
+ // octal, per-part hex): canonicalise and re-check the dotted form.
107
+ const canonicalIpv4 = /^\d{1,3}(\.\d{1,3}){3}$/.test(host);
108
+ if (!canonicalIpv4) {
109
+ const dotted = looseIpv4ToDotted(host);
110
+ if (dotted !== null)
111
+ return isPrivateHost(dotted);
112
+ }
113
+ // IPv4 range checks
114
+ const ipv4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
115
+ if (ipv4) {
116
+ const a = Number(ipv4[1]);
117
+ const b = Number(ipv4[2]);
118
+ if (a === 127)
119
+ return true; // 127.0.0.0/8 loopback
120
+ if (a === 10)
121
+ return true; // 10.0.0.0/8 RFC 1918 private
122
+ if (a === 172 && b >= 16 && b <= 31)
123
+ return true; // 172.16.0.0/12 RFC 1918 private
124
+ if (a === 192 && b === 168)
125
+ return true; // 192.168.0.0/16 RFC 1918 private
126
+ if (a === 169 && b === 254)
127
+ return true; // 169.254.0.0/16 link-local / AWS metadata
128
+ if (a === 100 && b >= 64 && b <= 127)
129
+ return true; // 100.64.0.0/10 CGNAT (RFC 6598)
130
+ if (a === 0)
131
+ return true; // 0.0.0.0/8
132
+ }
133
+ // IPv6 checks
134
+ if (host === "::1")
135
+ return true; // loopback
136
+ if (host.startsWith("fe80:"))
137
+ return true; // link-local
138
+ if (host.startsWith("fc") || host.startsWith("fd"))
139
+ return true; // ULA (RFC 4193)
140
+ if (host.startsWith("2002:"))
141
+ return true; // 6to4 (RFC 3056) — embeds IPv4 in bits 16-47;
142
+ // a 6to4 address for a private IPv4 (e.g. 2002:c0a8:0101:: → 192.168.1.1) bypasses
143
+ // the IPv4 checks above unless we block the entire /16 here.
144
+ // Check longer prefix first — ::ffff:0: (IPv4-translated) before ::ffff: (IPv4-mapped)
145
+ // Also handle hex-compressed form that new URL() may return (e.g. "7f00:1" = 127.0.0.1).
146
+ if (host.startsWith("::ffff:0:")) {
147
+ const rest = host.slice(9);
148
+ const dotted = hexIpv4ToDotted(rest);
149
+ return isPrivateHost(dotted ?? rest);
150
+ }
151
+ if (host.startsWith("::ffff:")) {
152
+ const rest = host.slice(7);
153
+ const dotted = hexIpv4ToDotted(rest);
154
+ return isPrivateHost(dotted ?? rest);
155
+ }
156
+ return false;
157
+ }
158
+ /**
159
+ * Loopback-only check (127.0.0.0/8, ::1, localhost). Lexical-only.
160
+ *
161
+ * Used by `isPrivateNonLoopbackHost` and by automation webhook fan-out, which
162
+ * intentionally ALLOWS loopback (sidecars) but blocks every other private
163
+ * range (RFC 1918, link-local, ULA, IMDS, 6to4-wrapped private, etc.).
164
+ */
165
+ export function isLoopbackHost(hostname) {
166
+ const host = hostname.startsWith("[") && hostname.endsWith("]")
167
+ ? hostname.slice(1, -1).toLowerCase()
168
+ : hostname.toLowerCase();
169
+ if (host === "localhost" || host.endsWith(".localhost"))
170
+ return true;
171
+ if (host === "::1")
172
+ return true;
173
+ const ipv4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
174
+ if (ipv4 && Number(ipv4[1]) === 127)
175
+ return true;
176
+ // IPv6-mapped/translated loopback: ::ffff:127.0.0.1 / ::ffff:0:127.0.0.1
177
+ // Also handle hex-compressed form from new URL() (e.g. "7f00:1" = 127.0.0.1).
178
+ if (host.startsWith("::ffff:0:")) {
179
+ const rest = host.slice(9);
180
+ const dotted = hexIpv4ToDotted(rest);
181
+ return isLoopbackHost(dotted ?? rest);
182
+ }
183
+ if (host.startsWith("::ffff:")) {
184
+ const rest = host.slice(7);
185
+ const dotted = hexIpv4ToDotted(rest);
186
+ return isLoopbackHost(dotted ?? rest);
187
+ }
188
+ return false;
189
+ }
190
+ /**
191
+ * Private host MINUS loopback. Lexical-only.
192
+ *
193
+ * Use when a sink intentionally allows loopback (e.g. webhook fan-out to local
194
+ * sidecars) but must still block RFC 1918, link-local, IMDS, ULA, 6to4-wrapped
195
+ * private, and IPv4-mapped/translated private addresses.
196
+ */
197
+ export function isPrivateNonLoopbackHost(hostname) {
198
+ if (isLoopbackHost(hostname))
199
+ return false;
200
+ return isPrivateHost(hostname);
201
+ }
202
+ //# sourceMappingURL=privateHost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privateHost.js","sourceRoot":"","sources":["../src/privateHost.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@
10
10
  * the recipe explicitly opts in with `allowPrivate: true`.
11
11
  */
12
12
  import dns from "node:dns/promises";
13
- import { Agent, fetch as undiciFetch } from "undici";
13
+ import { fetch as undiciFetch } from "undici";
14
14
  import { assertWriteAllowed, FLAG_BLOCK_RECIPE_ALLOW_PRIVATE, isEnabled, } from "../../featureFlags.js";
15
15
  // Canonical SSRF guard — single source of truth. `isPrivateHost` is
16
16
  // re-exported below for the lexical unit tests; execution goes through
@@ -19,24 +19,19 @@ import { assertWriteAllowed, FLAG_BLOCK_RECIPE_ALLOW_PRIVATE, isEnabled, } from
19
19
  import { isPrivateHost, OutboundHttpError, safeFetch, } from "../../ssrfGuard.js";
20
20
  import { CommonSchemas, registerTool } from "../toolRegistry.js";
21
21
  import { UncertainOutcomeError } from "../uncertainOutcome.js";
22
- // Custom dispatcher pinning DNS resolution to IPv4. Node's Happy-Eyeballs
23
- // implementation (autoSelectFamily) is documented to flip families after
24
- // 250 ms, but on macOS networks that lack a usable IPv6 path
25
- // (most home/office LANs, despite the host having public AAAA records)
26
- // the IPv6 attempt stalls past Node's request timeout and surfaces as
27
- // ETIMEDOUT — even though IPv4 would have succeeded instantly. Probing
28
- // repro'd this against ntfy.sh on 2026-05-12.
22
+ // IPv4 only, for both DNS (`resolveDns` below) and the socket (`connect`
23
+ // passed to safeFetch). Node's Happy-Eyeballs implementation
24
+ // (autoSelectFamily) is documented to flip families after 250 ms, but on
25
+ // macOS networks that lack a usable IPv6 path (most home/office LANs,
26
+ // despite the host having public AAAA records) the IPv6 attempt stalls past
27
+ // Node's request timeout and surfaces as ETIMEDOUT — even though IPv4 would
28
+ // have succeeded instantly. Probing repro'd this against ntfy.sh on
29
+ // 2026-05-12. IPv6-only networks are vanishingly rare in 2026; if one comes
30
+ // up we can add a step-level `family: 6` override.
29
31
  //
30
- // IPv6-only networks are vanishingly rare in 2026; if one comes up we
31
- // can add a step-level `family: 6` override.
32
- const httpAgent = new Agent({
33
- // biome-ignore lint/suspicious/noExplicitAny: undici's TcpNetConnectOpts type insists on `port` but it isn't required at the Agent-default-connect level
34
- connect: { family: 4 },
35
- // Keep idle sockets short to avoid stale connections to the same host
36
- // hanging across recipe fires.
37
- keepAliveTimeout: 5_000,
38
- keepAliveMaxTimeout: 10_000,
39
- });
32
+ // There is no shared agent any more: safeFetch builds a dispatcher per hop
33
+ // because that dispatcher IS the DNS pin (2026-10-02 — pinning by rewriting
34
+ // the URL to the IP broke every public HTTPS request).
40
35
  /**
41
36
  * The "sent" boundary. A per-request view over the shared agent whose ONLY
42
37
  * job is to record whether undici ever handed this request to a socket.
@@ -59,14 +54,18 @@ const httpAgent = new Agent({
59
54
  */
60
55
  function trackedDispatcher() {
61
56
  let sent = false;
62
- const dispatcher = httpAgent.compose((dispatch) => (opts, handler) => dispatch(opts, {
57
+ // The same interceptor applied to ANY dispatcher: safeFetch now builds the
58
+ // per-hop dispatcher itself (that is where the DNS pin lives), so the
59
+ // sent-boundary has to wrap the dispatcher it was given rather than a
60
+ // shared agent the pin cannot reach.
61
+ const wrap = (d) => d.compose((dispatch) => (opts, handler) => dispatch(opts, {
63
62
  ...handler,
64
63
  onConnect(abort) {
65
64
  sent = true;
66
65
  handler.onConnect?.(abort);
67
66
  },
68
67
  }));
69
- return { dispatcher, sent: () => sent };
68
+ return { wrap, sent: () => sent };
70
69
  }
71
70
  registerTool({
72
71
  id: "http.post",
@@ -185,9 +184,13 @@ registerTool({
185
184
  body,
186
185
  headers,
187
186
  signal: ctrl.signal,
188
- dispatcher: tracked.dispatcher,
189
187
  }, {
190
188
  allowPrivate,
189
+ // IPv4 for the same reason as `httpAgent` above; safeFetch builds
190
+ // the per-hop dispatcher (it holds the DNS pin), so the family
191
+ // preference and the sent-boundary tracking ride on it.
192
+ connect: { family: 4 },
193
+ wrapDispatcher: tracked.wrap,
191
194
  resolveDns: async (hostname) => (await dns.lookup(hostname, { family: 4 })).address,
192
195
  // undici's Response type lags the DOM lib (no `bytes()`); same
193
196
  // object at runtime.
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/recipes/tools/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,GAAG,MAAM,mBAAmB,CAAC;AACpC,OAAO,EAAE,KAAK,EAAmB,KAAK,IAAI,WAAW,EAAE,MAAM,QAAQ,CAAC;AACtE,OAAO,EACL,kBAAkB,EAClB,+BAA+B,EAC/B,SAAS,GACV,MAAM,uBAAuB,CAAC;AAC/B,oEAAoE;AACpE,uEAAuE;AACvE,2EAA2E;AAC3E,yEAAyE;AACzE,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,SAAS,GACV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,0EAA0E;AAC1E,yEAAyE;AACzE,6DAA6D;AAC7D,uEAAuE;AACvE,sEAAsE;AACtE,uEAAuE;AACvE,8CAA8C;AAC9C,EAAE;AACF,sEAAsE;AACtE,6CAA6C;AAC7C,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC;IAC1B,yJAAyJ;IACzJ,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,EAAS;IAC7B,sEAAsE;IACtE,+BAA+B;IAC/B,gBAAgB,EAAE,KAAK;IACvB,mBAAmB,EAAE,MAAM;CAC5B,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,iBAAiB;IACxB,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAClC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAC9B,QAAQ,CAAC,IAAI,EAAE;QACb,GAAG,OAAO;QACV,SAAS,CAAC,KAAK;YACb,IAAI,GAAG,IAAI,CAAC;YACZ,OAAO,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC,CACL,CAAC;IACF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,YAAY,CAAC;IACX,EAAE,EAAE,WAAW;IACf,SAAS,EAAE,MAAM;IACjB,WAAW,EACT,sEAAsE;QACtE,wEAAwE;IAC1E,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,GAAG,EAAE;gBACH,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,4BAA4B;aAC1C;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC;gBAC9B,OAAO,EAAE,MAAM;aAChB;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,+DAA+D;oBAC/D,qEAAqE;aACxE;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,oBAAoB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACxC,WAAW,EACT,8DAA8D;aACjE;YACD,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,kBAAkB;gBAC3B,WAAW,EAAE,mCAAmC;aACjD;YACD,SAAS,EAAE;gBACT,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,mDAAmD;aACjE;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,KAAK;gBACd,WAAW,EACT,0EAA0E;aAC7E;YACD,IAAI,EAAE,aAAa,CAAC,IAAI;SACzB;QACD,QAAQ,EAAE,CAAC,KAAK,CAAC;KAClB;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1B,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YACvB,UAAU,EAAE;gBACV,IAAI,EAAE,SAAS;gBACf,WAAW,EACT,mIAAmI;aACtI;YACD,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SACzB;KACF;IACD,WAAW,EAAE,QAAQ;IACrB,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QAC5B,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAEhC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAa,CAAC;QACjC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QAED,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CACb,6CAA6C,MAAM,CAAC,QAAQ,GAAG,CAChE,CAAC;QACJ,CAAC;QAED,wEAAwE;QACxE,0EAA0E;QAC1E,2BAA2B;QAC3B,MAAM,cAAc,GAAG,SAAS,CAAC,+BAA+B,CAAC,CAAC;QAClE,MAAM,YAAY,GAAG,CAAC,cAAc,IAAI,MAAM,CAAC,YAAY,KAAK,IAAI,CAAC;QAErE,MAAM,MAAM,GAAG,CAAE,MAAM,CAAC,MAAiB,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QACnE,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,iCAAiC,MAAM,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAA0B,CAAC;QAC/C,MAAM,WAAW,GACd,MAAM,CAAC,WAAkC,IAAI,kBAAkB,CAAC;QACnE,MAAM,YAAY,GACf,MAAM,CAAC,OAA8C,IAAI,EAAE,CAAC;QAC/D,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,WAAW;YAC3B,GAAG,YAAY;SAChB,CAAC;QAEF,MAAM,UAAU,GAAG,MAAM,CAAC,SAA+B,CAAC;QAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACxB,IAAI,CAAC,GAAG,CAAC,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,EACnE,MAAM,CACP,CAAC;QAEF,qEAAqE;QACrE,0DAA0D;QAC1D,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;QACpC,uEAAuE;QACvE,yEAAyE;QACzE,yEAAyE;QACzE,MAAM,WAAW,GAAG,GAAG,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;QACnE,yEAAyE;QACzE,qEAAqE;QACrE,uEAAuE;QACvE,uEAAuE;QACvE,yDAAyD;QACzD,IAAI,GAAa,CAAC;QAClB,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAC5B,MAAM,EACN;gBACE,MAAM;gBACN,IAAI;gBACJ,OAAO;gBACP,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,UAAU,EAAE,OAAO,CAAC,UAAU;aAC/B,EACD;gBACE,YAAY;gBACZ,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAC7B,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO;gBACrD,+DAA+D;gBAC/D,qBAAqB;gBACrB,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAClB,WAAW,CACT,CAAC,EACD,CAAsC,CACP;aACpC,CACF,CAAC;YACF,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC;YACtB,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,iBAAiB,EAAE,CAAC;gBACrC,MAAM,IAAI,GACR,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc;oBAC1B,GAAG,CAAC,IAAI,KAAK,wBAAwB,CAAC;oBACxC,CAAC,cAAc;oBACb,CAAC,CAAC,uCAAuC;oBACzC,CAAC,CAAC,EAAE,CAAC;gBACT,MAAM,IAAI,KAAK,CACb,uDAAuD,GAAG,CAAC,OAAO,GAAG,IAAI,EAAE,CAC5E,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAI,GAAa,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;YACrD,qEAAqE;YACrE,mEAAmE;YACnE,mEAAmE;YACnE,gEAAgE;YAChE,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;gBACnB,MAAM,IAAI,qBAAqB,CAC7B,kCAAkC,WAAW,mFAAmF,MAAM,EAAE,EACxI,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,EAAE,CAAC,CAAC;QAC1D,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QAED,wEAAwE;QACxE,uEAAuE;QACvE,sEAAsE;QACtE,6BAA6B;QAC7B,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC1B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,GAAI,GAAa,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;YACrD,MAAM,IAAI,qBAAqB,CAC7B,kCAAkC,WAAW,iBAAiB,GAAG,CAAC,MAAM,6FAA6F,MAAM,EAAE,EAC7K,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,uEAAuE;YACvE,yEAAyE;YACzE,GAAG,CAAC,SAAS,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;YAC1C,IAAI,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI;SACvE,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,iDAAiD;AACjD,OAAO,EAAE,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/recipes/tools/http.ts"],"names":[],"mappings":"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"}
@@ -26,13 +26,24 @@
26
26
  * both walked straight past it):
27
27
  * - `validateOutboundUrl(url, opts)` — parse, protocol, userinfo strip,
28
28
  * lexical check, DNS pre-resolution re-check, returns the address to pin.
29
- * - `safeFetch(url, init, opts)` — validates, pins the connection to the
30
- * resolved address (hostname rewritten to the IP, original name carried
31
- * in the Host header so SNI / virtual hosting keep working), follows up
32
- * to N redirects manually re-validating EVERY hop, downgrades method/body
33
- * per RFC 7231 on 301/302/303, and drops credential headers on a
34
- * cross-origin hop.
29
+ * - `safeFetch(url, init, opts)` — validates, pins the CONNECTION to the
30
+ * resolved address through an undici dispatcher whose `connect.lookup`
31
+ * returns that address, follows up to N redirects manually re-validating
32
+ * EVERY hop, downgrades method/body per RFC 7231 on 301/302/303, and
33
+ * drops credential headers on a cross-origin hop.
34
+ *
35
+ * The pin lives in the connection, never in the URL. #1572 first pinned by
36
+ * rewriting the URL's hostname to the IP and carrying the name in a `Host`
37
+ * header; TLS takes SNI and the certificate check from the URL, so every
38
+ * public HTTPS request failed ERR_TLS_CERT_ALTNAME_INVALID, and `fetch`
39
+ * silently drops a caller-set `Host`, so plain HTTP reached virtual hosts
40
+ * with the wrong one. Found 2026-10-02 against the installed build; nothing
41
+ * in the suite could see it because every test used a mocked fetch or a
42
+ * loopback HTTP server.
35
43
  */
44
+ import { type Dispatcher } from "undici";
45
+ import { isLoopbackHost, isPrivateHost, isPrivateNonLoopbackHost } from "./privateHost.js";
46
+ export { isLoopbackHost, isPrivateHost, isPrivateNonLoopbackHost };
36
47
  export interface UrlValidationResult {
37
48
  ok: boolean;
38
49
  /** Parsed URL when ok === true. */
@@ -44,30 +55,6 @@ export interface UrlValidationResult {
44
55
  /** Human-readable detail for logs. */
45
56
  detail?: string;
46
57
  }
47
- /**
48
- * Block requests to private/loopback addresses. Lexical-only check.
49
- *
50
- * Mirrors the predicate previously inlined in `tools/httpClient.ts`. Updates
51
- * here MUST stay in sync with the test fixtures in
52
- * `src/tools/__tests__/httpClient.test.ts`.
53
- */
54
- export declare function isPrivateHost(hostname: string): boolean;
55
- /**
56
- * Loopback-only check (127.0.0.0/8, ::1, localhost). Lexical-only.
57
- *
58
- * Used by `isPrivateNonLoopbackHost` and by automation webhook fan-out, which
59
- * intentionally ALLOWS loopback (sidecars) but blocks every other private
60
- * range (RFC 1918, link-local, ULA, IMDS, 6to4-wrapped private, etc.).
61
- */
62
- export declare function isLoopbackHost(hostname: string): boolean;
63
- /**
64
- * Private host MINUS loopback. Lexical-only.
65
- *
66
- * Use when a sink intentionally allows loopback (e.g. webhook fan-out to local
67
- * sidecars) but must still block RFC 1918, link-local, IMDS, ULA, 6to4-wrapped
68
- * private, and IPv4-mapped/translated private addresses.
69
- */
70
- export declare function isPrivateNonLoopbackHost(hostname: string): boolean;
71
58
  /**
72
59
  * Async URL safety gate used by `/recipes/install` and `commands/recipeInstall.ts`.
73
60
  *
@@ -124,8 +111,24 @@ export declare class OutboundHttpError extends Error {
124
111
  /** Minimal fetch shape so undici's fetch and globalThis.fetch both fit. */
125
112
  export type FetchLike = (url: string, init: Record<string, unknown>) => Promise<Response>;
126
113
  export interface SafeFetchOptions extends OutboundUrlOptions {
127
- /** Defaults to `globalThis.fetch`. */
114
+ /**
115
+ * Defaults to `globalThis.fetch`. Node's built-in fetch accepts the pinning
116
+ * `Agent` from the `undici` package as its `dispatcher` — verified against a
117
+ * public HTTPS endpoint on Node 24 (2026-10-02).
118
+ */
128
119
  fetchImpl?: FetchLike;
120
+ /**
121
+ * Extra socket options for every hop's connection (e.g. `{ family: 4 }`).
122
+ * `lookup` is always overridden when a hop is pinned.
123
+ */
124
+ connect?: Record<string, unknown>;
125
+ /**
126
+ * Wrap the per-hop dispatcher safeFetch builds — the seam `http.post` uses
127
+ * to observe `onConnect` (its "was anything sent" boundary). A caller's
128
+ * `init.dispatcher` is IGNORED: honouring it would let a caller route
129
+ * around the pin, which is the whole guard.
130
+ */
131
+ wrapDispatcher?: (d: Dispatcher) => Dispatcher;
129
132
  /** Follow 3xx redirects (default true). */
130
133
  followRedirects?: boolean;
131
134
  /** Redirect hop cap (default 10). */
@@ -143,9 +146,11 @@ export interface SafeFetchResult {
143
146
  * refusal (initial URL or any redirect hop); transport errors propagate from
144
147
  * `fetchImpl` untouched so callers keep their own timeout/abort messages.
145
148
  *
146
- * `init.headers` must be a plain object; keys are lowercased. `host` is set
147
- * by this function AFTER caller headers so a caller cannot un-pin a hop.
148
- * Every other `init` field (signal, dispatcher, …) is passed through.
149
+ * `init.headers` must be a plain object; keys are lowercased. A caller's
150
+ * `host` header and `dispatcher` are DROPPED: Host is derived from the URL,
151
+ * and the dispatcher is the pin — accepting either from the caller is a way
152
+ * to steer the request somewhere the validation never looked. Every other
153
+ * `init` field (signal, …) is passed through.
149
154
  */
150
155
  export declare function safeFetch(input: string | URL, init?: Record<string, unknown> & {
151
156
  method?: string;
package/dist/ssrfGuard.js CHANGED
@@ -26,204 +26,26 @@
26
26
  * both walked straight past it):
27
27
  * - `validateOutboundUrl(url, opts)` — parse, protocol, userinfo strip,
28
28
  * lexical check, DNS pre-resolution re-check, returns the address to pin.
29
- * - `safeFetch(url, init, opts)` — validates, pins the connection to the
30
- * resolved address (hostname rewritten to the IP, original name carried
31
- * in the Host header so SNI / virtual hosting keep working), follows up
32
- * to N redirects manually re-validating EVERY hop, downgrades method/body
33
- * per RFC 7231 on 301/302/303, and drops credential headers on a
34
- * cross-origin hop.
35
- */
36
- import dns from "node:dns/promises";
37
- /**
38
- * Convert the hex-compressed form that `new URL()` may return for an
39
- * IPv4-mapped/translated IPv6 address back to dotted-decimal so the existing
40
- * `isPrivateHost` checks apply.
41
- *
42
- * Handles:
43
- * "xxxx:yyyy" — two 16-bit groups (up to 4 hex digits each, no leading zeros)
44
- * "xxxxxxxx" — a single 32-bit group
45
- *
46
- * Returns null when the input is not recognisable as 32-bit hex-IPv4.
47
- */
48
- function hexIpv4ToDotted(s) {
49
- const colon = s.indexOf(":");
50
- if (colon !== -1) {
51
- const hi = s.slice(0, colon);
52
- const lo = s.slice(colon + 1);
53
- if (!/^[0-9a-f]{1,4}$/i.test(hi) || !/^[0-9a-f]{1,4}$/i.test(lo))
54
- return null;
55
- const hiN = parseInt(hi, 16);
56
- const loN = parseInt(lo, 16);
57
- return `${(hiN >>> 8) & 0xff}.${hiN & 0xff}.${(loN >>> 8) & 0xff}.${loN & 0xff}`;
58
- }
59
- if (!/^[0-9a-f]{1,8}$/i.test(s))
60
- return null;
61
- const n = parseInt(s, 16);
62
- return `${(n >>> 24) & 0xff}.${(n >>> 16) & 0xff}.${(n >>> 8) & 0xff}.${n & 0xff}`;
63
- }
64
- /**
65
- * inet_aton-style loose IPv4 parse: 1-4 parts, each decimal, octal (leading
66
- * 0) or hex (0x). Returns canonical dotted-quad or null when the string is
67
- * not an all-numeric IPv4 form. `new URL()` already canonicalises these, but
68
- * callers that hand `isPrivateHost` a raw string (a DNS answer, a Location
69
- * header hostname) must not be bypassable by `0x7f000001`, `2130706433`,
70
- * `0177.0.0.1` or `127.1`.
71
- */
72
- function looseIpv4ToDotted(host) {
73
- if (!/^[0-9a-fx.]+$/i.test(host) ||
74
- host.startsWith(".") ||
75
- host.endsWith("."))
76
- return null;
77
- const parts = host.split(".");
78
- if (parts.length < 1 || parts.length > 4)
79
- return null;
80
- const nums = [];
81
- for (const part of parts) {
82
- let n;
83
- if (/^0x[0-9a-f]+$/i.test(part))
84
- n = parseInt(part.slice(2), 16);
85
- else if (/^0[0-7]+$/.test(part))
86
- n = parseInt(part, 8);
87
- else if (/^\d+$/.test(part))
88
- n = parseInt(part, 10);
89
- else
90
- return null;
91
- if (!Number.isFinite(n))
92
- return null;
93
- nums.push(n);
94
- }
95
- // Last part fills the remaining bytes (inet_aton semantics).
96
- const last = nums[nums.length - 1];
97
- const remainingBytes = 4 - (nums.length - 1);
98
- if (last >= 2 ** (8 * remainingBytes))
99
- return null;
100
- for (let i = 0; i < nums.length - 1; i++) {
101
- if (nums[i] > 255)
102
- return null;
103
- }
104
- let value = 0;
105
- for (let i = 0; i < nums.length - 1; i++) {
106
- value = value * 256 + nums[i];
107
- }
108
- value = value * 2 ** (8 * remainingBytes) + last;
109
- return `${(value >>> 24) & 0xff}.${(value >>> 16) & 0xff}.${(value >>> 8) & 0xff}.${value & 0xff}`;
110
- }
111
- /**
112
- * Block requests to private/loopback addresses. Lexical-only check.
113
- *
114
- * Mirrors the predicate previously inlined in `tools/httpClient.ts`. Updates
115
- * here MUST stay in sync with the test fixtures in
116
- * `src/tools/__tests__/httpClient.test.ts`.
117
- */
118
- export function isPrivateHost(hostname) {
119
- const host = hostname.startsWith("[") && hostname.endsWith("]")
120
- ? hostname.slice(1, -1).toLowerCase()
121
- : hostname.toLowerCase();
122
- if (host === "localhost" || host.endsWith(".localhost"))
123
- return true;
124
- if (host === "0.0.0.0")
125
- return true;
126
- // Reject non-decimal IPv4 notations (hex/octal) that bypass the dotted-quad
127
- // regex below. Node's URL parser may normalize them on some platforms.
128
- if (/^0x[0-9a-f]+$/i.test(host) || /^0[0-7]{7,}$/.test(host))
129
- return true;
130
- // Unusual IPv4 notations (decimal integer, short-form "127.1", zero-padded
131
- // octal, per-part hex): canonicalise and re-check the dotted form.
132
- const canonicalIpv4 = /^\d{1,3}(\.\d{1,3}){3}$/.test(host);
133
- if (!canonicalIpv4) {
134
- const dotted = looseIpv4ToDotted(host);
135
- if (dotted !== null)
136
- return isPrivateHost(dotted);
137
- }
138
- // IPv4 range checks
139
- const ipv4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
140
- if (ipv4) {
141
- const a = Number(ipv4[1]);
142
- const b = Number(ipv4[2]);
143
- if (a === 127)
144
- return true; // 127.0.0.0/8 loopback
145
- if (a === 10)
146
- return true; // 10.0.0.0/8 RFC 1918 private
147
- if (a === 172 && b >= 16 && b <= 31)
148
- return true; // 172.16.0.0/12 RFC 1918 private
149
- if (a === 192 && b === 168)
150
- return true; // 192.168.0.0/16 RFC 1918 private
151
- if (a === 169 && b === 254)
152
- return true; // 169.254.0.0/16 link-local / AWS metadata
153
- if (a === 100 && b >= 64 && b <= 127)
154
- return true; // 100.64.0.0/10 CGNAT (RFC 6598)
155
- if (a === 0)
156
- return true; // 0.0.0.0/8
157
- }
158
- // IPv6 checks
159
- if (host === "::1")
160
- return true; // loopback
161
- if (host.startsWith("fe80:"))
162
- return true; // link-local
163
- if (host.startsWith("fc") || host.startsWith("fd"))
164
- return true; // ULA (RFC 4193)
165
- if (host.startsWith("2002:"))
166
- return true; // 6to4 (RFC 3056) — embeds IPv4 in bits 16-47;
167
- // a 6to4 address for a private IPv4 (e.g. 2002:c0a8:0101:: → 192.168.1.1) bypasses
168
- // the IPv4 checks above unless we block the entire /16 here.
169
- // Check longer prefix first — ::ffff:0: (IPv4-translated) before ::ffff: (IPv4-mapped)
170
- // Also handle hex-compressed form that new URL() may return (e.g. "7f00:1" = 127.0.0.1).
171
- if (host.startsWith("::ffff:0:")) {
172
- const rest = host.slice(9);
173
- const dotted = hexIpv4ToDotted(rest);
174
- return isPrivateHost(dotted ?? rest);
175
- }
176
- if (host.startsWith("::ffff:")) {
177
- const rest = host.slice(7);
178
- const dotted = hexIpv4ToDotted(rest);
179
- return isPrivateHost(dotted ?? rest);
180
- }
181
- return false;
182
- }
183
- /**
184
- * Loopback-only check (127.0.0.0/8, ::1, localhost). Lexical-only.
185
- *
186
- * Used by `isPrivateNonLoopbackHost` and by automation webhook fan-out, which
187
- * intentionally ALLOWS loopback (sidecars) but blocks every other private
188
- * range (RFC 1918, link-local, ULA, IMDS, 6to4-wrapped private, etc.).
189
- */
190
- export function isLoopbackHost(hostname) {
191
- const host = hostname.startsWith("[") && hostname.endsWith("]")
192
- ? hostname.slice(1, -1).toLowerCase()
193
- : hostname.toLowerCase();
194
- if (host === "localhost" || host.endsWith(".localhost"))
195
- return true;
196
- if (host === "::1")
197
- return true;
198
- const ipv4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
199
- if (ipv4 && Number(ipv4[1]) === 127)
200
- return true;
201
- // IPv6-mapped/translated loopback: ::ffff:127.0.0.1 / ::ffff:0:127.0.0.1
202
- // Also handle hex-compressed form from new URL() (e.g. "7f00:1" = 127.0.0.1).
203
- if (host.startsWith("::ffff:0:")) {
204
- const rest = host.slice(9);
205
- const dotted = hexIpv4ToDotted(rest);
206
- return isLoopbackHost(dotted ?? rest);
207
- }
208
- if (host.startsWith("::ffff:")) {
209
- const rest = host.slice(7);
210
- const dotted = hexIpv4ToDotted(rest);
211
- return isLoopbackHost(dotted ?? rest);
212
- }
213
- return false;
214
- }
215
- /**
216
- * Private host MINUS loopback. Lexical-only.
29
+ * - `safeFetch(url, init, opts)` — validates, pins the CONNECTION to the
30
+ * resolved address through an undici dispatcher whose `connect.lookup`
31
+ * returns that address, follows up to N redirects manually re-validating
32
+ * EVERY hop, downgrades method/body per RFC 7231 on 301/302/303, and
33
+ * drops credential headers on a cross-origin hop.
217
34
  *
218
- * Use when a sink intentionally allows loopback (e.g. webhook fan-out to local
219
- * sidecars) but must still block RFC 1918, link-local, IMDS, ULA, 6to4-wrapped
220
- * private, and IPv4-mapped/translated private addresses.
35
+ * The pin lives in the connection, never in the URL. #1572 first pinned by
36
+ * rewriting the URL's hostname to the IP and carrying the name in a `Host`
37
+ * header; TLS takes SNI and the certificate check from the URL, so every
38
+ * public HTTPS request failed ERR_TLS_CERT_ALTNAME_INVALID, and `fetch`
39
+ * silently drops a caller-set `Host`, so plain HTTP reached virtual hosts
40
+ * with the wrong one. Found 2026-10-02 against the installed build; nothing
41
+ * in the suite could see it because every test used a mocked fetch or a
42
+ * loopback HTTP server.
221
43
  */
222
- export function isPrivateNonLoopbackHost(hostname) {
223
- if (isLoopbackHost(hostname))
224
- return false;
225
- return isPrivateHost(hostname);
226
- }
44
+ import dns from "node:dns/promises";
45
+ import { Agent } from "undici";
46
+ import { isLoopbackHost, isPrivateHost, isPrivateNonLoopbackHost, } from "./privateHost.js";
47
+ // Re-exported so every existing `from "./ssrfGuard.js"` importer is unchanged.
48
+ export { isLoopbackHost, isPrivateHost, isPrivateNonLoopbackHost };
227
49
  /**
228
50
  * Async URL safety gate used by `/recipes/install` and `commands/recipeInstall.ts`.
229
51
  *
@@ -355,21 +177,45 @@ function refusalMessage(v, hop) {
355
177
  return `${where}request refused`;
356
178
  }
357
179
  }
358
- function pin(url, address) {
359
- if (address === undefined)
360
- return url.toString();
361
- const pinned = new URL(url.toString());
362
- pinned.hostname = address.includes(":") ? `[${address}]` : address;
363
- return pinned.toString();
180
+ /**
181
+ * A dispatcher whose connections go to `address` (when given), however the
182
+ * URL's hostname would resolve. The URL is left alone, so TLS uses the real
183
+ * name for SNI and certificate verification and the Host header is derived
184
+ * from it. Node's `net`/`tls` call `lookup` with `{ all: true }` when
185
+ * autoSelectFamily is on, so both callback shapes are answered.
186
+ *
187
+ * One Agent per hop, with short keep-alive: the pin is per-hostname-per-hop,
188
+ * and a shared pool keyed only on origin would happily reuse a socket pinned
189
+ * for a previous answer.
190
+ */
191
+ function hopDispatcher(address, opts) {
192
+ const connect = { ...(opts.connect ?? {}) };
193
+ if (address !== undefined) {
194
+ const family = address.includes(":") ? 6 : 4;
195
+ connect.lookup = (_hostname, lookupOpts, cb) => {
196
+ if (lookupOpts?.all)
197
+ cb(null, [{ address, family }]);
198
+ else
199
+ cb(null, address, family);
200
+ };
201
+ }
202
+ const agent = new Agent({
203
+ connect,
204
+ keepAliveTimeout: 1_000,
205
+ keepAliveMaxTimeout: 1_000,
206
+ });
207
+ return opts.wrapDispatcher ? opts.wrapDispatcher(agent) : agent;
364
208
  }
365
209
  /**
366
210
  * Fetch with the full outbound discipline. Throws `OutboundHttpError` on any
367
211
  * refusal (initial URL or any redirect hop); transport errors propagate from
368
212
  * `fetchImpl` untouched so callers keep their own timeout/abort messages.
369
213
  *
370
- * `init.headers` must be a plain object; keys are lowercased. `host` is set
371
- * by this function AFTER caller headers so a caller cannot un-pin a hop.
372
- * Every other `init` field (signal, dispatcher, …) is passed through.
214
+ * `init.headers` must be a plain object; keys are lowercased. A caller's
215
+ * `host` header and `dispatcher` are DROPPED: Host is derived from the URL,
216
+ * and the dispatcher is the pin — accepting either from the caller is a way
217
+ * to steer the request somewhere the validation never looked. Every other
218
+ * `init` field (signal, …) is passed through.
373
219
  */
374
220
  export async function safeFetch(input, init = {}, opts = {}) {
375
221
  const fetchImpl = opts.fetchImpl ?? ((u, i) => globalThis.fetch(u, i));
@@ -381,15 +227,17 @@ export async function safeFetch(input, init = {}, opts = {}) {
381
227
  }
382
228
  const headers = {};
383
229
  for (const [k, v] of Object.entries(init.headers ?? {})) {
384
- headers[k.toLowerCase()] = v;
230
+ const key = k.toLowerCase();
231
+ if (key === "host")
232
+ continue;
233
+ headers[key] = v;
385
234
  }
386
- const { method: initMethod, headers: _h, body: initBody, ...rest } = init;
235
+ const { method: initMethod, headers: _h, body: initBody, dispatcher: _callerDispatcher, ...rest } = init;
387
236
  let currentMethod = (initMethod ?? "GET").toUpperCase();
388
237
  let currentBody = initBody;
389
238
  let displayUrl = first.url;
390
- let currentUrl = pin(first.url, first.pinnedAddress);
391
- if (first.pinnedAddress !== undefined)
392
- headers.host = first.url.hostname;
239
+ let currentUrl = first.url.toString();
240
+ let currentDispatcher = hopDispatcher(first.pinnedAddress, opts);
393
241
  const originalOrigin = first.url.origin;
394
242
  let redirects = 0;
395
243
  while (true) {
@@ -399,6 +247,7 @@ export async function safeFetch(input, init = {}, opts = {}) {
399
247
  headers,
400
248
  body: currentBody,
401
249
  redirect: "manual",
250
+ dispatcher: currentDispatcher,
402
251
  });
403
252
  const isRedirect = response.status >= 300 && response.status < 400;
404
253
  if (!followRedirects || !isRedirect) {
@@ -424,9 +273,6 @@ export async function safeFetch(input, init = {}, opts = {}) {
424
273
  if (!next.ok || next.url === undefined) {
425
274
  throw new OutboundHttpError(next.reason ?? "invalid_redirect", refusalMessage(next, "redirect"));
426
275
  }
427
- // Host always from the real name — even when this hop's DNS failed —
428
- // so the previous hop's Host never leaks onto the new request.
429
- headers.host = next.url.hostname;
430
276
  // RFC 7231 + browser/fetch semantics: 301/302 of a non-GET/HEAD and any
431
277
  // 303 become GET without a body; 307/308 preserve both.
432
278
  if (response.status === 303 ||
@@ -442,8 +288,12 @@ export async function safeFetch(input, init = {}, opts = {}) {
442
288
  for (const h of CREDENTIAL_HEADERS)
443
289
  delete headers[h];
444
290
  }
291
+ // The previous hop's response is fully handled (a redirect body is never
292
+ // read), so its dispatcher can close; the next hop gets its own pin.
293
+ void currentDispatcher.close?.().catch?.(() => { });
445
294
  displayUrl = next.url;
446
- currentUrl = pin(next.url, next.pinnedAddress);
295
+ currentUrl = next.url.toString();
296
+ currentDispatcher = hopDispatcher(next.pinnedAddress, opts);
447
297
  redirects++;
448
298
  }
449
299
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ssrfGuard.js","sourceRoot":"","sources":["../src/ssrfGuard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ssrfGuard.js","sourceRoot":"","sources":["../src/ssrfGuard.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchwork-os",
3
- "version": "1.2.0-beta.3.canary.875",
3
+ "version": "1.2.0-beta.3.canary.876",
4
4
  "description": "Your personal AI runtime, local-first. Patchwork OS gives any AI model a consistent set of tools, YAML recipes, a delegation policy with approval queue, and a durable trace memory — all on your machine, all under your policy.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",