secure_headers 5.1.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -4
  3. data/CHANGELOG.md +3 -3
  4. data/Gemfile +1 -1
  5. data/README.md +2 -2
  6. data/docs/upgrading-to-6-0.md +50 -0
  7. data/lib/secure_headers/configuration.rb +114 -164
  8. data/lib/secure_headers/headers/clear_site_data.rb +1 -3
  9. data/lib/secure_headers/headers/content_security_policy.rb +8 -74
  10. data/lib/secure_headers/headers/content_security_policy_config.rb +3 -13
  11. data/lib/secure_headers/headers/expect_certificate_transparency.rb +2 -3
  12. data/lib/secure_headers/headers/policy_management.rb +14 -65
  13. data/lib/secure_headers/headers/public_key_pins.rb +2 -3
  14. data/lib/secure_headers/headers/referrer_policy.rb +2 -2
  15. data/lib/secure_headers/headers/strict_transport_security.rb +2 -2
  16. data/lib/secure_headers/headers/x_content_type_options.rb +2 -2
  17. data/lib/secure_headers/headers/x_download_options.rb +2 -2
  18. data/lib/secure_headers/headers/x_frame_options.rb +1 -2
  19. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +2 -2
  20. data/lib/secure_headers/headers/x_xss_protection.rb +3 -3
  21. data/lib/secure_headers/view_helper.rb +9 -8
  22. data/lib/secure_headers.rb +14 -78
  23. data/secure_headers.gemspec +1 -2
  24. data/spec/lib/secure_headers/configuration_spec.rb +15 -70
  25. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +2 -70
  26. data/spec/lib/secure_headers/headers/policy_management_spec.rb +35 -9
  27. data/spec/lib/secure_headers/middleware_spec.rb +7 -1
  28. data/spec/lib/secure_headers/view_helpers_spec.rb +29 -0
  29. data/spec/lib/secure_headers_spec.rb +38 -76
  30. data/spec/spec_helper.rb +7 -3
  31. metadata +3 -16
@@ -2,7 +2,6 @@
2
2
  module SecureHeaders
3
3
  module DynamicConfig
4
4
  def self.included(base)
5
- base.send(:attr_writer, :modified)
6
5
  base.send(:attr_reader, *base.attrs)
7
6
  base.attrs.each do |attr|
8
7
  base.send(:define_method, "#{attr}=") do |value|
@@ -42,7 +41,6 @@ module SecureHeaders
42
41
  @upgrade_insecure_requests = nil
43
42
 
44
43
  from_hash(hash)
45
- @modified = false
46
44
  end
47
45
 
48
46
  def update_directive(directive, value)
@@ -55,12 +53,10 @@ module SecureHeaders
55
53
  end
56
54
  end
57
55
 
58
- def modified?
59
- @modified
60
- end
61
-
62
56
  def merge(new_hash)
63
- ContentSecurityPolicy.combine_policies(self.to_h, new_hash)
57
+ new_config = self.dup
58
+ new_config.send(:from_hash, new_hash)
59
+ new_config
64
60
  end
65
61
 
66
62
  def merge!(new_hash)
@@ -109,17 +105,12 @@ module SecureHeaders
109
105
  def write_attribute(attr, value)
110
106
  value = value.dup if PolicyManagement::DIRECTIVE_VALUE_TYPES[attr] == :source_list
111
107
  attr_variable = "@#{attr}"
112
- prev_value = self.instance_variable_get(attr_variable)
113
108
  self.instance_variable_set(attr_variable, value)
114
- if prev_value != value
115
- @modified = true
116
- end
117
109
  end
118
110
  end
119
111
 
120
112
  class ContentSecurityPolicyConfigError < StandardError; end
121
113
  class ContentSecurityPolicyConfig
122
- CONFIG_KEY = :csp
123
114
  HEADER_NAME = "Content-Security-Policy".freeze
124
115
 
125
116
  ATTRS = PolicyManagement::ALL_DIRECTIVES + PolicyManagement::META_CONFIGS + PolicyManagement::NONCES
@@ -149,7 +140,6 @@ module SecureHeaders
149
140
  end
