Merge branch 'develop' of https://github.com/NodeBB/NodeBB into develop

This commit is contained in:
Barış Soner Uşaklı
2026-04-14 21:22:59 -04:00
4 changed files with 57 additions and 47 deletions

View File

@@ -42,7 +42,7 @@
"ace-builds": "1.43.6",
"archiver": "7.0.1",
"async": "3.2.6",
"autoprefixer": "10.4.27",
"autoprefixer": "10.5.0",
"bcryptjs": "3.0.3",
"benchpressjs": "2.5.5",
"body-parser": "2.2.2",
@@ -90,7 +90,7 @@
"jsonwebtoken": "9.0.3",
"lodash": "4.18.1",
"logrotate-stream": "0.2.9",
"lru-cache": "11.3.3",
"lru-cache": "11.3.5",
"mime": "3.0.0",
"mkdirp": "3.0.1",
"mongodb": "7.1.1",

View File

@@ -6,9 +6,9 @@ const db = require('../database');
const request = require('../request');
const activitypub = module.parent.exports;
const blocklists = module.exports;
const Blocklists = module.exports;
blocklists.list = async () => {
Blocklists.list = async () => {
const blocklists = await db.getSortedSetMembers('blocklists');
const counts = await db.sortedSetsCard(blocklists.map(blocklist => `blocklist:${blocklist}`));
@@ -17,7 +17,7 @@ blocklists.list = async () => {
});
};
blocklists.get = async (url) => {
Blocklists.get = async (url) => {
const domains = await db.getSortedSetMembers(`blocklist:${url}`);
return {
@@ -26,23 +26,23 @@ blocklists.get = async (url) => {
};
};
blocklists.add = async (url) => {
Blocklists.add = async (url) => {
const now = Date.now();
await Promise.all([
db.sortedSetAdd('blocklists', now, url),
blocklists.refresh(url),
Blocklists.refresh(url),
]);
};
blocklists.remove = async (url) => {
Blocklists.remove = async (url) => {
await Promise.all([
db.sortedSetRemove('blocklists', url),
db.delete(`blocklist:${url}`),
]);
};
blocklists.refresh = async (url) => {
Blocklists.refresh = async (url) => {
activitypub.helpers.log(`[blocklists/refresh] Processing ${url}`);
const { body: csv } = await request.get(url);
@@ -69,8 +69,8 @@ blocklists.refresh = async (url) => {
return records.length;
};
blocklists.check = async (domain) => {
const blocklists = await blocklists.list();
Blocklists.check = async (domain) => {
const blocklists = await Blocklists.list();
let present = await db.isMemberOfSortedSets(blocklists.map(({ url }) => `blocklist:${url}`), domain);
present = present.reduce((memo, present) => memo || present, false);

View File

@@ -499,10 +499,10 @@ ActivityPub.record.send = async ({ type, target }) => {
]);
};
ActivityPub.record.sendError = async ({ payload, target, error }) => {
ActivityPub.record.sendError = async ({ payload, uri, error }) => {
const { id } = payload;
const now = Date.now();
const { hostname } = new URL(target);
const { hostname } = new URL(uri);
await Promise.all([
db.sortedSetAdd('ap.errors', now, id),
db.setObject(`ap.errors:${id}`, {

View File

@@ -6,17 +6,21 @@ const path = require('path');
const db = require('../mocks/databasemock');
const request = require('../../src/request');
const meta = require('../../src/meta');
const install = require('../../src/install');
const activitypub = require('../../src/activitypub');
describe('ActivityPub blocklists', () => {
before(async () => {
before(async function () {
meta.config.activitypubEnabled = 1;
meta.config.activitypubAllowLoopback = 1;
await install.giveWorldPrivileges();
this.getHandler = request.get;
});
after(() => {
after(function () {
delete meta.config.activitypubEnabled;
request.get = this.getHandler;
});
describe('blocklists.list()', () => {
@@ -106,7 +110,7 @@ describe('ActivityPub blocklists', () => {
// Verify the blocklist was added and refreshed
const result = await activitypub.blocklists.get(url);
assert.strictEqual(result.count, 0); // Empty initially
assert.strictEqual(result.count, 2);
});
});
@@ -134,66 +138,78 @@ describe('ActivityPub blocklists', () => {
});
describe('blocklists.refresh()', () => {
it('should process a valid CSV', async () => {
const url = 'https://example.com/blocklist.csv';
await activitypub.blocklists.add(url);
before(function () {
this.url = 'https://example.com/blocklist.csv';
});
afterEach(async function () {
await activitypub.blocklists.remove(this.url);
});
it('should process a valid CSV', async function () {
await activitypub.blocklists.add(this.url);
const csvData = '#domain,#severity\nexample.com,1\nexample.org,2\nsilence.example.com,2';
const mockResponse = { body: csvData };
request.get = () => mockResponse;
const result = await activitypub.blocklists.refresh(url);
const result = await activitypub.blocklists.refresh(this.url);
assert.strictEqual(result, 3);
});
it('should return 0 for empty CSV', async () => {
const url = 'https://example.com/blocklist.csv';
await activitypub.blocklists.add(url);
it('should return 0 for empty CSV', async function () {
await activitypub.blocklists.add(this.url);
const csvData = '';
const mockResponse = { body: csvData };
request.get = () => mockResponse;
const result = await activitypub.blocklists.refresh(url);
const result = await activitypub.blocklists.refresh(this.url);
assert.strictEqual(result, 0);
});
it('should return 0 on parse error', async () => {
const url = 'https://example.com/blocklist.csv';
await activitypub.blocklists.add(url);
it('should return 0 on parse error', async function () {
await activitypub.blocklists.add(this.url);
const csvData = 'invalid,csv,data';
const mockResponse = { body: csvData };
request.get = () => mockResponse;
const result = await activitypub.blocklists.refresh(url);
const result = await activitypub.blocklists.refresh(this.url);
assert.strictEqual(result, 0);
});
it('should handle severity levels correctly', async () => {
const url = 'https://example.com/blocklist.csv';
await activitypub.blocklists.add(url);
it('should handle severity levels correctly', async function () {
await activitypub.blocklists.add(this.url);
const csvData = '#domain,#severity\nexample.com,1\nsilence.example.com,2';
const mockResponse = { body: csvData };
request.get = () => mockResponse;
await activitypub.blocklists.refresh(url);
await activitypub.blocklists.refresh(this.url);
const result = await activitypub.blocklists.get(url);
const result = await activitypub.blocklists.get(this.url);
assert.strictEqual(result.count, 2);
});
});
describe('blocklists.check()', () => {
it('should return true when domain is not blocked', async () => {
const url = 'https://example.com/blocklist.csv';
await activitypub.blocklists.add(url);
async function clear () {
const url1 = 'https://example.com/blocklist1.csv';
const url2 = 'https://example.com/blocklist2.csv';
const url3 = 'https://example.com/blocklist.csv';
await Promise.all([url1, url2, url3].map(async (url) => {
await activitypub.blocklists.remove(url);
}));
}
before(clear);
afterEach(clear);
it('should return true when domain is not blocked', async () => {
const result = await activitypub.blocklists.check('example.com');
assert.strictEqual(result, true);
@@ -218,8 +234,6 @@ describe('ActivityPub blocklists', () => {
const url1 = 'https://example.com/blocklist1.csv';
const url2 = 'https://example.com/blocklist2.csv';
await activitypub.blocklists.add(url1);
await activitypub.blocklists.add(url2);
const csvData1 = '#domain,#severity\nblocked.com,1';
const csvData2 = '#domain,#severity\nblocked.org,1';
@@ -227,11 +241,10 @@ describe('ActivityPub blocklists', () => {
const mockResponse1 = { body: csvData1 };
const mockResponse2 = { body: csvData2 };
request.get = () => mockResponse1;
await activitypub.blocklists.refresh(url1);
await activitypub.blocklists.add(url1);
request.get = () => mockResponse2;
await activitypub.blocklists.refresh(url2);
await activitypub.blocklists.add(url2);
const result = await activitypub.blocklists.check('example.com');
@@ -242,20 +255,17 @@ describe('ActivityPub blocklists', () => {
const url1 = 'https://example.com/blocklist1.csv';
const url2 = 'https://example.com/blocklist2.csv';
await activitypub.blocklists.add(url1);
await activitypub.blocklists.add(url2);
const csvData1 = '#domain,#severity\nexample.com,1';
const csvData2 = '#domain,#severity\nblocked.org,1';
const mockResponse1 = { body: csvData1 };
const mockResponse2 = { body: csvData2 };
request.get = () => mockResponse1;
await activitypub.blocklists.refresh(url1);
request.get = () => mockResponse1;
await activitypub.blocklists.add(url1);
request.get = () => mockResponse2;
await activitypub.blocklists.refresh(url2);
await activitypub.blocklists.add(url2);
const result = await activitypub.blocklists.check('example.com');