diff --git a/.github/history-manual.json b/.github/history-manual.json index 336285f844a..c970f31f718 100644 --- a/.github/history-manual.json +++ b/.github/history-manual.json @@ -88,5 +88,14 @@ "KevLehman", "renatobecker" ] + }], + "3.14.2": [{ + "title": "[FIX] Security Hotfix (https://docs.rocket.chat/guides/security/security-updates)", + "userLogin": "sampaiodiego", + "contributors": [ + "sampaiodiego", + "KevLehman", + "g-thome" + ] }] } diff --git a/.github/history.json b/.github/history.json index 6172429f2ae..4c2fc56abee 100644 --- a/.github/history.json +++ b/.github/history.json @@ -59612,6 +59612,26 @@ ] } ] + }, + "3.14.2": { + "node_version": "12.22.1", + "npm_version": "6.14.1", + "apps_engine_version": "1.25.0", + "mongo_versions": [ + "3.4", + "3.6", + "4.0" + ], + "pull_requests": [ + { + "pr": "48", + "title": "Update README.md", + "userLogin": "hehsiao", + "contributors": [ + "hehsiao" + ] + } + ] } } -} +} \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md index 25dee4fadfe..e1f2a7b0a47 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,36 @@ +# 3.14.2 +`2021-05-25 ยท 1 ๐Ÿ› ยท 1 ๐Ÿ” ยท 4 ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป` + +### Engine versions +- Node: `12.22.1` +- NPM: `6.14.1` +- MongoDB: `3.4, 3.6, 4.0` +- Apps-Engine: `1.25.0` + +### ๐Ÿ› Bug fixes + + +- Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) + +
+๐Ÿ” Minor changes + + +- Update README.md ([#48](https://github.com/RocketChat/Rocket.Chat/pull/48) by [@hehsiao](https://github.com/hehsiao)) + +
+ +### ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป Contributors ๐Ÿ˜ + +- [@hehsiao](https://github.com/hehsiao) + +### ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป Core Team ๐Ÿค“ + +- [@KevLehman](https://github.com/KevLehman) +- [@g-thome](https://github.com/g-thome) +- [@sampaiodiego](https://github.com/sampaiodiego) + # 3.14.1 `2021-05-19 ยท 1 ๐ŸŽ‰ ยท 2 ๐Ÿš€ ยท 4 ๐Ÿ› ยท 3 ๐Ÿ” ยท 7 ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป` diff --git a/app/api/server/lib/cleanQuery.ts b/app/api/server/lib/cleanQuery.ts index 86f5b288e81..1fe250e9460 100644 --- a/app/api/server/lib/cleanQuery.ts +++ b/app/api/server/lib/cleanQuery.ts @@ -18,7 +18,7 @@ export function clean(v: Query, allowList: string[] = []): Query { if (v instanceof Object) { /* eslint-disable guard-for-in */ for (const key in typedParam) { - if (/^$/.test(key) && !allowList.includes(key)) { + if (key.startsWith('$') && !allowList.includes(key)) { delete typedParam[key]; } else { clean(typedParam[key], allowList); diff --git a/app/lib/server/functions/createRoom.js b/app/lib/server/functions/createRoom.js index 5d61b5e8d04..56f1676ad63 100644 --- a/app/lib/server/functions/createRoom.js +++ b/app/lib/server/functions/createRoom.js @@ -51,6 +51,7 @@ export const createRoom = function(type, name, owner, members = [], readOnly, { } let room = { + ...extraData, name: getValidRoomName(name, null, validRoomNameOptions), fname: name, t: type, @@ -60,7 +61,6 @@ export const createRoom = function(type, name, owner, members = [], readOnly, { _id: owner._id, username: owner.username, }, - ...extraData, ts: now, ro: readOnly === true, }; diff --git a/app/user-data-download/server/cronProcessDownloads.js b/app/user-data-download/server/cronProcessDownloads.js index 95e8f6ed360..0c8e65d4c0e 100644 --- a/app/user-data-download/server/cronProcessDownloads.js +++ b/app/user-data-download/server/cronProcessDownloads.js @@ -7,6 +7,7 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { SyncedCron } from 'meteor/littledata:synced-cron'; import archiver from 'archiver'; import moment from 'moment'; +import { v4 as uuidv4 } from 'uuid'; import { settings } from '../../settings/server'; import { Subscriptions, Rooms, Users, Uploads, Messages, UserDataFiles, ExportOperations, Avatars } from '../../models/server'; @@ -504,12 +505,14 @@ const continueExportOperation = async function(exportOperation) { } } + const generatedFileName = uuidv4(); + if (exportOperation.status === 'downloading') { exportOperation.fileList.forEach((attachmentData) => { copyFile(attachmentData, exportOperation.assetsPath); }); - const targetFile = joinPath(zipFolder, `${ exportOperation.userId }.zip`); + const targetFile = joinPath(zipFolder, `${ generatedFileName }.zip`); if (await fsExists(targetFile)) { await fsUnlink(targetFile); } @@ -520,7 +523,7 @@ const continueExportOperation = async function(exportOperation) { if (exportOperation.status === 'compressing') { createDir(zipFolder); - exportOperation.generatedFile = joinPath(zipFolder, `${ exportOperation.userId }.zip`); + exportOperation.generatedFile = joinPath(zipFolder, `${ generatedFileName }.zip`); if (!await fsExists(exportOperation.generatedFile)) { await makeZipFile(exportOperation.exportPath, exportOperation.generatedFile); } diff --git a/app/utils/lib/getValidRoomName.js b/app/utils/lib/getValidRoomName.js index e1e216756ba..e67d4540aef 100644 --- a/app/utils/lib/getValidRoomName.js +++ b/app/utils/lib/getValidRoomName.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; import limax from 'limax'; +import { escapeHTML } from '@rocket.chat/string-helpers'; import { settings } from '../../settings'; import { Rooms } from '../../models'; @@ -9,17 +10,18 @@ export const getValidRoomName = (displayName, rid = '', options = {}) => { let slugifiedName = displayName; if (settings.get('UI_Allow_room_names_with_special_chars')) { + const cleanName = limax(displayName); if (options.allowDuplicates !== true) { const room = Rooms.findOneByDisplayName(displayName); if (room && room._id !== rid) { if (room.archived) { - throw new Meteor.Error('error-archived-duplicate-name', `There's an archived channel with name ${ displayName }`, { function: 'RocketChat.getValidRoomName', channel_name: displayName }); + throw new Meteor.Error('error-archived-duplicate-name', `There's an archived channel with name ${ cleanName }`, { function: 'RocketChat.getValidRoomName', channel_name: cleanName }); } else { - throw new Meteor.Error('error-duplicate-channel-name', `A channel with name '${ displayName }' exists`, { function: 'RocketChat.getValidRoomName', channel_name: displayName }); + throw new Meteor.Error('error-duplicate-channel-name', `A channel with name '${ cleanName }' exists`, { function: 'RocketChat.getValidRoomName', channel_name: cleanName }); } } } - slugifiedName = limax(displayName); + slugifiedName = cleanName; } let nameValidation; @@ -35,9 +37,9 @@ export const getValidRoomName = (displayName, rid = '', options = {}) => { } if (!nameValidation.test(slugifiedName) || !validateName(slugifiedName)) { - throw new Meteor.Error('error-invalid-room-name', `${ slugifiedName } is not a valid room name.`, { + throw new Meteor.Error('error-invalid-room-name', `${ escapeHTML(slugifiedName) } is not a valid room name.`, { function: 'RocketChat.getValidRoomName', - channel_name: slugifiedName, + channel_name: escapeHTML(slugifiedName), }); } @@ -52,9 +54,9 @@ export const getValidRoomName = (displayName, rid = '', options = {}) => { } slugifiedName = tmpName; } else if (room.archived) { - throw new Meteor.Error('error-archived-duplicate-name', `There's an archived channel with name ${ slugifiedName }`, { function: 'RocketChat.getValidRoomName', channel_name: slugifiedName }); + throw new Meteor.Error('error-archived-duplicate-name', `There's an archived channel with name ${ escapeHTML(slugifiedName) }`, { function: 'RocketChat.getValidRoomName', channel_name: escapeHTML(slugifiedName) }); } else { - throw new Meteor.Error('error-duplicate-channel-name', `A channel with name '${ slugifiedName }' exists`, { function: 'RocketChat.getValidRoomName', channel_name: slugifiedName }); + throw new Meteor.Error('error-duplicate-channel-name', `A channel with name '${ escapeHTML(slugifiedName) }' exists`, { function: 'RocketChat.getValidRoomName', channel_name: escapeHTML(slugifiedName) }); } } } diff --git a/packages/meteor-jalik-ufs/ufs-methods.js b/packages/meteor-jalik-ufs/ufs-methods.js index 664772d5e69..409a40b7727 100644 --- a/packages/meteor-jalik-ufs/ufs-methods.js +++ b/packages/meteor-jalik-ufs/ufs-methods.js @@ -221,6 +221,17 @@ if (Meteor.isServer) { throw new Meteor.Error('invalid-store', 'The store does not exist'); } + let parsedUrl; + try { + parsedUrl = new URL(url); + } catch (e) { + throw new Meteor.Error('invalid-url', 'The url is not valid'); + } + + if (['localhost', '127.0.0.1', '0.0.0.0'].includes(parsedUrl.hostname)) { + throw new Meteor.Error('invalid-url', 'URL cannot reference localhost'); + } + // Extract file info if (!file.name) { file.name = url.replace(/\?.*$/, '').split('/').pop();