secure_headers 3.5.0.pre → 3.6.2

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +2 -0
  4. data/CHANGELOG.md +20 -1
  5. data/Gemfile +1 -0
  6. data/LICENSE +4 -199
  7. data/README.md +37 -388
  8. data/docs/HPKP.md +17 -0
  9. data/docs/cookies.md +50 -0
  10. data/docs/hashes.md +64 -0
  11. data/docs/named_overrides_and_appends.md +107 -0
  12. data/docs/per_action_configuration.md +105 -0
  13. data/docs/sinatra.md +25 -0
  14. data/lib/secure_headers/configuration.rb +5 -3
  15. data/lib/secure_headers/headers/clear_site_data.rb +51 -0
  16. data/lib/secure_headers/headers/content_security_policy.rb +6 -2
  17. data/lib/secure_headers/headers/content_security_policy_config.rb +21 -12
  18. data/lib/secure_headers/headers/policy_management.rb +14 -2
  19. data/lib/secure_headers/headers/referrer_policy.rb +4 -1
  20. data/lib/secure_headers/headers/strict_transport_security.rb +1 -1
  21. data/lib/secure_headers/headers/x_content_type_options.rb +1 -1
  22. data/lib/secure_headers/headers/x_download_options.rb +1 -1
  23. data/lib/secure_headers/headers/x_frame_options.rb +1 -1
  24. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +1 -1
  25. data/lib/secure_headers/headers/x_xss_protection.rb +1 -1
  26. data/lib/secure_headers/view_helper.rb +8 -0
  27. data/lib/secure_headers.rb +4 -1
  28. data/secure_headers.gemspec +1 -1
  29. data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +103 -0
  30. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +17 -0
  31. data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +18 -0
  32. data/spec/lib/secure_headers/middleware_spec.rb +12 -0
  33. data/spec/lib/secure_headers/view_helpers_spec.rb +10 -0
  34. data/spec/lib/secure_headers_spec.rb +22 -0
  35. data/spec/spec_helper.rb +2 -1
  36. metadata +14 -5
