From 065590279ebd74cd3e231327cc6f5275a38dc81d Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Tue, 29 Oct 2024 17:44:40 +0100 Subject: [PATCH] [backend] Strengthen checks against local object resolution This commit addresses disclosed primitives 26-29 & 31-33 --- .../backend/src/remote/activitypub/db-resolver.ts | 15 ++++++++------- .../backend/src/remote/activitypub/models/note.ts | 4 ++-- .../src/remote/activitypub/models/person.ts | 6 +++--- .../src/remote/activitypub/models/question.ts | 3 ++- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/backend/src/remote/activitypub/db-resolver.ts b/packages/backend/src/remote/activitypub/db-resolver.ts index 961d67ceb..122c23f51 100644 --- a/packages/backend/src/remote/activitypub/db-resolver.ts +++ b/packages/backend/src/remote/activitypub/db-resolver.ts @@ -19,6 +19,7 @@ import type { IObject } from "./type.js"; import { getApId } from "./type.js"; import { resolvePerson, updatePerson } from "./models/person.js"; import {redisClient, subscriber} from "@/db/redis.js"; +import { extractDbHost, toPuny } from "@/misc/convert-host.js"; const publicKeyCache = new Cache("publicKey", 60 * 30); const publicKeyByUserIdCache = new Cache( @@ -46,15 +47,15 @@ export type UriParseResult = export function parseUri(value: string | IObject): UriParseResult { const uri = getApId(value); + const parsed = new URL(uri); - // the host part of a URL is case insensitive, so use the 'i' flag. - const localRegex = new RegExp( - `^${escapeRegexp(config.url)}/(\\w+)/(\\w+)(?:/(.+))?`, - "i", - ); - const matchLocal = uri.match(localRegex); + if (toPuny(parsed.host) === toPuny(config.host)) { + const localRegex = new RegExp(`^.*?/(\\w+)/(\\w+)(?:/(.+))?`); + const matchLocal = uri.match(localRegex); + if (matchLocal == null) { + throw new Error(`Failed to parse local URI: ${uri}`); + } - if (matchLocal) { return { local: true, type: matchLocal[1], diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 827cc9ed6..f0e461c53 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -455,7 +455,7 @@ export async function resolveNote( } //#endregion - if (uri.startsWith(config.url)) { + if (extractDbHost(uri) === toPuny(config.host)) { throw new StatusError( "cannot resolve local note", 400, @@ -572,7 +572,7 @@ export async function updateNote(value: string | IObject, actor: CacheableRemote if (!uri) throw new Error("Missing note uri"); // Skip if URI points to this server - if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local"); + if (extractDbHost(uri) === toPuny(config.host)) throw new Error("uri points local"); // A new resolver is created if not specified if (resolver == null) resolver = new Resolver(); diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index b8dd57ac2..0ca986d30 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -174,7 +174,7 @@ export async function fetchPerson( if (cached) return cached; // Fetch from the database if the URI points to this server - if (uri.startsWith(`${config.url}/`)) { + if (extractDbHost(uri) === toPuny(config.host)) { const id = uri.split("/").pop(); const u = await Users.findOneBy({ id }); if (u) await uriPersonCache.set(uri, u); @@ -204,7 +204,7 @@ export async function createPerson( ): Promise { if (typeof uri !== "string") throw new Error("uri is not string"); - if (uri.startsWith(config.url)) { + if (extractDbHost(uri) === toPuny(config.host)) { throw new StatusError( "cannot resolve local user", 400, @@ -507,7 +507,7 @@ export async function updatePerson( if (typeof uri !== "string") throw new Error("uri is not string"); // Skip if the URI points to this server - if (uri.startsWith(`${config.url}/`)) { + if (extractDbHost(uri) === toPuny(config.host)) { return; } diff --git a/packages/backend/src/remote/activitypub/models/question.ts b/packages/backend/src/remote/activitypub/models/question.ts index 6f65866fe..614dfe113 100644 --- a/packages/backend/src/remote/activitypub/models/question.ts +++ b/packages/backend/src/remote/activitypub/models/question.ts @@ -5,6 +5,7 @@ import { getApId, isQuestion } from "../type.js"; import { apLogger } from "../logger.js"; import { Notes, Polls } from "@/models/index.js"; import type { IPoll } from "@/models/entities/poll.js"; +import { extractDbHost, toPuny } from "@/misc/convert-host.js"; export async function extractPollFromQuestion( source: string | IObject, @@ -55,7 +56,7 @@ export async function updateQuestion( const uri = typeof value === "string" ? value : getApId(value); // Skip if URI points to this server - if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local"); + if (extractDbHost(uri) === toPuny(config.host)) throw new Error("uri points local"); //#region Already registered with this server? const note = await Notes.findOneBy({ uri });