diff --git a/src/lib/blockchain-api.ts b/src/lib/blockchain-api.ts index f6296f51..b668e7f7 100644 --- a/src/lib/blockchain-api.ts +++ b/src/lib/blockchain-api.ts @@ -9,11 +9,30 @@ const MEMPOOL_SPACE_API_BASE = 'https://mempool.space/api'; const ESPLORA_BASES = [BLOCKSTREAM_API_BASE, MEMPOOL_SPACE_API_BASE]; +const ALLOWED_HOSTS = new Set([ + 'blockstream.info', + 'mempool.space', + 'api.coingecko.com', + 'blockchain.info', + 'api.alternative.me', +]); + function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } export async function fetchJson(url: string, options?: RequestInit, revalidate?: number): Promise { + let parsedUrl: URL; + try { + parsedUrl = new URL(url); + } catch { + throw new Error('Invalid provider URL.'); + } + + if (parsedUrl.protocol !== 'https:' || !ALLOWED_HOSTS.has(parsedUrl.hostname)) { + throw new Error('Disallowed provider URL.'); + } + const headers: Record = { 'Accept': 'application/json', 'User-Agent': 'BitSleuth/1.0', diff --git a/src/lib/mempool.ts b/src/lib/mempool.ts index b38a209f..844d52b0 100644 --- a/src/lib/mempool.ts +++ b/src/lib/mempool.ts @@ -51,8 +51,15 @@ export async function getMempoolData(): Promise<{ data: MempoolData | null; erro export async function getBlockDetails(hash: string, startIndex: number = 0): Promise<{ data: BlockDetails | null; error: string | null; }> { try { - const blockUrl = `https://mempool.space/api/block/${hash}`; - const txsUrl = `https://mempool.space/api/block/${hash}/txs/${startIndex}`; + const normalizedHash = hash.trim(); + if (!/^[a-fA-F0-9]{64}$/.test(normalizedHash)) { + return { data: null, error: 'The block hash you entered is not valid.' }; + } + + const safeStartIndex = Number.isInteger(startIndex) && startIndex >= 0 ? startIndex : 0; + const encodedHash = encodeURIComponent(normalizedHash); + const blockUrl = `https://mempool.space/api/block/${encodedHash}`; + const txsUrl = `https://mempool.space/api/block/${encodedHash}/txs/${safeStartIndex}`; // Block data is immutable, so we can cache it for a long time. const [blockData, blockTxsData] = await Promise.all([