v0.6.2 — guard the guard
SheetDiff v0.6.2 — guard the guard
The latest audit pass attacked v0.6.1's own fixes. It won. Everything it found is fixed and regression-tested.
CRITICAL: the SSRF guard was bypassable
The WHATWG URL parser canonicalizes dotted IPv4-mapped IPv6 literals to hex — new URL("http://[::ffff:127.0.0.1]/").hostname is [::ffff:7f00:1] — so v0.6.1's dotted-only unwrap never fired on a URL-derived hostname. The auditor demonstrated end-to-end, with a live listener, that ::ffff:7f00:1 reached loopback with the guard passing, and that ::ffff:a9fe:a9fe (cloud metadata) sailed through too.
Mapped addresses are now parsed in both spellings; unparseable ::ffff: forms are refused outright; and NAT64 (64:ff9b::/96), 6to4 (2002::/16), and CGNAT (100.64.0.0/10 — Tailscale ranges) joined the block list.
The rebinding window, closed
The guard resolved the hostname, then fetch() resolved it again independently — a rebinding DNS server could pass the check publicly and answer the fetch privately. Plain-http push URLs are now refused unless NOTIFY_ALLOW_PRIVATE_URLS=1 is set: a rebound connection can't complete TLS without a hostname-valid certificate, and LAN-ntfy users already have the opt-in. Response bodies are released so sockets pool promptly.
"Line List FIRST" no longer flips the billing basis
Compilation tabs re-list the working tabs with fewer columns (the real Line List carries 20 vs the PE tabs' 23). With raw position order, a compilation that precedes the tabs it copies owned everything — and the working tabs got classified as its copies. Measured on the real tracker: 208,961 ft placed-since and zero open holes instead of the true 55,683 ft / 28,299.
Ownership now runs richest-tab-first (column count, then position). Same-width sheets behave exactly as before. Verified on the real file: Line List first, last, or absent now all produce identical billing packets.
Also
- Import-quiet push suppression is per-tab — an import between one tab's base and now silences only that tab's echoes; genuine changes on clean tabs still notify.
- The 0.6.1 "honesty" fixes actually render now (they didn't): failed test pushes and invalid URLs show their messages on /settings, and an invalid entry never clears a previously saved topic.
- Pino redaction restores the
req.headerspaths 0.6.1 dropped, censorserr.response.data/err.config.headers, and its comment now tells the truth about wildcard depth. .env.exampledocumentsLOG_LEVELandNOTIFY_ALLOW_PRIVATE_URLS.
394 tests + 7 E2E, all green in CI. Node 22+.