secure_headers 6.3.1 → 7.1.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/Gemfile +3 -1
  4. data/README.md +22 -17
  5. data/lib/secure_headers/configuration.rb +11 -7
  6. data/lib/secure_headers/headers/clear_site_data.rb +4 -4
  7. data/lib/secure_headers/headers/content_security_policy.rb +25 -38
  8. data/lib/secure_headers/headers/content_security_policy_config.rb +17 -54
  9. data/lib/secure_headers/headers/cookie.rb +2 -2
  10. data/lib/secure_headers/headers/expect_certificate_transparency.rb +2 -2
  11. data/lib/secure_headers/headers/policy_management.rb +54 -12
  12. data/lib/secure_headers/headers/referrer_policy.rb +1 -1
  13. data/lib/secure_headers/headers/strict_transport_security.rb +1 -1
  14. data/lib/secure_headers/headers/x_content_type_options.rb +1 -1
  15. data/lib/secure_headers/headers/x_download_options.rb +2 -2
  16. data/lib/secure_headers/headers/x_frame_options.rb +1 -1
  17. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +2 -2
  18. data/lib/secure_headers/headers/x_xss_protection.rb +2 -2
  19. data/lib/secure_headers/railtie.rb +5 -5
  20. data/lib/secure_headers/version.rb +1 -1
  21. data/lib/secure_headers/view_helper.rb +7 -6
  22. data/lib/tasks/tasks.rake +6 -7
  23. data/secure_headers.gemspec +17 -7
  24. metadata +22 -67
  25. data/.github/ISSUE_TEMPLATE.md +0 -41
  26. data/.github/PULL_REQUEST_TEMPLATE.md +0 -20
  27. data/.github/workflows/build.yml +0 -24
  28. data/.github/workflows/sync.yml +0 -20
  29. data/.gitignore +0 -13
  30. data/.rspec +0 -3
  31. data/.rubocop.yml +0 -4
  32. data/.ruby-gemset +0 -1
  33. data/.ruby-version +0 -1
  34. data/CODE_OF_CONDUCT.md +0 -46
  35. data/CONTRIBUTING.md +0 -41
  36. data/Guardfile +0 -13
  37. data/Rakefile +0 -32
  38. data/docs/cookies.md +0 -65
  39. data/docs/hashes.md +0 -64
  40. data/docs/named_overrides_and_appends.md +0 -104
  41. data/docs/per_action_configuration.md +0 -141
  42. data/docs/sinatra.md +0 -25
  43. data/docs/upgrading-to-3-0.md +0 -42
  44. data/docs/upgrading-to-4-0.md +0 -35
  45. data/docs/upgrading-to-5-0.md +0 -15
  46. data/docs/upgrading-to-6-0.md +0 -50
  47. data/spec/lib/secure_headers/configuration_spec.rb +0 -121
  48. data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +0 -87
  49. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +0 -165
  50. data/spec/lib/secure_headers/headers/cookie_spec.rb +0 -179
  51. data/spec/lib/secure_headers/headers/expect_certificate_transparency_spec.rb +0 -42
  52. data/spec/lib/secure_headers/headers/policy_management_spec.rb +0 -260
  53. data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +0 -91
  54. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +0 -33
  55. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +0 -31
  56. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +0 -29
  57. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +0 -36
  58. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +0 -48
  59. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +0 -47
  60. data/spec/lib/secure_headers/middleware_spec.rb +0 -117
  61. data/spec/lib/secure_headers/view_helpers_spec.rb +0 -191
  62. data/spec/lib/secure_headers_spec.rb +0 -516
  63. data/spec/spec_helper.rb +0 -64
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b136a1c21b128826c37c798c9e20db99b1d5a5c035001ed8289692ed8f0096f
4
- data.tar.gz: d93c60ba6357a9cd8f64c16b53c9f2843753101e1669fb1bbaea56e549d89466
3
+ metadata.gz: 484062b599a7d8ca3ad93c0b91bd6f88b9f80eb7b3f5106fbb4b94b0ae7a82f9
4
+ data.tar.gz: 68c9dc56b62c0d0c77f166e08ae24e6f13dadcda1a9ebaff8b18e4dad0177fa9
5
5
  SHA512:
