From 788009330cf9c3dd705ea4c4ddd7d958d712987f Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Fri, 12 Jul 2024 11:25:45 +0200 Subject: [PATCH] Merge pull request #19357 from overleaf/msm-hotfix-4-2-7 [CE/SP] Hotfix 4.2.7/5.0.7 GitOrigin-RevId: e36a5af392c2356d4068a98acc2d637d879392f8 --- server-ce/hotfix/4.2.7/Dockerfile | 13 +++++ server-ce/hotfix/4.2.7/pr_19071.patch | 37 ++++++++++++++ server-ce/hotfix/4.2.7/pr_19293.patch | 17 +++++++ server-ce/hotfix/4.2.7/pr_19296.patch | 22 +++++++++ server-ce/hotfix/4.2.7/pr_19297.patch | 70 +++++++++++++++++++++++++++ server-ce/hotfix/5.0.7/Dockerfile | 13 +++++ server-ce/hotfix/5.0.7/pr_19071.patch | 37 ++++++++++++++ server-ce/hotfix/5.0.7/pr_19293.patch | 17 +++++++ server-ce/hotfix/5.0.7/pr_19296.patch | 22 +++++++++ server-ce/hotfix/5.0.7/pr_19297.patch | 70 +++++++++++++++++++++++++++ 10 files changed, 318 insertions(+) create mode 100644 server-ce/hotfix/4.2.7/Dockerfile create mode 100644 server-ce/hotfix/4.2.7/pr_19071.patch create mode 100644 server-ce/hotfix/4.2.7/pr_19293.patch create mode 100644 server-ce/hotfix/4.2.7/pr_19296.patch create mode 100644 server-ce/hotfix/4.2.7/pr_19297.patch create mode 100644 server-ce/hotfix/5.0.7/Dockerfile create mode 100644 server-ce/hotfix/5.0.7/pr_19071.patch create mode 100644 server-ce/hotfix/5.0.7/pr_19293.patch create mode 100644 server-ce/hotfix/5.0.7/pr_19296.patch create mode 100644 server-ce/hotfix/5.0.7/pr_19297.patch diff --git a/server-ce/hotfix/4.2.7/Dockerfile b/server-ce/hotfix/4.2.7/Dockerfile new file mode 100644 index 0000000000..088bd4d64f --- /dev/null +++ b/server-ce/hotfix/4.2.7/Dockerfile @@ -0,0 +1,13 @@ +FROM sharelatex/sharelatex:4.2.6 + +COPY pr_19293.patch . +RUN patch -p1 < pr_19293.patch && rm pr_19293.patch + +COPY pr_19296.patch . +RUN patch -p1 < pr_19296.patch && rm pr_19296.patch + +COPY pr_19297.patch . +RUN patch -p1 < pr_19297.patch && rm pr_19297.patch + +COPY pr_19071.patch . +RUN patch -p1 < pr_19071.patch && rm pr_19071.patch diff --git a/server-ce/hotfix/4.2.7/pr_19071.patch b/server-ce/hotfix/4.2.7/pr_19071.patch new file mode 100644 index 0000000000..2e925e8260 --- /dev/null +++ b/server-ce/hotfix/4.2.7/pr_19071.patch @@ -0,0 +1,37 @@ +--- a/services/web/app/src/Features/Collaborators/CollaboratorsInviteHandler.js ++++ b/services/web/app/src/Features/Collaborators/CollaboratorsInviteHandler.js +@@ -7,13 +7,16 @@ const UserGetter = require('../User/UserGetter') + const ProjectGetter = require('../Project/ProjectGetter') + const Crypto = require('crypto') + const NotificationsBuilder = require('../Notifications/NotificationsBuilder') ++const _ = require('lodash') + + const randomBytes = promisify(Crypto.randomBytes) + + const CollaboratorsInviteHandler = { + async getAllInvites(projectId) { + logger.debug({ projectId }, 'fetching invites for project') +- const invites = await ProjectInvite.find({ projectId }).exec() ++ const invites = await ProjectInvite.find({ projectId }) ++ .select('_id email sendingUserId projectId privileges createdAt expires') ++ .exec() + logger.debug( + { projectId, count: invites.length }, + 'found invites for project' +@@ -101,7 +104,15 @@ const CollaboratorsInviteHandler = { + logger.err({ err, projectId, email }, 'error sending messages for invite') + }) + +- return invite ++ return _.pick(invite.toObject(), [ ++ 'email', ++ 'sendingUserId', ++ 'projectId', ++ 'privileges', ++ '_id', ++ 'createdAt', ++ 'expires', ++ ]) + }, + + async revokeInvite(projectId, inviteId) { diff --git a/server-ce/hotfix/4.2.7/pr_19293.patch b/server-ce/hotfix/4.2.7/pr_19293.patch new file mode 100644 index 0000000000..3a9c7d0a53 --- /dev/null +++ b/server-ce/hotfix/4.2.7/pr_19293.patch @@ -0,0 +1,17 @@ +--- a/services/clsi/app/js/StaticServerForbidSymlinks.js ++++ b/services/clsi/app/js/StaticServerForbidSymlinks.js +@@ -25,9 +25,13 @@ module.exports = ForbidSymlinks = function (staticFn, root, options) { + let file, projectId, result + const path = req.url + // check that the path is of the form /project_id_or_name/path/to/file.log +- if ((result = path.match(/^\/?([a-zA-Z0-9_-]+)\/(.*)/))) { ++ if ((result = path.match(/^\/([a-zA-Z0-9_-]+)\/(.*)$/s))) { + projectId = result[1] + file = result[2] ++ if (path !== `/${projectId}/${file}`) { ++ logger.warn({ path }, 'unrecognized file request') ++ return res.sendStatus(404) ++ } + } else { + logger.warn({ path }, 'unrecognized file request') + return res.sendStatus(404) diff --git a/server-ce/hotfix/4.2.7/pr_19296.patch b/server-ce/hotfix/4.2.7/pr_19296.patch new file mode 100644 index 0000000000..578c488d01 --- /dev/null +++ b/server-ce/hotfix/4.2.7/pr_19296.patch @@ -0,0 +1,22 @@ +--- a/services/clsi/app/js/LatexRunner.js ++++ b/services/clsi/app/js/LatexRunner.js +@@ -110,11 +110,14 @@ function _writeLogOutput(projectId, directory, output, callback) { + // internal method for writing non-empty log files + function _writeFile(file, content, cb) { + if (content && content.length > 0) { +- fs.writeFile(file, content, err => { +- if (err) { +- logger.error({ err, projectId, file }, 'error writing log file') // don't fail on error +- } +- cb() ++ fs.unlink(file, () => { ++ fs.writeFile(file, content, { flag: 'wx' }, err => { ++ if (err) { ++ // don't fail on error ++ logger.error({ err, projectId, file }, 'error writing log file') ++ } ++ cb() ++ }) + }) + } else { + cb() diff --git a/server-ce/hotfix/4.2.7/pr_19297.patch b/server-ce/hotfix/4.2.7/pr_19297.patch new file mode 100644 index 0000000000..2e461830d1 --- /dev/null +++ b/server-ce/hotfix/4.2.7/pr_19297.patch @@ -0,0 +1,70 @@ +--- a/services/web/app/src/Features/Spelling/SpellingController.js ++++ b/services/web/app/src/Features/Spelling/SpellingController.js +@@ -28,39 +28,35 @@ module.exports = { + }) + }, + +- proxyRequestToSpellingApi(req, res) { ++ proxyCheckRequestToSpellingApi(req, res) { + const { language } = req.body + +- let url = req.url.slice('/spelling'.length) +- +- if (url === '/check') { +- if (!language) { +- logger.error('"language" field should be included for spell checking') +- return res.status(422).json({ misspellings: [] }) +- } ++ if (!language) { ++ logger.error({}, '"language" field should be included for spell checking') ++ return res.status(422).json({ misspellings: [] }) ++ } + +- if (!languageCodeIsSupported(language)) { +- // this log statement can be changed to 'error' once projects with +- // unsupported languages are removed from the DB +- logger.debug({ language }, 'language not supported') +- return res.status(422).json({ misspellings: [] }) +- } ++ if (!languageCodeIsSupported(language)) { ++ // this log statement can be changed to 'error' once projects with ++ // unsupported languages are removed from the DB ++ logger.debug({ language }, 'language not supported') ++ return res.status(422).json({ misspellings: [] }) + } + + const userId = SessionManager.getLoggedInUserId(req.session) +- url = `/user/${userId}${url}` ++ const url = `${Settings.apis.spelling.url}/user/${userId}/check` + req.headers.Host = Settings.apis.spelling.host + return request({ +- url: Settings.apis.spelling.url + url, +- method: req.method, ++ url, ++ method: 'POST', + headers: req.headers, + json: req.body, + timeout: TEN_SECONDS, + }) + .on('error', function (error) { +- logger.error({ err: error }, 'Spelling API error') ++ logger.error({ err: error }, 'Spelling Check API error') + return res.status(500).end() + }) + .pipe(res) + }, +-} ++} +\ No newline at end of file + +--- a/services/web/app/src/router.js ++++ b/services/web/app/src/router.js +@@ -1083,7 +1083,7 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) { + webRouter.post( + '/spelling/check', + AuthenticationController.requireLogin(), +- SpellingController.proxyRequestToSpellingApi ++ SpellingController.proxyCheckRequestToSpellingApi + ) + webRouter.post( + '/spelling/learn', diff --git a/server-ce/hotfix/5.0.7/Dockerfile b/server-ce/hotfix/5.0.7/Dockerfile new file mode 100644 index 0000000000..cb9c22702b --- /dev/null +++ b/server-ce/hotfix/5.0.7/Dockerfile @@ -0,0 +1,13 @@ +FROM sharelatex/sharelatex:5.0.6 + +COPY pr_19293.patch . +RUN patch -p1 < pr_19293.patch && rm pr_19293.patch + +COPY pr_19296.patch . +RUN patch -p1 < pr_19296.patch && rm pr_19296.patch + +COPY pr_19297.patch . +RUN patch -p1 < pr_19297.patch && rm pr_19297.patch + +COPY pr_19071.patch . +RUN patch -p1 < pr_19071.patch && rm pr_19071.patch diff --git a/server-ce/hotfix/5.0.7/pr_19071.patch b/server-ce/hotfix/5.0.7/pr_19071.patch new file mode 100644 index 0000000000..2e925e8260 --- /dev/null +++ b/server-ce/hotfix/5.0.7/pr_19071.patch @@ -0,0 +1,37 @@ +--- a/services/web/app/src/Features/Collaborators/CollaboratorsInviteHandler.js ++++ b/services/web/app/src/Features/Collaborators/CollaboratorsInviteHandler.js +@@ -7,13 +7,16 @@ const UserGetter = require('../User/UserGetter') + const ProjectGetter = require('../Project/ProjectGetter') + const Crypto = require('crypto') + const NotificationsBuilder = require('../Notifications/NotificationsBuilder') ++const _ = require('lodash') + + const randomBytes = promisify(Crypto.randomBytes) + + const CollaboratorsInviteHandler = { + async getAllInvites(projectId) { + logger.debug({ projectId }, 'fetching invites for project') +- const invites = await ProjectInvite.find({ projectId }).exec() ++ const invites = await ProjectInvite.find({ projectId }) ++ .select('_id email sendingUserId projectId privileges createdAt expires') ++ .exec() + logger.debug( + { projectId, count: invites.length }, + 'found invites for project' +@@ -101,7 +104,15 @@ const CollaboratorsInviteHandler = { + logger.err({ err, projectId, email }, 'error sending messages for invite') + }) + +- return invite ++ return _.pick(invite.toObject(), [ ++ 'email', ++ 'sendingUserId', ++ 'projectId', ++ 'privileges', ++ '_id', ++ 'createdAt', ++ 'expires', ++ ]) + }, + + async revokeInvite(projectId, inviteId) { diff --git a/server-ce/hotfix/5.0.7/pr_19293.patch b/server-ce/hotfix/5.0.7/pr_19293.patch new file mode 100644 index 0000000000..3a9c7d0a53 --- /dev/null +++ b/server-ce/hotfix/5.0.7/pr_19293.patch @@ -0,0 +1,17 @@ +--- a/services/clsi/app/js/StaticServerForbidSymlinks.js ++++ b/services/clsi/app/js/StaticServerForbidSymlinks.js +@@ -25,9 +25,13 @@ module.exports = ForbidSymlinks = function (staticFn, root, options) { + let file, projectId, result + const path = req.url + // check that the path is of the form /project_id_or_name/path/to/file.log +- if ((result = path.match(/^\/?([a-zA-Z0-9_-]+)\/(.*)/))) { ++ if ((result = path.match(/^\/([a-zA-Z0-9_-]+)\/(.*)$/s))) { + projectId = result[1] + file = result[2] ++ if (path !== `/${projectId}/${file}`) { ++ logger.warn({ path }, 'unrecognized file request') ++ return res.sendStatus(404) ++ } + } else { + logger.warn({ path }, 'unrecognized file request') + return res.sendStatus(404) diff --git a/server-ce/hotfix/5.0.7/pr_19296.patch b/server-ce/hotfix/5.0.7/pr_19296.patch new file mode 100644 index 0000000000..578c488d01 --- /dev/null +++ b/server-ce/hotfix/5.0.7/pr_19296.patch @@ -0,0 +1,22 @@ +--- a/services/clsi/app/js/LatexRunner.js ++++ b/services/clsi/app/js/LatexRunner.js +@@ -110,11 +110,14 @@ function _writeLogOutput(projectId, directory, output, callback) { + // internal method for writing non-empty log files + function _writeFile(file, content, cb) { + if (content && content.length > 0) { +- fs.writeFile(file, content, err => { +- if (err) { +- logger.error({ err, projectId, file }, 'error writing log file') // don't fail on error +- } +- cb() ++ fs.unlink(file, () => { ++ fs.writeFile(file, content, { flag: 'wx' }, err => { ++ if (err) { ++ // don't fail on error ++ logger.error({ err, projectId, file }, 'error writing log file') ++ } ++ cb() ++ }) + }) + } else { + cb() diff --git a/server-ce/hotfix/5.0.7/pr_19297.patch b/server-ce/hotfix/5.0.7/pr_19297.patch new file mode 100644 index 0000000000..2e461830d1 --- /dev/null +++ b/server-ce/hotfix/5.0.7/pr_19297.patch @@ -0,0 +1,70 @@ +--- a/services/web/app/src/Features/Spelling/SpellingController.js ++++ b/services/web/app/src/Features/Spelling/SpellingController.js +@@ -28,39 +28,35 @@ module.exports = { + }) + }, + +- proxyRequestToSpellingApi(req, res) { ++ proxyCheckRequestToSpellingApi(req, res) { + const { language } = req.body + +- let url = req.url.slice('/spelling'.length) +- +- if (url === '/check') { +- if (!language) { +- logger.error('"language" field should be included for spell checking') +- return res.status(422).json({ misspellings: [] }) +- } ++ if (!language) { ++ logger.error({}, '"language" field should be included for spell checking') ++ return res.status(422).json({ misspellings: [] }) ++ } + +- if (!languageCodeIsSupported(language)) { +- // this log statement can be changed to 'error' once projects with +- // unsupported languages are removed from the DB +- logger.debug({ language }, 'language not supported') +- return res.status(422).json({ misspellings: [] }) +- } ++ if (!languageCodeIsSupported(language)) { ++ // this log statement can be changed to 'error' once projects with ++ // unsupported languages are removed from the DB ++ logger.debug({ language }, 'language not supported') ++ return res.status(422).json({ misspellings: [] }) + } + + const userId = SessionManager.getLoggedInUserId(req.session) +- url = `/user/${userId}${url}` ++ const url = `${Settings.apis.spelling.url}/user/${userId}/check` + req.headers.Host = Settings.apis.spelling.host + return request({ +- url: Settings.apis.spelling.url + url, +- method: req.method, ++ url, ++ method: 'POST', + headers: req.headers, + json: req.body, + timeout: TEN_SECONDS, + }) + .on('error', function (error) { +- logger.error({ err: error }, 'Spelling API error') ++ logger.error({ err: error }, 'Spelling Check API error') + return res.status(500).end() + }) + .pipe(res) + }, +-} ++} +\ No newline at end of file + +--- a/services/web/app/src/router.js ++++ b/services/web/app/src/router.js +@@ -1083,7 +1083,7 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) { + webRouter.post( + '/spelling/check', + AuthenticationController.requireLogin(), +- SpellingController.proxyRequestToSpellingApi ++ SpellingController.proxyCheckRequestToSpellingApi + ) + webRouter.post( + '/spelling/learn',