@absol-labs/agent 0.11.0 → 0.12.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -51
- package/dist/capability/invocation-capability.d.ts +2 -2
- package/dist/capability/probe-capability.d.ts +225 -0
- package/dist/capability/probe-capability.d.ts.map +1 -0
- package/dist/capability/probe-capability.js +248 -0
- package/dist/capability/probe-capability.js.map +1 -0
- package/dist/cli/args.d.ts +11 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +23 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands/claim.d.ts +32 -0
- package/dist/cli/commands/claim.d.ts.map +1 -1
- package/dist/cli/commands/claim.js +116 -28
- package/dist/cli/commands/claim.js.map +1 -1
- package/dist/cli/commands/close.d.ts.map +1 -1
- package/dist/cli/commands/close.js +35 -3
- package/dist/cli/commands/close.js.map +1 -1
- package/dist/cli/commands/invoke.d.ts +2 -0
- package/dist/cli/commands/invoke.d.ts.map +1 -1
- package/dist/cli/commands/invoke.js +2 -2
- package/dist/cli/commands/invoke.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +15 -2
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/open.d.ts.map +1 -1
- package/dist/cli/commands/open.js +58 -4
- package/dist/cli/commands/open.js.map +1 -1
- package/dist/cli/commands/pay.d.ts +34 -0
- package/dist/cli/commands/pay.d.ts.map +1 -0
- package/dist/cli/commands/pay.js +479 -0
- package/dist/cli/commands/pay.js.map +1 -0
- package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
- package/dist/cli/commands/reclaim-unverified.js +3 -2
- package/dist/cli/commands/reclaim-unverified.js.map +1 -1
- package/dist/cli/commands/reclaim.d.ts +23 -2
- package/dist/cli/commands/reclaim.d.ts.map +1 -1
- package/dist/cli/commands/reclaim.js +204 -20
- package/dist/cli/commands/reclaim.js.map +1 -1
- package/dist/cli/commands/refund.d.ts +31 -0
- package/dist/cli/commands/refund.d.ts.map +1 -0
- package/dist/cli/commands/refund.js +397 -0
- package/dist/cli/commands/refund.js.map +1 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +21 -2
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/streams.js +8 -12
- package/dist/cli/commands/streams.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +4 -1
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli/config.d.ts +44 -26
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +88 -38
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/context.d.ts +19 -0
- package/dist/cli/context.d.ts.map +1 -1
- package/dist/cli/deps.d.ts +53 -0
- package/dist/cli/deps.d.ts.map +1 -1
- package/dist/cli/deps.js +51 -1
- package/dist/cli/deps.js.map +1 -1
- package/dist/cli/errors.d.ts.map +1 -1
- package/dist/cli/errors.js +21 -1
- package/dist/cli/errors.js.map +1 -1
- package/dist/cli/escape-hatch.d.ts +6 -4
- package/dist/cli/escape-hatch.d.ts.map +1 -1
- package/dist/cli/escape-hatch.js +6 -2
- package/dist/cli/escape-hatch.js.map +1 -1
- package/dist/cli/listings.d.ts +18 -0
- package/dist/cli/listings.d.ts.map +1 -1
- package/dist/cli/listings.js +22 -12
- package/dist/cli/listings.js.map +1 -1
- package/dist/cli/preflight.d.ts +20 -14
- package/dist/cli/preflight.d.ts.map +1 -1
- package/dist/cli/preflight.js +34 -20
- package/dist/cli/preflight.js.map +1 -1
- package/dist/cli/privy.d.ts.map +1 -1
- package/dist/cli/privy.js +4 -2
- package/dist/cli/privy.js.map +1 -1
- package/dist/cli/progress.d.ts +13 -1
- package/dist/cli/progress.d.ts.map +1 -1
- package/dist/cli/progress.js +7 -1
- package/dist/cli/progress.js.map +1 -1
- package/dist/cli/reclaimability.d.ts +82 -0
- package/dist/cli/reclaimability.d.ts.map +1 -0
- package/dist/cli/reclaimability.js +80 -0
- package/dist/cli/reclaimability.js.map +1 -0
- package/dist/cli/run.d.ts +2 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +62 -21
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/scan-range.d.ts +2 -2
- package/dist/cli/scan-range.js +2 -2
- package/dist/cli/status.d.ts +106 -1
- package/dist/cli/status.d.ts.map +1 -1
- package/dist/cli/status.js +325 -44
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/wallet.d.ts +2 -2
- package/dist/cli/wallet.d.ts.map +1 -1
- package/dist/cli/wallet.js +29 -5
- package/dist/cli/wallet.js.map +1 -1
- package/dist/discovery/rate-band.d.ts +74 -0
- package/dist/discovery/rate-band.d.ts.map +1 -0
- package/dist/discovery/rate-band.js +114 -0
- package/dist/discovery/rate-band.js.map +1 -0
- package/dist/escrow-generation.d.ts +81 -0
- package/dist/escrow-generation.d.ts.map +1 -0
- package/dist/escrow-generation.js +76 -0
- package/dist/escrow-generation.js.map +1 -0
- package/dist/frameworks/agentkit.d.ts +8 -4
- package/dist/frameworks/agentkit.d.ts.map +1 -1
- package/dist/frameworks/agentkit.js +27 -10
- package/dist/frameworks/agentkit.js.map +1 -1
- package/dist/frameworks/eliza.d.ts +3 -3
- package/dist/frameworks/eliza.d.ts.map +1 -1
- package/dist/frameworks/eliza.js +15 -6
- package/dist/frameworks/eliza.js.map +1 -1
- package/dist/frameworks/langchain.d.ts.map +1 -1
- package/dist/frameworks/langchain.js +8 -0
- package/dist/frameworks/langchain.js.map +1 -1
- package/dist/gateway/caller-auth-gateway.d.ts +83 -31
- package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
- package/dist/gateway/caller-auth-gateway.js +180 -27
- package/dist/gateway/caller-auth-gateway.js.map +1 -1
- package/dist/gateway/http-server.d.ts +128 -44
- package/dist/gateway/http-server.d.ts.map +1 -1
- package/dist/gateway/http-server.js +506 -155
- package/dist/gateway/http-server.js.map +1 -1
- package/dist/gateway/probe-auth.d.ts +112 -0
- package/dist/gateway/probe-auth.d.ts.map +1 -0
- package/dist/gateway/probe-auth.js +127 -0
- package/dist/gateway/probe-auth.js.map +1 -0
- package/dist/gateway/signed-rate-bounds.d.ts +51 -0
- package/dist/gateway/signed-rate-bounds.d.ts.map +1 -0
- package/dist/gateway/signed-rate-bounds.js +94 -0
- package/dist/gateway/signed-rate-bounds.js.map +1 -0
- package/dist/gateway/x402-opener-mode.d.ts +252 -0
- package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
- package/dist/gateway/x402-opener-mode.js +772 -0
- package/dist/gateway/x402-opener-mode.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +64 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +247 -37
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/x402-tools.d.ts +109 -0
- package/dist/mcp/x402-tools.d.ts.map +1 -0
- package/dist/mcp/x402-tools.js +554 -0
- package/dist/mcp/x402-tools.js.map +1 -0
- package/dist/network.d.ts +119 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +199 -0
- package/dist/network.js.map +1 -0
- package/dist/sdk/client.d.ts +13 -0
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/client.js +15 -0
- package/dist/sdk/client.js.map +1 -1
- package/dist/sdk/invoke.d.ts +4 -0
- package/dist/sdk/invoke.d.ts.map +1 -1
- package/dist/sdk/invoke.js +2 -5
- package/dist/sdk/invoke.js.map +1 -1
- package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
- package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
- package/dist/wallet/autonomous-wallet-broker.js +27 -23
- package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
- package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
- package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
- package/dist/wallet/autonomous-wallet-protocol.js +9 -2
- package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
- package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
- package/dist/wallet/autonomous-wallet.js +3 -2
- package/dist/wallet/autonomous-wallet.js.map +1 -1
- package/dist/wallet/broker-escrows.d.ts +222 -0
- package/dist/wallet/broker-escrows.d.ts.map +1 -0
- package/dist/wallet/broker-escrows.js +36 -0
- package/dist/wallet/broker-escrows.js.map +1 -0
- package/dist/wallet/lifecycle.d.ts +21 -5
- package/dist/wallet/lifecycle.d.ts.map +1 -1
- package/dist/wallet/lifecycle.js +32 -12
- package/dist/wallet/lifecycle.js.map +1 -1
- package/dist/wallet/privy-broker-server.d.ts.map +1 -1
- package/dist/wallet/privy-broker-server.js +31 -26
- package/dist/wallet/privy-broker-server.js.map +1 -1
- package/dist/wallet/privy-session-broker.d.ts +14 -5
- package/dist/wallet/privy-session-broker.d.ts.map +1 -1
- package/dist/wallet/privy-session-broker.js +29 -17
- package/dist/wallet/privy-session-broker.js.map +1 -1
- package/dist/wallet/privy-session-provider.d.ts +3 -1
- package/dist/wallet/privy-session-provider.d.ts.map +1 -1
- package/dist/wallet/privy-session-provider.js +5 -3
- package/dist/wallet/privy-session-provider.js.map +1 -1
- package/dist/wallet/provider.d.ts.map +1 -1
- package/dist/wallet/provider.js +15 -8
- package/dist/wallet/provider.js.map +1 -1
- package/dist/x402/facilitator.d.ts +3 -30
- package/dist/x402/facilitator.d.ts.map +1 -1
- package/dist/x402/facilitator.js +0 -27
- package/dist/x402/facilitator.js.map +1 -1
- package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
- package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
- package/dist/x402-buyer/guarded-fetch.js +147 -0
- package/dist/x402-buyer/guarded-fetch.js.map +1 -0
- package/dist/x402-buyer/index.d.ts +10 -0
- package/dist/x402-buyer/index.d.ts.map +1 -0
- package/dist/x402-buyer/index.js +10 -0
- package/dist/x402-buyer/index.js.map +1 -0
- package/dist/x402-buyer/pay.d.ts +157 -0
- package/dist/x402-buyer/pay.d.ts.map +1 -0
- package/dist/x402-buyer/pay.js +366 -0
- package/dist/x402-buyer/pay.js.map +1 -0
- package/dist/x402-opener/abi.d.ts +684 -0
- package/dist/x402-opener/abi.d.ts.map +1 -0
- package/dist/x402-opener/abi.js +880 -0
- package/dist/x402-opener/abi.js.map +1 -0
- package/dist/x402-opener/facilitator-entry.d.ts +3 -0
- package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
- package/dist/x402-opener/facilitator-entry.js +40 -0
- package/dist/x402-opener/facilitator-entry.js.map +1 -0
- package/dist/x402-opener/facilitator-server.d.ts +209 -0
- package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
- package/dist/x402-opener/facilitator-server.js +762 -0
- package/dist/x402-opener/facilitator-server.js.map +1 -0
- package/dist/x402-opener/index.d.ts +14 -0
- package/dist/x402-opener/index.d.ts.map +1 -0
- package/dist/x402-opener/index.js +14 -0
- package/dist/x402-opener/index.js.map +1 -0
- package/dist/x402-opener/listing-resolver.d.ts +55 -0
- package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
- package/dist/x402-opener/listing-resolver.js +125 -0
- package/dist/x402-opener/listing-resolver.js.map +1 -0
- package/dist/x402-opener/refund-relay.d.ts +266 -0
- package/dist/x402-opener/refund-relay.d.ts.map +1 -0
- package/dist/x402-opener/refund-relay.js +1341 -0
- package/dist/x402-opener/refund-relay.js.map +1 -0
- package/dist/x402-opener/relayer-queue.d.ts +14 -0
- package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
- package/dist/x402-opener/relayer-queue.js +18 -0
- package/dist/x402-opener/relayer-queue.js.map +1 -0
- package/dist/x402-opener/scheme.d.ts +296 -0
- package/dist/x402-opener/scheme.d.ts.map +1 -0
- package/dist/x402-opener/scheme.js +1035 -0
- package/dist/x402-opener/scheme.js.map +1 -0
- package/dist/x402-opener/terms.d.ts +165 -0
- package/dist/x402-opener/terms.d.ts.map +1 -0
- package/dist/x402-opener/terms.js +304 -0
- package/dist/x402-opener/terms.js.map +1 -0
- package/dist/zktls/reclaim.d.ts +2 -2
- package/dist/zktls/t2-delivery-proof.d.ts +6 -6
- package/docs/agent-layer.md +3 -2
- package/docs/autonomous-privy-wallet.md +188 -0
- package/docs/cli.md +265 -34
- package/docs/langchain.md +5 -0
- package/docs/mcp-reference.md +91 -5
- package/docs/probe-capability.md +191 -0
- package/docs/quickstart.md +39 -4
- package/docs/threat-model.md +20 -0
- package/docs/x402.md +766 -593
- package/package.json +19 -17
- package/src/capability/probe-capability.ts +355 -0
- package/src/cli/args.ts +29 -0
- package/src/cli/commands/claim.ts +168 -33
- package/src/cli/commands/close.ts +36 -3
- package/src/cli/commands/invoke.ts +5 -2
- package/src/cli/commands/mcp.ts +18 -2
- package/src/cli/commands/open.ts +75 -4
- package/src/cli/commands/pay.ts +584 -0
- package/src/cli/commands/reclaim-unverified.ts +8 -2
- package/src/cli/commands/reclaim.ts +277 -19
- package/src/cli/commands/refund.ts +475 -0
- package/src/cli/commands/status.ts +28 -2
- package/src/cli/commands/streams.ts +10 -14
- package/src/cli/commands/watch.ts +11 -1
- package/src/cli/config.ts +139 -52
- package/src/cli/context.ts +16 -0
- package/src/cli/deps.ts +111 -1
- package/src/cli/errors.ts +24 -1
- package/src/cli/escape-hatch.ts +19 -5
- package/src/cli/listings.ts +43 -10
- package/src/cli/preflight.ts +36 -21
- package/src/cli/privy.ts +4 -2
- package/src/cli/progress.ts +21 -2
- package/src/cli/reclaimability.ts +154 -0
- package/src/cli/run.ts +77 -21
- package/src/cli/scan-range.ts +2 -2
- package/src/cli/status.ts +454 -44
- package/src/cli/wallet.ts +45 -8
- package/src/discovery/rate-band.ts +172 -0
- package/src/escrow-generation.ts +135 -0
- package/src/frameworks/agentkit.ts +45 -11
- package/src/frameworks/eliza.ts +27 -5
- package/src/frameworks/langchain.ts +14 -0
- package/src/gateway/caller-auth-gateway.ts +320 -68
- package/src/gateway/http-server.ts +717 -220
- package/src/gateway/probe-auth.ts +191 -0
- package/src/gateway/signed-rate-bounds.ts +164 -0
- package/src/gateway/x402-opener-mode.ts +1171 -0
- package/src/index.ts +82 -19
- package/src/mcp/server.ts +351 -42
- package/src/mcp/x402-tools.ts +785 -0
- package/src/network.ts +302 -0
- package/src/sdk/client.ts +25 -0
- package/src/sdk/invoke.ts +6 -5
- package/src/wallet/autonomous-wallet-broker.ts +46 -20
- package/src/wallet/autonomous-wallet-protocol.ts +11 -2
- package/src/wallet/autonomous-wallet.ts +4 -2
- package/src/wallet/broker-escrows.ts +66 -0
- package/src/wallet/lifecycle.ts +53 -18
- package/src/wallet/privy-broker-server.ts +35 -25
- package/src/wallet/privy-session-broker.ts +58 -25
- package/src/wallet/privy-session-provider.ts +7 -3
- package/src/wallet/provider.ts +20 -15
- package/src/x402/facilitator.ts +3 -43
- package/src/x402-buyer/guarded-fetch.ts +213 -0
- package/src/x402-buyer/index.ts +9 -0
- package/src/x402-buyer/pay.ts +555 -0
- package/src/x402-opener/abi.ts +881 -0
- package/src/x402-opener/facilitator-entry.ts +48 -0
- package/src/x402-opener/facilitator-server.ts +1114 -0
- package/src/x402-opener/index.ts +13 -0
- package/src/x402-opener/listing-resolver.ts +202 -0
- package/src/x402-opener/refund-relay.ts +1873 -0
- package/src/x402-opener/relayer-queue.ts +18 -0
- package/src/x402-opener/scheme.ts +1551 -0
- package/src/x402-opener/terms.ts +453 -0
- package/dist/gateway/x402-mode.d.ts +0 -123
- package/dist/gateway/x402-mode.d.ts.map +0 -1
- package/dist/gateway/x402-mode.js +0 -212
- package/dist/gateway/x402-mode.js.map +0 -1
- package/dist/x402/delivery-oracle.d.ts +0 -108
- package/dist/x402/delivery-oracle.d.ts.map +0 -1
- package/dist/x402/delivery-oracle.js +0 -174
- package/dist/x402/delivery-oracle.js.map +0 -1
- package/dist/x402/facilitator-server-entry.d.ts +0 -3
- package/dist/x402/facilitator-server-entry.d.ts.map +0 -1
- package/dist/x402/facilitator-server-entry.js +0 -27
- package/dist/x402/facilitator-server-entry.js.map +0 -1
- package/dist/x402/facilitator-server.d.ts +0 -73
- package/dist/x402/facilitator-server.d.ts.map +0 -1
- package/dist/x402/facilitator-server.js +0 -305
- package/dist/x402/facilitator-server.js.map +0 -1
- package/dist/x402/payment-requirements.d.ts +0 -106
- package/dist/x402/payment-requirements.d.ts.map +0 -1
- package/dist/x402/payment-requirements.js +0 -138
- package/dist/x402/payment-requirements.js.map +0 -1
- package/dist/x402/relayer.d.ts +0 -41
- package/dist/x402/relayer.d.ts.map +0 -1
- package/dist/x402/relayer.js +0 -64
- package/dist/x402/relayer.js.map +0 -1
- package/dist/x402/resource-index.d.ts +0 -75
- package/dist/x402/resource-index.d.ts.map +0 -1
- package/dist/x402/resource-index.js +0 -126
- package/dist/x402/resource-index.js.map +0 -1
- package/dist/x402/verified-delivery-facilitator.d.ts +0 -285
- package/dist/x402/verified-delivery-facilitator.d.ts.map +0 -1
- package/dist/x402/verified-delivery-facilitator.js +0 -423
- package/dist/x402/verified-delivery-facilitator.js.map +0 -1
- package/src/gateway/x402-mode.ts +0 -350
- package/src/x402/delivery-oracle.ts +0 -285
- package/src/x402/facilitator-server-entry.ts +0 -35
- package/src/x402/facilitator-server.ts +0 -445
- package/src/x402/payment-requirements.ts +0 -231
- package/src/x402/relayer.ts +0 -122
- package/src/x402/resource-index.ts +0 -228
- package/src/x402/verified-delivery-facilitator.ts +0 -744
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { lookup as dnsLookup } from "node:dns/promises";
|
|
2
|
+
import { isIP } from "node:net";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Outbound-request guard for x402 surfaces where the URL comes from a model
|
|
6
|
+
* (MCP `x402_pay`, `refund`, `refund_status`): without it, those tools are an
|
|
7
|
+
* SSRF probe of whatever network the server runs on.
|
|
8
|
+
*
|
|
9
|
+
* Every request is refused unless:
|
|
10
|
+
* - the URL is `https:` (or `http:` only when private networks are explicitly
|
|
11
|
+
* allowed, i.e. local testing);
|
|
12
|
+
* - its host is on the allowlist, when one is configured
|
|
13
|
+
* (`METRIK_AGENT_X402_ALLOWED_HOSTS`; `*.example.com` matches subdomains);
|
|
14
|
+
* - its host — an IP literal, or EVERY address the name resolves to — is not
|
|
15
|
+
* loopback, private, link-local, unique-local, CGNAT, multicast, reserved or
|
|
16
|
+
* unspecified.
|
|
17
|
+
*
|
|
18
|
+
* Redirects are never followed (`redirect: "manual"`; a 3xx is a refusal), so
|
|
19
|
+
* a public host cannot bounce the request — or a payment signature — onto an
|
|
20
|
+
* internal one.
|
|
21
|
+
*
|
|
22
|
+
* Limitation, stated: the address is checked at lookup time and the platform
|
|
23
|
+
* `fetch` resolves the name again when it connects, so a DNS-rebinding host
|
|
24
|
+
* with a near-zero TTL can still race the check. Use the allowlist where that
|
|
25
|
+
* matters.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export type LookupFn = (
|
|
29
|
+
hostname: string,
|
|
30
|
+
) => Promise<readonly { readonly address: string; readonly family: number }[]>;
|
|
31
|
+
|
|
32
|
+
export interface GuardedFetchOptions {
|
|
33
|
+
/** Exact hostnames, or `*.domain` for subdomains. Unset = any public host. */
|
|
34
|
+
readonly allowedHosts?: readonly string[];
|
|
35
|
+
/** Tests / local development only: permit loopback and private ranges. */
|
|
36
|
+
readonly allowPrivateNetworks?: boolean;
|
|
37
|
+
/** The fetch to wrap. Default `globalThis.fetch`. */
|
|
38
|
+
readonly fetch?: typeof fetch;
|
|
39
|
+
/** DNS resolution. Default `dns.lookup(host, { all: true })`. */
|
|
40
|
+
readonly lookup?: LookupFn;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A request the guard refused; nothing was sent. */
|
|
44
|
+
export class OutboundRequestRefusedError extends Error {
|
|
45
|
+
constructor(reason: string) {
|
|
46
|
+
super(`outbound request refused: ${reason}`);
|
|
47
|
+
this.name = "OutboundRequestRefusedError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const defaultLookup: LookupFn = async (hostname) =>
|
|
52
|
+
await dnsLookup(hostname, { all: true, verbatim: true });
|
|
53
|
+
|
|
54
|
+
function ipv4ToInt(address: string): number {
|
|
55
|
+
return address.split(".").reduce((acc, part) => acc * 256 + Number(part), 0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const BLOCKED_V4: readonly (readonly [string, number])[] = [
|
|
59
|
+
["0.0.0.0", 8],
|
|
60
|
+
["10.0.0.0", 8],
|
|
61
|
+
["100.64.0.0", 10],
|
|
62
|
+
["127.0.0.0", 8],
|
|
63
|
+
["169.254.0.0", 16],
|
|
64
|
+
["172.16.0.0", 12],
|
|
65
|
+
["192.0.0.0", 24],
|
|
66
|
+
["192.0.2.0", 24],
|
|
67
|
+
["192.168.0.0", 16],
|
|
68
|
+
["198.18.0.0", 15],
|
|
69
|
+
["198.51.100.0", 24],
|
|
70
|
+
["203.0.113.0", 24],
|
|
71
|
+
["224.0.0.0", 4],
|
|
72
|
+
["240.0.0.0", 4],
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
function blockedV4(address: string): boolean {
|
|
76
|
+
const value = ipv4ToInt(address);
|
|
77
|
+
return BLOCKED_V4.some(([base, bits]) => {
|
|
78
|
+
const mask = bits === 0 ? 0 : (~0 << (32 - bits)) >>> 0;
|
|
79
|
+
return (value & mask) >>> 0 === (ipv4ToInt(base) & mask) >>> 0;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function blockedV6(raw: string): boolean {
|
|
84
|
+
const address = raw.toLowerCase().replace(/^\[|\]$/g, "");
|
|
85
|
+
if (address === "::" || address === "::1") return true;
|
|
86
|
+
// IPv4-mapped / -compatible: judge the embedded IPv4 address.
|
|
87
|
+
const mapped = /^(?:::ffff:|::)(\d+\.\d+\.\d+\.\d+)$/.exec(address);
|
|
88
|
+
if (mapped?.[1] !== undefined) return blockedV4(mapped[1]);
|
|
89
|
+
const hexMapped = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(address);
|
|
90
|
+
if (hexMapped?.[1] !== undefined && hexMapped[2] !== undefined) {
|
|
91
|
+
const hi = parseInt(hexMapped[1], 16);
|
|
92
|
+
const lo = parseInt(hexMapped[2], 16);
|
|
93
|
+
return blockedV4(`${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`);
|
|
94
|
+
}
|
|
95
|
+
const first = parseInt(address.split(":")[0] || "0", 16);
|
|
96
|
+
return (
|
|
97
|
+
(first & 0xfe00) === 0xfc00 || // fc00::/7 unique local
|
|
98
|
+
(first & 0xffc0) === 0xfe80 || // fe80::/10 link-local
|
|
99
|
+
(first & 0xffc0) === 0xfec0 || // fec0::/10 site-local (deprecated)
|
|
100
|
+
(first & 0xff00) === 0xff00 || // ff00::/8 multicast
|
|
101
|
+
address.startsWith("64:ff9b:") || // NAT64 (embeds IPv4)
|
|
102
|
+
address.startsWith("2001:db8:") // documentation
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Whether an IP literal is in a range an outbound x402 request must not reach. */
|
|
107
|
+
export function isBlockedAddress(address: string): boolean {
|
|
108
|
+
const bare = address.replace(/^\[|\]$/g, "");
|
|
109
|
+
const family = isIP(bare);
|
|
110
|
+
if (family === 4) return blockedV4(bare);
|
|
111
|
+
if (family === 6) return blockedV6(bare);
|
|
112
|
+
return true; // not an IP: never "safe" by default
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function hostAllowed(hostname: string, allowed: readonly string[]): boolean {
|
|
116
|
+
const host = hostname.toLowerCase();
|
|
117
|
+
return allowed.some((entry) => {
|
|
118
|
+
const rule = entry.trim().toLowerCase();
|
|
119
|
+
if (rule.startsWith("*.")) return host.endsWith(rule.slice(1));
|
|
120
|
+
return host === rule;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Validate one URL against the guard; throws {@link OutboundRequestRefusedError}. */
|
|
125
|
+
export async function assertOutboundUrlAllowed(
|
|
126
|
+
raw: string | URL,
|
|
127
|
+
options: GuardedFetchOptions = {},
|
|
128
|
+
): Promise<URL> {
|
|
129
|
+
let url: URL;
|
|
130
|
+
try {
|
|
131
|
+
url = new URL(String(raw));
|
|
132
|
+
} catch {
|
|
133
|
+
throw new OutboundRequestRefusedError(`"${String(raw)}" is not a URL`);
|
|
134
|
+
}
|
|
135
|
+
const allowPrivate = options.allowPrivateNetworks === true;
|
|
136
|
+
if (
|
|
137
|
+
url.protocol !== "https:" &&
|
|
138
|
+
!(url.protocol === "http:" && allowPrivate)
|
|
139
|
+
) {
|
|
140
|
+
throw new OutboundRequestRefusedError(
|
|
141
|
+
`${url.protocol} is not allowed (https only)`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (url.username !== "" || url.password !== "") {
|
|
145
|
+
throw new OutboundRequestRefusedError("credentials in the URL");
|
|
146
|
+
}
|
|
147
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
148
|
+
if (
|
|
149
|
+
options.allowedHosts !== undefined &&
|
|
150
|
+
options.allowedHosts.length > 0 &&
|
|
151
|
+
!hostAllowed(hostname, options.allowedHosts)
|
|
152
|
+
) {
|
|
153
|
+
throw new OutboundRequestRefusedError(
|
|
154
|
+
`${hostname} is not in METRIK_AGENT_X402_ALLOWED_HOSTS`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
if (allowPrivate) return url;
|
|
158
|
+
if (isIP(hostname) !== 0) {
|
|
159
|
+
if (isBlockedAddress(hostname)) {
|
|
160
|
+
throw new OutboundRequestRefusedError(
|
|
161
|
+
`${hostname} is a loopback, private, link-local or reserved address`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return url;
|
|
165
|
+
}
|
|
166
|
+
if (hostname === "localhost" || hostname.endsWith(".localhost")) {
|
|
167
|
+
throw new OutboundRequestRefusedError(`${hostname} is loopback`);
|
|
168
|
+
}
|
|
169
|
+
let addresses: readonly { readonly address: string }[];
|
|
170
|
+
try {
|
|
171
|
+
addresses = await (options.lookup ?? defaultLookup)(hostname);
|
|
172
|
+
} catch {
|
|
173
|
+
throw new OutboundRequestRefusedError(`${hostname} did not resolve`);
|
|
174
|
+
}
|
|
175
|
+
if (addresses.length === 0) {
|
|
176
|
+
throw new OutboundRequestRefusedError(`${hostname} did not resolve`);
|
|
177
|
+
}
|
|
178
|
+
const blocked = addresses.find((entry) => isBlockedAddress(entry.address));
|
|
179
|
+
if (blocked !== undefined) {
|
|
180
|
+
throw new OutboundRequestRefusedError(
|
|
181
|
+
`${hostname} resolves to ${blocked.address}, a loopback, private, link-local or reserved address`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return url;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A `fetch` that applies {@link assertOutboundUrlAllowed} to every request and
|
|
189
|
+
* never follows a redirect.
|
|
190
|
+
*/
|
|
191
|
+
export function createGuardedFetch(
|
|
192
|
+
options: GuardedFetchOptions = {},
|
|
193
|
+
): typeof fetch {
|
|
194
|
+
const base = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
195
|
+
const guarded = async (
|
|
196
|
+
input: string | URL | Request,
|
|
197
|
+
init?: RequestInit,
|
|
198
|
+
): Promise<Response> => {
|
|
199
|
+
const target = input instanceof Request ? input.url : input;
|
|
200
|
+
await assertOutboundUrlAllowed(target, options);
|
|
201
|
+
const response = await base(input, { ...init, redirect: "manual" });
|
|
202
|
+
if (
|
|
203
|
+
response.type === "opaqueredirect" ||
|
|
204
|
+
(response.status >= 300 && response.status < 400)
|
|
205
|
+
) {
|
|
206
|
+
throw new OutboundRequestRefusedError(
|
|
207
|
+
`${new URL(String(target)).host} answered with a redirect (${response.status}), which is never followed`,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return response;
|
|
211
|
+
};
|
|
212
|
+
return guarded as typeof fetch;
|
|
213
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@absol-labs/agent/x402-buyer` — the BUYER side of Metrik x402: pay an x402
|
|
3
|
+
* v2 endpoint through `@absol-labs/sdk/x402`'s `payWithX402` with every SDK
|
|
4
|
+
* protection intact, gated by a spend mandate (`checkMandate()`) before
|
|
5
|
+
* anything is signed. Kept out of the root barrel so consumers that never use
|
|
6
|
+
* x402 do not load `@x402/*`. See docs/x402.md.
|
|
7
|
+
*/
|
|
8
|
+
export * from "./pay.js";
|
|
9
|
+
export * from "./guarded-fetch.js";
|