@@ -0,0 +1,107 @@
1
+ ## Named Appends
2
+
3
+ Named Appends are blocks of code that can be reused and composed during requests. e.g. If a certain partial is rendered conditionally, and the csp needs to be adjusted for that partial, you can create a named append for that situation. The value returned by the block will be passed into `append_content_security_policy_directives`. The current request object is passed as an argument to the block for even more flexibility.
4
+
5
+ ```ruby
6
+ def show
7
+ if include_widget?
8
+ @widget = widget.render
9
+ use_content_security_policy_named_append(:widget_partial)
10
+ end
11
+ end
12
+
13
+
14
+ SecureHeaders::Configuration.named_append(:widget_partial) do |request|
15
+ SecureHeaders.override_x_frame_options(request, "DENY")
16
+ if request.controller_instance.current_user.in_test_bucket?
17
+ { child_src: %w(beta.thirdpartyhost.com) }
18
+ else
19
+ { child_src: %w(thirdpartyhost.com) }
20
+ end
21
+ end
22
+ ```
23
+
24
+ You can use as many named appends as you would like per request, but be careful because order of inclusion matters. Consider the following:
25
+
26
+ ```ruby
27
+ SecureHeader::Configuration.default do |config|
28
+ config.csp = { default_src: %w('self')}
29
+ end
30
+
31
+ SecureHeaders::Configuration.named_append(:A) do |request|
32
+ { default_src: %w(myhost.com) }
33
+ end
34
+
35
+ SecureHeaders::Configuration.named_append(:B) do |request|
36
+ { script_src: %w('unsafe-eval') }
37
+ end
38
+ ```
39
+
40
+ The following code will produce different policies due to the way policies are normalized (e.g. providing a previously undefined directive that inherits from `default-src`, removing host source values when `*` is provided. Removing `'none'` when additional values are present, etc.):
41
+
42
+ ```ruby
43
+ def index
44
+ use_content_security_policy_named_append(:A)
45
+ use_content_security_policy_named_append(:B)
46
+ # produces default-src 'self' myhost.com; script-src 'self' myhost.com 'unsafe-eval';
47
+ end
48
+
49
+ def show
50
+ use_content_security_policy_named_append(:B)
51
+ use_content_security_policy_named_append(:A)
52
+ # produces default-src 'self' myhost.com; script-src 'self' 'unsafe-eval';
53
+ end
54
+ ```
55
+
56
+
57
+ ## Named overrides
58
+
59
+ Named overrides serve two purposes:
60
+
61
+ * To be able to refer to a configuration by simple name.
62
+ * By precomputing the headers for a named configuration, the headers generated once and reused over every request.
63
+
64
+ To use a named override, drop a `SecureHeaders::Configuration.override` block **outside** of method definitions and then declare which named override you'd like to use. You can even override an override.
65
+
66
+ ```ruby
67
+ class ApplicationController < ActionController::Base
68
+ SecureHeaders::Configuration.default do |config|
69
+ config.csp = {
70
+ default_src: %w('self'),
71
+ script_src: %w(example.org)
72
+ }
73
+ end
74
+
75
+ # override default configuration
76
+ SecureHeaders::Configuration.override(:script_from_otherdomain_com) do |config|
77
+ config.csp[:script_src] << "otherdomain.com"
78
+ end
79
+
80
+ # overrides the :script_from_otherdomain_com configuration
81
+ SecureHeaders::Configuration.override(:another_config, :script_from_otherdomain_com) do |config|
82
+ config.csp[:script_src] << "evenanotherdomain.com"
83
+ end
84
+ end
85
+
86
+ class MyController < ApplicationController
87
+ def index
88
+ # Produces default-src 'self'; script-src example.org otherdomain.com
89
+ use_secure_headers_override(:script_from_otherdomain_com)
90
+ end
91
+
92
+ def show
93
+ # Produces default-src 'self'; script-src example.org otherdomain.org evenanotherdomain.com
94
+ use_secure_headers_override(:another_config)
95
+ end
96
+ end
97
+ ```
98
+
99
+ By default, a no-op configuration is provided. No headers will be set when this default override is used.
100
+
101
+ ```ruby
102
+ class MyController < ApplicationController
103
+ def index
104
+ SecureHeaders.opt_out_of_all_protection(request)
105
+ end
106
+ end
107
+ ```
@@ -0,0 +1,105 @@
1
+ ## Per-action configuration
2
+
3
+ You can override the settings for a given action by producing a temporary override. Be aware that because of the dynamic nature of the value, the header values will be computed per request.
4
+
5
+ ```ruby
6
+ # Given a config of:
7
+ ::SecureHeaders::Configuration.default do |config|
8
+ config.csp = {
9
+ default_src: %w('self'),
10
+ script_src: %w('self')
11
+ }
12
+ end
13
+
14
+ class MyController < ApplicationController
15
+ def index
16
+ # Append value to the source list, override 'none' values
17
+ # Produces: default-src 'self'; script-src 'self' s3.amazonaws.com; object-src 'self' www.youtube.com
18
+ append_content_security_policy_directives(script_src: %w(s3.amazonaws.com), object_src: %w('self' www.youtube.com))
19
+
20
+ # Overrides the previously set source list, override 'none' values
21
+ # Produces: default-src 'self'; script-src s3.amazonaws.com; object-src 'self'
22
+ override_content_security_policy_directives(script_src: %w(s3.amazonaws.com), object_src: %w('self'))
23
+
24
+ # Global settings default to "sameorigin"
25
+ override_x_frame_options("DENY")
26
+ end
27
+ ```
28
+
29
+ The following methods are available as controller instance methods. They are also available as class methods, but require you to pass in the `request` object.
30
+ * `append_content_security_policy_directives(hash)`: appends each value to the corresponding CSP app-wide configuration.
31
+ * `override_content_security_policy_directives(hash)`: merges the hash into the app-wide configuration, overwriting any previous config
32
+ * `override_x_frame_options(value)`: sets the `X-Frame-Options header` to `value`
33
+
34
+ ## Appending / overriding Content Security Policy
35
+
36
+ When manipulating content security policy, there are a few things to consider. The default header value is `default-src https:` which corresponds to a default configuration of `{ default_src: %w(https:)}`.
37
+
38
+ #### Append to the policy with a directive other than `default_src`
39
+
40
+ The value of `default_src` is joined with the addition if the it is a [fetch directive](https://w3c.github.io/webappsec-csp/#directives-fetch). Note the `https:` is carried over from the `default-src` config. If you do not want this, use `override_content_security_policy_directives` instead. To illustrate:
41
+
42
+ ```ruby
43
+ ::SecureHeaders::Configuration.default do |config|
44
+ config.csp = {
45
+ default_src: %w('self')
46
+ }
47
+ end
48
+ ```
49
+
50
+ Code | Result
51
+ ------------- | -------------
52
+ `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src 'self' mycdn.com`
53
+ `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src mycdn.com`
54
+
55
+ #### Nonce
56
+
57
+ You can use a view helper to automatically add nonces to script tags:
58
+
59
+ ```erb
60
+ <%= nonced_javascript_tag do %>
61
+ console.log("nonced!");
62
+ <% end %>
63
+
64
+ <%= nonced_style_tag do %>
65
+ body {
66
+ background-color: black;
67
+ }
68
+ <% end %>
69
+ ```
70
+
71
+ becomes:
72
+
73
+ ```html
74
+ <script nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
75
+ console.log("nonced!")
76
+ </script>
77
+ <style nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
78
+ body {
79
+ background-color: black;
80
+ }
81
+ </style>
82
+ ```
83
+
84
+ ```
85
+
86
+ Content-Security-Policy: ...
87
+ script-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
88
+ style-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
89
+ ```
90
+
91
+ `script`/`style-nonce` can be used to whitelist inline content. To do this, call the `content_security_policy_script_nonce` or `content_security_policy_style_nonce` then set the nonce attributes on the various tags.
92
+
93
+ ```erb
94
+ <script nonce="<%= content_security_policy_script_nonce %>">
95
+ console.log("whitelisted, will execute")
96
+ </script>
97
+
98
+ <script nonce="lol">
99
+ console.log("won't execute, not whitelisted")
100
+ </script>
101
+
102
+ <script>
103
+ console.log("won't execute, not whitelisted")
104
+ </script>
105
+ ```
data/docs/sinatra.md ADDED
@@ -0,0 +1,25 @@
1
+ ## Sinatra
2
+
3
+ Here's an example using SecureHeaders for Sinatra applications:
4
+
5
+ ```ruby
6
+ require 'rubygems'
7
+ require 'sinatra'
8
+ require 'haml'
9
+ require 'secure_headers'
10
+
11
+ use SecureHeaders::Middleware
12
+
13
+ SecureHeaders::Configuration.default do |config|
14
+ ...
15
+ end
16
+
17
+ class Donkey < Sinatra::Application
18
+ set :root, APP_ROOT
19
+
20
+ get '/' do
21
+ SecureHeaders.override_x_frame_options(request, SecureHeaders::OPT_OUT)
22
+ haml :index
23
+ end
24
+ end
25
+ ```
@@ -116,7 +116,7 @@ module SecureHeaders
116
116
 
117
117
  attr_writer :hsts, :x_frame_options, :x_content_type_options,
118
118
  :x_xss_protection, :x_download_options, :x_permitted_cross_domain_policies,
119
- :referrer_policy
119
+ :referrer_policy, :clear_site_data
120
120
 
121
121
  attr_reader :cached_headers, :csp, :cookies, :csp_report_only, :hpkp, :hpkp_report_host
122
122
 
@@ -140,7 +140,7 @@ module SecureHeaders
140
140
  # Returns a deep-dup'd copy of this configuration.
141
141
  def dup
142
142
  copy = self.class.new
143
- copy.cookies = @cookies
143
+ copy.cookies = self.class.send(:deep_copy_if_hash, @cookies)
144
144
  copy.csp = @csp.dup if @csp
145
145
  copy.csp_report_only = @csp_report_only.dup if @csp_report_only
146
146
  copy.cached_headers = self.class.send(:deep_copy_if_hash, @cached_headers)
@@ -150,6 +150,7 @@ module SecureHeaders
150
150
  copy.x_xss_protection = @x_xss_protection
151
151
  copy.x_download_options = @x_download_options
152
152
  copy.x_permitted_cross_domain_policies = @x_permitted_cross_domain_policies
153
+ copy.clear_site_data = @clear_site_data
153
154
  copy.referrer_policy = @referrer_policy
154
155
  copy.hpkp = @hpkp
155
156
  copy.hpkp_report_host = @hpkp_report_host
@@ -181,6 +182,7 @@ module SecureHeaders
181
182
  XXssProtection.validate_config!(@x_xss_protection)
182
183
  XDownloadOptions.validate_config!(@x_download_options)
183
184
  XPermittedCrossDomainPolicies.validate_config!(@x_permitted_cross_domain_policies)
185
+ ClearSiteData.validate_config!(@clear_site_data)
184
186
  PublicKeyPins.validate_config!(@hpkp)
185
187
  Cookie.validate_config!(@cookies)
186
188
  end
@@ -260,7 +262,7 @@ module SecureHeaders
260
262
  end
261
263
  end
262
264
 
263
- # Public: Precompute the header names and values for this configuraiton.
265
+ # Public: Precompute the header names and values for this configuration.
264
266
  # Ensures that headers generated at configure time, not on demand.
265
267
  #
266
268
  # Returns the cached headers
@@ -0,0 +1,51 @@
1
+ module SecureHeaders
2
+ class ClearSiteDataConfigError < StandardError; end
3
+ class ClearSiteData
4
+ HEADER_NAME = "Clear-Site-Data".freeze
5
+ TYPES = "types".freeze
6
+
7
+ # Valid `types`
8
+ CACHE = "cache".freeze
9
+ COOKIES = "cookies".freeze
10
+ STORAGE = "storage".freeze
11
+ EXECTION_CONTEXTS = "executionContexts".freeze
12
+ ALL_TYPES = [CACHE, COOKIES, STORAGE, EXECTION_CONTEXTS]
13
+
14
+ CONFIG_KEY = :clear_site_data
15
+
16
+ class << self
17
+ # Public: make an Clear-Site-Data header name, value pair
18
+ #
19
+ # Returns nil if not configured, returns header name and value if configured.
20
+ def make_header(config=nil)
21
+ case config
22
+ when nil, OPT_OUT, []
23
+ # noop
24
+ when Array
25
+ [HEADER_NAME, JSON.dump(TYPES => config)]
26
+ when true
27
+ [HEADER_NAME, JSON.dump(TYPES => ALL_TYPES)]
28
+ end
29
+ end
30
+
31
+ def validate_config!(config)
32
+ case config
33
+ when nil, OPT_OUT, true
34
+ # valid
35
+ when Array
36
+ unless config.all? { |t| t.is_a?(String) }
37
+ raise ClearSiteDataConfigError.new("types must be Strings")
38
+ end
39
+
40
+ begin
41
+ JSON.dump(config)
42
+ rescue JSON::GeneratorError, Encoding::UndefinedConversionError
43
+ raise ClearSiteDataConfigError.new("types must serializable by JSON")
44
+ end
45
+ else
46
+ raise ClearSiteDataConfigError.new("config must be an Array of Strings or `true`")
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -8,6 +8,8 @@ module SecureHeaders
8
8
 
9
9
  # constants to be used for version-specific UA sniffing
10
10
  VERSION_46 = ::UserAgent::Version.new("46")
11
+ VERSION_10 = ::UserAgent::Version.new("10")
12
+ FALLBACK_VERSION = ::UserAgent::Version.new("0")
11
13
 
12
14
  def initialize(config = nil, user_agent = OTHER)
13
15
  @config = if config.is_a?(Hash)
@@ -212,8 +214,10 @@ module SecureHeaders
212
214
  # Returns an array of symbols representing the directives.
213
215
  def supported_directives
214
216
  @supported_directives ||= if VARIATIONS[@parsed_ua.browser]
215
- if @parsed_ua.browser == "Firefox" && @parsed_ua.version >= VERSION_46
217
+ if @parsed_ua.browser == "Firefox" && ((@parsed_ua.version || FALLBACK_VERSION) >= VERSION_46)
216
218
  VARIATIONS["FirefoxTransitional"]
219
+ elsif @parsed_ua.browser == "Safari" && ((@parsed_ua.version || FALLBACK_VERSION) >= VERSION_10)
220
+ VARIATIONS["SafariTransitional"]
217
221
  else
218
222
  VARIATIONS[@parsed_ua.browser]
219
223
  end
@@ -223,7 +227,7 @@ module SecureHeaders
223
227
  end
224
228
 
225
229
  def nonces_supported?
226
- @nonces_supported ||= MODERN_BROWSERS.include?(@parsed_ua.browser)
230
+ @nonces_supported ||= self.class.nonces_supported?(@parsed_ua)
227
231
  end
228
232
 
229
233
  def symbol_to_hyphen_case(sym)
@@ -6,12 +6,7 @@ module SecureHeaders
6
6
  base.attrs.each do |attr|
7
7
  base.send(:define_method, "#{attr}=") do |value|
8
8
  if self.class.attrs.include?(attr)
9
- value = value.dup if PolicyManagement::DIRECTIVE_VALUE_TYPES[attr] == :source_list
10
- prev_value = self.instance_variable_get("@#{attr}")
11
- self.instance_variable_set("@#{attr}", value)
12
- if prev_value != self.instance_variable_get("@#{attr}")
13
- @modified = true
14
- end
9
+ write_attribute(attr, value)
15
10
  else
16
11
  raise ContentSecurityPolicyConfigError, "Unknown config directive: #{attr}=#{value}"
17
12
  end
@@ -52,8 +47,9 @@ module SecureHeaders
52
47
 
53
48
  def to_h
54
49
  self.class.attrs.each_with_object({}) do |key, hash|
55
- hash[key] = self.send(key)
56
- end.reject { |_, v| v.nil? }
50
+ value = self.send(key)
51
+ hash[key] = value unless value.nil?
52
+ end
57
53
  end
58
54
 
59
55
  def dup
@@ -73,14 +69,26 @@ module SecureHeaders
73
69
 
74
70
  private
75
71
  def from_hash(hash)
76
- hash.keys.reject { |k| hash[k].nil? }.map do |k|
72
+ hash.each_pair do |k, v|
73
+ next if v.nil?
74
+
77
75
  if self.class.attrs.include?(k)
78
- self.send("#{k}=", hash[k])
76
+ write_attribute(k, v)
79
77
  else
80
- raise ContentSecurityPolicyConfigError, "Unknown config directive: #{k}=#{hash[k]}"
78
+ raise ContentSecurityPolicyConfigError, "Unknown config directive: #{k}=#{v}"
81
79
  end
82
80
  end
83
81
  end
82
+
83
+ def write_attribute(attr, value)
84
+ value = value.dup if PolicyManagement::DIRECTIVE_VALUE_TYPES[attr] == :source_list
85
+ attr_variable = "@#{attr}"
86
+ prev_value = self.instance_variable_get(attr_variable)
87
+ self.instance_variable_set(attr_variable, value)
88
+ if prev_value != value
89
+ @modified = true
90
+ end
91
+ end
84
92
  end
85
93
 
86
94
  class ContentSecurityPolicyConfigError < StandardError; end
@@ -88,8 +96,9 @@ module SecureHeaders
88
96
  CONFIG_KEY = :csp
89
97
  HEADER_NAME = "Content-Security-Policy".freeze
90
98
 
99
+ ATTRS = PolicyManagement::ALL_DIRECTIVES + PolicyManagement::META_CONFIGS + PolicyManagement::NONCES
91
100
  def self.attrs
92
- PolicyManagement::ALL_DIRECTIVES + PolicyManagement::META_CONFIGS + PolicyManagement::NONCES
101
+ ATTRS
93
102
  end
94
103
 
95
104
  include DynamicConfig
@@ -14,6 +14,7 @@ module SecureHeaders
14
14
  STAR = "*".freeze
15
15
  UNSAFE_INLINE = "'unsafe-inline'".freeze
16
16
  UNSAFE_EVAL = "'unsafe-eval'".freeze
17
+ STRICT_DYNAMIC = "'strict-dynamic'".freeze
17
18
 
18
19
  # leftover deprecated values that will be in common use upon upgrading.
19
20
  DEPRECATED_SOURCE_VALUES = [SELF, NONE, UNSAFE_EVAL, UNSAFE_INLINE, "inline", "eval"].map { |value| value.delete("'") }.freeze
@@ -80,6 +81,7 @@ module SecureHeaders
80
81
 
81
82
  EDGE_DIRECTIVES = DIRECTIVES_1_0
82
83
  SAFARI_DIRECTIVES = DIRECTIVES_1_0
84
+ SAFARI_10_DIRECTIVES = DIRECTIVES_2_0
83
85
 
84
86
  FIREFOX_UNSUPPORTED_DIRECTIVES = [
85
87
  BLOCK_ALL_MIXED_CONTENT,
@@ -108,7 +110,7 @@ module SecureHeaders
108
110
  DIRECTIVES_2_0 + DIRECTIVES_DRAFT
109
111
  ).freeze
110
112
 
111
- ALL_DIRECTIVES = [DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_DRAFT].flatten.uniq.sort
113
+ ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_DRAFT).uniq.sort
112
114
 
