You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+11Lines changed: 11 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,6 +21,17 @@ For a feature matrix, examples, and execution order (middleware vs retry vs inte
21
21
22
22
---
23
23
24
+
## [Unreleased]
25
+
26
+
### Changed (security defaults)
27
+
28
+
-**`OpenFetchError.toShape()` / `toJSON()`** — Response **`data`** and **`headers`** are omitted unless you pass `includeResponseData: true` / `includeResponseHeaders: true`.
29
+
-**`createCacheMiddleware`** — Cache keys **always** fold `authorization` and `cookie` unless `varyHeaderNames` is explicitly `[]`. Extra `varyHeaderNames` entries are merged with those two. The one-time `console.warn` now applies only when `varyHeaderNames: []` is explicit with auth/cookie and no custom `key`.
30
+
31
+
### Added
32
+
33
+
-**`assertSafeUrl`** on `OpenFetchConfig` — When true, runs `assertSafeHttpUrl` on the fully resolved URL before `fetch` (e.g. `createClient({ assertSafeUrl: true })`).
Or build a custom `key` and use `appendCacheKeyVaryHeaders` from the package exports. See [SECURITY.md](SECURITY.md).
114
+
Or build a custom `key` and use `appendCacheKeyVaryHeaders` from the package exports. See [SECURITY.md](https://github.com/openfetch-js/OpenFetch/blob/main/SECURITY.md).
115
115
116
116
### Retries and POST/PUT
117
117
@@ -125,18 +125,19 @@ For low-level access without consuming the body in openFetch, set `rawResponse:
125
125
126
126
### Optional URL guard (server-side)
127
127
128
-
For URLs influenced by untrusted input, call `assertSafeHttpUrl(url)` before requesting. It blocks literal private/loopback IPs for `http:`/`https:`; it does not fix DNS rebinding — see [SECURITY.md](SECURITY.md).
128
+
For URLs influenced by untrusted input, either call `assertSafeHttpUrl(url)` before requesting or enable **`assertSafeUrl: true`** on the client (defaults or per request). That blocks literal private/loopback IPs for `http:`/`https:` on the fully resolved URL; it does not fix DNS rebinding — see [SECURITY.md](https://github.com/openfetch-js/OpenFetch/blob/main/SECURITY.md).
129
129
130
130
### Errors and logging
131
131
132
-
`OpenFetchError.toShape()`omits `config.auth` andby default**redacts sensitive query parameters** in the `url` field; pass `redactSensitiveUrlQuery: false` only for trusted diagnostics. It may still include **response `data` and `headers`**. For client-facing or shared logs, use `toShape({ includeResponseData: false, includeResponseHeaders: false })`. The error instance itself can still hold full `config`; do not expose it raw.
132
+
`OpenFetchError.toShape()`/ `toJSON()` omit `config.auth` and, **by default**, omit response **`data`** and **`headers`**; pass `includeResponseData: true` / `includeResponseHeaders: true` when you need them for trusted diagnostics. By default the serialized `url`**redacts common sensitive query parameters**; pass `redactSensitiveUrlQuery: false` only in trusted environments. The error instance itself can still hold full `config`; do not expose it raw.
-**Claude Code:** add the marketplace with `claude plugin marketplace add openfetch-js/OpenFetch`, then `claude plugin install openfetch@openfetch-js` (see the plugin bundle in [`openfetchskill/`](openfetchskill/README.md)).
Copy file name to clipboardExpand all lines: SECURITY.md
+6-8Lines changed: 6 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@ openfetch is a thin `fetch` wrapper. Callers supply URLs, headers, and bodies. T
7
7
-**Axios-class proxy CVEs (e.g. CVE-2025-62718 / `NO_PROXY` normalization)** — openfetch does **not** implement axios-style `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` matching. Outbound routing follows the host runtime’s `fetch` (and any platform proxy). Those CVEs therefore do **not** map to openfetch code paths; policy still belongs at the app, proxy, or mesh layer.
8
8
9
9
-**Network trust** — You choose endpoints. Blocking private IPs, metadata hosts, or open redirects is an **application** concern for partially trusted URLs.
10
-
-**Secrets** — `toShape()` on `OpenFetchError`avoids echoing `config.auth`, but the full `Error` object may still carry `config` (including credentials). Response bodies and headers in `toShape()`may still contain tokens or PII; use `toShape({ includeResponseData: false, includeResponseHeaders: false })` when serializing for untrusted clients or broad logs. By default, `toShape()`also **redacts common sensitive query parameters** in the serialized `url` (for example `token`, `code`, `password`); use `redactSensitiveUrlQuery: false` only for trusted diagnostics. The `debug()` plugin applies the same redaction to logged URLs. Never send raw errors to untrusted clients without redaction.
10
+
-**Secrets** — `toShape()`/ `toJSON()`on `OpenFetchError`omit `config.auth` and, **by default**, omit response **`data`** and **`headers`** (pass `includeResponseData: true` / `includeResponseHeaders: true` only for trusted diagnostics). The live `Error` instance may still carry full `config` and `response`; never expose it raw to untrusted clients. By default, `toShape()`**redacts common sensitive query parameters** in the serialized `url` (for example `token`, `code`, `password`); use `redactSensitiveUrlQuery: false` only for trusted diagnostics. The `debug()` plugin applies the same redaction to logged URLs.
11
11
-**Supply chain** — Install this package from npm or a verified Git tag; verify integrity with your package manager.
12
12
13
13
## Server-side usage and SSRF
@@ -17,24 +17,22 @@ When a URL (or part of it) comes from user input or another untrusted source on
17
17
Mitigations (combine as appropriate):
18
18
19
19
-**Allowlist** hostnames or full URL prefixes your backend is allowed to call.
20
-
-**Block literal private IPs** — Use the optional helper `assertSafeHttpUrl(url)` before issuing the request. It rejects `http`/`https` URLs whose host is a loopback, private, link-local, or IPv4-mapped private address. On runtimes that use the WHATWG URL parser (including Node.js), hosts written as **decimal integers**, **hex/octal IPv4 segments**, or **shorthand** forms (for example `127.1`) are **normalized** to dotted-quad literals before `hostname` is read; `assertSafeHttpUrl` still applies its checks to that normalized host. It does **not** stop a public hostname from resolving to an internal IP (DNS rebinding); resolve and validate in a controlled resolver or use an outbound proxy.
20
+
-**Block literal private IPs** — Call `assertSafeHttpUrl(url)` before issuing the request, or set **`assertSafeUrl: true`** on `createClient` / per request so the fully resolved URL is checked automatically inside the dispatcher. The helper rejects `http`/`https` URLs whose host is a loopback, private, link-local, or IPv4-mapped private address. On runtimes that use the WHATWG URL parser (including Node.js), hosts written as **decimal integers**, **hex/octal IPv4 segments**, or **shorthand** forms (for example `127.1`) are **normalized** to dotted-quad literals before `hostname` is read; `assertSafeHttpUrl` still applies its checks to that normalized host. It does **not** stop a public hostname from resolving to an internal IP (DNS rebinding); resolve and validate in a controlled resolver or use an outbound proxy.
21
21
-**Egress controls** — Route outbound HTTP through a proxy or service mesh that enforces policy.
22
22
23
23
## Memory cache and multi-tenant / authenticated traffic
24
24
25
-
`createCacheMiddleware`defaults to a cache key of``METHOD fullUrl`` (plus optional custom `key`). That key does **not** include `Authorization`, `Cookie`, or other `Vary` inputs unless you add them.
25
+
`createCacheMiddleware`builds a cache key from``METHOD fullUrl`` (plus optional custom `key`). **By default**, `authorization` and `cookie` request header values are folded into the key (and any extra names you pass in `varyHeaderNames` are merged with those two), so authenticated GETs do not share entries across different credentials.
26
26
27
-
**Risk:**In a BFF, SSR, or shared worker, the first successful response for a URL can be served to **other** callers until the entry expires — cross-user or cross-tenant data leakage.
27
+
**Risk:**If you set **`varyHeaderNames: []`** explicitly, the key is URL-only; the first successful response for that URL can be served to **other** callers until the entry expires.
28
28
29
29
**Mitigations:**
30
30
31
-
-Pass`varyHeaderNames: ["authorization", "cookie"]` (and any other headers your origin varies on), **or**
31
+
-Omit`varyHeaderNames` (secure default), or pass additional header names (they are merged with `authorization` and `cookie`), **or**
32
32
- Provide a custom `key` that incorporates a stable tenant or session identifier, **or**
33
33
- Use `appendCacheKeyVaryHeaders` when building a custom key.
34
34
35
-
Unauthenticated, fully public GETs may keep the default key.
36
-
37
-
The middleware emits a **one-time `console.warn`** the first time it sees `Authorization` or `Cookie` on a cacheable request while `varyHeaderNames` is empty and no custom `key` is set. Suppress with `suppressAuthCacheKeyWarning: true` when you know the cache is safe (for example anonymous-only endpoints).
35
+
The middleware emits a **one-time `console.warn`** the first time it sees `Authorization` or `Cookie` while `varyHeaderNames` was explicitly set to `[]` and no custom `key` is set. Suppress with `suppressAuthCacheKeyWarning: true` when that configuration is intentional (for example anonymous-only CDN).
0 commit comments