secure_headers 6.3.4 → 6.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3953b8d0c4ce0a01012d4d6475ad94c60ce4b06b9d93994ef14cc2474ced6177
4
- data.tar.gz: 3aa96804cacf26d4a275b19e870755313a3afd055b598014749d0338e4f0f4af
3
+ metadata.gz: bafd5e6390db0f1975599ab34f1293986a5c0a1ced14f6cfeca47fd114ce87d4
4
+ data.tar.gz: 177dc27fa8238fe1a8baa238f6baa244b2c03393f61269e9b417e5ea3a4a4bba
5
5
  SHA512:
6
- metadata.gz: b04fd60ff28519273f29d335b81b44ebfe938d4e97d82d31b69d8596dc84e38c812573248e7b70bb142fecebab357c7f34f9f10934edaf354e3174915220580f
7
- data.tar.gz: 10748a3ff12365fffe42828fe324e0bfbb3f146635b095a9e8a13b5a57b5bdfe17a5463df3b5009d8591dbc88494c45036a12dd061fc6b8e921c4c99a0d523ef
6
+ metadata.gz: 7a24f853958892e2780dec3cfd8f631d394c959cda6d3f8be5d701792fc1ce57d4141ca88e7b0980fcac979cc855a0c5bc9165a05b314fe281ce092a438f1fe1
7
+ data.tar.gz: c082a3ee192452712f8e9c7ed18d5c21b5b3eb1712c3d9a4df44220093cf1be09a8e5384f5c8a8909820f1e0048c6d3b6915cc29081288bcf13361bf8cf7dbed
@@ -1,5 +1,5 @@
1
1
  name: Build + Test
2
- on: [pull_request]
2
+ on: [pull_request, push]
3
3
 
4
4
  jobs:
5
5
  build:
@@ -7,7 +7,7 @@ jobs:
7
7
  runs-on: ubuntu-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby: [ '2.5', '2.6', '2.7', '3.0' ]
10
+ ruby: [ '2.6', '2.7', '3.0', '3.1' ]
11
11
 
12
12
  steps:
13
13
  - uses: actions/checkout@v2
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.6.6
1
+ 3.1.1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
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
+
1
9
  ## 6.3.4
2
10
 
3
11
  - CSP: Do not deduplicate alternate schema source expressions (@keithamus): https://github.com/github/secure_headers/pull/478
@@ -84,11 +84,12 @@ module SecureHeaders
84
84
  def deep_copy(config)
85
85
  return unless config
86
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
87
+ hash[key] =
88
+ if value.is_a?(Array)
89
+ value.dup
90
+ else
91
+ value
92
+ end
92
93
  end
93
94
  end
94
95
 
@@ -7,17 +7,18 @@ 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
23
  @preserve_schemes = @config.preserve_schemes
23
24
  @script_nonce = @config.script_nonce
@@ -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,24 +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
- schemes = sources.map { |source| [source, URI(source).scheme] }.to_h
159
- sources.reject do |source|
160
- !wild_sources.include?(source) &&
161
- wild_sources.any? { |pattern| schemes[pattern] == schemes[source] && File.fnmatch(pattern, source) }
162
- end
163
- else
164
- sources
165
- end
166
- end
167
-
168
154
  # Private: append a nonce to the script/style directories if script_nonce
169
155
  # or style_nonce are provided.
170
156
  def populate_nonces(directive, source_list)
@@ -35,6 +35,7 @@ module SecureHeaders
35
35
  @report_only = nil
36
36
  @report_uri = nil
37
37
  @require_sri_for = nil
38
+ @require_trusted_types_for = nil
38
39
  @sandbox = nil
39
40
  @script_nonce = nil
40
41
  @script_src = nil
@@ -44,6 +45,7 @@ module SecureHeaders
44
45
  @style_src = nil
45
46
  @style_src_elem = nil
46
47
  @style_src_attr = nil
48
+ @trusted_types = nil
47
49
  @worker_src = nil
48
50
  @upgrade_insecure_requests = nil
49
51
  @disable_nonce_backwards_compatibility = nil
@@ -98,7 +98,19 @@ module SecureHeaders
98
98
  STYLE_SRC_ATTR
99
99
  ].flatten.freeze
100
100
 
