diff --git a/app/controllers/discourse_ai/ai_bot/artifacts_controller.rb b/app/controllers/discourse_ai/ai_bot/artifacts_controller.rb
index 251d765e..63a04a1b 100644
--- a/app/controllers/discourse_ai/ai_bot/artifacts_controller.rb
+++ b/app/controllers/discourse_ai/ai_bot/artifacts_controller.rb
@@ -19,8 +19,8 @@ module DiscourseAi
raise Discourse::NotFound if !guardian.can_see?(post)
end
- # Prepare the HTML document
- html = <<~HTML
+ # Prepare the inner (untrusted) HTML document
+ untrusted_html = <<~HTML
@@ -39,11 +39,33 @@ module DiscourseAi
HTML
+ # Prepare the outer (trusted) HTML document
+ trusted_html = <<~HTML
+
+
+
+
+ #{ERB::Util.html_escape(artifact.name)}
+
+
+
+
+
+
+ HTML
+
response.headers.delete("X-Frame-Options")
response.headers["Content-Security-Policy"] = "script-src 'unsafe-inline';"
# Render the content
- render html: html.html_safe, layout: false, content_type: "text/html"
+ render html: trusted_html.html_safe, layout: false, content_type: "text/html"
end
private
diff --git a/spec/requests/ai_bot/artifacts_controller_spec.rb b/spec/requests/ai_bot/artifacts_controller_spec.rb
index 0899db08..10f7bcde 100644
--- a/spec/requests/ai_bot/artifacts_controller_spec.rb
+++ b/spec/requests/ai_bot/artifacts_controller_spec.rb
@@ -18,6 +18,10 @@ RSpec.describe DiscourseAi::AiBot::ArtifactsController do
)
end
+ def parse_srcdoc(html)
+ Nokogiri.HTML5(html).at_css("iframe")["srcdoc"]
+ end
+
before do
SiteSetting.discourse_ai_enabled = true
SiteSetting.ai_artifact_security = "strict"
@@ -46,9 +50,10 @@ RSpec.describe DiscourseAi::AiBot::ArtifactsController do
sign_in(user)
get "/discourse-ai/ai-bot/artifacts/#{artifact.id}"
expect(response.status).to eq(200)
- expect(response.body).to include(artifact.html)
- expect(response.body).to include(artifact.css)
- expect(response.body).to include(artifact.js)
+ untrusted_html = parse_srcdoc(response.body)
+ expect(untrusted_html).to include(artifact.html)
+ expect(untrusted_html).to include(artifact.css)
+ expect(untrusted_html).to include(artifact.js)
end
end
@@ -58,7 +63,7 @@ RSpec.describe DiscourseAi::AiBot::ArtifactsController do
it "shows artifact without authentication" do
get "/discourse-ai/ai-bot/artifacts/#{artifact.id}"
expect(response.status).to eq(200)
- expect(response.body).to include(artifact.html)
+ expect(parse_srcdoc(response.body)).to include(artifact.html)
end
end