secure_headers 5.0.4 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d30a295775010a197058029cc447bdf51ad1f93e
4
- data.tar.gz: 79a4928de6fe2508ebdfed92cfe36ba1d70cdce8
3
+ metadata.gz: 1b901bf6e9f4127c81ec207e46599054feb18f32
4
+ data.tar.gz: 642585cfc04901c83ff5d90ea1600f5ef141fec4
5
5
  SHA512:
6
- metadata.gz: 206573552433a49370adf984f538091ac3bd4c2e6ee0ba521f949762e145259725a3938ab2ec5124b5556fdd1502388ac8dd7e8d5fcc947c9f96e3ed78947cfa
7
- data.tar.gz: c53aab77376ad2d8fac21c2d684cc02dcf14322cd51591d7048f0865315a60b6266ce7f066651040b8d626adf311101e31b2b1690ce1e3b18190ea42f90f24ac
6
+ metadata.gz: 8e304a7d52e6dbf0a4fd2bb114201ba781e8565fd9ecbd4977e6169f922101f186c7024194c81ecd780376f2e4c18f7af3bd88f98cd2b86301b15de5e4148c44
7
+ data.tar.gz: 9d5e1e8f35954baea9e4fcec70af22e7f3c2d8b16deaa98d6c882c9373c9a76556a711696b081920d9279df96375dd6e2a473e265c9bfbf2e12a6f9a62da486f
data/.travis.yml CHANGED
@@ -2,9 +2,9 @@ language: ruby
2
2
 
3
3
  rvm:
4
4
  - ruby-head
5
- - 2.4.2
6
- - 2.3.5
7
- - 2.2.8
5
+ - 2.6
6
+ - 2.5
7
+ - 2.4
8
8
  - jruby-head
9
9
 
10
10
  env:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 5.2.0
2
+
3
+ Fixes newline injection issue
4
+
5
+ ## 5.1.0
6
+
7
+ Fixes semicolon injection issue reported by @mvgijssel see https://github.com/twitter/secure_headers/issues/418
8
+
9
+ ## 5.0.5
10
+
11
+ A release to deprecate `SecureHeaders::Configuration#get` in prep for 6.x
12
+
1
13
  ## 5.0.4
2
14
 
3
15
  - Adds support for `nonced_stylesheet_pack_tag` #373 (@paulfri)
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ group :test do
9
9
  gem "pry-nav"
10
10
  gem "rack"
11
11
  gem "rspec"
12
- gem "rubocop"
12
+ gem "rubocop", "< 0.68"
13
13
  gem "rubocop-github"
14
14
  gem "term-ansicolor"
15
15
  gem "tins"
@@ -19,4 +19,5 @@ group :guard do
19
19
  gem "growl"
20
20
  gem "guard-rspec", platforms: [:ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24]
21
21
  gem "rb-fsevent"
22
+ gem "terminal-notifier-guard"
22
23
  end
@@ -196,7 +196,7 @@ module SecureHeaders
196
196
  #
197
197
  # name - the name of the previously configured override.
198
198
  def use_secure_headers_override(request, name)
199
- if config = Configuration.get(name)
199
+ if config = Configuration.get(name, internal: true)
200
200
  override_secure_headers_request_config(request, config)
201
201
  else
202
202
  raise ArgumentError.new("no override by the name of #{name} has been configured")
@@ -228,7 +228,7 @@ module SecureHeaders
228
228
  # Falls back to the global config
229
229
  def config_for(request, prevent_dup = false)
230
230
  config = request.env[SECURE_HEADERS_CONFIG] ||
231
- Configuration.get(Configuration::DEFAULT_CONFIG)
231
+ Configuration.get(Configuration::DEFAULT_CONFIG, internal: true)
232
232
 
233
233
 
234
234
  # Global configs are frozen, per-request configs are not. When we're not
@@ -28,7 +28,7 @@ module SecureHeaders
28
28
  #
29
29
  # Returns: the newly created config
30
30
  def override(name, base = DEFAULT_CONFIG, &block)
31
- unless get(base)
31
+ unless get(base, internal: true)
32
32
  raise NotYetConfiguredError, "#{base} policy not yet supplied"
33
33
  end
34
34
  override = @configurations[base].dup
@@ -40,7 +40,11 @@ module SecureHeaders
40
40
  #
41
41
  # Returns the configuration with a given name or raises a
42
42
  # NotYetConfiguredError if `default` has not been called.
