diff --git a/server-ce/Dockerfile b/server-ce/Dockerfile index 940b7f17a2..de4d3e634f 100644 --- a/server-ce/Dockerfile +++ b/server-ce/Dockerfile @@ -20,6 +20,10 @@ ADD services/ /overleaf/services/ ARG MONOREPO_REVISION RUN echo "monorepo-server-ce,$MONOREPO_REVISION" > /var/www/revisions.txt +# Add npm patches +# ----------------------- +ADD patches/ /overleaf/patches + # Install npm dependencies # ------------------------ ENV CYPRESS_INSTALL_BINARY=0 diff --git a/server-ce/hotfix/4.1.6/Dockerfile b/server-ce/hotfix/4.1.6/Dockerfile new file mode 100644 index 0000000000..33c19af7f7 --- /dev/null +++ b/server-ce/hotfix/4.1.6/Dockerfile @@ -0,0 +1,5 @@ +FROM sharelatex/sharelatex:4.1.5 + +# Adds missing dependency patches +ADD patches /overleaf/patches +RUN npm run postinstall diff --git a/server-ce/hotfix/4.1.6/patches/@google-cloud+storage++retry-request+5.0.2.patch b/server-ce/hotfix/4.1.6/patches/@google-cloud+storage++retry-request+5.0.2.patch new file mode 100644 index 0000000000..bbf8a8f66b --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/@google-cloud+storage++retry-request+5.0.2.patch @@ -0,0 +1,30 @@ +diff --git a/node_modules/@google-cloud/storage/node_modules/retry-request/index.js b/node_modules/@google-cloud/storage/node_modules/retry-request/index.js +index a293298..df21af6 100644 +--- a/node_modules/@google-cloud/storage/node_modules/retry-request/index.js ++++ b/node_modules/@google-cloud/storage/node_modules/retry-request/index.js +@@ -1,6 +1,6 @@ + 'use strict'; + +-const {PassThrough} = require('stream'); ++const { PassThrough, pipeline } = require('stream'); + const debug = require('debug')('retry-request'); + const extend = require('extend'); + +@@ -166,7 +166,7 @@ function retryRequest(requestOpts, opts, callback) { + }) + .on('complete', retryStream.emit.bind(retryStream, 'complete')); + +- requestStream.pipe(delayStream); ++ pipeline(requestStream, delayStream, () => {}); + } else { + activeRequest = opts.request(requestOpts, onResponse); + } +@@ -232,7 +232,7 @@ function retryRequest(requestOpts, opts, callback) { + // No more attempts need to be made, just continue on. + if (streamMode) { + retryStream.emit('response', response); +- delayStream.pipe(retryStream); ++ pipeline(delayStream, retryStream, () => {}); + requestStream.on('error', err => { + retryStream.destroy(err); + }); diff --git a/server-ce/hotfix/4.1.6/patches/@google-cloud+storage++teeny-request+8.0.2.patch b/server-ce/hotfix/4.1.6/patches/@google-cloud+storage++teeny-request+8.0.2.patch new file mode 100644 index 0000000000..738eef51dc --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/@google-cloud+storage++teeny-request+8.0.2.patch @@ -0,0 +1,50 @@ +diff --git a/node_modules/@google-cloud/storage/node_modules/teeny-request/build/src/index.js b/node_modules/@google-cloud/storage/node_modules/teeny-request/build/src/index.js +index a2251ca..e29e796 100644 +--- a/node_modules/@google-cloud/storage/node_modules/teeny-request/build/src/index.js ++++ b/node_modules/@google-cloud/storage/node_modules/teeny-request/build/src/index.js +@@ -166,27 +166,27 @@ function teenyRequest(reqOpts, callback) { + } + if (callback === undefined) { + // Stream mode +- const requestStream = streamEvents(new stream_1.PassThrough()); +- // eslint-disable-next-line @typescript-eslint/no-explicit-any +- let responseStream; +- requestStream.once('reading', () => { +- if (responseStream) { +- responseStream.pipe(requestStream); +- } +- else { +- requestStream.once('response', () => { +- responseStream.pipe(requestStream); +- }); +- } +- }); ++ const requestStream = new stream_1.PassThrough(); ++ // // eslint-disable-next-line @typescript-eslint/no-explicit-any ++ // let responseStream; ++ // requestStream.once('reading', () => { ++ // if (responseStream) { ++ // responseStream.pipe(requestStream); ++ // } ++ // else { ++ // requestStream.once('response', () => { ++ // responseStream.pipe(requestStream); ++ // }); ++ // } ++ // }); + options.compress = false; + teenyRequest.stats.requestStarting(); + (0, node_fetch_1.default)(uri, options).then(res => { +- teenyRequest.stats.requestFinished(); +- responseStream = res.body; +- responseStream.on('error', (err) => { +- requestStream.emit('error', err); +- }); ++ teenyRequest.stats.requestFinished(); stream_1.pipeline(res.body, requestStream, () => {}); ++ // responseStream = res.body; ++ // responseStream.on('error', (err) => { ++ // requestStream.emit('error', err); ++ // }); + const response = fetchToRequestResponse(options, res); + requestStream.emit('response', response); + }, err => { diff --git a/server-ce/hotfix/4.1.6/patches/README.md b/server-ce/hotfix/4.1.6/patches/README.md new file mode 100644 index 0000000000..c158e27699 --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/README.md @@ -0,0 +1 @@ +The patches in this folder are applied by `patch-package` to dependencies, particularly those which need changes that are difficult to apply upstream. diff --git a/server-ce/hotfix/4.1.6/patches/body-parser+1.20.1.patch b/server-ce/hotfix/4.1.6/patches/body-parser+1.20.1.patch new file mode 100644 index 0000000000..b41d212440 --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/body-parser+1.20.1.patch @@ -0,0 +1,44 @@ +diff --git a/node_modules/body-parser/lib/read.js b/node_modules/body-parser/lib/read.js +index fce6283..6131c31 100644 +--- a/node_modules/body-parser/lib/read.js ++++ b/node_modules/body-parser/lib/read.js +@@ -18,7 +18,7 @@ var iconv = require('iconv-lite') + var onFinished = require('on-finished') + var unpipe = require('unpipe') + var zlib = require('zlib') +- ++var Stream = require('stream') + /** + * Module exports. + */ +@@ -166,25 +166,25 @@ function contentstream (req, debug, inflate) { + case 'deflate': + stream = zlib.createInflate() + debug('inflate body') +- req.pipe(stream) ++ // req.pipe(stream) + break + case 'gzip': + stream = zlib.createGunzip() + debug('gunzip body') +- req.pipe(stream) ++ // req.pipe(stream) + break + case 'identity': + stream = req + stream.length = length +- break ++ return req + default: + throw createError(415, 'unsupported content encoding "' + encoding + '"', { + encoding: encoding, + type: 'encoding.unsupported' + }) + } +- +- return stream ++ var pass = new Stream.PassThrough(); Stream.pipeline(req, stream, pass, () => {}) ++ return pass + } + + /** diff --git a/server-ce/hotfix/4.1.6/patches/express++finalhandler+1.2.0.patch b/server-ce/hotfix/4.1.6/patches/express++finalhandler+1.2.0.patch new file mode 100644 index 0000000000..a7e0db2c9f --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/express++finalhandler+1.2.0.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/express/node_modules/finalhandler/index.js b/node_modules/express/node_modules/finalhandler/index.js +index f628e42..72f17d6 100644 +--- a/node_modules/express/node_modules/finalhandler/index.js ++++ b/node_modules/express/node_modules/finalhandler/index.js +@@ -125,7 +125,7 @@ function finalhandler (req, res, options) { + // cannot actually respond + if (headersSent(res)) { + debug('cannot %d after headers sent', status) +- req.socket.destroy() ++ if (req.socket) req.socket.destroy() + return + } + diff --git a/server-ce/hotfix/4.1.6/patches/express++send+0.18.0.patch b/server-ce/hotfix/4.1.6/patches/express++send+0.18.0.patch new file mode 100644 index 0000000000..323c6eace0 --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/express++send+0.18.0.patch @@ -0,0 +1,57 @@ +diff --git a/node_modules/express/node_modules/send/index.js b/node_modules/express/node_modules/send/index.js +index 89afd7e..de56daf 100644 +--- a/node_modules/express/node_modules/send/index.js ++++ b/node_modules/express/node_modules/send/index.js +@@ -789,29 +789,29 @@ SendStream.prototype.stream = function stream (path, options) { + // pipe + var stream = fs.createReadStream(path, options) + this.emit('stream', stream) +- stream.pipe(res) +- +- // cleanup +- function cleanup () { +- destroy(stream, true) +- } +- +- // response finished, cleanup +- onFinished(res, cleanup) +- +- // error handling +- stream.on('error', function onerror (err) { +- // clean up stream early +- cleanup() +- +- // error +- self.onStatError(err) +- }) +- +- // end +- stream.on('end', function onend () { +- self.emit('end') +- }) ++ Stream.pipeline(stream, res, err => { if (err) { self.onStatError(err) } else { self.emit('end') } }) ++ ++ // // cleanup ++ // function cleanup () { ++ // destroy(stream, true) ++ // } ++ // ++ // // response finished, cleanup ++ // onFinished(res, cleanup) ++ // ++ // // error handling ++ // stream.on('error', function onerror (err) { ++ // // clean up stream early ++ // cleanup() ++ // ++ // // error ++ // self.onStatError(err) ++ // }) ++ // ++ // // end ++ // stream.on('end', function onend () { ++ // self.emit('end') ++ // }) + } + + /** diff --git a/server-ce/hotfix/4.1.6/patches/finalhandler+1.1.2.patch b/server-ce/hotfix/4.1.6/patches/finalhandler+1.1.2.patch new file mode 100644 index 0000000000..74f746194d --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/finalhandler+1.1.2.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/finalhandler/index.js b/node_modules/finalhandler/index.js +index 5673507..40f4684 100644 +--- a/node_modules/finalhandler/index.js ++++ b/node_modules/finalhandler/index.js +@@ -125,7 +125,7 @@ function finalhandler (req, res, options) { + // cannot actually respond + if (headersSent(res)) { + debug('cannot %d after headers sent', status) +- req.socket.destroy() ++ if (req.socket) req.socket.destroy() + return + } + diff --git a/server-ce/hotfix/4.1.6/patches/forwarded+0.2.0.patch b/server-ce/hotfix/4.1.6/patches/forwarded+0.2.0.patch new file mode 100644 index 0000000000..7c13376899 --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/forwarded+0.2.0.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/forwarded/index.js b/node_modules/forwarded/index.js +index b2b6bdd..75e6254 100644 +--- a/node_modules/forwarded/index.js ++++ b/node_modules/forwarded/index.js +@@ -46,7 +46,7 @@ function forwarded (req) { + function getSocketAddr (req) { + return req.socket + ? req.socket.remoteAddress +- : req.connection.remoteAddress ++ : req.connection && req.connection.remoteAddress + } + + /** diff --git a/server-ce/hotfix/4.1.6/patches/ngcomponent+4.1.0.patch b/server-ce/hotfix/4.1.6/patches/ngcomponent+4.1.0.patch new file mode 100644 index 0000000000..d8bc68175e --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/ngcomponent+4.1.0.patch @@ -0,0 +1,9 @@ +diff --git a/node_modules/ngcomponent/index.ts b/node_modules/ngcomponent/index.ts +index 5fe33c5..8e1c6fc 100644 +--- a/node_modules/ngcomponent/index.ts ++++ b/node_modules/ngcomponent/index.ts +@@ -1,3 +1,4 @@ ++// @ts-nocheck + import { IChangesObject } from 'angular' + import assign = require('lodash/assign') + import mapValues = require('lodash/mapValues') diff --git a/server-ce/hotfix/4.1.6/patches/node-fetch+2.6.7.patch b/server-ce/hotfix/4.1.6/patches/node-fetch+2.6.7.patch new file mode 100644 index 0000000000..87077262aa --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/node-fetch+2.6.7.patch @@ -0,0 +1,76 @@ +diff --git a/node_modules/node-fetch/lib/index.js b/node_modules/node-fetch/lib/index.js +index e5b04f1..8c80924 100644 +--- a/node_modules/node-fetch/lib/index.js ++++ b/node_modules/node-fetch/lib/index.js +@@ -545,8 +545,8 @@ function clone(instance) { + // tee instance body + p1 = new PassThrough(); + p2 = new PassThrough(); +- body.pipe(p1); +- body.pipe(p2); ++ Stream.pipeline(body, p1, () => {}); ++ Stream.pipeline(body, p2, () => {}); + // set instance body to teed body and return the other teed body + instance[INTERNALS].body = p1; + body = p2; +@@ -648,14 +648,14 @@ function writeToStream(dest, instance) { + // body is null + dest.end(); + } else if (isBlob(body)) { +- body.stream().pipe(dest); ++ Stream.pipeline(body.stream(), dest, () => {}); + } else if (Buffer.isBuffer(body)) { + // body is buffer + dest.write(body); + dest.end(); + } else { + // body is stream +- body.pipe(dest); ++ Stream.pipeline(body, dest, () => {}); + } + } + +@@ -1594,7 +1594,7 @@ function fetch(url, opts) { + res.once('end', function () { + if (signal) signal.removeEventListener('abort', abortAndFinalize); + }); +- let body = res.pipe(new PassThrough$1()); ++ let body = new PassThrough$1(); setTimeout(() => Stream.pipeline(res, body, (err) => { if (err) req.abort() }), 0); // Note: let the call-site attach event handler to "body" before we start streaming. + + const response_options = { + url: request.url, +@@ -1635,7 +1635,7 @@ function fetch(url, opts) { + + // for gzip + if (codings == 'gzip' || codings == 'x-gzip') { +- body = body.pipe(zlib.createGunzip(zlibOptions)); ++ const bodyGzip = zlib.createGunzip(zlibOptions); Stream.pipeline(body, bodyGzip, () => {}); body = bodyGzip; + response = new Response(body, response_options); + resolve(response); + return; +@@ -1645,13 +1645,13 @@ function fetch(url, opts) { + if (codings == 'deflate' || codings == 'x-deflate') { + // handle the infamous raw deflate response from old servers + // a hack for old IIS and Apache servers +- const raw = res.pipe(new PassThrough$1()); ++ const raw = new PassThrough$1(); setTimeout(() => Stream.pipeline(res, raw, () => {}), 0); // Note: delay piping into "raw" until we start piping into "body". + raw.once('data', function (chunk) { + // see http://stackoverflow.com/questions/37519828 + if ((chunk[0] & 0x0F) === 0x08) { +- body = body.pipe(zlib.createInflate()); ++ const bodyDeflate = zlib.createInflate(); Stream.pipeline(body, bodyDeflate, () => {}); body = bodyDeflate; + } else { +- body = body.pipe(zlib.createInflateRaw()); ++ const bodyDeflate = zlib.createInflateRaw(); Stream.pipeline(body, bodyDeflate, () => {}); body = bodyDeflate; + } + response = new Response(body, response_options); + resolve(response); +@@ -1661,7 +1661,7 @@ function fetch(url, opts) { + + // for br + if (codings == 'br' && typeof zlib.createBrotliDecompress === 'function') { +- body = body.pipe(zlib.createBrotliDecompress()); ++ const bodyBrotli = zlib.createBrotliDecompress(); Stream.pipeline(body, bodyBrotli, () => {}); body = bodyBrotli; + response = new Response(body, response_options); + resolve(response); + return; diff --git a/server-ce/hotfix/4.1.6/patches/passport-oauth2+1.6.1.patch b/server-ce/hotfix/4.1.6/patches/passport-oauth2+1.6.1.patch new file mode 100644 index 0000000000..25a571b449 --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/passport-oauth2+1.6.1.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/passport-oauth2/lib/utils.js b/node_modules/passport-oauth2/lib/utils.js +index 486f9e1..4584507 100644 +--- a/node_modules/passport-oauth2/lib/utils.js ++++ b/node_modules/passport-oauth2/lib/utils.js +@@ -24,7 +24,7 @@ exports.originalURL = function(req, options) { + var trustProxy = options.proxy; + + var proto = (req.headers['x-forwarded-proto'] || '').toLowerCase() +- , tls = req.connection.encrypted || (trustProxy && 'https' == proto.split(/\s*,\s*/)[0]) ++ , tls = (req.connection && req.connection.encrypted) || (trustProxy && 'https' == proto.split(/\s*,\s*/)[0]) + , host = (trustProxy && req.headers['x-forwarded-host']) || req.headers.host + , protocol = tls ? 'https' : 'http' + , path = req.url || ''; diff --git a/server-ce/hotfix/4.1.6/patches/react2angular+4.0.6.patch b/server-ce/hotfix/4.1.6/patches/react2angular+4.0.6.patch new file mode 100644 index 0000000000..afcf627f67 --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/react2angular+4.0.6.patch @@ -0,0 +1,9 @@ +diff --git a/node_modules/react2angular/index.tsx b/node_modules/react2angular/index.tsx +index 5cee831..a07e040 100644 +--- a/node_modules/react2angular/index.tsx ++++ b/node_modules/react2angular/index.tsx +@@ -1,3 +1,4 @@ ++// @ts-nocheck + import { IAugmentedJQuery, IComponentOptions } from 'angular' + import fromPairs = require('lodash.frompairs') + import NgComponent from 'ngcomponent' diff --git a/server-ce/hotfix/4.1.6/patches/retry-request+4.2.2.patch b/server-ce/hotfix/4.1.6/patches/retry-request+4.2.2.patch new file mode 100644 index 0000000000..f3096b54d4 --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/retry-request+4.2.2.patch @@ -0,0 +1,30 @@ +diff --git a/node_modules/retry-request/index.js b/node_modules/retry-request/index.js +index 6cd6f65..39efb89 100644 +--- a/node_modules/retry-request/index.js ++++ b/node_modules/retry-request/index.js +@@ -1,6 +1,6 @@ + 'use strict'; + +-var { PassThrough } = require('stream'); ++var { PassThrough, pipeline } = require('stream'); + var debug = require('debug')('retry-request'); + var extend = require('extend'); + +@@ -164,7 +164,7 @@ function retryRequest(requestOpts, opts, callback) { + }) + .on('complete', retryStream.emit.bind(retryStream, 'complete')); + +- requestStream.pipe(delayStream); ++ pipeline(requestStream, delayStream, () => {}); + } else { + activeRequest = opts.request(requestOpts, onResponse); + } +@@ -220,7 +220,7 @@ function retryRequest(requestOpts, opts, callback) { + // No more attempts need to be made, just continue on. + if (streamMode) { + retryStream.emit('response', response); +- delayStream.pipe(retryStream); ++ pipeline(delayStream, retryStream, () => {}); + requestStream.on('error', function (err) { + retryStream.destroy(err); + }); diff --git a/server-ce/hotfix/4.1.6/patches/send+0.17.2.patch b/server-ce/hotfix/4.1.6/patches/send+0.17.2.patch new file mode 100644 index 0000000000..1c0b779983 --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/send+0.17.2.patch @@ -0,0 +1,61 @@ +diff --git a/node_modules/send/index.js b/node_modules/send/index.js +index 06d7507..8854216 100644 +--- a/node_modules/send/index.js ++++ b/node_modules/send/index.js +@@ -795,31 +795,31 @@ SendStream.prototype.stream = function stream (path, options) { + // pipe + var stream = fs.createReadStream(path, options) + this.emit('stream', stream) +- stream.pipe(res) +- +- // response finished, done with the fd +- onFinished(res, function onfinished () { +- finished = true +- destroy(stream) +- }) +- +- // error handling code-smell +- stream.on('error', function onerror (err) { +- // request already finished +- if (finished) return +- +- // clean up stream +- finished = true +- destroy(stream) +- +- // error +- self.onStatError(err) +- }) +- +- // end +- stream.on('end', function onend () { +- self.emit('end') +- }) ++ Stream.pipeline(stream, res, err => { if (err) { self.onStatError(err) } else { self.emit('end') } }) ++ ++ // // response finished, done with the fd ++ // onFinished(res, function onfinished () { ++ // finished = true ++ // destroy(stream) ++ // }) ++ // ++ // // error handling code-smell ++ // stream.on('error', function onerror (err) { ++ // // request already finished ++ // if (finished) return ++ // ++ // // clean up stream ++ // finished = true ++ // destroy(stream) ++ // ++ // // error ++ // self.onStatError(err) ++ // }) ++ // ++ // // end ++ // stream.on('end', function onend () { ++ // self.emit('end') ++ // }) + } + + /** diff --git a/server-ce/hotfix/4.1.6/patches/teeny-request+7.1.3.patch b/server-ce/hotfix/4.1.6/patches/teeny-request+7.1.3.patch new file mode 100644 index 0000000000..213ed046e3 --- /dev/null +++ b/server-ce/hotfix/4.1.6/patches/teeny-request+7.1.3.patch @@ -0,0 +1,49 @@ +diff --git a/node_modules/teeny-request/build/src/index.js b/node_modules/teeny-request/build/src/index.js +index f209888..e9fe982 100644 +--- a/node_modules/teeny-request/build/src/index.js ++++ b/node_modules/teeny-request/build/src/index.js +@@ -166,27 +166,27 @@ function teenyRequest(reqOpts, callback) { + } + if (callback === undefined) { + // Stream mode +- const requestStream = streamEvents(new stream_1.PassThrough()); ++ const requestStream = new stream_1.PassThrough(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any +- let responseStream; +- requestStream.once('reading', () => { +- if (responseStream) { +- responseStream.pipe(requestStream); +- } +- else { +- requestStream.once('response', () => { +- responseStream.pipe(requestStream); +- }); +- } +- }); ++ // let responseStream; ++ // requestStream.once('reading', () => { ++ // if (responseStream) { ++ // responseStream.pipe(requestStream); ++ // } ++ // else { ++ // requestStream.once('response', () => { ++ // responseStream.pipe(requestStream); ++ // }); ++ // } ++ // }); + options.compress = false; + teenyRequest.stats.requestStarting(); + node_fetch_1.default(uri, options).then(res => { +- teenyRequest.stats.requestFinished(); +- responseStream = res.body; +- responseStream.on('error', (err) => { +- requestStream.emit('error', err); +- }); ++ teenyRequest.stats.requestFinished(); stream_1.pipeline(res.body, requestStream, () => {}); ++ // responseStream = res.body; ++ // responseStream.on('error', (err) => { ++ // requestStream.emit('error', err); ++ // }); + const response = fetchToRequestResponse(options, res); + requestStream.emit('response', response); + }, err => {