From 2355d9d5dde02b9ed61ec1d80b10e5bde51e9b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 26 Jun 2020 15:19:18 -0400 Subject: [PATCH] fix: escape navigation item fields, theme:id, category fields --- public/src/admin/settings/navigation.js | 19 ++++++------------- src/categories/data.js | 9 ++++++--- src/controllers/admin/settings.js | 4 +++- src/meta/themes.js | 7 ++++++- src/middleware/header.js | 3 ++- src/navigation/admin.js | 15 +++++++++------ src/navigation/index.js | 5 ----- 7 files changed, 32 insertions(+), 30 deletions(-) diff --git a/public/src/admin/settings/navigation.js b/public/src/admin/settings/navigation.js index cfb78cf754..2209bb4fd1 100644 --- a/public/src/admin/settings/navigation.js +++ b/public/src/admin/settings/navigation.js @@ -8,18 +8,11 @@ define('admin/settings/navigation', ['translator', 'iconSelect', 'benchpress', ' navigation.init = function () { available = ajaxify.data.available; - $('#enabled .unescape').each(function () { - $(this).val(translator.unescape($(this).val())); - }); - - translator.translate($('#available').html(), function (html) { - $('#available').html(translator.unescape(html)) - .find('li .drag-item').draggable({ - connectToSortable: '#active-navigation', - helper: 'clone', - distance: 10, - stop: drop, - }); + $('#available').find('li .drag-item').draggable({ + connectToSortable: '#active-navigation', + helper: 'clone', + distance: 10, + stop: drop, }); $('#active-navigation').sortable().droppable({ @@ -112,7 +105,7 @@ define('admin/settings/navigation', ['translator', 'iconSelect', 'benchpress', ' } data[input.name].push(input.value); } else { - data[input.name] = translator.escape(input.value); + data[input.name] = input.value; } }); diff --git a/src/categories/data.js b/src/categories/data.js index 07aa9eb3b9..ddbc6362e3 100644 --- a/src/categories/data.js +++ b/src/categories/data.js @@ -76,9 +76,12 @@ function modifyCategory(category, fields) { db.parseIntFields(category, intFields, fields); - if (category.hasOwnProperty('name')) { - category.name = validator.escape(String(category.name || '')); - } + const escapeFields = ['name', 'color', 'bgColor', 'imageClass', 'class', 'link']; + escapeFields.forEach((field) => { + if (category.hasOwnProperty(field)) { + category[field] = validator.escape(String(category[field] || '')); + } + }); if (category.hasOwnProperty('icon')) { category.icon = category.icon || 'hidden'; diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index f7b0d529e7..50ee575cea 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -10,6 +10,7 @@ const navigationAdmin = require('../../navigation/admin'); const social = require('../../social'); const helpers = require('../helpers'); +const translator = require('../../../public/src/modules/translator'); const settingsController = module.exports; settingsController.get = async function (req, res) { @@ -104,7 +105,8 @@ settingsController.navigation = async function (req, res) { admin.enabled.forEach(function (enabled, index) { enabled.index = index; enabled.selected = index === 0; - + enabled.title = translator.escape(enabled.title); + enabled.text = translator.escape(enabled.text); enabled.groups = admin.groups.map(function (group) { return { displayName: group.displayName, diff --git a/src/meta/themes.js b/src/meta/themes.js index 3710af3a2e..a5a2dd43ff 100644 --- a/src/meta/themes.js +++ b/src/meta/themes.js @@ -90,7 +90,12 @@ Themes.set = async (data) => { case 'local': { const current = await Meta.configs.get('theme:id'); if (current !== data.id) { - let config = await fsReadfile(path.join(nconf.get('themes_path'), data.id, 'theme.json'), 'utf8'); + const pathToThemeJson = path.join(nconf.get('themes_path'), data.id, 'theme.json'); + if (!pathToThemeJson.startsWith(nconf.get('themes_path'))) { + throw new Error('[[error:invalid-theme-id]]'); + } + + let config = await fsReadfile(pathToThemeJson, 'utf8'); config = JSON.parse(config); await db.sortedSetRemove('plugins:active', current); diff --git a/src/middleware/header.js b/src/middleware/header.js index ea208895ab..4873a210b2 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -3,6 +3,7 @@ var nconf = require('nconf'); var jsesc = require('jsesc'); var _ = require('lodash'); +const validator = require('validator'); var util = require('util'); var db = require('../database'); @@ -121,7 +122,7 @@ module.exports = function (middleware) { const tidsByFilter = results.unreadData.tidsByFilter; results.navigation = results.navigation.map(function (item) { function modifyNavItem(item, route, filter, content) { - if (item && item.originalRoute === route) { + if (item && validator.unescape(item.originalRoute) === route) { unreadData[filter] = _.zipObject(tidsByFilter[filter], tidsByFilter[filter].map(() => true)); item.content = content; if (unreadCounts[filter] > 0) { diff --git a/src/navigation/admin.js b/src/navigation/admin.js index 5398ac8375..499fd9c5ee 100644 --- a/src/navigation/admin.js +++ b/src/navigation/admin.js @@ -1,10 +1,10 @@ 'use strict'; const _ = require('lodash'); +const validator = require('validator'); const plugins = require('../plugins'); const db = require('../database'); -const translator = require('../translator'); const pubsub = require('../pubsub'); const admin = module.exports; @@ -17,11 +17,6 @@ pubsub.on('admin:navigation:save', function () { admin.save = async function (data) { const order = Object.keys(data); const items = data.map(function (item, index) { - for (var i in item) { - if (item.hasOwnProperty(i) && typeof item[i] === 'string' && (i === 'title' || i === 'text')) { - item[i] = translator.escape(item[i]); - } - } item.order = order[index]; return JSON.stringify(item); }); @@ -45,8 +40,16 @@ admin.get = async function () { return _.cloneDeep(cache); } const data = await db.getSortedSetRange('navigation:enabled', 0, -1); + const escapeFields = ['iconClass', 'class', 'route', 'id', 'text', 'textClass', 'title']; cache = data.map(function (item) { item = JSON.parse(item); + + escapeFields.forEach((field) => { + if (item.hasOwnProperty(field)) { + item[field] = validator.escape(String(item[field])); + } + }); + item.groups = item.groups || []; if (item.groups && !Array.isArray(item.groups)) { item.groups = [item.groups]; diff --git a/src/navigation/index.js b/src/navigation/index.js index cf47d3dd5c..f679cc29a3 100644 --- a/src/navigation/index.js +++ b/src/navigation/index.js @@ -2,7 +2,6 @@ const nconf = require('nconf'); const admin = require('./admin'); -const translator = require('../translator'); const groups = require('../groups'); const navigation = module.exports; @@ -17,10 +16,6 @@ navigation.get = async function (uid) { item.route = nconf.get('relative_path') + item.route; } - Object.keys(item).forEach(function (key) { - item[key] = translator.unescape(item[key]); - }); - return item; });