43
- def get(name = DEFAULT_CONFIG)
43
+ def get(name = DEFAULT_CONFIG, internal: false)
44
+ unless internal
45
+ Kernel.warn "#{Kernel.caller.first}: [DEPRECATION] `#get` is deprecated. It will be removed in the next major release. Use SecureHeaders::Configuration.dup to retrieve the default config."
46
+ end
47
+
44
48
  if @configurations.nil?
45
49
  raise NotYetConfiguredError, "Default policy not yet supplied"
46
50
  end
@@ -48,7 +48,7 @@ module SecureHeaders
48
48
  #
49
49
  # Returns a String of quoted values that are comma separated.
50
50
  def make_header_value(types)
51
- types.map { |t| "\"#{t}\""}.join(", ")
51
+ types.map { |t| %("#{t}") }.join(", ")
52
52
  end
53
53
  end
54
54
  end
@@ -138,8 +138,14 @@ module SecureHeaders
138
138
  end
139
139
 
140
140
  if source_list != OPT_OUT && source_list && source_list.any?
141
- normalized_source_list = minify_source_list(directive, source_list)
142
- [symbol_to_hyphen_case(directive), normalized_source_list].join(" ")
141
+ minified_source_list = minify_source_list(directive, source_list).join(" ")
142
+
143
+ if minified_source_list =~ /(\n|;)/
144
+ Kernel.warn("#{directive} contains a #{$1} in #{minified_source_list.inspect} which will raise an error in future versions. It has been replaced with a blank space.")
145
+ end
146
+
147
+ escaped_source_list = minified_source_list.gsub(/[\n;]/, " ")
148
+ [symbol_to_hyphen_case(directive), escaped_source_list].join(" ").strip
143
149
  end
144
150
  end
145
151
 
@@ -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.0.4"
5
+ gem.version = "5.2.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."
@@ -17,13 +17,4 @@ Gem::Specification.new do |gem|
17
17
  gem.require_paths = ["lib"]
18
18
  gem.add_development_dependency "rake"
19
19
  gem.add_dependency "useragent", ">= 0.15.0"
20
-
21
- # TODO: delete this after 4.1 is cut or a number of 4.0.x releases have occurred
22
- gem.post_install_message = <<-POST_INSTALL
23
-
24
- **********
25
- :wave: secure_headers 5.0 introduces a lot of breaking changes (in the name of security!). It's highly likely you will need to update your secure_headers cookie configuration to avoid breaking things. See the upgrade guide for details: https://github.com/twitter/secureheaders/blob/master/docs/upgrading-to-5-0.md
26
- **********
27
-
28
- POST_INSTALL
29
20
  end
@@ -9,15 +9,20 @@ module SecureHeaders
9
9
  end
10
10
 
11
11
  it "has a default config" do
12
- expect(Configuration.get(Configuration::DEFAULT_CONFIG)).to_not be_nil
12
+ expect(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true)).to_not be_nil
13
+ end
14
+
15
+ it "warns when using deprecated internal-ish #get API" do
16
+ expect(Kernel).to receive(:warn).once.with(/`#get` is deprecated/)
17
+ Configuration.get(Configuration::DEFAULT_CONFIG)
13
18
  end
14
19
 
15
20
  it "has an 'noop' config" do
16
- expect(Configuration.get(Configuration::NOOP_CONFIGURATION)).to_not be_nil
21
+ expect(Configuration.get(Configuration::NOOP_CONFIGURATION, internal: true)).to_not be_nil
17
22
  end
18
23
 
19
24
  it "precomputes headers upon creation" do
20
- default_config = Configuration.get(Configuration::DEFAULT_CONFIG)
25
+ default_config = Configuration.get(Configuration::DEFAULT_CONFIG, internal: true)
21
26
  header_hash = default_config.cached_headers.each_with_object({}) do |(key, value), hash|
22
27
  header_name, header_value = if key == :csp
23
28
  value["Chrome"]
@@ -35,8 +40,8 @@ module SecureHeaders
35
40
  # do nothing, just copy it
36
41
  end
37
42
 
38
- config = Configuration.get(:test_override)
39
- noop = Configuration.get(Configuration::NOOP_CONFIGURATION)
43
+ config = Configuration.get(:test_override, internal: true)
44
+ noop = Configuration.get(Configuration::NOOP_CONFIGURATION, internal: true)
40
45
  [:csp, :csp_report_only, :cookies].each do |key|
41
46
  expect(config.send(key)).to eq(noop.send(key))
42
47
  end
@@ -47,7 +52,7 @@ module SecureHeaders
47
52
  config.x_content_type_options = OPT_OUT
48
53
  end
49
54
 
50
- expect(Configuration.get.cached_headers).to_not eq(Configuration.get(:test_override).cached_headers)
55
+ expect(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).cached_headers).to_not eq(Configuration.get(:test_override, internal: true).cached_headers)
51
56
  end