113
115
  # Think of default-src and report-uri as the beginning and end respectively,
114
116
  # everything else is in between.
@@ -132,6 +134,7 @@ module SecureHeaders
132
134
  "Firefox" => FIREFOX_DIRECTIVES,
133
135
  "FirefoxTransitional" => FIREFOX_46_DIRECTIVES,
134
136
  "Safari" => SAFARI_DIRECTIVES,
137
+ "SafariTransitional" => SAFARI_10_DIRECTIVES,
135
138
  "Edge" => EDGE_DIRECTIVES,
136
139
  "Other" => CHROME_DIRECTIVES
137
140
  }.freeze
@@ -211,6 +214,15 @@ module SecureHeaders
211
214
  end
212
215
  end
213
216
 
217
+ # Public: check if a user agent supports CSP nonces
218
+ #
219
+ # user_agent - a String or a UserAgent object
220
+ def nonces_supported?(user_agent)
221
+ user_agent = UserAgent.parse(user_agent) if user_agent.is_a?(String)
222
+ MODERN_BROWSERS.include?(user_agent.browser) ||
223
+ user_agent.browser == "Safari" && (user_agent.version || CSP::FALLBACK_VERSION) >= CSP::VERSION_10
224
+ end
225
+
214
226
  # Public: combine the values from two different configs.
