@provian/mcp 1.2.3 → 1.3.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 (35) hide show
  1. package/CHANGELOG.md +6 -2
  2. package/README.md +1 -1
  3. package/dist/core/egress-guard.d.ts +39 -0
  4. package/dist/core/egress-guard.d.ts.map +1 -0
  5. package/dist/core/egress-guard.js +303 -0
  6. package/dist/core/egress-guard.js.map +1 -0
  7. package/dist/core/guarded-fetch.d.ts +46 -0
  8. package/dist/core/guarded-fetch.d.ts.map +1 -0
  9. package/dist/core/guarded-fetch.js +236 -0
  10. package/dist/core/guarded-fetch.js.map +1 -0
  11. package/dist/core/pinned-agent.d.ts +61 -0
  12. package/dist/core/pinned-agent.d.ts.map +1 -0
  13. package/dist/core/pinned-agent.js +125 -0
  14. package/dist/core/pinned-agent.js.map +1 -0
  15. package/dist/core/pinned-connection.d.ts +15 -0
  16. package/dist/core/pinned-connection.d.ts.map +1 -0
  17. package/dist/core/pinned-connection.js +172 -0
  18. package/dist/core/pinned-connection.js.map +1 -0
  19. package/dist/core/protocol-pointer-pin.generated.d.ts +6 -0
  20. package/dist/core/protocol-pointer-pin.generated.d.ts.map +1 -0
  21. package/dist/core/protocol-pointer-pin.generated.js +6 -0
  22. package/dist/core/protocol-pointer-pin.generated.js.map +1 -0
  23. package/dist/core/protocol-pointer.d.ts +25 -0
  24. package/dist/core/protocol-pointer.d.ts.map +1 -0
  25. package/dist/core/protocol-pointer.js +132 -0
  26. package/dist/core/protocol-pointer.js.map +1 -0
  27. package/dist/core/protocol-runtime.d.ts +4 -0
  28. package/dist/core/protocol-runtime.d.ts.map +1 -0
  29. package/dist/core/protocol-runtime.js +31 -0
  30. package/dist/core/protocol-runtime.js.map +1 -0
  31. package/dist/core/tools.d.ts +1 -14
  32. package/dist/core/tools.d.ts.map +1 -1
  33. package/dist/core/tools.js +35 -56
  34. package/dist/core/tools.js.map +1 -1
  35. package/package.json +5 -3
package/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # @provian/mcp release notes
2
2
 
3
- **Published version: 1.2.3.** The package manifest in this directory defines the installed version.
3
+ **Version: 1.3.0.** The package manifest in this directory defines the installed version.
4
+
5
+ Ownership and recovery are independent of a wallet key. The common-owner link across bound addresses and portfolio totals remain private to the relay; an individual recovery may publicly link an old address to its replacement. This release resolves the permanent protocol pointer before supported PVT and institution operations and refuses retired targets.
6
+
7
+ The optional Recourse Pool uses reversal authorized by both the restoration authority and the owner first, then its member-funded reserve for a shortfall accepted by discretionary determination, pro rata when underfunded. Contributions are nonrefundable; an allocated restoration has a direct exit without the relay. Members separately burn 100 PVT each month and contribute 1% of earnings, or 100 PVT from holdings in a month without earnings, under the signed rule. Ordinary transfers remain permissionless. The SDK and CLI include hash-based recovery and restoration workflows; the MCP package uses the same verified current deployment for its supported tools.
4
8
 
5
9
  A Provenance Envelope (PE) is signed proof owned by its contributor. PVT is the currency on Base minted from witnessed work. Every act of work mints once at its best-supported value; bookkeeping and duplicate witnesses do not mint separately.
6
10
 
@@ -10,6 +14,6 @@ The hosted MCP endpoint is `https://api.provian.ai/mcp`. Its public tool descrip
10
14
 
11
15
  The observer delivers Commons messages directly to the configured runtime over authenticated events. Follow the owner’s reply policy. No Commons heartbeat is required. On a machine with a service manager, cron, or a shell that reads its startup files, there is nothing to schedule: the observer restarts itself, the Provian startup hook prints any notice before the agent’s next command, and every `provian` command carries it. On a Grok Bot computer, whose commands run in a shell that reads no startup file and which has no cron or systemd, the agent keeps the observer running with a scheduled `provian observe --if-not-running`, and keeps that task.
12
16
 
