From f7c47429879f757e08975b5cd003416db00f5568 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Wed, 17 Sep 2025 10:44:51 -0400
Subject: [PATCH 1/7] fix: add pre-processing step to title generation logic so
sbd doesn't fall over so badly
---
src/activitypub/helpers.js | 46 --------------------------------------
src/activitypub/notes.js | 6 ++++-
2 files changed, 5 insertions(+), 47 deletions(-)
diff --git a/src/activitypub/helpers.js b/src/activitypub/helpers.js
index e6eb2e1c08..01cfd86d10 100644
--- a/src/activitypub/helpers.js
+++ b/src/activitypub/helpers.js
@@ -339,52 +339,6 @@ Helpers.resolveObjects = async (ids) => {
return objects.length === 1 ? objects[0] : objects;
};
-const titleishTags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'title', 'p', 'span'];
-const titleRegex = new RegExp(`<(${titleishTags.join('|')})>(.+?)\\1>`, 'm');
-Helpers.generateTitle = (html) => {
- // Given an html string, generates a more appropriate title if possible
- let title;
-
- // Try the first paragraph-like element
- const match = html.match(titleRegex);
- if (match && match.index === 0) {
- title = match[2];
- }
-
- // Fall back to newline splitting (i.e. if no paragraph elements)
- title = title || html.split('\n').filter(Boolean).shift();
-
- // Discard everything after a line break element
- title = title.replace(/
.*/g, '');
-
- // Strip html
- title = utils.stripHTMLTags(title);
-
- // Split sentences and use only first one
- const sentences = title
- .split(/(\.|\?|!)\s/)
- .reduce((memo, cur, idx, sentences) => {
- if (idx % 2) {
- memo.push(`${sentences[idx - 1]}${cur}`);
- } else if (idx === sentences.length - 1) {
- memo.push(cur);
- }
-
- return memo;
- }, []);
-
- if (sentences.length > 1) {
- title = sentences.shift();
- }
-
- // Truncate down if too long
- if (title.length > meta.config.maximumTitleLength) {
- title = `${title.slice(0, meta.config.maximumTitleLength - 3)}...`;
- }
-
- return title;
-};
-
Helpers.remoteAnchorToLocalProfile = async (content, isMarkdown = false) => {
let anchorRegex;
if (isMarkdown) {
diff --git a/src/activitypub/notes.js b/src/activitypub/notes.js
index ef4abe9add..6ccb2ca209 100644
--- a/src/activitypub/notes.js
+++ b/src/activitypub/notes.js
@@ -165,7 +165,11 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => {
// mainPid ok to leave as-is
if (!title) {
- const sentences = tokenizer.sentences(content || sourceContent, { sanitize: true });
+ // Naive pre-processing prior to sbd tokenization
+ let sbdInput = content || sourceContent;
+ sbdInput = sbdInput.replace('
', '
\n');
+
+ const sentences = tokenizer.sentences(sbdInput, { sanitize: true, newline_boundaries: true });
title = sentences.shift();
}
From 6cca55e37f0bce389c3094c5aae07ed1bbed3297 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Wed, 17 Sep 2025 10:50:35 -0400
Subject: [PATCH 2/7] fix: use parameterized query for key lookup
---
src/database/postgres/main.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/database/postgres/main.js b/src/database/postgres/main.js
index c0838b45a0..5b3c7f7e9d 100644
--- a/src/database/postgres/main.js
+++ b/src/database/postgres/main.js
@@ -85,7 +85,8 @@ module.exports = function (module) {
text: `
SELECT o."_key"
FROM "legacy_object_live" o
- WHERE o."_key" LIKE '${match}'`,
+ WHERE o."_key" LIKE $1`,
+ values: [match],
});
return res.rows.map(r => r._key);
From 532653110c9e0400967c42352415be6dccb8e6a4 Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Wed, 17 Sep 2025 10:58:07 -0400
Subject: [PATCH 3/7] Revert "fix: add pre-processing step to title generation
logic so sbd doesn't fall over so badly"
This reverts commit f7c47429879f757e08975b5cd003416db00f5568.
---
src/activitypub/helpers.js | 46 ++++++++++++++++++++++++++++++++++++++
src/activitypub/notes.js | 6 +----
2 files changed, 47 insertions(+), 5 deletions(-)
diff --git a/src/activitypub/helpers.js b/src/activitypub/helpers.js
index 01cfd86d10..e6eb2e1c08 100644
--- a/src/activitypub/helpers.js
+++ b/src/activitypub/helpers.js
@@ -339,6 +339,52 @@ Helpers.resolveObjects = async (ids) => {
return objects.length === 1 ? objects[0] : objects;
};
+const titleishTags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'title', 'p', 'span'];
+const titleRegex = new RegExp(`<(${titleishTags.join('|')})>(.+?)\\1>`, 'm');
+Helpers.generateTitle = (html) => {
+ // Given an html string, generates a more appropriate title if possible
+ let title;
+
+ // Try the first paragraph-like element
+ const match = html.match(titleRegex);
+ if (match && match.index === 0) {
+ title = match[2];
+ }
+
+ // Fall back to newline splitting (i.e. if no paragraph elements)
+ title = title || html.split('\n').filter(Boolean).shift();
+
+ // Discard everything after a line break element
+ title = title.replace(/
.*/g, '');
+
+ // Strip html
+ title = utils.stripHTMLTags(title);
+
+ // Split sentences and use only first one
+ const sentences = title
+ .split(/(\.|\?|!)\s/)
+ .reduce((memo, cur, idx, sentences) => {
+ if (idx % 2) {
+ memo.push(`${sentences[idx - 1]}${cur}`);
+ } else if (idx === sentences.length - 1) {
+ memo.push(cur);
+ }
+
+ return memo;
+ }, []);
+
+ if (sentences.length > 1) {
+ title = sentences.shift();
+ }
+
+ // Truncate down if too long
+ if (title.length > meta.config.maximumTitleLength) {
+ title = `${title.slice(0, meta.config.maximumTitleLength - 3)}...`;
+ }
+
+ return title;
+};
+
Helpers.remoteAnchorToLocalProfile = async (content, isMarkdown = false) => {
let anchorRegex;
if (isMarkdown) {
diff --git a/src/activitypub/notes.js b/src/activitypub/notes.js
index 6ccb2ca209..ef4abe9add 100644
--- a/src/activitypub/notes.js
+++ b/src/activitypub/notes.js
@@ -165,11 +165,7 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => {
// mainPid ok to leave as-is
if (!title) {
- // Naive pre-processing prior to sbd tokenization
- let sbdInput = content || sourceContent;
- sbdInput = sbdInput.replace('
', '
\n');
-
- const sentences = tokenizer.sentences(sbdInput, { sanitize: true, newline_boundaries: true });
+ const sentences = tokenizer.sentences(content || sourceContent, { sanitize: true });
title = sentences.shift();
}
From a6674f67a1cfb92f6236e76447e5e9213b1b5710 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Wed, 17 Sep 2025 10:58:26 -0400
Subject: [PATCH 4/7] lint: remove unused
---
src/activitypub/helpers.js | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/activitypub/helpers.js b/src/activitypub/helpers.js
index 01cfd86d10..f24d18b730 100644
--- a/src/activitypub/helpers.js
+++ b/src/activitypub/helpers.js
@@ -8,7 +8,6 @@ const validator = require('validator');
// const cheerio = require('cheerio');
const crypto = require('crypto');
-const meta = require('../meta');
const posts = require('../posts');
const categories = require('../categories');
const messaging = require('../messaging');
@@ -16,7 +15,6 @@ const request = require('../request');
const db = require('../database');
const ttl = require('../cache/ttl');
const user = require('../user');
-const utils = require('../utils');
const activitypub = require('.');
const webfingerRegex = /^(@|acct:)?[\w-.]+@.+$/;
From 5beeedd67cc2fc08b6dda77f237ba7892b5329b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?=
Date: Wed, 17 Sep 2025 11:09:02 -0400
Subject: [PATCH 5/7] Revert "lint: remove unused"
This reverts commit a6674f67a1cfb92f6236e76447e5e9213b1b5710.
---
src/activitypub/helpers.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/activitypub/helpers.js b/src/activitypub/helpers.js
index e0f96fe3aa..e6eb2e1c08 100644
--- a/src/activitypub/helpers.js
+++ b/src/activitypub/helpers.js
@@ -8,6 +8,7 @@ const validator = require('validator');
// const cheerio = require('cheerio');
const crypto = require('crypto');
+const meta = require('../meta');
const posts = require('../posts');
const categories = require('../categories');
const messaging = require('../messaging');
@@ -15,6 +16,7 @@ const request = require('../request');
const db = require('../database');
const ttl = require('../cache/ttl');
const user = require('../user');
+const utils = require('../utils');
const activitypub = require('.');
const webfingerRegex = /^(@|acct:)?[\w-.]+@.+$/;
From d1f5060f11a257388690d1441726efd58ca88b5a Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Thu, 18 Sep 2025 13:33:16 -0400
Subject: [PATCH 6/7] fix(deps): bump 2factor to 7.6.0
---
install/package.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/install/package.json b/install/package.json
index cd5d08f5c1..f9e5b8b0ed 100644
--- a/install/package.json
+++ b/install/package.json
@@ -96,7 +96,7 @@
"mousetrap": "1.6.5",
"multer": "2.0.2",
"nconf": "0.13.0",
- "nodebb-plugin-2factor": "7.5.10",
+ "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",
@@ -201,4 +201,4 @@
"url": "https://github.com/barisusakli"
}
]
-}
\ No newline at end of file
+}
From f9edb13f6209b075d4a53c130d1bba166ae188fa Mon Sep 17 00:00:00 2001
From: Julian Lam
Date: Fri, 19 Sep 2025 14:43:04 -0400
Subject: [PATCH 7/7] fix: missing actor assertion on 1b12 announced upboat
---
src/activitypub/inbox.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/activitypub/inbox.js b/src/activitypub/inbox.js
index 754720f208..ea5b032a1a 100644
--- a/src/activitypub/inbox.js
+++ b/src/activitypub/inbox.js
@@ -298,6 +298,7 @@ inbox.announce = async (req) => {
const exists = await posts.exists(localId || id);
if (exists) {
try {
+ await activitypub.actors.assert(object.actor);
const result = await posts.upvote(localId || id, object.actor);
if (localId) {
socketHelpers.upvote(result, 'notifications:upvoted-your-post-in');