101
- ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0).uniq.sort
101
+ # Experimental directives - these vary greatly in support
102
+ # See MDN for details.
103
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types
104
+ TRUSTED_TYPES = :trusted_types
105
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-trusted-types-for
106
+ REQUIRE_TRUSTED_TYPES_FOR = :require_trusted_types_for
107
+
108
+ DIRECTIVES_EXPERIMENTAL = [
109
+ TRUSTED_TYPES,
110
+ REQUIRE_TRUSTED_TYPES_FOR,
111
+ ].flatten.freeze
112
+
113
+ ALL_DIRECTIVES = (DIRECTIVES_1_0 + DIRECTIVES_2_0 + DIRECTIVES_3_0 + DIRECTIVES_EXPERIMENTAL).uniq.sort
102
114
 
103
115
  # Think of default-src and report-uri as the beginning and end respectively,
104
116
  # everything else is in between.
@@ -121,6 +133,7 @@ module SecureHeaders
121
133
  OBJECT_SRC => :source_list,
122
134
  PLUGIN_TYPES => :media_type_list,
123
135
  REQUIRE_SRI_FOR => :require_sri_for_list,
136
+ REQUIRE_TRUSTED_TYPES_FOR => :require_trusted_types_for_list,
124
137
  REPORT_URI => :source_list,
125
138
  PREFETCH_SRC => :source_list,
126
139
  SANDBOX => :sandbox_list,
@@ -130,6 +143,7 @@ module SecureHeaders
130
143
  STYLE_SRC => :source_list,
131
144
  STYLE_SRC_ELEM => :source_list,
132
145
  STYLE_SRC_ATTR => :source_list,
146
+ TRUSTED_TYPES => :source_list,
133
147
  WORKER_SRC => :source_list,
134
148
  UPGRADE_INSECURE_REQUESTS => :boolean,
135
149
  }.freeze
@@ -175,6 +189,7 @@ module SecureHeaders
175
189
  ].freeze
176
190
 
177
191
  REQUIRE_SRI_FOR_VALUES = Set.new(%w(script style))
192
+ REQUIRE_TRUSTED_TYPES_FOR_VALUES = Set.new(%w('script'))
178
193
 
179
194
  module ClassMethods
180
195
  # Public: generate a header name, value array that is user-agent-aware.
@@ -270,7 +285,8 @@ module SecureHeaders
270
285
  source_list?(directive) ||
271
286
  sandbox_list?(directive) ||
272
287
  media_type_list?(directive) ||
273
- require_sri_for_list?(directive)
288
+ require_sri_for_list?(directive) ||
289
+ require_trusted_types_for_list?(directive)
274
290
  end
275
291
 
276
292
  # For each directive in additions that does not exist in the original config,
@@ -278,11 +294,12 @@ module SecureHeaders
278
294
  def populate_fetch_source_with_default!(original, additions)
279
295
  # in case we would be appending to an empty directive, fill it with the default-src value
280
296
  additions.each_key do |directive|
281
- directive = if directive.to_s.end_with?("_nonce")
282
- directive.to_s.gsub(/_nonce/, "_src").to_sym
283
- else
284
- directive
285
- end
297
+ directive =
298
+ if directive.to_s.end_with?("_nonce")
299
+ directive.to_s.gsub(/_nonce/, "_src").to_sym
300
+ else
301
+ directive
302
+ end
286
303
  # Don't set a default if directive has an existing value
287
304
  next if original[directive]
288
305
  if FETCH_SOURCES.include?(directive)
@@ -307,6 +324,10 @@ module SecureHeaders
307
324
  DIRECTIVE_VALUE_TYPES[directive] == :require_sri_for_list
308
325
  end
309
326
 
327
+ def require_trusted_types_for_list?(directive)
328
+ DIRECTIVE_VALUE_TYPES[directive] == :require_trusted_types_for_list
329
+ end
330
+
310
331
  # Private: Validates that the configuration has a valid type, or that it is a valid
311
332
  # source expression.
312
333
  def validate_directive!(directive, value)
@@ -324,6 +345,8 @@ module SecureHeaders
324
345
  validate_media_type_expression!(directive, value)
325
346
  when :require_sri_for_list
326
347
  validate_require_sri_source_expression!(directive, value)
348
+ when :require_trusted_types_for_list
349
+ validate_require_trusted_types_for_source_expression!(directive, value)
327
350
  else
328
351
  raise ContentSecurityPolicyConfigError.new("Unknown directive #{directive}")
329
352
  end
@@ -368,6 +391,16 @@ module SecureHeaders
368
391
  end
369
392
  end
370
393
 
394
+ # Private: validates that a require trusted types for expression:
395
+ # 1. is an array of strings
396
+ # 2. is a subset of ["'script'"]
397
+ def validate_require_trusted_types_for_source_expression!(directive, require_trusted_types_for_expression)
398
+ ensure_array_of_strings!(directive, require_trusted_types_for_expression)
399
+ unless require_trusted_types_for_expression.to_set.subset?(REQUIRE_TRUSTED_TYPES_FOR_VALUES)
400
+ 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}))
401
+ end
402
+ end
403
+
371
404
  # Private: validates that a source expression:
