From 8d60bd52345a7b949421d8750895d5cea34b7063 Mon Sep 17 00:00:00 2001 From: Claudius Mbemba Date: Tue, 30 May 2017 05:36:29 +0200 Subject: [PATCH 01/11] fixed plugin dependency issue running ./bin/run.sh after the 1st time caused the following error "TypeError: Cannot read property 'dependencies' of undefined". fixed it. --- src/static/js/pluginfw/plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.js index 0d0fa1ed..7cb4b1bd 100644 --- a/src/static/js/pluginfw/plugins.js +++ b/src/static/js/pluginfw/plugins.js @@ -124,7 +124,7 @@ exports.getPackages = function (cb) { var tmp = {}; tmp[data.name] = data; - flatten(tmp[undefined].dependencies); + flatten(tmp[data.name].dependencies); cb(null, packages); }); }; From dd7894d3c9389a000d11d3a89962d9fcc9c6c44b Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Fri, 23 Mar 2018 11:17:39 +0000 Subject: [PATCH 02/11] Added a jsonp var checker --- src/node/hooks/express/apicalls.js | 10 +-- src/node/hooks/express/isValidJSONPName.js | 83 ++++++++++++++++++++++ 2 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 src/node/hooks/express/isValidJSONPName.js diff --git a/src/node/hooks/express/apicalls.js b/src/node/hooks/express/apicalls.js index 009a93d7..e07bbb0b 100644 --- a/src/node/hooks/express/apicalls.js +++ b/src/node/hooks/express/apicalls.js @@ -3,7 +3,7 @@ var apiLogger = log4js.getLogger("API"); var clientLogger = log4js.getLogger("client"); var formidable = require('formidable'); var apiHandler = require('../../handler/APIHandler'); -var isVarName = require('is-var-name'); +var isValidJSONPName = require('./isValidJsonPName'); //This is for making an api call, collecting all post information and passing it to the apiHandler var apiCaller = function(req, res, fields) { @@ -19,7 +19,7 @@ var apiCaller = function(req, res, fields) { apiLogger.info("RESPONSE, " + req.params.func + ", " + response); //is this a jsonp call, if yes, add the function call - if(req.query.jsonp && isVarName(req.query.jsonp)) + if(req.query.jsonp && isValidJSONPName.check(req.query.jsonp)) response = req.query.jsonp + "(" + response + ")"; res._____send(response); @@ -46,7 +46,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { //The Etherpad client side sends information about how a disconnect happened args.app.post('/ep/pad/connection-diagnostic-info', function(req, res) { - new formidable.IncomingForm().parse(req, function(err, fields, files) { + new formidable.IncomingForm().parse(req, function(err, fields, files) { clientLogger.info("DIAGNOSTIC-INFO: " + fields.diagnosticInfo); res.end("OK"); }); @@ -54,7 +54,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { //The Etherpad client side sends information about client side javscript errors args.app.post('/jserror', function(req, res) { - new formidable.IncomingForm().parse(req, function(err, fields, files) { + new formidable.IncomingForm().parse(req, function(err, fields, files) { try { var data = JSON.parse(fields.errorInfo) }catch(e){ @@ -64,7 +64,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { res.end("OK"); }); }); - + //Provide a possibility to query the latest available API version args.app.get('/api', function (req, res) { res.json({"currentVersion" : apiHandler.latestApiVersion}); diff --git a/src/node/hooks/express/isValidJSONPName.js b/src/node/hooks/express/isValidJSONPName.js new file mode 100644 index 00000000..47755ef8 --- /dev/null +++ b/src/node/hooks/express/isValidJSONPName.js @@ -0,0 +1,83 @@ +const RESERVED_WORDS = [ + 'abstract', + 'arguments', + 'await', + 'boolean', + 'break', + 'byte', + 'case', + 'catch', + 'char', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'double', + 'else', + 'enum', + 'eval', + 'export', + 'extends', + 'false', + 'final', + 'finally', + 'float', + 'for', + 'function', + 'goto', + 'if', + 'implements', + 'import', + 'in', + 'instanceof', + 'int', + 'interface', + 'let', + 'long', + 'native', + 'new', + 'null', + 'package', + 'private', + 'protected', + 'public', + 'return', + 'short', + 'static', + 'super', + 'switch', + 'synchronized', + 'this', + 'throw', + 'throws', + 'transient', + 'true', + 'try', + 'typeof', + 'var', + 'void', + 'volatile', + 'while', + 'with', + 'yield' +]; + +const regex = /^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|\'.+\'|\d+)\])*?$/; + +module.exports.check = function(inputStr) { + var isValid = true; + inputStr.split(".").forEach(function(part) { + if (!regex.test(part)) { + isValid = false; + } + + if (RESERVED_WORDS.indexOf(part) !== -1) { + isValid = false; + } + }); + + return isValid; +} From e285db9e80f5f7271bc6fb6ec05772763f0c3659 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 23 Mar 2018 13:21:59 +0000 Subject: [PATCH 03/11] Update Deps https://i.imgur.com/cxFXNeY.png --- src/package.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/package.json b/src/package.json index a88b5c6c..6aa17a69 100644 --- a/src/package.json +++ b/src/package.json @@ -24,7 +24,7 @@ "async" : "0.9.0", "clean-css" : "3.4.19", "uglify-js" : "2.6.2", - "formidable" : "1.0.17", + "formidable" : "1.2.1", "log4js" : "0.6.35", "cheerio" : "0.20.0", "async-stacktrace" : "0.0.2", @@ -42,13 +42,12 @@ "channels" : "0.0.4", "jsonminify" : "0.4.1", "measured" : "1.1.0", - "mocha" : "2.4.5", - "supertest" : "1.2.0", - "is-var-name" : "1.0.0" - }, + "mocha" : "5.0.5", + "supertest" : "3.0.0" + }, "bin": { "etherpad-lite": "./node/server.js" }, "devDependencies": { - "wd" : "0.3.11" + "wd" : "1.6.1" }, "engines" : { "node" : ">=0.10.0", "npm" : ">=1.0" From 8767410a36be9c42b85d0a11f199497caf79a1d8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 23 Mar 2018 19:21:52 +0000 Subject: [PATCH 04/11] be more strict on password check --- src/node/hooks/express/webaccess.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/hooks/express/webaccess.js b/src/node/hooks/express/webaccess.js index 190021a3..1faceec7 100644 --- a/src/node/hooks/express/webaccess.js +++ b/src/node/hooks/express/webaccess.js @@ -37,7 +37,7 @@ exports.basicAuth = function (req, res, next) { var username = userpass.shift(); var password = userpass.join(':'); - if (settings.users[username] != undefined && settings.users[username].password == password) { + if (settings.users[username] != undefined && settings.users[username].password === password) { settings.users[username].username = username; req.session.user = settings.users[username]; return cb(true); From bb40aa00bec361cef475791af338e3d0db68c3ae Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 3 Apr 2018 10:59:10 +0100 Subject: [PATCH 05/11] Update express.js --- src/node/hooks/express.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/node/hooks/express.js b/src/node/hooks/express.js index 17910e4b..48dcf56c 100644 --- a/src/node/hooks/express.js +++ b/src/node/hooks/express.js @@ -25,6 +25,10 @@ exports.createServer = function () { else{ console.warn("Admin username and password not set in settings.json. To access admin please uncomment and edit 'users' in settings.json"); } + var env = process.env.NODE_ENV || 'development'; + if(env !== 'production'){ + console.warn("Etherpad is running in Development mode. This mode is slower for users and less secure than production mode. You should set the NODE_ENV environment variable to production by using: export NODE_ENV=production"); + } } exports.restartServer = function () { From 806c9207e365304ef0f3130d7d3ec59f362f8f9d Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Wed, 4 Apr 2018 18:02:54 +0100 Subject: [PATCH 06/11] remove findkeys from pad export --- src/node/utils/ExportEtherpad.js | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/node/utils/ExportEtherpad.js b/src/node/utils/ExportEtherpad.js index 46ae0d7a..a68ab0b2 100644 --- a/src/node/utils/ExportEtherpad.js +++ b/src/node/utils/ExportEtherpad.js @@ -22,25 +22,18 @@ var ERR = require("async-stacktrace"); exports.getPadRaw = function(padId, callback){ async.waterfall([ function(cb){ - - // Get the Pad - db.findKeys("pad:"+padId, null, function(err,padcontent){ - if(!err){ - cb(err, padcontent); - } - }) + db.get("pad:"+padId, cb); }, function(padcontent,cb){ + var records = ["pad:"+padId]; + for (var i = 0; i <= padcontent.head; i++) { + records.push("pad:"+padId+":revs:" + i); + } + + for (var i = 0; i <= padcontent.chatHead; i++) { + records.push("pad:"+padId+":chat:" + i); + } - // Get the Pad available content keys - db.findKeys("pad:"+padId+":*", null, function(err,records){ - if(!err){ - for (var key in padcontent) { records.push(padcontent[key]);} - cb(err, records); - } - }) - }, - function(records, cb){ var data = {}; async.forEachSeries(Object.keys(records), function(key, r){ @@ -69,7 +62,7 @@ exports.getPadRaw = function(padId, callback){ } r(null); // callback; }); - }, function(err){ + }, function(err){ cb(err, data); }) } From a08c4383b82b1e5377a85ed0125ed1b977f7f324 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Wed, 4 Apr 2018 21:48:32 +0100 Subject: [PATCH 07/11] check pad exists before importing / exporting --- src/node/hooks/express/importexport.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/node/hooks/express/importexport.js b/src/node/hooks/express/importexport.js index 5ebac1db..a62942cc 100644 --- a/src/node/hooks/express/importexport.js +++ b/src/node/hooks/express/importexport.js @@ -2,6 +2,7 @@ var hasPadAccess = require("../../padaccess"); var settings = require('../../utils/Settings'); var exportHandler = require('../../handler/ExportHandler'); var importHandler = require('../../handler/ImportHandler'); +var padManager = require("../../db/PadManager"); exports.expressCreateServer = function (hook_name, args, cb) { args.app.get('/p/:pad/:rev?/export/:type', function(req, res, next) { @@ -22,14 +23,29 @@ exports.expressCreateServer = function (hook_name, args, cb) { res.header("Access-Control-Allow-Origin", "*"); hasPadAccess(req, res, function() { - exportHandler.doExport(req, res, req.params.pad, req.params.type); + console.log('req.params.pad', req.params.pad); + padManager.doesPadExists(req.params.pad, function(err, exists) + { + if(!exists) { + return next(); + } + + exportHandler.doExport(req, res, req.params.pad, req.params.type); + }); }); }); //handle import requests args.app.post('/p/:pad/import', function(req, res, next) { hasPadAccess(req, res, function() { - importHandler.doImport(req, res, req.params.pad); + padManager.doesPadExists(req.params.pad, function(err, exists) + { + if(!exists) { + return next(); + } + + importHandler.doImport(req, res, req.params.pad); + }); }); }); } From ffe24c3dd93efc73e0cbf924db9a0cc40be9511b Mon Sep 17 00:00:00 2001 From: thomas Date: Fri, 6 Apr 2018 22:21:33 +0200 Subject: [PATCH 08/11] Update webaccess.js --- src/node/hooks/express/webaccess.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/hooks/express/webaccess.js b/src/node/hooks/express/webaccess.js index 1faceec7..29475037 100644 --- a/src/node/hooks/express/webaccess.js +++ b/src/node/hooks/express/webaccess.js @@ -20,7 +20,7 @@ exports.basicAuth = function (req, res, next) { // Do not require auth for static paths and the API...this could be a bit brittle if (req.path.match(/^\/(static|javascripts|pluginfw|api)/)) return cb(true); - if (req.path.indexOf('/admin') != 0) { + if (req.path.toLowerCase().indexOf('/admin') != 0) { if (!settings.requireAuthentication) return cb(true); if (!settings.requireAuthorization && req.session && req.session.user) return cb(true); } From c34350f3075de5bd8a9055c174b4182c167f64b2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 7 Apr 2018 09:22:13 +0100 Subject: [PATCH 09/11] Beginning to make release --- CHANGELOG.md | 26 ++++++++++++++++++++------ src/package.json | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74d06f45..bd79182c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 1.6.4 + * SECURITY: exploitable /admin access + * SECURITY: DoS with pad exports + * SECURITY: Remote Code Execution + * SECURITY: Pad data leak + * Fix: Admin redirect URL + * Fix: Various script Fixes + * Fix: Various CSS/Style/Layout fixes + * NEW: Improved Pad contents readability + * NEW: Hook: onAccessCheck + * NEW: SESSIONKEY and APIKey customizable path + * NEW: checkPads script + * NEW: Support "cluster mode" + # 1.6.3 * SECURITY: Update ejs * SECURITY: xss vulnerability when reading window.location.href @@ -56,7 +70,7 @@ * NEW: Allow LibreOffice to be used when exporting a pad * NEW: Create hook exportHtmlAdditionalTagsWithData * NEW: Improve DB migration performance - * NEW: allow settings to be applied from the filesystem + * NEW: allow settings to be applied from the filesystem * NEW: remove applySettings hook and allow credentials.json to be part of core * NEW: Use exec to switch to node process * NEW: Validate incoming color codes @@ -85,7 +99,7 @@ * Fix: switchToPad method * Fix: Dead keys * Fix: Preserve new lines in copy-pasted text - * Fix: Compatibility mode on IE + * Fix: Compatibility mode on IE * Fix: Content Collector to get the class of the DOM-node * Fix: Timeslider export links * Fix: Double prompt on file upload @@ -212,7 +226,7 @@ * Fix: Session Deletion error * Fix: Allow browser tabs to be cycled when focus is in editor * Fix: Various Editor issues with Easysync potentially entering forever loop on bad changeset - + # 1.4 * NEW: Disable toolbar items through settings.json * NEW: Internal stats/metrics engine @@ -244,7 +258,7 @@ # 1.3 * NEW: We now follow the semantic versioning scheme! * NEW: Option to disable IP logging - * NEW: Localisation updates from http://translatewiki.net. + * NEW: Localisation updates from http://translatewiki.net. * Fix: Fix readOnly group pads * Fix: don't fetch padList on every request @@ -337,7 +351,7 @@ * NEW: Add authorId to chat and userlist as a data attribute * NEW: Refactor and fix our frontend tests * NEW: Localisation updates - + # 1.2.81 * Fix: CtrlZ-Y for Undo Redo @@ -377,7 +391,7 @@ * Other: Change loading message asking user to please wait on first build * Other: Allow etherpad to use global npm installation (Safe since node 6.3) * Other: Better documentation for log rotation and log message handling - + # 1.2.7 diff --git a/src/package.json b/src/package.json index 189d8a7b..5b3d6d43 100644 --- a/src/package.json +++ b/src/package.json @@ -56,6 +56,6 @@ "repository" : { "type" : "git", "url" : "http://github.com/ether/etherpad-lite.git" }, - "version" : "1.6.3", + "version" : "1.6.4", "license" : "Apache-2.0" } From 0132f4d1da983638bcf8f13cc1a1fef6decc387b Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 7 Apr 2018 10:13:09 +0100 Subject: [PATCH 10/11] Include CVE # --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd79182c..02accf8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # 1.6.4 - * SECURITY: exploitable /admin access - * SECURITY: DoS with pad exports - * SECURITY: Remote Code Execution - * SECURITY: Pad data leak + * SECURITY: exploitable /admin access - CVE-2018-9845 + * SECURITY: DoS with pad exports - CVE-2018-9327 + * SECURITY: Remote Code Execution - CVE-2018-9326 + * SECURITY: Pad data leak - CVE-2018-9325 * Fix: Admin redirect URL * Fix: Various script Fixes * Fix: Various CSS/Style/Layout fixes @@ -10,7 +10,7 @@ * NEW: Hook: onAccessCheck * NEW: SESSIONKEY and APIKey customizable path * NEW: checkPads script - * NEW: Support "cluster mode" + * NEW: Support "cluster mode" # 1.6.3 * SECURITY: Update ejs From fa83de778cdc6e7c3b83e356b45b76b955ec63ba Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 7 Apr 2018 10:31:47 +0100 Subject: [PATCH 11/11] Password check fix --- src/node/hooks/express/webaccess.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/hooks/express/webaccess.js b/src/node/hooks/express/webaccess.js index 4b9ad4eb..4cb4b9d3 100644 --- a/src/node/hooks/express/webaccess.js +++ b/src/node/hooks/express/webaccess.js @@ -38,7 +38,7 @@ exports.basicAuth = function (req, res, next) { var password = userpass.join(':'); var fallback = function(success) { if (success) return cb(true); - if (settings.users[username] != undefined && settings.users[username].password == password) { + if (settings.users[username] != undefined && settings.users[username].password === password) { settings.users[username].username = username; req.session.user = settings.users[username]; return cb(true);