6
- metadata.gz: 13083c3da3a4f68d445be6012a1fa6e37c052e6ac6bdc457d379d725383142c8698f91c2d1a6fc75f13bfad52298e291131e9828bb6f6a1fc6b8cba9bc3d5892
7
- data.tar.gz: 92214a6b589ba640e504e58f07b051351a7eea7bce87701730101a68a29045fe81c90d79600f00a36ee7bf3e4c5c7c4071ad74b01bab5e40c53751858bc198d7
6
+ metadata.gz: 63969aa532b3aa321b2e848764e1ddcbbd9e36fc57bd0e2d98bb4f8ede7c94e32ec6d2aef3f81581d99c1bc623c108d159be635e8df238390304077360fa6f9f
7
+ data.tar.gz: fbc1a3a713680ac487ad16185176ebb5cbdce5416bdd9e765faf0757aa0c10a8ef71b825225bfaf40a66c16ce955edacb2deeedc910e14264bd5b8469be8805d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 6.5.0
2
+
3
+ - CSP: Remove source expression deduplication. (@lgarron) https://github.com/github/secure_headers/pull/499
4
+
5
+ ## 6.4.0
6
+
7
+ - CSP: Add support for trusted-types, require-trusted-types-for directive (@JackMc): https://github.com/github/secure_headers/pull/486
8
+
9
+ ## 6.3.4
10
+
11
+ - CSP: Do not deduplicate alternate schema source expressions (@keithamus): https://github.com/github/secure_headers/pull/478
12
+
13
+ ## 6.3.3
14
+
15
+ Fix hash generation for indented helper methods (@rahearn)
16
+
17
+ ## 6.3.2
18
+
19
+ Add support for style-src-attr, style-src-elem, script-src-attr, and script-src-elem directives (@ggalmazor)
20
+
1
21
  ## 6.3.1
2
22
 
3
23
  Fixes deprecation warnings when running under ruby 2.7
data/Gemfile CHANGED
@@ -3,13 +3,15 @@ source "https://rubygems.org"
3
3
 
4
4
  gemspec
5
5
 
6
+ gem "benchmark-ips"
7
+
6
8
  group :test do
7
9
  gem "coveralls"
8
10
  gem "json"
9
11
  gem "pry-nav"
10
12
  gem "rack"
11
13
  gem "rspec"
12
- gem "rubocop", "< 0.68"
14
+ gem "rubocop"
13
15
  gem "rubocop-github"
14
16
  gem "rubocop-performance"
