From 351c9abc6f3016a55b34b6c791b85a7bb7f9a8d7 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Fri, 17 Oct 2025 15:21:57 +0000 Subject: [PATCH 01/17] chore: incrementing version number - v4.6.1 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 6e48066aed..d49977d6fb 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "4.6.0", + "version": "4.6.1", "homepage": "https://www.nodebb.org", "repository": { "type": "git", From 655c858b5de68e5e439ecb96600ed0aa99bf6caf Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Fri, 17 Oct 2025 15:21:57 +0000 Subject: [PATCH 02/17] chore: update changelog for v4.6.1 --- CHANGELOG.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbb3067783..d4de66ec7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,44 @@ +#### v4.6.1 (2025-10-17) + +##### Chores + +* up persona (b309a672) +* up harmony (79327e6c) +* incrementing version number - v4.6.0 (ee395bc5) +* update changelog for v4.6.0 (c0d9bb07) +* incrementing version number - v4.5.2 (ad2da639) +* incrementing version number - v4.5.1 (69f4b61f) +* incrementing version number - v4.5.0 (f05c5d06) +* incrementing version number - v4.4.6 (074043ad) +* incrementing version number - v4.4.5 (6f106923) +* incrementing version number - v4.4.4 (d323af44) +* incrementing version number - v4.4.3 (d354c2eb) +* incrementing version number - v4.4.2 (55c510ae) +* incrementing version number - v4.4.1 (5ae79b4e) +* incrementing version number - v4.4.0 (0a75eee3) +* incrementing version number - v4.3.2 (b92b5d80) +* incrementing version number - v4.3.1 (308e6b9f) +* incrementing version number - v4.3.0 (bff291db) +* incrementing version number - v4.2.2 (17fecc24) +* incrementing version number - v4.2.1 (852a270c) +* incrementing version number - v4.2.0 (87581958) +* incrementing version number - v4.1.1 (b2afbb16) +* incrementing version number - v4.1.0 (36c80850) +* incrementing version number - v4.0.6 (4a52fb2e) +* incrementing version number - v4.0.5 (1792a62b) +* incrementing version number - v4.0.4 (b1125cce) +* incrementing version number - v4.0.3 (2b65c735) +* incrementing version number - v4.0.2 (73fe5fcf) +* incrementing version number - v4.0.1 (a461b758) +* incrementing version number - v4.0.0 (c1eaee45) + +##### Bug Fixes + +* do not include image or icon props if they are falsy values (ecf95d18) +* #13705, don't cover link if preview is opening up (499c50a4) +* logic error in image mime type checking (623cec9d) +* omg what. (ec399897) + #### v4.6.0 (2025-10-01) ##### Chores From 9410f466d80b30f52e5f926e5d17a513beec1084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 24 Oct 2025 11:04:29 -0400 Subject: [PATCH 03/17] fix: closes #13729, fix filename encoding --- src/middleware/multer.js | 16 ++++++++++++++++ src/routes/admin.js | 4 +--- src/routes/api.js | 4 +--- src/routes/authentication.js | 5 +---- src/routes/helpers.js | 4 +--- 5 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 src/middleware/multer.js diff --git a/src/middleware/multer.js b/src/middleware/multer.js new file mode 100644 index 0000000000..e15c146ff3 --- /dev/null +++ b/src/middleware/multer.js @@ -0,0 +1,16 @@ +'use strict'; + +const multer = require('multer'); +const storage = multer.diskStorage({}); +const upload = multer({ storage, + // from https://github.com/TriliumNext/Trilium/pull/3058/files + fileFilter: (req, file, cb) => { + // UTF-8 file names are not well decoded by multer/busboy, so we handle the conversion on our side. + // See https://github.com/expressjs/multer/pull/1102. + file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf-8'); + cb(null, true); + } +}); + +module.exports = upload; + diff --git a/src/routes/admin.js b/src/routes/admin.js index 5421b4d0ef..4788593c5a 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -85,9 +85,7 @@ function apiRoutes(router, name, middleware, controllers) { router.post(`/api/${name}/manage/categories/:cid/name`, middleware.ensureLoggedIn, helpers.tryRoute(controllers.admin.categories.renameRemote)); router.delete(`/api/${name}/manage/categories/:cid`, middleware.ensureLoggedIn, helpers.tryRoute(controllers.admin.categories.removeRemote)); - const multer = require('multer'); - const storage = multer.diskStorage({}); - const upload = multer({ storage }); + const upload = require('../middleware/multer'); const middlewares = [ upload.array('files[]', 20), diff --git a/src/routes/api.js b/src/routes/api.js index 4424d9a979..79fb83b811 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -23,9 +23,7 @@ module.exports = function (app, middleware, controllers) { router.get('/topic/teaser/:topic_id', [...middlewares], helpers.tryRoute(controllers.topics.teaser)); router.get('/topic/pagination/:topic_id', [...middlewares], helpers.tryRoute(controllers.topics.pagination)); - const multer = require('multer'); - const storage = multer.diskStorage({}); - const upload = multer({ storage }); + const upload = require('../middleware/multer'); const postMiddlewares = [ middleware.maintenanceMode, diff --git a/src/routes/authentication.js b/src/routes/authentication.js index 720675b29d..9836cf22a5 100644 --- a/src/routes/authentication.js +++ b/src/routes/authentication.js @@ -154,10 +154,7 @@ Auth.reloadRoutes = async function (params) { }); }); - - const multer = require('multer'); - const storage = multer.diskStorage({}); - const upload = multer({ storage }); + const upload = require('../middleware/multer') const middlewares = [ upload.any(), Auth.middleware.applyCSRF, diff --git a/src/routes/helpers.js b/src/routes/helpers.js index 2109a0bd9c..079eb36536 100644 --- a/src/routes/helpers.js +++ b/src/routes/helpers.js @@ -54,9 +54,7 @@ helpers.setupApiRoute = function (...args) { const [router, verb, name] = args; let middlewares = args.length > 4 ? args[args.length - 2] : []; const controller = args[args.length - 1]; - const multer = require('multer'); - const storage = multer.diskStorage({}); - const upload = multer({ storage }); + const upload = require('../middleware/multer'); middlewares = [ middleware.autoLocale, middleware.applyBlacklist, From 430a3e81130ac6c1393f9854e1ac78de484e8c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 24 Oct 2025 11:12:20 -0400 Subject: [PATCH 04/17] test: add test for #13729 --- test/files/测试.jpg | Bin 0 -> 1010 bytes test/uploads.js | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 test/files/测试.jpg diff --git a/test/files/测试.jpg b/test/files/测试.jpg new file mode 100644 index 0000000000000000000000000000000000000000..905e886eef32d9fa695e35e69f69314c0d5850cc GIT binary patch literal 1010 zcmex=5A1R0qH8UG()kY-?DVrFDyWIzCRpldmpn1Pb|0t}4IV8Fxa4 zIw}PwvKKar8i_dtB~4uP5U4?dkpXBJ!YCk%iG>+tkCGq*6B837kOP-xWD;Zsnxka+ z!6Sa$#(TjgQ|Y5V{SLOjx6Mv%A%bW#uXH*X0MjL*yQWsYu~uNGq;8Qn&jcU-r>jO z0N>0fJIgE7jT`f&oHmJP-g$9v$~L9Sw`zMM9~hcvYgAtMnqw67Mqu%?+TUTq@*UTv z|J2^TRqbe``R=8c3fZ%EH#JQ1;dquE_A1wOo8X@`Zmn%aj0?g(2`qV|X~^~Nub7&$IkY{)M;AjwMxmbk&%?_R~_AZtI8Y8)^gWuXVk5 z{ryKge}BE((ajvW2cF%${fc?dtl0@ucl|7N5f58@X7ZNm$+z~c-O}65^>?XyNp-09 zb^qH_Hr;N|&l6^1_`dF0S80e@D=ntK7x@s)sqJ;7)a)%=m*(4xQ?(^`>2pc#5zlvwe3C1}W%@YztC*O(R{nQ> do1crieDwF!%ojGi?EiKChm1vCJLdeq2>^^Eisk?S literal 0 HcmV?d00001 diff --git a/test/uploads.js b/test/uploads.js index 1e5b3d19e3..8a57c0d06e 100644 --- a/test/uploads.js +++ b/test/uploads.js @@ -194,6 +194,12 @@ describe('Upload Controllers', () => { assert(body.response.images[0].url); }); + it('should upload a file with utf8 characters in the name to a post', async () => { + const { body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/测试.jpg'), {}, jar, csrf_token); + + assert(body.response.images[0].url.endsWith('测试.jpg')); + }); + it('should fail to upload image to post if image dimensions are too big', async () => { const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/toobig.png'), {}, jar, csrf_token); assert.strictEqual(response.statusCode, 500); From 008e1ae4e497f2ea8aa1f1eeab8533567dbad586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 24 Oct 2025 11:27:43 -0400 Subject: [PATCH 05/17] lint: fix lint --- src/middleware/multer.js | 5 +++-- src/routes/authentication.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/middleware/multer.js b/src/middleware/multer.js index e15c146ff3..e288fe309e 100644 --- a/src/middleware/multer.js +++ b/src/middleware/multer.js @@ -2,14 +2,15 @@ const multer = require('multer'); const storage = multer.diskStorage({}); -const upload = multer({ storage, +const upload = multer({ + storage, // from https://github.com/TriliumNext/Trilium/pull/3058/files fileFilter: (req, file, cb) => { // UTF-8 file names are not well decoded by multer/busboy, so we handle the conversion on our side. // See https://github.com/expressjs/multer/pull/1102. file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf-8'); cb(null, true); - } + }, }); module.exports = upload; diff --git a/src/routes/authentication.js b/src/routes/authentication.js index 9836cf22a5..a4290544a1 100644 --- a/src/routes/authentication.js +++ b/src/routes/authentication.js @@ -154,7 +154,7 @@ Auth.reloadRoutes = async function (params) { }); }); - const upload = require('../middleware/multer') + const upload = require('../middleware/multer'); const middlewares = [ upload.any(), Auth.middleware.applyCSRF, From 07eb16150c4e4d14757be1964faa5bafd28d58b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 27 Oct 2025 20:07:33 -0400 Subject: [PATCH 06/17] center user count in chat, add commas to usercount, make last user image full width --- src/views/partials/chats/options.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/partials/chats/options.tpl b/src/views/partials/chats/options.tpl index 0c651d503c..88deba08d2 100644 --- a/src/views/partials/chats/options.tpl +++ b/src/views/partials/chats/options.tpl @@ -82,7 +82,7 @@ {{{ if users.length }}} -
+
{{{ if ./users.0 }}} {buildAvatar(./users.0, "24px", true)} @@ -91,10 +91,10 @@ {buildAvatar(./users.1, "24px", true)} {{{ end }}} {{{ if ./users.2 }}} - {buildAvatar(./users.2, "24px", true)} + {buildAvatar(./users.2, "24px", true)} {{{ end }}}
- {./userCount} + {formattedNumber(./userCount)}
{{{ end }}}
From 5cfec5b1a9003aae2ce851688336775b5d2932af Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 28 Oct 2025 11:51:02 -0400 Subject: [PATCH 07/17] fix: order of operations when updating category handle --- src/categories/update.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/categories/update.js b/src/categories/update.js index bf32317ae2..ff4d6e4d11 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -165,9 +165,9 @@ module.exports = function (Categories) { throw new Error('[[error:category.handle-taken]]'); } + await db.sortedSetRemove('categoryhandle:cid', existing); await Promise.all([ db.setObjectField(`category:${cid}`, 'handle', handle), - db.sortedSetRemove('categoryhandle:cid', existing), db.sortedSetAdd('categoryhandle:cid', cid, handle), ]); } From be4d0e811e925ff19a30afd912139f959e6afd14 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 4 Nov 2025 11:09:15 -0500 Subject: [PATCH 08/17] fix: wrong auto-categorization if group actor is explicitly included in `audience` --- src/activitypub/notes.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/activitypub/notes.js b/src/activitypub/notes.js index 130cb1874f..973e97a7b6 100644 --- a/src/activitypub/notes.js +++ b/src/activitypub/notes.js @@ -137,7 +137,11 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => { const { hostname } = new URL(mainPid); remoteCid = Array.from(set).filter((id, idx) => { const { hostname: cidHostname } = new URL(id); - return assertedGroups[idx] && cidHostname === hostname; + const explicitAudience = Array.isArray(_activitypub.audience) ? + _activitypub.audience.includes(id) : + _activitypub.audience === id; + + return assertedGroups[idx] && (explicitAudience || cidHostname === hostname); }).shift(); } catch (e) { // noop From 3a81f9032201adfe355a1aa35cdf59bb67172f93 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 6 Nov 2025 15:51:23 -0500 Subject: [PATCH 09/17] fix: make i18n test failure message easier to read --- test/i18n.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/i18n.js b/test/i18n.js index 5798f3d12f..f2abe4f17a 100644 --- a/test/i18n.js +++ b/test/i18n.js @@ -163,7 +163,7 @@ describe('i18n', () => { assert.strictEqual( sourceKeys.length, translationKeys.length, - `Extra keys found in namespace ${namespace.slice(1, -5)} for language "${language}"` + `Extra keys found in namespace "${namespace.slice(1, -5)}" for language "${language}"` ); }); }); From 5d9da6035e3aa74c0e95f7c16d36c18deb690999 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 10 Nov 2025 11:55:19 -0500 Subject: [PATCH 10/17] fix: log out user if session cookie resolves to non-existent uid --- src/controllers/authentication.js | 2 +- src/middleware/user.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 581f7d931c..162bea8ecd 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -490,7 +490,7 @@ authenticationController.logout = async function (req, res) { }; await plugins.hooks.fire('filter:user.logout', payload); - if (req.body?.noscript === 'true') { + if (req.body?.noscript === 'true' || res.locals.logoutRedirect === true) { return res.redirect(payload.next); } res.status(200).send(payload); diff --git a/src/middleware/user.js b/src/middleware/user.js index c49481e625..620c4bad9c 100644 --- a/src/middleware/user.js +++ b/src/middleware/user.js @@ -53,6 +53,12 @@ module.exports = function (middleware) { } if (req.loggedIn) { + const exists = await user.exists(req.uid); + if (!exists) { + res.locals.logoutRedirect = true; + return controllers.authentication.logout(req, res); + } + return true; } else if (req.headers.hasOwnProperty('authorization')) { const user = await passportAuthenticateAsync(req, res); From 9900171f24f26fa6d88237d109ce401926616843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 13 Nov 2025 12:25:16 -0500 Subject: [PATCH 11/17] fix: crash in resolveInboxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2025-11-13T12:38:44.161Z [4568/2508892] - error: uncaughtException: Invalid URL TypeError: Invalid URL at new URL (node:internal/url:818:25) at /home/saas/nodebb/src/activitypub/index.js:123:25 at Array.filter () at ActivityPub.resolveInboxes (/home/saas/nodebb/src/activitypub/index.js:122:13) at ActivityPub.send (/home/saas/nodebb/src/activitypub/index.js:424:36) at Object.announce (/home/saas/nodebb/src/activitypub/feps.js:72:20) --- src/activitypub/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/activitypub/index.js b/src/activitypub/index.js index 5971c69326..e64dd66d6b 100644 --- a/src/activitypub/index.js +++ b/src/activitypub/index.js @@ -119,8 +119,13 @@ ActivityPub.resolveInboxes = async (ids) => { if (!meta.config.activitypubAllowLoopback) { ids = ids.filter((id) => { - const { hostname } = new URL(id); - return hostname !== nconf.get('url_parsed').hostname; + try { + const { hostname } = new URL(id); + return hostname !== nconf.get('url_parsed').hostname; + } catch (err) { + winston.error(`[activitypub/resolveInboxes] Invalid id: ${id}`); + return false; + } }); } From f764b79118c427cfa6e2e718efb28582769c1a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 17 Nov 2025 07:59:49 -0500 Subject: [PATCH 12/17] chore: up peace, closes #13774 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index d49977d6fb..ea388aeb73 100644 --- a/install/package.json +++ b/install/package.json @@ -108,7 +108,7 @@ "nodebb-rewards-essentials": "1.0.2", "nodebb-theme-harmony": "2.1.21", "nodebb-theme-lavender": "7.1.19", - "nodebb-theme-peace": "2.2.48", + "nodebb-theme-peace": "2.2.49", "nodebb-theme-persona": "14.1.15", "nodebb-widget-essentials": "7.0.40", "nodemailer": "7.0.6", From 5bc5bb3d0828392bdf7a8e8c73c67608c8212271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 17 Nov 2025 13:01:37 -0500 Subject: [PATCH 13/17] chore: up emoji --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index ea388aeb73..77d0c46b2c 100644 --- a/install/package.json +++ b/install/package.json @@ -99,7 +99,7 @@ "nodebb-plugin-2factor": "7.6.0", "nodebb-plugin-composer-default": "10.3.1", "nodebb-plugin-dbsearch": "6.3.2", - "nodebb-plugin-emoji": "6.0.3", + "nodebb-plugin-emoji": "6.0.5", "nodebb-plugin-emoji-android": "4.1.1", "nodebb-plugin-markdown": "13.2.1", "nodebb-plugin-mentions": "4.7.6", From aacd27ee3242afd164b0bb394743e73f9e39f0b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 17 Nov 2025 20:13:55 -0500 Subject: [PATCH 14/17] refactor: remove unused share --- public/src/client/category.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/src/client/category.js b/public/src/client/category.js index c90b2710ad..9ae2152b41 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -2,7 +2,6 @@ define('forum/category', [ 'forum/infinitescroll', - 'share', 'navigator', 'topicList', 'sort', @@ -11,7 +10,7 @@ define('forum/category', [ 'alerts', 'api', 'clipboard', -], function (infinitescroll, share, navigator, topicList, sort, categorySelector, hooks, alerts, api, clipboard) { +], function (infinitescroll, navigator, topicList, sort, categorySelector, hooks, alerts, api, clipboard) { const Category = {}; $(window).on('action:ajaxify.start', function (ev, data) { From dece0628bf59bfd533f54fe1d4ab797abd2aa60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 17 Nov 2025 20:25:05 -0500 Subject: [PATCH 15/17] fix: category labels showing up on infinite scroll on category page regression from cross posting tpl change --- public/src/modules/topicList.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/src/modules/topicList.js b/public/src/modules/topicList.js index bc5a6e4abd..4af7eee9c5 100644 --- a/public/src/modules/topicList.js +++ b/public/src/modules/topicList.js @@ -216,9 +216,12 @@ define('topicList', [ 'reputation:disabled': data['reputation:disabled'], template: { name: templateName, + [templateName]: true, }, }; - tplData.template[templateName] = true; + if (ajaxify.data.cid) { + tplData.cid = ajaxify.data.cid; + } hooks.fire('action:topics.loading', { topics: topics, after: after, before: before }); From abfb6d13654a3b87cfed3e9c40ec41f342b6b7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 18 Nov 2025 10:23:49 -0500 Subject: [PATCH 16/17] fix: #13776, if plugin is in install/package.json use latest version from there otherwise show latest version from nbbpm --- src/plugins/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/index.js b/src/plugins/index.js index 47676a5197..f1265d4c7f 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -194,7 +194,7 @@ Plugins.listTrending = async () => { Plugins.normalise = async function (apiReturn) { const pluginMap = {}; - const { dependencies } = require(paths.currentPackage); + const { dependencies } = require(paths.installPackage); apiReturn = Array.isArray(apiReturn) ? apiReturn : []; apiReturn.forEach((packageData) => { packageData.id = packageData.name; @@ -229,7 +229,7 @@ Plugins.normalise = async function (apiReturn) { pluginMap[plugin.id].settingsRoute = plugin.settingsRoute; pluginMap[plugin.id].license = plugin.license; - // If package.json defines a version to use, stick to that + // If install/package.json defines a version to use, stick to that if (dependencies.hasOwnProperty(plugin.id) && semver.valid(dependencies[plugin.id])) { pluginMap[plugin.id].latest = dependencies[plugin.id]; } else { From e30024119334479c57e442a72ed72a07084fe1e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 19 Nov 2025 08:45:08 -0500 Subject: [PATCH 17/17] fix: #13779, svg uploads if tmp file doesn't have extension stripEXIF was called for gifs/svgs --- src/controllers/uploads.js | 2 +- src/image.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index d2cfb48107..11fcebc95a 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -61,7 +61,7 @@ async function uploadAsImage(req, uploadedFile) { throw new Error('[[error:no-privileges]]'); } await image.checkDimensions(uploadedFile.path); - await image.stripEXIF(uploadedFile.path); + await image.stripEXIF({ path: uploadedFile.path, type: uploadedFile.type }); if (plugins.hooks.hasListeners('filter:uploadImage')) { return await plugins.hooks.fire('filter:uploadImage', { diff --git a/src/image.js b/src/image.js index 06e3f2d76d..0d60fddc6a 100644 --- a/src/image.js +++ b/src/image.js @@ -102,14 +102,15 @@ image.size = async function (path) { return imageData ? { width: imageData.width, height: imageData.height } : undefined; }; -image.stripEXIF = async function (path) { - if (!meta.config.stripEXIFData || path.endsWith('.gif') || path.endsWith('.svg')) { +image.stripEXIF = async function ({ path, type }) { + if (!meta.config.stripEXIFData || type === 'image/gif' || type === 'image/svg+xml') { return; } try { if (plugins.hooks.hasListeners('filter:image.stripEXIF')) { await plugins.hooks.fire('filter:image.stripEXIF', { path: path, + type: type, }); return; }