diff --git a/database/entities/Status.ts b/database/entities/Status.ts index 4347a453..a7727eee 100644 --- a/database/entities/Status.ts +++ b/database/entities/Status.ts @@ -1,3 +1,4 @@ +import { mentionValidator } from "@api"; import markdownItTaskLists from "@hackmd/markdown-it-task-lists"; import { dualLogger } from "@loggers"; import { sanitizeHtml, sanitizeHtmlInline } from "@sanitization"; @@ -365,26 +366,13 @@ export const resolveNote = async ( return createdNote; }; -export const createMentionRegExp = () => - createRegExp( - exactly("@"), - oneOrMore(anyOf(letter.lowercase, digit, charIn("-"))).groupedAs( - "username", - ), - maybe( - exactly("@"), - oneOrMore(anyOf(letter, digit, charIn("_-.:"))).groupedAs("domain"), - ), - [global], - ); - /** * Get people mentioned in the content (match @username or @username@domain.com mentions) * @param text The text to parse mentions from. * @returns An array of users mentioned in the text. */ export const parseTextMentions = async (text: string): Promise => { - const mentionedPeople = [...text.matchAll(createMentionRegExp())] ?? []; + const mentionedPeople = [...text.matchAll(mentionValidator)] ?? []; if (mentionedPeople.length === 0) return []; const baseUrlHost = new URL(config.http.base_url).host; diff --git a/server/api/well-known/webfinger/index.ts b/server/api/well-known/webfinger/index.ts index 9742bd61..941e62db 100644 --- a/server/api/well-known/webfinger/index.ts +++ b/server/api/well-known/webfinger/index.ts @@ -1,4 +1,9 @@ -import { applyConfig, handleZodError, idValidator } from "@api"; +import { + applyConfig, + handleZodError, + idValidator, + webfingerMention, +} from "@api"; import { zValidator } from "@hono/zod-validator"; import { errorResponse, jsonResponse } from "@response"; import { eq } from "drizzle-orm"; @@ -36,7 +41,7 @@ export default (app: Hono) => const { resource } = context.req.valid("query"); // Check if resource is in the correct format (acct:uuid/username@domain) - if (!resource.match(/^acct:[a-zA-Z0-9-]+@[a-zA-Z0-9.-:]+$/)) { + if (!resource.match(webfingerMention)) { return errorResponse( "Invalid resource (should be acct:(id or username)@domain)", 400, diff --git a/utils/api.ts b/utils/api.ts index fc1233f0..57232d98 100644 --- a/utils/api.ts +++ b/utils/api.ts @@ -11,7 +11,9 @@ import { createRegExp, digit, exactly, + global, letter, + maybe, oneOrMore, } from "magic-regexp"; import { parse } from "qs"; @@ -64,6 +66,28 @@ export const emojiValidatorWithColons = createRegExp( [caseInsensitive], ); +export const mentionValidator = createRegExp( + exactly("@"), + oneOrMore(anyOf(letter.lowercase, digit, charIn("-"))).groupedAs( + "username", + ), + maybe( + exactly("@"), + oneOrMore(anyOf(letter, digit, charIn("_-.:"))).groupedAs("domain"), + ), + [global], +); + +export const webfingerMention = createRegExp( + exactly("acct:"), + oneOrMore(anyOf(letter, digit, charIn("-"))).groupedAs("username"), + maybe( + exactly("@"), + oneOrMore(anyOf(letter, digit, charIn("_-.:"))).groupedAs("domain"), + ), + [], +); + export const handleZodError = ( result: | { success: true; data?: object }