From 31be083e86060b5903d9f3ba6cba65f6c86a9c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 12 May 2025 09:12:51 -0400 Subject: [PATCH 1/5] fix: #13407, don't restart user jobs if jobsDisabled=true on that process --- src/socket.io/admin/user.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js index 9367ff9c53..698f854597 100644 --- a/src/socket.io/admin/user.js +++ b/src/socket.io/admin/user.js @@ -2,6 +2,8 @@ const async = require('async'); const winston = require('winston'); +const nconf = require('nconf'); +const pubsub = require('../../pubsub'); const db = require('../../database'); const groups = require('../../groups'); @@ -129,8 +131,15 @@ User.forcePasswordReset = async function (socket, uids) { uids.forEach(uid => sockets.in(`uid_${uid}`).emit('event:logout')); }; +pubsub.on('admin.user.restartJobs', () => { + if (nconf.get('runJobs')) { + winston.verbose('[user/jobs] Restarting jobs...'); + user.startJobs(); + } +}); + User.restartJobs = async function () { - user.startJobs(); + pubsub.publish('admin.user.restartJobs', {}); }; User.loadGroups = async function (socket, uids) { From 285d438cb3f98a729765a1bde6b12efa535a3940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 12 May 2025 09:30:33 -0400 Subject: [PATCH 2/5] fix: escape flag filters --- src/controllers/mods.js | 5 +++-- test/flags.js | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 4976dd9e82..c0abc18fe8 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -1,6 +1,7 @@ 'use strict'; const _ = require('lodash'); +const validator = require('validator'); const user = require('../user'); const groups = require('../groups'); @@ -43,9 +44,9 @@ modsController.flags.list = async function (req, res) { filters = filters.reduce((memo, cur) => { if (req.query.hasOwnProperty(cur)) { if (typeof req.query[cur] === 'string' && req.query[cur].trim() !== '') { - memo[cur] = req.query[cur].trim(); + memo[cur] = validator.escape(String(req.query[cur].trim())); } else if (Array.isArray(req.query[cur]) && req.query[cur].length) { - memo[cur] = req.query[cur]; + memo[cur] = req.query[cur].map(item => validator.escape(String(item).trim())); } } diff --git a/test/flags.js b/test/flags.js index ee150a10c4..aad4802e7a 100644 --- a/test/flags.js +++ b/test/flags.js @@ -928,6 +928,11 @@ describe('Flags', () => { assert.strictEqual(flagData.reports[0].value, '"<script>alert('ok');</script>'); }); + it('should escape filters', async () => { + const { body } = await request.get(`${nconf.get('url')}/api/flags?quick="`, { jar }); + assert.strictEqual(body.filters.quick, '"<script>alert('foo');</script>'); + }); + it('should not allow flagging post in private category', async () => { const category = await Categories.create({ name: 'private category' }); @@ -1185,5 +1190,7 @@ describe('Flags', () => { } }); }); + + }); }); From 16504bad8100aa25327dd8b8b26483df9e087b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 12 May 2025 10:02:59 -0400 Subject: [PATCH 3/5] fix: sql injection in sortedSetScan --- src/database/postgres/sorted.js | 4 ++-- test/database/sorted.js | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/database/postgres/sorted.js b/src/database/postgres/sorted.js index 916425e0c1..9f59224497 100644 --- a/src/database/postgres/sorted.js +++ b/src/database/postgres/sorted.js @@ -707,9 +707,9 @@ SELECT z."value", ON o."_key" = z."_key" AND o."type" = z."type" WHERE o."_key" = $1::TEXT - AND z."value" LIKE '${match}' + AND z."value" LIKE $3 LIMIT $2::INTEGER`, - values: [params.key, params.limit], + values: [params.key, params.limit, match], }); if (!params.withScores) { return res.rows.map(r => r.value); diff --git a/test/database/sorted.js b/test/database/sorted.js index 33d3e4c4b5..b98d969730 100644 --- a/test/database/sorted.js +++ b/test/database/sorted.js @@ -78,6 +78,21 @@ describe('Sorted Set methods', () => { assert(data.includes('ddb')); assert(data.includes('adb')); }); + + it('should not error with invalid input', async () => { + const query = `-3217' +OR 1251=CAST((CHR(113)||CHR(98)||CHR(118)||CHR(98)||CHR(113))||(SELECT +(CASE WHEN (1251=1251) THEN 1 ELSE 0 +END))::text||(CHR(113)||CHR(113)||CHR(118)||CHR(98)||CHR(113)) AS +NUMERIC)-- WsPn&query[cid]=-1&parentCid=0&selectedCids[]=-1&privilege=topics:read&states[]=watching&states[]=tracking&states[]=notwatching&showLinks=`; + const match = `*${query.toLowerCase()}*`; + const data = await db.getSortedSetScan({ + key: 'categories:name', + match: match, + limit: 500, + }); + assert.strictEqual(data.length, 0); + }); }); describe('sortedSetAdd()', () => { From dfa213298b7dce55845c18eee657ad4d32145ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 12 May 2025 10:28:26 -0400 Subject: [PATCH 4/5] refactor: call verify if request is POST --- src/middleware/activitypub.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/middleware/activitypub.js b/src/middleware/activitypub.js index 490715dbcb..b38caa552a 100644 --- a/src/middleware/activitypub.js +++ b/src/middleware/activitypub.js @@ -33,10 +33,12 @@ middleware.verify = async function (req, res, next) { return next(); } - const verified = await activitypub.verify(req); - if (!verified && req.method === 'POST') { - activitypub.helpers.log('[middleware/activitypub] HTTP signature verification failed.'); - return res.sendStatus(400); + if (req.method === 'POST') { + const verified = await activitypub.verify(req); + if (!verified) { + activitypub.helpers.log('[middleware/activitypub] HTTP signature verification failed.'); + return res.sendStatus(400); + } } // Set calling user From 00668bdc342f22b1ef263ca2d6003b12bbb194af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 12 May 2025 10:29:32 -0400 Subject: [PATCH 5/5] refactor: wrap ap routes in try/catch --- src/routes/activitypub.js | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/routes/activitypub.js b/src/routes/activitypub.js index b6a516b481..760287e102 100644 --- a/src/routes/activitypub.js +++ b/src/routes/activitypub.js @@ -25,28 +25,28 @@ module.exports = function (app, middleware, controllers) { middleware.activitypub.normalize, ]; - app.get('/actor', middlewares, controllers.activitypub.actors.application); - app.post('/inbox', [...middlewares, ...inboxMiddlewares], controllers.activitypub.postInbox); + app.get('/actor', middlewares, helpers.tryRoute(controllers.activitypub.actors.application)); + app.post('/inbox', [...middlewares, ...inboxMiddlewares], helpers.tryRoute(controllers.activitypub.postInbox)); - app.get('/uid/:uid', [...middlewares, middleware.assert.user], controllers.activitypub.actors.user); - app.get('/user/:userslug', [...middlewares, middleware.exposeUid, middleware.assert.user], controllers.activitypub.actors.userBySlug); - app.get('/uid/:uid/inbox', [...middlewares, middleware.assert.user], controllers.activitypub.getInbox); - app.post('/uid/:uid/inbox', [...middlewares, middleware.assert.user, ...inboxMiddlewares], controllers.activitypub.postInbox); - app.get('/uid/:uid/outbox', [...middlewares, middleware.assert.user], controllers.activitypub.getOutbox); - app.post('/uid/:uid/outbox', [...middlewares, middleware.assert.user], controllers.activitypub.postOutbox); - app.get('/uid/:uid/following', [...middlewares, middleware.assert.user], controllers.activitypub.getFollowing); - app.get('/uid/:uid/followers', [...middlewares, middleware.assert.user], controllers.activitypub.getFollowers); + app.get('/uid/:uid', [...middlewares, middleware.assert.user], helpers.tryRoute(controllers.activitypub.actors.user)); + app.get('/user/:userslug', [...middlewares, middleware.exposeUid, middleware.assert.user], helpers.tryRoute(controllers.activitypub.actors.userBySlug)); + app.get('/uid/:uid/inbox', [...middlewares, middleware.assert.user], helpers.tryRoute(controllers.activitypub.getInbox)); + app.post('/uid/:uid/inbox', [...middlewares, middleware.assert.user, ...inboxMiddlewares], helpers.tryRoute(controllers.activitypub.postInbox)); + app.get('/uid/:uid/outbox', [...middlewares, middleware.assert.user], helpers.tryRoute(controllers.activitypub.getOutbox)); + app.post('/uid/:uid/outbox', [...middlewares, middleware.assert.user], helpers.tryRoute(controllers.activitypub.postOutbox)); + app.get('/uid/:uid/following', [...middlewares, middleware.assert.user], helpers.tryRoute(controllers.activitypub.getFollowing)); + app.get('/uid/:uid/followers', [...middlewares, middleware.assert.user], helpers.tryRoute(controllers.activitypub.getFollowers)); - app.get('/post/:pid', [...middlewares, middleware.assert.post], controllers.activitypub.actors.note); - app.get('/post/:pid/replies', [...middlewares, middleware.assert.post], controllers.activitypub.actors.replies); + app.get('/post/:pid', [...middlewares, middleware.assert.post], helpers.tryRoute(controllers.activitypub.actors.note)); + app.get('/post/:pid/replies', [...middlewares, middleware.assert.post], helpers.tryRoute(controllers.activitypub.actors.replies)); - app.get('/topic/:tid/:slug?', [...middlewares, middleware.assert.topic], controllers.activitypub.actors.topic); + app.get('/topic/:tid/:slug?', [...middlewares, middleware.assert.topic], helpers.tryRoute(controllers.activitypub.actors.topic)); - app.get('/category/:cid/inbox', [...middlewares, middleware.assert.category], controllers.activitypub.getInbox); - app.post('/category/:cid/inbox', [...inboxMiddlewares, middleware.assert.category, ...inboxMiddlewares], controllers.activitypub.postInbox); - app.get('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.getCategoryOutbox); - app.post('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.postOutbox); - app.get('/category/:cid/:slug?', [...middlewares, middleware.assert.category], controllers.activitypub.actors.category); + app.get('/category/:cid/inbox', [...middlewares, middleware.assert.category], helpers.tryRoute(controllers.activitypub.getInbox)); + app.post('/category/:cid/inbox', [...inboxMiddlewares, middleware.assert.category, ...inboxMiddlewares], helpers.tryRoute(controllers.activitypub).postInbox); + app.get('/category/:cid/outbox', [...middlewares, middleware.assert.category], helpers.tryRoute(controllers.activitypub.getCategoryOutbox)); + app.post('/category/:cid/outbox', [...middlewares, middleware.assert.category], helpers.tryRoute(controllers.activitypub.postOutbox)); + app.get('/category/:cid/:slug?', [...middlewares, middleware.assert.category], helpers.tryRoute(controllers.activitypub.actors.category)); - app.get('/message/:mid', [...middlewares, middleware.assert.message], controllers.activitypub.actors.message); + app.get('/message/:mid', [...middlewares, middleware.assert.message], helpers.tryRoute(controllers.activitypub.actors.message)); };