mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
feat: restrict access to ap.probe method to registered users, add rate limiting protection
This commit is contained in:
@@ -27,6 +27,9 @@ const probeCache = ttl({
|
|||||||
max: 500,
|
max: 500,
|
||||||
ttl: 1000 * 60 * 60, // 1 hour
|
ttl: 1000 * 60 * 60, // 1 hour
|
||||||
});
|
});
|
||||||
|
const probeRateLimit = ttl({
|
||||||
|
ttl: 1000 * 3, // 3 seconds
|
||||||
|
});
|
||||||
|
|
||||||
const ActivityPub = module.exports;
|
const ActivityPub = module.exports;
|
||||||
|
|
||||||
@@ -506,6 +509,13 @@ ActivityPub.probe = async ({ uid, url }) => {
|
|||||||
* - Returns a relative path if already available, true if not, and false otherwise.
|
* - Returns a relative path if already available, true if not, and false otherwise.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Disable on config setting; restrict lookups to HTTPS-enabled URLs only
|
||||||
|
const { activitypubProbe } = meta.config;
|
||||||
|
const { protocol } = new URL(url);
|
||||||
|
if (!activitypubProbe || protocol !== 'https:') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Known resources
|
// Known resources
|
||||||
const [isNote, isMessage, isActor, isActorUrl] = await Promise.all([
|
const [isNote, isMessage, isActor, isActorUrl] = await Promise.all([
|
||||||
posts.exists(url),
|
posts.exists(url),
|
||||||
@@ -541,6 +551,17 @@ ActivityPub.probe = async ({ uid, url }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Guests not allowed to use expensive logic path
|
||||||
|
if (!uid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One request allowed every 3 seconds (configured at top)
|
||||||
|
const limited = probeRateLimit.get(uid);
|
||||||
|
if (limited) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Cached result
|
// Cached result
|
||||||
if (probeCache.has(url)) {
|
if (probeCache.has(url)) {
|
||||||
return probeCache.get(url);
|
return probeCache.get(url);
|
||||||
@@ -572,6 +593,7 @@ ActivityPub.probe = async ({ uid, url }) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
probeRateLimit.set(uid, true);
|
||||||
return await checkHeader(meta.config.activitypubProbeTimeout || 2000);
|
return await checkHeader(meta.config.activitypubProbeTimeout || 2000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === 'TimeoutError') {
|
if (e.name === 'TimeoutError') {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Controller.fetch = async (req, res, next) => {
|
|||||||
if (typeof result === 'string') {
|
if (typeof result === 'string') {
|
||||||
return helpers.redirect(res, result);
|
return helpers.redirect(res, result);
|
||||||
} else if (result) {
|
} else if (result) {
|
||||||
const { id, type } = await activitypub.get('uid', req.uid || 0, url.href);
|
const { id, type } = await activitypub.get('uid', req.uid, url.href);
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case activitypub._constants.acceptedPostTypes.includes(type): {
|
case activitypub._constants.acceptedPostTypes.includes(type): {
|
||||||
return helpers.redirect(res, `/post/${encodeURIComponent(id)}`);
|
return helpers.redirect(res, `/post/${encodeURIComponent(id)}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user