Skip to content

Commit 0ebdcd2

Browse files
Merge pull request #22 from fern-api/devin/1773485226-fix-undici-vulnerabilities
fix: update undici override to ^6.24.1 to resolve all security advisories
2 parents fed7ad1 + 2fa7e96 commit 0ebdcd2

3 files changed

Lines changed: 104 additions & 16 deletions

File tree

dist/index.js

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14461,6 +14461,24 @@ class SecureProxyConnectionError extends UndiciError {
1446114461
[kSecureProxyConnectionError] = true
1446214462
}
1446314463

14464+
const kMessageSizeExceededError = Symbol.for('undici.error.UND_ERR_WS_MESSAGE_SIZE_EXCEEDED')
14465+
class MessageSizeExceededError extends UndiciError {
14466+
constructor (message) {
14467+
super(message)
14468+
this.name = 'MessageSizeExceededError'
14469+
this.message = message || 'Max decompressed message size exceeded'
14470+
this.code = 'UND_ERR_WS_MESSAGE_SIZE_EXCEEDED'
14471+
}
14472+
14473+
static [Symbol.hasInstance] (instance) {
14474+
return instance && instance[kMessageSizeExceededError] === true
14475+
}
14476+
14477+
get [kMessageSizeExceededError] () {
14478+
return true
14479+
}
14480+
}
14481+
1446414482
module.exports = {
1446514483
AbortError,
1446614484
HTTPParserError,
@@ -14484,7 +14502,8 @@ module.exports = {
1448414502
ResponseExceededMaxSizeError,
1448514503
RequestRetryError,
1448614504
ResponseError,
14487-
SecureProxyConnectionError
14505+
SecureProxyConnectionError,
14506+
MessageSizeExceededError
1448814507
}
1448914508

1449014509

@@ -14562,6 +14581,10 @@ class Request {
1456214581
throw new InvalidArgumentError('upgrade must be a string')
1456314582
}
1456414583

14584+
if (upgrade && !isValidHeaderValue(upgrade)) {
14585+
throw new InvalidArgumentError('invalid upgrade header')
14586+
}
14587+
1456514588
if (headersTimeout != null && (!Number.isFinite(headersTimeout) || headersTimeout < 0)) {
1456614589
throw new InvalidArgumentError('invalid headersTimeout')
1456714590
}
@@ -14856,13 +14879,19 @@ function processHeader (request, key, val) {
1485614879
val = `${val}`
1485714880
}
1485814881

14859-
if (request.host === null && headerName === 'host') {
14882+
if (headerName === 'host') {
14883+
if (request.host !== null) {
14884+
throw new InvalidArgumentError('duplicate host header')
14885+
}
1486014886
if (typeof val !== 'string') {
1486114887
throw new InvalidArgumentError('invalid host header')
1486214888
}
1486314889
// Consumed by Client
1486414890
request.host = val
14865-
} else if (request.contentLength === null && headerName === 'content-length') {
14891+
} else if (headerName === 'content-length') {
14892+
if (request.contentLength !== null) {
14893+
throw new InvalidArgumentError('duplicate content-length header')
14894+
}
1486614895
request.contentLength = parseInt(val, 10)
1486714896
if (!Number.isFinite(request.contentLength)) {
1486814897
throw new InvalidArgumentError('invalid content-length header')
@@ -37653,17 +37682,30 @@ module.exports = {
3765337682

3765437683
const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = __nccwpck_require__(5628)
3765537684
const { isValidClientWindowBits } = __nccwpck_require__(9902)
37685+
const { MessageSizeExceededError } = __nccwpck_require__(8045)
3765637686

3765737687
const tail = Buffer.from([0x00, 0x00, 0xff, 0xff])
3765837688
const kBuffer = Symbol('kBuffer')
3765937689
const kLength = Symbol('kLength')
3766037690

37691+
// Default maximum decompressed message size: 4 MB
37692+
const kDefaultMaxDecompressedSize = 4 * 1024 * 1024
37693+
3766137694
class PerMessageDeflate {
3766237695
/** @type {import('node:zlib').InflateRaw} */
3766337696
#inflate
3766437697

3766537698
#options = {}
3766637699

37700+
/** @type {boolean} */
37701+
#aborted = false
37702+
37703+
/** @type {Function|null} */
37704+
#currentCallback = null
37705+
37706+
/**
37707+
* @param {Map<string, string>} extensions
37708+
*/
3766737709
constructor (extensions) {
3766837710
this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
3766937711
this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
@@ -37675,6 +37717,11 @@ class PerMessageDeflate {
3767537717
// payload of the message.
3767637718
// 2. Decompress the resulting data using DEFLATE.
3767737719

37720+
if (this.#aborted) {
37721+
callback(new MessageSizeExceededError())
37722+
return
37723+
}
37724+
3767837725
if (!this.#inflate) {
3767937726
let windowBits = Z_DEFAULT_WINDOWBITS
3768037727

@@ -37687,13 +37734,37 @@ class PerMessageDeflate {
3768737734
windowBits = Number.parseInt(this.#options.serverMaxWindowBits)
3768837735
}
3768937736

37690-
this.#inflate = createInflateRaw({ windowBits })
37737+
try {
37738+
this.#inflate = createInflateRaw({ windowBits })
37739+
} catch (err) {
37740+
callback(err)
37741+
return
37742+
}
3769137743
this.#inflate[kBuffer] = []
3769237744
this.#inflate[kLength] = 0
3769337745

3769437746
this.#inflate.on('data', (data) => {
37695-
this.#inflate[kBuffer].push(data)
37747+
if (this.#aborted) {
37748+
return
37749+
}
37750+
3769637751
this.#inflate[kLength] += data.length
37752+
37753+
if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) {
37754+
this.#aborted = true
37755+
this.#inflate.removeAllListeners()
37756+
this.#inflate.destroy()
37757+
this.#inflate = null
37758+
37759+
if (this.#currentCallback) {
37760+
const cb = this.#currentCallback
37761+
this.#currentCallback = null
37762+
cb(new MessageSizeExceededError())
37763+
}
37764+
return
37765+
}
37766+
37767+
this.#inflate[kBuffer].push(data)
3769737768
})
3769837769

3769937770
this.#inflate.on('error', (err) => {
@@ -37702,16 +37773,22 @@ class PerMessageDeflate {
3770237773
})
3770337774
}
3770437775

37776+
this.#currentCallback = callback
3770537777
this.#inflate.write(chunk)
3770637778
if (fin) {
3770737779
this.#inflate.write(tail)
3770837780
}
3770937781

3771037782
this.#inflate.flush(() => {
37783+
if (this.#aborted || !this.#inflate) {
37784+
return
37785+
}
37786+
3771137787
const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength])
3771237788

3771337789
this.#inflate[kBuffer].length = 0
3771437790
this.#inflate[kLength] = 0
37791+
this.#currentCallback = null
3771537792

3771637793
callback(null, full)
3771737794
})
@@ -37766,6 +37843,10 @@ class ByteParser extends Writable {
3776637843
/** @type {Map<string, PerMessageDeflate>} */
3776737844
#extensions
3776837845

37846+
/**
37847+
* @param {import('./websocket').WebSocket} ws
37848+
* @param {Map<string, string>|null} extensions
37849+
*/
3776937850
constructor (ws, extensions) {
3777037851
super()
3777137852

@@ -37908,21 +37989,20 @@ class ByteParser extends Writable {
3790837989

3790937990
const buffer = this.consume(8)
3791037991
const upper = buffer.readUInt32BE(0)
37992+
const lower = buffer.readUInt32BE(4)
3791137993

3791237994
// 2^31 is the maximum bytes an arraybuffer can contain
3791337995
// on 32-bit systems. Although, on 64-bit systems, this is
3791437996
// 2^53-1 bytes.
3791537997
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length
3791637998
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275
3791737999
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e
37918-
if (upper > 2 ** 31 - 1) {
38000+
if (upper !== 0 || lower > 2 ** 31 - 1) {
3791938001
failWebsocketConnection(this.ws, 'Received payload length > 2^31 bytes.')
3792038002
return
3792138003
}
3792238004

37923-
const lower = buffer.readUInt32BE(4)
37924-
37925-
this.#info.payloadLength = (upper << 8) + lower
38005+
this.#info.payloadLength = lower
3792638006
this.#state = parserStates.READ_DATA
3792738007
} else if (this.#state === parserStates.READ_DATA) {
3792838008
if (this.#byteOffset < this.#info.payloadLength) {
@@ -37952,7 +38032,7 @@ class ByteParser extends Writable {
3795238032
} else {
3795338033
this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => {
3795438034
if (error) {
37955-
closeWebSocketConnection(this.ws, 1007, error.message, error.message.length)
38035+
failWebsocketConnection(this.ws, error.message)
3795638036
return
3795738037
}
3795838038

@@ -38559,6 +38639,12 @@ function parseExtensions (extensions) {
3855938639
* @param {string} value
3856038640
*/
3856138641
function isValidClientWindowBits (value) {
38642+
// Must have at least one character
38643+
if (value.length === 0) {
38644+
return false
38645+
}
38646+
38647+
// Check all characters are ASCII digits
3856238648
for (let i = 0; i < value.length; i++) {
3856338649
const byte = value.charCodeAt(i)
3856438650

@@ -38567,7 +38653,9 @@ function isValidClientWindowBits (value) {
3856738653
}
3856838654
}
3856938655

38570-
return true
38656+
// Check numeric range: zlib requires windowBits in range 8-15
38657+
const num = Number.parseInt(value, 10)
38658+
return num >= 8 && num <= 15
3857138659
}
3857238660

3857338661
// https://nodejs.org/api/intl.html#detecting-internationalization-support
@@ -39046,7 +39134,7 @@ class WebSocket extends EventTarget {
3904639134
* @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol
3904739135
*/
3904839136
#onConnectionEstablished (response, parsedExtensions) {
39049-
// processResponse is called when the "responses header list has been received and initialized."
39137+
// processResponse is called when the "response's header list has been received and initialized."
3905039138
// once this happens, the connection is open
3905139139
this[kResponse] = response
3905239140

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@
3535
"vitest": "^4.0.18"
3636
},
3737
"overrides": {
38-
"undici": "^6.23.0"
38+
"undici": "^6.24.1"
3939
}
4040
}

0 commit comments

Comments
 (0)