1
0
Fork 0
mirror of https://github.com/overleaf/overleaf.git synced 2025-04-08 01:24:24 +00:00

Update req.i18n.translate to contains better filtering security and inject appName variable by default to all translation keys ()

Also remove the unused translate wrapper function since the content has been imported directly in the `req.i18n.translate`

GitOrigin-RevId: ed9cee76783e4d41819845a82f66afaed47e2ebd
This commit is contained in:
M Fahru 2023-02-09 10:04:58 -07:00 committed by Copybot
parent 9719b0439c
commit 811b224d81
3 changed files with 38 additions and 38 deletions
services/web/app/src
Features/Helpers
infrastructure

View file

@ -1,34 +0,0 @@
const Settings = require('@overleaf/settings')
const pug = require('pug-runtime')
const logger = require('@overleaf/logger')
const SafeHTMLSubstitute = require('./SafeHTMLSubstitution')
const I18N_HTML_INJECTIONS = new Set()
function translate(key, req, vars, components) {
vars = vars || {}
if (Settings.i18n.checkForHTMLInVars) {
Object.entries(vars).forEach(([field, value]) => {
if (pug.escape(value) !== value) {
const violationsKey = key + field
// do not flood the logs, log one sample per pod + key + field
if (!I18N_HTML_INJECTIONS.has(violationsKey)) {
logger.warn(
{ key, field, value },
'html content in translations context vars'
)
I18N_HTML_INJECTIONS.add(violationsKey)
}
}
})
}
vars.appName = Settings.appName
const locale = req.i18n.translate(key, vars)
if (components) {
return SafeHTMLSubstitute.render(locale, components)
} else {
return locale
}
}
module.exports = { translate }

View file

@ -18,7 +18,6 @@ const {
const {
addOptionalCleanupHandlerAfterDrainingConnections,
} = require('./GracefulShutdown')
const { translate } = require('../Features/Helpers/Translate')
const IEEE_BRAND_ID = Settings.ieeeBrandId
@ -227,8 +226,8 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) {
})
webRouter.use(function (req, res, next) {
res.locals.translate = (key, vars, components) =>
translate(key, req, vars, components)
res.locals.translate = req.i18n.translate
// Don't include the query string parameters, otherwise Google
// treats ?nocdn=true as the canonical version
const parsedOriginalUrl = new URL(req.originalUrl, Settings.siteUrl)

View file

@ -4,11 +4,16 @@ const middleware = require('i18next-http-middleware')
const path = require('path')
const Settings = require('@overleaf/settings')
const { URL } = require('url')
const pug = require('pug-runtime')
const logger = require('@overleaf/logger')
const SafeHTMLSubstitution = require('../Features/Helpers/SafeHTMLSubstitution')
const fallbackLanguageCode = Settings.i18n.defaultLng || 'en'
const availableLanguageCodes = []
const availableHosts = new Map()
const subdomainConfigs = new Map()
const I18N_HTML_INJECTIONS = new Set()
Object.values(Settings.i18n.subdomainLang || {}).forEach(function (spec) {
availableLanguageCodes.push(spec.lngCode)
// prebuild a host->lngCode mapping for the usage at runtime in the
@ -50,6 +55,10 @@ i18n
// Disable nesting in interpolated values, preventing user input
// injection via another nested value
skipOnVariables: true,
defaultVariables: {
appName: Settings.appName,
},
},
preload: availableLanguageCodes,
@ -83,7 +92,33 @@ function setLangBasedOnDomainMiddleware(req, res, next) {
// Decorate req.i18n with translate function alias for backwards
// compatibility usage in requests
req.i18n.translate = req.i18n.t
req.i18n.translate = (key, vars, components) => {
vars = vars || {}
if (Settings.i18n.checkForHTMLInVars) {
Object.entries(vars).forEach(([field, value]) => {
if (pug.escape(value) !== value) {
const violationsKey = key + field
// do not flood the logs, log one sample per pod + key + field
if (!I18N_HTML_INJECTIONS.has(violationsKey)) {
logger.warn(
{ key, field, value },
'html content in translations context vars'
)
I18N_HTML_INJECTIONS.add(violationsKey)
}
}
})
}
const locale = req.i18n.t(key, vars)
if (components) {
return SafeHTMLSubstitution.render(locale, components)
} else {
return locale
}
}
next()
}