SSRF-safe recipe fetcher (#65)
CI / ci (push) Successful in 1m34s

Co-authored-by: dev_agent <dev_agent@cubecraftlabs.com>
This commit was merged in pull request #65.
This commit is contained in:
2026-10-05 12:38:58 -04:00
committed by approver_agent
parent e6fd24db0a
commit 24eee8bc61
3 changed files with 1259 additions and 1 deletions
+15 -1
View File
@@ -208,7 +208,21 @@ automatic** (D-021).
- Dinners are grounded in real recipe pages found through recipe search
(`searxng` or `brave`, per `recipe_search_settings`).
- Fetching is **SSRF-safe**.
- Fetching is **SSRF-safe** (`recipes.Fetcher`): https:// only, at every
redirect hop. Its dialer resolves the host once and refuses unless every
answer is public — every non-globally-reachable IPv4 special-purpose
range (loopback, RFC 1918, link-local, carrier-grade NAT, documentation
TEST-NETs, benchmarking 198.18/15, multicast, reserved and more) is
refused, IPv4-mapped forms included, and IPv6 must be global unicast
(2000::/3) outside documentation, 6to4, Teredo and other special ranges —
then dials the address it checked, so a
rebinding DNS answer cannot swap in a private one. Keep-alives and
environment proxies are off, so every request dials and checks afresh. At
most 3 redirects; a body over 2 MB is an error, never truncated; 10 s for
the whole fetch; a descriptive `User-Agent`; requests to one host at least
1 s apart, counted from requests actually sent (an abandoned wait holds no
slot); 2xx pages cached by URL for an hour (64 pages, oldest evicted), and
callers get their own copy of a cached body.
- Recipe data is parsed from schema.org `Recipe` JSON-LD.
- Photos are cached locally (`photos`) and processed with
`golang.org/x/image`.