215
227
  #
216
228
  # original - the main config
@@ -261,7 +273,7 @@ module SecureHeaders
261
273
  # copy the default-src value to the original config. This modifies the original hash.
262
274
  def populate_fetch_source_with_default!(original, additions)
263
275
  # in case we would be appending to an empty directive, fill it with the default-src value
264
- additions.keys.each do |directive|
276
+ additions.each_key do |directive|
265
277
  if !original[directive] && ((source_list?(directive) && FETCH_SOURCES.include?(directive)) || nonce_added?(original, additions))
266
278
  if nonce_added?(original, additions)
267
279
  inferred_directive = directive.to_s.gsub(/_nonce/, "_src").to_sym
@@ -1,11 +1,14 @@
1
1
  module SecureHeaders
2
2
  class ReferrerPolicyConfigError < StandardError; end
3
3
  class ReferrerPolicy
4
- HEADER_NAME = "Referrer-Policy"
4
+ HEADER_NAME = "Referrer-Policy".freeze
5
5
  DEFAULT_VALUE = "origin-when-cross-origin"
6
6
  VALID_POLICIES = %w(
7
7
  no-referrer
8
8
  no-referrer-when-downgrade
9
+ same-origin
10
+ strict-origin
11
+ strict-origin-when-cross-origin
9
12
  origin
10
13
  origin-when-cross-origin
11
14
  unsafe-url
@@ -2,7 +2,7 @@ module SecureHeaders
2
2
  class STSConfigError < StandardError; end
3
3
 
4
4
  class StrictTransportSecurity
5
- HEADER_NAME = 'Strict-Transport-Security'
5
+ HEADER_NAME = 'Strict-Transport-Security'.freeze
6
6
  HSTS_MAX_AGE = "631138519"
7
7
  DEFAULT_VALUE = "max-age=" + HSTS_MAX_AGE
8
8
  VALID_STS_HEADER = /\Amax-age=\d+(; includeSubdomains)?(; preload)?\z/i
@@ -2,7 +2,7 @@ module SecureHeaders
2
2
  class XContentTypeOptionsConfigError < StandardError; end
3
3
 
4
4
  class XContentTypeOptions
5
- HEADER_NAME = "X-Content-Type-Options"
5
+ HEADER_NAME = "X-Content-Type-Options".freeze
6
6
  DEFAULT_VALUE = "nosniff"
7
7
  CONFIG_KEY = :x_content_type_options
8
8
 
@@ -1,7 +1,7 @@
1
1
  module SecureHeaders
2
2
  class XDOConfigError < StandardError; end
3
3
  class XDownloadOptions
4
- HEADER_NAME = "X-Download-Options"
4
+ HEADER_NAME = "X-Download-Options".freeze
5
5
  DEFAULT_VALUE = 'noopen'
6
6
  CONFIG_KEY = :x_download_options
7
7
 
@@ -1,7 +1,7 @@
1
1
  module SecureHeaders
2
2
  class XFOConfigError < StandardError; end
3
3
  class XFrameOptions
4
- HEADER_NAME = "X-Frame-Options"
4
+ HEADER_NAME = "X-Frame-Options".freeze
5
5
  CONFIG_KEY = :x_frame_options
6
6
  SAMEORIGIN = "sameorigin"
7
7
  DENY = "deny"
@@ -1,7 +1,7 @@
1
1
  module SecureHeaders
2
2
  class XPCDPConfigError < StandardError; end
3
3
  class XPermittedCrossDomainPolicies
4
- HEADER_NAME = "X-Permitted-Cross-Domain-Policies"
4
+ HEADER_NAME = "X-Permitted-Cross-Domain-Policies".freeze
5
5
  DEFAULT_VALUE = 'none'
6
6
  VALID_POLICIES = %w(all none master-only by-content-type by-ftp-filename)
7
7
  CONFIG_KEY = :x_permitted_cross_domain_policies
@@ -1,7 +1,7 @@
1
1
  module SecureHeaders
2
2
  class XXssProtectionConfigError < StandardError; end
3
3
  class XXssProtection
4
- HEADER_NAME = 'X-XSS-Protection'
4
+ HEADER_NAME = 'X-XSS-Protection'.freeze
5
5
  DEFAULT_VALUE = "1; mode=block"
6
6
  VALID_X_XSS_HEADER = /\A[01](; mode=block)?(; report=.*)?\z/i
7
7
  CONFIG_KEY = :x_xss_protection
@@ -34,6 +34,14 @@ module SecureHeaders
34
34
  end
35
35
  end
36
36
 
37
+ def content_security_policy_script_nonce
38
+ content_security_policy_nonce(:script)
39
+ end
40
+
41
+ def content_security_policy_style_nonce
42
+ content_security_policy_nonce(:style)
43
+ end
44
+
37
45
  ##
38
46
  # Checks to see if the hashed code is expected and adds the hash source
39
47
  # value to the current CSP.
@@ -10,6 +10,7 @@ require "secure_headers/headers/x_content_type_options"
10
10
  require "secure_headers/headers/x_download_options"
11
11
  require "secure_headers/headers/x_permitted_cross_domain_policies"
12
12
  require "secure_headers/headers/referrer_policy"
13
+ require "secure_headers/headers/clear_site_data"
13
14
  require "secure_headers/middleware"
14
15
  require "secure_headers/railtie"
15
16
  require "secure_headers/view_helper"
@@ -47,8 +48,10 @@ module SecureHeaders
47
48
  SECURE_HEADERS_CONFIG = "secure_headers_request_config".freeze
48
49
  NONCE_KEY = "secure_headers_content_security_policy_nonce".freeze
49
50
  HTTPS = "https".freeze
51
+ CSP = ContentSecurityPolicy
50
52
 
51
53
  ALL_HEADER_CLASSES = [
54
+ ClearSiteData,
52
55
  ContentSecurityPolicyConfig,
53
56
  ContentSecurityPolicyReportOnlyConfig,
54
57
  StrictTransportSecurity,
@@ -174,7 +177,7 @@ module SecureHeaders
174
177
 
175
178
  header_classes_for(request).each_with_object({}) do |klass, hash|
176
179
  if header = headers[klass::CONFIG_KEY]
177
- header_name, value = if [ContentSecurityPolicyConfig, ContentSecurityPolicyReportOnlyConfig].include?(klass)
180
+ header_name, value = if klass == ContentSecurityPolicyConfig || klass == ContentSecurityPolicyReportOnlyConfig
178
181
  csp_header_for_ua(header, user_agent)
179
182
  else
180
183
  header
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
3
  gem.name = "secure_headers"
4
- gem.version = "3.5.0.pre"
4
+ gem.version = "3.6.2"
5
5
  gem.authors = ["Neil Matatall"]
6
6
  gem.email = ["neil.matatall@gmail.com"]
7
7
  gem.description = 'Security related headers all in one gem.'