Harden CH filing-document proxy per review (SSRF, size guard, nits)

↗ view on GitHub · sloth-ninja · 2026-07-28 · aed9d357

Addresses independent review (REQUEST_CHANGES) of the filing-document path.

BLOCKER - bypassable host check: the `startsWith(DOCUMENT_API_BASE)` guard
on the metadata/content links passed for suffix-domain
(`...service.gov.uk.evil.com`) and userinfo (`...service.gov.uk@evil.com`)
spoofs; since Authorization is attached to the initial request, a tampered
link would exfiltrate the CH API key and give an SSRF primitive. Replaced
both checks with `isDocumentApiUrl` - `new URL(u)` + exact
`protocol === "https:" && host === "document-api.company-information.service.gov.uk"`.
Added regression tests for both exact attack URLs (rejected 404, no
key-attached fetch).

SHOULD-FIX - size guard buffered before checking: the post-`arrayBuffer`
byteLength check could balloon memory on a lying/chunked response. Now
streams the body with a running byte counter and aborts via AbortController
the moment the 25 MB cap is exceeded (Content-Length stays as a fast path).
Test: a mocked stream exceeding the cap is aborted and maps to the fixed
generic 502.

NITS: (1) allowlist the response Content-Type (application/pdf,
application/octet-stream) - anything else forced to octet-stream so an inline
disposition never renders untrusted upstream content; test added.
(2) FilingHistoryList: handle `window.open` returning null (pop-up blocked) -
revoke the blob URL immediately and show the inline error.

Docs: corrected the DURABLE_LESSONS entry to prescribe URL-parse + exact
protocol/host equality (naming both bypass shapes) instead of startsWith;
BUILD_LOG updated to reflect the hardened guard + streaming + allowlist.

Backend: tsc clean; vitest 388/388 (+3 lib tests). Frontend: tsc + eslint
clean. No new deps; no migrations touched; API key never logged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repository Sloth-ninja/JessicaOSS
Author sloth-ninja <ezanahaddis@gmail.com>
Authored
Parents 70836776
Stats 5 files changed , +290 , -53
Part of WS7: Company Search panel + saves/status trains

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-aed9d357.md from inside the repo you want the change in.

⬇ Download capture-commit-aed9d357.md