From 1a50578b3921a9333cd94fc869f18759773b10c1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 4 Nov 2024 10:12:21 -0600 Subject: [PATCH 1/6] Add PleromaConfigDB module --- src/controllers/api/pleroma.ts | 27 +++++++++++++-------------- src/utils/PleromaConfigDB.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 src/utils/PleromaConfigDB.ts diff --git a/src/controllers/api/pleroma.ts b/src/controllers/api/pleroma.ts index 31d8545f..98e64b51 100644 --- a/src/controllers/api/pleroma.ts +++ b/src/controllers/api/pleroma.ts @@ -3,16 +3,17 @@ import { z } from 'zod'; import { type AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; -import { configSchema, elixirTupleSchema, type PleromaConfig } from '@/schemas/pleroma-api.ts'; +import { configSchema, elixirTupleSchema } from '@/schemas/pleroma-api.ts'; import { AdminSigner } from '@/signers/AdminSigner.ts'; import { Storages } from '@/storages.ts'; import { createAdminEvent, updateAdminEvent, updateUser } from '@/utils/api.ts'; import { lookupPubkey } from '@/utils/lookup.ts'; +import { PleromaConfigDB } from '@/utils/PleromaConfigDB.ts'; const frontendConfigController: AppController = async (c) => { const store = await Storages.db(); - const configs = await getConfigs(store, c.req.raw.signal); - const frontendConfig = configs.find(({ group, key }) => group === ':pleroma' && key === ':frontend_configurations'); + const configDB = await getConfigs(store, c.req.raw.signal); + const frontendConfig = configDB.get(':pleroma', ':frontend_configurations'); if (frontendConfig) { const schema = elixirTupleSchema.transform(({ tuple }) => tuple).array(); @@ -40,14 +41,7 @@ const updateConfigController: AppController = async (c) => { const configs = await getConfigs(store, c.req.raw.signal); const { configs: newConfigs } = z.object({ configs: z.array(configSchema) }).parse(await c.req.json()); - for (const { group, key, value } of newConfigs) { - const index = configs.findIndex((c) => c.group === group && c.key === key); - if (index === -1) { - configs.push({ group, key, value }); - } else { - configs[index].value = value; - } - } + configs.merge(newConfigs); await createAdminEvent({ kind: 30078, @@ -70,7 +64,7 @@ const pleromaAdminDeleteStatusController: AppController = async (c) => { return c.json({}); }; -async function getConfigs(store: NStore, signal: AbortSignal): Promise { +async function getConfigs(store: NStore, signal: AbortSignal): Promise { const { pubkey } = Conf; const [event] = await store.query([{ @@ -80,11 +74,16 @@ async function getConfigs(store: NStore, signal: AbortSignal): Promise c.group === group && c.key === key); + } + + set(group: string, key: string, value: PleromaConfig): void { + const index = this.configs.findIndex((c) => c.group === group && c.key === key); + if (index === -1) { + this.configs.push(value); + } else { + this.configs[index] = value; + } + } + + merge(configs: PleromaConfig[]): void { + for (const { group, key, value } of configs) { + this.set(group, key, { group, key, value }); + } + } + + toJSON(): PleromaConfig[] { + return this.configs; + } +} From 3e27e902d59222ffcef38619f4d2cc1f91f628c3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 14 Nov 2024 17:49:30 -0600 Subject: [PATCH 2/6] Add PleromaConfigDB test --- fixtures/config-db.json | 291 ++++++++++++++++++++++++++++++ src/utils/PleromaConfigDB.test.ts | 14 ++ src/utils/PleromaConfigDB.ts | 3 +- 3 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 fixtures/config-db.json create mode 100644 src/utils/PleromaConfigDB.test.ts diff --git a/fixtures/config-db.json b/fixtures/config-db.json new file mode 100644 index 00000000..9c590c79 --- /dev/null +++ b/fixtures/config-db.json @@ -0,0 +1,291 @@ +{ + "configs": [{ + "db": [ + ":soapbox_fe" + ], + "group": ":pleroma", + "key": ":frontend_configurations", + "value": [ + { + "tuple": [ + ":pleroma_fe", + { + ":alwaysShowSubjectInput": true, + ":background": "/images/city.jpg", + ":collapseMessageWithSubject": false, + ":disableChat": false, + ":greentext": false, + ":hideFilteredStatuses": false, + ":hideMutedPosts": false, + ":hidePostStats": false, + ":hideSitename": false, + ":hideUserStats": false, + ":loginMethod": "password", + ":logo": "/static/logo.svg", + ":logoMargin": ".1em", + ":logoMask": true, + ":minimalScopesMode": false, + ":noAttachmentLinks": false, + ":nsfwCensorImage": "", + ":postContentType": "text/plain", + ":redirectRootLogin": "/main/friends", + ":redirectRootNoLogin": "/main/all", + ":scopeCopy": true, + ":showFeaturesPanel": true, + ":showInstanceSpecificPanel": false, + ":sidebarRight": false, + ":subjectLineBehavior": "email", + ":theme": "pleroma-dark", + ":webPushNotifications": false + } + ] + }, + { + "tuple": [ + ":soapbox_fe", + { + "aboutPages": {}, + "ads": [ + { + "card": { + "height": 564, + "image": "https://media.gleasonator.com/3c331456d0d0f9f9ad91eab0efbb4df22a044f92bdf6ef349b26de97db5ca3bd.png", + "type": "link", + "url": "https://www.veganbodybuilding.com/", + "width": 564 + } + }, + { + "card": { + "height": 250, + "image": "https://media.gleasonator.com/22590c7cb3edd8ac82660301be980c6fcad6b96a320e24f709ad0571a29ea0aa.png", + "type": "link", + "url": "https://poa.st", + "width": 300 + } + } + ], + "allowedEmoji": [ + "👍", + "⚡", + "❤", + "😂", + "😯", + "😢", + "😡" + ], + "authenticatedProfile": false, + "banner": "", + "betaPages": {}, + "brandColor": "#1ca82b", + "colors": { + "accent": { + "100": "#eafae7", + "200": "#caf4c3", + "300": "#6bdf58", + "400": "#55da40", + "50": "#f4fdf3", + "500": "#2bd110", + "600": "#27bc0e", + "700": "#209d0c", + "800": "#0d3f05", + "900": "#082803" + }, + "accent-blue": "#199727", + "danger": { + "100": "#fee2e2", + "200": "#fecaca", + "300": "#fca5a5", + "400": "#f87171", + "50": "#fef2f2", + "500": "#ef4444", + "600": "#dc2626", + "700": "#b91c1c", + "800": "#991b1b", + "900": "#7f1d1d" + }, + "gradient-end": "#2bd110", + "gradient-start": "#1ca82b", + "gray": { + "100": "#f1f6f2", + "200": "#dde8de", + "300": "#9ebfa2", + "400": "#91b595", + "50": "#f8faf8", + "500": "#75a37a", + "600": "#69936e", + "700": "#4c504c", + "800": "#233125", + "900": "#161f17" + }, + "greentext": "#789922", + "primary": { + "100": "#e8f6ea", + "200": "#c6e9ca", + "300": "#60c26b", + "400": "#49b955", + "50": "#f4fbf4", + "500": "#1ca82b", + "600": "#199727", + "700": "#157e20", + "800": "#08320d", + "900": "#052008" + }, + "secondary": { + "100": "#eafae7", + "200": "#cef4c3", + "300": "#7cdf58", + "400": "#71da40", + "50": "#f9fdf3", + "500": "#359713", + "600": "#4ebc0e", + "700": "#2f9d0c", + "800": "#173f05", + "900": "#282828" + }, + "success": { + "100": "#dcfce7", + "200": "#bbf7d0", + "300": "#86efac", + "400": "#4ade80", + "50": "#f0fdf4", + "500": "#22c55e", + "600": "#16a34a", + "700": "#15803d", + "800": "#166534", + "900": "#14532d" + } + }, + "copyright": "♥2022. Copying is an act of love. Please copy and share.", + "cryptoAddresses": [ + { + "address": "bc1q9cx35adpm73aq2fw40ye6ts8hfxqzjr5unwg0n", + "ticker": "btc" + }, + { + "address": "0xAc9aB5Fc04Dc1cB1789Af75b523Bd23C70B2D717", + "ticker": "eth" + }, + { + "address": "D5zVZs6jrRakaPVGiErkQiHt9sayzm6V5D", + "ticker": "doge" + }, + { + "address": "0x541a45cb212b57f41393427fb15335fc89c35851", + "ticker": "ubq" + }, + { + "address": "45JDCLrjJ4bgVUSbbs2yjy9m5Mf4VLPW8fG7jw9sq5u69rXZZopQogZNeyYkMBnXpkaip4p4QwaaJNhdTotPa9g44DBCzdK", + "ticker": "xmr" + }, + { + "address": "ltc1qda645jdf4jszwxcvsn32ykdhemvlx7yl9n5gz9", + "ticker": "ltc" + }, + { + "address": "bitcoincash:qpcfnm9w8uemax38yqhyg58zn2ptpf6szvkr0n48a7", + "ticker": "bch" + }, + { + "address": "XnB5p4JvL3So91A1c1MERozZEjeMSsAD7J", + "ticker": "dash" + }, + { + "address": "t1PHZX5ZjY7y61iC19A958W9hdyH3SiLJuF", + "ticker": "zec" + }, + { + "address": "0xB81BAEE10d163404a1c60045a872a0da9E258465", + "ticker": "etc" + }, + { + "address": "AGTLRXapPYpxt3PLdiXEs8y4kLw6Qy3C4t", + "ticker": "btg" + }, + { + "address": "SbQcFUDi7kKyxkmskzW3w74x68H5eUrg76", + "ticker": "dgb" + }, + { + "address": "N7nompUVxz5ATrzRVTzw7CaAJoSiVtEcQx", + "ticker": "nmc" + }, + { + "address": "3AQcUgCbF6ymiR4HGCU8ANx9SqbzL6nx8r", + "ticker": "vtc" + } + ], + "cryptoDonatePanel": { + "limit": 1 + }, + "customCss": [], + "defaultSettings": { + "themeMode": "system" + }, + "displayFqn": true, + "extensions": { + "ads": { + "enabled": false, + "interval": 40, + "provider": "soapbox" + }, + "patron": { + "enabled": true + } + }, + "features": { + "accountAliases": true + }, + "feedInjection": true, + "gdpr": false, + "greentext": true, + "logo": "https://media.gleasonator.com/0c760b3ecdbc993ba47b785d0adecf0ec71fd9c59808e27d0665b9f77a32d8de.png", + "mediaPreview": false, + "mobilePages": {}, + "navlinks": { + "homeFooter": [ + { + "title": "About", + "url": "/about" + }, + { + "title": "Terms of Service", + "url": "/about/tos" + }, + { + "title": "Privacy Policy", + "url": "/about/privacy" + }, + { + "title": "DMCA", + "url": "/about/dmca" + }, + { + "title": "Source Code", + "url": "/about#opensource" + } + ] + }, + "promoPanel": { + "items": [ + { + "icon": "music", + "text": "Gleasonator theme song", + "url": "https://media.gleasonator.com/custom/261905_gleasonator_song.mp3" + } + ] + }, + "redirectRootNoLogin": "", + "sentryDsn": "https://95c1dd3284d7284134928059844ba086@o4505999744499712.ingest.sentry.io/4505999904931840", + "singleUserMode": false, + "singleUserModeProfile": "", + "tileServer": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "tileServerAttribution": "© OpenStreetMap Contributors", + "verifiedCanEditName": true, + "verifiedIcon": "" + } + ] + } + ] + }] +} diff --git a/src/utils/PleromaConfigDB.test.ts b/src/utils/PleromaConfigDB.test.ts new file mode 100644 index 00000000..28085178 --- /dev/null +++ b/src/utils/PleromaConfigDB.test.ts @@ -0,0 +1,14 @@ +import { assertEquals } from '@std/assert'; + +import data from '~/fixtures/config-db.json' with { type: 'json' }; + +import { PleromaConfig } from '@/schemas/pleroma-api.ts'; +import { PleromaConfigDB } from '@/utils/PleromaConfigDB.ts'; + +Deno.test('PleromaConfigDB', () => { + const configs = new PleromaConfigDB(data.configs as PleromaConfig[]); + + const frontendConfigurations = configs.get(':pleroma', ':frontend_configurations'); + + assertEquals((frontendConfigurations as any).value[1].tuple[0], ':soapbox_fe'); +}); diff --git a/src/utils/PleromaConfigDB.ts b/src/utils/PleromaConfigDB.ts index ac30daec..4200d7b0 100644 --- a/src/utils/PleromaConfigDB.ts +++ b/src/utils/PleromaConfigDB.ts @@ -1,8 +1,7 @@ import type { PleromaConfig } from '@/schemas/pleroma-api.ts'; export class PleromaConfigDB { - constructor(private configs: PleromaConfig[]) { - } + constructor(private configs: PleromaConfig[]) {} get(group: string, key: string): PleromaConfig | undefined { return this.configs.find((c) => c.group === group && c.key === key); From 55f50ba93d0d9759a5c7cb7e24ee9f49ec712f89 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 14 Nov 2024 19:54:29 -0600 Subject: [PATCH 3/6] PleromaConfigDB: implement .getIn --- src/utils/PleromaConfigDB.test.ts | 21 +++++++++++++--- src/utils/PleromaConfigDB.ts | 41 ++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/utils/PleromaConfigDB.test.ts b/src/utils/PleromaConfigDB.test.ts index 28085178..4e1242dd 100644 --- a/src/utils/PleromaConfigDB.test.ts +++ b/src/utils/PleromaConfigDB.test.ts @@ -5,10 +5,23 @@ import data from '~/fixtures/config-db.json' with { type: 'json' }; import { PleromaConfig } from '@/schemas/pleroma-api.ts'; import { PleromaConfigDB } from '@/utils/PleromaConfigDB.ts'; -Deno.test('PleromaConfigDB', () => { - const configs = new PleromaConfigDB(data.configs as PleromaConfig[]); +Deno.test('PleromaConfigDB.getIn', () => { + const configDB = new PleromaConfigDB(data.configs as PleromaConfig[]); - const frontendConfigurations = configs.get(':pleroma', ':frontend_configurations'); + assertEquals( + configDB.get(':pleroma', ':frontend_configurations')?.value, + configDB.getIn(':pleroma', ':frontend_configurations'), + ); - assertEquals((frontendConfigurations as any).value[1].tuple[0], ':soapbox_fe'); + assertEquals(configDB.getIn(':pleroma', ':frontend_configurations', ':bleroma'), undefined); + + assertEquals( + configDB.getIn(':pleroma', ':frontend_configurations', ':soapbox_fe', 'colors', 'primary', '500'), + '#1ca82b', + ); + + assertEquals( + configDB.getIn(':pleroma', ':frontend_configurations', ':soapbox_fe', 'colors', 'primary', '99999999'), + undefined, + ); }); diff --git a/src/utils/PleromaConfigDB.ts b/src/utils/PleromaConfigDB.ts index 4200d7b0..63839348 100644 --- a/src/utils/PleromaConfigDB.ts +++ b/src/utils/PleromaConfigDB.ts @@ -1,4 +1,4 @@ -import type { PleromaConfig } from '@/schemas/pleroma-api.ts'; +import type { ElixirTuple, ElixirValue, PleromaConfig } from '@/schemas/pleroma-api.ts'; export class PleromaConfigDB { constructor(private configs: PleromaConfig[]) {} @@ -7,6 +7,34 @@ export class PleromaConfigDB { return this.configs.find((c) => c.group === group && c.key === key); } + getIn(group: string, key: string, ...paths: string[]): ElixirValue | undefined { + const config = this.get(group, key); + if (!config) return undefined; + + let value = config.value; + + for (const path of paths) { + if (Array.isArray(value)) { + const tuple = value.find((item): item is ElixirTuple => { + return PleromaConfigDB.isTuple(item) && item.tuple[0] === path; + }); + if (tuple) { + value = tuple.tuple[1]; + } else { + return; + } + } else if (PleromaConfigDB.isTuple(value) && value.tuple[0] === path) { + value = value.tuple[1]; + } else if (!PleromaConfigDB.isTuple(value) && value && typeof value === 'object' && path in value) { + value = value[path]; + } else { + return; + } + } + + return value; + } + set(group: string, key: string, value: PleromaConfig): void { const index = this.configs.findIndex((c) => c.group === group && c.key === key); if (index === -1) { @@ -25,4 +53,15 @@ export class PleromaConfigDB { toJSON(): PleromaConfig[] { return this.configs; } + + private static isTuple(value: ElixirValue): value is ElixirTuple { + return Boolean( + value && + typeof value === 'object' && + 'tuple' in value && + Array.isArray(value.tuple) && + value.tuple.length === 2 && + typeof value.tuple[0] === 'string', + ); + } } From 02ada73f48d5faec9e15091021d5c1cc52eeac96 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 14 Nov 2024 20:12:27 -0600 Subject: [PATCH 4/6] Include Soapbox sentryDsn in CSP --- src/controllers/api/pleroma.ts | 32 ++++---------------------------- src/middleware/cspMiddleware.ts | 17 ++++++++++++++++- src/utils/pleroma.ts | 29 +++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 src/utils/pleroma.ts diff --git a/src/controllers/api/pleroma.ts b/src/controllers/api/pleroma.ts index 98e64b51..d9289df1 100644 --- a/src/controllers/api/pleroma.ts +++ b/src/controllers/api/pleroma.ts @@ -1,4 +1,3 @@ -import { NSchema as n, NStore } from '@nostrify/nostrify'; import { z } from 'zod'; import { type AppController } from '@/app.ts'; @@ -8,11 +7,11 @@ import { AdminSigner } from '@/signers/AdminSigner.ts'; import { Storages } from '@/storages.ts'; import { createAdminEvent, updateAdminEvent, updateUser } from '@/utils/api.ts'; import { lookupPubkey } from '@/utils/lookup.ts'; -import { PleromaConfigDB } from '@/utils/PleromaConfigDB.ts'; +import { getPleromaConfigs } from '@/utils/pleroma.ts'; const frontendConfigController: AppController = async (c) => { const store = await Storages.db(); - const configDB = await getConfigs(store, c.req.raw.signal); + const configDB = await getPleromaConfigs(store, c.req.raw.signal); const frontendConfig = configDB.get(':pleroma', ':frontend_configurations'); if (frontendConfig) { @@ -29,7 +28,7 @@ const frontendConfigController: AppController = async (c) => { const configController: AppController = async (c) => { const store = await Storages.db(); - const configs = await getConfigs(store, c.req.raw.signal); + const configs = await getPleromaConfigs(store, c.req.raw.signal); return c.json({ configs, need_reboot: false }); }; @@ -38,7 +37,7 @@ const updateConfigController: AppController = async (c) => { const { pubkey } = Conf; const store = await Storages.db(); - const configs = await getConfigs(store, c.req.raw.signal); + const configs = await getPleromaConfigs(store, c.req.raw.signal); const { configs: newConfigs } = z.object({ configs: z.array(configSchema) }).parse(await c.req.json()); configs.merge(newConfigs); @@ -64,29 +63,6 @@ const pleromaAdminDeleteStatusController: AppController = async (c) => { return c.json({}); }; -async function getConfigs(store: NStore, signal: AbortSignal): Promise { - const { pubkey } = Conf; - - const [event] = await store.query([{ - kinds: [30078], - authors: [pubkey], - '#d': ['pub.ditto.pleroma.config'], - limit: 1, - }], { signal }); - - if (!event) { - return new PleromaConfigDB([]); - } - - try { - const decrypted = await new AdminSigner().nip44.decrypt(Conf.pubkey, event.content); - const configs = n.json().pipe(configSchema.array()).catch([]).parse(decrypted); - return new PleromaConfigDB(configs); - } catch (_e) { - return new PleromaConfigDB([]); - } -} - const pleromaAdminTagSchema = z.object({ nicknames: z.string().array(), tags: z.string().array(), diff --git a/src/middleware/cspMiddleware.ts b/src/middleware/cspMiddleware.ts index 00c4ecc3..2701e214 100644 --- a/src/middleware/cspMiddleware.ts +++ b/src/middleware/cspMiddleware.ts @@ -1,15 +1,30 @@ import { AppMiddleware } from '@/app.ts'; import { Conf } from '@/config.ts'; +import { PleromaConfigDB } from '@/utils/PleromaConfigDB.ts'; +import { Storages } from '@/storages.ts'; +import { getPleromaConfigs } from '@/utils/pleroma.ts'; + +let configDBCache: Promise | undefined; export const cspMiddleware = (): AppMiddleware => { return async (c, next) => { + const store = await Storages.db(); + + if (!configDBCache) { + configDBCache = getPleromaConfigs(store); + } + const { host, protocol, origin } = Conf.url; const wsProtocol = protocol === 'http:' ? 'ws:' : 'wss:'; + const configDB = await configDBCache; + const sentryDsn = configDB.getIn(':pleroma', ':frontend_configurations', ':soapbox_fe', 'sentryDsn'); const policies = [ 'upgrade-insecure-requests', `script-src 'self'`, - `connect-src 'self' blob: ${origin} ${wsProtocol}//${host}`, + `connect-src 'self' blob: ${origin} ${wsProtocol}//${host}` + typeof sentryDsn === 'string' + ? ` ${sentryDsn}` + : '', `media-src 'self' https:`, `img-src 'self' data: blob: https:`, `default-src 'none'`, diff --git a/src/utils/pleroma.ts b/src/utils/pleroma.ts new file mode 100644 index 00000000..05c35b7c --- /dev/null +++ b/src/utils/pleroma.ts @@ -0,0 +1,29 @@ +import { NSchema as n, NStore } from '@nostrify/nostrify'; + +import { Conf } from '@/config.ts'; +import { configSchema } from '@/schemas/pleroma-api.ts'; +import { AdminSigner } from '@/signers/AdminSigner.ts'; +import { PleromaConfigDB } from '@/utils/PleromaConfigDB.ts'; + +export async function getPleromaConfigs(store: NStore, signal?: AbortSignal): Promise { + const { pubkey } = Conf; + + const [event] = await store.query([{ + kinds: [30078], + authors: [pubkey], + '#d': ['pub.ditto.pleroma.config'], + limit: 1, + }], { signal }); + + if (!event) { + return new PleromaConfigDB([]); + } + + try { + const decrypted = await new AdminSigner().nip44.decrypt(Conf.pubkey, event.content); + const configs = n.json().pipe(configSchema.array()).catch([]).parse(decrypted); + return new PleromaConfigDB(configs); + } catch (_e) { + return new PleromaConfigDB([]); + } +} From 3d376ba8b3e747c7590502f9110492657f2d5401 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 14 Nov 2024 20:18:03 -0600 Subject: [PATCH 5/6] csp: fix connect-src --- src/middleware/cspMiddleware.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/middleware/cspMiddleware.ts b/src/middleware/cspMiddleware.ts index 2701e214..ed102f7a 100644 --- a/src/middleware/cspMiddleware.ts +++ b/src/middleware/cspMiddleware.ts @@ -19,12 +19,16 @@ export const cspMiddleware = (): AppMiddleware => { const configDB = await configDBCache; const sentryDsn = configDB.getIn(':pleroma', ':frontend_configurations', ':soapbox_fe', 'sentryDsn'); + const connectSrc = ["'self'", 'blob:', origin, `${wsProtocol}//${host}`]; + + if (typeof sentryDsn === 'string') { + connectSrc.push(sentryDsn); + } + const policies = [ 'upgrade-insecure-requests', `script-src 'self'`, - `connect-src 'self' blob: ${origin} ${wsProtocol}//${host}` + typeof sentryDsn === 'string' - ? ` ${sentryDsn}` - : '', + `connect-src ${connectSrc.join(' ')}`, `media-src 'self' https:`, `img-src 'self' data: blob: https:`, `default-src 'none'`, From aea31bce5d6072e6f8ad1b63125cd74afa6096f3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 14 Nov 2024 20:28:14 -0600 Subject: [PATCH 6/6] csp: use the sentry origin instead of the URL itself --- src/middleware/cspMiddleware.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/middleware/cspMiddleware.ts b/src/middleware/cspMiddleware.ts index ed102f7a..70c9316d 100644 --- a/src/middleware/cspMiddleware.ts +++ b/src/middleware/cspMiddleware.ts @@ -22,7 +22,12 @@ export const cspMiddleware = (): AppMiddleware => { const connectSrc = ["'self'", 'blob:', origin, `${wsProtocol}//${host}`]; if (typeof sentryDsn === 'string') { - connectSrc.push(sentryDsn); + try { + const dsn = new URL(sentryDsn); + connectSrc.push(dsn.origin); + } catch { + // Ignore + } } const policies = [