150
141
 
151
142
  class ContentSecurityPolicyReportOnlyConfig < ContentSecurityPolicyConfig
152
- CONFIG_KEY = :csp_report_only
153
143
  HEADER_NAME = "Content-Security-Policy-Report-Only".freeze
154
144
 
155
145
  def report_only?
@@ -4,7 +4,6 @@ module SecureHeaders
4
4
 
5
5
  class ExpectCertificateTransparency
6
6
  HEADER_NAME = "Expect-CT".freeze
7
- CONFIG_KEY = :expect_certificate_transparency
8
7
  INVALID_CONFIGURATION_ERROR = "config must be a hash.".freeze
9
8
  INVALID_ENFORCE_VALUE_ERROR = "enforce must be a boolean".freeze
10
9
  REQUIRED_MAX_AGE_ERROR = "max-age is a required directive.".freeze
@@ -15,8 +14,8 @@ module SecureHeaders
15
14
  #
16
15
  # Returns nil if not configured, returns header name and value if
17
16
  # configured.
18
- def make_header(config)
19
- return if config.nil?
17
+ def make_header(config, use_agent = nil)
18
+ return if config.nil? || config == OPT_OUT
20
19
 
21
20
  header = new(config)
22
21
  [HEADER_NAME, header.value]
@@ -5,7 +5,6 @@ module SecureHeaders
5
5
  base.extend(ClassMethods)
6
6
  end
7
7
 
8
- MODERN_BROWSERS = %w(Chrome Opera Firefox)
9
8
  DEFAULT_CONFIG = {
10
9
  default_src: %w(https:),
11
10
  img_src: %w(https: data: 'self'),
@@ -82,58 +81,12 @@ module SecureHeaders
82
81
  UPGRADE_INSECURE_REQUESTS
83
82
  ].flatten.freeze
84
83
 
85
- EDGE_DIRECTIVES = DIRECTIVES_1_0
86
- SAFARI_DIRECTIVES = DIRECTIVES_1_0
87
- SAFARI_10_DIRECTIVES = DIRECTIVES_2_0
88
-
89
- FIREFOX_UNSUPPORTED_DIRECTIVES = [
90
- BLOCK_ALL_MIXED_CONTENT,
91
- CHILD_SRC,
92
- WORKER_SRC,
93
- PLUGIN_TYPES
94
- ].freeze
95
-
96
- FIREFOX_46_DEPRECATED_DIRECTIVES = [
97
- FRAME_SRC
98
- ].freeze
99
-
100
- FIREFOX_46_UNSUPPORTED_DIRECTIVES = [
101
- BLOCK_ALL_MIXED_CONTENT,
102
- WORKER_SRC,
103
- PLUGIN_TYPES
104
- ].freeze
105
-
106
- FIREFOX_DIRECTIVES = (
107
- DIRECTIVES_3_0 - FIREFOX_UNSUPPORTED_DIRECTIVES
108
- ).freeze
109
-
110
- FIREFOX_46_DIRECTIVES = (
111
- DIRECTIVES_3_0 - FIREFOX_46_UNSUPPORTED_DIRECTIVES - FIREFOX_46_DEPRECATED_DIRECTIVES
112
- ).freeze
113
-
114
- CHROME_DIRECTIVES = (
115
- DIRECTIVES_3_0
116
- ).freeze
117
-
118
84
  ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0).uniq.sort
119
85
 
120
86
  # Think of default-src and report-uri as the beginning and end respectively,
121
87
  # everything else is in between.
122
88
  BODY_DIRECTIVES = ALL_DIRECTIVES - [DEFAULT_SRC, REPORT_URI]
123
89
 
