Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit 3ae1e4e

Browse files
authored
FIX: properly bypass CSP for artifacts (#920)
Was meant to be bypassed but was not implemented correctly
1 parent 755b63f commit 3ae1e4e

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

app/controllers/discourse_ai/ai_bot/artifacts_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def show
4040
HTML
4141

4242
response.headers.delete("X-Frame-Options")
43-
response.headers.delete("Content-Security-Policy")
43+
response.headers["Content-Security-Policy"] = "script-src 'unsafe-inline';"
4444

4545
# Render the content
4646
render html: html.html_safe, layout: false, content_type: "text/html"
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe DiscourseAi::AiBot::ArtifactsController do
4+
fab!(:user)
5+
fab!(:topic) { Fabricate(:private_message_topic, user: user) }
6+
fab!(:post) { Fabricate(:post, user: user, topic: topic) }
7+
fab!(:artifact) do
8+
AiArtifact.create!(
9+
user: user,
10+
post: post,
11+
name: "Test Artifact",
12+
html: "<div>Hello World</div>",
13+
css: "div { color: blue; }",
14+
js: "console.log('test');",
15+
metadata: {
16+
public: false,
17+
},
18+
)
19+
end
20+
21+
before do
22+
SiteSetting.discourse_ai_enabled = true
23+
SiteSetting.ai_artifact_security = "strict"
24+
end
25+
26+
describe "#show" do
27+
it "returns 404 when discourse_ai is disabled" do
28+
SiteSetting.discourse_ai_enabled = false
29+
get "/discourse-ai/ai-bot/artifacts/#{artifact.id}"
30+
expect(response.status).to eq(404)
31+
end
32+
33+
it "returns 404 when ai_artifact_security disables it" do
34+
SiteSetting.ai_artifact_security = "disabled"
35+
get "/discourse-ai/ai-bot/artifacts/#{artifact.id}"
36+
expect(response.status).to eq(404)
37+
end
38+
39+
context "with private artifact" do
40+
it "returns 404 when user cannot see the post" do
41+
get "/discourse-ai/ai-bot/artifacts/#{artifact.id}"
42+
expect(response.status).to eq(404)
43+
end
44+
45+
it "shows artifact when user can see the post" do
46+
sign_in(user)
47+
get "/discourse-ai/ai-bot/artifacts/#{artifact.id}"
48+
expect(response.status).to eq(200)
49+
expect(response.body).to include(artifact.html)
50+
expect(response.body).to include(artifact.css)
51+
expect(response.body).to include(artifact.js)
52+
end
53+
end
54+
55+
context "with public artifact" do
56+
before { artifact.update!(metadata: { public: true }) }
57+
58+
it "shows artifact without authentication" do
59+
get "/discourse-ai/ai-bot/artifacts/#{artifact.id}"
60+
expect(response.status).to eq(200)
61+
expect(response.body).to include(artifact.html)
62+
end
63+
end
64+
65+
it "removes security headers" do
66+
sign_in(user)
67+
get "/discourse-ai/ai-bot/artifacts/#{artifact.id}"
68+
expect(response.headers["X-Frame-Options"]).to eq(nil)
69+
expect(response.headers["Content-Security-Policy"]).to eq("script-src 'unsafe-inline';")
70+
end
71+
end
72+
end

0 commit comments

Comments
 (0)