mirror of
https://github.com/discourse/discourse.git
synced 2025-02-24 18:57:14 +08:00

[Security
patch](5558e72f22
)
(for this [CVE](https://nvd.nist.gov/vuln/detail/CVE-2024-54133)) from
rails actionpack was backported from [Rails
8.0.0.1](https://github.com/rails/rails/blob/v8.0.1/actionpack/CHANGELOG.md#rails-8001-december-10-2024)
to previous stable versions including `7-1-stable` / `7-2-stable`.
Any previous version of Discourse upgrading to v3.4.0.beta3 and above
would have observed their sites crashing if they had invalid sources in
their CSP directive extensions.
This fix removes such invalid sources during our build of the CSP, and
logs these at a warning level so devs are able to find out why their CSP
sources were filtered out of the extendable directives.
147 lines
4.2 KiB
Ruby
147 lines
4.2 KiB
Ruby
# frozen_string_literal: true
|
|
RSpec.describe ContentSecurityPolicy do
|
|
after { DiscoursePluginRegistry.reset! }
|
|
|
|
describe "report-uri" do
|
|
it "is enabled by SiteSetting" do
|
|
SiteSetting.content_security_policy_collect_reports = true
|
|
report_uri = parse(policy)["report-uri"].first
|
|
expect(report_uri).to eq("http://test.localhost/csp_reports")
|
|
|
|
SiteSetting.content_security_policy_collect_reports = false
|
|
report_uri = parse(policy)["report-uri"]
|
|
expect(report_uri).to eq(nil)
|
|
end
|
|
end
|
|
|
|
describe "base-uri" do
|
|
it "is set to self" do
|
|
base_uri = parse(policy)["base-uri"]
|
|
expect(base_uri).to eq(["'self'"])
|
|
end
|
|
end
|
|
|
|
describe "object-src" do
|
|
it "is set to none" do
|
|
object_srcs = parse(policy)["object-src"]
|
|
expect(object_srcs).to eq(["'none'"])
|
|
end
|
|
end
|
|
|
|
describe "upgrade-insecure-requests" do
|
|
it "is not included when force_https is off" do
|
|
SiteSetting.force_https = false
|
|
expect(parse(policy)["upgrade-insecure-requests"]).to eq(nil)
|
|
end
|
|
|
|
it "is included when force_https is on" do
|
|
SiteSetting.force_https = true
|
|
expect(parse(policy)["upgrade-insecure-requests"]).to eq([])
|
|
end
|
|
end
|
|
|
|
describe "strict-dynamic script-src and worker-src" do
|
|
it "includes strict-dynamic keyword" do
|
|
script_srcs = parse(policy)["script-src"]
|
|
expect(script_srcs).to include("'strict-dynamic'")
|
|
end
|
|
|
|
it "does not set worker-src" do
|
|
worker_src = parse(policy)["worker-src"]
|
|
expect(worker_src).to eq(nil)
|
|
end
|
|
|
|
it 'includes "report-sample" when report collection is enabled' do
|
|
SiteSetting.content_security_policy_collect_reports = true
|
|
script_srcs = parse(policy)["script-src"]
|
|
expect(script_srcs).to include("'report-sample'")
|
|
end
|
|
end
|
|
|
|
describe "manifest-src" do
|
|
it "is set to self" do
|
|
expect(parse(policy)["manifest-src"]).to eq(["'self'"])
|
|
end
|
|
end
|
|
|
|
describe "frame-ancestors" do
|
|
context "with content_security_policy_frame_ancestors enabled" do
|
|
before do
|
|
SiteSetting.content_security_policy_frame_ancestors = true
|
|
Fabricate(:embeddable_host, host: "https://a.org")
|
|
Fabricate(:embeddable_host, host: "https://b.org")
|
|
end
|
|
|
|
it "always has self" do
|
|
frame_ancestors = parse(policy)["frame-ancestors"]
|
|
expect(frame_ancestors).to include("'self'")
|
|
end
|
|
|
|
it "includes all EmbeddableHost" do
|
|
frame_ancestors = parse(policy)["frame-ancestors"]
|
|
expect(frame_ancestors).to include("https://a.org")
|
|
expect(frame_ancestors).to include("https://b.org")
|
|
end
|
|
end
|
|
|
|
context "with content_security_policy_frame_ancestors disabled" do
|
|
before { SiteSetting.content_security_policy_frame_ancestors = false }
|
|
|
|
it "does not set frame-ancestors" do
|
|
frame_ancestors = parse(policy)["frame-ancestors"]
|
|
expect(frame_ancestors).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with a plugin" do
|
|
let(:plugin_class) do
|
|
Class.new(Plugin::Instance) do
|
|
attr_accessor :enabled
|
|
|
|
def enabled?
|
|
@enabled
|
|
end
|
|
end
|
|
end
|
|
|
|
it "can extend frame_ancestors" do
|
|
SiteSetting.content_security_policy_frame_ancestors = true
|
|
plugin = plugin_class.new(nil, "#{Rails.root}/spec/fixtures/plugins/csp_extension/plugin.rb")
|
|
|
|
plugin.activate!
|
|
Discourse.plugins << plugin
|
|
|
|
plugin.enabled = true
|
|
expect(parse(policy)["frame-ancestors"]).to include("'self'")
|
|
expect(parse(policy)["frame-ancestors"]).to include("https://frame-ancestors-plugin.ext")
|
|
|
|
plugin.enabled = false
|
|
expect(parse(policy)["frame-ancestors"]).to_not include("https://frame-ancestors-plugin.ext")
|
|
|
|
Discourse.plugins.delete plugin
|
|
DiscoursePluginRegistry.reset!
|
|
end
|
|
end
|
|
|
|
it "can be extended by site setting" do
|
|
SiteSetting.content_security_policy_script_src = "'unsafe-eval'"
|
|
|
|
expect(parse(policy)["script-src"]).to include("'unsafe-eval'")
|
|
end
|
|
|
|
def parse(csp_string)
|
|
csp_string
|
|
.split(";")
|
|
.map do |policy|
|
|
directive, *sources = policy.split
|
|
[directive, sources]
|
|
end
|
|
.to_h
|
|
end
|
|
|
|
def policy(theme_id = nil, path_info: "/")
|
|
ContentSecurityPolicy.policy(theme_id, path_info: path_info)
|
|
end
|
|
end
|