secure_headers 3.6.4 → 3.6.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of secure_headers might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/CHANGELOG.md +5 -1
- data/CONTRIBUTING.md +2 -4
- data/README.md +1 -1
- data/docs/hashes.md +1 -1
- data/lib/secure_headers.rb +2 -1
- data/lib/secure_headers/configuration.rb +20 -3
- data/lib/secure_headers/headers/clear_site_data.rb +12 -9
- data/lib/secure_headers/headers/content_security_policy_config.rb +25 -0
- data/lib/secure_headers/headers/policy_management.rb +3 -3
- data/lib/secure_headers/headers/public_key_pins.rb +1 -1
- data/lib/secure_headers/view_helper.rb +2 -2
- data/secure_headers.gemspec +1 -1
- data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +9 -26
- data/spec/lib/secure_headers/view_helpers_spec.rb +0 -1
- data/spec/lib/secure_headers_spec.rb +4 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17b1294265412b033c265b34e05ce2e4b2655d58
|
4
|
+
data.tar.gz: 2febd47d96fa32beb7b662e7c990f8ac5f3c3f64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cccc0f3501e0ceaa5ce9bed046952d0469c06f74201afbca405686508ec87ca93fbd301018a1134227827af1069b738b6849cc3415cd115810014894fff42e46
|
7
|
+
data.tar.gz: 1e22a0849cfbbe6a24eb3de3026b2b3a0c4c581d0a825abd6d31fcb23df591ff74f563c82101a787596f650b5dd6be4994a1fc3b8ab8a946e2c166f74b86cd85
|
data/.rspec
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
## 3.6.5
|
2
|
+
|
3
|
+
Update clear-site-data header to use current format specified by the specification.
|
4
|
+
|
1
5
|
## 3.6.4
|
2
6
|
|
3
7
|
Fix case where mixing frame-src/child-src dynamically would behave in unexpected ways: https://github.com/twitter/secureheaders/pull/325
|
@@ -196,7 +200,7 @@ You can add hash sources directly to your policy :
|
|
196
200
|
rake secure_headers:generate_hashes
|
197
201
|
```
|
198
202
|
|
199
|
-
This will generate a file (`config/
|
203
|
+
This will generate a file (`config/secure_headers_generated_hashes.yml` by default, you can override by setting `ENV["secure_headers_generated_hashes_file"]`) containing a mapping of file names with the array of hash values found on that page. When ActionView renders a given file, we check if there are any known hashes for that given file. If so, they are added as values to the header.
|
200
204
|
|
201
205
|
```yaml
|
202
206
|
---
|
data/CONTRIBUTING.md
CHANGED
@@ -15,7 +15,7 @@ Please note that this project is released with a [Contributor Code of Conduct][c
|
|
15
15
|
0. Configure and install the dependencies: `bundle install`
|
16
16
|
0. Make sure the tests pass on your machine: `bundle exec rspec spec`
|
17
17
|
0. Create a new branch: `git checkout -b my-branch-name`
|
18
|
-
0. Make your change, add tests, and make sure the tests still pass
|
18
|
+
0. Make your change, add tests, and make sure the tests still pass and that no warnings are raised
|
19
19
|
0. Push to your fork and [submit a pull request][pr]
|
20
20
|
0. Pat your self on the back and wait for your pull request to be reviewed and merged.
|
21
21
|
|
@@ -25,7 +25,7 @@ Here are a few things you can do that will increase the likelihood of your pull
|
|
25
25
|
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
|
26
26
|
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
27
27
|
|
28
|
-
## Releasing
|
28
|
+
## Releasing
|
29
29
|
|
30
30
|
0. Ensure CI is green
|
31
31
|
0. Pull the latest code
|
@@ -39,5 +39,3 @@ Here are a few things you can do that will increase the likelihood of your pull
|
|
39
39
|
|
40
40
|
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
41
41
|
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
42
|
-
|
43
|
-
|
data/README.md
CHANGED
@@ -18,7 +18,7 @@ The gem will automatically apply several headers that are related to security.
|
|
18
18
|
- 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)
|
19
19
|
- Referrer-Policy - [Referrer Policy draft](https://w3c.github.io/webappsec-referrer-policy/)
|
20
20
|
- Public Key Pinning - Pin certificate fingerprints in the browser to prevent man-in-the-middle attacks due to compromised Certificate Authorities. [Public Key Pinning Specification](https://tools.ietf.org/html/rfc7469)
|
21
|
-
- Clear-Site-Data - Clearing browser data for origin. [Clear-Site-Data specification](https://
|
21
|
+
- Clear-Site-Data - Clearing browser data for origin. [Clear-Site-Data specification](https://w3c.github.io/webappsec-clear-site-data/).
|
22
22
|
|
23
23
|
It can also mark all http cookies with the Secure, HttpOnly and SameSite attributes (when configured to do so).
|
24
24
|
|
data/docs/hashes.md
CHANGED
@@ -21,7 +21,7 @@ You can add hash sources directly to your policy :
|
|
21
21
|
rake secure_headers:generate_hashes
|
22
22
|
```
|
23
23
|
|
24
|
-
This will generate a file (`config/
|
24
|
+
This will generate a file (`config/secure_headers_generated_hashes.yml` by default, you can override by setting `ENV["secure_headers_generated_hashes_file"]`) containing a mapping of file names with the array of hash values found on that page. When ActionView renders a given file, we check if there are any known hashes for that given file. If so, they are added as values to the header.
|
25
25
|
|
26
26
|
```yaml
|
27
27
|
---
|
data/lib/secure_headers.rb
CHANGED
@@ -163,7 +163,8 @@ module SecureHeaders
|
|
163
163
|
# returned is meant to be merged into the header value from `@app.call(env)`
|
164
164
|
# in Rack middleware.
|
165
165
|
def header_hash_for(request)
|
166
|
-
|
166
|
+
prevent_dup = true
|
167
|
+
config = config_for(request, prevent_dup)
|
167
168
|
headers = config.cached_headers
|
168
169
|
user_agent = UserAgent.parse(request.user_agent)
|
169
170
|
|
@@ -31,7 +31,7 @@ module SecureHeaders
|
|
31
31
|
raise NotYetConfiguredError, "#{base} policy not yet supplied"
|
32
32
|
end
|
33
33
|
override = @configurations[base].dup
|
34
|
-
override.instance_eval
|
34
|
+
override.instance_eval(&block) if block_given?
|
35
35
|
add_configuration(name, override)
|
36
36
|
end
|
37
37
|
|
@@ -120,19 +120,36 @@ module SecureHeaders
|
|
120
120
|
|
121
121
|
attr_reader :cached_headers, :csp, :cookies, :csp_report_only, :hpkp, :hpkp_report_host
|
122
122
|
|
123
|
+
@script_hashes = nil
|
124
|
+
@style_hashes = nil
|
125
|
+
|
123
126
|
HASH_CONFIG_FILE = ENV["secure_headers_generated_hashes_file"] || "config/secure_headers_generated_hashes.yml"
|
124
|
-
if File.
|
127
|
+
if File.exist?(HASH_CONFIG_FILE)
|
125
128
|
config = YAML.safe_load(File.open(HASH_CONFIG_FILE))
|
126
129
|
@script_hashes = config["scripts"]
|
127
130
|
@style_hashes = config["styles"]
|
128
131
|
end
|
129
132
|
|
130
133
|
def initialize(&block)
|
134
|
+
@cookies = nil
|
135
|
+
@clear_site_data = nil
|
136
|
+
@csp = nil
|
137
|
+
@csp_report_only = nil
|
138
|
+
@hpkp_report_host = nil
|
139
|
+
@hpkp = nil
|
140
|
+
@hsts = nil
|
141
|
+
@x_content_type_options = nil
|
142
|
+
@x_download_options = nil
|
143
|
+
@x_frame_options = nil
|
144
|
+
@x_permitted_cross_domain_policies = nil
|
145
|
+
@x_xss_protection = nil
|
146
|
+
|
131
147
|
self.hpkp = OPT_OUT
|
132
148
|
self.referrer_policy = OPT_OUT
|
133
149
|
self.csp = ContentSecurityPolicyConfig.new(ContentSecurityPolicyConfig::DEFAULT)
|
134
150
|
self.csp_report_only = OPT_OUT
|
135
|
-
|
151
|
+
|
152
|
+
instance_eval(&block) if block_given?
|
136
153
|
end
|
137
154
|
|
138
155
|
# Public: copy everything but the cached headers
|
@@ -2,7 +2,6 @@ module SecureHeaders
|
|
2
2
|
class ClearSiteDataConfigError < StandardError; end
|
3
3
|
class ClearSiteData
|
4
4
|
HEADER_NAME = "Clear-Site-Data".freeze
|
5
|
-
TYPES = "types".freeze
|
6
5
|
|
7
6
|
# Valid `types`
|
8
7
|
CACHE = "cache".freeze
|
@@ -22,9 +21,9 @@ module SecureHeaders
|
|
22
21
|
when nil, OPT_OUT, []
|
23
22
|
# noop
|
24
23
|
when Array
|
25
|
-
[HEADER_NAME,
|
24
|
+
[HEADER_NAME, make_header_value(config)]
|
26
25
|
when true
|
27
|
-
[HEADER_NAME,
|
26
|
+
[HEADER_NAME, make_header_value(ALL_TYPES)]
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
@@ -36,16 +35,20 @@ module SecureHeaders
|
|
36
35
|
unless config.all? { |t| t.is_a?(String) }
|
37
36
|
raise ClearSiteDataConfigError.new("types must be Strings")
|
38
37
|
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
38
|
else
|
46
39
|
raise ClearSiteDataConfigError.new("config must be an Array of Strings or `true`")
|
47
40
|
end
|
48
41
|
end
|
42
|
+
|
43
|
+
# Public: Transform a Clear-Site-Data config (an Array of Strings) into a
|
44
|
+
# String that can be used as the value for the Clear-Site-Data header.
|
45
|
+
#
|
46
|
+
# types - An Array of String of types of data to clear.
|
47
|
+
#
|
48
|
+
# Returns a String of quoted values that are comma separated.
|
49
|
+
def make_header_value(types)
|
50
|
+
types.map { |t| "\"#{t}\""}.join(", ")
|
51
|
+
end
|
49
52
|
end
|
50
53
|
end
|
51
54
|
end
|
@@ -15,6 +15,31 @@ module SecureHeaders
|
|
15
15
|
end
|
16
16
|
|
17
17
|
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
|
+
@object_src = nil
|
31
|
+
@plugin_types = nil
|
32
|
+
@preserve_schemes = nil
|
33
|
+
@reflected_xss = nil
|
34
|
+
@report_only = nil
|
35
|
+
@report_uri = nil
|
36
|
+
@sandbox = nil
|
37
|
+
@script_nonce = nil
|
38
|
+
@script_src = nil
|
39
|
+
@style_nonce = nil
|
40
|
+
@style_src = nil
|
41
|
+
@upgrade_insecure_requests = nil
|
42
|
+
|
18
43
|
from_hash(hash)
|
19
44
|
@modified = false
|
20
45
|
end
|
@@ -348,9 +348,9 @@ module SecureHeaders
|
|
348
348
|
end
|
349
349
|
|
350
350
|
def ensure_valid_sources!(directive, source_expression)
|
351
|
-
source_expression.each do |
|
352
|
-
if ContentSecurityPolicy::DEPRECATED_SOURCE_VALUES.include?(
|
353
|
-
raise ContentSecurityPolicyConfigError.new("#{directive} contains an invalid keyword source (#{
|
351
|
+
source_expression.each do |expression|
|
352
|
+
if ContentSecurityPolicy::DEPRECATED_SOURCE_VALUES.include?(expression)
|
353
|
+
raise ContentSecurityPolicyConfigError.new("#{directive} contains an invalid keyword source (#{expression}). This value must be single quoted.")
|
354
354
|
end
|
355
355
|
end
|
356
356
|
end
|
@@ -95,9 +95,9 @@ module SecureHeaders
|
|
95
95
|
<<-EOF
|
96
96
|
\n\n*** WARNING: Unrecognized hash in #{file_path}!!! Value: #{hash_value} ***
|
97
97
|
#{content}
|
98
|
-
*** Run #{SECURE_HEADERS_RAKE_TASK} or add the following to config/
|
98
|
+
*** Run #{SECURE_HEADERS_RAKE_TASK} or add the following to config/secure_headers_generated_hashes.yml:***
|
99
99
|
#{file_path}:
|
100
|
-
- #{hash_value}\n\n
|
100
|
+
- \"#{hash_value}\"\n\n
|
101
101
|
NOTE: dynamic javascript is not supported using script hash integration
|
102
102
|
on purpose. It defeats the point of using it in the first place.
|
103
103
|
EOF
|
data/secure_headers.gemspec
CHANGED
@@ -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.6.
|
4
|
+
gem.version = "3.6.5"
|
5
5
|
gem.authors = ["Neil Matatall"]
|
6
6
|
gem.email = ["neil.matatall@gmail.com"]
|
7
7
|
gem.description = 'Manages application of security headers with many safe defaults.'
|
@@ -19,30 +19,16 @@ module SecureHeaders
|
|
19
19
|
name, value = described_class.make_header(true)
|
20
20
|
|
21
21
|
expect(name).to eq(ClearSiteData::HEADER_NAME)
|
22
|
-
expect(value).to eq(
|
23
|
-
|
24
|
-
|
25
|
-
"cache",
|
26
|
-
"cookies",
|
27
|
-
"storage",
|
28
|
-
"executionContexts"
|
29
|
-
]
|
30
|
-
}
|
31
|
-
HERE
|
22
|
+
expect(value).to eq(
|
23
|
+
%("cache", "cookies", "storage", "executionContexts")
|
24
|
+
)
|
32
25
|
end
|
33
26
|
|
34
27
|
it "returns specified types" do
|
35
28
|
name, value = described_class.make_header(["foo", "bar"])
|
36
29
|
|
37
30
|
expect(name).to eq(ClearSiteData::HEADER_NAME)
|
38
|
-
expect(value).to eq(
|
39
|
-
{
|
40
|
-
"types": [
|
41
|
-
"foo",
|
42
|
-
"bar"
|
43
|
-
]
|
44
|
-
}
|
45
|
-
HERE
|
31
|
+
expect(value).to eq(%("foo", "bar"))
|
46
32
|
end
|
47
33
|
end
|
48
34
|
|
@@ -83,12 +69,6 @@ module SecureHeaders
|
|
83
69
|
end.to raise_error(ClearSiteDataConfigError)
|
84
70
|
end
|
85
71
|
|
86
|
-
it "fails for non-serializable config" do
|
87
|
-
expect do
|
88
|
-
described_class.validate_config!(["hi \255"])
|
89
|
-
end.to raise_error(ClearSiteDataConfigError)
|
90
|
-
end
|
91
|
-
|
92
72
|
it "fails for other types of config" do
|
93
73
|
expect do
|
94
74
|
described_class.validate_config!(:cookies)
|
@@ -96,8 +76,11 @@ module SecureHeaders
|
|
96
76
|
end
|
97
77
|
end
|
98
78
|
|
99
|
-
|
100
|
-
|
79
|
+
describe "make_header_value" do
|
80
|
+
it "returns a string of quoted values that are comma separated" do
|
81
|
+
value = described_class.make_header_value(["foo", "bar"])
|
82
|
+
expect(value).to eq(%("foo", "bar"))
|
83
|
+
end
|
101
84
|
end
|
102
85
|
end
|
103
86
|
end
|
@@ -105,7 +105,7 @@ module SecureHeaders
|
|
105
105
|
end
|
106
106
|
|
107
107
|
it "produces a UA-specific CSP when overriding (and busting the cache)" do
|
108
|
-
|
108
|
+
Configuration.default do |config|
|
109
109
|
config.csp = {
|
110
110
|
default_src: %w('self'),
|
111
111
|
child_src: %w('self')
|
@@ -231,7 +231,7 @@ module SecureHeaders
|
|
231
231
|
|
232
232
|
SecureHeaders.content_security_policy_script_nonce(request) # should add the value to the header
|
233
233
|
hash = SecureHeaders.header_hash_for(chrome_request)
|
234
|
-
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to match
|
234
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/\Adefault-src 'self'; script-src 'self' 'nonce-.*'\z/)
|
235
235
|
end
|
236
236
|
|
237
237
|
it "appends a hash to a missing script-src value" do
|
@@ -243,7 +243,7 @@ module SecureHeaders
|
|
243
243
|
|
244
244
|
SecureHeaders.append_content_security_policy_directives(request, script_src: %w('sha256-abc123'))
|
245
245
|
hash = SecureHeaders.header_hash_for(chrome_request)
|
246
|
-
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to match
|
246
|
+
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to match(/\Adefault-src 'self'; script-src 'self' 'sha256-abc123'\z/)
|
247
247
|
end
|
248
248
|
|
249
249
|
it "overrides individual directives" do
|
@@ -279,7 +279,7 @@ module SecureHeaders
|
|
279
279
|
end
|
280
280
|
|
281
281
|
safari_request = Rack::Request.new(request.env.merge("HTTP_USER_AGENT" => USER_AGENTS[:safari5]))
|
282
|
-
|
282
|
+
SecureHeaders.content_security_policy_script_nonce(safari_request)
|
283
283
|
hash = SecureHeaders.header_hash_for(safari_request)
|
284
284
|
expect(hash[ContentSecurityPolicyConfig::HEADER_NAME]).to eq("default-src 'self'; script-src mycdn.com 'unsafe-inline'; style-src 'self'")
|
285
285
|
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: 3.6.
|
4
|
+
version: 3.6.5
|
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-
|
11
|
+
date: 2017-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|