I just tracked down the source of this issue with the embed plugin. When configuring the embedding, there is an “Allowed Hosts” setting, which then sets the CSP for the iframe and the iframe will only show up at that host. For example if I configure embeds with an allowed host of example.com then try to embed the JS on attacker.com the iframe refuses to load with the error
Refused to frame 'https://forum.example.com/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'self' https://example.com".
However, the JS has already actually run at this point and created a topic on the forum with whatever URL was given to the embed code.
What’s happening in my case is we use Netlify to preview builds, so a copy of the blog is actually being served from netlify.app URLs, which is then loading that embed.js and creating the forum thread for the posts that I had previously incorrectly imported.
This isn’t necessarily a security issue since it can still only create topics for URLs at the configured website and that match the path allowlist. But it’s an issue in this migration strategy since I had no way to set the embed_url on the forum threads when migrating old posts into Discourse.
The fix is to add a CSP rule to prevent that embed.js from being allowed to run on domains that are not configured under the “allowed hosts” setting.
As a proof of concept, I copied the embed code to a completely unrelated domain and set the discourseEmbedUrl to one of the old posts that I imported. I loaded the page and the iframe was blocked, but the JS had already run and created the forum thread.
If you think this is more of a security issue than a bug, I’m happy to delete this post and report it through hackerone instead.