From 26b521e86e6ac40b7ba25bbe8dac7bf4d48d43ce Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 1 Mar 2020 20:26:29 +0200 Subject: [PATCH 01/70] Upgrade Meteor to 1.10-rc.2 --- .meteor/packages | 8 ++++---- .meteor/release | 2 +- .meteor/versions | 30 +++++++++++++++--------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.meteor/packages b/.meteor/packages index dc70f580d..0f764d86c 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -23,7 +23,7 @@ dburles:collection-helpers idmontie:migrations matb33:collection-hooks matteodem:easy-search -mongo@1.8.0 +mongo@1.9.0-rc110.2 mquandalle:collection-mutations # Account system @@ -38,7 +38,7 @@ wekan-accounts-oidc # Utilities check@1.3.1 jquery@1.11.10 -random@1.1.0 +random@1.2.0-rc110.2 reactive-dict@1.3.0 session@1.2.0 tracker@1.2.0 @@ -67,7 +67,7 @@ templates:tabs verron:autosize simple:json-routes rajit:bootstrap3-datepicker -shell-server@0.4.0 +shell-server@0.5.0-rc110.2 simple:rest-accounts-password useraccounts:core email@1.2.3 @@ -75,7 +75,7 @@ horka:swipebox dynamic-import@0.5.1 staringatlights:fast-render -accounts-password@1.5.2 +accounts-password@1.6.0-rc110.2 cfs:gridfs rzymek:fullcalendar momentjs:moment@2.22.2 diff --git a/.meteor/release b/.meteor/release index 8558e1492..fa971c2ad 100644 --- a/.meteor/release +++ b/.meteor/release @@ -1 +1 @@ -METEOR@1.9.2 +METEOR@1.10-rc.2 diff --git a/.meteor/versions b/.meteor/versions index 1c6ceb8bc..caf2c7d15 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,7 +1,7 @@ 3stack:presence@1.1.2 -accounts-base@1.5.0 -accounts-oauth@1.1.16 -accounts-password@1.5.3 +accounts-base@1.6.0-rc110.2 +accounts-oauth@1.2.0-rc110.2 +accounts-password@1.6.0-rc110.2 aldeed:collection2@2.10.0 aldeed:collection2-core@1.2.0 aldeed:schema-deny@1.1.0 @@ -17,7 +17,7 @@ base64@1.0.12 binary-heap@1.0.11 blaze@2.3.4 blaze-tools@1.0.10 -boilerplate-generator@1.6.0 +boilerplate-generator@1.7.0-rc110.2 browser-policy-common@1.0.11 browser-policy-framing@1.1.0 caching-compiler@1.2.1 @@ -75,7 +75,7 @@ htmljs@1.0.11 http@1.4.2 id-map@1.1.0 idmontie:migrations@1.0.3 -inter-process-messaging@0.1.0 +inter-process-messaging@0.1.1-rc110.2 jquery@1.11.11 kadira:blaze-layout@2.3.0 kadira:dochead@1.5.0 @@ -84,7 +84,7 @@ kenton:accounts-sandstorm@0.7.0 konecty:mongo-counter@0.0.5_3 lamhieu:meteorx@2.1.1 lamhieu:unblock@1.0.0 -launch-screen@1.1.1 +launch-screen@1.2.0-rc110.2 livedata@1.0.18 localstorage@1.2.0 logging@1.1.20 @@ -104,13 +104,13 @@ meteorspark:util@0.2.0 minifier-css@1.5.0 minifier-js@2.6.0 minifiers@1.1.8-faster-rebuild.0 -minimongo@1.4.5 -mobile-status-bar@1.0.14 +minimongo@1.5.0-rc110.2 +mobile-status-bar@1.1.0-rc110.2 modern-browsers@0.1.5 modules@0.15.0 modules-runtime@0.12.0 momentjs:moment@2.24.0 -mongo@1.8.1 +mongo@1.9.0-rc110.2 mongo-decimal@0.1.1 mongo-dev-server@1.1.0 mongo-id@1.0.7 @@ -127,9 +127,9 @@ mquandalle:mousetrap-bindglobal@0.0.1 mquandalle:perfect-scrollbar@0.6.5_2 msavin:usercache@1.8.0 npm-bcrypt@0.9.3 -npm-mongo@3.3.0 -oauth@1.2.8 -oauth2@1.2.1 +npm-mongo@3.5.0-rc110.2 +oauth@1.3.0-rc110.2 +oauth2@1.3.0-rc110.2 observe-sequence@1.0.16 ongoworks:speakingurl@1.1.0 ordered-dict@1.1.0 @@ -144,7 +144,7 @@ promise@0.11.2 raix:eventemitter@0.1.3 raix:handlebar-helpers@0.2.5 rajit:bootstrap3-datepicker@1.7.1_1 -random@1.1.0 +random@1.2.0-rc110.2 rate-limit@1.0.9 reactive-dict@1.3.0 reactive-var@1.0.11 @@ -156,7 +156,7 @@ server-render@0.3.1 service-configuration@1.0.11 session@1.2.0 sha@1.0.9 -shell-server@0.4.0 +shell-server@0.5.0-rc110.2 simple:authenticate-user-by-token@1.0.1 simple:json-routes@2.1.0 simple:rest-accounts-password@1.1.2 @@ -186,7 +186,7 @@ useraccounts:core@1.14.2 useraccounts:flow-routing@1.14.2 useraccounts:unstyled@1.14.2 verron:autosize@3.0.8 -webapp@1.8.2 +webapp@1.9.0-rc110.2 webapp-hashing@1.0.9 wekan-accounts-cas@0.1.0 wekan-accounts-oidc@1.0.10 From 0d3002f69d97e646fa7368bfdade4f78c51e9884 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 1 Mar 2020 20:33:23 +0200 Subject: [PATCH 02/70] Try to make Meteor build time shorter by excluding legacy and cordova. Thanks to xet7 ! --- Dockerfile | 4 ++-- rebuild-wekan.bat | 2 +- rebuild-wekan.sh | 2 +- releases/rebuild-release.sh | 2 +- snapcraft.yaml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index e548c15b4..ae609f381 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ LABEL maintainer="wekan" ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-essential git ca-certificates python3" \ DEBUG=false \ NODE_VERSION=v12.16.1 \ - METEOR_RELEASE=1.9.0 \ + METEOR_RELEASE=1.10-rc.2 \ USE_EDGE=false \ METEOR_EDGE=1.5-beta.17 \ NPM_VERSION=latest \ @@ -249,7 +249,7 @@ RUN \ chown wekan --recursive /home/wekan/.npm /home/wekan/.config /home/wekan/.meteor && \ #gosu wekan:wekan /home/wekan/.meteor/meteor add standard-minifier-js && \ gosu wekan:wekan npm install && \ - gosu wekan:wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build && \ + gosu wekan:wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build --exclude-archs web.browser.legacy,web.cordova && \ cp /home/wekan/app/fix-download-unicode/cfs_access-point.txt /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \ #rm /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs && \ chown wekan /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \ diff --git a/rebuild-wekan.bat b/rebuild-wekan.bat index 0a9a8eaca..e61c20be5 100644 --- a/rebuild-wekan.bat +++ b/rebuild-wekan.bat @@ -51,7 +51,7 @@ cd .. REM del /S /F /Q node_modules call meteor npm install REM del /S /F /Q .build -call meteor build .build --directory +call meteor build .build --directory --exclude-archs web.browser.legacy,web.cordova copy fix-download-unicode\cfs_access-point.txt .build\bundle\programs\server\packages\cfs_access-point.js cd .build\bundle\programs\server call meteor npm install diff --git a/rebuild-wekan.sh b/rebuild-wekan.sh index 957e634f5..0bf6c68ae 100755 --- a/rebuild-wekan.sh +++ b/rebuild-wekan.sh @@ -146,7 +146,7 @@ do rm -rf node_modules .meteor/local npm install rm -rf .build - meteor build .build --directory + meteor build .build --directory --exclude-archs web.browser.legacy,web.cordova cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js #Removed binary version of bcrypt because of security vulnerability that is not fixed yet. #https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac diff --git a/releases/rebuild-release.sh b/releases/rebuild-release.sh index e8393313d..a9619b064 100755 --- a/releases/rebuild-release.sh +++ b/releases/rebuild-release.sh @@ -9,7 +9,7 @@ sudo chown -R $(id -u):$(id -g) $HOME/.npm $HOME/.meteor rm -rf node_modules meteor npm install rm -rf .build -meteor build .build --directory +meteor build .build --directory --exclude-archs web.browser.legacy,web.cordova cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js cd .build/bundle/programs/server rm -rf node_modules diff --git a/snapcraft.yaml b/snapcraft.yaml index 2b6b1ef96..f59e32005 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -206,7 +206,7 @@ parts: meteor add standard-minifier-js --allow-superuser meteor npm install --allow-superuser meteor npm install --allow-superuser --save babel-runtime - meteor build .build --directory --allow-superuser + meteor build .build --directory --allow-superuser --exclude-archs web.browser.legacy,web.cordova cp -f fix-download-unicode/cfs_access-point.txt .build/bundle/programs/server/packages/cfs_access-point.js #Removed binary version of bcrypt because of security vulnerability that is not fixed yet. #https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac From fc35c234a78fb2137f0f78a3a6f353c46734ed72 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 1 Mar 2020 20:38:05 +0200 Subject: [PATCH 03/70] Update translations. --- i18n/pl.i18n.json | 6 +++--- i18n/sl.i18n.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/i18n/pl.i18n.json b/i18n/pl.i18n.json index 025b8d4ca..67af6c982 100644 --- a/i18n/pl.i18n.json +++ b/i18n/pl.i18n.json @@ -757,7 +757,7 @@ "cardAssigneesPopup-title": "Przypisujący", "addmore-detail": "Dodaj bardziej szczegółowy opis", "show-on-card": "Pokaż na karcie", - "new": "New", - "editUserPopup-title": "Edit User", - "newUserPopup-title": "New User" + "new": "Nowy", + "editUserPopup-title": "Edytuj użytkownika", + "newUserPopup-title": "Nowy użytkownik" } diff --git a/i18n/sl.i18n.json b/i18n/sl.i18n.json index a580daa71..15163a0fe 100644 --- a/i18n/sl.i18n.json +++ b/i18n/sl.i18n.json @@ -757,7 +757,7 @@ "cardAssigneesPopup-title": "Dodeljen član", "addmore-detail": "Dodaj podrobnejši opis", "show-on-card": "Prikaži na kartici", - "new": "New", - "editUserPopup-title": "Edit User", - "newUserPopup-title": "New User" + "new": "Novo", + "editUserPopup-title": "Uredi uporabnika", + "newUserPopup-title": "Nov uporabnik" } From aac7c380c8c389b0683b2bd64e2cc856993f0e30 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 1 Mar 2020 20:59:53 +0200 Subject: [PATCH 04/70] - Fix critical and moderate security vulnerabilities reported at 2020-02-26 with responsible disclosure by [Dejan Zelic](https://twitter.com/dejandayoff), Justin Benjamin and others at [Offensive Security](https://twitter.com/offsectraining), that follow standard 90 days before public disclosure. Thanks to xet7. - Fix webhook error that prevented some card etc deleting from web UI of board. Thanks to xet7. - Add some more Font Awesome icons. Thanks to xet7. - Remove autofocus from many form input boxes so that they would not cause warnings. Thanks to xet7. --- client/components/settings/peopleBody.jade | 4 +- client/components/sidebar/sidebar.jade | 9 +- models/activities.js | 9 +- models/users.js | 169 ++++----- server/notifications/outgoing.js | 377 +++++++++++---------- server/statistics.js | 138 ++++---- 6 files changed, 368 insertions(+), 338 deletions(-) diff --git a/client/components/settings/peopleBody.jade b/client/components/settings/peopleBody.jade index ca4bc382d..fef1067e3 100644 --- a/client/components/settings/peopleBody.jade +++ b/client/components/settings/peopleBody.jade @@ -110,7 +110,7 @@ template(name="editUserPopup") label.hide.userId(type="text" value=user._id) label | {{_ 'fullname'}} - input.js-profile-fullname(type="text" value=user.profile.fullname autofocus) + input.js-profile-fullname(type="text" value=user.profile.fullname) label | {{_ 'username'}} span.error.hide.username-taken @@ -159,7 +159,7 @@ template(name="newUserPopup") //label.hide.userId(type="text" value=user._id) label | {{_ 'fullname'}} - input.js-profile-fullname(type="text" value="" autofocus) + input.js-profile-fullname(type="text" value="") label | {{_ 'username'}} span.error.hide.username-taken diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index ebcd84865..f0b0e4be1 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -245,7 +245,7 @@ template(name="outgoingWebhooksPopup") b   .materialCheckBox(class="{{#unless enabled}}is-checked{{/unless}}") input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" value=title) - input.js-outgoing-webhooks-url(type="text" name="url" value=url autofocus) + input.js-outgoing-webhooks-url(type="text" name="url" value=url) input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" value=token name="token") select.js-outgoing-webhooks-type(name="type") each _type in types @@ -257,7 +257,7 @@ template(name="outgoingWebhooksPopup") input(type="hidden" value=_id name="id") input.primary.wide(type="submit" value="{{_ 'save'}}") form.integration-form - input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title" autofocus) + input.js-outgoing-webhooks-title(placeholder="{{_ 'webhook-title'}}" type="text" name="title") input.js-outgoing-webhooks-url(placeholder="{{_ 'URL' }}" type="text" name="url") input.js-outgoing-webhooks-token(placeholder="{{_ 'webhook-token' }}" type="text" name="token") select.js-outgoing-webhooks-type(name="type") @@ -267,7 +267,10 @@ template(name="outgoingWebhooksPopup") template(name="boardMenuPopup") ul.pop-over-list - li: a.js-custom-fields {{_ 'custom-fields'}} + li + a.js-custom-fields + i.fa.fa-list-alt + | {{_ 'custom-fields'}} li a.js-open-archives i.fa.fa-archive diff --git a/models/activities.js b/models/activities.js index 19e3fb7d6..568859a9d 100644 --- a/models/activities.js +++ b/models/activities.js @@ -108,7 +108,7 @@ if (Meteor.isServer) { let participants = []; let watchers = []; let title = 'act-activity-notify'; - let board = null; + const board = Boards.findOne(activity.boardId); const description = `act-${activity.activityType}`; const params = { activityId: activity._id, @@ -122,8 +122,11 @@ if (Meteor.isServer) { params.userId = activity.userId; } if (activity.boardId) { - board = activity.board(); - params.board = board.title; + if (board.title.length > 0) { + params.board = board.title; + } else { + params.board = ''; + } title = 'act-withBoardTitle'; params.url = board.absoluteUrl(); params.boardId = activity.boardId; diff --git a/models/users.js b/models/users.js index 000762532..d56f14ff7 100644 --- a/models/users.js +++ b/models/users.js @@ -620,44 +620,6 @@ Users.mutations({ }); Meteor.methods({ - setCreateUser(fullname, username, password, isAdmin, isActive, email) { - if (Meteor.user().isAdmin) { - check(fullname, String); - check(username, String); - check(password, String); - check(isAdmin, String); - check(isActive, String); - check(email, String); - - const nUsersWithUsername = Users.find({ username }).count(); - const nUsersWithEmail = Users.find({ email }).count(); - if (nUsersWithUsername > 0) { - throw new Meteor.Error('username-already-taken'); - } else if (nUsersWithEmail > 0) { - throw new Meteor.Error('email-already-taken'); - } else { - Accounts.createUser({ - fullname, - username, - password, - isAdmin, - isActive, - email: email.toLowerCase(), - from: 'admin', - }); - } - } - }, - setUsername(username, userId) { - check(username, String); - check(userId, String); - const nUsersWithUsername = Users.find({ username }).count(); - if (nUsersWithUsername > 0) { - throw new Meteor.Error('username-already-taken'); - } else { - Users.update(userId, { $set: { username } }); - } - }, setListSortBy(value) { check(value, String); Meteor.user().setListSortBy(value); @@ -678,51 +640,97 @@ Meteor.methods({ check(limit, Number); Meteor.user().setShowCardsCountAt(limit); }, - setEmail(email, userId) { - if (Array.isArray(email)) { - email = email.shift(); - } - check(email, String); - const existingUser = Users.findOne( - { 'emails.address': email }, - { fields: { _id: 1 } }, - ); - if (existingUser) { - throw new Meteor.Error('email-already-taken'); - } else { - Users.update(userId, { - $set: { - emails: [ - { - address: email, - verified: false, - }, - ], - }, - }); - } - }, - setUsernameAndEmail(username, email, userId) { - check(username, String); - if (Array.isArray(email)) { - email = email.shift(); - } - check(email, String); - check(userId, String); - Meteor.call('setUsername', username, userId); - Meteor.call('setEmail', email, userId); - }, - setPassword(newPassword, userId) { - check(userId, String); - check(newPassword, String); - if (Meteor.user().isAdmin) { - Accounts.setPassword(userId, newPassword); - } - }, }); if (Meteor.isServer) { Meteor.methods({ + setCreateUser(fullname, username, password, isAdmin, isActive, email) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(fullname, String); + check(username, String); + check(password, String); + check(isAdmin, String); + check(isActive, String); + check(email, String); + + const nUsersWithUsername = Users.find({ username }).count(); + const nUsersWithEmail = Users.find({ email }).count(); + if (nUsersWithUsername > 0) { + throw new Meteor.Error('username-already-taken'); + } else if (nUsersWithEmail > 0) { + throw new Meteor.Error('email-already-taken'); + } else { + Accounts.createUser({ + fullname, + username, + password, + isAdmin, + isActive, + email: email.toLowerCase(), + from: 'admin', + }); + } + } + }, + setUsername(username, userId) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(username, String); + check(userId, String); + const nUsersWithUsername = Users.find({ username }).count(); + if (nUsersWithUsername > 0) { + throw new Meteor.Error('username-already-taken'); + } else { + Users.update(userId, { $set: { username } }); + } + } + }, + setEmail(email, userId) { + if (Meteor.user() && Meteor.user().isAdmin) { + if (Array.isArray(email)) { + email = email.shift(); + } + check(email, String); + const existingUser = Users.findOne( + { 'emails.address': email }, + { fields: { _id: 1 } }, + ); + if (existingUser) { + throw new Meteor.Error('email-already-taken'); + } else { + Users.update(userId, { + $set: { + emails: [ + { + address: email, + verified: false, + }, + ], + }, + }); + } + } + }, + setUsernameAndEmail(username, email, userId) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(username, String); + if (Array.isArray(email)) { + email = email.shift(); + } + check(email, String); + check(userId, String); + Meteor.call('setUsername', username, userId); + Meteor.call('setEmail', email, userId); + } + }, + setPassword(newPassword, userId) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(userId, String); + check(newPassword, String); + if (Meteor.user().isAdmin) { + Accounts.setPassword(userId, newPassword); + } + } + }, // we accept userId, username, email inviteUserToBoard(username, boardId) { check(username, String); @@ -754,8 +762,9 @@ if (Meteor.isServer) { throw new Meteor.Error('error-user-notAllowSelf'); } else { if (posAt <= 0) throw new Meteor.Error('error-user-doesNotExist'); - if (Settings.findOne().disableRegistration) + if (Settings.findOne({ disableRegistration: true })) { throw new Meteor.Error('error-user-notCreated'); + } // Set in lowercase email before creating account const email = username.toLowerCase(); username = email.substring(0, posAt); diff --git a/server/notifications/outgoing.js b/server/notifications/outgoing.js index 5bc2c540b..9a741ea1f 100644 --- a/server/notifications/outgoing.js +++ b/server/notifications/outgoing.js @@ -1,192 +1,199 @@ -const postCatchError = Meteor.wrapAsync((url, options, resolve) => { - HTTP.post(url, options, (err, res) => { - if (err) { - resolve(null, err.response); - } else { - resolve(null, res); - } +if (Meteor.isServer) { + const postCatchError = Meteor.wrapAsync((url, options, resolve) => { + HTTP.post(url, options, (err, res) => { + if (err) { + resolve(null, err.response); + } else { + resolve(null, res); + } + }); }); -}); -const Lock = { - _lock: {}, - _timer: {}, - echoDelay: 500, // echo should be happening much faster - normalDelay: 1e3, // normally user typed comment will be much slower - ECHO: 2, - NORMAL: 1, - NULL: 0, - has(id, value) { - const existing = this._lock[id]; - let ret = this.NULL; - if (existing) { - ret = existing === value ? this.ECHO : this.NORMAL; - } - return ret; - }, - clear(id, delay) { - const previous = this._timer[id]; - if (previous) { - Meteor.clearTimeout(previous); - } - this._timer[id] = Meteor.setTimeout(() => this.unset(id), delay); - }, - set(id, value) { - const state = this.has(id, value); - let delay = this.normalDelay; - if (state === this.ECHO) { - delay = this.echoDelay; - } - if (!value) { - // user commented, we set a lock - value = 1; - } - this._lock[id] = value; - this.clear(id, delay); // always auto reset the locker after delay - }, - unset(id) { - delete this._lock[id]; - }, -}; + const Lock = { + _lock: {}, + _timer: {}, + echoDelay: 500, // echo should be happening much faster + normalDelay: 1e3, // normally user typed comment will be much slower + ECHO: 2, + NORMAL: 1, + NULL: 0, + has(id, value) { + const existing = this._lock[id]; + let ret = this.NULL; + if (existing) { + ret = existing === value ? this.ECHO : this.NORMAL; + } + return ret; + }, + clear(id, delay) { + const previous = this._timer[id]; + if (previous) { + Meteor.clearTimeout(previous); + } + this._timer[id] = Meteor.setTimeout(() => this.unset(id), delay); + }, + set(id, value) { + const state = this.has(id, value); + let delay = this.normalDelay; + if (state === this.ECHO) { + delay = this.echoDelay; + } + if (!value) { + // user commented, we set a lock + value = 1; + } + this._lock[id] = value; + this.clear(id, delay); // always auto reset the locker after delay + }, + unset(id) { + delete this._lock[id]; + }, + }; -const webhooksAtbts = (process.env.WEBHOOKS_ATTRIBUTES && - process.env.WEBHOOKS_ATTRIBUTES.split(',')) || [ - 'cardId', - 'listId', - 'oldListId', - 'boardId', - 'comment', - 'user', - 'card', - 'commentId', - 'swimlaneId', -]; -const responseFunc = data => { - const paramCommentId = data.commentId; - const paramCardId = data.cardId; - const paramBoardId = data.boardId; - const newComment = data.comment; - if (paramCardId && paramBoardId && newComment) { - // only process data with the cardid, boardid and comment text, TODO can expand other functions here to react on returned data - const comment = CardComments.findOne({ - _id: paramCommentId, - cardId: paramCardId, - boardId: paramBoardId, - }); - const board = Boards.findOne(paramBoardId); - const card = Cards.findOne(paramCardId); - if (board && card) { - if (comment) { - Lock.set(comment._id, newComment); - CardComments.direct.update(comment._id, { - $set: { + const webhooksAtbts = (process.env.WEBHOOKS_ATTRIBUTES && + process.env.WEBHOOKS_ATTRIBUTES.split(',')) || [ + 'cardId', + 'listId', + 'oldListId', + 'boardId', + 'comment', + 'user', + 'card', + 'commentId', + 'swimlaneId', + ]; + const responseFunc = data => { + const paramCommentId = data.commentId; + const paramCardId = data.cardId; + const paramBoardId = data.boardId; + const newComment = data.comment; + if (paramCardId && paramBoardId && newComment) { + // only process data with the cardid, boardid and comment text, TODO can expand other functions here to react on returned data + const comment = CardComments.findOne({ + _id: paramCommentId, + cardId: paramCardId, + boardId: paramBoardId, + }); + const board = Boards.findOne(paramBoardId); + const card = Cards.findOne(paramCardId); + if (board && card) { + if (comment) { + Lock.set(comment._id, newComment); + CardComments.direct.update(comment._id, { + $set: { + text: newComment, + }, + }); + } + } else { + const userId = data.userId; + if (userId) { + const inserted = CardComments.direct.insert({ text: newComment, - }, - }); - } - } else { - const userId = data.userId; - if (userId) { - const inserted = CardComments.direct.insert({ - text: newComment, - userId, - cardId, - boardId, - }); - Lock.set(inserted._id, newComment); - } - } - } -}; -Meteor.methods({ - outgoingWebhooks(integration, description, params) { - check(integration, Object); - check(description, String); - check(params, Object); - this.unblock(); - - // label activity did not work yet, see wekan/models/activities.js - const quoteParams = _.clone(params); - const clonedParams = _.clone(params); - [ - 'card', - 'list', - 'oldList', - 'board', - 'oldBoard', - 'comment', - 'checklist', - 'swimlane', - 'oldSwimlane', - 'label', - 'attachment', - ].forEach(key => { - if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`; - }); - - const userId = params.userId ? params.userId : integrations[0].userId; - const user = Users.findOne(userId); - const text = `${params.user} ${TAPi18n.__( - description, - quoteParams, - user.getLanguage(), - )}\n${params.url}`; - - if (text.length === 0) return; - - const value = { - text: `${text}`, - }; - - webhooksAtbts.forEach(key => { - if (params[key]) value[key] = params[key]; - }); - value.description = description; - //integrations.forEach(integration => { - const is2way = integration.type === Integrations.Const.TWOWAY; - const token = integration.token || ''; - const headers = { - 'Content-Type': 'application/json', - }; - if (token) headers['X-Wekan-Token'] = token; - const options = { - headers, - data: is2way ? { description, ...clonedParams } : value, - }; - const url = integration.url; - if (is2way) { - const cid = params.commentId; - const comment = params.comment; - const lockState = cid && Lock.has(cid, comment); - if (cid && lockState !== Lock.NULL) { - // it's a comment and there is a previous lock - return; - } else if (cid) { - Lock.set(cid, comment); // set a lock here - } - } - const response = postCatchError(url, options); - - if ( - response && - response.statusCode && - response.statusCode >= 200 && - response.statusCode < 300 - ) { - if (is2way) { - const data = response.data; // only an JSON encoded response will be actioned - if (data) { - try { - responseFunc(data); - } catch (e) { - throw new Meteor.Error('error-process-data'); - } + userId, + cardId, + boardId, + }); + Lock.set(inserted._id, newComment); } } - return response; // eslint-disable-line consistent-return - } else { - throw new Meteor.Error('error-invalid-webhook-response'); } - //}); - }, -}); + }; + Meteor.methods({ + outgoingWebhooks(integration, description, params) { + if (Meteor.user()) { + check(integration, Object); + check(description, String); + check(params, Object); + this.unblock(); + + // label activity did not work yet, see wekan/models/activities.js + const quoteParams = _.clone(params); + const clonedParams = _.clone(params); + [ + 'card', + 'list', + 'oldList', + 'board', + 'oldBoard', + 'comment', + 'checklist', + 'swimlane', + 'oldSwimlane', + 'label', + 'attachment', + ].forEach(key => { + if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`; + }); + + const userId = params.userId ? params.userId : integrations[0].userId; + const user = Users.findOne(userId); + const text = `${params.user} ${TAPi18n.__( + description, + quoteParams, + user.getLanguage(), + )}\n${params.url}`; + + if (text.length === 0) return; + + const value = { + text: `${text}`, + }; + + webhooksAtbts.forEach(key => { + if (params[key]) value[key] = params[key]; + }); + value.description = description; + //integrations.forEach(integration => { + const is2way = integration.type === Integrations.Const.TWOWAY; + const token = integration.token || ''; + const headers = { + 'Content-Type': 'application/json', + }; + if (token) headers['X-Wekan-Token'] = token; + const options = { + headers, + data: is2way ? { description, ...clonedParams } : value, + }; + + if (!Integrations.findOne({ url: integration.url })) return; + + const url = integration.url; + + if (is2way) { + const cid = params.commentId; + const comment = params.comment; + const lockState = cid && Lock.has(cid, comment); + if (cid && lockState !== Lock.NULL) { + // it's a comment and there is a previous lock + return; + } else if (cid) { + Lock.set(cid, comment); // set a lock here + } + } + const response = postCatchError(url, options); + + if ( + response && + response.statusCode && + response.statusCode >= 200 && + response.statusCode < 300 + ) { + if (is2way) { + const data = response.data; // only an JSON encoded response will be actioned + if (data) { + try { + responseFunc(data); + } catch (e) { + throw new Meteor.Error('error-process-data'); + } + } + } + return response; // eslint-disable-line consistent-return + } else { + throw new Meteor.Error('error-invalid-webhook-response'); + } + } + }, + }); +} diff --git a/server/statistics.js b/server/statistics.js index 997fd86f3..0ead840f4 100644 --- a/server/statistics.js +++ b/server/statistics.js @@ -1,68 +1,76 @@ import { MongoInternals } from 'meteor/mongo'; -Meteor.methods({ - getStatistics() { - const os = require('os'); - const pjson = require('/package.json'); - const statistics = {}; - let wekanVersion = pjson.version; - wekanVersion = wekanVersion.replace('v', ''); - statistics.version = wekanVersion; - statistics.os = { - type: os.type(), - platform: os.platform(), - arch: os.arch(), - release: os.release(), - uptime: os.uptime(), - loadavg: os.loadavg(), - totalmem: os.totalmem(), - freemem: os.freemem(), - cpus: os.cpus(), - }; - let nodeVersion = process.version; - nodeVersion = nodeVersion.replace('v', ''); - statistics.process = { - nodeVersion, - pid: process.pid, - uptime: process.uptime(), - }; - // Remove beginning of Meteor release text METEOR@ - let meteorVersion = Meteor.release; - meteorVersion = meteorVersion.replace('METEOR@', ''); - statistics.meteor = { - meteorVersion, - }; - // Thanks to RocketChat for MongoDB version detection ! - // https://github.com/RocketChat/Rocket.Chat/blob/develop/app/utils/server/functions/getMongoInfo.js - let mongoVersion; - let mongoStorageEngine; - let mongoOplogEnabled; - try { - const { mongo } = MongoInternals.defaultRemoteCollectionDriver(); - oplogEnabled = Boolean( - mongo._oplogHandle && mongo._oplogHandle.onOplogEntry, - ); - const { version, storageEngine } = Promise.await( - mongo.db.command({ serverStatus: 1 }), - ); - mongoVersion = version; - mongoStorageEngine = storageEngine.name; - mongoOplogEnabled = oplogEnabled; - } catch (e) { - try { - const { version } = Promise.await(mongo.db.command({ buildinfo: 1 })); - mongoVersion = version; - mongoStorageEngine = 'unknown'; - } catch (e) { - mongoVersion = 'unknown'; - mongoStorageEngine = 'unknown'; +if (Meteor.isServer) { + Meteor.methods({ + getStatistics() { + if (Meteor.user() && Meteor.user().isAdmin) { + const os = require('os'); + const pjson = require('/package.json'); + const statistics = {}; + let wekanVersion = pjson.version; + wekanVersion = wekanVersion.replace('v', ''); + statistics.version = wekanVersion; + statistics.os = { + type: os.type(), + platform: os.platform(), + arch: os.arch(), + release: os.release(), + uptime: os.uptime(), + loadavg: os.loadavg(), + totalmem: os.totalmem(), + freemem: os.freemem(), + cpus: os.cpus(), + }; + let nodeVersion = process.version; + nodeVersion = nodeVersion.replace('v', ''); + statistics.process = { + nodeVersion, + pid: process.pid, + uptime: process.uptime(), + }; + // Remove beginning of Meteor release text METEOR@ + let meteorVersion = Meteor.release; + meteorVersion = meteorVersion.replace('METEOR@', ''); + statistics.meteor = { + meteorVersion, + }; + // Thanks to RocketChat for MongoDB version detection ! + // https://github.com/RocketChat/Rocket.Chat/blob/develop/app/utils/server/functions/getMongoInfo.js + let mongoVersion; + let mongoStorageEngine; + let mongoOplogEnabled; + try { + const { mongo } = MongoInternals.defaultRemoteCollectionDriver(); + oplogEnabled = Boolean( + mongo._oplogHandle && mongo._oplogHandle.onOplogEntry, + ); + const { version, storageEngine } = Promise.await( + mongo.db.command({ serverStatus: 1 }), + ); + mongoVersion = version; + mongoStorageEngine = storageEngine.name; + mongoOplogEnabled = oplogEnabled; + } catch (e) { + try { + const { version } = Promise.await( + mongo.db.command({ buildinfo: 1 }), + ); + mongoVersion = version; + mongoStorageEngine = 'unknown'; + } catch (e) { + mongoVersion = 'unknown'; + mongoStorageEngine = 'unknown'; + } + } + statistics.mongo = { + mongoVersion, + mongoStorageEngine, + mongoOplogEnabled, + }; + return statistics; + } else { + return false; } - } - statistics.mongo = { - mongoVersion, - mongoStorageEngine, - mongoOplogEnabled, - }; - return statistics; - }, -}); + }, + }); +} From 897506eeac2cf9f71f9c293f0a7ee055d8caa095 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 1 Mar 2020 21:30:43 +0200 Subject: [PATCH 05/70] v3.81 --- .snap-meteor-1.8/package-lock.json | 2 +- .snap-meteor-1.8/package.json | 2 +- CHANGELOG.md | 28 ++++++++++++++++++++++++++-- Stackerfile.yml | 2 +- package-lock.json | 2 +- package.json | 2 +- public/api/wekan.html | 4 ++-- public/api/wekan.yml | 2 +- sandstorm-pkgdef.capnp | 4 ++-- 9 files changed, 36 insertions(+), 12 deletions(-) diff --git a/.snap-meteor-1.8/package-lock.json b/.snap-meteor-1.8/package-lock.json index de06db8e6..8682fb2dd 100644 --- a/.snap-meteor-1.8/package-lock.json +++ b/.snap-meteor-1.8/package-lock.json @@ -1,6 +1,6 @@ { "name": "wekan", - "version": "v3.80.0", + "version": "v3.81.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/.snap-meteor-1.8/package.json b/.snap-meteor-1.8/package.json index a5897c32d..ae1c0ea5c 100644 --- a/.snap-meteor-1.8/package.json +++ b/.snap-meteor-1.8/package.json @@ -1,6 +1,6 @@ { "name": "wekan", - "version": "v3.80.0", + "version": "v3.81.0", "description": "Open-Source kanban", "private": true, "scripts": { diff --git a/CHANGELOG.md b/CHANGELOG.md index d430f6728..c5cc92450 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,34 @@ -# Upcoming Wekan release +# v3.81 2020-03-01 Wekan release -This release tries to fix the following bugs: +This release [fixes](https://github.com/wekan/wekan/commit/aac7c380c8c389b0683b2bd64e2cc856993f0e30) the following CRITICAL SECURITY VULNERABILITIES and other bugs: + +- Fix critical and moderate security vulnerabilities reported at 2020-02-26 with + responsible disclosure by [Dejan Zelic](https://twitter.com/dejandayoff), + Justin Benjamin and others at [Offensive Security](https://twitter.com/offsectraining), + that follow standard 90 days before public disclosure. + Thanks to xet7. +- Fix webhook error that prevented some card etc deleting from web UI of board. + Thanks to xet7. +- Add missing Font Awesome icon to Board Settings Menu. + Thanks to xet7. +- Remove autofocus from many form input boxes so that they would not cause warnings. + Thanks to xet7. + +and does the following upgrades: + +- [Upgrade Meteor to 1.10-rc.2](https://github.com/wekan/wekan/commit/26b521e86e6ac40b7ba25bbe8dac7bf4d48d43ce). + Thanks to xet7. +- [Try to make Meteor build time shorter by excluding legacy and cordova. This was made possible by + Meteor 1.10-rc.2](https://github.com/wekan/wekan/commit/0d3002f69d97e646fa7368bfdade4f78c51e9884). + Thanks to xet7. + +and fixes the following bugs: - [Try to fix afterwards loading of cards by adding fallback when requestIdleCallback is not available](https://github.com/wekan/wekan/commit/2b9540ce02de604bf84ea082f2dcb1d01673708c). Thanks to xet7. +- [Make profile.initials available in publications](https://github.com/wekan/wekan/pull/2948). + Thanks to NicoP-S. Thanks to above GitHub users for their contributions and translators for their translations. diff --git a/Stackerfile.yml b/Stackerfile.yml index 8b544acef..80d3733d5 100644 --- a/Stackerfile.yml +++ b/Stackerfile.yml @@ -1,5 +1,5 @@ appId: wekan-public/apps/77b94f60-dec9-0136-304e-16ff53095928 -appVersion: "v3.80.0" +appVersion: "v3.81.0" files: userUploads: - README.md diff --git a/package-lock.json b/package-lock.json index 53a819181..d141fbc77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "wekan", - "version": "v3.80.0", + "version": "v3.81.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 19bc387e8..e771e0dc7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wekan", - "version": "v3.80.0", + "version": "v3.81.0", "description": "Open-Source kanban", "private": true, "scripts": { diff --git a/public/api/wekan.html b/public/api/wekan.html index ed25869ea..e50da96d8 100644 --- a/public/api/wekan.html +++ b/public/api/wekan.html @@ -1524,7 +1524,7 @@ var n=this.pipeline.run(e.tokenizer(t)),r=new e.Vector,i=[],o=this._fields.reduc