124
- VARIATIONS = {
125
- "Chrome" => CHROME_DIRECTIVES,
126
- "Opera" => CHROME_DIRECTIVES,
127
- "Firefox" => FIREFOX_DIRECTIVES,
128
- "FirefoxTransitional" => FIREFOX_46_DIRECTIVES,
129
- "Safari" => SAFARI_DIRECTIVES,
130
- "SafariTransitional" => SAFARI_10_DIRECTIVES,
131
- "Edge" => EDGE_DIRECTIVES,
132
- "Other" => CHROME_DIRECTIVES
133
- }.freeze
134
-
135
- OTHER = "Other".freeze
136
-
137
90
  DIRECTIVE_VALUE_TYPES = {
138
91
  BASE_URI => :source_list,
139
92
  BLOCK_ALL_MIXED_CONTENT => :boolean,
@@ -200,8 +153,9 @@ module SecureHeaders
200
153
  #
201
154
  # Returns a default policy if no configuration is provided, or a
202
155
  # header name and value based on the config.
203
- def make_header(config, user_agent)
204
- header = new(config, user_agent)
156
+ def make_header(config)
157
+ return if config.nil? || config == OPT_OUT
158
+ header = new(config)
205
159
  [header.name, header.value]
206
160
  end
207
161
 
@@ -215,27 +169,28 @@ module SecureHeaders
215
169
  if config.directive_value(:script_src).nil?
216
170
  raise ContentSecurityPolicyConfigError.new(":script_src is required, falling back to default-src is too dangerous. Use `script_src: OPT_OUT` to override")
217
171
  end
172
+ if !config.report_only? && config.directive_value(:report_only)
173
+ raise ContentSecurityPolicyConfigError.new("Only the csp_report_only config should set :report_only to true")
174
+ end
175
+
176
+ if config.report_only? && config.directive_value(:report_only) == false
177
+ raise ContentSecurityPolicyConfigError.new("csp_report_only config must have :report_only set to true")
178
+ end
218
179
 
219
180
  ContentSecurityPolicyConfig.attrs.each do |key|
220
181
  value = config.directive_value(key)
221
182
  next unless value
183
+
222
184
  if META_CONFIGS.include?(key)
223
185
  raise ContentSecurityPolicyConfigError.new("#{key} must be a boolean value") unless boolean?(value) || value.nil?
186
+ elsif NONCES.include?(key)
187
+ raise ContentSecurityPolicyConfigError.new("#{key} must be a non-nil value") if value.nil?
224
188
  else
225
189
  validate_directive!(key, value)
226
190
  end
227
191
  end
228
192
  end
229
193
 
230
- # Public: check if a user agent supports CSP nonces
231
- #
232
- # user_agent - a String or a UserAgent object
233
- def nonces_supported?(user_agent)
234
- user_agent = UserAgent.parse(user_agent) if user_agent.is_a?(String)
235
- MODERN_BROWSERS.include?(user_agent.browser) ||
236
- user_agent.browser == "Safari" && (user_agent.version || CSP::FALLBACK_VERSION) >= CSP::VERSION_10
237
- end
238
-
239
194
  # Public: combine the values from two different configs.
240
195
  #
241
196
  # original - the main config
@@ -302,17 +257,11 @@ module SecureHeaders
302
257
  # Don't set a default if directive has an existing value
303
258
  next if original[directive]
304
259
  if FETCH_SOURCES.include?(directive)
305
- original[directive] = default_for(directive, original)
260
+ original[directive] = original[DEFAULT_SRC]
306
261
  end
307
262
  end
308
263
  end
309
264
 
310
- def default_for(directive, original)
311
- return original[FRAME_SRC] if directive == CHILD_SRC && original[FRAME_SRC]
312
- return original[CHILD_SRC] if directive == FRAME_SRC && original[CHILD_SRC]
313
- original[DEFAULT_SRC]
314
- end
315
-
316
265
  def source_list?(directive)
317
266
  DIRECTIVE_VALUE_TYPES[directive] == :source_list
318
267
  end
@@ -5,15 +5,14 @@ module SecureHeaders
5
5
  HEADER_NAME = "Public-Key-Pins".freeze
6
6
  REPORT_ONLY = "Public-Key-Pins-Report-Only".freeze
7
7
  HASH_ALGORITHMS = [:sha256].freeze
8
- CONFIG_KEY = :hpkp
9
8
 
10
9
 
11
10
  class << self
12
11
  # Public: make an hpkp header name, value pair
13
12
  #
14
13
  # Returns nil if not configured, returns header name and value if configured.
15
- def make_header(config)
16
- return if config.nil?
14
+ def make_header(config, user_agent = nil)
15
+ return if config.nil? || config == OPT_OUT
17
16
  header = new(config)
18
17
  [header.name, header.value]
19
18
  end
@@ -14,14 +14,14 @@ module SecureHeaders
14
14
  origin-when-cross-origin
15
15
  unsafe-url
16
16
  )
