//#region extensions/crypto/src/services/endpoint-allowlist.ts
/**
* Endpoint Allowlist — restrict outbound HTTP requests to approved hosts.
*
* Inspired by IronClaw's endpoint allowlisting pattern.
* Prevents prompt injection attacks from tricking the agent into
* exfiltrating data to attacker-controlled URLs.
*
* Usage:
*   import { guardedFetch, isAllowedEndpoint } from './endpoint-allowlist.js';
*
*   // Use guardedFetch as a drop-in replacement for fetch()
*   const resp = await guardedFetch('https://api.0x.org/swap/v1/quote?...');
*
*   // Or check manually before calling fetch
*   if (!isAllowedEndpoint(url)) throw new Error('Blocked');
*/
const ALLOWED_HOSTS = new Set([
	"api.0x.org",
	"api.1inch.dev",
	"apiv5.paraswap.io",
	"api.odos.xyz",
	"aggregator-api.kyberswap.com",
	"open-api.openocean.finance",
	"api.dexscreener.com",
	"api.coingecko.com",
	"pro-api.coingecko.com",
	"pro-api.coinmarketcap.com",
	"coins.llama.fi",
	"yields.llama.fi",
	"public-api.birdeye.so",
	"base-mainnet.g.alchemy.com",
	"eth-mainnet.g.alchemy.com",
	"arb-mainnet.g.alchemy.com",
	"opt-mainnet.g.alchemy.com",
	"polygon-mainnet.g.alchemy.com",
	"base.llamarpc.com",
	"eth.llamarpc.com",
	"arbitrum.llamarpc.com",
	"optimism.llamarpc.com",
	"polygon.llamarpc.com",
	"mainnet.base.org",
	"base.drpc.org",
	"base.meowrpc.com",
	"1rpc.io",
	"ethereum.publicnode.com",
	"eth.drpc.org",
	"rpc.ankr.com",
	"arb1.arbitrum.io",
	"arbitrum.drpc.org",
	"mainnet.optimism.io",
	"optimism.drpc.org",
	"polygon-rpc.com",
	"polygon.drpc.org",
	"api.basescan.org",
	"api.etherscan.io",
	"api.arbiscan.io",
	"api-optimistic.etherscan.io",
	"api.polygonscan.com",
	"li.quest",
	"api.bankr.bot",
	"llm.bankr.bot",
	"clawn.ch",
	"api.clawn.ch",
	"relay.walletconnect.com",
	"relay.walletconnect.org",
	"explorer-api.walletconnect.com",
	"herdintelligence.com",
	"api.herdintelligence.com",
	"api.molten.gg",
	"api.wayfinder.ai",
	"api.reservoir.tools",
	"api-base.reservoir.tools",
	"api-arbitrum.reservoir.tools",
	"api-optimism.reservoir.tools",
	"api-polygon.reservoir.tools",
	"hub.snapshot.org",
	"api.tally.xyz",
	"api.neynar.com",
	"safe-transaction-mainnet.safe.global",
	"safe-transaction-base.safe.global",
	"safe-transaction-arbitrum.safe.global",
	"safe-transaction-optimism.safe.global",
	"safe-transaction-polygon.safe.global",
	"claims.eigenfoundation.org",
	"www.layerzero.foundation",
	"api.twitter.com",
	"api.x.com",
	"api.telegram.org",
	"api.machines.dev",
	"api.fly.io"
]);
const EXACT_ONLY_HOSTS = new Set(["localhost", "127.0.0.1"]);
const _userHosts = /* @__PURE__ */ new Set();
let _allowlistMode = process.env.OPENCLAWNCH_ALLOWLIST_MODE ?? "enforce";
function loadUserHosts() {
	const extra = process.env.OPENCLAWNCH_ALLOWED_HOSTS;
	if (extra) for (const h of extra.split(",")) {
		const trimmed = h.trim().toLowerCase();
		if (trimmed) _userHosts.add(trimmed);
	}
}
loadUserHosts();
/**
* Check if a URL targets an allowed host.
* Returns true if the host is in the allowlist.
*/
function isAllowedEndpoint(urlOrHost) {
	try {
		let host;
		if (urlOrHost.includes("://")) host = new URL(urlOrHost).hostname.toLowerCase();
		else host = urlOrHost.split(":")[0].toLowerCase();
		if (EXACT_ONLY_HOSTS.has(host)) return true;
		if (ALLOWED_HOSTS.has(host) || _userHosts.has(host)) return true;
		for (const allowed of ALLOWED_HOSTS) if (host.endsWith(`.${allowed}`)) return true;
		for (const allowed of _userHosts) if (host.endsWith(`.${allowed}`)) return true;
		return false;
	} catch {
		return false;
	}
}
/**
* A guarded fetch wrapper that blocks requests to non-allowlisted hosts.
* Drop-in replacement for global `fetch()`.
*
* Security features:
* - Blocks requests to non-allowlisted hosts
* - Prevents redirect-based allowlist bypass (redirect: 'manual')
* - Mode locked at startup (cannot be changed via env injection)
*/
async function guardedFetch(input, init) {
	if (_allowlistMode === "off") return fetch(input, init);
	const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
	if (!isAllowedEndpoint(url)) {
		const msg = `[endpoint-allowlist] Blocked request to non-allowlisted host: ${url}`;
		if (_allowlistMode === "warn") {
			console.warn(msg);
			return fetch(input, init);
		}
		throw new EndpointBlockedError(url);
	}
	const mergedInit = {
		...init,
		redirect: "manual"
	};
	const response = await fetch(input, mergedInit);
	if (response.status >= 300 && response.status < 400) {
		const location = response.headers.get("location");
		if (location) {
			const resolvedUrl = new URL(location, url).toString();
			if (!isAllowedEndpoint(resolvedUrl)) {
				const msg = `[endpoint-allowlist] Blocked redirect to non-allowlisted host: ${resolvedUrl} (from ${url})`;
				if (_allowlistMode === "warn") {
					console.warn(msg);
					return fetch(resolvedUrl, init);
				}
				throw new EndpointBlockedError(resolvedUrl);
			}
			return guardedFetch(resolvedUrl, init);
		}
	}
	return response;
}
/**
* Add a host to the runtime allowlist (does not persist across restarts).
* Useful for dynamically discovered endpoints (e.g., user-configured RPC URLs).
*/
function addAllowedHost(host) {
	_userHosts.add(host.toLowerCase());
}
/**
* Get the full list of allowed hosts (for diagnostics).
*/
function getAllowedHosts() {
	return [
		...ALLOWED_HOSTS,
		...EXACT_ONLY_HOSTS,
		..._userHosts
	].sort();
}
var EndpointBlockedError = class extends Error {
	blockedUrl;
	constructor(url) {
		super(`Request blocked: "${url}" is not in the endpoint allowlist. If this is a legitimate endpoint, add it to OPENCLAWNCH_ALLOWED_HOSTS or the allowlist in endpoint-allowlist.ts.`);
		this.name = "EndpointBlockedError";
		this.blockedUrl = url;
	}
};
//#endregion
export { isAllowedEndpoint as i, getAllowedHosts as n, guardedFetch as r, addAllowedHost as t };

//# sourceMappingURL=endpoint-allowlist-C4ojr3jY.mjs.map