mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-03-06 20:41:17 +01:00
Merge commit '00668bdc342f22b1ef263ca2d6003b12bbb194af' into v4.x
This commit is contained in:
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,3 +1,36 @@
|
||||
#### v4.3.1 (2025-05-07)
|
||||
|
||||
##### Chores
|
||||
|
||||
* node 18 eol (800426d6)
|
||||
* up widgets (ee2f91ad)
|
||||
* up themes (18867fb1)
|
||||
* update bundled plugins to use eslint9 (343f13e1)
|
||||
* incrementing version number - v4.3.0 (bff291db)
|
||||
* update changelog for v4.3.0 (76c03019)
|
||||
* 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)
|
||||
|
||||
##### Other Changes
|
||||
|
||||
* //github.com/NodeBB/NodeBB/issues/13367 (d35aad31)
|
||||
|
||||
##### Tests
|
||||
|
||||
* fix android test (31af05c7)
|
||||
* fix android test (25979294)
|
||||
* fix a test (7ef79981)
|
||||
|
||||
#### v4.3.0 (2025-05-01)
|
||||
|
||||
##### Chores
|
||||
|
||||
@@ -58,8 +58,8 @@ RUN corepack enable \
|
||||
&& mkdir -p /usr/src/app/logs/ /opt/config/ \
|
||||
&& chown -R ${USER}:${USER} /usr/src/app/ /opt/config/
|
||||
|
||||
COPY --from=build --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/
|
||||
COPY --from=build --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/
|
||||
COPY --from=git --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/
|
||||
COPY --from=git --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/
|
||||
COPY --from=node_modules_touch --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/
|
||||
COPY --from=git --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
"nodebb-plugin-emoji": "6.0.2",
|
||||
"nodebb-plugin-emoji-android": "4.1.1",
|
||||
"nodebb-plugin-markdown": "13.1.2",
|
||||
"nodebb-plugin-mentions": "4.7.4",
|
||||
"nodebb-plugin-mentions": "4.7.5",
|
||||
"nodebb-plugin-spam-be-gone": "2.3.2",
|
||||
"nodebb-plugin-web-push": "0.7.4",
|
||||
"nodebb-rewards-essentials": "1.0.2",
|
||||
|
||||
@@ -108,7 +108,11 @@ Helpers.query = async (id) => {
|
||||
let response;
|
||||
let body;
|
||||
try {
|
||||
({ response, body } = await request.get(`https://${hostname}/.well-known/webfinger?${query}`));
|
||||
({ response, body } = await request.get(`https://${hostname}/.well-known/webfinger?${query}`, {
|
||||
headers: {
|
||||
accept: 'application/jrd+json',
|
||||
},
|
||||
}));
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -232,49 +232,49 @@ ActivityPub.verify = async (req) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Break the signature apart
|
||||
let { keyId, headers, signature, algorithm, created, expires } = req.headers.signature.split(',').reduce((memo, cur) => {
|
||||
const split = cur.split('="');
|
||||
const key = split.shift();
|
||||
const value = split.join('="');
|
||||
memo[key] = value.slice(0, -1);
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
const acceptableHashes = getHashes();
|
||||
if (algorithm === 'hs2019' || !acceptableHashes.includes(algorithm)) {
|
||||
algorithm = 'sha256';
|
||||
}
|
||||
|
||||
// Re-construct signature string
|
||||
const signed_string = headers.split(' ').reduce((memo, cur) => {
|
||||
switch (cur) {
|
||||
case '(request-target)': {
|
||||
memo.push(`${cur}: ${String(req.method).toLowerCase()} ${req.baseUrl}${req.path}`);
|
||||
break;
|
||||
}
|
||||
|
||||
case '(created)': {
|
||||
memo.push(`${cur}: ${created}`);
|
||||
break;
|
||||
}
|
||||
|
||||
case '(expires)': {
|
||||
memo.push(`${cur}: ${expires}`);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
memo.push(`${cur}: ${req.headers[cur]}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return memo;
|
||||
}, []).join('\n');
|
||||
|
||||
// Verify the signature string via public key
|
||||
try {
|
||||
// Break the signature apart
|
||||
let { keyId, headers, signature, algorithm, created, expires } = req.headers.signature.split(',').reduce((memo, cur) => {
|
||||
const split = cur.split('="');
|
||||
const key = split.shift();
|
||||
const value = split.join('="');
|
||||
memo[key] = value.slice(0, -1);
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
const acceptableHashes = getHashes();
|
||||
if (algorithm === 'hs2019' || !acceptableHashes.includes(algorithm)) {
|
||||
algorithm = 'sha256';
|
||||
}
|
||||
|
||||
// Re-construct signature string
|
||||
const signed_string = headers.split(' ').reduce((memo, cur) => {
|
||||
switch (cur) {
|
||||
case '(request-target)': {
|
||||
memo.push(`${cur}: ${String(req.method).toLowerCase()} ${req.baseUrl}${req.path}`);
|
||||
break;
|
||||
}
|
||||
|
||||
case '(created)': {
|
||||
memo.push(`${cur}: ${created}`);
|
||||
break;
|
||||
}
|
||||
|
||||
case '(expires)': {
|
||||
memo.push(`${cur}: ${expires}`);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
memo.push(`${cur}: ${req.headers[cur]}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return memo;
|
||||
}, []).join('\n');
|
||||
|
||||
// Retrieve public key from remote instance
|
||||
ActivityPub.helpers.log(`[activitypub/verify] Retrieving pubkey for ${keyId}`);
|
||||
const { publicKeyPem } = await ActivityPub.fetchPublicKey(keyId);
|
||||
|
||||
@@ -13,6 +13,7 @@ const winston = require('winston');
|
||||
|
||||
const db = require('../database');
|
||||
const user = require('../user');
|
||||
const categories = require('../categories');
|
||||
const meta = require('../meta');
|
||||
const privileges = require('../privileges');
|
||||
const activitypub = require('../activitypub');
|
||||
@@ -43,7 +44,14 @@ activitypubApi.follow = enabledCheck(async (caller, { type, id, actor } = {}) =>
|
||||
throw new Error('[[error:activitypub.invalid-id]]');
|
||||
}
|
||||
|
||||
actor = actor.includes('@') ? await user.getUidByUserslug(actor) : actor;
|
||||
if (actor.includes('@')) {
|
||||
const [uid, cid] = await Promise.all([
|
||||
user.getUidByUserslug(actor),
|
||||
categories.getCidByHandle(actor),
|
||||
]);
|
||||
|
||||
actor = uid || cid;
|
||||
}
|
||||
const [handle, isFollowing] = await Promise.all([
|
||||
user.getUserField(actor, 'username'),
|
||||
db.isSortedSetMember(type === 'uid' ? `followingRemote:${id}` : `cid:${id}:following`, actor),
|
||||
@@ -76,13 +84,22 @@ activitypubApi.unfollow = enabledCheck(async (caller, { type, id, actor }) => {
|
||||
throw new Error('[[error:activitypub.invalid-id]]');
|
||||
}
|
||||
|
||||
actor = actor.includes('@') ? await user.getUidByUserslug(actor) : actor;
|
||||
const [handle, isFollowing] = await Promise.all([
|
||||
if (actor.includes('@')) {
|
||||
const [uid, cid] = await Promise.all([
|
||||
user.getUidByUserslug(actor),
|
||||
categories.getCidByHandle(actor),
|
||||
]);
|
||||
|
||||
actor = uid || cid;
|
||||
}
|
||||
|
||||
const [handle, isFollowing, isPending] = await Promise.all([
|
||||
user.getUserField(actor, 'username'),
|
||||
db.isSortedSetMember(type === 'uid' ? `followingRemote:${id}` : `cid:${id}:following`, actor),
|
||||
db.isSortedSetMember(`followRequests:${type === 'uid' ? 'uid' : 'cid'}.${id}`, actor),
|
||||
]);
|
||||
|
||||
if (!isFollowing) { // already not following
|
||||
if (!isFollowing && !isPending) { // already not following/pending
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,13 @@ Categories.getCategoryById = async function (data) {
|
||||
};
|
||||
|
||||
Categories.getCidByHandle = async function (handle) {
|
||||
return await db.sortedSetScore('categoryhandle:cid', handle);
|
||||
let cid = await db.sortedSetScore('categoryhandle:cid', handle);
|
||||
if (!cid) {
|
||||
// remote cids
|
||||
cid = await db.getObjectField('handle:cid', handle);
|
||||
}
|
||||
|
||||
return cid;
|
||||
};
|
||||
|
||||
Categories.getAllCidsFromSet = async function (key) {
|
||||
|
||||
@@ -84,7 +84,7 @@ Actors.note = async function (req, res, next) {
|
||||
res.status(200).json(payload);
|
||||
};
|
||||
|
||||
Actors.replies = async function (req, res) {
|
||||
Actors.replies = async function (req, res, next) {
|
||||
const allowed = utils.isNumber(req.params.pid) && await privileges.posts.can('topics:read', req.params.pid, activitypub._constants.uid);
|
||||
const exists = await posts.exists(req.params.pid);
|
||||
if (!allowed || !exists) {
|
||||
@@ -92,12 +92,17 @@ Actors.replies = async function (req, res) {
|
||||
}
|
||||
|
||||
const page = parseInt(req.query.page, 10);
|
||||
const replies = await activitypub.helpers.generateCollection({
|
||||
set: `pid:${req.params.pid}:replies`,
|
||||
page,
|
||||
perPage: meta.config.postsPerPage,
|
||||
url: `${nconf.get('url')}/post/${req.params.pid}/replies`,
|
||||
});
|
||||
let replies;
|
||||
try {
|
||||
replies = await activitypub.helpers.generateCollection({
|
||||
set: `pid:${req.params.pid}:replies`,
|
||||
page,
|
||||
perPage: meta.config.postsPerPage,
|
||||
url: `${nconf.get('url')}/post/${req.params.pid}/replies`,
|
||||
});
|
||||
} catch (e) {
|
||||
return next(); // invalid page; 404
|
||||
}
|
||||
|
||||
// Convert pids to urls
|
||||
replies.orderedItems = replies.orderedItems.map(pid => (utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid));
|
||||
@@ -126,16 +131,22 @@ Actors.topic = async function (req, res, next) {
|
||||
return next();
|
||||
}
|
||||
|
||||
let [collection, pids] = await Promise.all([
|
||||
activitypub.helpers.generateCollection({
|
||||
set: `tid:${req.params.tid}:posts`,
|
||||
method: posts.getPidsFromSet,
|
||||
page,
|
||||
perPage,
|
||||
url: `${nconf.get('url')}/topic/${req.params.tid}/posts`,
|
||||
}),
|
||||
db.getSortedSetMembers(`tid:${req.params.tid}:posts`),
|
||||
]);
|
||||
let collection;
|
||||
let pids;
|
||||
try {
|
||||
([collection, pids] = await Promise.all([
|
||||
activitypub.helpers.generateCollection({
|
||||
set: `tid:${req.params.tid}:posts`,
|
||||
method: posts.getPidsFromSet,
|
||||
page,
|
||||
perPage,
|
||||
url: `${nconf.get('url')}/topic/${req.params.tid}/posts`,
|
||||
}),
|
||||
db.getSortedSetMembers(`tid:${req.params.tid}:posts`),
|
||||
]));
|
||||
} catch (e) {
|
||||
return next(); // invalid page; 404
|
||||
}
|
||||
pids.push(mainPid);
|
||||
pids = pids.map(pid => (utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid));
|
||||
collection.totalItems += 1; // account for mainPid
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
12
src/upgrades/4.3.2/fix_category_sync_null_values.js
Normal file
12
src/upgrades/4.3.2/fix_category_sync_null_values.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const db = require('../../database');
|
||||
|
||||
module.exports = {
|
||||
name: 'Fix null values in category synchronization list',
|
||||
timestamp: Date.UTC(2025, 4, 8),
|
||||
method: async () => {
|
||||
const cids = await db.getSortedSetMembers('categories:cid');
|
||||
await db.sortedSetsRemove(cids.map(cid => `followRequests:cid.${cid}`), 'null');
|
||||
},
|
||||
};
|
||||
@@ -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()', () => {
|
||||
|
||||
@@ -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="<script>alert('foo');</script>`, { 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', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user