From 3849e8c15aefd72e7fa2cea471f88143708f717e Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Fri, 4 Jul 2025 14:53:02 -0400 Subject: use shared URL verification in verifyLinkFields --- packages/backend/src/misc/verify-field-link.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/misc') diff --git a/packages/backend/src/misc/verify-field-link.ts b/packages/backend/src/misc/verify-field-link.ts index f9fc352806..f90b25248f 100644 --- a/packages/backend/src/misc/verify-field-link.ts +++ b/packages/backend/src/misc/verify-field-link.ts @@ -10,7 +10,7 @@ type Field = { name: string, value: string }; export async function verifyFieldLinks(fields: Field[], profile_url: string, httpRequestService: HttpRequestService): Promise { const verified_links = []; - for (const field_url of fields.filter(x => URL.canParse(x.value) && ['http:', 'https:'].includes((new URL(x.value).protocol)))) { + for (const field_url of fields) { try { const html = await httpRequestService.getHtml(field_url.value); -- cgit v1.2.3-freya From dc19b181123bfe2e92ca8f7edaee13215724c7fc Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Mon, 7 Jul 2025 11:46:35 -0400 Subject: add comment about validation in verify-field-link.ts --- packages/backend/src/misc/verify-field-link.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'packages/backend/src/misc') diff --git a/packages/backend/src/misc/verify-field-link.ts b/packages/backend/src/misc/verify-field-link.ts index f90b25248f..37161f16e5 100644 --- a/packages/backend/src/misc/verify-field-link.ts +++ b/packages/backend/src/misc/verify-field-link.ts @@ -12,6 +12,7 @@ export async function verifyFieldLinks(fields: Field[], profile_url: string, htt const verified_links = []; for (const field_url of fields) { try { + // getHtml validates the input URL, so we can safely pass in untrusted values const html = await httpRequestService.getHtml(field_url.value); const doc = cheerio(html); -- cgit v1.2.3-freya From 3dde7f25a6d1696c449b4c1d4e781697cf7e5b95 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 8 Jul 2025 11:01:37 -0400 Subject: move CaptchaError to a separate file to avoid circular import errors --- packages/backend/src/core/CaptchaService.ts | 17 ++++------------- packages/backend/src/misc/captcha-error.ts | 18 ++++++++++++++++++ packages/backend/src/misc/render-inline-error.ts | 2 +- 3 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 packages/backend/src/misc/captcha-error.ts (limited to 'packages/backend/src/misc') diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index c526a80aeb..020984a37f 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -9,7 +9,10 @@ import { bindThis } from '@/decorators.js'; import { MetaService } from '@/core/MetaService.js'; import { MiMeta } from '@/models/Meta.js'; import Logger from '@/logger.js'; -import { LoggerService } from './LoggerService.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { CaptchaError } from '@/misc/captcha-error.js'; + +export { CaptchaError } from '@/misc/captcha-error.js'; export const supportedCaptchaProviders = ['none', 'hcaptcha', 'mcaptcha', 'recaptcha', 'turnstile', 'fc', 'testcaptcha'] as const; export type CaptchaProvider = typeof supportedCaptchaProviders[number]; @@ -49,18 +52,6 @@ export type CaptchaSetting = { } }; -export class CaptchaError extends Error { - public readonly code: CaptchaErrorCode; - public readonly cause?: unknown; - - constructor(code: CaptchaErrorCode, message: string, cause?: unknown) { - super(message, cause ? { cause } : undefined); - this.code = code; - this.cause = cause; - this.name = 'CaptchaError'; - } -} - export type CaptchaSaveSuccess = { success: true; }; diff --git a/packages/backend/src/misc/captcha-error.ts b/packages/backend/src/misc/captcha-error.ts new file mode 100644 index 0000000000..217018ec68 --- /dev/null +++ b/packages/backend/src/misc/captcha-error.ts @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import type { CaptchaErrorCode } from '@/core/CaptchaService.js'; + +export class CaptchaError extends Error { + public readonly code: CaptchaErrorCode; + public readonly cause?: unknown; + + constructor(code: CaptchaErrorCode, message: string, cause?: unknown) { + super(message, cause ? { cause } : undefined); + this.code = code; + this.cause = cause; + this.name = 'CaptchaError'; + } +} diff --git a/packages/backend/src/misc/render-inline-error.ts b/packages/backend/src/misc/render-inline-error.ts index 07f9f3068e..886efcb86e 100644 --- a/packages/backend/src/misc/render-inline-error.ts +++ b/packages/backend/src/misc/render-inline-error.ts @@ -5,7 +5,7 @@ import { IdentifiableError } from '@/misc/identifiable-error.js'; import { StatusError } from '@/misc/status-error.js'; -import { CaptchaError } from '@/core/CaptchaService.js'; +import { CaptchaError } from '@/misc/captcha-error.js'; export function renderInlineError(err: unknown): string { const parts: string[] = []; -- cgit v1.2.3-freya From 2c8c422cb6d27515fdebf42f19f1d85a7fdac3fe Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Fri, 4 Jul 2025 10:00:40 -0400 Subject: include profile URI for link verification --- packages/backend/src/core/activitypub/models/ApPersonService.ts | 6 ++++-- packages/backend/src/misc/verify-field-link.ts | 4 ++-- packages/backend/src/server/api/endpoints/i/update.ts | 8 ++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'packages/backend/src/misc') diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 29f7459219..bc602bbd5b 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -377,7 +377,8 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown { const url = this.apUtilityService.findBestObjectUrl(person); - const verifiedLinks = url ? await verifyFieldLinks(fields, url, this.httpRequestService) : []; + const profileUrls = url ? [url, person.id] : [person.id]; + const verifiedLinks = await verifyFieldLinks(fields, profileUrls, this.httpRequestService); // Create user let user: MiRemoteUser | null = null; @@ -626,7 +627,8 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown { const url = this.apUtilityService.findBestObjectUrl(person); - const verifiedLinks = url ? await verifyFieldLinks(fields, url, this.httpRequestService) : []; + const profileUrls = url ? [url, person.id] : [person.id]; + const verifiedLinks = await verifyFieldLinks(fields, profileUrls, this.httpRequestService); const updates = { lastFetchedAt: new Date(), diff --git a/packages/backend/src/misc/verify-field-link.ts b/packages/backend/src/misc/verify-field-link.ts index f9fc352806..6a3c950059 100644 --- a/packages/backend/src/misc/verify-field-link.ts +++ b/packages/backend/src/misc/verify-field-link.ts @@ -8,7 +8,7 @@ import type { HttpRequestService } from '@/core/HttpRequestService.js'; type Field = { name: string, value: string }; -export async function verifyFieldLinks(fields: Field[], profile_url: string, httpRequestService: HttpRequestService): Promise { +export async function verifyFieldLinks(fields: Field[], profileUrls: string[], httpRequestService: HttpRequestService): Promise { const verified_links = []; for (const field_url of fields.filter(x => URL.canParse(x.value) && ['http:', 'https:'].includes((new URL(x.value).protocol)))) { try { @@ -18,7 +18,7 @@ export async function verifyFieldLinks(fields: Field[], profile_url: string, htt const links = doc('a[rel~="me"][href], link[rel~="me"][href]').toArray(); - const includesProfileLinks = links.some(link => link.attribs.href === profile_url); + const includesProfileLinks = links.some(link => profileUrls.includes(link.attribs.href)); if (includesProfileLinks) { verified_links.push(field_url.value); } diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 5767880531..65dcf6301f 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -603,11 +603,15 @@ export default class extends Endpoint { // eslint- this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); } - const verified_links = await verifyFieldLinks(newFields, `${this.config.url}/@${user.username}`, this.httpRequestService); + const profileUrls = [ + this.userEntityService.genLocalUserUri(user.id), + `${this.config.url}/@${user.username}`, + ]; + const verifiedLinks = await verifyFieldLinks(newFields, profileUrls, this.httpRequestService); await this.userProfilesRepository.update(user.id, { ...profileUpdates, - verifiedLinks: verified_links, + verifiedLinks, }); const iObj = await this.userEntityService.pack(user.id, user, { -- cgit v1.2.3-freya