diff --git a/api/utils/common.js b/api/utils/common.js index 6ff89755b3b..0891c3271e8 100644 --- a/api/utils/common.js +++ b/api/utils/common.js @@ -1562,7 +1562,10 @@ common.returnRaw = function(params, returnCode, body, heads) { else { console.error("Output already closed, can't write more"); console.trace(); - console.log(params); + // Don't dump the full params object — req.body/req.headers can + // contain credentials, session cookies, or other secrets. Log + // only the pathname (query string can carry api_key/auth_token). + console.log({pathname: params.urlParts && params.urlParts.pathname, apiPath: params.apiPath, qstringKeys: params.qstring && Object.keys(params.qstring)}); } } }; @@ -1625,7 +1628,10 @@ common.returnMessage = function(params, returnCode, message, heads, noResult = f else { console.error("Output already closed, can't write more"); console.trace(); - console.log(params); + // Don't dump the full params object — req.body/req.headers can + // contain credentials, session cookies, or other secrets. Log + // only the pathname (query string can carry api_key/auth_token). + console.log({pathname: params.urlParts && params.urlParts.pathname, apiPath: params.apiPath, qstringKeys: params.qstring && Object.keys(params.qstring)}); } } }; @@ -1703,7 +1709,10 @@ common.returnOutput = function(params, output, noescape, heads) { else { console.error("Output already closed, can't write more"); console.trace(); - console.log(params); + // Don't dump the full params object — req.body/req.headers can + // contain credentials, session cookies, or other secrets. Log + // only the pathname (query string can carry api_key/auth_token). + console.log({pathname: params.urlParts && params.urlParts.pathname, apiPath: params.apiPath, qstringKeys: params.qstring && Object.keys(params.qstring)}); } } }; diff --git a/frontend/express/app.js b/frontend/express/app.js index 890f10538e6..d5c78751b73 100644 --- a/frontend/express/app.js +++ b/frontend/express/app.js @@ -439,14 +439,29 @@ Promise.all([plugins.dbConnection(countlyConfig), plugins.dbConnection("countly_ app.use(cookieParser()); //server theme images app.use(function(req, res, next) { - var urlPath = req.url.replace(countlyConfig.path, ""); + var urlPath = req.path.replace(countlyConfig.path, ""); var theme = req.cookies.theme || curTheme; - if (theme && theme.length && (req.url.indexOf(countlyConfig.path + '/images/') === 0 || req.url.indexOf(countlyConfig.path + '/geodata/') === 0)) { - fs.exists(__dirname + '/public/themes/' + theme + urlPath, function(exists) { - if (exists) { - res.sendFile(__dirname + '/public/themes/' + theme + urlPath); + if (theme && theme.length && (req.path.indexOf(countlyConfig.path + '/images/') === 0 || req.path.indexOf(countlyConfig.path + '/geodata/') === 0)) { + // The `theme` cookie is user-controlled. Restrict it to a plain + // filename (no separators, no leading dots, no nulls) before + // building the path. This is defense-in-depth on top of the + // `root` option below; reject anything that doesn't survive + // sanitizeFilename unchanged. + if (common.sanitizeFilename(theme) !== theme) { + next(); + return; + } + // Hand the relative path to res.sendFile with `root` set to + // /public/themes — express normalizes the path and rejects any + // `..` traversal before touching the filesystem. Missing files + // and traversal-blocked requests fall through to next(); other + // errors are logged server-side but still fall through so a + // theme misconfiguration doesn't 500 the page. + res.sendFile(theme + urlPath, {root: path.resolve(__dirname, 'public/themes')}, function(err) { + if (err && err.code !== 'ENOENT' && err.statusCode !== 403 && err.statusCode !== 404) { + log.e('Error serving theme image %j: %s', req.path, err.message); } - else { + if (err) { next(); } }); diff --git a/plugins/sdk/api/api.js b/plugins/sdk/api/api.js index bf3a3a0c17c..dec9512e3f5 100644 --- a/plugins/sdk/api/api.js +++ b/plugins/sdk/api/api.js @@ -27,7 +27,8 @@ plugins.register("/permissions/features", function(ob) { common.returnOutput(params, config); }) .catch(function(err) { - common.returnMessage(params, 400, 'Error: ' + err); + console.error("Error retrieving SDK config", err); + common.returnMessage(params, 400, 'Error retrieving SDK config'); }) .finally(function() { resolve(); @@ -72,7 +73,8 @@ plugins.register("/permissions/features", function(ob) { common.returnOutput(params, res.config || {}); }) .catch(function(err) { - common.returnMessage(params, 400, 'Error: ' + err); + console.error("Error retrieving SDK config", err); + common.returnMessage(params, 400, 'Error retrieving SDK config'); }); }); diff --git a/plugins/two-factor-auth/frontend/public/templates/enter2fa_login.html b/plugins/two-factor-auth/frontend/public/templates/enter2fa_login.html index 06e5feddfe9..1d2c334d0c5 100644 --- a/plugins/two-factor-auth/frontend/public/templates/enter2fa_login.html +++ b/plugins/two-factor-auth/frontend/public/templates/enter2fa_login.html @@ -88,8 +88,8 @@ <%- inject_template.form %> <% } %>
- - + + diff --git a/plugins/two-factor-auth/frontend/public/templates/setup2fa.html b/plugins/two-factor-auth/frontend/public/templates/setup2fa.html index 54e107e7355..1c05bf28e2e 100644 --- a/plugins/two-factor-auth/frontend/public/templates/setup2fa.html +++ b/plugins/two-factor-auth/frontend/public/templates/setup2fa.html @@ -81,8 +81,8 @@ <% } %>
- - + +