mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
* feat(taps): shared address contract for receiver taps Receiver taps (#57 RTL-SDR, #56 Rayhunter, #7 TAK, #6 Meshtastic) differ from every other provider in this tree in one way that matters: the upstream address comes from the user at runtime rather than from a fixed constant, so each tap route is an SSRF surface unless the address is constrained. Today each tap would answer that question for itself, and they would disagree. parseTapAddress() is the single place the rule lives: A tap may address loopback or an RFC1918 private network, plus the `localhost` and `*.local` (mDNS) names. Nothing else. That is what a tap is for — reading your own device on your own network. A tap pointed at a public address is either a mistake or the proxy being used as a confused deputy, and both get the same answer. Link-local (169.254/16) is excluded wholesale, which covers the cloud metadata address by construction rather than by blocklist. Deliberately NOT reusing isNonGlobalIpv4() from radio/stations.js: it reports malformed input like 999.1.1.1 as non-global, which is the safe direction for the radio proxy's "is this public enough to fetch?" test but inverts into *allowing* garbage under a tap's "is this local enough to fetch?" test. A positive allowlist cannot invert that way, and the tests pin that case. Two documented non-goals so they don't read as oversights: no general hostname resolution (a name other than localhost/*.local is rejected rather than resolved-and-pinned, avoiding DNS-rebinding defence for no gain), and no IPv6 (zone-index handling is platform-specific and no tap has needed it). Scoped to the address rule alone. The recorded-fixture harness lands with the first tap, designed against a real recorded response rather than guessed at with no consumer. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01KGKBNNjPh4iQe8sviGZNPb * style: adopt the tap address contract into the format scope Mechanical. Adds server/providers/common/tap-address.js and its test to scripts/format-scope.json and applies prettier, matching the six sibling modules in server/providers/common/ that are already adopted. No behaviour change. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01KGKBNNjPh4iQe8sviGZNPb * docs(taps): state accurately which taps this contract governs The header claimed #57, #56, #7 and #6 as consumers. Only #56 (Rayhunter) actually is today: it takes ?base=host:port from the browser, which is the shape that makes a route an SSRF surface. #57 would qualify if built server-side. The other three are different shapes, and forcing them through a private-only check would be wrong rather than merely unnecessary: - Meshtastic (#6) reads a fixed public broker, mqtt.meshtastic.org. - WiGLE (#11) reads a fixed public API behind a token. - TAK (#7) takes its host from operator-set env config rather than from the browser, and a TAK server is frequently a legitimate remote host. No behaviour change; comment only. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01KGKBNNjPh4iQe8sviGZNPb * refactor(taps): move the tap address contract to src/data so both sides share it The browser needs the same answer the server does — the server as a trust boundary, the client to give immediate feedback on a typed address without a round trip. Keeping the rule under server/providers/ would have meant the client re-deriving it, which is how the Rayhunter branch already carried a hand-synced copy of the regex with a comment admitting it. #274 is open about eight independent copies of haversine, one of which had silently swapped its arguments. Shipping a new duplicated validator across a trust boundary in the same tree would be hard to defend. server/providers/gbfs.js already imports src/data/gbfsSource.js, so shared validation living in src/data with the server importing it is the established direction here rather than a new one. Pure move plus the path/docblock updates. No behaviour change; the 12 contract tests are untouched and still pass. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01KGKBNNjPh4iQe8sviGZNPb --------- Co-authored-by: Claude Opus 5 (1M context) <[email protected]> Co-authored-by: Bilawal Sidhu <[email protected]>