Expand ecash explorer capabilities
Add Fedimint discovery, dual SQLite/Postgres storage, richer review handling, and generated social imagery.
This commit is contained in:
+38
-1
@@ -26,13 +26,50 @@ function isDisallowedHost(hostname: string): boolean {
|
||||
if (hostname === 'localhost' || hostname.endsWith('.localhost')) return true;
|
||||
if (hostname.endsWith('.onion')) return true;
|
||||
if (hostname.endsWith('.local')) return true;
|
||||
if (hostname === '::1' || hostname === '[::1]') return true;
|
||||
if (PRIVATE_IPV4.test(hostname)) return true;
|
||||
if (isPrivateIpv6(hostname)) return true;
|
||||
// A bare label with no dot cannot be a public host.
|
||||
if (!hostname.includes('.') && !hostname.includes(':')) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* IPv6 forms that are loopback, link-local (fe80::/10), unique-local (fc00::/7) or an
|
||||
* IPv4-mapped address whose IPv4 part is private. The URL parser brackets an IPv6
|
||||
* hostname, so both spellings are accepted.
|
||||
*/
|
||||
function isPrivateIpv6(hostname: string): boolean {
|
||||
const bare =
|
||||
hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;
|
||||
if (!bare.includes(':')) return false;
|
||||
if (bare === '::' || bare === '::1') return true;
|
||||
if (/^f[cd]/i.test(bare)) return true;
|
||||
if (/^fe[89ab]/i.test(bare)) return true;
|
||||
const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i.exec(bare);
|
||||
if (mapped?.[1]) return PRIVATE_IPV4.test(mapped[1]) || mapped[1].startsWith('127.');
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* May the indexer fetch this URL? http(s) only, and never a private or local host.
|
||||
*
|
||||
* For URLs a mint *publishes* rather than the URL it lives at — its `icon_url` above
|
||||
* all. Those never pass through `normalizeMintUrl`, so without this check a mint's
|
||||
* /v1/info could point the indexer at a cloud metadata endpoint or anything else on
|
||||
* the API host's own network. Callers that follow redirects must re-check every hop.
|
||||
*/
|
||||
export function isFetchableUrl(value: URL | string): boolean {
|
||||
let u: URL;
|
||||
try {
|
||||
u = typeof value === 'string' ? new URL(value) : value;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
if (u.protocol !== 'https:' && u.protocol !== 'http:') return false;
|
||||
const hostname = u.hostname.toLowerCase();
|
||||
return hostname !== '' && !isDisallowedHost(hostname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a mint URL as found in a Nostr `u` tag or typed by a user.
|
||||
* Returns null when the input is not a usable public mint URL.
|
||||
|
||||
Reference in New Issue
Block a user