diff --git a/changelogs/unreleased/default-csp.yml b/changelogs/unreleased/default-csp.yml new file mode 100644 index 0000000000000000000000000000000000000000..5ae79627dd8ecf299405a37b3fed51be848178e4 --- /dev/null +++ b/changelogs/unreleased/default-csp.yml @@ -0,0 +1,5 @@ +--- +title: Enable Content-Security-Policy header by default +merge_request: 56923 +author: +type: other diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb index ff844645b11cdc1ae0c8e9964498e961221e55e7..6f6147f0f32659756fc15e94c50f45409555cf31 100644 --- a/lib/gitlab/content_security_policy/config_loader.rb +++ b/lib/gitlab/content_security_policy/config_loader.rb @@ -8,11 +8,33 @@ class ConfigLoader media_src object_src report_uri script_src style_src worker_src).freeze def self.default_settings_hash - { - 'enabled' => false, + settings_hash = { + 'enabled' => true, 'report_only' => false, - 'directives' => DIRECTIVES.each_with_object({}) { |directive, hash| hash[directive] = nil } + 'directives' => { + 'default_src' => "'self'", + 'base_uri' => "'self'", + 'child_src' => "'none'", + 'connect_src' => "'self'", + 'font_src' => "'self'", + 'form_action' => "'self' https: http:", + 'frame_ancestors' => "'self'", + 'frame_src' => "'self' https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com", + 'img_src' => "'self' data: blob: http: https:", + 'manifest_src' => "'self'", + 'media_src' => "'self'", + 'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.recaptcha.net https://apis.google.com", + 'style_src' => "'self' 'unsafe-inline'", + 'worker_src' => "'self'", + 'object_src' => "'none'", + 'report_uri' => nil + } } + + allow_webpack_dev_server(settings_hash) if Rails.env.development? + allow_cdn(settings_hash) if ENV['GITLAB_CDN_HOST'].present? + + settings_hash end def initialize(csp_directives) @@ -38,6 +60,26 @@ def arguments_for(directive) arguments.strip.split(' ').map(&:strip) end + + def self.allow_webpack_dev_server(settings_hash) + secure = Settings.webpack.dev_server['https'] + host_and_port = "#{Settings.webpack.dev_server['host']}:#{Settings.webpack.dev_server['port']}" + http_url = "#{secure ? 'https' : 'http'}://#{host_and_port}" + ws_url = "#{secure ? 'wss' : 'ws'}://#{host_and_port}" + + append_to_directive(settings_hash, 'connect_src', "#{http_url} #{ws_url}") + end + + def self.allow_cdn(settings_hash) + cdn_host = ENV['GITLAB_CDN_HOST'] + + append_to_directive(settings_hash, 'script_src', cdn_host) + append_to_directive(settings_hash, 'style_src', cdn_host) + end + + def self.append_to_directive(settings_hash, directive, text) + settings_hash['directives'][directive] = "#{settings_hash['directives'][directive]} #{text}".strip + end end end end diff --git a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb index a94fd6acd3218ed0efc66e28eff6c0a61790587c..41a6c06f9c928f3fcf21496431ad31b7d2065048 100644 --- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb +++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb @@ -20,15 +20,34 @@ end describe '.default_settings_hash' do - it 'returns empty defaults' do + it 'returns defaults for all keys' do settings = described_class.default_settings_hash - expect(settings['enabled']).to be_falsey + expect(settings['enabled']).to be_truthy expect(settings['report_only']).to be_falsey - described_class::DIRECTIVES.each do |directive| - expect(settings['directives'].has_key?(directive)).to be_truthy - expect(settings['directives'][directive]).to be_nil + directives = settings['directives'] + directive_names = (described_class::DIRECTIVES - ['report_uri']) + directive_names.each do |directive| + expect(directives.has_key?(directive)).to be_truthy + expect(directives[directive]).to be_truthy + end + + expect(directives.has_key?('report_uri')).to be_truthy + expect(directives['report_uri']).to be_nil + end + + context 'when GITLAB_CDN_HOST is set' do + before do + stub_env('GITLAB_CDN_HOST', 'https://example.com') + end + + it 'adds GITLAB_CDN_HOST to CSP' do + settings = described_class.default_settings_hash + directives = settings['directives'] + + expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.recaptcha.net https://apis.google.com https://example.com") + expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://example.com") end end end