secure_headers 3.0.3 → 3.1.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.
- checksums.yaml +4 -4
- data/.gitignore +0 -9
- data/.travis.yml +13 -5
- data/CHANGELOG.md +27 -0
- data/Gemfile +5 -2
- data/README.md +10 -42
- data/lib/secure_headers/configuration.rb +94 -54
- data/lib/secure_headers/headers/content_security_policy.rb +31 -309
- data/lib/secure_headers/headers/policy_management.rb +320 -0
- data/lib/secure_headers/headers/x_content_type_options.rb +1 -1
- data/lib/secure_headers/middleware.rb +23 -0
- data/lib/secure_headers/railtie.rb +1 -1
- data/lib/secure_headers.rb +37 -63
- data/secure_headers.gemspec +1 -1
- data/spec/lib/secure_headers/configuration_spec.rb +10 -4
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +0 -175
- data/spec/lib/secure_headers/headers/policy_management_spec.rb +190 -0
- data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +1 -1
- data/spec/lib/secure_headers/middleware_spec.rb +23 -4
- data/spec/lib/secure_headers_spec.rb +119 -42
- data/spec/spec_helper.rb +4 -1
- metadata +5 -4
- data/lib/secure_headers/padrino.rb +0 -13
- data/travis.sh +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ebe74bc751469bb4305dd36b59fe4881a87ec3d
|
|
4
|
+
data.tar.gz: 83fed6de7e4cab010cab6010caa0ba005ac43fc7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bd02880ba737a5a9489dd6e8209420259fec8d78330f5992e7b34f61ebe16677a93ff123dd3b417f6973bfcd85e03524b053bcd1487458492aa4c7b8a7a9bb40
|
|
7
|
+
data.tar.gz: 550c48cfd47e656e70dd3ab39ed6386b8d148231d55a553395da6076bc88bdaaa7fe1803695b093cf3e4fd49cb30bed0aff15a2455652efd00d7a27d9ce55636
|
data/.gitignore
CHANGED
|
@@ -6,17 +6,8 @@
|
|
|
6
6
|
.yardoc
|
|
7
7
|
*.log
|
|
8
8
|
Gemfile.lock
|
|
9
|
-
InstalledFiles
|
|
10
9
|
_yardoc
|
|
11
10
|
coverage
|
|
12
|
-
doc/
|
|
13
|
-
lib/bundler/man
|
|
14
11
|
pkg
|
|
15
12
|
rdoc
|
|
16
13
|
spec/reports
|
|
17
|
-
test/tmp
|
|
18
|
-
test/version_tmp
|
|
19
|
-
*tmp
|
|
20
|
-
*.sqlite3
|
|
21
|
-
fixtures/rails_3_2_12_no_init/log
|
|
22
|
-
fixtures/rails_3_2_12/log
|
data/.travis.yml
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
language: ruby
|
|
2
2
|
|
|
3
3
|
rvm:
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
4
|
+
- ruby-head
|
|
5
|
+
- 2.2
|
|
6
|
+
- 2.1
|
|
7
|
+
- 2.0.0
|
|
8
|
+
- 1.9.3
|
|
9
|
+
- jruby-19mode
|
|
10
|
+
- jruby-head
|
|
11
|
+
|
|
12
|
+
matrix:
|
|
13
|
+
allow_failures:
|
|
14
|
+
- rvm: jruby-head
|
|
15
|
+
- rvm: ruby-head
|
|
9
16
|
|
|
10
17
|
before_install: gem update bundler
|
|
18
|
+
bundler_args: --without guard -j 3
|
|
11
19
|
|
|
12
20
|
sudo: false
|
|
13
21
|
cache: bundler
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## 3.1.2 Bug fix for regression
|
|
2
|
+
|
|
3
|
+
See https://github.com/twitter/secureheaders/pull/239
|
|
4
|
+
|
|
5
|
+
This meant that when header caches were regenerated upon calling `SecureHeaders.override(:name)` and using it with `use_secure_headers_override` would result in default values for anything other than CSP/HPKP.
|
|
6
|
+
|
|
7
|
+
## 3.1.1 Bug fix for regression
|
|
8
|
+
|
|
9
|
+
See https://github.com/twitter/secureheaders/pull/235
|
|
10
|
+
|
|
11
|
+
`idempotent_additions?` would return false when comparing `OPT_OUT` with `OPT_OUT`, causing `header_hash_for` to return a header cache with `{ nil => nil }` which cause the middleware to blow up when `{ nil => nil }` was merged into the rack header hash.
|
|
12
|
+
|
|
13
|
+
This is a regression in 3.1.0 only.
|
|
14
|
+
|
|
15
|
+
Now it returns true. I've added a test case to ensure that `header_hash_for` will never return such an element.
|
|
16
|
+
|
|
17
|
+
## 3.1.0 Adding secure cookie support
|
|
18
|
+
|
|
19
|
+
New feature: marking all cookies as secure. Added by @jmera in https://github.com/twitter/secureheaders/pull/231. In the future, we'll probably add the ability to whitelist individual cookies that should not be marked secure. PRs welcome.
|
|
20
|
+
|
|
21
|
+
Internal refactoring: In https://github.com/twitter/secureheaders/pull/232, we changed the way dynamic CSP is handled internally. The biggest benefit is that highly dynamic policies (which can happen with multiple `append/override` calls per request) are handled better:
|
|
22
|
+
|
|
23
|
+
1. Only the CSP header cache is busted when using a dynamic policy. All other headers are preserved and don't need to be generated. Dynamic X-Frame-Options changes modify the cache directly.
|
|
24
|
+
1. Idempotency checks for policy modifications are deferred until the end of the request lifecycle and only happen once, instead of per `append/override` call. The idempotency check itself is fairly expensive itself.
|
|
25
|
+
1. CSP header string is produced at most once per request.
|
|
26
|
+
|
|
1
27
|
## 3.0.3
|
|
2
28
|
|
|
3
29
|
Bug fix for handling policy merges where appending a non-default source value (report-uri, plugin-types, frame-ancestors, base-uri, and form-action) would be combined with the default-src value. Appending a directive that doesn't exist in the current policy combines the new value with `default-src` to mimic the actual behavior of the addition. However, this does not make sense for non-default-src values (a.k.a. "fetch directives") and can lead to unexpected behavior like a `report-uri` value of `*`. Previously, this config:
|
|
@@ -14,6 +40,7 @@ When appending:
|
|
|
14
40
|
{
|
|
15
41
|
report_uri => %w(https://report-uri.io/asdf)
|
|
16
42
|
}
|
|
43
|
+
```
|
|
17
44
|
|
|
18
45
|
Would result in `default-src *; report-uri *` which doesn't make any sense at all.
|
|
19
46
|
|
data/Gemfile
CHANGED
|
@@ -6,9 +6,12 @@ group :test do
|
|
|
6
6
|
gem "tins", "~> 1.6.0" # 1.7 requires ruby 2.0
|
|
7
7
|
gem "pry-nav"
|
|
8
8
|
gem "rack"
|
|
9
|
+
gem "rspec"
|
|
10
|
+
gem "coveralls"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
group :guard do
|
|
9
14
|
gem "guard-rspec", platforms: [:ruby_19, :ruby_20, :ruby_21, :ruby_22]
|
|
10
|
-
gem "rspec", ">= 3.1"
|
|
11
15
|
gem "growl"
|
|
12
16
|
gem "rb-fsevent"
|
|
13
|
-
gem "coveralls", platforms: [:ruby_19, :ruby_20, :ruby_21, :ruby_22]
|
|
14
17
|
end
|
data/README.md
CHANGED
|
@@ -15,6 +15,8 @@ The gem will automatically apply several headers that are related to security.
|
|
|
15
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
16
|
- 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)
|
|
17
17
|
|
|
18
|
+
It can also mark all http cookies with the secure attribute (when configured to do so).
|
|
19
|
+
|
|
18
20
|
`secure_headers` is a library with a global config, per request overrides, and rack middleware that enables you customize your application settings.
|
|
19
21
|
|
|
20
22
|
## Use
|
|
@@ -29,7 +31,8 @@ All `nil` values will fallback to their default values. `SecureHeaders::OPT_OUT`
|
|
|
29
31
|
|
|
30
32
|
```ruby
|
|
31
33
|
SecureHeaders::Configuration.default do |config|
|
|
32
|
-
config.
|
|
34
|
+
config.secure_cookies = true # mark all cookies as "secure"
|
|
35
|
+
config.hsts = "max-age=#{20.years.to_i}; includeSubdomains; preload"
|
|
33
36
|
config.x_frame_options = "DENY"
|
|
34
37
|
config.x_content_type_options = "nosniff"
|
|
35
38
|
config.x_xss_protection = "1; mode=block"
|
|
@@ -57,13 +60,13 @@ SecureHeaders::Configuration.default do |config|
|
|
|
57
60
|
plugin_types: %w(application/x-shockwave-flash),
|
|
58
61
|
block_all_mixed_content: true, # see [http://www.w3.org/TR/mixed-content/](http://www.w3.org/TR/mixed-content/)
|
|
59
62
|
upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
|
|
60
|
-
report_uri: %w(https://
|
|
63
|
+
report_uri: %w(https://report-uri.io/example-csp)
|
|
61
64
|
}
|
|
62
65
|
config.hpkp = {
|
|
63
66
|
report_only: false,
|
|
64
67
|
max_age: 60.days.to_i,
|
|
65
68
|
include_subdomains: true,
|
|
66
|
-
report_uri: "https://
|
|
69
|
+
report_uri: "https://report-uri.io/example-hpkp",
|
|
67
70
|
pins: [
|
|
68
71
|
{sha256: "abc"},
|
|
69
72
|
{sha256: "123"}
|
|
@@ -175,7 +178,7 @@ When manipulating content security policy, there are a few things to consider. T
|
|
|
175
178
|
|
|
176
179
|
#### Append to the policy with a directive other than `default_src`
|
|
177
180
|
|
|
178
|
-
The value of `default_src` is joined with the addition. 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:
|
|
181
|
+
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:
|
|
179
182
|
|
|
180
183
|
```ruby
|
|
181
184
|
::SecureHeaders::Configuration.default do |config|
|
|
@@ -255,7 +258,7 @@ config.hpkp = {
|
|
|
255
258
|
{sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
|
|
256
259
|
],
|
|
257
260
|
report_only: true, # defaults to false (report-only mode)
|
|
258
|
-
report_uri: '
|
|
261
|
+
report_uri: 'https://report-uri.io/example-hpkp',
|
|
259
262
|
app_name: 'example',
|
|
260
263
|
tag_report_uri: true
|
|
261
264
|
}
|
|
@@ -287,43 +290,6 @@ class Donkey < Sinatra::Application
|
|
|
287
290
|
end
|
|
288
291
|
```
|
|
289
292
|
|
|
290
|
-
### Using with Padrino
|
|
291
|
-
|
|
292
|
-
You can use SecureHeaders for Padrino applications as well:
|
|
293
|
-
|
|
294
|
-
In your `Gemfile`:
|
|
295
|
-
|
|
296
|
-
```ruby
|
|
297
|
-
gem "secure_headers", require: 'secure_headers'
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
then in your `app.rb` file you can:
|
|
301
|
-
|
|
302
|
-
```ruby
|
|
303
|
-
Padrino.use(SecureHeaders::Middleware)
|
|
304
|
-
require 'secure_headers/padrino'
|
|
305
|
-
|
|
306
|
-
module Web
|
|
307
|
-
class App < Padrino::Application
|
|
308
|
-
register SecureHeaders::Padrino
|
|
309
|
-
|
|
310
|
-
get '/' do
|
|
311
|
-
render 'index'
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
end
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
and in `config/boot.rb`:
|
|
318
|
-
|
|
319
|
-
```ruby
|
|
320
|
-
def before_load
|
|
321
|
-
SecureHeaders::Configuration.default do |config|
|
|
322
|
-
...
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
```
|
|
326
|
-
|
|
327
293
|
## Similar libraries
|
|
328
294
|
|
|
329
295
|
* Rack [rack-secure_headers](https://github.com/frodsan/rack-secure_headers)
|
|
@@ -334,6 +300,8 @@ end
|
|
|
334
300
|
* Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security)
|
|
335
301
|
* Go - [secureheader](https://github.com/kr/secureheader)
|
|
336
302
|
* Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
|
|
303
|
+
* Dropwizard [dropwizard-web-security](https://github.com/palantir/dropwizard-web-security)
|
|
304
|
+
* Ember.js [ember-cli-content-security-policy](https://github.com/rwjblue/ember-cli-content-security-policy/)
|
|
337
305
|
|
|
338
306
|
## License
|
|
339
307
|
|
|
@@ -3,6 +3,7 @@ module SecureHeaders
|
|
|
3
3
|
DEFAULT_CONFIG = :default
|
|
4
4
|
NOOP_CONFIGURATION = "secure_headers_noop_config"
|
|
5
5
|
class NotYetConfiguredError < StandardError; end
|
|
6
|
+
class IllegalPolicyModificationError < StandardError; end
|
|
6
7
|
class << self
|
|
7
8
|
# Public: Set the global default configuration.
|
|
8
9
|
#
|
|
@@ -23,12 +24,12 @@ module SecureHeaders
|
|
|
23
24
|
# if no value is supplied.
|
|
24
25
|
#
|
|
25
26
|
# Returns: the newly created config
|
|
26
|
-
def override(name, base = DEFAULT_CONFIG)
|
|
27
|
+
def override(name, base = DEFAULT_CONFIG, &block)
|
|
27
28
|
unless get(base)
|
|
28
29
|
raise NotYetConfiguredError, "#{base} policy not yet supplied"
|
|
29
30
|
end
|
|
30
31
|
override = @configurations[base].dup
|
|
31
|
-
|
|
32
|
+
override.instance_eval &block if block_given?
|
|
32
33
|
add_configuration(name, override)
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -43,18 +44,6 @@ module SecureHeaders
|
|
|
43
44
|
@configurations[name]
|
|
44
45
|
end
|
|
45
46
|
|
|
46
|
-
# Public: perform a basic deep dup. The shallow copy provided by dup/clone
|
|
47
|
-
# can lead to modifying parent objects.
|
|
48
|
-
def deep_copy(config)
|
|
49
|
-
config.each_with_object({}) do |(key, value), hash|
|
|
50
|
-
hash[key] = if value.is_a?(Array)
|
|
51
|
-
value.dup
|
|
52
|
-
else
|
|
53
|
-
value
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
47
|
private
|
|
59
48
|
|
|
60
49
|
# Private: add a valid configuration to the global set of named configs.
|
|
@@ -82,20 +71,44 @@ module SecureHeaders
|
|
|
82
71
|
ALL_HEADER_CLASSES.each do |klass|
|
|
83
72
|
config.send("#{klass::CONFIG_KEY}=", OPT_OUT)
|
|
84
73
|
end
|
|
74
|
+
config.dynamic_csp = OPT_OUT
|
|
85
75
|
end
|
|
86
76
|
|
|
87
77
|
add_configuration(NOOP_CONFIGURATION, noop_config)
|
|
88
78
|
end
|
|
79
|
+
|
|
80
|
+
# Public: perform a basic deep dup. The shallow copy provided by dup/clone
|
|
81
|
+
# can lead to modifying parent objects.
|
|
82
|
+
def deep_copy(config)
|
|
83
|
+
config.each_with_object({}) do |(key, value), hash|
|
|
84
|
+
hash[key] = if value.is_a?(Array)
|
|
85
|
+
value.dup
|
|
86
|
+
else
|
|
87
|
+
value
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Private: convenience method purely DRY things up. The value may not be a
|
|
93
|
+
# hash (e.g. OPT_OUT, nil)
|
|
94
|
+
def deep_copy_if_hash(value)
|
|
95
|
+
if value.is_a?(Hash)
|
|
96
|
+
deep_copy(value)
|
|
97
|
+
else
|
|
98
|
+
value
|
|
99
|
+
end
|
|
100
|
+
end
|
|
89
101
|
end
|
|
90
102
|
|
|
91
|
-
|
|
92
|
-
:x_xss_protection, :
|
|
93
|
-
:hpkp
|
|
94
|
-
|
|
103
|
+
attr_writer :hsts, :x_frame_options, :x_content_type_options,
|
|
104
|
+
:x_xss_protection, :x_download_options, :x_permitted_cross_domain_policies,
|
|
105
|
+
:hpkp, :dynamic_csp, :secure_cookies
|
|
106
|
+
|
|
107
|
+
attr_reader :cached_headers, :csp, :dynamic_csp, :secure_cookies
|
|
95
108
|
|
|
96
109
|
def initialize(&block)
|
|
97
110
|
self.hpkp = OPT_OUT
|
|
98
|
-
self.csp = self.class.deep_copy
|
|
111
|
+
self.csp = self.class.send(:deep_copy, CSP::DEFAULT_CONFIG)
|
|
99
112
|
instance_eval &block if block_given?
|
|
100
113
|
end
|
|
101
114
|
|
|
@@ -104,33 +117,45 @@ module SecureHeaders
|
|
|
104
117
|
# Returns a deep-dup'd copy of this configuration.
|
|
105
118
|
def dup
|
|
106
119
|
copy = self.class.new
|
|
107
|
-
copy.
|
|
108
|
-
copy.
|
|
109
|
-
copy.
|
|
110
|
-
copy.
|
|
111
|
-
copy.
|
|
112
|
-
copy.
|
|
113
|
-
copy.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
copy.secure_cookies = @secure_cookies
|
|
121
|
+
copy.csp = self.class.send(:deep_copy_if_hash, @csp)
|
|
122
|
+
copy.dynamic_csp = self.class.send(:deep_copy_if_hash, @dynamic_csp)
|
|
123
|
+
copy.cached_headers = self.class.send(:deep_copy_if_hash, @cached_headers)
|
|
124
|
+
copy.x_content_type_options = @x_content_type_options
|
|
125
|
+
copy.hsts = @hsts
|
|
126
|
+
copy.x_frame_options = @x_frame_options
|
|
127
|
+
copy.x_xss_protection = @x_xss_protection
|
|
128
|
+
copy.x_download_options = @x_download_options
|
|
129
|
+
copy.x_permitted_cross_domain_policies = @x_permitted_cross_domain_policies
|
|
130
|
+
copy.hpkp = @hpkp
|
|
131
|
+
copy
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def opt_out(header)
|
|
135
|
+
send("#{header}=", OPT_OUT)
|
|
136
|
+
if header == CSP::CONFIG_KEY
|
|
137
|
+
dynamic_csp = OPT_OUT
|
|
117
138
|
end
|
|
139
|
+
self.cached_headers.delete(header)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def update_x_frame_options(value)
|
|
143
|
+
@x_frame_options = value
|
|
144
|
+
self.cached_headers[XFrameOptions::CONFIG_KEY] = XFrameOptions.make_header(value)
|
|
145
|
+
end
|
|
118
146
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
147
|
+
# Public: generated cached headers for a specific user agent.
|
|
148
|
+
def rebuild_csp_header_cache!(user_agent)
|
|
149
|
+
self.cached_headers[CSP::CONFIG_KEY] = {}
|
|
150
|
+
unless current_csp == OPT_OUT
|
|
151
|
+
user_agent = UserAgent.parse(user_agent)
|
|
152
|
+
variation = CSP.ua_to_variation(user_agent)
|
|
153
|
+
self.cached_headers[CSP::CONFIG_KEY][variation] = CSP.make_header(current_csp, user_agent)
|
|
123
154
|
end
|
|
124
|
-
copy
|
|
125
155
|
end
|
|
126
156
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
# Returns the value if available, and returns a dup of any hash values.
|
|
130
|
-
def fetch(key)
|
|
131
|
-
config = send(key)
|
|
132
|
-
config = self.class.deep_copy(config) if config.is_a?(Hash)
|
|
133
|
-
config
|
|
157
|
+
def current_csp
|
|
158
|
+
@dynamic_csp || @csp
|
|
134
159
|
end
|
|
135
160
|
|
|
136
161
|
# Public: validates all configurations values.
|
|
@@ -139,16 +164,32 @@ module SecureHeaders
|
|
|
139
164
|
#
|
|
140
165
|
# Returns nothing
|
|
141
166
|
def validate_config!
|
|
142
|
-
StrictTransportSecurity.validate_config!(hsts)
|
|
143
|
-
ContentSecurityPolicy.validate_config!(csp)
|
|
144
|
-
XFrameOptions.validate_config!(x_frame_options)
|
|
145
|
-
XContentTypeOptions.validate_config!(x_content_type_options)
|
|
146
|
-
XXssProtection.validate_config!(x_xss_protection)
|
|
147
|
-
XDownloadOptions.validate_config!(x_download_options)
|
|
148
|
-
XPermittedCrossDomainPolicies.validate_config!(x_permitted_cross_domain_policies)
|
|
149
|
-
PublicKeyPins.validate_config!(hpkp)
|
|
167
|
+
StrictTransportSecurity.validate_config!(@hsts)
|
|
168
|
+
ContentSecurityPolicy.validate_config!(@csp)
|
|
169
|
+
XFrameOptions.validate_config!(@x_frame_options)
|
|
170
|
+
XContentTypeOptions.validate_config!(@x_content_type_options)
|
|
171
|
+
XXssProtection.validate_config!(@x_xss_protection)
|
|
172
|
+
XDownloadOptions.validate_config!(@x_download_options)
|
|
173
|
+
XPermittedCrossDomainPolicies.validate_config!(@x_permitted_cross_domain_policies)
|
|
174
|
+
PublicKeyPins.validate_config!(@hpkp)
|
|
150
175
|
end
|
|
151
176
|
|
|
177
|
+
protected
|
|
178
|
+
|
|
179
|
+
def csp=(new_csp)
|
|
180
|
+
if self.dynamic_csp
|
|
181
|
+
raise IllegalPolicyModificationError, "You are attempting to modify CSP settings directly. Use dynamic_csp= isntead."
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
@csp = new_csp
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def cached_headers=(headers)
|
|
188
|
+
@cached_headers = headers
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
152
193
|
# Public: Precompute the header names and values for this configuraiton.
|
|
153
194
|
# Ensures that headers generated at configure time, not on demand.
|
|
154
195
|
#
|
|
@@ -156,7 +197,7 @@ module SecureHeaders
|
|
|
156
197
|
def cache_headers!
|
|
157
198
|
# generate defaults for the "easy" headers
|
|
158
199
|
headers = (ALL_HEADERS_BESIDES_CSP).each_with_object({}) do |klass, hash|
|
|
159
|
-
config =
|
|
200
|
+
config = instance_variable_get("@#{klass::CONFIG_KEY}")
|
|
160
201
|
unless config == OPT_OUT
|
|
161
202
|
hash[klass::CONFIG_KEY] = klass.make_header(config).freeze
|
|
162
203
|
end
|
|
@@ -165,7 +206,7 @@ module SecureHeaders
|
|
|
165
206
|
generate_csp_headers(headers)
|
|
166
207
|
|
|
167
208
|
headers.freeze
|
|
168
|
-
|
|
209
|
+
self.cached_headers = headers
|
|
169
210
|
end
|
|
170
211
|
|
|
171
212
|
# Private: adds CSP headers for each variation of CSP support.
|
|
@@ -175,11 +216,10 @@ module SecureHeaders
|
|
|
175
216
|
#
|
|
176
217
|
# Returns nothing
|
|
177
218
|
def generate_csp_headers(headers)
|
|
178
|
-
unless csp == OPT_OUT
|
|
219
|
+
unless @csp == OPT_OUT
|
|
179
220
|
headers[CSP::CONFIG_KEY] = {}
|
|
180
|
-
|
|
221
|
+
csp_config = self.current_csp
|
|
181
222
|
CSP::VARIATIONS.each do |name, _|
|
|
182
|
-
csp_config = fetch(CSP::CONFIG_KEY)
|
|
183
223
|
csp = CSP.make_header(csp_config, UserAgent.parse(name))
|
|
184
224
|
headers[CSP::CONFIG_KEY][name] = csp.freeze
|
|
185
225
|
end
|