v0.1.12 — upload-file-from-url (pinned transport, opt-in)
The server-side URL fetcher held back in 0.1.11 — now with its DNS-rebinding TOCTOU closed by connection-level IP pinning. Based on the contribution by @gutencoder (#39, merged as #41).
Added
upload-file-from-url— fetch a file from a share link server-side and store it in Lexware, so a receipt already in OneDrive/SharePoint reaches the books without its bytes crossing the model context. Off by default (LEXWARE_ENABLE_URL_UPLOAD); its own capability; requires the drafts tier (does not enable it).LEXWARE_UPLOAD_ALLOWED_HOSTS— hosts the tool may fetch from (dot-boundary match; unset = Microsoft file-sharing defaults; a set value replaces them; empty blocks every host).
Security
- The connection is pinned to the address that was checked (
src/uploads/pinned-fetch.ts, onnode:https— no new dependency). The guard's one DNS resolution is both range-checked and fed to the socket, so there is no second lookup to disagree with the first. TLS is untouched: SNI and certificate validation still bind to the hostname; only the dialled address comes from the pin. - Three layers at every redirect hop: host allow-list → resolved-address range check (loopback/private/link-local/metadata/CGNAT/… in every IPv4/IPv6 spelling, fail-closed) → pinning.
- URLs with embedded credentials are refused; filenames are sanitized (the same trust boundary as the ticket flow).
Verification
- 321 tests, including real-certificate TLS tests proving pinning doesn't weaken cert validation, a raw-ClientHello SNI check, and an RFC-6761
.invalid-host connection proof. - Reviewed through multiple adversarial passes,
/code-review high, and/security-review high(no findings).
Default-off, so deploying changes no behavior until an operator sets LEXWARE_ENABLE_URL_UPLOAD=true. Full detail in CHANGELOG.md.