52
57
 
53
58
  it "stores an override of the global config" do
@@ -55,7 +60,7 @@ module SecureHeaders
55
60
  config.x_frame_options = "DENY"
56
61
  end
57
62
 
58
- expect(Configuration.get(:test_override)).to_not be_nil
63
+ expect(Configuration.get(:test_override, internal: true)).to_not be_nil
59
64
  end
60
65
 
61
66
  it "deep dup's config values when overriding so the original cannot be modified" do
@@ -63,8 +68,8 @@ module SecureHeaders
63
68
  config.csp[:default_src] << "'self'"
64
69
  end
65
70
 
66
- default = Configuration.get
67
- override = Configuration.get(:override)
71
+ default = Configuration.get(Configuration::DEFAULT_CONFIG, internal: true)
72
+ override = Configuration.get(:override, internal: true)
68
73
 
69
74
  expect(override.csp.directive_value(:default_src)).not_to be(default.csp.directive_value(:default_src))
70
75
  end
@@ -78,9 +83,9 @@ module SecureHeaders
78
83
  config.csp = config.csp.merge(script_src: %w(example.org))
79
84
  end
80
85
 
81
- original_override = Configuration.get(:override)
86
+ original_override = Configuration.get(:override, internal: true)
82
87
  expect(original_override.csp.to_h).to eq(default_src: %w('self'), script_src: %w('self'))
83
- override_config = Configuration.get(:second_override)
88
+ override_config = Configuration.get(:second_override, internal: true)
84
89
  expect(override_config.csp.to_h).to eq(default_src: %w('self'), script_src: %w('self' example.org))
85
90
  end
86
91
 
@@ -101,7 +106,7 @@ module SecureHeaders
101
106
  config.cookies = OPT_OUT
102
107
  end
103
108
 
104
- config = Configuration.get
109
+ config = Configuration.get(Configuration::DEFAULT_CONFIG, internal: true)
105
110
  expect(config.cookies).to eq(OPT_OUT)
106
111
  end
107
112
 
@@ -110,7 +115,7 @@ module SecureHeaders
110
115
  config.cookies = {httponly: true, secure: true, samesite: {lax: false}}
111
116
  end
112
117
 
113
- config = Configuration.get
118
+ config = Configuration.get(Configuration::DEFAULT_CONFIG, internal: true)
114
119
  expect(config.cookies).to eq({httponly: true, secure: true, samesite: {lax: false}})
115
120
  end
116
121
  end
@@ -28,6 +28,16 @@ module SecureHeaders
28
28
  expect(ContentSecurityPolicy.new.value).to eq("default-src https:; form-action 'self'; img-src https: data: 'self'; object-src 'none'; script-src https:; style-src 'self' 'unsafe-inline' https:")
29
29
  end
30
30
 
31
+ it "deprecates and escapes semicolons in directive source lists" do
32
+ expect(Kernel).to receive(:warn).with(%(frame_ancestors contains a ; in "google.com;script-src *;.;" which will raise an error in future versions. It has been replaced with a blank space.))
33
+ expect(ContentSecurityPolicy.new(frame_ancestors: %w(https://google.com;script-src https://*;.;)).value).to eq("frame-ancestors google.com script-src * .")
34
+ end
35
+
36
+ it "deprecates and escapes semicolons in directive source lists" do
37
+ expect(Kernel).to receive(:warn).with(%(frame_ancestors contains a \n in "\\nfoo.com\\nhacked" which will raise an error in future versions. It has been replaced with a blank space.))
38
+ expect(ContentSecurityPolicy.new(frame_ancestors: ["\nfoo.com\nhacked"]).value).to eq("frame-ancestors foo.com hacked")
39
+ end
40
+
31
41
  it "discards 'none' values if any other source expressions are present" do
32
42
  csp = ContentSecurityPolicy.new(default_opts.merge(child_src: %w('self' 'none')))
33
43
  expect(csp.value).not_to include("'none'")
@@ -152,7 +152,7 @@ module SecureHeaders
152
152
  script_src: %w('self'),
153
153
  }
154
154
  end
155
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, style_src: %w(anothercdn.com))
155
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, style_src: %w(anothercdn.com))
156
156
  csp = ContentSecurityPolicy.new(combined_config)
157
157
  expect(csp.name).to eq(ContentSecurityPolicyConfig::HEADER_NAME)
158
158
  expect(csp.value).to eq("default-src https:; script-src 'self'; style-src https: anothercdn.com")
@@ -167,7 +167,7 @@ module SecureHeaders
167
167
  }.freeze
