Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/content_security_policy/config_loader.rb')
-rw-r--r--lib/gitlab/content_security_policy/config_loader.rb309
1 files changed, 164 insertions, 145 deletions
diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb
index 8d1fcf4f916..9fb3c7d362f 100644
--- a/lib/gitlab/content_security_policy/config_loader.rb
+++ b/lib/gitlab/content_security_policy/config_loader.rb
@@ -11,194 +11,213 @@ module Gitlab
DEFAULT_FALLBACK_VALUE = '<default_value>'
HTTP_PORTS = [80, 443].freeze
- def self.default_enabled
- Rails.env.development? || Rails.env.test?
- end
+ class << self
+ def default_enabled
+ Rails.env.development? || Rails.env.test?
+ end
- def self.default_directives
- directives = default_directives_defaults
+ def default_directives
+ directives = default_directives_defaults
+
+ allow_development_tooling(directives)
+ allow_websocket_connections(directives)
+ allow_lfs(directives)
+ allow_cdn(directives)
+ allow_zuora(directives)
+ allow_sentry(directives)
+ allow_framed_gitlab_paths(directives)
+ allow_customersdot(directives)
+ allow_review_apps(directives)
+ csp_level_3_backport(directives)
+
+ directives
+ end
- allow_development_tooling(directives)
- allow_websocket_connections(directives)
- allow_cdn(directives)
- allow_zuora(directives)
- allow_sentry(directives)
- allow_framed_gitlab_paths(directives)
- allow_customersdot(directives)
- allow_review_apps(directives)
- csp_level_3_backport(directives)
+ def default_directives_defaults
+ {
+ 'default_src' => "'self'",
+ 'base_uri' => "'self'",
+ 'connect_src' => ContentSecurityPolicy::Directives.connect_src,
+ 'font_src' => "'self'",
+ 'form_action' => "'self' https: http:",
+ 'frame_ancestors' => "'self'",
+ 'frame_src' => ContentSecurityPolicy::Directives.frame_src,
+ 'img_src' => "'self' data: blob: http: https:",
+ 'manifest_src' => "'self'",
+ 'media_src' => "'self' data: blob: http: https:",
+ 'script_src' => ContentSecurityPolicy::Directives.script_src,
+ 'style_src' => ContentSecurityPolicy::Directives.style_src,
+ 'worker_src' => "#{Gitlab::Utils.append_path(Gitlab.config.gitlab.url, 'assets/')} blob: data:",
+ 'object_src' => "'none'",
+ 'report_uri' => nil
+ }
+ end
- directives
- end
+ # 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
+ def allow_development_tooling(directives)
+ return unless Rails.env.development?
- def initialize(csp_directives)
- # Using <default_value> falls back to the default values.
- directives = csp_directives.reject { |_, value| value == DEFAULT_FALLBACK_VALUE }
- @merged_csp_directives =
- HashWithIndifferentAccess.new(directives)
- .reverse_merge(::Gitlab::ContentSecurityPolicy::ConfigLoader.default_directives)
- end
+ allow_webpack_dev_server(directives)
+ allow_letter_opener(directives)
+ allow_snowplow_micro(directives) if Gitlab::Tracking.snowplow_micro_enabled?
+ end
- def load(policy)
- DIRECTIVES.each do |directive|
- arguments = arguments_for(directive)
+ def allow_webpack_dev_server(directives)
+ 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}"
- next unless arguments.present?
+ append_to_directive(directives, 'connect_src', "#{http_url} #{ws_url}")
+ end
- policy.public_send(directive, *arguments) # rubocop:disable GitlabSecurity/PublicSend
+ def allow_letter_opener(directives)
+ url = Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/rails/letter_opener/')
+ append_to_directive(directives, 'frame_src', url)
end
- end
- private
+ def allow_snowplow_micro(directives)
+ url = URI.join(Gitlab::Tracking::Destinations::SnowplowMicro.new.uri, '/').to_s
+ append_to_directive(directives, 'connect_src', url)
+ end
- def self.default_directives_defaults
- {
- 'default_src' => "'self'",
- 'base_uri' => "'self'",
- 'connect_src' => ContentSecurityPolicy::Directives.connect_src,
- 'font_src' => "'self'",
- 'form_action' => "'self' https: http:",
- 'frame_ancestors' => "'self'",
- 'frame_src' => ContentSecurityPolicy::Directives.frame_src,
- 'img_src' => "'self' data: blob: http: https:",
- 'manifest_src' => "'self'",
- 'media_src' => "'self' data: blob: http: https:",
- 'script_src' => ContentSecurityPolicy::Directives.script_src,
- 'style_src' => ContentSecurityPolicy::Directives.style_src,
- 'worker_src' => "#{Gitlab::Utils.append_path(Gitlab.config.gitlab.url, 'assets/')} blob: data:",
- 'object_src' => "'none'",
- 'report_uri' => nil
- }
- end
+ def allow_lfs(directives)
+ return unless Gitlab.config.lfs.enabled && LfsObjectUploader.direct_download_enabled?
- # 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
- def self.allow_development_tooling(directives)
- return unless Rails.env.development?
+ lfs_url = build_lfs_url
+ return unless lfs_url.present?
- allow_webpack_dev_server(directives)
- allow_letter_opener(directives)
- allow_snowplow_micro(directives) if Gitlab::Tracking.snowplow_micro_enabled?
- end
+ append_to_directive(directives, 'connect_src', lfs_url)
+ end
- def self.allow_webpack_dev_server(directives)
- 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}"
+ def allow_websocket_connections(directives)
+ host = Gitlab.config.gitlab.host
+ port = Gitlab.config.gitlab.port
+ secure = Gitlab.config.gitlab.https
+ protocol = secure ? 'wss' : 'ws'
- append_to_directive(directives, 'connect_src', "#{http_url} #{ws_url}")
- end
+ ws_url = "#{protocol}://#{host}"
+ ws_url = "#{ws_url}:#{port}" unless HTTP_PORTS.include?(port)
- def self.allow_letter_opener(directives)
- url = Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/rails/letter_opener/')
- append_to_directive(directives, 'frame_src', url)
- end
+ append_to_directive(directives, 'connect_src', ws_url)
+ end
- 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
+ def allow_cdn(directives)
+ cdn_host = Settings.gitlab.cdn_host.presence
+ return unless cdn_host
- def self.allow_websocket_connections(directives)
- host = Gitlab.config.gitlab.host
- port = Gitlab.config.gitlab.port
- secure = Gitlab.config.gitlab.https
- protocol = secure ? 'wss' : 'ws'
+ append_to_directive(directives, 'script_src', cdn_host)
+ append_to_directive(directives, 'style_src', cdn_host)
+ append_to_directive(directives, 'font_src', cdn_host)
+ append_to_directive(directives, 'worker_src', cdn_host)
+ append_to_directive(directives, 'frame_src', cdn_host)
+ end
- ws_url = "#{protocol}://#{host}"
- ws_url = "#{ws_url}:#{port}" unless HTTP_PORTS.include?(port)
+ def allow_zuora(directives)
+ return unless Gitlab.com?
- append_to_directive(directives, 'connect_src', ws_url)
- end
+ append_to_directive(directives, 'frame_src', zuora_host)
+ end
- def self.allow_cdn(directives)
- cdn_host = Settings.gitlab.cdn_host.presence
- return unless cdn_host
+ def allow_sentry(directives)
+ allow_legacy_sentry(directives) if legacy_sentry_configured?
+ return unless sentry_client_side_dsn_enabled?
- append_to_directive(directives, 'script_src', cdn_host)
- append_to_directive(directives, 'style_src', cdn_host)
- append_to_directive(directives, 'font_src', cdn_host)
- append_to_directive(directives, 'worker_src', cdn_host)
- append_to_directive(directives, 'frame_src', cdn_host)
- end
+ sentry_uri = URI(Gitlab::CurrentSettings.sentry_clientside_dsn)
- def self.allow_zuora(directives)
- return unless Gitlab.com?
+ append_to_directive(directives, 'connect_src', "#{sentry_uri.scheme}://#{sentry_uri.host}")
+ end
- append_to_directive(directives, 'frame_src', zuora_host)
- end
+ def allow_legacy_sentry(directives)
+ # Support for Sentry setup via configuration files will be removed in 16.0
+ # in favor of Gitlab::CurrentSettings.
+ sentry_uri = URI(Gitlab.config.sentry.clientside_dsn)
- def self.allow_sentry(directives)
- allow_legacy_sentry(directives) if legacy_sentry_configured?
- return unless sentry_client_side_dsn_enabled?
+ append_to_directive(directives, 'connect_src', "#{sentry_uri.scheme}://#{sentry_uri.host}")
+ end
- sentry_uri = URI(Gitlab::CurrentSettings.sentry_clientside_dsn)
+ def legacy_sentry_configured?
+ Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn
+ end
- append_to_directive(directives, 'connect_src', "#{sentry_uri.scheme}://#{sentry_uri.host}")
- end
+ def sentry_client_side_dsn_enabled?
+ Gitlab::CurrentSettings.try(:sentry_enabled) && Gitlab::CurrentSettings.try(:sentry_clientside_dsn)
+ end
- def self.allow_legacy_sentry(directives)
- # Support for Sentry setup via configuration files will be removed in 16.0
- # in favor of Gitlab::CurrentSettings.
- sentry_uri = URI(Gitlab.config.sentry.clientside_dsn)
+ # Using 'self' in the CSP introduces several CSP bypass opportunities
+ # for this reason we list the URLs where GitLab frames itself instead
+ def allow_framed_gitlab_paths(directives)
+ ['/admin/', '/assets/', '/-/speedscope/index.html', '/-/sandbox/'].map do |path|
+ append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, path))
+ end
+ end
- append_to_directive(directives, 'connect_src', "#{sentry_uri.scheme}://#{sentry_uri.host}")
- end
+ def allow_customersdot(directives)
+ customersdot_host = ENV['CUSTOMER_PORTAL_URL'].presence
+ return unless customersdot_host
- def self.legacy_sentry_configured?
- Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn
- end
+ append_to_directive(directives, 'frame_src', customersdot_host)
+ end
- def self.sentry_client_side_dsn_enabled?
- Gitlab::CurrentSettings.try(:sentry_enabled) && Gitlab::CurrentSettings.try(:sentry_clientside_dsn)
- end
+ def allow_review_apps(directives)
+ return unless ENV['REVIEW_APPS_ENABLED'].presence
- # 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)
- ['/admin/', '/assets/', '/-/speedscope/index.html', '/-/sandbox/'].map do |path|
- append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, path))
+ # 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
- end
- def self.allow_customersdot(directives)
- customersdot_host = ENV['CUSTOMER_PORTAL_URL'].presence
- return unless customersdot_host
+ # The follow contains workarounds to patch Safari's lack of support for CSP Level 3
+ def csp_level_3_backport(directives)
+ # 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'])
+ end
- append_to_directive(directives, 'frame_src', customersdot_host)
- end
+ def append_to_directive(directives, directive, text)
+ directives[directive] = "#{directives[directive]} #{text}".strip
+ end
- def self.allow_review_apps(directives)
- return unless ENV['REVIEW_APPS_ENABLED'].presence
+ def zuora_host
+ "https://*.zuora.com/apps/PublicHostedPageLite.do"
+ end
- # 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/')
+ def build_lfs_url
+ uploader = LfsObjectUploader.new(nil)
+ fog = CarrierWave::Storage::Fog.new(uploader)
+ fog_file = CarrierWave::Storage::Fog::File.new(uploader, fog, nil)
+ fog_file.public_url || fog_file.url
+ end
end
- # The follow contains workarounds to patch Safari's lack of support for CSP Level 3
- def self.csp_level_3_backport(directives)
- # 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'])
+ def initialize(csp_directives)
+ # Using <default_value> falls back to the default values.
+ @merged_csp_directives = csp_directives
+ .reject { |_, value| value == DEFAULT_FALLBACK_VALUE }
+ .with_indifferent_access
+ .reverse_merge(ConfigLoader.default_directives)
end
- def self.append_to_directive(directives, directive, text)
- directives[directive] = "#{directives[directive]} #{text}".strip
- end
+ def load(policy)
+ DIRECTIVES.each do |directive|
+ arguments = arguments_for(directive)
+
+ next unless arguments.present?
- def self.zuora_host
- "https://*.zuora.com/apps/PublicHostedPageLite.do"
+ policy.public_send(directive, *arguments) # rubocop:disable GitlabSecurity/PublicSend
+ end
end
+ private
+
def arguments_for(directive)
# In order to disable a directive, the user can explicitly
# set a falsy value like nil, false or empty string