372
405
  # 1. is an array of strings
373
406
  # 2. does not contain any deprecated, now invalid values (inline, eval, self, none)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SecureHeaders
4
- VERSION = "6.3.4"
4
+ VERSION = "6.5.0"
5
5
  end
@@ -147,12 +147,13 @@ module SecureHeaders
147
147
 
148
148
  def nonced_tag(type, content_or_options, block)
149
149
  options = {}
150
- content = if block
151
- options = content_or_options
152
- capture(&block)
153
- else
154
- content_or_options.html_safe # :'(
155
- end
150
+ content =
151
+ if block
152
+ options = content_or_options
153
+ capture(&block)
154
+ else
155
+ content_or_options.html_safe # :'(
156
+ end
156
157
  content_tag type, content, options.merge(nonce: _content_security_policy_nonce(type))
157
158
  end
158
159
 
@@ -9,12 +9,12 @@ Gem::Specification.new do |gem|
9
9
  gem.version = SecureHeaders::VERSION
10
10
  gem.authors = ["Neil Matatall"]
11
11
  gem.email = ["neil.matatall@gmail.com"]
12
- gem.description = "Manages application of security headers with many safe defaults."
13
- gem.summary = 'Add easily configured security headers to responses
12
+ gem.summary = "Manages application of security headers with many safe defaults."
13
+ gem.description = 'Add easily configured security headers to responses
14
14
  including content-security-policy, x-frame-options,
15
15
  strict-transport-security, etc.'
16
16
  gem.homepage = "https://github.com/twitter/secureheaders"
17
- gem.license = "Apache Public License 2.0"
17
+ gem.license = "MIT"
18
18
  gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
19
19
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
20
20
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
@@ -48,12 +48,12 @@ module SecureHeaders
48
48
  expect(csp.value).to eq("default-src * 'unsafe-inline' 'unsafe-eval' data: blob:")
49
49
  end
50
50
 