15
17
  gem "term-ansicolor"
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Secure Headers ![Build + Test](https://github.com/github/secure_headers/workflows/Build%20+%20Test/badge.svg?branch=main)
2
2
 
3
- **main branch represents 6.x line**. See the [upgrading to 4.x doc](docs/upgrading-to-4-0.md), [upgrading to 5.x doc](docs/upgrading-to-5-0.md), or [upgrading to 6.x doc](docs/upgrading-to-6-0.md) for instructions on how to upgrade. Bug fixes should go in the 5.x branch for now.
3
+ **main branch represents 7.x line**. See the [upgrading to 4.x doc](docs/upgrading-to-4-0.md), [upgrading to 5.x doc](docs/upgrading-to-5-0.md), [upgrading to 6.x doc](docs/upgrading-to-6-0.md) or [upgrading to 7.x doc](docs/upgrading-to-7-0.md) for instructions on how to upgrade. Bug fixes should go in the `6.x` branch for now.
4
4
 
5
5
  The gem will automatically apply several headers that are related to security. This includes:
6
- - Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 2 Specification](http://www.w3.org/TR/CSP2/)
6
+ - Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 2 Specification](https://www.w3.org/TR/CSP2/)
7
7
  - https://csp.withgoogle.com
8
8
  - https://csp.withgoogle.com/docs/strict-csp.html
9
9
  - https://csp-evaluator.withgoogle.com
@@ -11,11 +11,11 @@ The gem will automatically apply several headers that are related to security.
11
11
  - X-Frame-Options (XFO) - Prevents your content from being framed and potentially clickjacked. [X-Frame-Options Specification](https://tools.ietf.org/html/rfc7034)
12
12
  - X-XSS-Protection - [Cross site scripting heuristic filter for IE/Chrome](https://msdn.microsoft.com/en-us/library/dd565647\(v=vs.85\).aspx)
13
13
  - X-Content-Type-Options - [Prevent content type sniffing](https://msdn.microsoft.com/library/gg622941\(v=vs.85\).aspx)
14
- - X-Download-Options - [Prevent file downloads opening](https://msdn.microsoft.com/library/jj542450(v=vs.85).aspx)
15
- - X-Permitted-Cross-Domain-Policies - [Restrict Adobe Flash Player's access to data](https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html)
16
- - Referrer-Policy - [Referrer Policy draft](https://w3c.github.io/webappsec-referrer-policy/)
17
- - Expect-CT - Only use certificates that are present in the certificate transparency logs. [Expect-CT draft specification](https://datatracker.ietf.org/doc/draft-stark-expect-ct/).
18
- - Clear-Site-Data - Clearing browser data for origin. [Clear-Site-Data specification](https://w3c.github.io/webappsec-clear-site-data/).
14
+ - x-download-options - [Prevent file downloads opening](https://msdn.microsoft.com/library/jj542450(v=vs.85).aspx)
15
+ - x-permitted-cross-domain-policies - [Restrict Adobe Flash Player's access to data](https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html)
16
+ - referrer-policy - [Referrer Policy draft](https://w3c.github.io/webappsec-referrer-policy/)
17
+ - expect-ct - Only use certificates that are present in the certificate transparency logs. [expect-ct draft specification](https://datatracker.ietf.org/doc/draft-stark-expect-ct/).
18
+ - clear-site-data - Clearing browser data for origin. [clear-site-data specification](https://w3c.github.io/webappsec-clear-site-data/).
19
19
 
20
20
  It can also mark all http cookies with the Secure, HttpOnly and SameSite attributes. This is on default but can be turned off by using `config.cookies = SecureHeaders::OPT_OUT`.
21
21
 
@@ -62,7 +62,6 @@ SecureHeaders::Configuration.default do |config|
62
62
  # directive values: these values will directly translate into source directives
63
63
  default_src: %w('none'),
64
64
  base_uri: %w('self'),
65
- block_all_mixed_content: true, # see http://www.w3.org/TR/mixed-content/
66
65
  child_src: %w('self'), # if child-src isn't supported, the value for frame-src will be set.
67
66
  connect_src: %w(wss:),
68
67
  font_src: %w('self' data:),
@@ -75,7 +74,11 @@ SecureHeaders::Configuration.default do |config|
75
74
  sandbox: true, # true and [] will set a maximally restrictive setting
76
75
  plugin_types: %w(application/x-shockwave-flash),
77
76
  script_src: %w('self'),
77
+ script_src_elem: %w('self'),
78
+ script_src_attr: %w('self'),
78
79
  style_src: %w('unsafe-inline'),
80
+ style_src_elem: %w('unsafe-inline'),
81
+ style_src_attr: %w('unsafe-inline'),
79
82
  worker_src: %w('self'),
80
83
  upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
81
84
  report_uri: %w(https://report-uri.io/example-csp)
@@ -88,18 +91,21 @@ SecureHeaders::Configuration.default do |config|
88
91
  end
89
92
  ```
90
93
 
94
+ ### Deprecated Configuration Values
95
+ * `block_all_mixed_content` - this value is deprecated in favor of `upgrade_insecure_requests`. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/block-all-mixed-content for more information.
96
+
91
97
  ## Default values
92
98
 
93
99
  All headers except for PublicKeyPins and ClearSiteData have a default value. The default set of headers is:
94
100
 
95
101
  ```
96
- Content-Security-Policy: default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
97
- Strict-Transport-Security: max-age=631138519
98
- X-Content-Type-Options: nosniff
99
- X-Download-Options: noopen
100
- X-Frame-Options: sameorigin
101
- X-Permitted-Cross-Domain-Policies: none
102
- X-Xss-Protection: 1; mode=block
102
+ content-security-policy: default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
103
+ strict-transport-security: max-age=631138519
104
+ x-content-type-options: nosniff
105
+ x-download-options: noopen
106
+ x-frame-options: sameorigin
107
+ x-permitted-cross-domain-policies: none
108
+ x-xss-protection: 0
103
109
  ```
104
110
 
105
111
  ## API configurations
@@ -165,9 +171,8 @@ If you've made a contribution and see your name missing from the list, make a PR
165
171
  * Rack [rack-secure_headers](https://github.com/frodsan/rack-secure_headers)
166
172
  * Node.js (express) [helmet](https://github.com/helmetjs/helmet) and [hood](https://github.com/seanmonstar/hood)
167
173
  * Node.js (hapi) [blankie](https://github.com/nlf/blankie)
168
- * J2EE Servlet >= 3.0 [headlines](https://github.com/sourceclear/headlines)
169
174
  * ASP.NET - [NWebsec](https://github.com/NWebsec/NWebsec/wiki)
170
- * Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security)
175
+ * Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security), [secure](https://github.com/TypeError/secure)
171
176
  * Go - [secureheader](https://github.com/kr/secureheader)
172
177
  * Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
173
178
  * Dropwizard [dropwizard-web-security](https://github.com/palantir/dropwizard-web-security)
@@ -83,13 +83,17 @@ module SecureHeaders
83
83
  # can lead to modifying parent objects.
84
84
  def deep_copy(config)
85
85
  return unless config
86
- config.each_with_object({}) do |(key, value), hash|
87
- hash[key] = if value.is_a?(Array)
88
- value.dup
89
- else
90
- value
91
- end
86
+ result = {}
87
+ config.each_pair do |key, value|
88
+ result[key] =
89
+ case value
90
+ when Array
91
+ value.dup
92
+ else
93
+ value
94
+ end
92
95
  end
96
+ result
93
97
  end
94
98
 
95
99
  # Private: Returns the internal default configuration. This should only
@@ -252,7 +256,7 @@ module SecureHeaders
252
256
  end
253
257
  end
254
258
 
255
- # Configures the Content-Security-Policy-Report-Only header. `new_csp` cannot
259
+ # Configures the content-security-policy-report-only header. `new_csp` cannot
256
260
  # contain `report_only: false` or an error will be raised.
257
261
  #
258
262
  # NOTE: if csp has not been configured/has the default value when
@@ -2,7 +2,7 @@
2
2
  module SecureHeaders
3
3
  class ClearSiteDataConfigError < StandardError; end
4
4
  class ClearSiteData
5
- HEADER_NAME = "Clear-Site-Data".freeze
5
+ HEADER_NAME = "clear-site-data".freeze
6
6
 
7
7
  # Valid `types`
8
8
  CACHE = "cache".freeze
@@ -12,7 +12,7 @@ module SecureHeaders
12
12
  ALL_TYPES = [CACHE, COOKIES, STORAGE, EXECUTION_CONTEXTS]
13
13
 
14
14
  class << self
15
- # Public: make an Clear-Site-Data header name, value pair
15
+ # Public: make an clear-site-data header name, value pair
16
16
  #
17
17
  # Returns nil if not configured, returns header name and value if configured.
18
18
  def make_header(config = nil, user_agent = nil)
@@ -39,8 +39,8 @@ module SecureHeaders
39
39
  end
40
40
  end
41
41
 
42
- # Public: Transform a Clear-Site-Data config (an Array of Strings) into a
43
- # String that can be used as the value for the Clear-Site-Data header.
42
+ # Public: Transform a clear-site-data config (an Array of Strings) into a
43
+ # String that can be used as the value for the clear-site-data header.
44
44
  #
45
45
  # types - An Array of String of types of data to clear.
46
46
  #
@@ -7,26 +7,27 @@ module SecureHeaders
7
7
  include PolicyManagement
8
8
 
9
9
  def initialize(config = nil)
10
- @config = if config.is_a?(Hash)
11
- if config[:report_only]
12
- ContentSecurityPolicyReportOnlyConfig.new(config || DEFAULT_CONFIG)
10
+ @config =
11
+ if config.is_a?(Hash)
12
+ if config[:report_only]
13
+ ContentSecurityPolicyReportOnlyConfig.new(config || DEFAULT_CONFIG)
14
+ else
15
+ ContentSecurityPolicyConfig.new(config || DEFAULT_CONFIG)
16
+ end
17
+ elsif config.nil?
18
+ ContentSecurityPolicyConfig.new(DEFAULT_CONFIG)
13
19
  else
14
- ContentSecurityPolicyConfig.new(config || DEFAULT_CONFIG)
20
+ config
15
21
  end
16
- elsif config.nil?
17
- ContentSecurityPolicyConfig.new(DEFAULT_CONFIG)
18
- else
19
- config
20
- end
21
22
 
22
- @preserve_schemes = @config.preserve_schemes
23
- @script_nonce = @config.script_nonce
24
- @style_nonce = @config.style_nonce
23
+ @preserve_schemes = @config[:preserve_schemes]
24
+ @script_nonce = @config[:script_nonce]
25
+ @style_nonce = @config[:style_nonce]
25
26
  end
26
27
 
27
28
  ##
28
- # Returns the name to use for the header. Either "Content-Security-Policy" or
29
- # "Content-Security-Policy-Report-Only"
29
+ # Returns the name to use for the header. Either "content-security-policy" or
30
+ # "content-security-policy-report-only"
30
31
  def name
31
32
  @config.class.const_get(:HEADER_NAME)
32
33
  end
@@ -34,11 +35,12 @@ module SecureHeaders
34
35
  ##
35
36
  # Return the value of the CSP header
36
37
  def value
37
- @value ||= if @config
38
- build_value
39
- else
40
- DEFAULT_VALUE
41
- end
38
+ @value ||=
39
+ if @config
40
+ build_value
41
+ else
42
+ DEFAULT_VALUE
43
+ end
42
44
  end
43
45
 
44
46
  private
@@ -51,7 +53,9 @@ module SecureHeaders
51
53
  def build_value
52
54
  directives.map do |directive_name|
53
55
  case DIRECTIVE_VALUE_TYPES[directive_name]
54
- when :source_list, :require_sri_for_list # require_sri is a simple set of strings that don't need to deal with symbol casing
56
+ when :source_list,
57
+ :require_sri_for_list, # require_sri is a simple set of strings that don't need to deal with symbol casing
58
+ :require_trusted_types_for_list
55
59
  build_source_list_directive(directive_name)
56
60
  when :boolean
57
61
  symbol_to_hyphen_case(directive_name) if @config.directive_value(directive_name)
@@ -129,7 +133,7 @@ module SecureHeaders
129
133
  unless directive == REPORT_URI || @preserve_schemes
130
134
  source_list = strip_source_schemes(source_list)
131
135
  end
132
- dedup_source_list(source_list)
136
+ source_list.uniq
133
137
  end
134
138
  end
135
139
 
@@ -147,23 +151,6 @@ module SecureHeaders
147
151
  end
148
152
  end
149
153
 
150
- # Removes duplicates and sources that already match an existing wild card.
151
- #
152
- # e.g. *.github.com asdf.github.com becomes *.github.com
153
- def dedup_source_list(sources)
154
- sources = sources.uniq
155
- wild_sources = sources.select { |source| source =~ STAR_REGEXP }
156
-
157
- if wild_sources.any?
158
- sources.reject do |source|
159
- !wild_sources.include?(source) &&
160
- wild_sources.any? { |pattern| File.fnmatch(pattern, source) }
161
- end
162
- else
163
- sources
164
- end
165
- end
166
-
167
154
  # Private: append a nonce to the script/style directories if script_nonce
168
155
  # or style_nonce are provided.
169
156
  def populate_nonces(directive, source_list)
@@ -1,60 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
  module SecureHeaders
3
3
  module DynamicConfig
4
- def self.included(base)
5
- base.send(:attr_reader, *base.attrs)
6
- base.attrs.each do |attr|
7
- base.send(:define_method, "#{attr}=") do |value|
8
- if self.class.attrs.include?(attr)
9
- write_attribute(attr, value)
10
- else
11
- raise ContentSecurityPolicyConfigError, "Unknown config directive: #{attr}=#{value}"
12
- end
13
- end
14
- end
15
- end
16
-
17
4
  def initialize(hash)
18
- @base_uri = nil
19
- @block_all_mixed_content = nil
20
- @child_src = nil
21
- @connect_src = nil
22
- @default_src = nil
23
- @font_src = nil
24
- @form_action = nil
25
- @frame_ancestors = nil
26
- @frame_src = nil
27
- @img_src = nil
28
- @manifest_src = nil
29
- @media_src = nil
30
- @navigate_to = nil
31
- @object_src = nil
32
- @plugin_types = nil
33
- @prefetch_src = nil
34
- @preserve_schemes = nil
35
- @report_only = nil
36
- @report_uri = nil
37
- @require_sri_for = nil
38
- @sandbox = nil
39
- @script_nonce = nil
40
- @script_src = nil
41
- @style_nonce = nil
42
- @style_src = nil
43
- @worker_src = nil
44
- @upgrade_insecure_requests = nil
45
- @disable_nonce_backwards_compatibility = nil
5
+ @config = {}
46
6
 
47
7
  from_hash(hash)
48
8
  end
49
9
 
10
+ def initialize_copy(hash)
11
+ @config = hash.to_h
12
+ end
13
+
50
14
  def update_directive(directive, value)
51
- self.send("#{directive}=", value)
15
+ @config[directive] = value
52
16
  end
53
17
 
54
18
  def directive_value(directive)
55
- if self.class.attrs.include?(directive)
56
- self.send(directive)
57
- end
19
+ # No need to check attrs, as we only assign valid keys
20
+ @config[directive]
58
21
  end
59
22
 
60
23
  def merge(new_hash)
@@ -72,10 +35,7 @@ module SecureHeaders
72
35
  end
73
36
 
74
37
  def to_h
75
- self.class.attrs.each_with_object({}) do |key, hash|
76
- value = self.send(key)
77
- hash[key] = value unless value.nil?
78
- end
38
+ @config.dup
79
39
  end
80
40
 
81
41
  def dup
@@ -108,16 +68,19 @@ module SecureHeaders
108
68
 
109
69
  def write_attribute(attr, value)
110
70
  value = value.dup if PolicyManagement::DIRECTIVE_VALUE_TYPES[attr] == :source_list
111
- attr_variable = "@#{attr}"
112
- self.instance_variable_set(attr_variable, value)
71
+ if value.nil?
72
+ @config.delete(attr)
73
+ else
74
+ @config[attr] = value
75
+ end
113
76
  end
114
77
  end
115
78
 
116
79
  class ContentSecurityPolicyConfigError < StandardError; end
117
80
  class ContentSecurityPolicyConfig
118
- HEADER_NAME = "Content-Security-Policy".freeze
81
+ HEADER_NAME = "content-security-policy".freeze
119
82
 
120
- ATTRS = PolicyManagement::ALL_DIRECTIVES + PolicyManagement::META_CONFIGS + PolicyManagement::NONCES
83
+ ATTRS = Set.new(PolicyManagement::ALL_DIRECTIVES + PolicyManagement::META_CONFIGS + PolicyManagement::NONCES)
121
84
  def self.attrs
122
85
  ATTRS
123
86
  end
@@ -144,7 +107,7 @@ module SecureHeaders
144
107
  end
145
108
 
146
109
  class ContentSecurityPolicyReportOnlyConfig < ContentSecurityPolicyConfig
147
- HEADER_NAME = "Content-Security-Policy-Report-Only".freeze
110
+ HEADER_NAME = "content-security-policy-report-only".freeze
148
111
 
149
112
  def report_only?
150
113
  true
@@ -80,9 +80,9 @@ module SecureHeaders
80
80
  end
81
81
 
82
82
  def conditionally_flag?(configuration)
83
- if(Array(configuration[:only]).any? && (Array(configuration[:only]) & parsed_cookie.keys).any?)
83
+ if (Array(configuration[:only]).any? && (Array(configuration[:only]) & parsed_cookie.keys).any?)
84
84
  true
85
- elsif(Array(configuration[:except]).any? && (Array(configuration[:except]) & parsed_cookie.keys).none?)
85
+ elsif (Array(configuration[:except]).any? && (Array(configuration[:except]) & parsed_cookie.keys).none?)
86
86
  true
87
87
  else
88
88
  false
@@ -3,14 +3,14 @@ module SecureHeaders
3
3
  class ExpectCertificateTransparencyConfigError < StandardError; end
4
4
 
5
5
  class ExpectCertificateTransparency
6
- HEADER_NAME = "Expect-CT".freeze
6
+ HEADER_NAME = "expect-ct".freeze
7
7
  INVALID_CONFIGURATION_ERROR = "config must be a hash.".freeze
8
8
  INVALID_ENFORCE_VALUE_ERROR = "enforce must be a boolean".freeze
9
9
  REQUIRED_MAX_AGE_ERROR = "max-age is a required directive.".freeze
10
10
  INVALID_MAX_AGE_ERROR = "max-age must be a number.".freeze
11
11
 
12
12
  class << self
13
- # Public: Generate a Expect-CT header.
13
+ # Public: Generate a expect-ct header.
14
14
  #
15
15
  # Returns nil if not configured, returns header name and value if
16
16
  # configured.
@@ -71,26 +71,44 @@ module SecureHeaders
71
71
 
72
72
  # All the directives currently under consideration for CSP level 3.
73
73
  # https://w3c.github.io/webappsec/specs/CSP2/
74
- BLOCK_ALL_MIXED_CONTENT = :block_all_mixed_content
75
74
  MANIFEST_SRC = :manifest_src
76
75
  NAVIGATE_TO = :navigate_to
77
76
  PREFETCH_SRC = :prefetch_src
78
77
  REQUIRE_SRI_FOR = :require_sri_for
79
78
  UPGRADE_INSECURE_REQUESTS = :upgrade_insecure_requests
80
79
  WORKER_SRC = :worker_src
80
+ SCRIPT_SRC_ELEM = :script_src_elem
81
+ SCRIPT_SRC_ATTR = :script_src_attr
82
+ STYLE_SRC_ELEM = :style_src_elem
83
+ STYLE_SRC_ATTR = :style_src_attr
81
84
 
82
85
  DIRECTIVES_3_0 = [
83
86
  DIRECTIVES_2_0,
84
- BLOCK_ALL_MIXED_CONTENT,
85
87
  MANIFEST_SRC,
86
88
  NAVIGATE_TO,
87
89
  PREFETCH_SRC,
88
90
  REQUIRE_SRI_FOR,
89
91
  WORKER_SRC,
90
- UPGRADE_INSECURE_REQUESTS
92
+ UPGRADE_INSECURE_REQUESTS,
93
+ SCRIPT_SRC_ELEM,
94
+ SCRIPT_SRC_ATTR,
95
+ STYLE_SRC_ELEM,
96
+ STYLE_SRC_ATTR
91
97
  ].flatten.freeze
92
98
 
93
- ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0).uniq.sort
99
+ # Experimental directives - these vary greatly in support
100
+ # See MDN for details.
101
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/content-security-policy/trusted-types
102
+ TRUSTED_TYPES = :trusted_types
103
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/content-security-policy/require-trusted-types-for
104
+ REQUIRE_TRUSTED_TYPES_FOR = :require_trusted_types_for
105
+
106
+ DIRECTIVES_EXPERIMENTAL = [
107
+ TRUSTED_TYPES,
108
+ REQUIRE_TRUSTED_TYPES_FOR,
109
+ ].flatten.freeze
110
+
111
+ ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_EXPERIMENTAL).uniq.sort
94
112
 
95
113
  # Think of default-src and report-uri as the beginning and end respectively,
96
114
  # everything else is in between.
@@ -98,7 +116,6 @@ module SecureHeaders
98
116
 
99
117
  DIRECTIVE_VALUE_TYPES = {
100
118
  BASE_URI => :source_list,
101
- BLOCK_ALL_MIXED_CONTENT => :boolean,
102
119
  CHILD_SRC => :source_list,
103
120
  CONNECT_SRC => :source_list,
104
121
  DEFAULT_SRC => :source_list,
@@ -113,11 +130,17 @@ module SecureHeaders
113
130
  OBJECT_SRC => :source_list,
114
131
  PLUGIN_TYPES => :media_type_list,
115
132
  REQUIRE_SRI_FOR => :require_sri_for_list,
133
+ REQUIRE_TRUSTED_TYPES_FOR => :require_trusted_types_for_list,
116
134
  REPORT_URI => :source_list,
117
135
  PREFETCH_SRC => :source_list,
118
136
  SANDBOX => :sandbox_list,
119
137
  SCRIPT_SRC => :source_list,
138
+ SCRIPT_SRC_ELEM => :source_list,
139
+ SCRIPT_SRC_ATTR => :source_list,
120
140
  STYLE_SRC => :source_list,
141
+ STYLE_SRC_ELEM => :source_list,
142
+ STYLE_SRC_ATTR => :source_list,
143
+ TRUSTED_TYPES => :source_list,
121
144
  WORKER_SRC => :source_list,
122
145
  UPGRADE_INSECURE_REQUESTS => :boolean,
123
146
  }.freeze
@@ -163,6 +186,7 @@ module SecureHeaders
163
186
  ].freeze
164
187
 
165
188
  REQUIRE_SRI_FOR_VALUES = Set.new(%w(script style))
189
+ REQUIRE_TRUSTED_TYPES_FOR_VALUES = Set.new(%w('script'))
166
190
 
167
191
  module ClassMethods
168
192
  # Public: generate a header name, value array that is user-agent-aware.
@@ -214,7 +238,7 @@ module SecureHeaders
214
238
  #
215
239
  # raises an error if the original config is OPT_OUT
216
240
  #
217
- # 1. for non-source-list values (report_only, block_all_mixed_content, upgrade_insecure_requests),
241
+ # 1. for non-source-list values (report_only, upgrade_insecure_requests),
218
242
  # additions will overwrite the original value.
219
243
  # 2. if a value in additions does not exist in the original config, the
220
244
  # default-src value is included to match original behavior.
@@ -258,7 +282,8 @@ module SecureHeaders
258
282
  source_list?(directive) ||
259
283
  sandbox_list?(directive) ||
260
284
  media_type_list?(directive) ||
261
- require_sri_for_list?(directive)
285
+ require_sri_for_list?(directive) ||
286
+ require_trusted_types_for_list?(directive)
262
287
  end
263
288
 
264
289
  # For each directive in additions that does not exist in the original config,
@@ -266,11 +291,12 @@ module SecureHeaders
266
291
  def populate_fetch_source_with_default!(original, additions)
267
292
  # in case we would be appending to an empty directive, fill it with the default-src value
268
293
  additions.each_key do |directive|
269
- directive = if directive.to_s.end_with?("_nonce")
270
- directive.to_s.gsub(/_nonce/, "_src").to_sym
271
- else
272
- directive
273
- end
294
+ directive =
295
+ if directive.to_s.end_with?("_nonce")
296
+ directive.to_s.gsub(/_nonce/, "_src").to_sym
297
+ else
298
+ directive
299
+ end
274
300
  # Don't set a default if directive has an existing value
275
301
  next if original[directive]
276
302
  if FETCH_SOURCES.include?(directive)
@@ -295,6 +321,10 @@ module SecureHeaders
295
321
  DIRECTIVE_VALUE_TYPES[directive] == :require_sri_for_list
296
322
  end
297
323
 
324
+ def require_trusted_types_for_list?(directive)
325
+ DIRECTIVE_VALUE_TYPES[directive] == :require_trusted_types_for_list
326
+ end
327
+
298
328
  # Private: Validates that the configuration has a valid type, or that it is a valid
299
329
  # source expression.
300
330
  def validate_directive!(directive, value)
@@ -312,6 +342,8 @@ module SecureHeaders
312
342
  validate_media_type_expression!(directive, value)
313
343
  when :require_sri_for_list
314
344
  validate_require_sri_source_expression!(directive, value)
345
+ when :require_trusted_types_for_list
346
+ validate_require_trusted_types_for_source_expression!(directive, value)
315
347
  else
316
348
  raise ContentSecurityPolicyConfigError.new("Unknown directive #{directive}")
317
349
  end
@@ -356,6 +388,16 @@ module SecureHeaders
356
388
  end
357
389
  end
358
390
 
391
+ # Private: validates that a require trusted types for expression:
392
+ # 1. is an array of strings
393
+ # 2. is a subset of ["'script'"]
394
+ def validate_require_trusted_types_for_source_expression!(directive, require_trusted_types_for_expression)
395
+ ensure_array_of_strings!(directive, require_trusted_types_for_expression)
396
+ unless require_trusted_types_for_expression.to_set.subset?(REQUIRE_TRUSTED_TYPES_FOR_VALUES)
397
+ raise ContentSecurityPolicyConfigError.new(%(require-trusted-types-for for must be a subset of #{REQUIRE_TRUSTED_TYPES_FOR_VALUES.to_a} but was #{require_trusted_types_for_expression}))
398
+ end
399
+ end
400
+
359
401
  # Private: validates that a source expression:
360
402
  # 1. is an array of strings
361
403
  # 2. does not contain any deprecated, now invalid values (inline, eval, self, none)
@@ -2,7 +2,7 @@
2
2
  module SecureHeaders
3
3
  class ReferrerPolicyConfigError < StandardError; end
4
4
  class ReferrerPolicy
5
- HEADER_NAME = "Referrer-Policy".freeze
5
+ HEADER_NAME = "referrer-policy".freeze
6
6
  DEFAULT_VALUE = "origin-when-cross-origin"
7
7
  VALID_POLICIES = %w(
8
8
  no-referrer
@@ -3,7 +3,7 @@ module SecureHeaders
3
3
  class STSConfigError < StandardError; end
4
4
 
5
5
  class StrictTransportSecurity
6
- HEADER_NAME = "Strict-Transport-Security".freeze
6
+ HEADER_NAME = "strict-transport-security".freeze
7
7
  HSTS_MAX_AGE = "631138519"
8
8
  DEFAULT_VALUE = "max-age=" + HSTS_MAX_AGE
9
9
  VALID_STS_HEADER = /\Amax-age=\d+(; includeSubdomains)?(; preload)?\z/i
@@ -3,7 +3,7 @@ module SecureHeaders
3
3
  class XContentTypeOptionsConfigError < StandardError; end
4
4
 
5
5
  class XContentTypeOptions
6
- HEADER_NAME = "X-Content-Type-Options".freeze
6
+ HEADER_NAME = "x-content-type-options".freeze
7
7
  DEFAULT_VALUE = "nosniff"
8
8
 
9
9
  class << self
@@ -2,11 +2,11 @@
2
2
  module SecureHeaders
3
3
  class XDOConfigError < StandardError; end
4
4
  class XDownloadOptions
5
- HEADER_NAME = "X-Download-Options".freeze
5
+ HEADER_NAME = "x-download-options".freeze
6
6
  DEFAULT_VALUE = "noopen"
7
7
 
8
8
  class << self
9
- # Public: generate an X-Download-Options header.
9
+ # Public: generate an x-download-options header.
10
10
  #
11
11
  # Returns a default header if no configuration is provided, or a
12
12
  # header name and value based on the config.