13
- Standard earnings settle on a schedule Provian sets, currently every 30 days, Genesis grants daily, and an owner may settle ready earnings sooner for a flat $1 USDC service fee. Genesis is open through December 31, 2026. Court/Disputes, Voting, Compute market and Insurance are not open. Android out-of-app AI capture and Grok Bot’s provider-side tool calls are not available.
17
+ Standard earnings settle on a schedule Provian sets, currently every 30 days, Genesis grants daily, and an owner may settle ready earnings sooner for a flat $1 USDC service fee. Genesis is open through December 31, 2026. Court/Disputes, Voting, Compute market and other recourse programs are not open. Android out-of-app AI capture and Grok Bot’s provider-side tool calls are not available.
14
18
 
15
19
  See [README](README.md) for the supported commands, interfaces and security boundaries.
package/README.md CHANGED
@@ -216,7 +216,7 @@ Court escalation is not offered on a job.
216
216
 
217
217
  The Commons institution catalog is served by `GET /v1/commons/institutions`. Its not-open-yet
218
218
  set is exactly Disputes (the Court: it opens once enough members qualify to judge cases), Voting,
219
- Compute market, and Insurance. Insurance is not open. Provian provides the rails, not the insurance.
219
+ and Compute market.
220
220
 
221
221
  Every act of work mints PVT once at its best-supported value. The PE mints its base; telemetry and independence multiply on top. There is no tool to report your own work: an actor's own claim about its work is never a PE. Your work mints from what something other than you vouches for: tool-calling model responses witnessed by the observer on a registered device. Each program has exactly one witness: its described session files if it keeps them, otherwise the proxy, otherwise OTel, otherwise — on its own machine — the process lane, which mints each command the program runs once. Each response calling at least one tool, including errored calls, mints once; a response without tools does not mint. Switching sign-in, model or provider keeps that witness. Other work earns through commits via the forge webhooks, your Commons acts, and your on-chain acts signed with your own key. This holds for a hosted bot on a cloud VM too. Capture health for each device shows which lanes are live. A Grok Bot runs its turns on xAI's servers and writes no transcript on its machine; the process lane witnesses each command it runs there, read from the operating system, beside its commits, Commons acts and on-chain acts.
222
222
 
