diff --git a/CHANGELOG.md b/CHANGELOG.md
index 33da12d0e3..ce6bf7959e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,34 @@
+#### v4.2.1 (2025-04-10)
+
+##### Chores
+
+* up harmony (d161eb6f)
+* up persona (2237e17a)
+* up persona (75f1f6fb)
+* incrementing version number - v4.2.0 (87581958)
+* update changelog for v4.2.0 (c9e0198d)
+* 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
+
+* closes #13317, fix email confirm for changing email (33d50637)
+* check if latestversion is valid before using semver.gt (6fe066ce)
+* closes #13256, allow keyboard access to icon colors (c6620170)
+
+##### Refactors
+
+* get rid of async.parallel (e722e869)
+* remove pointless true (747457d7)
+* make register intro heading (c258f597)
+
#### v4.2.0 (2025-03-19)
##### Chores
diff --git a/public/src/client/topic/delete-posts.js b/public/src/client/topic/delete-posts.js
index 58b4058f09..8e01a839e0 100644
--- a/public/src/client/topic/delete-posts.js
+++ b/public/src/client/topic/delete-posts.js
@@ -31,6 +31,8 @@ define('forum/topic/delete-posts', [
postSelect.init(function () {
checkButtonEnable();
showPostsSelected();
+ }, {
+ allowMainPostSelect: true,
});
showPostsSelected();
diff --git a/src/categories/create.js b/src/categories/create.js
index f2e8f8811d..d9d59dac59 100644
--- a/src/categories/create.js
+++ b/src/categories/create.js
@@ -151,7 +151,15 @@ module.exports = function (Categories) {
}
async function generateHandle(slug) {
- let taken = await meta.slugTaken(slug);
+ let taken;
+ try {
+ taken = await meta.slugTaken(slug);
+ } catch (e) {
+ // invalid slug passed in
+ slug = 'category';
+ taken = true;
+ }
+
let suffix;
while (taken) {
suffix = utils.generateUUID().slice(0, 8);
diff --git a/src/topics/events.js b/src/topics/events.js
index 10ab9b8936..0748318c37 100644
--- a/src/topics/events.js
+++ b/src/topics/events.js
@@ -1,5 +1,6 @@
'use strict';
+const validator = require('validator');
const _ = require('lodash');
const nconf = require('nconf');
const db = require('../database');
@@ -107,7 +108,13 @@ function renderUser(event) {
if (!event.user || event.user.system) {
return '[[global:system-user]]';
}
- return `${helpers.buildAvatar(event.user, '16px', true)} ${event.user.displayname}`;
+
+ const user = {
+ ...event.user,
+ displayname: validator.escape(String(event.user.displayname)),
+ };
+
+ return `${helpers.buildAvatar(user, '16px', true)} ${user.displayname}`;
}
function renderTimeago(event) {
diff --git a/src/user/follow.js b/src/user/follow.js
index c89f90b979..0595e50ff8 100644
--- a/src/user/follow.js
+++ b/src/user/follow.js
@@ -58,11 +58,8 @@ module.exports = function (User) {
]);
}
- const [followingCount, followingRemoteCount, followerCount, followerRemoteCount] = await Promise.all([
- db.sortedSetCard(`following:${uid}`),
- db.sortedSetCard(`followingRemote:${uid}`),
- db.sortedSetCard(`followers:${theiruid}`),
- db.sortedSetCard(`followersRemote:${theiruid}`),
+ const [followingCount, followingRemoteCount, followerCount, followerRemoteCount] = await db.sortedSetsCard([
+ `following:${uid}`, `followingRemote:${uid}`, `followers:${theiruid}`, `followersRemote:${theiruid}`,
]);
await Promise.all([
User.setUserField(uid, 'followingCount', followingCount + followingRemoteCount),