feat(14-04): fetchguard.IsPrivateAddr exposes the dial guard's address classification
The golem SSRF guard checks a URL's resolved addresses before it connects, as PHP's SSRFGuard does, with the same table the dial guard uses.
This commit is contained in:
@@ -18,6 +18,7 @@ Guarded outbound HTTP client and fetcher that blocks private and reserved addres
|
||||
- `fetchguard.Client` sends any method: `fetchguard.Client.Do` and `fetchguard.Client.Send` take an `http.Request`, and `fetchguard.Client.Get`, `fetchguard.Client.PostJSON`, `fetchguard.Client.PutJSON` and `fetchguard.Client.PostMultipart` cover the common shapes. `fetchguard.Bearer` builds an Authorization value. Response status and headers come back unjudged in `fetchguard.Result`.
|
||||
- Code-only transport seam: `fetchguard.WithTransport` routes a context's requests to a test `http.RoundTripper`. No policy field, config key, environment variable or header can set it.
|
||||
- The private and reserved address check runs on the resolved IP at dial time, so DNS answers that point inside the network are refused (`fetchguard.ReasonPrivateIP`); environment proxies are ignored so the check sees the real target.
|
||||
- `fetchguard.IsPrivateAddr` exposes the same address classification, for callers that check a resolved address before they connect (an allowlist guard that also refuses hosts resolving into the network, for example).
|
||||
- Redirects are never followed, in any mode: a 3xx response is returned as a successful `fetchguard.Result`, and a caller that wants to follow the Location header sends a new request, which re-runs the guard.
|
||||
- The response body is capped at the policy's byte limit (`fetchguard.ReasonTooLarge` when exceeded), with a per-call timeout. Request bodies are not capped; callers bound their own inputs.
|
||||
- Limits left at zero in the policy fall back to config keys, then to framework defaults of 10 MiB and 10 seconds (`fetchguard.Defaults`, `fetchguard.DefaultsFromConfig`).
|
||||
@@ -98,6 +99,7 @@ res, err = vendor.PostMultipart(ctx, "https://api.example.com/v1/files", header,
|
||||
| `fetchguard.FormFile` | One multipart file part (`Field`, `Filename`, `ContentType`, `Body`); the content type defaults to `application/octet-stream`. |
|
||||
| `fetchguard.Bearer` | Returns the Authorization value `Bearer <token>`. |
|
||||
| `fetchguard.WithTransport` | Returns a context whose client requests go to the given `http.RoundTripper`; the test and parity-replay seam, settable only from code. |
|
||||
| `fetchguard.IsPrivateAddr` | Reports whether an address is private, loopback, link-local, reserved or otherwise not public: the classification the dial guard applies. |
|
||||
| `fetchguard.Error` | Failure carrying a `fetchguard.Reason` and the underlying error for logging. |
|
||||
| `fetchguard.Reason` | Closed set of failure reasons (`invalid_url`, `scheme`, `unresolvable`, `private_ip`, `network_error`, `too_large`). |
|
||||
| `fetchguard.Defaults` | Framework fallback limits: 10 MiB and 10 seconds. |
|
||||
|
||||
Reference in New Issue
Block a user