17
- CONFIG_KEY = :referrer_policy
18
17
 
19
18
  class << self
20
19
  # Public: generate an Referrer Policy header.
21
20
  #
22
21
  # Returns a default header if no configuration is provided, or a
23
22
  # header name and value based on the config.
24
- def make_header(config = nil)
23
+ def make_header(config = nil, user_agent = nil)
24
+ return if config == OPT_OUT
25
25
  config ||= DEFAULT_VALUE
26
26
  [HEADER_NAME, Array(config).join(", ")]
27
27
  end
@@ -8,14 +8,14 @@ module SecureHeaders
8
8
  DEFAULT_VALUE = "max-age=" + HSTS_MAX_AGE
9
9
  VALID_STS_HEADER = /\Amax-age=\d+(; includeSubdomains)?(; preload)?\z/i
10
10
  MESSAGE = "The config value supplied for the HSTS header was invalid. Must match #{VALID_STS_HEADER}"
11
- CONFIG_KEY = :hsts
12
11
 
13
12
  class << self
14
13
  # Public: generate an hsts header name, value pair.
15
14
  #
16
15
  # Returns a default header if no configuration is provided, or a
17
16
  # header name and value based on the config.
18
- def make_header(config = nil)
17
+ def make_header(config = nil, user_agent = nil)
18
+ return if config == OPT_OUT
19
19
  [HEADER_NAME, config || DEFAULT_VALUE]
20
20
  end
21
21
 
@@ -5,14 +5,14 @@ module SecureHeaders
5
5
  class XContentTypeOptions
6
6
  HEADER_NAME = "X-Content-Type-Options".freeze
7
7
  DEFAULT_VALUE = "nosniff"
8
- CONFIG_KEY = :x_content_type_options
9
8
 
10
9
  class << self
11
10
  # Public: generate an X-Content-Type-Options header.
12
11
  #
13
12
  # Returns a default header if no configuration is provided, or a
14
13
  # header name and value based on the config.
15
- def make_header(config = nil)
14
+ def make_header(config = nil, user_agent = nil)
15
+ return if config == OPT_OUT
16
16
  [HEADER_NAME, config || DEFAULT_VALUE]
17
17
  end
18
18
 
@@ -4,14 +4,14 @@ module SecureHeaders
4
4
  class XDownloadOptions
5
5
  HEADER_NAME = "X-Download-Options".freeze
6
6
  DEFAULT_VALUE = "noopen"
7
- CONFIG_KEY = :x_download_options
8
7
 
9
8
  class << self
10
9
  # Public: generate an X-Download-Options header.
11
10
  #
12
11
  # Returns a default header if no configuration is provided, or a
13
12
  # header name and value based on the config.
14
- def make_header(config = nil)
13
+ def make_header(config = nil, user_agent = nil)
14
+ return if config == OPT_OUT
15
15
  [HEADER_NAME, config || DEFAULT_VALUE]
16
16
  end
17
17
 
@@ -3,7 +3,6 @@ module SecureHeaders
3
3
  class XFOConfigError < StandardError; end
4
4
  class XFrameOptions
5
5
  HEADER_NAME = "X-Frame-Options".freeze
6
- CONFIG_KEY = :x_frame_options
7
6
  SAMEORIGIN = "sameorigin"
8
7
  DENY = "deny"
9
8
  ALLOW_FROM = "allow-from"
@@ -16,7 +15,7 @@ module SecureHeaders
16
15
  #
17
16
  # Returns a default header if no configuration is provided, or a
18
17
  # header name and value based on the config.
19
- def make_header(config = nil)
18
+ def make_header(config = nil, user_agent = nil)
20
19
  return if config == OPT_OUT
