From e08057828f424c8011dbf43c3bc48d1176fb16e5 Mon Sep 17 00:00:00 2001 From: Miguel Serrano Date: Tue, 13 Aug 2024 15:15:09 +0200 Subject: [PATCH] [CE/SP] Hotfix 5.1.1 and 4.2.8 (#19771) * [CE/SP] Hotfix 5.1.1 and 4.2.8 * Add script to build hotfix releases GitOrigin-RevId: 8ece5e5fd5ec360b66069ce774319511ed82a13a --- server-ce/hotfix/4.2.8/Dockerfile | 11 +++++ server-ce/hotfix/4.2.8/is_19575.patch | 19 +++++++++ server-ce/hotfix/4.2.8/pr_19550.patch | 58 ++++++++++++++++++++++++++ server-ce/hotfix/4.2.8/pr_19612.patch | 46 +++++++++++++++++++++ server-ce/hotfix/5.1.1/Dockerfile | 20 +++++++++ server-ce/hotfix/5.1.1/is_19575.patch | 19 +++++++++ server-ce/hotfix/5.1.1/pr_19543.patch | 30 ++++++++++++++ server-ce/hotfix/5.1.1/pr_19550.patch | 59 +++++++++++++++++++++++++++ server-ce/hotfix/5.1.1/pr_19612.patch | 46 +++++++++++++++++++++ server-ce/hotfix/5.1.1/pr_19676.patch | 26 ++++++++++++ server-ce/nginx/overleaf.conf | 10 +++++ 11 files changed, 344 insertions(+) create mode 100644 server-ce/hotfix/4.2.8/Dockerfile create mode 100644 server-ce/hotfix/4.2.8/is_19575.patch create mode 100644 server-ce/hotfix/4.2.8/pr_19550.patch create mode 100644 server-ce/hotfix/4.2.8/pr_19612.patch create mode 100644 server-ce/hotfix/5.1.1/Dockerfile create mode 100644 server-ce/hotfix/5.1.1/is_19575.patch create mode 100644 server-ce/hotfix/5.1.1/pr_19543.patch create mode 100644 server-ce/hotfix/5.1.1/pr_19550.patch create mode 100644 server-ce/hotfix/5.1.1/pr_19612.patch create mode 100644 server-ce/hotfix/5.1.1/pr_19676.patch diff --git a/server-ce/hotfix/4.2.8/Dockerfile b/server-ce/hotfix/4.2.8/Dockerfile new file mode 100644 index 0000000000..a2dbe4f3b5 --- /dev/null +++ b/server-ce/hotfix/4.2.8/Dockerfile @@ -0,0 +1,11 @@ +FROM sharelatex/sharelatex:4.2.7 + +# Fix crash on on invalid URLs +COPY pr_19612.patch . +RUN patch -p1 < pr_19612.patch && rm pr_19612.patch + +COPY pr_19550.patch . +RUN patch -p1 < pr_19550.patch && rm pr_19550.patch + +COPY is_19575.patch /etc/nginx/sites-enabled/ +RUN cd /etc/nginx/sites-enabled && patch -p0 < is_19575.patch && rm is_19575.patch diff --git a/server-ce/hotfix/4.2.8/is_19575.patch b/server-ce/hotfix/4.2.8/is_19575.patch new file mode 100644 index 0000000000..5854d85234 --- /dev/null +++ b/server-ce/hotfix/4.2.8/is_19575.patch @@ -0,0 +1,19 @@ +--- sharelatex.conf ++++ sharelatex.conf +@@ -67,6 +67,16 @@ server { + proxy_http_version 1.1; + } + ++ # block external access to metrics ++ location ~* ^/metrics/?$ { ++ return 404 'Not found'; ++ } ++ ++ # block external access to all health checks /health_check, /health_check/full, etc ++ location ~* ^/health_check { ++ return 404 'Not found'; ++ } ++ + # Load any extra configuration for this vhost + include /etc/nginx/vhost-extras/overleaf/*.conf; + } diff --git a/server-ce/hotfix/4.2.8/pr_19550.patch b/server-ce/hotfix/4.2.8/pr_19550.patch new file mode 100644 index 0000000000..484a3f2980 --- /dev/null +++ b/server-ce/hotfix/4.2.8/pr_19550.patch @@ -0,0 +1,58 @@ +diff --git a/services/web/app/src/infrastructure/CSP.js b/services/web/app/src/infrastructure/CSP.js +index 28f4f380d3d..abc11c59a48 100644 +--- a/services/web/app/src/infrastructure/CSP.js ++++ b/services/web/app/src/infrastructure/CSP.js +@@ -6,6 +6,7 @@ module.exports = function ({ + reportPercentage, + reportOnly = false, + exclude = [], ++ viewDirectives = {}, + }) { + const header = reportOnly + ? 'Content-Security-Policy-Report-Only' +@@ -33,7 +34,12 @@ module.exports = function ({ + + res.locals.scriptNonce = scriptNonce + +- const policy = buildViewPolicy(scriptNonce, reportPercentage, reportUri) ++ const policy = buildViewPolicy( ++ scriptNonce, ++ reportPercentage, ++ reportUri, ++ viewDirectives[view] ++ ) + + // Note: https://csp-evaluator.withgoogle.com/ is useful for checking the policy + +@@ -68,11 +74,17 @@ const buildDefaultPolicy = (reportUri, styleSrc) => { + return directives.join('; ') + } + +-const buildViewPolicy = (scriptNonce, reportPercentage, reportUri) => { ++const buildViewPolicy = ( ++ scriptNonce, ++ reportPercentage, ++ reportUri, ++ viewDirectives ++) => { + const directives = [ + `script-src 'nonce-${scriptNonce}' 'unsafe-inline' 'strict-dynamic' https: 'report-sample'`, // only allow scripts from certain sources + `object-src 'none'`, // forbid loading an "object" element + `base-uri 'none'`, // forbid setting a "base" element ++ ...(viewDirectives ?? []), + ] + + if (reportUri) { +--- a/services/web/config/settings.defaults.js ++++ b/services/web/config/settings.defaults.js +@@ -868,6 +868,9 @@ module.exports = { + reportPercentage: parseFloat(process.env.CSP_REPORT_PERCENTAGE) || 0, + reportUri: process.env.CSP_REPORT_URI, + exclude: ['app/views/project/editor'], ++ viewDirectives: { ++ 'app/views/project/ide-react': [`img-src 'self' data: blob:`], ++ }, + }, + + unsupportedBrowsers: { + diff --git a/server-ce/hotfix/4.2.8/pr_19612.patch b/server-ce/hotfix/4.2.8/pr_19612.patch new file mode 100644 index 0000000000..3f46f92f90 --- /dev/null +++ b/server-ce/hotfix/4.2.8/pr_19612.patch @@ -0,0 +1,46 @@ +diff --git a/services/web/app/src/Features/HealthCheck/HealthCheckController.js b/services/web/app/src/Features/HealthCheck/HealthCheckController.js +index 278f04bb767..ff074cfa816 100644 +--- a/services/web/app/src/Features/HealthCheck/HealthCheckController.js ++++ b/services/web/app/src/Features/HealthCheck/HealthCheckController.js +@@ -45,6 +45,10 @@ module.exports = { + logger.err({ err }, 'failed api redis health check') + return res.sendStatus(500) + } ++ if (!settings.smokeTest.userId) { ++ logger.err({}, 'smokeTest.userId is undefined in health check') ++ return res.sendStatus(404) ++ } + UserGetter.getUserEmail(settings.smokeTest.userId, (err, email) => { + if (err) { + logger.err({ err }, 'failed api mongo health check') +diff --git a/services/web/app/src/infrastructure/ExpressLocals.js b/services/web/app/src/infrastructure/ExpressLocals.js +index 5f14977d3a3..2e9ed4f1ebb 100644 +--- a/services/web/app/src/infrastructure/ExpressLocals.js ++++ b/services/web/app/src/infrastructure/ExpressLocals.js +@@ -11,6 +11,7 @@ const Features = require('./Features') + const SessionManager = require('../Features/Authentication/SessionManager') + const PackageVersions = require('./PackageVersions') + const Modules = require('./Modules') ++const Errors = require('../Features/Errors/Errors') + const { + canRedirectToAdminDomain, + hasAdminAccess, +@@ -236,10 +237,14 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) { + + // Don't include the query string parameters, otherwise Google + // treats ?nocdn=true as the canonical version +- const parsedOriginalUrl = new URL(req.originalUrl, Settings.siteUrl) +- res.locals.currentUrl = parsedOriginalUrl.pathname +- res.locals.currentUrlWithQueryParams = +- parsedOriginalUrl.pathname + parsedOriginalUrl.search ++ try { ++ const parsedOriginalUrl = new URL(req.originalUrl, Settings.siteUrl) ++ res.locals.currentUrl = parsedOriginalUrl.pathname ++ res.locals.currentUrlWithQueryParams = ++ parsedOriginalUrl.pathname + parsedOriginalUrl.search ++ } catch (err) { ++ return next(new Errors.InvalidError()) ++ } + res.locals.capitalize = function (string) { + if (string.length === 0) { + return '' diff --git a/server-ce/hotfix/5.1.1/Dockerfile b/server-ce/hotfix/5.1.1/Dockerfile new file mode 100644 index 0000000000..5936808262 --- /dev/null +++ b/server-ce/hotfix/5.1.1/Dockerfile @@ -0,0 +1,20 @@ +FROM sharelatex/sharelatex:5.1.0 + +# Confirmation email fix +COPY pr_19676.patch . +RUN patch -p1 < pr_19676.patch && rm pr_19676.patch + +# Fix crash on on invalid URLs +COPY pr_19612.patch . +RUN patch -p1 < pr_19612.patch && rm pr_19612.patch + +# Remove Editor Resources check from launchpad +COPY pr_19543.patch . +RUN patch -p1 < pr_19543.patch && rm pr_19543.patch + +COPY pr_19550.patch . +RUN patch -p1 < pr_19550.patch && rm pr_19550.patch + +COPY is_19575.patch /etc/nginx/sites-enabled/ +RUN cd /etc/nginx/sites-enabled && patch -p0 < is_19575.patch && rm is_19575.patch + diff --git a/server-ce/hotfix/5.1.1/is_19575.patch b/server-ce/hotfix/5.1.1/is_19575.patch new file mode 100644 index 0000000000..ca78ae50d6 --- /dev/null +++ b/server-ce/hotfix/5.1.1/is_19575.patch @@ -0,0 +1,19 @@ +--- overleaf.conf ++++ overleaf.conf +@@ -67,6 +67,16 @@ server { + proxy_http_version 1.1; + } + ++ # block external access to metrics ++ location ~* ^/metrics/?$ { ++ return 404 'Not found'; ++ } ++ ++ # block external access to all health checks /health_check, /health_check/full, etc ++ location ~* ^/health_check { ++ return 404 'Not found'; ++ } ++ + # Load any extra configuration for this vhost + include /etc/nginx/vhost-extras/overleaf/*.conf; + } diff --git a/server-ce/hotfix/5.1.1/pr_19543.patch b/server-ce/hotfix/5.1.1/pr_19543.patch new file mode 100644 index 0000000000..77e2b1152f --- /dev/null +++ b/server-ce/hotfix/5.1.1/pr_19543.patch @@ -0,0 +1,30 @@ +diff --git a/services/web/locales/en.json b/services/web/locales/en.json +index a953a01a1d7..13e20b37279 100644 +--- a/services/web/locales/en.json ++++ b/services/web/locales/en.json +@@ -519,7 +519,6 @@ + "editor_disconected_click_to_reconnect": "Editor disconnected, click anywhere to reconnect.", + "editor_limit_exceeded_in_this_project": "Too many editors in this project", + "editor_only_hide_pdf": "Editor only <0>(hide PDF)", +- "editor_resources": "Editor Resources", + "editor_theme": "Editor theme", + "educational_discount_applied": "40% educational discount applied!", + "educational_discount_available_for_groups_of_ten_or_more": "The educational discount is available for groups of 10 or more", +diff --git a/services/web/modules/launchpad/app/views/launchpad.pug b/services/web/modules/launchpad/app/views/launchpad.pug +index c478fe7b649..28d3ff8fc83 100644 +--- a/services/web/modules/launchpad/app/views/launchpad.pug ++++ b/services/web/modules/launchpad/app/views/launchpad.pug +@@ -166,13 +166,6 @@ block content + + h2 #{translate('status_checks')} + +- +- .row.row-spaced-small +- .col-sm-5 +- | #{translate('editor_resources')} +- .col-sm-7 +- +launchpad-check('ide') +- + + .row.row-spaced-small + .col-sm-5 diff --git a/server-ce/hotfix/5.1.1/pr_19550.patch b/server-ce/hotfix/5.1.1/pr_19550.patch new file mode 100644 index 0000000000..f52a5dd7e1 --- /dev/null +++ b/server-ce/hotfix/5.1.1/pr_19550.patch @@ -0,0 +1,59 @@ +diff --git a/services/web/app/src/infrastructure/CSP.js b/services/web/app/src/infrastructure/CSP.js +index 28f4f380d3d..abc11c59a48 100644 +--- a/services/web/app/src/infrastructure/CSP.js ++++ b/services/web/app/src/infrastructure/CSP.js +@@ -6,6 +6,7 @@ module.exports = function ({ + reportPercentage, + reportOnly = false, + exclude = [], ++ viewDirectives = {}, + }) { + const header = reportOnly + ? 'Content-Security-Policy-Report-Only' +@@ -33,7 +34,12 @@ module.exports = function ({ + + res.locals.scriptNonce = scriptNonce + +- const policy = buildViewPolicy(scriptNonce, reportPercentage, reportUri) ++ const policy = buildViewPolicy( ++ scriptNonce, ++ reportPercentage, ++ reportUri, ++ viewDirectives[view] ++ ) + + // Note: https://csp-evaluator.withgoogle.com/ is useful for checking the policy + +@@ -68,11 +74,17 @@ const buildDefaultPolicy = (reportUri, styleSrc) => { + return directives.join('; ') + } + +-const buildViewPolicy = (scriptNonce, reportPercentage, reportUri) => { ++const buildViewPolicy = ( ++ scriptNonce, ++ reportPercentage, ++ reportUri, ++ viewDirectives ++) => { + const directives = [ + `script-src 'nonce-${scriptNonce}' 'unsafe-inline' 'strict-dynamic' https: 'report-sample'`, // only allow scripts from certain sources + `object-src 'none'`, // forbid loading an "object" element + `base-uri 'none'`, // forbid setting a "base" element ++ ...(viewDirectives ?? []), + ] + + if (reportUri) { +diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js +index cad13ab8156..ab738babdcd 100644 +--- a/services/web/config/settings.defaults.js ++++ b/services/web/config/settings.defaults.js +@@ -911,6 +911,9 @@ module.exports = { + reportPercentage: parseFloat(process.env.CSP_REPORT_PERCENTAGE) || 0, + reportUri: process.env.CSP_REPORT_URI, + exclude: [], ++ viewDirectives: { ++ 'app/views/project/ide-react': [`img-src 'self' data: blob:`], ++ }, + }, + + unsupportedBrowsers: { diff --git a/server-ce/hotfix/5.1.1/pr_19612.patch b/server-ce/hotfix/5.1.1/pr_19612.patch new file mode 100644 index 0000000000..3f46f92f90 --- /dev/null +++ b/server-ce/hotfix/5.1.1/pr_19612.patch @@ -0,0 +1,46 @@ +diff --git a/services/web/app/src/Features/HealthCheck/HealthCheckController.js b/services/web/app/src/Features/HealthCheck/HealthCheckController.js +index 278f04bb767..ff074cfa816 100644 +--- a/services/web/app/src/Features/HealthCheck/HealthCheckController.js ++++ b/services/web/app/src/Features/HealthCheck/HealthCheckController.js +@@ -45,6 +45,10 @@ module.exports = { + logger.err({ err }, 'failed api redis health check') + return res.sendStatus(500) + } ++ if (!settings.smokeTest.userId) { ++ logger.err({}, 'smokeTest.userId is undefined in health check') ++ return res.sendStatus(404) ++ } + UserGetter.getUserEmail(settings.smokeTest.userId, (err, email) => { + if (err) { + logger.err({ err }, 'failed api mongo health check') +diff --git a/services/web/app/src/infrastructure/ExpressLocals.js b/services/web/app/src/infrastructure/ExpressLocals.js +index 5f14977d3a3..2e9ed4f1ebb 100644 +--- a/services/web/app/src/infrastructure/ExpressLocals.js ++++ b/services/web/app/src/infrastructure/ExpressLocals.js +@@ -11,6 +11,7 @@ const Features = require('./Features') + const SessionManager = require('../Features/Authentication/SessionManager') + const PackageVersions = require('./PackageVersions') + const Modules = require('./Modules') ++const Errors = require('../Features/Errors/Errors') + const { + canRedirectToAdminDomain, + hasAdminAccess, +@@ -236,10 +237,14 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) { + + // Don't include the query string parameters, otherwise Google + // treats ?nocdn=true as the canonical version +- const parsedOriginalUrl = new URL(req.originalUrl, Settings.siteUrl) +- res.locals.currentUrl = parsedOriginalUrl.pathname +- res.locals.currentUrlWithQueryParams = +- parsedOriginalUrl.pathname + parsedOriginalUrl.search ++ try { ++ const parsedOriginalUrl = new URL(req.originalUrl, Settings.siteUrl) ++ res.locals.currentUrl = parsedOriginalUrl.pathname ++ res.locals.currentUrlWithQueryParams = ++ parsedOriginalUrl.pathname + parsedOriginalUrl.search ++ } catch (err) { ++ return next(new Errors.InvalidError()) ++ } + res.locals.capitalize = function (string) { + if (string.length === 0) { + return '' diff --git a/server-ce/hotfix/5.1.1/pr_19676.patch b/server-ce/hotfix/5.1.1/pr_19676.patch new file mode 100644 index 0000000000..287ee78376 --- /dev/null +++ b/server-ce/hotfix/5.1.1/pr_19676.patch @@ -0,0 +1,26 @@ +diff --git a/services/web/app/src/Features/Email/EmailBuilder.js b/services/web/app/src/Features/Email/EmailBuilder.js +index 46d014a8e14..d839d67f634 100644 +--- a/services/web/app/src/Features/Email/EmailBuilder.js ++++ b/services/web/app/src/Features/Email/EmailBuilder.js +@@ -234,7 +234,7 @@ templates.confirmEmail = ctaTemplate({ + }, + secondaryMessage() { + return [ +- 'If you did not request this, please let us know at support@overleaf.com.', ++ `If you did not request this, please let us know at ${settings.adminEmail}.`, + `If you have any questions or trouble confirming your email address, please get in touch with our support team at ${settings.adminEmail}.`, + ] + }, +diff --git a/services/web/app/src/Features/User/UserRegistrationHandler.js b/services/web/app/src/Features/User/UserRegistrationHandler.js +index 2802fdc81c5..02c52f73fd2 100644 +--- a/services/web/app/src/Features/User/UserRegistrationHandler.js ++++ b/services/web/app/src/Features/User/UserRegistrationHandler.js +@@ -113,7 +113,7 @@ const UserRegistrationHandler = { + + const setNewPasswordUrl = `${settings.siteUrl}/user/activate?token=${token}&user_id=${user._id}` + +- EmailHandler.promises ++ await EmailHandler.promises + .sendEmail('registered', { + to: user.email, + setNewPasswordUrl, diff --git a/server-ce/nginx/overleaf.conf b/server-ce/nginx/overleaf.conf index a860087087..78af603c1e 100644 --- a/server-ce/nginx/overleaf.conf +++ b/server-ce/nginx/overleaf.conf @@ -67,6 +67,16 @@ server { proxy_http_version 1.1; } + # block external access to metrics + location ~* ^/metrics/?$ { + return 404 'Not found'; + } + + # block external access to all health checks /health_check, /health_check/full, etc + location ~* ^/health_check { + return 404 'Not found'; + } + # Load any extra configuration for this vhost include /etc/nginx/vhost-extras/overleaf/*.conf; }