From f8e77d90eb302521f1a6920592c9a27b13485fd3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 10 Dec 2024 17:43:01 -0600 Subject: [PATCH] Filter out invalid WebSocket URLs from pool, prevent admins from setting them Fixes https://gitlab.com/soapbox-pub/ditto/-/issues/276 --- src/controllers/api/ditto.ts | 4 ++-- src/schema.ts | 11 +++++++++++ src/storages.ts | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/controllers/api/ditto.ts b/src/controllers/api/ditto.ts index e3c6017f..765862ec 100644 --- a/src/controllers/api/ditto.ts +++ b/src/controllers/api/ditto.ts @@ -12,7 +12,7 @@ import { DittoEvent } from '@/interfaces/DittoEvent.ts'; import { DittoZapSplits, getZapSplits } from '@/utils/zap-split.ts'; import { AdminSigner } from '@/signers/AdminSigner.ts'; import { screenshotsSchema } from '@/schemas/nostr.ts'; -import { booleanParamSchema, percentageSchema } from '@/schema.ts'; +import { booleanParamSchema, percentageSchema, wsUrlSchema } from '@/schema.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; import { renderNameRequest } from '@/views/ditto.ts'; import { accountFromPubkey } from '@/views/mastodon/accounts.ts'; @@ -23,7 +23,7 @@ import { updateListAdminEvent } from '@/utils/api.ts'; const markerSchema = z.enum(['read', 'write']); const relaySchema = z.object({ - url: z.string().url(), + url: wsUrlSchema, marker: markerSchema.optional(), }); diff --git a/src/schema.ts b/src/schema.ts index 6147f562..b55a1f9a 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -33,6 +33,16 @@ const hashtagSchema = z.string().regex(/^\w{1,30}$/); */ const safeUrlSchema = z.string().max(2048).url(); +/** WebSocket URL. */ +const wsUrlSchema = z.string().refine((val) => { + try { + const { protocol } = new URL(val); + return protocol === 'wss:' || protocol === 'ws:'; + } catch { + return false; + } +}, 'Invalid WebSocket URL'); + /** https://github.com/colinhacks/zod/issues/1630#issuecomment-1365983831 */ const booleanParamSchema = z.enum(['true', 'false']).transform((value) => value === 'true'); @@ -81,4 +91,5 @@ export { percentageSchema, safeUrlSchema, sizesSchema, + wsUrlSchema, }; diff --git a/src/storages.ts b/src/storages.ts index 643de7a5..867c7939 100644 --- a/src/storages.ts +++ b/src/storages.ts @@ -3,6 +3,7 @@ import { Conf } from '@/config.ts'; import { DittoDatabase } from '@/db/DittoDatabase.ts'; import { DittoDB } from '@/db/DittoDB.ts'; import { internalSubscriptionsSizeGauge } from '@/metrics.ts'; +import { wsUrlSchema } from '@/schema.ts'; import { AdminStore } from '@/storages/AdminStore.ts'; import { EventsDB } from '@/storages/EventsDB.ts'; import { SearchStore } from '@/storages/search-store.ts'; @@ -80,7 +81,9 @@ export class Storages { const tags = relayList?.tags ?? []; const activeRelays = tags.reduce((acc, [name, url, marker]) => { - if (name === 'r' && (!marker || marker === 'write')) { + const valid = wsUrlSchema.safeParse(url).success; + + if (valid && name === 'r' && (!marker || marker === 'write')) { acc.push(url); } return acc;