21
20
  [HEADER_NAME, config || DEFAULT_VALUE]
22
21
  end
@@ -5,14 +5,14 @@ module SecureHeaders
5
5
  HEADER_NAME = "X-Permitted-Cross-Domain-Policies".freeze
6
6
  DEFAULT_VALUE = "none"
7
7
  VALID_POLICIES = %w(all none master-only by-content-type by-ftp-filename)
8
- CONFIG_KEY = :x_permitted_cross_domain_policies
9
8
 
10
9
  class << self
11
10
  # Public: generate an X-Permitted-Cross-Domain-Policies header.
12
11
  #
13
12
  # Returns a default header if no configuration is provided, or a
14
13
  # header name and value based on the config.
15
- def make_header(config = nil)
14
+ def make_header(config = nil, user_agent = nil)
15
+ return if config == OPT_OUT
16
16
  [HEADER_NAME, config || DEFAULT_VALUE]
17
17
  end
18
18
 
@@ -4,15 +4,15 @@ module SecureHeaders
4
4
  class XXssProtection
5
5
  HEADER_NAME = "X-XSS-Protection".freeze
6
6
  DEFAULT_VALUE = "1; mode=block"
7
- VALID_X_XSS_HEADER = /\A[01](; mode=block)?(; report=.*)?\z/i
8
- CONFIG_KEY = :x_xss_protection
7
+ VALID_X_XSS_HEADER = /\A[01](; mode=block)?(; report=.*)?\z/
9
8
 
10
9
  class << self
11
10
  # Public: generate an X-Xss-Protection header.
12
11
  #
13
12
  # Returns a default header if no configuration is provided, or a
14
13
  # header name and value based on the config.
15
- def make_header(config = nil)
14
+ def make_header(config = nil, user_agent = nil)
15
+ return if config == OPT_OUT
16
16
  [HEADER_NAME, config || DEFAULT_VALUE]
17
17
  end
18
18
 
@@ -19,7 +19,7 @@ module SecureHeaders
19
19
  #
20
20
  # Returns an html-safe link tag with the nonce attribute.
21
21
  def nonced_stylesheet_link_tag(*args, &block)
22
- opts = extract_options(args).merge(nonce: content_security_policy_nonce(:style))
22
+ opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:style))
23
23
 
24
24
  stylesheet_link_tag(*args, opts, &block)
25
25
  end
@@ -37,7 +37,7 @@ module SecureHeaders
37
37
  #
38
38
  # Returns an html-safe script tag with the nonce attribute.
39
39
  def nonced_javascript_include_tag(*args, &block)
40
- opts = extract_options(args).merge(nonce: content_security_policy_nonce(:script))
40
+ opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:script))
41
41
 
42
42
  javascript_include_tag(*args, opts, &block)
43
43
  end
@@ -47,7 +47,7 @@ module SecureHeaders
47
47
  #
48
48
  # Returns an html-safe script tag with the nonce attribute.
49
49
  def nonced_javascript_pack_tag(*args, &block)
50
- opts = extract_options(args).merge(nonce: content_security_policy_nonce(:script))
50
+ opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:script))
51
51
 
52
52
  javascript_pack_tag(*args, opts, &block)
53
53
  end
@@ -57,7 +57,7 @@ module SecureHeaders
57
57
  #
58
58
  # Returns an html-safe link tag with the nonce attribute.
59
59
  def nonced_stylesheet_pack_tag(*args, &block)
60
- opts = extract_options(args).merge(nonce: content_security_policy_nonce(:style))
60
+ opts = extract_options(args).merge(nonce: _content_security_policy_nonce(:style))
61
61
 
62
62
  stylesheet_pack_tag(*args, opts, &block)
63
63
  end
@@ -66,7 +66,7 @@ module SecureHeaders
66
66
  # Instructs secure_headers to append a nonce to style/script-src directives.
67
67
  #
68
68
  # Returns a non-html-safe nonce value.
69
- def content_security_policy_nonce(type)
69
+ def _content_security_policy_nonce(type)
70
70
  case type
71
71
  when :script
72
72
  SecureHeaders.content_security_policy_script_nonce(@_request)
