debian-mirror-gitlab/lib/gitlab/content_security_policy/config_loader.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

169 lines
6.9 KiB
Ruby
Raw Normal View History

2019-10-12 21:52:04 +05:30
# frozen_string_literal: true
module Gitlab
module ContentSecurityPolicy
class ConfigLoader
DIRECTIVES = %w(base_uri child_src connect_src default_src font_src
form_action frame_ancestors frame_src img_src manifest_src
media_src object_src report_uri script_src style_src worker_src).freeze
2022-08-13 15:12:31 +05:30
DEFAULT_FALLBACK_VALUE = '<default_value>'
2021-10-27 15:23:28 +05:30
def self.default_enabled
Rails.env.development? || Rails.env.test?
end
def self.default_directives
directives = {
'default_src' => "'self'",
'base_uri' => "'self'",
2022-05-07 20:08:51 +05:30
'connect_src' => ContentSecurityPolicy::Directives.connect_src,
2021-10-27 15:23:28 +05:30
'font_src' => "'self'",
'form_action' => "'self' https: http:",
'frame_ancestors' => "'self'",
2021-12-11 22:18:48 +05:30
'frame_src' => ContentSecurityPolicy::Directives.frame_src,
2021-10-27 15:23:28 +05:30
'img_src' => "'self' data: blob: http: https:",
'manifest_src' => "'self'",
2022-06-21 17:19:12 +05:30
'media_src' => "'self' data:",
2021-12-11 22:18:48 +05:30
'script_src' => ContentSecurityPolicy::Directives.script_src,
2021-10-27 15:23:28 +05:30
'style_src' => "'self' 'unsafe-inline'",
2021-12-11 22:18:48 +05:30
'worker_src' => "#{Gitlab::Utils.append_path(Gitlab.config.gitlab.url, 'assets/')} blob: data:",
2021-10-27 15:23:28 +05:30
'object_src' => "'none'",
'report_uri' => nil
2019-10-12 21:52:04 +05:30
}
2021-06-08 01:23:25 +05:30
2021-11-18 22:05:49 +05:30
# connect_src with 'self' includes https/wss variations of the origin,
# however, safari hasn't covered this yet and we need to explicitly add
# support for websocket origins until Safari catches up with the specs
2021-12-11 22:18:48 +05:30
if Rails.env.development?
allow_webpack_dev_server(directives)
allow_letter_opener(directives)
2022-01-26 12:08:38 +05:30
allow_snowplow_micro(directives) if Gitlab::Tracking.snowplow_micro_enabled?
2021-12-11 22:18:48 +05:30
end
2021-11-18 22:05:49 +05:30
allow_websocket_connections(directives)
2021-10-27 15:23:28 +05:30
allow_cdn(directives, Settings.gitlab.cdn_host) if Settings.gitlab.cdn_host.present?
allow_sentry(directives) if Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn
2021-12-11 22:18:48 +05:30
allow_framed_gitlab_paths(directives)
2022-06-21 17:19:12 +05:30
allow_customersdot(directives) if ENV['CUSTOMER_PORTAL_URL'].present?
2022-07-23 23:45:48 +05:30
allow_review_apps(directives) if ENV['REVIEW_APPS_ENABLED']
2021-12-11 22:18:48 +05:30
# The follow section contains workarounds to patch Safari's lack of support for CSP Level 3
# See https://gitlab.com/gitlab-org/gitlab/-/issues/343579
# frame-src was deprecated in CSP level 2 in favor of child-src
# CSP level 3 "undeprecated" frame-src and browsers fall back on child-src if it's missing
# However Safari seems to read child-src first so we'll just keep both equal
append_to_directive(directives, 'child_src', directives['frame_src'])
# Safari also doesn't support worker-src and only checks child-src
# So for compatibility until it catches up to other browsers we need to
# append worker-src's content to child-src
append_to_directive(directives, 'child_src', directives['worker_src'])
2021-06-08 01:23:25 +05:30
2021-10-27 15:23:28 +05:30
directives
2019-10-12 21:52:04 +05:30
end
def initialize(csp_directives)
2022-08-13 15:12:31 +05:30
# Using <default_value> falls back to the default values.
directives = csp_directives.reject { |_, value| value == DEFAULT_FALLBACK_VALUE }
2022-07-16 23:28:13 +05:30
@merged_csp_directives =
2022-08-13 15:12:31 +05:30
HashWithIndifferentAccess.new(directives)
2022-07-16 23:28:13 +05:30
.reverse_merge(::Gitlab::ContentSecurityPolicy::ConfigLoader.default_directives)
2019-10-12 21:52:04 +05:30
end
def load(policy)
DIRECTIVES.each do |directive|
arguments = arguments_for(directive)
next unless arguments.present?
policy.public_send(directive, *arguments) # rubocop:disable GitlabSecurity/PublicSend
end
end
private
def arguments_for(directive)
2022-07-16 23:28:13 +05:30
# In order to disable a directive, the user can explicitly
# set a falsy value like nil, false or empty string
arguments = @merged_csp_directives[directive]
2019-10-12 21:52:04 +05:30
return unless arguments.present? && arguments.is_a?(String)
arguments.strip.split(' ').map(&:strip)
end
2021-06-08 01:23:25 +05:30
2021-11-18 22:05:49 +05:30
def self.allow_websocket_connections(directives)
http_ports = [80, 443]
host = Gitlab.config.gitlab.host
port = Gitlab.config.gitlab.port
secure = Gitlab.config.gitlab.https
protocol = secure ? 'wss' : 'ws'
ws_url = "#{protocol}://#{host}"
unless http_ports.include?(port)
ws_url = "#{ws_url}:#{port}"
end
append_to_directive(directives, 'connect_src', ws_url)
end
2021-10-27 15:23:28 +05:30
def self.allow_webpack_dev_server(directives)
2021-06-08 01:23:25 +05:30
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}"
2021-10-27 15:23:28 +05:30
append_to_directive(directives, 'connect_src', "#{http_url} #{ws_url}")
2021-06-08 01:23:25 +05:30
end
2021-10-27 15:23:28 +05:30
def self.allow_cdn(directives, cdn_host)
append_to_directive(directives, 'script_src', cdn_host)
append_to_directive(directives, 'style_src', cdn_host)
append_to_directive(directives, 'font_src', cdn_host)
2021-12-11 22:18:48 +05:30
append_to_directive(directives, 'worker_src', cdn_host)
append_to_directive(directives, 'frame_src', cdn_host)
2021-06-08 01:23:25 +05:30
end
2021-10-27 15:23:28 +05:30
def self.append_to_directive(directives, directive, text)
directives[directive] = "#{directives[directive]} #{text}".strip
2021-06-08 01:23:25 +05:30
end
2021-09-30 23:02:18 +05:30
2021-10-27 15:23:28 +05:30
def self.allow_customersdot(directives)
2021-09-30 23:02:18 +05:30
customersdot_host = ENV['CUSTOMER_PORTAL_URL']
2021-10-27 15:23:28 +05:30
append_to_directive(directives, 'frame_src', customersdot_host)
end
def self.allow_sentry(directives)
sentry_dsn = Gitlab.config.sentry.clientside_dsn
sentry_uri = URI(sentry_dsn)
2022-08-13 15:12:31 +05:30
append_to_directive(directives, 'connect_src', "#{sentry_uri.scheme}://#{sentry_uri.host}")
2021-09-30 23:02:18 +05:30
end
2021-12-11 22:18:48 +05:30
def self.allow_letter_opener(directives)
append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/rails/letter_opener/'))
end
2022-01-26 12:08:38 +05:30
def self.allow_snowplow_micro(directives)
url = URI.join(Gitlab::Tracking::Destinations::SnowplowMicro.new.uri, '/').to_s
append_to_directive(directives, 'connect_src', url)
end
2021-12-11 22:18:48 +05:30
# Using 'self' in the CSP introduces several CSP bypass opportunities
# for this reason we list the URLs where GitLab frames itself instead
def self.allow_framed_gitlab_paths(directives)
2022-11-25 23:54:43 +05:30
['/admin/', '/assets/', '/-/speedscope/index.html', '/-/sandbox/'].map do |path|
2021-12-11 22:18:48 +05:30
append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, path))
end
end
2022-07-23 23:45:48 +05:30
def self.allow_review_apps(directives)
# Allow-listed to allow POSTs to https://gitlab.com/api/v4/projects/278964/merge_requests/:merge_request_iid/visual_review_discussions
append_to_directive(directives, 'connect_src', 'https://gitlab.com/api/v4/projects/278964/merge_requests/')
end
2019-10-12 21:52:04 +05:30
end
end
end