mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 03:38:23 +00:00
`MintHttpClient` only trimmed trailing slashes — no scheme check, no host check — and `token.mint` comes verbatim from any pasted or posted Cashu token. Tapping Redeem on a token in someone's note therefore made the device issue HTTP requests to an arbitrary URL: `http://127.0.0.1:<port>`, LAN addresses, `169.254.169.254` (cloud metadata), any scheme at all — plus it disclosed the user's IP to whoever controlled the URL. Validation now runs in the constructor, so no caller can issue a request before it. The rule: `https://` to a public host, or `http://` to a `.onion` host, and nothing else. Onion mints matter — a blanket "https only" rule would have silently broken every Tor mint. Rejected hosts cover the private/loopback/link-local/unique-local ranges plus CGNAT, multicast, reserved and `0/8`: none is a public unicast host, so allowing them buys nothing and leaks reachability. The bypasses are what make this worth care, and each has a test: IPv4-mapped and IPv4-compatible IPv6 (`::ffff:127.0.0.1`, `::127.0.0.1`), the full `inet_aton` spellings (`2130706433`, `0177.0.0.1`, `0x7f000001`, `127.1`), trailing-dot hosts, and userinfo disguise (`https://mint.example.com@127.0.0.1/`) — handled by splitting on the LAST `@`. The host parse is hand-rolled rather than delegated to `java.net.URI`/`HttpUrl` precisely because those normalise these forms inconsistently. A mint the user added to their own wallet is exempt from the host and https rules — a self-hosted mint on a LAN is a legitimate setup, and the threat here is a *pasted, untrusted* token pointing inward, not a mint the user chose. The exemption never relaxes the scheme check. It is threaded properly rather than TODO'd: the melt path passes the wallet's known mints and marks the mint user-configured only on a match; the wallet ops and CLI pass it directly, since those URLs are the user's own. Refusal gets its own message rather than reusing the mint-error string, whose wording would have misattributed our own refusal to the mint. DNS rebinding is out of scope and noted in a comment — the check runs pre-resolution and cannot defend against a host that resolves differently on the second lookup. Verified by disabling the scheme and host checks: 11 of 20 tests fail, every rejection case among them, and every allow case still passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>