@@ -74,13 +74,14 @@ module SecureHeaders
74
74
  SecureHeaders.content_security_policy_style_nonce(@_request)
75
75
  end
76
76
  end
77
+ alias_method :content_security_policy_nonce, :_content_security_policy_nonce
77
78
 
78
79
  def content_security_policy_script_nonce
79
- content_security_policy_nonce(:script)
80
+ _content_security_policy_nonce(:script)
80
81
  end
81
82
 
82
83
  def content_security_policy_style_nonce
83
- content_security_policy_nonce(:style)
84
+ _content_security_policy_nonce(:style)
84
85
  end
85
86
 
86
87
  ##
@@ -152,7 +153,7 @@ module SecureHeaders
152
153
  else
153
154
  content_or_options.html_safe # :'(
154
155
  end
155
- content_tag type, content, options.merge(nonce: content_security_policy_nonce(type))
156
+ content_tag type, content, options.merge(nonce: _content_security_policy_nonce(type))
156
157
  end
157
158
 
158
159
  def extract_options(args)
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require "secure_headers/configuration"
3
2
  require "secure_headers/hash_helper"
4
3
  require "secure_headers/headers/cookie"
5
4
  require "secure_headers/headers/public_key_pins"
@@ -16,8 +15,8 @@ require "secure_headers/headers/expect_certificate_transparency"
16
15
  require "secure_headers/middleware"
17
16
  require "secure_headers/railtie"
18
17
  require "secure_headers/view_helper"
19
- require "useragent"
20
18
  require "singleton"
19
+ require "secure_headers/configuration"
21
20
 
22
21
  # All headers (except for hpkp) have a default value. Provide SecureHeaders::OPT_OUT
23
22
  # or ":optout_of_protection" as a config value to disable a given header
@@ -52,29 +51,9 @@ module SecureHeaders
52
51
  HTTPS = "https".freeze
53
52
  CSP = ContentSecurityPolicy
54
53
 
55
- ALL_HEADER_CLASSES = [
56
- ExpectCertificateTransparency,
57
- ClearSiteData,
58
- ContentSecurityPolicyConfig,
59
- ContentSecurityPolicyReportOnlyConfig,
60
- StrictTransportSecurity,
61
- PublicKeyPins,
62
- ReferrerPolicy,
63
- XContentTypeOptions,
64
- XDownloadOptions,
65
- XFrameOptions,
66
- XPermittedCrossDomainPolicies,
67
- XXssProtection
68
- ].freeze
69
-
70
- ALL_HEADERS_BESIDES_CSP = (
71
- ALL_HEADER_CLASSES -
72
- [ContentSecurityPolicyConfig, ContentSecurityPolicyReportOnlyConfig]
73
- ).freeze
74
-
75
54
  # Headers set on http requests (excludes STS and HPKP)
76
- HTTP_HEADER_CLASSES =
77
- (ALL_HEADER_CLASSES - [StrictTransportSecurity, PublicKeyPins]).freeze
55
+ HTTPS_HEADER_CLASSES =
56
+ [StrictTransportSecurity, PublicKeyPins].freeze
78
57
 
79
58
  class << self
80
59
  # Public: override a given set of directives for the current request. If a
@@ -153,7 +132,7 @@ module SecureHeaders
153
132
  # Public: opts out of setting all headers by telling secure_headers to use
154
133
  # the NOOP configuration.
155
134
  def opt_out_of_all_protection(request)
156
- use_secure_headers_override(request, Configuration::NOOP_CONFIGURATION)
135
+ use_secure_headers_override(request, Configuration::NOOP_OVERRIDE)
157
136
  end
158
137
 
159
138
  # Public: Builds the hash of headers that should be applied base on the
@@ -168,27 +147,15 @@ module SecureHeaders
168
147
  def header_hash_for(request)
169
148
  prevent_dup = true
170
149
  config = config_for(request, prevent_dup)
