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

Commit 70d0c8d

Browse files
committed
FIX: properly bypass CSP for artifacts
1 parent 755b63f commit 70d0c8d

File tree

2 files changed

+71
-1
lines changed

2 files changed

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

0 commit comments

Comments
 (0)