@naturalcycles/backend-lib 9.70.0 → 9.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -64,6 +64,7 @@ export function compressionMiddleware(options) {
|
|
|
64
64
|
let length;
|
|
65
65
|
let listeners = [];
|
|
66
66
|
let stream;
|
|
67
|
+
let closed = false;
|
|
67
68
|
const _end = res.end;
|
|
68
69
|
const _on = res.on;
|
|
69
70
|
const _write = res.write;
|
|
@@ -126,6 +127,17 @@ export function compressionMiddleware(options) {
|
|
|
126
127
|
addListeners(res, _on, listeners);
|
|
127
128
|
listeners = null;
|
|
128
129
|
}
|
|
130
|
+
// Release the compression stream when the response closes, freeing native
|
|
131
|
+
// zlib memory even if the client disconnected before the response finished.
|
|
132
|
+
// Registered before onHeaders so a close that happens before the stream
|
|
133
|
+
// exists is not missed (the `closed` flag below handles a stream created
|
|
134
|
+
// afterwards). Fixes CVE-2026-87776 / GHSA-vc2v-76pw-4v95.
|
|
135
|
+
// We use `stream.destroy()` (not the `destroy` npm package upstream added)
|
|
136
|
+
// because our Node target frees the zlib handle on destroy().
|
|
137
|
+
_on.call(res, 'close', function onResponseClose() {
|
|
138
|
+
closed = true;
|
|
139
|
+
stream?.destroy();
|
|
140
|
+
});
|
|
129
141
|
onHeaders(res, function onResponseHeaders() {
|
|
130
142
|
// determine if request is filtered
|
|
131
143
|
if (!filter(req, res)) {
|
|
@@ -184,6 +196,14 @@ export function compressionMiddleware(options) {
|
|
|
184
196
|
else {
|
|
185
197
|
stream = zlib.createDeflate(zlibOpts);
|
|
186
198
|
}
|
|
199
|
+
// The response already closed before the stream was created, so the close
|
|
200
|
+
// listener above has already run. Release the stream now and drop the
|
|
201
|
+
// reference so later writes fall back to the raw response.
|
|
202
|
+
if (closed) {
|
|
203
|
+
stream.destroy();
|
|
204
|
+
stream = undefined;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
187
207
|
// add buffered listeners to stream
|
|
188
208
|
addListeners(stream, stream.on.bind(stream), listeners);
|
|
189
209
|
// header fields
|
package/package.json
CHANGED
|
@@ -113,6 +113,7 @@ export function compressionMiddleware(options?: CompressionOptions): BackendRequ
|
|
|
113
113
|
let length: number | undefined
|
|
114
114
|
let listeners: [string, (...args: any[]) => void][] | null = []
|
|
115
115
|
let stream: zlib.Gzip | zlib.Deflate | zlib.BrotliCompress | zlib.ZstdCompress | undefined
|
|
116
|
+
let closed = false
|
|
116
117
|
|
|
117
118
|
const _end = res.end
|
|
118
119
|
const _on = res.on
|
|
@@ -199,6 +200,18 @@ export function compressionMiddleware(options?: CompressionOptions): BackendRequ
|
|
|
199
200
|
listeners = null
|
|
200
201
|
}
|
|
201
202
|
|
|
203
|
+
// Release the compression stream when the response closes, freeing native
|
|
204
|
+
// zlib memory even if the client disconnected before the response finished.
|
|
205
|
+
// Registered before onHeaders so a close that happens before the stream
|
|
206
|
+
// exists is not missed (the `closed` flag below handles a stream created
|
|
207
|
+
// afterwards). Fixes CVE-2026-87776 / GHSA-vc2v-76pw-4v95.
|
|
208
|
+
// We use `stream.destroy()` (not the `destroy` npm package upstream added)
|
|
209
|
+
// because our Node target frees the zlib handle on destroy().
|
|
210
|
+
_on.call(res, 'close', function onResponseClose() {
|
|
211
|
+
closed = true
|
|
212
|
+
stream?.destroy()
|
|
213
|
+
})
|
|
214
|
+
|
|
202
215
|
onHeaders(res, function onResponseHeaders() {
|
|
203
216
|
// determine if request is filtered
|
|
204
217
|
if (!filter(req, res)) {
|
|
@@ -268,6 +281,15 @@ export function compressionMiddleware(options?: CompressionOptions): BackendRequ
|
|
|
268
281
|
stream = zlib.createDeflate(zlibOpts)
|
|
269
282
|
}
|
|
270
283
|
|
|
284
|
+
// The response already closed before the stream was created, so the close
|
|
285
|
+
// listener above has already run. Release the stream now and drop the
|
|
286
|
+
// reference so later writes fall back to the raw response.
|
|
287
|
+
if (closed) {
|
|
288
|
+
stream.destroy()
|
|
289
|
+
stream = undefined
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
|
|
271
293
|
// add buffered listeners to stream
|
|
272
294
|
addListeners(stream, stream.on.bind(stream), listeners!)
|
|
273
295
|
|