Переглянути джерело

[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
Miguel Serrano 2 роки тому
батько
коміт
e08057828f

+ 11 - 0
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

+ 19 - 0
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;
+ }

+ 58 - 0
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: {
+

+ 46 - 0
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 ''

+ 20 - 0
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
+

+ 19 - 0
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;
+ }

+ 30 - 0
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)</0>",
+-  "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')}
+
+-											<!-- ide js -->
+-											.row.row-spaced-small
+-												.col-sm-5
+-													| #{translate('editor_resources')}
+-												.col-sm-7
+-													+launchpad-check('ide')
+-
+ 											<!-- websocket -->
+ 											.row.row-spaced-small
+ 												.col-sm-5

+ 59 - 0
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: {

+ 46 - 0
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 ''

+ 26 - 0
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 <a href="mailto:support@overleaf.com">support@overleaf.com</a>.',
++      `If you did not request this, please let us know at <a href="mailto:${settings.adminEmail}">${settings.adminEmail}</a>.`,
+       `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,

+ 10 - 0
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;
 }