diff --git a/deps/undici/src/README.md b/deps/undici/src/README.md index 8ccd602b5c55..169d0cab13db 100644 --- a/deps/undici/src/README.md +++ b/deps/undici/src/README.md @@ -378,7 +378,16 @@ The `body` mixins are the most common way to format the request/response body. M > The body returned from `undici.request` does not implement `.formData()`. > [!WARNING] -> Calling `body.formData()` on a fetch response causes undici to buffer and parse the entire body. Since this is dictated by the spec, `body.formData()` must only be called on responses from trusted servers. +> The body mixins `.arrayBuffer()`, `.blob()`, `.bytes()`, `.json()`, `.text()`, +> and `.formData()` buffer the entire body in memory before returning. Where +> applicable, they also decode or parse the payload and retain that +> representation in memory. Calling these methods therefore means trusting that +> the response body is small enough to fit in the available memory. Do not use +> them for responses from untrusted or user-controlled sources. Instead, consume +> the response body as a stream and enforce an application-specific size limit: +> use `response.body` for fetch responses or the `body` returned by +> `undici.request()`. For streaming decoded text, use `body.textStream()` on a +> fetch `Request` or `Response`. Example usage: diff --git a/deps/undici/src/SECURITY.md b/deps/undici/src/SECURITY.md index ad984a32f281..6fed3847fba6 100644 --- a/deps/undici/src/SECURITY.md +++ b/deps/undici/src/SECURITY.md @@ -153,16 +153,26 @@ lead to a loss of confidentiality, integrity, or availability. resources, that is not considered a vulnerability. Applications are responsible for setting appropriate limits on response sizes. -#### Calling `body.formData()` on untrusted responses - -* `body.formData()` buffers and parses the entire response body. Multipart - parsing has inherent security risks, especially when the body is supplied by - an untrusted or user-controlled server. Applications must only call - `body.formData()` on responses from trusted servers. For untrusted responses, - applications should use a dedicated streaming multipart parser and enforce - application-specific limits. Resource exhaustion or parser exposure caused by - calling `body.formData()` on untrusted responses is considered an application - responsibility, not a vulnerability in undici. +#### Calling body-consuming methods on untrusted responses + +* The `body.arrayBuffer()`, `body.blob()`, `body.bytes()`, `body.formData()`, + `body.json()`, and `body.text()` methods buffer the entire response body in + memory before returning. Where applicable, they also decode or parse the + payload and retain that representation in memory. Calling one of these + methods means the application trusts that the response is small enough to + fit in the available memory. Applications must not use these methods on + responses from untrusted or user-controlled servers. They should instead + process the response with a streaming API, such as `Response.body`, + `body.textStream()`, or the `Readable` body returned by `undici.request()`, + and enforce application-specific size limits while streaming. Resource + exhaustion caused by buffering an untrusted response is considered an + application responsibility, not a vulnerability in undici. + +* Multipart parsing has additional inherent security risks. Applications + processing untrusted multipart responses should use a dedicated streaming + multipart parser and enforce application-specific limits. Parser exposure + caused by calling `body.formData()` on an untrusted response is considered an + application responsibility, not a vulnerability in undici. #### HTTP/1.1 keep-alive with untrusted servers diff --git a/deps/undici/src/docs/docs/api/Agent.md b/deps/undici/src/docs/docs/api/Agent.md index 0c59b6eac56a..7d2a093634df 100644 --- a/deps/undici/src/docs/docs/api/Agent.md +++ b/deps/undici/src/docs/docs/api/Agent.md @@ -56,16 +56,16 @@ changes: `Infinity`, no limit is enforced. Must be a number greater than `0`. **Default:** `Infinity`. -`Agent` inherits all {PoolOptions} (and therefore all {ClientOptions}). The -per-origin {Pool} it creates uses the default unlimited `connections`, so -concurrent requests to the same origin are spread across separate {Client} -instances on separate sockets. +`Agent` inherits all {PoolOptions} (and therefore all {ClientOptions}). Each +origin gets a separate {Pool}, with `connections` acting as the maximum number +of clients that pool may create. > [!NOTE] -> Because each concurrent request to an origin may use a different {Client}, -> HTTP/2 multiplexing on a shared session does not apply unless `connections` is -> set to a small value (for example `connections: 1`). See {PoolOptions} and -> {ClientOptions} for the full set of inherited options such as `allowH2` +> For an h2-capable HTTPS origin, the per-origin pool waits for the first TLS +> connection to finish ALPN negotiation. If the server selects h2, concurrent +> requests share that session up to `maxConcurrentStreams`. If it selects +> HTTP/1.1, normal connection fan-out resumes up to `connections`. See +> {PoolOptions} and {ClientOptions} for inherited options such as `allowH2` > (default `true`) and `maxConcurrentStreams` (default `100`). ### `agent.closed` diff --git a/deps/undici/src/docs/docs/api/DiagnosticsChannel.md b/deps/undici/src/docs/docs/api/DiagnosticsChannel.md index aee300302fc1..8ff80ecdbad3 100644 --- a/deps/undici/src/docs/docs/api/DiagnosticsChannel.md +++ b/deps/undici/src/docs/docs/api/DiagnosticsChannel.md @@ -120,7 +120,8 @@ diagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) => added: v6.3.0 --> -Published after the response headers have been received. +Published after the response headers have been received. This includes a +successful CONNECT or protocol upgrade response. * `message` {Object} * `request` {Object} The same object published by @@ -128,8 +129,9 @@ Published after the response headers have been received. * `response` {Object} The response being received. * `statusCode` {number} The HTTP status code. * `statusText` {string} The HTTP status message. - * `headers` {Buffer[]} The raw response headers as an array of buffers, - alternating between header name and value. + * `headers` {Buffer[]|Object} HTTP/1.1 response headers are an array of + buffers alternating between header name and value. HTTP/2 response + headers are an object. ```mjs import diagnosticsChannel from 'node:diagnostics_channel' @@ -137,7 +139,7 @@ import diagnosticsChannel from 'node:diagnostics_channel' diagnosticsChannel.channel('undici:request:headers').subscribe(({ request, response }) => { console.log('statusCode', response.statusCode) console.log(response.statusText) - console.log(response.headers.map((x) => x.toString())) + console.log(response.headers) }) ``` @@ -168,8 +170,9 @@ diagnosticsChannel.channel('undici:request:bodyChunkReceived').subscribe(({ requ added: v6.3.0 --> -Published after the response body and trailers have been received, that is, once -the response has fully completed. +Published once the response has fully completed. After an upgraded socket has +been passed to the request handler, this event is published with an empty +`trailers` array. * `message` {Object} * `request` {Object} The same object published by diff --git a/deps/undici/src/docs/docs/api/Dispatcher.md b/deps/undici/src/docs/docs/api/Dispatcher.md index 10d4fcf1968f..c85705b4c528 100644 --- a/deps/undici/src/docs/docs/api/Dispatcher.md +++ b/deps/undici/src/docs/docs/api/Dispatcher.md @@ -466,6 +466,15 @@ example, calling `text()` after `json()` throws a `TypeError`. The body also provides `dump({ limit })`, which discards up to `limit` bytes (default `131072`) without destroying the socket. +> [!WARNING] +> The `arrayBuffer()`, `blob()`, `bytes()`, `json()`, and `text()` methods buffer +> the entire body in memory before returning. Where applicable, they also decode +> or parse the payload and retain that representation in memory. Calling these +> methods therefore means trusting that the body is small enough to fit in the +> available memory. Do not use them for bodies received from untrusted or +> user-controlled sources. Instead, process `body` as a `Readable` stream and +> enforce an application-specific size limit. + The body is always a `Readable`, even when empty. Deserializing an empty body with `json()` throws. To guard against this, verify the status code is not `204` and the `content-type` header starts with `application/json` before calling diff --git a/deps/undici/src/docs/docs/api/Errors.md b/deps/undici/src/docs/docs/api/Errors.md index 713537395b22..d390bfaeb000 100644 --- a/deps/undici/src/docs/docs/api/Errors.md +++ b/deps/undici/src/docs/docs/api/Errors.md @@ -446,6 +446,31 @@ The response returned an error status code. This is raised, for example, when th * `headers` {Object|string[]|null} The response headers. (optional) * `body` {Object|string|null} The response body. (optional) +## Class: `ProxyConnectionError` + + + +* Extends: {UndiciError} + +A connection to the proxy failed in a way that cannot be recovered on the +same connection, so the request fails instead of being retried. + +* `name` {string} Always `'ProxyConnectionError'`. +* `code` {string} Always `'UND_ERR_PRX_CONN'`. +* `cause` {Error} The underlying error that caused the proxy connection to fail. + +### `new ProxyConnectionError(cause[, message[, options]])` + +* `cause` {Error} The underlying error. (optional) +* `message` {string} The error message. (optional) +* `options` {Object} Additional `Error` options merged with `cause`. (optional) + ## Class: `SecureProxyConnectionError`