From 8d8d102b951f8f8077aad9d730c52fe9042b8af0 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Fri, 12 May 2023 13:55:30 +0000 Subject: [PATCH 1/8] chore: incrementing version number - v3.1.2 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 5bb5361676..90b4916591 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "3.1.1", + "version": "3.1.2", "homepage": "https://www.nodebb.org", "repository": { "type": "git", From 5b3987828b40d8f457a3f1b3499cd710ab97b786 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Fri, 12 May 2023 13:55:31 +0000 Subject: [PATCH 2/8] chore: update changelog for v3.1.2 --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02db5f4bac..e846b96420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +#### v3.1.2 (2023-05-12) + +##### Chores + +* incrementing version number - v3.1.1 (40250733) +* update changelog for v3.1.1 (ccd6f48c) +* incrementing version number - v3.1.0 (0cb386bd) +* incrementing version number - v3.0.1 (26f6ea49) +* incrementing version number - v3.0.0 (224e08cd) + +##### Bug Fixes + +* #11595, use default value (28740de7) + #### v3.1.1 (2023-05-11) ##### Chores From cd7fdfcefd8fa8343821b795b495056f95ccf20c Mon Sep 17 00:00:00 2001 From: Opliko Date: Fri, 12 May 2023 17:48:46 +0200 Subject: [PATCH 3/8] ci: tag with branch name if not default branch This should prevent failed runs like https://github.com/NodeBB/NodeBB/actions/runs/4959641483 --- .github/workflows/docker.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fda479175b..2451d9515c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -47,6 +47,7 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.x type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=branch,enable={{#if is_default_branch}}false{{else}}true{{/if}} - name: Build and push Docker images uses: docker/build-push-action@v4 From 85d104c3752c377bfd40052a552b36779baa328f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 11 May 2023 22:39:37 -0400 Subject: [PATCH 4/8] fix: #11594 set the order of theme to same as the on it's replacing --- src/meta/themes.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/meta/themes.js b/src/meta/themes.js index d757cfb9ec..6cce964832 100644 --- a/src/meta/themes.js +++ b/src/meta/themes.js @@ -89,9 +89,9 @@ Themes.set = async (data) => { switch (data.type) { case 'local': { const current = await Meta.configs.get('theme:id'); + const score = await db.sortedSetScore('plugins:active', current); await db.sortedSetRemove('plugins:active', current); - const numPlugins = await db.sortedSetCard('plugins:active'); - await db.sortedSetAdd('plugins:active', numPlugins, data.id); + await db.sortedSetAdd('plugins:active', score || 0, data.id); if (current !== data.id) { const pathToThemeJson = path.join(nconf.get('themes_path'), data.id, 'theme.json'); @@ -103,9 +103,9 @@ Themes.set = async (data) => { config = JSON.parse(config); const activePluginsConfig = nconf.get('plugins:active'); if (!activePluginsConfig) { + const score = await db.sortedSetScore('plugins:active', current); await db.sortedSetRemove('plugins:active', current); - const numPlugins = await db.sortedSetCard('plugins:active'); - await db.sortedSetAdd('plugins:active', numPlugins, data.id); + await db.sortedSetAdd('plugins:active', score || 0, data.id); } else if (!activePluginsConfig.includes(data.id)) { // This prevents changing theme when configuration doesn't include it, but allows it otherwise winston.error(`When defining active plugins in configuration, changing themes requires adding the theme '${data.id}' to the list of active plugins before updating it in the ACP`); From 412a1ecf93067c3c373ca0a14e334a85a8a400a2 Mon Sep 17 00:00:00 2001 From: Opliko Date: Sat, 13 May 2023 20:36:59 +0200 Subject: [PATCH 5/8] ci: use GitHub Actions expression instead of handlebars template (#11599) Apparently Docker Meta Actions overrides, not merely adds, handlebars helpers. So `#if` just doesn't exist and errors out... As such here is an equivalent using GitHub Actions expression (and this time I tested that it works...) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 2451d9515c..ae2e03501a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -47,7 +47,7 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.x type=raw,value=latest,enable={{is_default_branch}} - type=ref,event=branch,enable={{#if is_default_branch}}false{{else}}true{{/if}} + type=ref,event=branch,enable=${{ github.event.repository.default_branch != github.ref }} - name: Build and push Docker images uses: docker/build-push-action@v4 From 8eed5a841375fb7b88dad6ae269c4481d75e1681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 13 May 2023 15:16:36 -0400 Subject: [PATCH 6/8] fix: #11600, prevent helmet crash on startup --- src/webserver.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/webserver.js b/src/webserver.js index c0a1c8e537..55e263486a 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -199,13 +199,17 @@ function setupHelmet(app) { } if (meta.config['hsts-enabled']) { options.hsts = { - maxAge: meta.config['hsts-maxage'], + maxAge: Math.max(0, meta.config['hsts-maxage']), includeSubDomains: !!meta.config['hsts-subdomains'], preload: !!meta.config['hsts-preload'], }; } - app.use(helmet(options)); + try { + app.use(helmet(options)); + } catch (err) { + winston.error(`[startup] unable to initialize helmet \n${err.stack}`); + } } From d55cd464a0b49e9f0230692fe416464770b58457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 13 May 2023 21:26:34 -0400 Subject: [PATCH 7/8] fix: #11601, dont trigger edit if chat input has text pressing escape will cancel edit --- public/src/client/chats.js | 5 ++--- public/src/client/chats/messages.js | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 98833e1e92..ecb724b3c9 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -235,15 +235,14 @@ define('forum/chats', [ } }); mousetrap.bind('up', function (e) { - if (e.target === components.get('chat/input').get(0)) { + const inputEl = components.get('chat/input'); + if (e.target === inputEl.get(0) && !inputEl.val()) { // Retrieve message id from messages list const message = components.get('chat/messages').find('.chat-message[data-self="1"]').last(); if (!message.length) { return; } const lastMid = message.attr('data-mid'); - const inputEl = components.get('chat/input'); - messages.prepEdit(inputEl, lastMid, ajaxify.data.roomId); } }); diff --git a/public/src/client/chats/messages.js b/public/src/client/chats/messages.js index e3bccacdca..b17a8f772b 100644 --- a/public/src/client/chats/messages.js +++ b/public/src/client/chats/messages.js @@ -176,6 +176,11 @@ define('forum/chats/messages', [ autoCompleteEl.destroy(); } } + textarea.on('keyup', (e) => { + if (e.key === 'Escape') { + finishEdit(); + } + }); editEl.find('[data-action="cancel"]').on('click', finishEdit); editEl.find('[data-action="save"]').on('click', function () { From 51096ad2345fb1d1380bec0a447113489ef6c359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 15 May 2023 11:12:46 -0400 Subject: [PATCH 8/8] poc: use csrf_token in ws handshake (#11573) --- public/src/sockets.js | 3 +++ src/middleware/csrf.js | 6 +++++- src/socket.io/index.js | 38 +++++++++++++++++++++++++------------- test/helpers/index.js | 5 ++++- test/socket.io.js | 2 +- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/public/src/sockets.js b/public/src/sockets.js index 6d3ccfbb80..acd3ba3dbe 100644 --- a/public/src/sockets.js +++ b/public/src/sockets.js @@ -15,6 +15,9 @@ app = window.app || {}; reconnectionDelay: config.reconnectionDelay, transports: config.socketioTransports, path: config.relative_path + '/socket.io', + query: { + _csrf: config.csrf_token, + }, }; window.socket = io(config.websocketAddress, ioParams); diff --git a/src/middleware/csrf.js b/src/middleware/csrf.js index f6af0c625b..be5b0761fe 100644 --- a/src/middleware/csrf.js +++ b/src/middleware/csrf.js @@ -5,12 +5,15 @@ const { csrfSync } = require('csrf-sync'); const { generateToken, csrfSynchronisedProtection, + isRequestValid, } = csrfSync({ getTokenFromRequest: (req) => { if (req.headers['x-csrf-token']) { return req.headers['x-csrf-token']; - } else if (req.body.csrf_token) { + } else if (req.body && req.body.csrf_token) { return req.body.csrf_token; + } else if (req.query) { + return req.query._csrf; } }, size: 64, @@ -19,4 +22,5 @@ const { module.exports = { generateToken, csrfSynchronisedProtection, + isRequestValid, }; diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 963267ed9a..d457afefbc 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -34,13 +34,25 @@ Sockets.init = async function (server) { } } - io.use(authorize); - io.on('connection', onConnection); const opts = { transports: nconf.get('socket.io:transports') || ['polling', 'websocket'], cookie: false, + allowRequest: (req, callback) => { + authorize(req, (err) => { + if (err) { + return callback(err); + } + const csrf = require('../middleware/csrf'); + const isValid = csrf.isRequestValid({ + session: req.session || {}, + query: req._query, + headers: req.headers, + }); + callback(null, isValid); + }); + }, }; /* * Restrict socket.io listener to cookie domain. If none is set, infer based on url. @@ -62,7 +74,11 @@ Sockets.init = async function (server) { }; function onConnection(socket) { - socket.ip = (socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress || '').split(',')[0]; + socket.uid = socket.request.uid; + socket.ip = ( + socket.request.headers['x-forwarded-for'] || + socket.request.connection.remoteAddress || '' + ).split(',')[0]; socket.request.ip = socket.ip; logger.io_one(socket, socket.uid); @@ -231,9 +247,7 @@ async function validateSession(socket, errorMsg) { const cookieParserAsync = util.promisify((req, callback) => cookieParser(req, {}, err => callback(err))); -async function authorize(socket, callback) { - const { request } = socket; - +async function authorize(request, callback) { if (!request) { return callback(new Error('[[error:not-authorized]]')); } @@ -246,15 +260,13 @@ async function authorize(socket, callback) { }); const sessionData = await getSessionAsync(sessionId); - + request.session = sessionData; + let uid = 0; if (sessionData && sessionData.passport && sessionData.passport.user) { - request.session = sessionData; - socket.uid = parseInt(sessionData.passport.user, 10); - } else { - socket.uid = 0; + uid = parseInt(sessionData.passport.user, 10); } - request.uid = socket.uid; - callback(); + request.uid = uid; + callback(null, uid); } Sockets.in = function (room) { diff --git a/test/helpers/index.js b/test/helpers/index.js index b79bf66e2d..4adcb1b0dd 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -96,7 +96,7 @@ helpers.logoutUser = function (jar, callback) { }); }; -helpers.connectSocketIO = function (res, callback) { +helpers.connectSocketIO = function (res, csrf_token, callback) { const io = require('socket.io-client'); let cookies = res.headers['set-cookie']; cookies = cookies.filter(c => /express.sid=[^;]+;/.test(c)); @@ -107,6 +107,9 @@ helpers.connectSocketIO = function (res, callback) { Origin: nconf.get('url'), Cookie: cookie, }, + query: { + _csrf: csrf_token, + }, }); socket.on('connect', () => { diff --git a/test/socket.io.js b/test/socket.io.js index 726f6f71ad..4e2292d4e5 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -73,7 +73,7 @@ describe('socket.io', () => { }, (err, res) => { assert.ifError(err); - helpers.connectSocketIO(res, (err, _io) => { + helpers.connectSocketIO(res, body.csrf_token, (err, _io) => { io = _io; assert.ifError(err);