@@ -0,0 +1,39 @@
1
+ /**
2
+ * SSRF egress guard (SDK-side enforcement).
3
+ *
4
+ * WS3 audit fix. The architecture promised that non-payment agent egress was
5
+ * SSRF/IP-pinning enforced, but the device-side gate that was supposed to do it
6
+ * had zero callers and the agent performed the fetch itself with no checks.
7
+ * Enforcement belongs where the fetch actually happens — here, in the SDK. (The
8
+ * unwired device module was deleted outright in; this is the only egress
9
+ * enforcement point, not a second one.)
10
+ *
11
+ * This is a self-contained port of the device SSRFValidator's synchronous
12
+ * classifier (src/core/credentials/proxy/SSRFValidator.ts), plus an async
13
+ * resolve-and-recheck step with an INJECTABLE resolver so it is portable
14
+ * (Node `dns` by default) and unit-testable (DNS-rebinding can be exercised by
15
+ * injecting a resolver that returns a private IP for a public-looking host).
16
+ *
17
+ * `guarded-fetch.ts` binds these exact addresses to the connection's lookup while
18
+ * retaining the original hostname for TLS/SNI and Host. Empty/failed DNS is refused.
19
+ */
20
+ /** True if a hostname/IP literal is an SSRF target (private / loopback / metadata). */
21
+ export declare function isSSRFTarget(hostname: string): boolean;
22
+ export interface EgressGuardResult {
23
+ ok: boolean;
24
+ reason?: string;
25
+ code?: 'INVALID_URL' | 'HTTPS_REQUIRED' | 'CREDENTIALS_IN_URL' | 'SSRF_BLOCKED' | 'DNS_FAILED';
26
+ hostname?: string;
27
+ resolvedIps?: string[];
28
+ }
29
+ export type HostResolver = (hostname: string) => Promise<string[]>;
30
+ /**
31
+ * Validate an egress target. Hard-rejects:
32
+ * - non-HTTPS URLs and URLs carrying credentials
33
+ * - SSRF targets (private/loopback/link-local IPs, IPv6 ULA, cloud metadata)
34
+ * - hosts that RESOLVE to a private IP (DNS rebinding at check time)
35
+ */
36
+ export declare function validateEgressTarget(url: string, opts?: {
37
+ resolveHost?: HostResolver;
38
+ }): Promise<EgressGuardResult>;
39
+ //# sourceMappingURL=egress-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"egress-guard.d.ts","sourceRoot":"","sources":["../../src/core/egress-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAqLH,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAStD;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,aAAa,GAAG,gBAAgB,GAAG,oBAAoB,GAAG,cAAc,GAAG,YAAY,CAAC;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAanE;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,YAAY,CAAA;CAAE,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAyC5B"}
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ /**
3
+ * SSRF egress guard (SDK-side enforcement).
4
+ *
5
+ * WS3 audit fix. The architecture promised that non-payment agent egress was
6
+ * SSRF/IP-pinning enforced, but the device-side gate that was supposed to do it
7
+ * had zero callers and the agent performed the fetch itself with no checks.
8
+ * Enforcement belongs where the fetch actually happens — here, in the SDK. (The
9
+ * unwired device module was deleted outright in; this is the only egress
10
+ * enforcement point, not a second one.)
11
+ *
12
+ * This is a self-contained port of the device SSRFValidator's synchronous
13
+ * classifier (src/core/credentials/proxy/SSRFValidator.ts), plus an async
14
+ * resolve-and-recheck step with an INJECTABLE resolver so it is portable
15
+ * (Node `dns` by default) and unit-testable (DNS-rebinding can be exercised by
16
+ * injecting a resolver that returns a private IP for a public-looking host).
17
+ *
18
+ * `guarded-fetch.ts` binds these exact addresses to the connection's lookup while
19
+ * retaining the original hostname for TLS/SNI and Host. Empty/failed DNS is refused.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.isSSRFTarget = isSSRFTarget;
23
+ exports.validateEgressTarget = validateEgressTarget;
24
+ const IPV4_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
25
+ const IPV6_REGEX = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,7}:$|^(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}$|^(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}$|^(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}$|^:(?::[0-9a-fA-F]{1,4}){1,7}$|^::$/;
26
+ function isValidIPAddress(value) {
27
+ if (!value || typeof value !== 'string')
28
+ return false;
29
+ const t = value.trim();
30
+ return IPV4_REGEX.test(t) || IPV6_REGEX.test(t);
31
+ }
32
+ const BLOCKED_IPV4_RANGES = [
33
+ { start: '127.0.0.0', end: '127.255.255.255', name: 'loopback' },
34
+ { start: '10.0.0.0', end: '10.255.255.255', name: 'private-class-a' },
35
+ { start: '172.16.0.0', end: '172.31.255.255', name: 'private-class-b' },
36
+ { start: '192.168.0.0', end: '192.168.255.255', name: 'private-class-c' },
37
+ { start: '169.254.0.0', end: '169.254.255.255', name: 'link-local' },
38
+ { start: '0.0.0.0', end: '0.255.255.255', name: 'current-network' },
39
+ { start: '255.255.255.255', end: '255.255.255.255', name: 'broadcast' },
40
+ { start: '100.64.0.0', end: '100.127.255.255', name: 'shared-address-space' },
41
+ { start: '198.18.0.0', end: '198.19.255.255', name: 'benchmark-testing' },
42
+ { start: '240.0.0.0', end: '255.255.255.254', name: 'reserved-future' },
43
+ { start: '224.0.0.0', end: '239.255.255.255', name: 'multicast' },
44
+ ];
45
+ const BLOCKED_HOSTNAMES = new Set([
46
+ 'localhost', 'localhost.localdomain', '127.0.0.1', '::1', '[::1]', '0.0.0.0',
47
+ '169.254.169.254', 'metadata.google.internal',
48
+ ]);
49
+ function ipv4ToInt(ip) {
50
+ const parts = ip.split('.');
51
+ if (parts.length !== 4)
52
+ return null;
53
+ let result = 0;
54
+ for (const part of parts) {
55
+ const num = parseInt(part, 10);
56
+ if (isNaN(num) || num < 0 || num > 255)
57
+ return null;
58
+ result = (result << 8) + num;
59
+ }
60
+ return result >>> 0;
61
+ }
62
+ function normalizeIpAddress(input) {
63
+ if (IPV4_REGEX.test(input))
64
+ return input;
65
+ if (/^\d+$/.test(input)) {
66
+ const num = parseInt(input, 10);
67
+ if (num >= 0 && num <= 0xFFFFFFFF) {
68
+ return [(num >>> 24) & 0xFF, (num >>> 16) & 0xFF, (num >>> 8) & 0xFF, num & 0xFF].join('.');
69
+ }
70
+ return null;
71
+ }
72
+ if (/^0x[0-9a-fA-F]+$/.test(input)) {
73
+ const num = parseInt(input, 16);
74
+ if (num >= 0 && num <= 0xFFFFFFFF) {
75
+ return [(num >>> 24) & 0xFF, (num >>> 16) & 0xFF, (num >>> 8) & 0xFF, num & 0xFF].join('.');
76
+ }
77
+ return null;
78
+ }
79
+ const parts = input.split('.');
80
+ if (parts.length >= 1 && parts.length <= 4) {
81
+ const octets = [];
82
+ for (const part of parts) {
83
+ let val;
84
+ if (part.startsWith('0x') || part.startsWith('0X'))
85
+ val = parseInt(part, 16);
86
+ else if (part.startsWith('0') && part.length > 1)
87
+ val = parseInt(part, 8);
88
+ else
89
+ val = parseInt(part, 10);
90
+ if (isNaN(val) || val < 0)
91
+ return null;
92
+ octets.push(val);
93
+ }
94
+ if (octets.length === 1 && octets[0] <= 0xFFFFFFFF) {
95
+ const n = octets[0];
96
+ return [(n >>> 24) & 0xFF, (n >>> 16) & 0xFF, (n >>> 8) & 0xFF, n & 0xFF].join('.');
97
+ }
98
+ if (octets.length === 2 && octets[0] <= 0xFF && octets[1] <= 0xFFFFFF) {
99
+ return [octets[0], (octets[1] >>> 16) & 0xFF, (octets[1] >>> 8) & 0xFF, octets[1] & 0xFF].join('.');
100
+ }
101
+ if (octets.length === 3 && octets[0] <= 0xFF && octets[1] <= 0xFF && octets[2] <= 0xFFFF) {
102
+ return [octets[0], octets[1], (octets[2] >>> 8) & 0xFF, octets[2] & 0xFF].join('.');
103
+ }
104
+ if (octets.length === 4 && octets.every(o => o <= 0xFF))
105
+ return octets.join('.');
106
+ }
107
+ return null;
108
+ }
109
+ function isBlockedIPv4(ip) {
110
+ if (ip.includes(':'))
111
+ return { blocked: false };
112
+ const normalized = normalizeIpAddress(ip);
113
+ if (normalized && normalized !== ip)
114
+ return isBlockedIPv4(normalized);
115
+ const ipInt = ipv4ToInt(ip);
116
+ if (ipInt === null)
117
+ return { blocked: false };
118
+ for (const range of BLOCKED_IPV4_RANGES) {
119
+ const s = ipv4ToInt(range.start);
120
+ const e = ipv4ToInt(range.end);
121
+ if (s !== null && e !== null && ipInt >= s && ipInt <= e) {
122
+ return { blocked: true, reason: `IP ${ip} is in blocked range: ${range.name}` };
123
+ }
124
+ }
125
+ return { blocked: false };
126
+ }
127
+ function looksLikePrivateHost(hostname) {
128
+ const lower = hostname.toLowerCase();
129
+ const m = lower.match(/(?:^|[.-])(\d{1,3})[.-](\d{1,3})[.-](\d{1,3})[.-](\d{1,3})(?:[.-]|$)/);
130
+ if (m && isBlockedIPv4(`${m[1]}.${m[2]}.${m[3]}.${m[4]}`).blocked)
131
+ return true;
132
+ if (lower.includes('localhost') || lower.includes('127.0.0.1') || lower.includes('0.0.0.0'))
133
+ return true;
134
+ if (lower.endsWith('.local') || lower.endsWith('.internal') || lower.endsWith('.corp') ||
135
+ lower.endsWith('.home') || lower.endsWith('.lan'))
136
+ return true;
137
+ return false;
138
+ }
139
+ function parseIPv6(ip) {
140
+ let cleanIp = ip.replace(/^\[|\]$/g, '');
141
+ const mapped = cleanIp.match(/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i);
142
+ if (mapped)
143
+ return { segments: [0, 0, 0, 0, 0, 0xFFFF, 0, 0], isIPv4Mapped: true, mappedIPv4: mapped[1] };
144
+ if (cleanIp.includes('::')) {
145
+ const parts = cleanIp.split('::');
146
+ if (parts.length > 2)
147
+ return null;
148
+ const left = parts[0] ? parts[0].split(':') : [];
149
+ const right = parts[1] ? parts[1].split(':') : [];
150
+ const missing = 8 - left.length - right.length;
151
+ if (missing < 0)
152
+ return null;
153
+ cleanIp = [...left, ...Array(missing).fill('0'), ...right].join(':');
154
+ }
155
+ const segStrs = cleanIp.split(':');
156
+ if (segStrs.length !== 8)
157
+ return null;
158
+ const segments = [];
159
+ for (const seg of segStrs) {
160
+ const num = parseInt(seg, 16);
161
+ if (isNaN(num) || num < 0 || num > 0xFFFF)
162
+ return null;
163
+ segments.push(num);
164
+ }
165
+ return { segments, isIPv4Mapped: false };
166
+ }
167
+ function isBlockedIPv6(ip) {
168
+ const parsed = parseIPv6(ip);
169
+ if (!parsed)
170
+ return { blocked: false };
171
+ if (parsed.isIPv4Mapped && parsed.mappedIPv4) {
172
+ const c = isBlockedIPv4(parsed.mappedIPv4);
173
+ if (c.blocked)
174
+ return { blocked: true, reason: `IPv4-mapped IPv6: ${c.reason}` };
175
+ }
176
+ const s = parsed.segments;
177
+ const firstFiveZero = s[0] === 0 && s[1] === 0 && s[2] === 0 && s[3] === 0 && s[4] === 0;
178
+ const embedded = `${s[6] >> 8}.${s[6] & 0xFF}.${s[7] >> 8}.${s[7] & 0xFF}`;
179
+ if (firstFiveZero && s[5] === 0xFFFF) {
180
+ const c = isBlockedIPv4(embedded);
181
+ if (c.blocked)
182
+ return { blocked: true, reason: `IPv4-mapped IPv6: ${c.reason}` };
183
+ }
184
+ if (firstFiveZero && s[5] === 0 && (s[6] !== 0 || s[7] !== 0)) {
185
+ if (!(s[6] === 0 && s[7] === 1)) {
186
+ const c = isBlockedIPv4(embedded);
187
+ if (c.blocked)
188
+ return { blocked: true, reason: `IPv4-compatible IPv6: ${c.reason}` };
189
+ }
190
+ }
191
+ // IPv4-translated (SIIT, ::ffff:0:a.b.c.d): the same embedded address one segment further along
192
+ // (the form parsed as an ordinary public IPv6 address).
193
+ if (s[0] === 0 && s[1] === 0 && s[2] === 0 && s[3] === 0 && s[4] === 0xFFFF && s[5] === 0) {
194
+ const c = isBlockedIPv4(embedded);
195
+ if (c.blocked)
196
+ return { blocked: true, reason: `IPv4-translated IPv6: ${c.reason}` };
197
+ }
198
+ // NAT64 (64:ff9b::/96 well-known, 64:ff9b:1::/48 local-use) and 6to4 (2002::/16) carry an IPv4
199
+ // address inside the IPv6 one; a private v4 reached that way is the same private host.
200
+ if (s[0] === 0x0064 && s[1] === 0xFF9B && ((s[2] === 0 && s[3] === 0 && s[4] === 0 && s[5] === 0) || s[2] === 1)) {
201
+ const c = isBlockedIPv4(embedded);
202
+ if (c.blocked)
203
+ return { blocked: true, reason: `NAT64 IPv6: ${c.reason}` };
204
+ }
205
+ if (s[0] === 0x2002) {
206
+ const c = isBlockedIPv4(`${s[1] >> 8}.${s[1] & 0xFF}.${s[2] >> 8}.${s[2] & 0xFF}`);
207
+ if (c.blocked)
208
+ return { blocked: true, reason: `6to4 IPv6: ${c.reason}` };
209
+ }
210
+ if ((s[0] & 0xFF00) === 0xFF00)
211
+ return { blocked: true, reason: 'IPv6 multicast (ff00::/8)' };
212
+ if (s.every(x => x === 0))
213
+ return { blocked: true, reason: 'IPv6 all-interfaces' };
214
+ if (s[0] === 0 && s[1] === 0 && s[2] === 0 && s[3] === 0 && s[4] === 0 && s[5] === 0 && s[6] === 0 && s[7] === 1) {
215
+ return { blocked: true, reason: 'IPv6 loopback' };
216
+ }
217
+ if ((s[0] & 0xFFC0) === 0xFE80)
218
+ return { blocked: true, reason: 'IPv6 link-local (fe80::/10)' };
219
+ if ((s[0] & 0xFE00) === 0xFC00)
220
+ return { blocked: true, reason: 'IPv6 unique-local (fc00::/7)' };
221
+ if ((s[0] & 0xFFC0) === 0xFEC0)
222
+ return { blocked: true, reason: 'IPv6 site-local' };
223
+ return { blocked: false };
224
+ }
225
+ /** True if a hostname/IP literal is an SSRF target (private / loopback / metadata). */
226
+ function isSSRFTarget(hostname) {
227
+ const lower = hostname.toLowerCase();
228
+ if (BLOCKED_HOSTNAMES.has(lower))
229
+ return true;
230
+ if (isBlockedIPv4(lower).blocked)
231
+ return true;
232
+ if (looksLikePrivateHost(lower))
233
+ return true;
234
+ const v6 = lower.replace(/^\[|\]$/g, '');
235
+ if (isBlockedIPv6(v6).blocked)
236
+ return true;
237
+ if (lower.includes(':') && isBlockedIPv6(lower).blocked)
238
+ return true;
239
+ return false;
240
+ }
241
+ /** Default Node resolver (lazy-imported so non-Node runtimes don't break at load). */
242
+ async function defaultResolveHost(hostname) {
243
+ try {
244
+ const dns = await import('node:dns/promises');
245
+ const records = await dns.lookup(hostname, { all: true });
246
+ return records.map(r => r.address);
247
+ }
248
+ catch {
249
+ return [];
250
+ }
251
+ }
252
+ /**
253
+ * Validate an egress target. Hard-rejects:
254
+ * - non-HTTPS URLs and URLs carrying credentials
255
+ * - SSRF targets (private/loopback/link-local IPs, IPv6 ULA, cloud metadata)
256
+ * - hosts that RESOLVE to a private IP (DNS rebinding at check time)
257
+ */
258
+ async function validateEgressTarget(url, opts) {
259
+ let parsed;
260
+ try {
261
+ parsed = new URL(url);
262
+ }
263
+ catch {
264
+ return { ok: false, code: 'INVALID_URL', reason: 'Invalid destination URL' };
265
+ }
266
+ if (parsed.protocol !== 'https:') {
267
+ return { ok: false, code: 'HTTPS_REQUIRED', reason: `Egress must be HTTPS, got ${parsed.protocol}`, hostname: parsed.hostname };
268
+ }
269
+ if (parsed.username || parsed.password) {
270
+ return { ok: false, code: 'CREDENTIALS_IN_URL', reason: 'Credentials in URL are not allowed', hostname: parsed.hostname };
271
+ }
272
+ const hostname = parsed.hostname;
273
+ if (isSSRFTarget(hostname)) {
274
+ return { ok: false, code: 'SSRF_BLOCKED', reason: `Blocked SSRF target: ${hostname}`, hostname };
275
+ }
276
+ // Every approval carries the addresses the transport must use.
277
+ const isIpLiteral = IPV4_REGEX.test(hostname) || hostname.includes(':');
278
+ if (!isIpLiteral) {
279
+ const resolve = opts?.resolveHost ?? defaultResolveHost;
280
+ let ips;
281
+ // A resolver may explain an empty answer by throwing (e.g. which lookups it tried); the
282
+ // reason carries that note so a caller can say what actually happened. Still refused.
283
+ let note = '';
284
+ try {
285
+ ips = await resolve(hostname);
286
+ }
287
+ catch (err) {
288
+ ips = [];
289
+ note = err instanceof Error && err.message ? ` (${err.message})` : '';
290
+ }
291
+ if (ips.length === 0 || ips.some(ip => !isValidIPAddress(ip))) {
292
+ return { ok: false, code: 'DNS_FAILED', reason: `No valid DNS addresses for ${hostname}${note}`, hostname, resolvedIps: ips };
293
+ }
294
+ for (const ip of ips) {
295
+ if (isBlockedIPv4(ip).blocked || isBlockedIPv6(ip).blocked) {
296
+ return { ok: false, code: 'SSRF_BLOCKED', reason: `Host ${hostname} resolved to blocked IP ${ip}`, hostname, resolvedIps: ips };
297
+ }
298
+ }
299
+ return { ok: true, hostname, resolvedIps: ips };
300
+ }
301
+ return { ok: true, hostname, resolvedIps: [hostname.replace(/^\[|\]$/g, '')] };
302
+ }
303
+ //# sourceMappingURL=egress-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"egress-guard.js","sourceRoot":"","sources":["../../src/core/egress-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { type HostResolver } from './egress-guard';
2
+ /** The guard refused the destination before any byte left the machine. */
3
+ export declare class EgressRefusedError extends Error {
4
+ readonly code: string;
5
+ constructor(code: string | undefined, reason: string | undefined);
6
+ }
7
+ export interface GuardedFetchOptions {
8
+ readonly fetchFn?: typeof globalThis.fetch;
9
+ readonly resolveHost?: HostResolver;
10
+ }
11
+ /**
12
+ * DNS-over-HTTPS resolvers, addressed by IP so reaching them needs no DNS. Each serves the
13
+ * dns-json shape at the path named here, and each certificate carries its IP as a SAN.
14
+ */
15
+ export declare const DOH_RESOLVERS: readonly string[];
16
+ /** Test seam: forget cached DNS-over-HTTPS answers. */
17
+ export declare function clearDohCache(): void;
18
+ export declare function resolveViaDoh(hostname: string, fetchFn?: typeof globalThis.fetch): Promise<string[]>;
19
+ /**
20
+ * A fake-IP gateway's answer: every name resolves into 198.18.0.0/15 (the benchmark range,
21
+ * RFC 2544), and the gateway maps that address back to the name when the box connects. Such an
22
+ * answer says nothing about where the name really points, so the guard cannot check it.
23
+ */
24
+ export declare function isFakeIpAnswer(ips: readonly string[]): boolean;
25
+ /**
26
+ * The resolver the guard uses for `url`, so a destination's REAL addresses are what it checks
27
+ * and pins (Security Invariant #6):
28
+ * - the box's own DNS answer, when it is a real answer (any blocked address in it refuses);
29
+ * - DNS-over-HTTPS when that answer is a fake-IP gateway's (all in 198.18.0.0/15): the real
30
+ * addresses are checked and the connection is pinned to them, never to the gateway's
31
+ * stand-in address;
32
+ * - DNS-over-HTTPS through the forward proxy when the box has no DNS at all and a proxy
33
+ * carries the request. With no proxy and no DNS: no address, refused (unknown = block).
34
+ */
35
+ export declare function egressResolver(url: string, fetchFn?: typeof globalThis.fetch): HostResolver;
36
+ /**
37
+ * No ambient dispatcher or later DNS lookup can change the approved destination.
38
+ *
39
+ * The pinned dispatcher is a `PinnedAgent` (`pinned-agent.ts`), which every Node's built-in fetch
40
+ * from 20 through 26 accepts — so the default (built-in) fetch, an injected one, and a caller's own
41
+ * wrapper that delegates to the built-in fetch all carry it, on every supported Node.
42
+ */
43
+ export declare function guardedFetch(input: Parameters<typeof globalThis.fetch>[0], init?: Parameters<typeof globalThis.fetch>[1], options?: GuardedFetchOptions): Promise<Response>;
44
+ /** Wrap an injected HTTP implementation as well as the native default. */
45
+ export declare function guardedFetcher(fetchFn?: typeof globalThis.fetch, resolveHost?: HostResolver): typeof globalThis.fetch;
46
+ //# sourceMappingURL=guarded-fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guarded-fetch.d.ts","sourceRoot":"","sources":["../../src/core/guarded-fetch.ts"],"names":[],"mappings":"AAKA,OAAO,EAAwB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEzE,0EAA0E;AAC1E,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS;CAKjE;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,YAAY,CAAC;CACrC;AAYD;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAG1C,CAAC;AAkBF,uDAAuD;AACvD,wBAAgB,aAAa,IAAI,IAAI,CAAsB;AAE3D,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,GAChC,OAAO,CAAC,MAAM,EAAE,CAAC,CA+BnB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAM9D;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,GAAG,YAAY,CAsC3F;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAC7C,IAAI,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAC7C,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,QAAQ,CAAC,CAsCnB;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,YAAY,GAAG,OAAO,UAAU,CAAC,KAAK,CAGrH"}