171
- headers = config.cached_headers
172
- user_agent = UserAgent.parse(request.user_agent)
173
-
174
- if !config.csp.opt_out? && config.csp.modified?
175
- headers = update_cached_csp(config.csp, headers, user_agent)
176
- end
177
-
178
- if !config.csp_report_only.opt_out? && config.csp_report_only.modified?
179
- headers = update_cached_csp(config.csp_report_only, headers, user_agent)
180
- end
150
+ config.validate_config!
151
+ headers = config.generate_headers
181
152
 
182
- header_classes_for(request).each_with_object({}) do |klass, hash|
183
- if header = headers[klass::CONFIG_KEY]
184
- header_name, value = if klass == ContentSecurityPolicyConfig || klass == ContentSecurityPolicyReportOnlyConfig
185
- csp_header_for_ua(header, user_agent)
186
- else
187
- header
188
- end
189
- hash[header_name] = value
153
+ if request.scheme != HTTPS
154
+ HTTPS_HEADER_CLASSES.each do |klass|
155
+ headers.delete(klass::HEADER_NAME)
190
156
  end
191
157
  end
158
+ headers
192
159
  end
193
160
 
194
161
  # Public: specify which named override will be used for this request.
@@ -196,11 +163,9 @@ module SecureHeaders
196
163
  #
197
164
  # name - the name of the previously configured override.
198
165
  def use_secure_headers_override(request, name)
199
- if config = Configuration.get(name, internal: true)
200
- override_secure_headers_request_config(request, config)
201
- else
202
- raise ArgumentError.new("no override by the name of #{name} has been configured")
203
- end
166
+ config = config_for(request)
167
+ config.override(name)
168
+ override_secure_headers_request_config(request, config)
204
169
  end
205
170
 
206
171
  # Public: gets or creates a nonce for CSP.
@@ -228,7 +193,7 @@ module SecureHeaders
228
193
  # Falls back to the global config
229
194
  def config_for(request, prevent_dup = false)
230
195
  config = request.env[SECURE_HEADERS_CONFIG] ||
231
- Configuration.get(Configuration::DEFAULT_CONFIG, internal: true)
196
+ Configuration.send(:default_config)
232
197
 
233
198
 
234
199
  # Global configs are frozen, per-request configs are not. When we're not
@@ -285,35 +250,6 @@ module SecureHeaders
285
250
  def override_secure_headers_request_config(request, config)
286
251
  request.env[SECURE_HEADERS_CONFIG] = config
287
252
  end
288
-
289
- # Private: determines which headers are applicable to a given request.
290
- #
291
- # Returns a list of classes whose corresponding header values are valid for
292
- # this request.
293
- def header_classes_for(request)
294
- if request.scheme == HTTPS
295
- ALL_HEADER_CLASSES
296
- else
297
- HTTP_HEADER_CLASSES
298
- end
299
- end
300
-
301
- def update_cached_csp(config, headers, user_agent)
302
- headers = Configuration.send(:deep_copy, headers)
303
- headers[config.class::CONFIG_KEY] = {}
304
- variation = ContentSecurityPolicy.ua_to_variation(user_agent)
305
- headers[config.class::CONFIG_KEY][variation] = ContentSecurityPolicy.make_header(config, user_agent)
306
- headers
307
- end
308
-
309
- # Private: chooses the applicable CSP header for the provided user agent.
310
- #
311
- # headers - a hash of header_config_key => [header_name, header_value]
312
- #
313
- # Returns a CSP [header, value] array
314
- def csp_header_for_ua(headers, user_agent)
315
- headers[ContentSecurityPolicy.ua_to_variation(user_agent)]
316
- end
317
253
  end
318
254
 
319
255
  # These methods are mixed into controllers and delegate to the class method
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "secure_headers"
5
- gem.version = "5.1.0"
5
+ gem.version = "6.0.0"
6
6
  gem.authors = ["Neil Matatall"]
7
7
  gem.email = ["neil.matatall@gmail.com"]
8
8
  gem.description = "Manages application of security headers with many safe defaults."
@@ -16,5 +16,4 @@ Gem::Specification.new do |gem|
16
16
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
17
  gem.require_paths = ["lib"]
18
18
  gem.add_development_dependency "rake"
19
- gem.add_dependency "useragent", ">= 0.15.0"
20
19
  end