168
168
  end
169
169
  report_uri = "https://report-uri.io/asdf"
170
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, report_uri: [report_uri])
170
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, report_uri: [report_uri])
171
171
  csp = ContentSecurityPolicy.new(combined_config, USER_AGENTS[:firefox])
172
172
  expect(csp.value).to include("report-uri #{report_uri}")
173
173
  end
@@ -183,7 +183,7 @@ module SecureHeaders
183
183
  non_default_source_additions = ContentSecurityPolicy::NON_FETCH_SOURCES.each_with_object({}) do |directive, hash|
184
184
  hash[directive] = %w("http://example.org)
185
185
  end
186
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, non_default_source_additions)
186
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, non_default_source_additions)
187
187
 
188
188
  ContentSecurityPolicy::NON_FETCH_SOURCES.each do |directive|
189
189
  expect(combined_config[directive]).to eq(%w("http://example.org))
@@ -198,7 +198,7 @@ module SecureHeaders
198
198
  report_only: false
199
199
  }
200
200
  end
201
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, report_only: true)
201
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, report_only: true)
202
202
  csp = ContentSecurityPolicy.new(combined_config, USER_AGENTS[:firefox])
203
203
  expect(csp.name).to eq(ContentSecurityPolicyReportOnlyConfig::HEADER_NAME)
204
204
  end
@@ -211,7 +211,7 @@ module SecureHeaders
211
211
  block_all_mixed_content: false
212
212
  }
213
213
  end
214
- combined_config = ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, block_all_mixed_content: true)
214
+ combined_config = ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, block_all_mixed_content: true)
215
215
  csp = ContentSecurityPolicy.new(combined_config)
216
216
  expect(csp.value).to eq("default-src https:; block-all-mixed-content; script-src 'self'")
217
217
  end
@@ -221,7 +221,7 @@ module SecureHeaders
221
221
  config.csp = OPT_OUT
222
222
  end
223
223
  expect do
224
- ContentSecurityPolicy.combine_policies(Configuration.get.csp.to_h, script_src: %w(anothercdn.com))
224
+ ContentSecurityPolicy.combine_policies(Configuration.get(Configuration::DEFAULT_CONFIG, internal: true).csp.to_h, script_src: %w(anothercdn.com))
225
225
  end.to raise_error(ContentSecurityPolicyConfigError)
226
226
  end
227
227
  end
@@ -50,7 +50,7 @@ module SecureHeaders
50
50
  end
51
51
  request = Rack::Request.new({})
52
52
  SecureHeaders.use_secure_headers_override(request, "my_custom_config")
53
- expect(request.env[SECURE_HEADERS_CONFIG]).to be(Configuration.get("my_custom_config"))
53
+ expect(request.env[SECURE_HEADERS_CONFIG]).to be(Configuration.get("my_custom_config", internal: true))
54
54
  _, env = middleware.call request.env
55
55
  expect(env[ContentSecurityPolicyConfig::HEADER_NAME]).to match("example.org")
56
56
  end
@@ -66,7 +66,7 @@ module SecureHeaders
66
66
  end
67
67
 
68
68
  it "allows opting out of cookie protection with OPT_OUT alone" do
69
- Configuration.default { |config| config.cookies = OPT_OUT}
69
+ Configuration.default { |config| config.cookies = OPT_OUT }
70
70
 
71
71
  # do NOT make this request https. non-https requests modify a config,
72
72
  # causing an exception when operating on OPT_OUT. This ensures we don't
@@ -67,7 +67,7 @@ TEMPLATE
67
67
  end
68
68
 
69
69
  if options.is_a?(Hash)
70
- options = options.map {|k, v| " #{k}=#{v}"}
70
+ options = options.map { |k, v| " #{k}=#{v}" }
71
71
  end
72
72
  "<#{type}#{options}>#{content}</#{type}>"
73
73
  end
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: 5.0.4
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Matatall
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-05 00:00:00.000000000 Z
11
+ date: 2020-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -114,12 +114,7 @@ homepage: https://github.com/twitter/secureheaders
114
114
  licenses:
115
115
  - Apache Public License 2.0
116
116
  metadata: {}
117
- post_install_message: |2+
118
-
119
- **********
120
- :wave: secure_headers 5.0 introduces a lot of breaking changes (in the name of security!). It's highly likely you will need to update your secure_headers cookie configuration to avoid breaking things. See the upgrade guide for details: https://github.com/twitter/secureheaders/blob/master/docs/upgrading-to-5-0.md
121
- **********
122
-
117
+ post_install_message:
123
118
  rdoc_options: []
124
119
  require_paths:
125
120
  - lib