From d74de2bd3973af663d76b37f4530873b486cd983 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Fri, 24 Jul 2026 13:00:30 +0000 Subject: [PATCH] deps: update undici to 6.28.0 --- deps/undici/src/lib/core/request.js | 13 ++- deps/undici/src/lib/dispatcher/client-h1.js | 13 ++- deps/undici/src/lib/handler/retry-handler.js | 34 +++++++ deps/undici/src/lib/llhttp/wasm_build_env.txt | 4 +- deps/undici/src/lib/web/cookies/util.js | 88 +++++++++++++++++-- deps/undici/src/package.json | 2 +- deps/undici/undici.js | 22 ++++- src/undici_version.h | 2 +- 8 files changed, 159 insertions(+), 19 deletions(-) diff --git a/deps/undici/src/lib/core/request.js b/deps/undici/src/lib/core/request.js index 4da60667ec2902..8e7ecc73084b8b 100644 --- a/deps/undici/src/lib/core/request.js +++ b/deps/undici/src/lib/core/request.js @@ -350,7 +350,13 @@ function processHeader (request, key, val) { } else if (typeof val[i] === 'object') { throw new InvalidArgumentError(`invalid ${key} header`) } else { - arr.push(`${val[i]}`) + // Coerce primitives (and reject unsafe coercions such as functions + // with a crafted toString/Symbol.toPrimitive). + const str = `${val[i]}` + if (!isValidHeaderValue(str)) { + throw new InvalidArgumentError(`invalid ${key} header`) + } + arr.push(str) } } val = arr @@ -361,7 +367,12 @@ function processHeader (request, key, val) { } else if (val === null) { val = '' } else { + // Coerce primitives (and reject unsafe coercions such as functions + // with a crafted toString/Symbol.toPrimitive). val = `${val}` + if (!isValidHeaderValue(val)) { + throw new InvalidArgumentError(`invalid ${key} header`) + } } if (headerName === 'host') { diff --git a/deps/undici/src/lib/dispatcher/client-h1.js b/deps/undici/src/lib/dispatcher/client-h1.js index 9455517a19b15f..a801ecb36046fc 100644 --- a/deps/undici/src/lib/dispatcher/client-h1.js +++ b/deps/undici/src/lib/dispatcher/client-h1.js @@ -10,6 +10,7 @@ const { RequestContentLengthMismatchError, ResponseContentLengthMismatchError, RequestAbortedError, + InvalidArgumentError, HeadersTimeoutError, HeadersOverflowError, SocketError, @@ -993,8 +994,16 @@ function writeH1 (client, request) { } body = bodyStream.stream contentLength = bodyStream.length - } else if (util.isBlobLike(body) && request.contentType == null && body.type) { - headers.push('content-type', body.type) + } else if (util.isBlobLike(body) && request.contentType == null) { + const contentType = body.type + if (contentType) { + const contentTypeValue = `${contentType}` + if (!util.isValidHeaderValue(contentTypeValue)) { + util.errorRequest(client, request, new InvalidArgumentError('invalid content-type header')) + return false + } + headers.push('content-type', contentTypeValue) + } } if (body && typeof body.read === 'function') { diff --git a/deps/undici/src/lib/handler/retry-handler.js b/deps/undici/src/lib/handler/retry-handler.js index 5d1ccf00538766..c62a2409f3aedc 100644 --- a/deps/undici/src/lib/handler/retry-handler.js +++ b/deps/undici/src/lib/handler/retry-handler.js @@ -15,6 +15,28 @@ function calculateRetryAfterHeader (retryAfter) { return new Date(retryAfter).getTime() - current } +function validatePartialResponseContentLength (headers, range, statusCode, retryCount) { + const contentLength = headers['content-length'] + if (contentLength == null) { + return null + } + + if (!Number.isFinite(range.start) || !Number.isFinite(range.end)) { + return null + } + + const length = Number(contentLength) + const expectedLength = range.end - range.start + 1 + if (!Number.isFinite(length) || length !== expectedLength) { + return new RequestRetryError('Content-Length mismatch', statusCode, { + headers, + data: { count: retryCount } + }) + } + + return null +} + class RetryHandler { constructor (opts, handlers) { const { retryOptions, ...dispatchOpts } = opts @@ -229,6 +251,12 @@ class RetryHandler { return false } + const contentLengthError = validatePartialResponseContentLength(headers, contentRange, statusCode, this.retryCount) + if (contentLengthError != null) { + this.abort(contentLengthError) + return false + } + const { start, size, end = size - 1 } = contentRange assert(this.start === start, 'content-range mismatch') @@ -252,6 +280,12 @@ class RetryHandler { ) } + const contentLengthError = validatePartialResponseContentLength(headers, range, statusCode, this.retryCount) + if (contentLengthError != null) { + this.abort(contentLengthError) + return false + } + const { start, size, end = size - 1 } = range assert( start != null && Number.isFinite(start), diff --git a/deps/undici/src/lib/llhttp/wasm_build_env.txt b/deps/undici/src/lib/llhttp/wasm_build_env.txt index f8ac034d6e1e1a..eb2202707a5173 100644 --- a/deps/undici/src/lib/llhttp/wasm_build_env.txt +++ b/deps/undici/src/lib/llhttp/wasm_build_env.txt @@ -1,12 +1,12 @@ -> undici@6.27.0 prebuild:wasm +> undici@6.28.0 prebuild:wasm > node build/wasm.js --prebuild > docker build --platform=linux/x86_64 -t llhttp_wasm_builder -f /home/runner/work/node/node/deps/undici/src/build/Dockerfile /home/runner/work/node/node/deps/undici/src -> undici@6.27.0 build:wasm +> undici@6.28.0 build:wasm > node build/wasm.js --docker > docker run --rm -t --platform=linux/x86_64 --user 1001:1001 --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/lib/llhttp,target=/home/node/undici/lib/llhttp llhttp_wasm_builder node build/wasm.js diff --git a/deps/undici/src/lib/web/cookies/util.js b/deps/undici/src/lib/web/cookies/util.js index 254f5419e905bb..ca408e153abb2f 100644 --- a/deps/undici/src/lib/web/cookies/util.js +++ b/deps/undici/src/lib/web/cookies/util.js @@ -105,7 +105,7 @@ function validateCookiePath (path) { if ( code < 0x20 || // exclude CTLs (0-31) - code === 0x7F || // DEL + code > 0x7E || // exclude DEL and non-ascii code === 0x3B // ; ) { throw new Error('Invalid cookie path') @@ -114,16 +114,80 @@ function validateCookiePath (path) { } /** - * I have no idea why these values aren't allowed to be honest, - * but Deno tests these. - Khafra + * ::= | + * + * ::= any one of the 52 alphabetic characters A through Z in + * upper case and a through z in lower case + * + * ::= any one of the ten digits 0 through 9r + * + * @see https://www.rfc-editor.org/rfc/rfc1034#section-3.5 + * @param {number} code + */ +function isLetterOrDigit (code) { + return ( + (code >= 0x30 && code <= 0x39) || // 0-9 + (code >= 0x41 && code <= 0x5A) || // A-Z + (code >= 0x61 && code <= 0x7A) // a-z + ) +} + +/** + * Validates a cookie domain against the "preferred name syntax". + * + * ::= | " " + * ::=