From fb9350e9a3339b561b95fc66580c1ef50c3d54d5 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Fri, 25 Oct 2019 14:41:14 -0700 Subject: [PATCH] Added user web state filtering. --- meshuser.js | 1 + views/default-min.handlebars | 19 ++++++++++++-- views/default.handlebars | 17 ++++++++++++- views/translations/default-min_fr.handlebars | 19 ++++++++++++-- views/translations/default_fr.handlebars | 19 ++++++++++++-- webserver.js | 26 ++++++++++++++++++-- 6 files changed, 92 insertions(+), 9 deletions(-) diff --git a/meshuser.js b/meshuser.js index 7abbf91b..3a605672 100644 --- a/meshuser.js +++ b/meshuser.js @@ -2881,6 +2881,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } case 'userWebState': { if (common.validateString(command.state, 1, 10000) == false) break; // Check state size, no more than 10k + command.state = parent.filterUserWebState(command.state); // Filter the state to remove anything bad db.Set({ _id: 'ws' + user._id, state: command.state }); parent.parent.DispatchEvent([user._id], obj, { action: 'userWebState', nolog: 1, domain: domain.id, state: command.state }); break; diff --git a/views/default-min.handlebars b/views/default-min.handlebars index 8e683d73..7c7f8939 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -3705,7 +3705,7 @@ var x = ''; if (nodeids.length > 1) { x = format("Are you sure you want to uninstall the selected {0} agents?", nodeids.length); } else { x = "Are you sure you want to uninstall selected agent?"; } x += '

'; - if (nodeids.length > 1) { x += "This will not remove the devices from the server, but the devices will not longer be able to connect to the server. All remote access to the devices will be lost. The devices must be connect for this command to work."; } else { x += "This will not remove this device from the server, but the device will not longer be able to connect to the server. All remote access to the device will be lost. The device must be connect for this command to work."; } + if (nodeids.length > 1) { x += "This will not remove the devices from the server, but the devices will not longer be able to connect to the server. All remote access to the devices will be lost. The devices must be connected for this command to work."; } else { x += "This will not remove this device from the server, but the device will not longer be able to connect to the server. All remote access to the device will be lost. The device must be connect for this command to work."; } x += '

'; setDialogMode(2, "Uninstall agent", 3, p10showSendUninstallAgentDialogEx, x, nodeids); p10validateSendUninstallAgentDialog(); @@ -8333,7 +8333,22 @@ // Generic methods function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); } - function putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } if (name[0] != '_') { var s = {}; for (var i = 0, len = localStorage.length; i < len; ++i) { var k = localStorage.key(i); if (k[0] != '_') { s[k] = localStorage.getItem(k); } } meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); } } + function putstore(name, val) { + try { + if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; + if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } + if (name[0] != '_') { + var s = {}; + for (var i = 0, len = localStorage.length; i < len; ++i) { + var k = localStorage.key(i); + if (k[0] != '_') { + s[k] = localStorage.getItem(k); + if ((k != 'desktopsettings') && (typeof s[k] == 'string') && (s[k].length > 64)) { delete s[k]; } + } + } + meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); + } + } function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } } function addLink(x, f) { return '' + x + ' '; } function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; } diff --git a/views/default.handlebars b/views/default.handlebars index 30698e71..4b8748fe 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -9315,7 +9315,22 @@ // Generic methods function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); } - function putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } if (name[0] != '_') { var s = {}; for (var i = 0, len = localStorage.length; i < len; ++i) { var k = localStorage.key(i); if (k[0] != '_') { s[k] = localStorage.getItem(k); } } meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); } } + function putstore(name, val) { + try { + if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; + if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } + if (name[0] != '_') { + var s = {}; + for (var i = 0, len = localStorage.length; i < len; ++i) { + var k = localStorage.key(i); + if (k[0] != '_') { + s[k] = localStorage.getItem(k); + if ((k != 'desktopsettings') && (typeof s[k] == 'string') && (s[k].length > 64)) { delete s[k]; } + } + } + meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); + } + } function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } } function addLink(x, f) { return '' + x + ' '; } function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; } diff --git a/views/translations/default-min_fr.handlebars b/views/translations/default-min_fr.handlebars index 0c1c28c3..b3559e19 100644 --- a/views/translations/default-min_fr.handlebars +++ b/views/translations/default-min_fr.handlebars @@ -3705,7 +3705,7 @@ var x = ''; if (nodeids.length > 1) { x = format("Are you sure you want to uninstall the selected {0} agents?", nodeids.length); } else { x = "Are you sure you want to uninstall selected agent?"; } x += '

'; - if (nodeids.length > 1) { x += "This will not remove the devices from the server, but the devices will not longer be able to connect to the server. All remote access to the devices will be lost. The devices must be connect for this command to work."; } else { x += "This will not remove this device from the server, but the device will not longer be able to connect to the server. All remote access to the device will be lost. The device must be connect for this command to work."; } + if (nodeids.length > 1) { x += "This will not remove the devices from the server, but the devices will not longer be able to connect to the server. All remote access to the devices will be lost. The devices must be connected for this command to work."; } else { x += "This will not remove this device from the server, but the device will not longer be able to connect to the server. All remote access to the device will be lost. The device must be connect for this command to work."; } x += '

'; setDialogMode(2, "Uninstall agent", 3, p10showSendUninstallAgentDialogEx, x, nodeids); p10validateSendUninstallAgentDialog(); @@ -8333,7 +8333,22 @@ // Generic methods function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); } - function putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } if (name[0] != '_') { var s = {}; for (var i = 0, len = localStorage.length; i < len; ++i) { var k = localStorage.key(i); if (k[0] != '_') { s[k] = localStorage.getItem(k); } } meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); } } + function putstore(name, val) { + try { + if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; + if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } + if (name[0] != '_') { + var s = {}; + for (var i = 0, len = localStorage.length; i < len; ++i) { + var k = localStorage.key(i); + if (k[0] != '_') { + s[k] = localStorage.getItem(k); + if ((k != 'desktopsettings') && (typeof s[k] == 'string') && (s[k].length > 64)) { delete s[k]; } + } + } + meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); + } + } function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } } function addLink(x, f) { return '' + x + ' '; } function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; } diff --git a/views/translations/default_fr.handlebars b/views/translations/default_fr.handlebars index 7974129e..109103e1 100644 --- a/views/translations/default_fr.handlebars +++ b/views/translations/default_fr.handlebars @@ -4685,7 +4685,7 @@ var x = ''; if (nodeids.length > 1) { x = format("Are you sure you want to uninstall the selected {0} agents?", nodeids.length); } else { x = "Are you sure you want to uninstall selected agent?"; } x += '

'; - if (nodeids.length > 1) { x += "This will not remove the devices from the server, but the devices will not longer be able to connect to the server. All remote access to the devices will be lost. The devices must be connect for this command to work."; } else { x += "This will not remove this device from the server, but the device will not longer be able to connect to the server. All remote access to the device will be lost. The device must be connect for this command to work."; } + if (nodeids.length > 1) { x += "This will not remove the devices from the server, but the devices will not longer be able to connect to the server. All remote access to the devices will be lost. The devices must be connected for this command to work."; } else { x += "This will not remove this device from the server, but the device will not longer be able to connect to the server. All remote access to the device will be lost. The device must be connect for this command to work."; } x += '

'; setDialogMode(2, "Uninstall agent", 3, p10showSendUninstallAgentDialogEx, x, nodeids); p10validateSendUninstallAgentDialog(); @@ -9313,7 +9313,22 @@ // Generic methods function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); } - function putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } if (name[0] != '_') { var s = {}; for (var i = 0, len = localStorage.length; i < len; ++i) { var k = localStorage.key(i); if (k[0] != '_') { s[k] = localStorage.getItem(k); } } meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); } } + function putstore(name, val) { + try { + if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; + if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } + if (name[0] != '_') { + var s = {}; + for (var i = 0, len = localStorage.length; i < len; ++i) { + var k = localStorage.key(i); + if (k[0] != '_') { + s[k] = localStorage.getItem(k); + if ((k != 'desktopsettings') && (typeof s[k] == 'string') && (s[k].length > 64)) { delete s[k]; } + } + } + meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); + } + } function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } } function addLink(x, f) { return '' + x + ' '; } function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; } diff --git a/webserver.js b/webserver.js index 9fc971a7..6fd34bbf 100644 --- a/webserver.js +++ b/webserver.js @@ -1527,8 +1527,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Fetch the web state parent.debug('web', 'handleRootRequestEx: success.'); obj.db.Get('ws' + user._id, function (err, states) { - var webstate = (states.length == 1) ? states[0].state : ''; - render(req, res, getRenderPage('default', req), { authCookie: authCookie, authRelayCookie: authRelayCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate), pluginHandler: (parent.pluginHandler == null)?'null':parent.pluginHandler.prepExports() }); + var webstate = (states.length == 1) ? obj.filterUserWebState(states[0].state) : ''; + render(req, res, getRenderPage('default', req), { authCookie: authCookie, authRelayCookie: authRelayCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate), pluginHandler: (parent.pluginHandler == null) ? 'null' : parent.pluginHandler.prepExports() }); }); } else { // Send back the login application @@ -3757,6 +3757,28 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { return r; } + // Filter the user web site and only output state that we need to keep + const acceptableUserWebStateStrings = ['webPageStackMenu', 'notifications', 'deviceView', 'nightMode', 'webPageFullScreen', 'search', 'showRealNames', 'sort', 'deskAspectRatio', 'viewsize', 'DeskControl', 'uiMode']; + const acceptableUserWebStateDesktopStrings = ['encoding', 'showfocus', 'showmouse', 'showcad', 'limitFrameRate', 'noMouseRotate', 'quality', 'scaling'] + obj.filterUserWebState = function (state) { + if (typeof state == 'string') { try { state = JSON.parse(state); } catch (ex) { return null; } } + var out = {}; + for (var i in acceptableUserWebStateStrings) { + var n = acceptableUserWebStateStrings[i]; + if ((state[n] != null) && ((typeof state[n] == 'number') || (typeof state[n] == 'boolean') || ((typeof state[n] == 'string') && (state[n].length < 32)))) { out[n] = state[n]; } + } + if (typeof state.desktopsettings == 'string') { try { state.desktopsettings = JSON.parse(state.desktopsettings); } catch (ex) { delete state.desktopsettings; } } + if (state.desktopsettings != null) { + out.desktopsettings = {}; + for (var i in acceptableUserWebStateDesktopStrings) { + var n = acceptableUserWebStateDesktopStrings[i]; + if ((state.desktopsettings[n] != null) && ((typeof state.desktopsettings[n] == 'number') || (typeof state.desktopsettings[n] == 'boolean') || ((typeof state.desktopsettings[n] == 'string') && (state.desktopsettings[n].length < 32)))) { out.desktopsettings[n] = state.desktopsettings[n]; } + } + out.desktopsettings = JSON.stringify(out.desktopsettings); + } + return JSON.stringify(out); + } + // Return the correct render page given mobile, minify and override path. function getRenderPage(pagename, req) { var mobile = isMobileBrowser(req), minify = obj.args.minify && !req.query.nominify, p;