httpfetcher: redirect-to-private-IP SSRF and semaphore release are untested #78
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Verified against
mainat61f42e6.internal/httpfetcherhas 15 tests, but they cover helpers in isolation (DefaultConfig,isAllowedContentType,extractHost,isLocalhost,isPrivateIP,validateURLvariants,limitedReader, the mock). The paths that actually carry risk have no coverage at all:The
CheckRedirectvalidator (httpfetcher.go:133-143) — i.e. redirect-based SSRF. An attacker who controls an allowlisted or signable host can return302 Location: http://169.254.169.254/...and, if this validator is wrong, pixa follows it. The initial-URL check is well tested; the redirect check, which is the easier one to get wrong, is not tested at all. This is the highest-value gap in the package.Per-host semaphore release on the failure path (
:186-192) andsemaphoreReleasingReadCloser(:466-477) — a leaked slot permanently reduces capacity for that host until restart. Silent, cumulative, and invisible until the host stops being fetchable.MaxResponseSizeenforcement end-to-end throughFetch—limitedReaderis unit-tested, but not thatFetchactually applies it.Non-2xx and disallowed-content-type rejection through
Fetch.ssrfSafeDialer— the authoritative check that closes the DNS-rebinding window. Untested.These need an
httptest.Server, which is why they were presumably skipped.Definition of done
httptest.Serverreturning a 302 to a private/link-local address; assert the fetch is refused withErrSSRFBlockedand that no connection to the private address is attempted. Include a redirect chain and a redirect to a public address (which must still succeed).Fetchreturns an error, when the response body is closed, and when the body is abandoned without a full read. Assert by saturating the semaphore and confirming a subsequent fetch still proceeds.MaxResponseSizetest: a server returning more than the limit results in a bounded read and a clear error, not a truncated success.Fetchpath.ssrfSafeDialertest proving the dial-time re-resolution actually blocks a host whose DNS answer changes between validation and dial (rebinding), if that can be simulated with a custom resolver; if not, document why and cover what can be.make checkgreen (suite runs with-raceas of #55).Related
The blocked-networks issue extends
isPrivateIP's coverage; these tests should be written so new ranges are easy to add to the table.