Мне удалось отследить источник этой проблемы с плагином embed. При настройке встраивания существует параметр «Разрешённые хосты» (Allowed Hosts), который устанавливает CSP для iframe, и iframe отображается только на этом хосте. Например, если я настрою embed с разрешённым хостом example.com, а затем попытаюсь встроить JS на attacker.com, iframe откажется загружаться с ошибкой:
Refused to frame 'https://forum.example.com/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'self' https://example.com".
Однако к этому моменту JS уже фактически выполнился и создал тему на форуме с тем URL, который был передан в коде встраивания.
В моём случае происходит следующее: мы используем Netlify для предпросмотра сборок, поэтому копия блога фактически обслуживается с URL netlify.app, которые затем загружают embed.js и создают темы форума для постов, которые я ранее импортировал с ошибками.
Это не обязательно проблема безопасности, так как создание тем всё равно возможно только для URL на настроенном сайте, соответствующих списку разрешённых путей. Однако это проблема в данной стратегии миграции, поскольку у меня не было возможности установить embed_url для тем форума при переносе старых постов в Discourse.
Решение заключается в добавлении правила CSP, чтобы запретить выполнение embed.js на доменах, не указанных в параметре «Разрешённые хосты».
В качестве доказательства концепции я скопировал код встраивания на совершенно чужеродный домен и установил discourseEmbedUrl на один из старых постов, которые я импортировал. После загрузки страницы iframe был заблокирован, но JS уже выполнился и создал тему форума.
Если вы считаете, что это больше проблема безопасности, чем баг, я с радостью удалю этот пост и сообщу о нём через HackerOne.