51
- it "minifies source expressions based on overlapping wildcards" do
51
+ it "does not minify source expressions based on overlapping wildcards" do
52
52
  config = {
53
53
  default_src: %w(a.example.org b.example.org *.example.org https://*.example.org)
54
54
  }
55
55
  csp = ContentSecurityPolicy.new(config)
56
- expect(csp.value).to eq("default-src *.example.org")
56
+ expect(csp.value).to eq("default-src a.example.org b.example.org *.example.org")
57
57
  end
58
58
 
59
59
  it "removes http/s schemes from hosts" do
@@ -101,8 +101,13 @@ module SecureHeaders
101
101
  expect(csp.value).to eq("default-src example.org; block-all-mixed-content")
102
102
  end
103
103
 
104
- it "deduplicates any source expressions" do
105
- csp = ContentSecurityPolicy.new(default_src: %w(example.org example.org example.org))
104
+ it "handles wildcard subdomain with wildcard port" do
105
+ csp = ContentSecurityPolicy.new(default_src: %w(https://*.example.org:*))
106
+ expect(csp.value).to eq("default-src *.example.org:*")
107
+ end
108
+
109
+ it "deduplicates source expressions that match exactly (after scheme stripping)" do
110
+ csp = ContentSecurityPolicy.new(default_src: %w(example.org https://example.org example.org))
106
111
  expect(csp.value).to eq("default-src example.org")
107
112
  end
108
113
 
@@ -146,6 +151,11 @@ module SecureHeaders
146
151
  expect(csp.value).to eq("default-src 'self'; require-sri-for script style")
147
152
  end
148
153
 
154
+ it "allows style as a require-trusted-types-for source" do
155
+ csp = ContentSecurityPolicy.new(default_src: %w('self'), require_trusted_types_for: %w(script))
156
+ expect(csp.value).to eq("default-src 'self'; require-trusted-types-for script")
157
+ end
158
+
149
159
  it "includes prefetch-src" do
150
160
  csp = ContentSecurityPolicy.new(default_src: %w('self'), prefetch_src: %w(foo.com))
151
161
  expect(csp.value).to eq("default-src 'self'; prefetch-src foo.com")
@@ -185,6 +195,21 @@ module SecureHeaders
185
195
  csp = ContentSecurityPolicy.new({style_src: %w('self'), style_src_attr: %w('self')})
186
196
  expect(csp.value).to eq("style-src 'self'; style-src-attr 'self'")
187
197
  end
198
+
199
+ it "supports trusted-types directive" do
200
+ csp = ContentSecurityPolicy.new({trusted_types: %w(blahblahpolicy)})
201
+ expect(csp.value).to eq("trusted-types blahblahpolicy")
202
+ end
203
+
204
+ it "supports trusted-types directive with 'none'" do
205
+ csp = ContentSecurityPolicy.new({trusted_types: %w('none')})
206
+ expect(csp.value).to eq("trusted-types 'none'")
207
+ end
208
+
209
+ it "allows duplicate policy names in trusted-types directive" do
210
+ csp = ContentSecurityPolicy.new({trusted_types: %w(blahblahpolicy 'allow-duplicates')})
211
+ expect(csp.value).to eq("trusted-types blahblahpolicy 'allow-duplicates'")
212
+ end
188
213
  end
189
214
  end
190
215
  end
@@ -45,6 +45,7 @@ module SecureHeaders
45
45
  plugin_types: %w(application/x-shockwave-flash),
46
46
  prefetch_src: %w(fetch.com),
47
47
  require_sri_for: %w(script style),
48
+ require_trusted_types_for: %w('script'),
48
49
  script_src: %w('self'),
49
50
  style_src: %w('unsafe-inline'),
50
51
  upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
@@ -53,6 +54,7 @@ module SecureHeaders
53
54
  script_src_attr: %w(example.com),
54
55
  style_src_elem: %w(example.com),
55
56
  style_src_attr: %w(example.com),
57
+ trusted_types: %w(abcpolicy),
56
58
 
57
59
  report_uri: %w(https://example.com/uri-directive),
58
60
  }
@@ -120,6 +122,12 @@ module SecureHeaders
120
122
  end.to raise_error(ContentSecurityPolicyConfigError)
121
123
  end
122
124
 
125
+ it "rejects style for trusted types" do
126
+ expect do
127
+ ContentSecurityPolicy.validate_config!(ContentSecurityPolicyConfig.new(default_opts.merge(style_src: %w('self'), require_trusted_types_for: %w(script style), trusted_types: %w(abcpolicy))))
128
+ end
129
+ end
130
+
123
131
  # this is mostly to ensure people don't use the antiquated shorthands common in other configs
124
132
  it "performs light validation on source lists" do
125
133
  expect do
@@ -6,7 +6,7 @@ class Message < ERB
6
6
  include SecureHeaders::ViewHelpers
7
7
 
8
8
  def self.template
9
- <<-TEMPLATE
9
+ <<-TEMPLATE
10
10
  <% hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
11
11
  console.log(1)
12
12
  <% end %>
@@ -62,9 +62,10 @@ TEMPLATE
62
62
  end
63
63
 
64
64
  def content_tag(type, content = nil, options = nil, &block)
65
- content = if block_given?
66
- capture(block)
67
- end
65
+ content =
66
+ if block_given?
67
+ capture(block)
68
+ end
68
69
 
69
70
  if options.is_a?(Hash)
70
71
  options = options.map { |k, v| " #{k}=#{v}" }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secure_headers
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.3.4
4
+ version: 6.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Matatall
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-28 00:00:00.000000000 Z
11
+ date: 2022-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -24,7 +24,10 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- description: Manages application of security headers with many safe defaults.
27
+ description: |-
28
+ Add easily configured security headers to responses
29
+ including content-security-policy, x-frame-options,
30
+ strict-transport-security, etc.
28
31
  email:
29
32
  - neil.matatall@gmail.com
30
33
  executables: []
@@ -99,9 +102,9 @@ files:
99
102
  - spec/spec_helper.rb
100
103
  homepage: https://github.com/twitter/secureheaders
101
104
  licenses:
102
- - Apache Public License 2.0
105
+ - MIT
103
106
  metadata: {}
104
- post_install_message:
107
+ post_install_message:
105
108
  rdoc_options: []
106
109
  require_paths:
107
110
  - lib
@@ -116,11 +119,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
119
  - !ruby/object:Gem::Version
117
120
  version: '0'
118
121
  requirements: []
119
- rubygems_version: 3.0.3.1
120
- signing_key:
122
+ rubygems_version: 3.3.7
123
+ signing_key:
121
124
  specification_version: 4
122
- summary: Add easily configured security headers to responses including content-security-policy,
123
- x-frame-options, strict-transport-security, etc.
125
+ summary: Manages application of security headers with many safe defaults.
124
126
  test_files:
125
127
  - spec/lib/secure_headers/configuration_spec.rb
126
128
  - spec/lib/secure_headers/headers/clear_site_data_spec.rb