secure_headers 3.0.0 → 3.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.
- checksums.yaml +4 -4
- data/.gitignore +0 -9
- data/.travis.yml +13 -5
- data/CHANGELOG.md +38 -0
- data/Gemfile +5 -2
- data/README.md +11 -45
- data/lib/secure_headers/configuration.rb +85 -54
- data/lib/secure_headers/headers/content_security_policy.rb +31 -294
- data/lib/secure_headers/headers/policy_management.rb +319 -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 +3 -3
- data/lib/secure_headers.rb +37 -63
- data/secure_headers.gemspec +1 -1
- data/spec/lib/secure_headers/configuration_spec.rb +2 -4
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +13 -143
- 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 +100 -41
- data/spec/spec_helper.rb +4 -1
- data/upgrading-to-3-0.md +1 -0
- 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: 7353ab86b76cba9baabc265f6321990163927fa0
|
|
4
|
+
data.tar.gz: 8081e9d2a284a26191899317d1947cde24d80d9f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37fd3a8aca07d1ce46bd7cdaf89ddb057d41b94f2f41625287d9a3118d19300708fd33f9d5f029c341a9c80295848101f01558d3dfd370a7bba34c8862e710d6
|
|
7
|
+
data.tar.gz: 0d810d24a48f53a45bec5621291475059e521d0d4f640bf1430ad6c86ecf020fea8dd8eb220e600b71c32539d7cfbc1f87567066477deebdf660d8a0161e2f8a
|
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,41 @@
|
|
|
1
|
+
## 3.1.0 Adding secure cookie support
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
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:
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
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.
|
|
9
|
+
1. CSP header string is produced at most once per request.
|
|
10
|
+
|
|
11
|
+
## 3.0.3
|
|
12
|
+
|
|
13
|
+
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
|
+
|
|
15
|
+
```
|
|
16
|
+
{
|
|
17
|
+
default_src => %w(*)
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
When appending:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
{
|
|
25
|
+
report_uri => %w(https://report-uri.io/asdf)
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Would result in `default-src *; report-uri *` which doesn't make any sense at all.
|
|
30
|
+
|
|
31
|
+
## 3.0.2
|
|
32
|
+
|
|
33
|
+
Bug fix for handling CSP configs that supply a frozen hash. If a directive value is `nil`, then appending to a config with a frozen hash would cause an error since we're trying to modify a frozen hash. See https://github.com/twitter/secureheaders/pull/223.
|
|
34
|
+
|
|
35
|
+
## 3.0.1
|
|
36
|
+
|
|
37
|
+
Adds `upgrade-insecure-requests` support for requests from Firefox and Chrome (and Opera). See [the spec](https://www.w3.org/TR/upgrade-insecure-requests/) for details.
|
|
38
|
+
|
|
1
39
|
## 3.0.0
|
|
2
40
|
|
|
3
41
|
secure_headers 3.0.0 is a near-complete, not-entirely-backward-compatible rewrite. Please see the [upgrade guide](https://github.com/twitter/secureheaders/blob/master/upgrading-to-3-0.md) for an in-depth explanation of the changes and the suggested upgrade path.
|
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
|
@@ -29,7 +29,7 @@ All `nil` values will fallback to their default values. `SecureHeaders::OPT_OUT`
|
|
|
29
29
|
|
|
30
30
|
```ruby
|
|
31
31
|
SecureHeaders::Configuration.default do |config|
|
|
32
|
-
config.hsts = "max-age=#{20.years.to_i}"
|
|
32
|
+
config.hsts = "max-age=#{20.years.to_i}; includeSubdomains; preload"
|
|
33
33
|
config.x_frame_options = "DENY"
|
|
34
34
|
config.x_content_type_options = "nosniff"
|
|
35
35
|
config.x_xss_protection = "1; mode=block"
|
|
@@ -56,13 +56,14 @@ SecureHeaders::Configuration.default do |config|
|
|
|
56
56
|
frame_ancestors: %w('none'),
|
|
57
57
|
plugin_types: %w(application/x-shockwave-flash),
|
|
58
58
|
block_all_mixed_content: true, # see [http://www.w3.org/TR/mixed-content/](http://www.w3.org/TR/mixed-content/)
|
|
59
|
-
|
|
59
|
+
upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
|
|
60
|
+
report_uri: %w(https://report-uri.io/example-csp)
|
|
60
61
|
}
|
|
61
62
|
config.hpkp = {
|
|
62
63
|
report_only: false,
|
|
63
64
|
max_age: 60.days.to_i,
|
|
64
65
|
include_subdomains: true,
|
|
65
|
-
report_uri: "https://
|
|
66
|
+
report_uri: "https://report-uri.io/example-hpkp",
|
|
66
67
|
pins: [
|
|
67
68
|
{sha256: "abc"},
|
|
68
69
|
{sha256: "123"}
|
|
@@ -130,7 +131,7 @@ By default, a noop configuration is provided. No headers will be set when this d
|
|
|
130
131
|
```ruby
|
|
131
132
|
class MyController < ApplicationController
|
|
132
133
|
def index
|
|
133
|
-
SecureHeaders
|
|
134
|
+
SecureHeaders.opt_out_of_all_protection(request)
|
|
134
135
|
end
|
|
135
136
|
end
|
|
136
137
|
```
|
|
@@ -174,7 +175,7 @@ When manipulating content security policy, there are a few things to consider. T
|
|
|
174
175
|
|
|
175
176
|
#### Append to the policy with a directive other than `default_src`
|
|
176
177
|
|
|
177
|
-
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:
|
|
178
|
+
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:
|
|
178
179
|
|
|
179
180
|
```ruby
|
|
180
181
|
::SecureHeaders::Configuration.default do |config|
|
|
@@ -191,7 +192,7 @@ Code | Result
|
|
|
191
192
|
|
|
192
193
|
#### Nonce
|
|
193
194
|
|
|
194
|
-
script/style-nonce can be used to whitelist inline content. To do this, call the SecureHeaders
|
|
195
|
+
script/style-nonce can be used to whitelist inline content. To do this, call the `SecureHeaders.content_security_policy_nonce` then set the nonce attributes on the various tags.
|
|
195
196
|
|
|
196
197
|
Setting a nonce will also set 'unsafe-inline' for browsers that don't support nonces for backwards compatibility. 'unsafe-inline' is ignored if a nonce is present in a directive in compliant browsers.
|
|
197
198
|
|
|
@@ -254,7 +255,7 @@ config.hpkp = {
|
|
|
254
255
|
{sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
|
|
255
256
|
],
|
|
256
257
|
report_only: true, # defaults to false (report-only mode)
|
|
257
|
-
report_uri: '
|
|
258
|
+
report_uri: 'https://report-uri.io/example-hpkp',
|
|
258
259
|
app_name: 'example',
|
|
259
260
|
tag_report_uri: true
|
|
260
261
|
}
|
|
@@ -280,49 +281,12 @@ class Donkey < Sinatra::Application
|
|
|
280
281
|
set :root, APP_ROOT
|
|
281
282
|
|
|
282
283
|
get '/' do
|
|
283
|
-
SecureHeaders.override_x_frame_options(SecureHeaders::OPT_OUT)
|
|
284
|
+
SecureHeaders.override_x_frame_options(request, SecureHeaders::OPT_OUT)
|
|
284
285
|
haml :index
|
|
285
286
|
end
|
|
286
287
|
end
|
|
287
288
|
```
|
|
288
289
|
|
|
289
|
-
### Using with Padrino
|
|
290
|
-
|
|
291
|
-
You can use SecureHeaders for Padrino applications as well:
|
|
292
|
-
|
|
293
|
-
In your `Gemfile`:
|
|
294
|
-
|
|
295
|
-
```ruby
|
|
296
|
-
gem "secure_headers", require: 'secure_headers'
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
then in your `app.rb` file you can:
|
|
300
|
-
|
|
301
|
-
```ruby
|
|
302
|
-
Padrino.use(SecureHeaders::Middleware)
|
|
303
|
-
require 'secure_headers/padrino'
|
|
304
|
-
|
|
305
|
-
module Web
|
|
306
|
-
class App < Padrino::Application
|
|
307
|
-
register SecureHeaders::Padrino
|
|
308
|
-
|
|
309
|
-
get '/' do
|
|
310
|
-
render 'index'
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
and in `config/boot.rb`:
|
|
317
|
-
|
|
318
|
-
```ruby
|
|
319
|
-
def before_load
|
|
320
|
-
SecureHeaders::Configuration.default do |config|
|
|
321
|
-
...
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
```
|
|
325
|
-
|
|
326
290
|
## Similar libraries
|
|
327
291
|
|
|
328
292
|
* Rack [rack-secure_headers](https://github.com/frodsan/rack-secure_headers)
|
|
@@ -333,6 +297,8 @@ end
|
|
|
333
297
|
* Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security)
|
|
334
298
|
* Go - [secureheader](https://github.com/kr/secureheader)
|
|
335
299
|
* Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
|
|
300
|
+
* Dropwizard [dropwizard-web-security](https://github.com/palantir/dropwizard-web-security)
|
|
301
|
+
* Ember.js [ember-cli-content-security-policy](https://github.com/rwjblue/ember-cli-content-security-policy/)
|
|
336
302
|
|
|
337
303
|
## License
|
|
338
304
|
|
|
@@ -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.
|
|
@@ -86,16 +75,39 @@ module SecureHeaders
|
|
|
86
75
|
|
|
87
76
|
add_configuration(NOOP_CONFIGURATION, noop_config)
|
|
88
77
|
end
|
|
78
|
+
|
|
79
|
+
# Public: perform a basic deep dup. The shallow copy provided by dup/clone
|
|
80
|
+
# can lead to modifying parent objects.
|
|
81
|
+
def deep_copy(config)
|
|
82
|
+
config.each_with_object({}) do |(key, value), hash|
|
|
83
|
+
hash[key] = if value.is_a?(Array)
|
|
84
|
+
value.dup
|
|
85
|
+
else
|
|
86
|
+
value
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Private: convenience method purely DRY things up. The value may not be a
|
|
92
|
+
# hash (e.g. OPT_OUT, nil)
|
|
93
|
+
def deep_copy_if_hash(value)
|
|
94
|
+
if value.is_a?(Hash)
|
|
95
|
+
deep_copy(value)
|
|
96
|
+
else
|
|
97
|
+
value
|
|
98
|
+
end
|
|
99
|
+
end
|
|
89
100
|
end
|
|
90
101
|
|
|
91
|
-
|
|
92
|
-
:x_xss_protection, :
|
|
93
|
-
:hpkp
|
|
94
|
-
|
|
102
|
+
attr_writer :hsts, :x_frame_options, :x_content_type_options,
|
|
103
|
+
:x_xss_protection, :x_download_options, :x_permitted_cross_domain_policies,
|
|
104
|
+
:hpkp, :dynamic_csp, :secure_cookies
|
|
105
|
+
|
|
106
|
+
attr_reader :cached_headers, :csp, :dynamic_csp, :secure_cookies
|
|
95
107
|
|
|
96
108
|
def initialize(&block)
|
|
97
109
|
self.hpkp = OPT_OUT
|
|
98
|
-
self.csp = self.class.deep_copy
|
|
110
|
+
self.csp = self.class.send(:deep_copy, CSP::DEFAULT_CONFIG)
|
|
99
111
|
instance_eval &block if block_given?
|
|
100
112
|
end
|
|
101
113
|
|
|
@@ -104,33 +116,37 @@ module SecureHeaders
|
|
|
104
116
|
# Returns a deep-dup'd copy of this configuration.
|
|
105
117
|
def dup
|
|
106
118
|
copy = self.class.new
|
|
107
|
-
copy.
|
|
108
|
-
copy.
|
|
109
|
-
copy.
|
|
110
|
-
copy.
|
|
111
|
-
copy
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
copy.secure_cookies = @secure_cookies
|
|
120
|
+
copy.csp = self.class.send(:deep_copy_if_hash, @csp)
|
|
121
|
+
copy.dynamic_csp = self.class.send(:deep_copy_if_hash, @dynamic_csp)
|
|
122
|
+
copy.cached_headers = self.class.send(:deep_copy_if_hash, @cached_headers)
|
|
123
|
+
copy
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def opt_out(header)
|
|
127
|
+
send("#{header}=", OPT_OUT)
|
|
128
|
+
if header == CSP::CONFIG_KEY
|
|
129
|
+
dynamic_csp = OPT_OUT
|
|
117
130
|
end
|
|
131
|
+
self.cached_headers.delete(header)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def update_x_frame_options(value)
|
|
135
|
+
self.cached_headers[XFrameOptions::CONFIG_KEY] = XFrameOptions.make_header(value)
|
|
136
|
+
end
|
|
118
137
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
138
|
+
# Public: generated cached headers for a specific user agent.
|
|
139
|
+
def rebuild_csp_header_cache!(user_agent)
|
|
140
|
+
self.cached_headers[CSP::CONFIG_KEY] = {}
|
|
141
|
+
unless current_csp == OPT_OUT
|
|
142
|
+
user_agent = UserAgent.parse(user_agent)
|
|
143
|
+
variation = CSP.ua_to_variation(user_agent)
|
|
144
|
+
self.cached_headers[CSP::CONFIG_KEY][variation] = CSP.make_header(current_csp, user_agent)
|
|
123
145
|
end
|
|
124
|
-
copy
|
|
125
146
|
end
|
|
126
147
|
|
|
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
|
|
148
|
+
def current_csp
|
|
149
|
+
@dynamic_csp || @csp
|
|
134
150
|
end
|
|
135
151
|
|
|
136
152
|
# Public: validates all configurations values.
|
|
@@ -139,16 +155,32 @@ module SecureHeaders
|
|
|
139
155
|
#
|
|
140
156
|
# Returns nothing
|
|
141
157
|
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)
|
|
158
|
+
StrictTransportSecurity.validate_config!(@hsts)
|
|
159
|
+
ContentSecurityPolicy.validate_config!(@csp)
|
|
160
|
+
XFrameOptions.validate_config!(@x_frame_options)
|
|
161
|
+
XContentTypeOptions.validate_config!(@x_content_type_options)
|
|
162
|
+
XXssProtection.validate_config!(@x_xss_protection)
|
|
163
|
+
XDownloadOptions.validate_config!(@x_download_options)
|
|
164
|
+
XPermittedCrossDomainPolicies.validate_config!(@x_permitted_cross_domain_policies)
|
|
165
|
+
PublicKeyPins.validate_config!(@hpkp)
|
|
150
166
|
end
|
|
151
167
|
|
|
168
|
+
protected
|
|
169
|
+
|
|
170
|
+
def csp=(new_csp)
|
|
171
|
+
if self.dynamic_csp
|
|
172
|
+
raise IllegalPolicyModificationError, "You are attempting to modify CSP settings directly. Use dynamic_csp= isntead."
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
@csp = new_csp
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def cached_headers=(headers)
|
|
179
|
+
@cached_headers = headers
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
private
|
|
183
|
+
|
|
152
184
|
# Public: Precompute the header names and values for this configuraiton.
|
|
153
185
|
# Ensures that headers generated at configure time, not on demand.
|
|
154
186
|
#
|
|
@@ -156,7 +188,7 @@ module SecureHeaders
|
|
|
156
188
|
def cache_headers!
|
|
157
189
|
# generate defaults for the "easy" headers
|
|
158
190
|
headers = (ALL_HEADERS_BESIDES_CSP).each_with_object({}) do |klass, hash|
|
|
159
|
-
config =
|
|
191
|
+
config = instance_variable_get("@#{klass::CONFIG_KEY}")
|
|
160
192
|
unless config == OPT_OUT
|
|
161
193
|
hash[klass::CONFIG_KEY] = klass.make_header(config).freeze
|
|
162
194
|
end
|
|
@@ -165,7 +197,7 @@ module SecureHeaders
|
|
|
165
197
|
generate_csp_headers(headers)
|
|
166
198
|
|
|
167
199
|
headers.freeze
|
|
168
|
-
|
|
200
|
+
self.cached_headers = headers
|
|
169
201
|
end
|
|
170
202
|
|
|
171
203
|
# Private: adds CSP headers for each variation of CSP support.
|
|
@@ -175,11 +207,10 @@ module SecureHeaders
|
|
|
175
207
|
#
|
|
176
208
|
# Returns nothing
|
|
177
209
|
def generate_csp_headers(headers)
|
|
178
|
-
unless csp == OPT_OUT
|
|
210
|
+
unless @csp == OPT_OUT
|
|
179
211
|
headers[CSP::CONFIG_KEY] = {}
|
|
180
|
-
|
|
212
|
+
csp_config = self.current_csp
|
|
181
213
|
CSP::VARIATIONS.each do |name, _|
|
|
182
|
-
csp_config = fetch(CSP::CONFIG_KEY)
|
|
183
214
|
csp = CSP.make_header(csp_config, UserAgent.parse(name))
|
|
184
215
|
headers[CSP::CONFIG_KEY][name] = csp.freeze
|
|
185
216
|
end
|