0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-08 17:53:55 +08:00
discourse/spec/requests/admin/embeddable_hosts_controller_spec.rb
Juan David Martínez Cubillos 1dd0603581
FIX: Validate and safely handle malformed embed allowlist regexes (#42327)
## Summary

EmbeddableHost allowed_paths values are stored as regular expression
source and compiled during request handling without validation or error
handling. Now new or updated allowlists are validated on save, and
legacy invalid or timed-out patterns are safely skipped as non-matches
rather than causing HTTP 500 errors on public embed endpoints. This
prevents an administrator from accidentally breaking embed functionality
across their site.

## Source

- Patch Triage: `patch-triage/1556`

Co-authored-by: discourse-patch-triage
<272280883+discourse-patch-triage[bot]@users.noreply.github.com>
2026-08-05 12:54:43 -05:00

222 lines
6.4 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe Admin::EmbeddableHostsController do
fab!(:admin)
fab!(:moderator)
fab!(:user)
fab!(:embeddable_host)
describe "#create" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "logs embeddable host create" do
post "/admin/embeddable_hosts.json", params: { embeddable_host: { host: "test.com" } }
expect(response.status).to eq(200)
expect(
UserHistory.where(
acting_user_id: admin.id,
action: UserHistory.actions[:embeddable_host_create],
).exists?,
).to eq(true)
end
it "rejects invalid path allowlists" do
post "/admin/embeddable_hosts.json",
params: {
embeddable_host: {
host: "example.com",
allowed_paths: "[invalid",
},
}
expect(response.status).to eq(422)
expect(response.parsed_body["errors"]).to include(
"#{EmbeddableHost.human_attribute_name(:allowed_paths)} #{I18n.t("errors.messages.invalid")}",
)
expect(EmbeddableHost.where(host: "example.com")).not_to exist
end
it "creates an embeddable host with associated tags" do
tag1 = Fabricate(:tag)
tag2 = Fabricate(:tag)
post "/admin/embeddable_hosts.json",
params: {
embeddable_host: {
host: "example.com",
tags: [tag1.id, tag2.id],
},
}
expect(response.status).to eq(200)
expect(EmbeddableHost.last.tags).to contain_exactly(tag1, tag2)
end
it "updates an embeddable host with associated tags" do
tag1 = Fabricate(:tag)
tag2 = Fabricate(:tag)
put "/admin/embeddable_hosts/#{embeddable_host.id}.json",
params: {
embeddable_host: {
host: "updated-example.com",
tags: [tag1.id, tag2.id],
},
}
expect(response.status).to eq(200)
expect(EmbeddableHost.find(embeddable_host.id).tags).to contain_exactly(tag1, tag2)
end
it "creates an embeddable host with an associated author" do
user = Fabricate(:user, username: "johndoe")
post "/admin/embeddable_hosts.json",
params: {
embeddable_host: {
host: "example.com",
user: "johndoe",
},
}
expect(response.status).to eq(200)
expect(EmbeddableHost.last.user).to eq(user)
end
it "updates an embeddable host with a new author" do
new_user = Fabricate(:user, username: "johndoe")
put "/admin/embeddable_hosts/#{embeddable_host.id}.json",
params: {
embeddable_host: {
host: "updated-example.com",
user: "johndoe",
},
}
expect(response.status).to eq(200)
expect(EmbeddableHost.find(embeddable_host.id).user).to eq(new_user)
end
end
shared_examples "embeddable host creation not allowed" do
it "prevents embeddable host creation with a 404 response" do
post "/admin/embeddable_hosts.json", params: { embeddable_host: { host: "test.com" } }
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "embeddable host creation not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "embeddable host creation not allowed"
end
end
describe "#update" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "logs embeddable host update" do
category = Fabricate(:category)
put "/admin/embeddable_hosts/#{embeddable_host.id}.json",
params: {
embeddable_host: {
host: "test.com",
category_id: category.id,
},
}
expect(response.status).to eq(200)
history_exists =
UserHistory.where(
acting_user_id: admin.id,
action: UserHistory.actions[:embeddable_host_update],
new_value: "category_id: #{category.id}, host: test.com",
).exists?
expect(history_exists).to eq(true)
end
end
shared_examples "embeddable host update not allowed" do
it "prevents updates with a 404 response" do
category = Fabricate(:category)
put "/admin/embeddable_hosts/#{embeddable_host.id}.json",
params: {
embeddable_host: {
host: "test.com",
category_id: category.id,
},
}
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "embeddable host update not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "embeddable host update not allowed"
end
end
describe "#destroy" do
context "when logged in as an admin" do
before { sign_in(admin) }
it "logs embeddable host destroy" do
delete "/admin/embeddable_hosts/#{embeddable_host.id}.json", params: {}
expect(response.status).to eq(200)
expect(
UserHistory.where(
acting_user_id: admin.id,
action: UserHistory.actions[:embeddable_host_destroy],
).exists?,
).to eq(true)
end
end
shared_examples "embeddable host deletion not allowed" do
it "prevents deletion with a 404 response" do
delete "/admin/embeddable_hosts/#{embeddable_host.id}.json", params: {}
expect(response.status).to eq(404)
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
end
end
context "when logged in as a moderator" do
before { sign_in(moderator) }
include_examples "embeddable host deletion not allowed"
end
context "when logged in as a non-staff user" do
before { sign_in(user) }
include_examples "embeddable host deletion not allowed"
end
end
end