Class: Banzai::Filter::AssetProxyFilter

Inherits:
HTML::Pipeline::Filter
  • Object
show all
Includes:
Concerns::AssetProxying, Concerns::PipelineTimingCheck
Defined in:
lib/banzai/filter/asset_proxy_filter.rb

Overview

Proxies images/assets through another server. Reduces mixed content warnings as well as hiding the customer’s IP address when requesting images. Copies the original img src to data-canonical-src and replaces the src with a new url to the proxy server.

See docs.gitlab.com/security/asset_proxy/ for more information.

Based on github.com/gjtorikian/html-pipeline/blob/v2.14.3/lib/html/pipeline/camo_filter.rb

Constant Summary

Constants included from Concerns::PipelineTimingCheck

Concerns::PipelineTimingCheck::MAX_PIPELINE_SECONDS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Concerns::AssetProxying

#asset_proxy_enabled?, #asset_proxy_url, #can_skip_asset_proxy_for_url?, #validate_asset_proxying

Methods included from Concerns::PipelineTimingCheck

#exceeded_pipeline_max?

Constructor Details

#initialize(text, context = nil, result = nil) ⇒ AssetProxyFilter

Returns a new instance of AssetProxyFilter.



17
18
19
# File 'lib/banzai/filter/asset_proxy_filter.rb', line 17

def initialize(text, context = nil, result = nil)
  super
end

Class Method Details

.csp_for_allowlist(allowlist) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/banzai/filter/asset_proxy_filter.rb', line 80

def self.csp_for_allowlist(allowlist)
  return unless Gitlab.config.asset_proxy.enabled

  # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#host-source.

  # Permit assets on the GitLab host itself.
  src = [:self]

  # We need to permit the asset proxy URL itself for it to work in the Mermaid sandbox.
  # The setting is already validated to be a valid URL; we need to ensure it ends in a
  # forward-slash for the CSP to ensure we permit the entire prefix.
  asset_proxy_url = Gitlab.config.asset_proxy.url
  asset_proxy_url += '/' unless asset_proxy_url.ends_with?('/')
  src << asset_proxy_url

  # We use HTTP (and not HTTPS) as the scheme as administrators may allowlist a host with the expectation that
  # they can use resources from it over HTTP, such as in an intranet.  Allowing http://... in a CSP also
  # explicitly permits HTTPS for the same directive. ("When matching schemes, secure upgrades are allowed.")
  allowlist.each do |host|
    src << "http://#{host}:*"
  end

  src
end

.determine_allowlist(application_settings) ⇒ Object



105
106
107
108
109
# File 'lib/banzai/filter/asset_proxy_filter.rb', line 105

def self.determine_allowlist(application_settings)
  application_settings.try(:asset_proxy_allowlist).presence ||
    application_settings.try(:asset_proxy_whitelist).presence ||
    [Gitlab.config.gitlab.host]
end

.host_regexp_for_allowlist(allowlist) ⇒ Object



73
74
75
76
77
78
# File 'lib/banzai/filter/asset_proxy_filter.rb', line 73

def self.host_regexp_for_allowlist(allowlist)
  return if allowlist.empty?

  escaped = allowlist.map { |domain| Regexp.escape(domain).gsub('\*', '.*?') }
  Regexp.new("^(#{escaped.join('|')})$", Regexp::IGNORECASE)
end

.initialize_settingsObject

called during an initializer. Caching the values in Gitlab.config significantly increased performance, rather than querying Gitlab::CurrentSettings.current_application_settings over and over. However, this does mean that the Rails servers need to get restarted whenever the application settings are changed



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/banzai/filter/asset_proxy_filter.rb', line 57

def self.initialize_settings
  application_settings           = Gitlab::CurrentSettings.current_application_settings
  Gitlab.config['asset_proxy'] ||= GitlabSettings::Options.build({})

  if application_settings.respond_to?(:asset_proxy_enabled)
    Gitlab.config.asset_proxy['enabled']        = application_settings.asset_proxy_enabled
    Gitlab.config.asset_proxy['url']            = application_settings.asset_proxy_url
    Gitlab.config.asset_proxy['secret_key']     = application_settings.asset_proxy_secret_key
    Gitlab.config.asset_proxy['allowlist']      = determine_allowlist(application_settings)
    Gitlab.config.asset_proxy['domain_regexp']  = host_regexp_for_allowlist(Gitlab.config.asset_proxy.allowlist)
    Gitlab.config.asset_proxy['csp_directives'] = csp_for_allowlist(Gitlab.config.asset_proxy.allowlist)
  else
    Gitlab.config.asset_proxy['enabled'] = ::ApplicationSetting.defaults[:asset_proxy_enabled]
  end
end

.transform_context(context) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/banzai/filter/asset_proxy_filter.rb', line 40

def self.transform_context(context)
  context[:disable_asset_proxy] = !Gitlab.config.asset_proxy.enabled

  unless context[:disable_asset_proxy]
    context[:asset_proxy_enabled]       = !context[:disable_asset_proxy]
    context[:asset_proxy]               = Gitlab.config.asset_proxy.url
    context[:asset_proxy_secret_key]    = Gitlab.config.asset_proxy.secret_key
    context[:asset_proxy_domain_regexp] = Gitlab.config.asset_proxy.domain_regexp
  end

  context
end

Instance Method Details

#callObject



21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/banzai/filter/asset_proxy_filter.rb', line 21

def call
  return doc unless asset_proxy_enabled?

  doc.search('img').each do |element|
    original_src = element['src']
    next unless original_src

    next if can_skip_asset_proxy_for_url?(original_src)

    element['src'] = asset_proxy_url(original_src)
    element['data-canonical-src'] = original_src
  end
  doc
end

#validateObject



36
37
38
# File 'lib/banzai/filter/asset_proxy_filter.rb', line 36

def validate
  validate_asset_proxying
end