Class: SecureHeaders::ContentSecurityPolicy

Inherits:
Header
  • Object
show all
Includes:
Constants
Defined in:
lib/secure_headers/headers/content_security_policy.rb,
lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb

Defined Under Namespace

Modules: Constants Classes: ScriptHashMiddleware

Constant Summary

Constants included from Constants

Constants::ALL_DIRECTIVES, Constants::CHROME_DIRECTIVES, Constants::CONFIG_KEY, Constants::DEFAULT_CSP_HEADER, Constants::DIRECTIVES_1_0, Constants::DIRECTIVES_2_0, Constants::DIRECTIVES_3_0, Constants::DIRECTIVES_DRAFT, Constants::ENV_KEY, Constants::FIREFOX_DIRECTIVES, Constants::FIREFOX_UNSUPPORTED_DIRECTIVES, Constants::HEADER_NAME, Constants::SAFARI_DIRECTIVES, Constants::USER_AGENT_PARSER

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = nil, options = {}) ⇒ ContentSecurityPolicy

options param contains :controller used for setting instance variables for nonces/hashes :ssl_request used to determine if http_additions should be used :ua the user agent (or just use Firefox/Chrome/MSIE/etc)

:report used to determine what :ssl_request, :ua, and :request_uri are set to



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/secure_headers/headers/content_security_policy.rb', line 126

def initialize(config=nil, options={})
  return unless config

  if options[:request]
    options = options.merge(self.class.options_from_request(options[:request]))
  end

  @controller = options[:controller]
  @ua = options[:ua]
  @ssl_request = !!options.delete(:ssl)
  @request_uri = options.delete(:request_uri)

  # Config values can be string, array, or lamdba values
  @config = config.inject({}) do |hash, (key, value)|
    config_val = value.respond_to?(:call) ? value.call(@controller) : value
    if ALL_DIRECTIVES.include?(key.to_sym) # directives need to be normalized to arrays of strings
      config_val = config_val.split if config_val.is_a? String
      if config_val.is_a?(Array)
        config_val = config_val.map do |val|
          translate_dir_value(val)
        end.flatten.uniq
      end
    end

    hash[key] = config_val
    hash
  end

  @http_additions = @config.delete(:http_additions)
  @disable_img_src_data_uri = !!@config.delete(:disable_img_src_data_uri)
  @tag_report_uri = !!@config.delete(:tag_report_uri)
  @script_hashes = @config.delete(:script_hashes) || []
  @app_name = @config.delete(:app_name)
  @app_name = @app_name.call(@controller) if @app_name.respond_to?(:call)
  @enforce = @config.delete(:enforce)
  @enforce = @enforce.call(@controller) if @enforce.respond_to?(:call)
  @enforce = !!@enforce

  # normalize and tag the report-uri
  if @config[:report_uri]
    @config[:report_uri] = @config[:report_uri].map do |report_uri|
      if report_uri.start_with?('//')
        report_uri = if @ssl_request
                       "https:" + report_uri
                     else
                       "http:" + report_uri
                     end
      end

      if @tag_report_uri
        report_uri = "#{report_uri}?enforce=#{@enforce}"
        report_uri += "&app_name=#{@app_name}" if @app_name
      end
      report_uri
    end
  end

  add_script_hashes if @script_hashes.any?
  strip_unsupported_directives
end

Instance Attribute Details

#ssl_requestObject (readonly) Also known as: ssl_request?

Returns the value of attribute ssl_request.



76
77
78
# File 'lib/secure_headers/headers/content_security_policy.rb', line 76

def ssl_request
  @ssl_request
end

Class Method Details

.add_to_env(request, controller, config) ⇒ Object



88
89
90
91
92
93
94
95
# File 'lib/secure_headers/headers/content_security_policy.rb', line 88

def add_to_env(request, controller, config)
  set_nonce(controller)
  options = options_from_request(request).merge(:controller => controller)
  request.env[Constants::ENV_KEY] = {
    :config => config,
    :options => options,
  }
end

.from_json(*json_configs) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
# File 'lib/secure_headers/headers/content_security_policy.rb', line 227

def self.from_json(*json_configs)
  json_configs.inject({}) do |combined_config, one_config|
    config = JSON.parse(one_config).inject({}) do |hash, (key, value)|
      hash[key.gsub(/(\w+)-(\w+)/, "\\1_\\2").to_sym] = value
      hash
    end
    combined_config.merge(config) do |_, lhs, rhs|
      lhs | rhs
    end
  end
end

.generate_nonceObject



80
81
82
# File 'lib/secure_headers/headers/content_security_policy.rb', line 80

def generate_nonce
  SecureRandom.base64(32).chomp
end

.options_from_request(request) ⇒ Object



97
98
99
100
101
102
103
# File 'lib/secure_headers/headers/content_security_policy.rb', line 97

def options_from_request(request)
  {
    :ssl => request.ssl?,
    :ua => request.env['HTTP_USER_AGENT'],
    :request_uri => request_uri_from_request(request),
  }
end

.request_uri_from_request(request) ⇒ Object



105
106
107
108
109
110
111
112
113
# File 'lib/secure_headers/headers/content_security_policy.rb', line 105

def request_uri_from_request(request)
  if request.respond_to?(:original_url)
    # rails 3.1+
    request.original_url
  else
    # rails 2/3.0
    request.url
  end
end

.set_nonce(controller, nonce = generate_nonce) ⇒ Object



84
85
86
# File 'lib/secure_headers/headers/content_security_policy.rb', line 84

def set_nonce(controller, nonce = generate_nonce)
  controller.instance_variable_set(:@content_security_policy_nonce, nonce)
end

.symbol_to_hyphen_case(sym) ⇒ Object



115
116
117
# File 'lib/secure_headers/headers/content_security_policy.rb', line 115

def symbol_to_hyphen_case sym
  sym.to_s.gsub('_', '-')
end

Instance Method Details

#nameObject

Returns the name to use for the header. Either “Content-Security-Policy” or “Content-Security-Policy-Report-Only”



198
199
200
201
202
203
204
# File 'lib/secure_headers/headers/content_security_policy.rb', line 198

def name
  base = HEADER_NAME
  if !@enforce
    base += "-Report-Only"
  end
  base
end

#nonceObject

Return or initialize the nonce value used for this header. If a reference to a controller is passed in the config, this method will check if a nonce has already been set and use it.



191
192
193
# File 'lib/secure_headers/headers/content_security_policy.rb', line 191

def nonce
  @nonce ||= @controller.instance_variable_get(:@content_security_policy_nonce) || self.class.generate_nonce
end

#to_jsonObject



217
218
219
220
221
222
223
224
225
# File 'lib/secure_headers/headers/content_security_policy.rb', line 217

def to_json
  build_value
  @config.inject({}) do |hash, (key, value)|
    if ALL_DIRECTIVES.include?(key)
      hash[key.to_s.gsub(/(\w+)_(\w+)/, "\\1-\\2")] = value
    end
    hash
  end.to_json
end

#valueObject

Return the value of the CSP header



208
209
210
211
212
213
214
215
# File 'lib/secure_headers/headers/content_security_policy.rb', line 208

def value
  return @config if @config.is_a?(String)
  if @config
    build_value
  else
    DEFAULT_CSP_HEADER
  end
end