secure_headers 1.2.0 → 1.3.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.
Potentially problematic release.
This version of secure_headers might be problematic. Click here for more details.
- checksums.yaml +8 -8
- data/Gemfile +0 -2
- data/HISTORY.md +9 -13
- data/README.md +54 -28
- data/fixtures/rails_3_2_12/Gemfile +0 -2
- data/fixtures/rails_3_2_12/config/initializers/secure_headers.rb +1 -0
- data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +4 -3
- data/fixtures/rails_3_2_12/spec/spec_helper.rb +1 -4
- data/fixtures/rails_3_2_12_no_init/Gemfile +0 -2
- data/fixtures/rails_3_2_12_no_init/spec/controllers/other_things_controller_spec.rb +1 -1
- data/fixtures/rails_3_2_12_no_init/spec/spec_helper.rb +3 -17
- data/lib/secure_headers/headers/content_security_policy.rb +33 -23
- data/lib/secure_headers/padrino.rb +14 -0
- data/lib/secure_headers/version.rb +1 -1
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +38 -10
- data/spec/spec_helper.rb +9 -23
- metadata +4 -5
- data/fixtures/rails_3_2_12/Guardfile +0 -14
- data/fixtures/rails_3_2_12_no_init/Guardfile +0 -14
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NTdiMjdmYmVhNzk2MWQ3YmEwNDNjOWQ0YmZhZDZkNTAyNDlmYmQ1ZQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZTIyYjBjNmM5ZTU5YzRhZjU4MjdmNTcwNDAzMjMyZjJlOTFjOWYxMA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NzRjM2QwM2IwZjIyZDMzYzY3OWE2MGVmZTEyNjU3MmQwMzhiODcxYjRlZDlh
|
10
|
+
ODFmMmExNzgxNDkzN2U5YWFiNGFjYWMwYjUxNGNiMmJiOWM4ZTA0ZmY3Y2Mz
|
11
|
+
MjFlMTRlNzdiMGQ0MDY5NTM1YTkyNGMxOGJjMDRmMjkyZTIyMjA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2I3MzExNWM5MGFjZDI3YjBjMDlkNjgyNWUyODcyZGU3N2NmMjVjZTZkMWVi
|
14
|
+
Y2FiYmI4YjY3NzRkYzM3NWNkNjE0MGE4ZWMwMjhlMGJhYTI0NmIwYmY4MGFi
|
15
|
+
YjlhNjdkMTQ1YWQ0NTYwNGJiZmMxMzA3MjlkNmMyMDgyZjhlY2E=
|
data/Gemfile
CHANGED
@@ -7,12 +7,10 @@ group :test do
|
|
7
7
|
gem 'sqlite3', :platform => [:ruby, :mswin, :mingw]
|
8
8
|
gem 'jdbc-sqlite3', :platform => :jruby
|
9
9
|
gem 'rspec-rails'
|
10
|
-
gem 'spork'
|
11
10
|
gem 'rspec'
|
12
11
|
gem 'guard-rspec', :platform => :ruby_19
|
13
12
|
gem 'growl'
|
14
13
|
gem 'rb-fsevent'
|
15
|
-
gem 'simplecov'
|
16
14
|
gem 'debugger', :platform => :ruby_19
|
17
15
|
gem 'ruby-debug', :platform => :ruby_18
|
18
16
|
end
|
data/HISTORY.md
CHANGED
@@ -1,18 +1,14 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
2
2
|
======
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
'//example.com/old_csp'
|
13
|
-
end
|
14
|
-
}
|
15
|
-
}
|
4
|
+
- CSP nonce support was added back and is compliant.
|
5
|
+
- Bugs:
|
6
|
+
-- enforce, disable_fill_missing, and disable_chrome_extension did not accept lambdas for no good reason
|
7
|
+
-- IF a default-src was specified, and an img-src was not, and disable_fill_missing was true, the img-src value would be :data
|
8
|
+
|
9
|
+
1.2.0
|
10
|
+
======
|
11
|
+
- Allow procs to be used as config values.
|
16
12
|
|
17
13
|
1.1.1
|
18
14
|
======
|
data/README.md
CHANGED
@@ -118,9 +118,6 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
|
|
118
118
|
|
119
119
|
```ruby
|
120
120
|
:csp => {
|
121
|
-
|
122
|
-
# All values can be a String of space-delimited values, an Array of Strings, or procs.
|
123
|
-
|
124
121
|
:enforce => false, # sets header to report-only, by default
|
125
122
|
# default_src is required!
|
126
123
|
:default_src => nil, # sets the default-src/allow+options directives
|
@@ -193,19 +190,40 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
|
|
193
190
|
:csp => {
|
194
191
|
:default_src => 'self',
|
195
192
|
:img_src => '*',
|
196
|
-
:connect_src => 'none'
|
197
193
|
:object_src => ['media1.com', 'media2.com', '*.cdn.com'],
|
198
|
-
# alternatively :object_src => 'media1.com media2.com *.cdn.com'
|
199
|
-
:script_src => 'trustedscripts.example.com'
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
194
|
+
# alternatively (NOT csv) :object_src => 'media1.com media2.com *.cdn.com'
|
195
|
+
:script_src => 'trustedscripts.example.com'
|
196
|
+
}
|
197
|
+
"default-src 'self'; img-src *; object-src media1.com media2.com *.cdn.com; script-src trustedscripts.example.com;"
|
198
|
+
```
|
199
|
+
|
200
|
+
### CSP Level 2 features
|
201
|
+
|
202
|
+
script/style-nonce can be used to whitelist inline content. To do this, add "nonce" to your script/style-src configuration, then set the nonce attributes on the various tags.
|
203
|
+
|
204
|
+
*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.
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
:csp => {
|
208
|
+
:default_src => 'self',
|
209
|
+
:script_src => 'self nonce'
|
207
210
|
}
|
208
|
-
|
211
|
+
```
|
212
|
+
|
213
|
+
> content-security-policy: default-src 'self'; script-src 'self' 'nonce-abc123' 'unsafe-inline'
|
214
|
+
|
215
|
+
```erb
|
216
|
+
<script nonce="<%= @content_security_policy_nonce %>">
|
217
|
+
console.log("whitelisted, will execute")
|
218
|
+
</script>
|
219
|
+
|
220
|
+
<script nonce="lol">
|
221
|
+
console.log("won't execute, not whitelisted")
|
222
|
+
</script>
|
223
|
+
|
224
|
+
<script>
|
225
|
+
console.log("won't execute, not whitelisted")
|
226
|
+
</script>
|
209
227
|
```
|
210
228
|
|
211
229
|
## Note on Firefox handling of CSP
|
@@ -277,22 +295,11 @@ In your `Gemfile`:
|
|
277
295
|
then in your `app.rb` file you can:
|
278
296
|
|
279
297
|
```ruby
|
298
|
+
require 'secure_headers/padrino'
|
299
|
+
|
280
300
|
module Web
|
281
301
|
class App < Padrino::Application
|
282
|
-
|
283
|
-
|
284
|
-
::SecureHeaders::Configuration.configure do |config|
|
285
|
-
config.hsts = {:max_age => 99, :include_subdomains => true}
|
286
|
-
config.x_frame_options = 'DENY'
|
287
|
-
config.x_content_type_options = "nosniff"
|
288
|
-
config.x_xss_protection = {:value => '1', :mode => false}
|
289
|
-
config.csp = {
|
290
|
-
:default_src => "https://* inline eval",
|
291
|
-
:report_uri => '//example.com/uri-directive',
|
292
|
-
:img_src => "https://* data:",
|
293
|
-
:frame_src => "https://* http://*.twimg.com http://itunes.apple.com"
|
294
|
-
}
|
295
|
-
end
|
302
|
+
register SecureHeaders::Padrino
|
296
303
|
|
297
304
|
get '/' do
|
298
305
|
set_csp_header
|
@@ -302,6 +309,25 @@ module Web
|
|
302
309
|
end
|
303
310
|
```
|
304
311
|
|
312
|
+
and in `config/boot.rb`:
|
313
|
+
|
314
|
+
```ruby
|
315
|
+
def before_load
|
316
|
+
::SecureHeaders::Configuration.configure do |config|
|
317
|
+
config.hsts = {:max_age => 99, :include_subdomains => true}
|
318
|
+
config.x_frame_options = 'DENY'
|
319
|
+
config.x_content_type_options = "nosniff"
|
320
|
+
config.x_xss_protection = {:value => '1', :mode => false}
|
321
|
+
config.csp = {
|
322
|
+
:default_src => "https://* inline eval",
|
323
|
+
:report_uri => '//example.com/uri-directive',
|
324
|
+
:img_src => "https://* data:",
|
325
|
+
:frame_src => "https://* http://*.twimg.com http://itunes.apple.com"
|
326
|
+
}
|
327
|
+
end
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
305
331
|
## Similar libraries
|
306
332
|
|
307
333
|
* Node.js (express) [helmet](https://github.com/evilpacket/helmet) and [hood](https://github.com/seanmonstar/hood)
|
@@ -4,11 +4,9 @@ gem 'rails', '3.2.12'
|
|
4
4
|
gem 'sqlite3'
|
5
5
|
gem 'rspec-rails', '>= 2.0.0'
|
6
6
|
gem 'secure_headers', :path => '../..'
|
7
|
-
gem 'spork'
|
8
7
|
gem 'debugger', :platform => :ruby_19
|
9
8
|
gem 'ruby-debug', :platform => :ruby_18
|
10
9
|
gem 'guard-rspec'
|
11
|
-
gem 'guard-spork'
|
12
10
|
gem 'rb-fsevent'
|
13
11
|
gem 'growl'
|
14
12
|
|
@@ -12,12 +12,13 @@ describe OtherThingsController, :type => :controller do
|
|
12
12
|
expect(response.headers['X-Frame-Options']).to eq('SAMEORIGIN')
|
13
13
|
end
|
14
14
|
|
15
|
-
it "sets the
|
15
|
+
it "sets the CSP header with a local reference to a nonce" do
|
16
16
|
get :index
|
17
|
-
|
17
|
+
nonce = controller.instance_exec { @content_security_policy_nonce }
|
18
|
+
expect(nonce).to match /[a-zA-Z0-9\+\/=]{44}/
|
19
|
+
expect(response.headers['Content-Security-Policy-Report-Only']).to match(/default-src 'self'; img-src 'self' data:; script-src 'self' 'nonce-[a-zA-Z0-9\+\/=]{44}' 'unsafe-inline'; report-uri somewhere;/)
|
18
20
|
end
|
19
21
|
|
20
|
-
#mock ssl
|
21
22
|
it "sets the Strict-Transport-Security header" do
|
22
23
|
request.env['HTTPS'] = 'on'
|
23
24
|
get :index
|
@@ -4,11 +4,9 @@ gem 'rails', '3.2.12'
|
|
4
4
|
gem 'sqlite3'
|
5
5
|
gem 'rspec-rails', '>= 2.0.0'
|
6
6
|
gem 'secure_headers', :path => '../..'
|
7
|
-
gem 'spork'
|
8
7
|
gem 'debugger', :platform => :ruby_19
|
9
8
|
gem 'ruby-debug', :platform => :ruby_18
|
10
9
|
gem 'guard-rspec'
|
11
|
-
gem 'guard-spork'
|
12
10
|
gem 'rb-fsevent'
|
13
11
|
gem 'growl'
|
14
12
|
|
@@ -14,7 +14,7 @@ describe OtherThingsController, :type => :controller do
|
|
14
14
|
|
15
15
|
it "sets the X-WebKit-CSP header" do
|
16
16
|
get :index
|
17
|
-
expect(response.headers['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; img-src data:;")
|
17
|
+
expect(response.headers['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; img-src 'self' data:;")
|
18
18
|
end
|
19
19
|
|
20
20
|
#mock ssl
|
@@ -1,19 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'spork'
|
3
|
-
#uncomment the following line to use spork with the debugger
|
4
|
-
#require 'spork/ext/ruby-debug'
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# need to restart spork for it take effect.
|
10
|
-
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
11
|
-
ENV["RAILS_ENV"] ||= 'test'
|
12
|
-
require File.expand_path("../../config/environment", __FILE__)
|
13
|
-
require 'rspec/rails'
|
14
|
-
# require 'ruby-debug'
|
15
|
-
end
|
16
|
-
|
17
|
-
Spork.each_run do
|
18
|
-
|
19
|
-
end
|
3
|
+
ENV["RAILS_ENV"] ||= 'test'
|
4
|
+
require File.expand_path("../../config/environment", __FILE__)
|
5
|
+
require 'rspec/rails'
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'uri'
|
2
|
+
require 'base64'
|
2
3
|
|
3
4
|
module SecureHeaders
|
4
5
|
class ContentSecurityPolicyBuildError < StandardError; end
|
@@ -8,7 +9,7 @@ module SecureHeaders
|
|
8
9
|
STANDARD_HEADER_NAME = "Content-Security-Policy"
|
9
10
|
FF_CSP_ENDPOINT = "/content_security_policy/forward_report"
|
10
11
|
DIRECTIVES = [:default_src, :script_src, :frame_src, :style_src, :img_src, :media_src, :font_src, :object_src, :connect_src]
|
11
|
-
META = [:
|
12
|
+
META = [:disable_chrome_extension, :disable_fill_missing, :forward_endpoint]
|
12
13
|
end
|
13
14
|
include Constants
|
14
15
|
|
@@ -30,6 +31,7 @@ module SecureHeaders
|
|
30
31
|
def initialize(config=nil, options={})
|
31
32
|
@experimental = !!options.delete(:experimental)
|
32
33
|
@controller = options.delete(:controller)
|
34
|
+
|
33
35
|
if options[:request]
|
34
36
|
parse_request(options[:request])
|
35
37
|
else
|
@@ -45,6 +47,10 @@ module SecureHeaders
|
|
45
47
|
configure(config) if config
|
46
48
|
end
|
47
49
|
|
50
|
+
def nonce
|
51
|
+
@nonce ||= SecureRandom.base64(32).chomp
|
52
|
+
end
|
53
|
+
|
48
54
|
def configure(config)
|
49
55
|
@config = config.dup
|
50
56
|
|
@@ -54,18 +60,24 @@ module SecureHeaders
|
|
54
60
|
@config.merge!(experimental_config)
|
55
61
|
end
|
56
62
|
|
63
|
+
# http_additions will be the only field that still doesn't support
|
64
|
+
# lambdas because it's an ugly api that's showing it's age.
|
65
|
+
@http_additions = @config.delete(:http_additions)
|
66
|
+
|
67
|
+
normalize_csp_options
|
68
|
+
|
57
69
|
META.each do |meta|
|
58
70
|
self.send("#{meta}=", @config.delete(meta))
|
59
71
|
end
|
60
72
|
|
61
|
-
|
73
|
+
@enforce = @config.delete(:enforce)
|
62
74
|
normalize_reporting_endpoint
|
63
75
|
fill_directives unless disable_fill_missing?
|
64
76
|
end
|
65
77
|
|
66
78
|
def name
|
67
79
|
base = STANDARD_HEADER_NAME
|
68
|
-
if
|
80
|
+
if !@enforce || experimental
|
69
81
|
base += "-Report-Only"
|
70
82
|
end
|
71
83
|
base
|
@@ -86,15 +98,9 @@ module SecureHeaders
|
|
86
98
|
raise "Expected to find default_src directive value" unless @config[:default_src]
|
87
99
|
append_http_additions unless ssl_request?
|
88
100
|
header_value = [
|
89
|
-
# ensure default-src is first
|
90
|
-
build_directive(:default_src),
|
91
101
|
generic_directives(@config),
|
92
102
|
report_uri_directive
|
93
|
-
].join
|
94
|
-
|
95
|
-
#store the value for next time
|
96
|
-
@config = header_value
|
97
|
-
header_value.strip
|
103
|
+
].join.strip
|
98
104
|
rescue StandardError => e
|
99
105
|
raise ContentSecurityPolicyBuildError.new("Couldn't build CSP header :( #{e}")
|
100
106
|
end
|
@@ -111,27 +117,27 @@ module SecureHeaders
|
|
111
117
|
end
|
112
118
|
|
113
119
|
def append_http_additions
|
114
|
-
return unless http_additions
|
115
|
-
http_additions.each do |k, v|
|
120
|
+
return unless @http_additions
|
121
|
+
@http_additions.each do |k, v|
|
116
122
|
@config[k] ||= []
|
117
123
|
@config[k] << v
|
118
124
|
end
|
119
125
|
end
|
120
126
|
|
121
127
|
def normalize_csp_options
|
122
|
-
@config = @config.inject({}) do |hash, (
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
v
|
127
|
-
end
|
128
|
-
|
128
|
+
@config = @config.inject({}) do |hash, (key, value)|
|
129
|
+
# lambdas
|
130
|
+
config_val = value.respond_to?(:call) ? value.call : value
|
131
|
+
# space-delimeted strings
|
129
132
|
config_val = config_val.split if config_val.is_a? String
|
130
|
-
|
131
|
-
|
133
|
+
# array of strings
|
134
|
+
if config_val.respond_to?(:map) #skip booleans
|
135
|
+
config_val = config_val.map do |val|
|
136
|
+
translate_dir_value(val)
|
137
|
+
end.flatten.uniq
|
132
138
|
end
|
133
139
|
|
134
|
-
hash[
|
140
|
+
hash[key] = config_val
|
135
141
|
hash
|
136
142
|
end
|
137
143
|
|
@@ -145,6 +151,9 @@ module SecureHeaders
|
|
145
151
|
# self/none are special sources/src-dir-values and need to be quoted in chrome
|
146
152
|
elsif %{self none}.include?(val)
|
147
153
|
"'#{val}'"
|
154
|
+
elsif val == 'nonce'
|
155
|
+
@controller.instance_variable_set(:@content_security_policy_nonce, nonce)
|
156
|
+
["'nonce-#{nonce}'", "'unsafe-inline'"]
|
148
157
|
else
|
149
158
|
val
|
150
159
|
end
|
@@ -192,9 +201,10 @@ module SecureHeaders
|
|
192
201
|
if config[:img_src]
|
193
202
|
config[:img_src] = config[:img_src] + ['data:'] unless config[:img_src].include?('data:')
|
194
203
|
else
|
195
|
-
config[:img_src] = ['data:']
|
204
|
+
config[:img_src] = config[:default_src] + ['data:']
|
196
205
|
end
|
197
206
|
|
207
|
+
header_value = build_directive(:default_src)
|
198
208
|
config.keys.sort_by{|k| k.to_s}.each do |k| # ensure consistent ordering
|
199
209
|
header_value += build_directive(k)
|
200
210
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SecureHeaders
|
2
|
+
module Padrino
|
3
|
+
class << self
|
4
|
+
##
|
5
|
+
# Main class that register this extension.
|
6
|
+
#
|
7
|
+
def registered(app)
|
8
|
+
app.extend SecureHeaders::ClassMethods
|
9
|
+
app.helpers SecureHeaders::InstanceMethods
|
10
|
+
end
|
11
|
+
alias :included :registered
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -91,11 +91,14 @@ module SecureHeaders
|
|
91
91
|
|
92
92
|
it "accepts procs for other fields" do
|
93
93
|
opts = {
|
94
|
-
:default_src => lambda { "http://lambda/result" }
|
94
|
+
:default_src => lambda { "http://lambda/result" },
|
95
|
+
:enforce => lambda { true },
|
96
|
+
:disable_fill_missing => lambda { true }
|
95
97
|
}
|
96
98
|
|
97
|
-
csp = ContentSecurityPolicy.new(opts)
|
98
|
-
expect(csp).to
|
99
|
+
csp = ContentSecurityPolicy.new(opts)
|
100
|
+
expect(csp.value).to eq("default-src http://lambda/result; img-src http://lambda/result data:;")
|
101
|
+
expect(csp.name).to match("Content-Security-Policy")
|
99
102
|
end
|
100
103
|
end
|
101
104
|
end
|
@@ -219,7 +222,7 @@ module SecureHeaders
|
|
219
222
|
context "auto-whitelists data: uris for img-src" do
|
220
223
|
it "sets the value if no img-src specified" do
|
221
224
|
csp = ContentSecurityPolicy.new({:default_src => 'self', :disable_fill_missing => true, :disable_chrome_extension => true}, :request => request_for(CHROME))
|
222
|
-
expect(csp.value).to eq("default-src 'self'; img-src data:;")
|
225
|
+
expect(csp.value).to eq("default-src 'self'; img-src 'self' data:;")
|
223
226
|
end
|
224
227
|
|
225
228
|
it "appends the value if img-src is specified" do
|
@@ -243,14 +246,14 @@ module SecureHeaders
|
|
243
246
|
context "Firefox" do
|
244
247
|
it "builds a csp header for firefox" do
|
245
248
|
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(FIREFOX))
|
246
|
-
expect(csp.value).to eq("default-src https://*; img-src data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* about:; report-uri /csp_report;")
|
249
|
+
expect(csp.value).to eq("default-src https://*; img-src https://* data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* about:; report-uri /csp_report;")
|
247
250
|
end
|
248
251
|
end
|
249
252
|
|
250
253
|
context "Chrome" do
|
251
254
|
it "builds a csp header for chrome" do
|
252
255
|
csp = ContentSecurityPolicy.new(default_opts, :request => request_for(CHROME))
|
253
|
-
expect(csp.value).to eq("default-src https://*; img-src data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* about:; report-uri /csp_report;")
|
256
|
+
expect(csp.value).to eq("default-src https://*; img-src https://* data:; script-src 'unsafe-inline' 'unsafe-eval' https://* data:; style-src 'unsafe-inline' https://* about:; report-uri /csp_report;")
|
254
257
|
end
|
255
258
|
|
256
259
|
it "ignores :forward_endpoint settings" do
|
@@ -259,6 +262,31 @@ module SecureHeaders
|
|
259
262
|
end
|
260
263
|
end
|
261
264
|
|
265
|
+
context "when using a nonce" do
|
266
|
+
it "adds a nonce and unsafe-inline to the script-src value" do
|
267
|
+
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(CHROME))
|
268
|
+
expect(header.value).to include("script-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
|
269
|
+
end
|
270
|
+
|
271
|
+
it "adds a nonce and unsafe-inline to the style-src value" do
|
272
|
+
header = ContentSecurityPolicy.new(default_opts.merge(:style_src => "self nonce"), :request => request_for(CHROME))
|
273
|
+
expect(header.value).to include("style-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
|
274
|
+
end
|
275
|
+
|
276
|
+
it "adds an identical nonce to the style and script-src directives" do
|
277
|
+
header = ContentSecurityPolicy.new(default_opts.merge(:style_src => "self nonce", :script_src => "self nonce"), :request => request_for(CHROME))
|
278
|
+
nonce = header.nonce
|
279
|
+
value = header.value
|
280
|
+
expect(value).to include("style-src 'self' 'nonce-#{nonce}' 'unsafe-inline'")
|
281
|
+
expect(value).to include("script-src 'self' 'nonce-#{nonce}' 'unsafe-inline'")
|
282
|
+
end
|
283
|
+
|
284
|
+
it "does not add 'unsafe-inline' twice" do
|
285
|
+
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce inline"), :request => request_for(CHROME))
|
286
|
+
expect(header.value).to include("script-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline';")
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
262
290
|
context "when supplying a experimental values" do
|
263
291
|
let(:options) {{
|
264
292
|
:disable_chrome_extension => true,
|
@@ -272,7 +300,7 @@ module SecureHeaders
|
|
272
300
|
|
273
301
|
it "returns the original value" do
|
274
302
|
header = ContentSecurityPolicy.new(options, :request => request_for(CHROME))
|
275
|
-
expect(header.value).to eq("default-src 'self'; img-src data:; script-src https://*;")
|
303
|
+
expect(header.value).to eq("default-src 'self'; img-src 'self' data:; script-src https://*;")
|
276
304
|
end
|
277
305
|
|
278
306
|
it "it returns the experimental value if requested" do
|
@@ -333,17 +361,17 @@ module SecureHeaders
|
|
333
361
|
|
334
362
|
it "uses the value in the experimental block over SSL" do
|
335
363
|
csp = ContentSecurityPolicy.new(options, :experimental => true, :request => request_for(FIREFOX, '/', :ssl => true))
|
336
|
-
expect(csp.value).to eq("default-src 'self'; img-src data:; script-src 'self';")
|
364
|
+
expect(csp.value).to eq("default-src 'self'; img-src 'self' data:; script-src 'self';")
|
337
365
|
end
|
338
366
|
|
339
367
|
it "detects the :ssl => true option" do
|
340
368
|
csp = ContentSecurityPolicy.new(options, :experimental => true, :ua => FIREFOX, :ssl => true)
|
341
|
-
expect(csp.value).to eq("default-src 'self'; img-src data:; script-src 'self';")
|
369
|
+
expect(csp.value).to eq("default-src 'self'; img-src 'self' data:; script-src 'self';")
|
342
370
|
end
|
343
371
|
|
344
372
|
it "merges the values from experimental/http_additions when not over SSL" do
|
345
373
|
csp = ContentSecurityPolicy.new(options, :experimental => true, :request => request_for(FIREFOX))
|
346
|
-
expect(csp.value).to eq("default-src 'self'; img-src data:; script-src 'self' https://mycdn.example.com;")
|
374
|
+
expect(csp.value).to eq("default-src 'self'; img-src 'self' data:; script-src 'self' https://mycdn.example.com;")
|
347
375
|
end
|
348
376
|
end
|
349
377
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,24 +1,10 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Spork.prefork do
|
12
|
-
require 'rspec'
|
13
|
-
end
|
14
|
-
|
15
|
-
Spork.each_run do
|
16
|
-
require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
|
17
|
-
require File.join(File.dirname(__FILE__), '..', 'app', 'controllers', 'content_security_policy_controller')
|
18
|
-
include ::SecureHeaders::StrictTransportSecurity::Constants
|
19
|
-
include ::SecureHeaders::ContentSecurityPolicy::Constants
|
20
|
-
include ::SecureHeaders::XFrameOptions::Constants
|
21
|
-
include ::SecureHeaders::XXssProtection::Constants
|
22
|
-
include ::SecureHeaders::XContentTypeOptions::Constants
|
23
|
-
end
|
24
|
-
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
|
5
|
+
require File.join(File.dirname(__FILE__), '..', 'app', 'controllers', 'content_security_policy_controller')
|
6
|
+
include ::SecureHeaders::StrictTransportSecurity::Constants
|
7
|
+
include ::SecureHeaders::ContentSecurityPolicy::Constants
|
8
|
+
include ::SecureHeaders::XFrameOptions::Constants
|
9
|
+
include ::SecureHeaders::XXssProtection::Constants
|
10
|
+
include ::SecureHeaders::XContentTypeOptions::Constants
|
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: 1.
|
4
|
+
version: 1.3.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: 2014-
|
11
|
+
date: 2014-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -46,7 +46,6 @@ files:
|
|
46
46
|
- config/routes.rb
|
47
47
|
- fixtures/rails_3_2_12/.rspec
|
48
48
|
- fixtures/rails_3_2_12/Gemfile
|
49
|
-
- fixtures/rails_3_2_12/Guardfile
|
50
49
|
- fixtures/rails_3_2_12/README.rdoc
|
51
50
|
- fixtures/rails_3_2_12/Rakefile
|
52
51
|
- fixtures/rails_3_2_12/app/controllers/application_controller.rb
|
@@ -87,7 +86,6 @@ files:
|
|
87
86
|
- fixtures/rails_3_2_12/vendor/plugins/.gitkeep
|
88
87
|
- fixtures/rails_3_2_12_no_init/.rspec
|
89
88
|
- fixtures/rails_3_2_12_no_init/Gemfile
|
90
|
-
- fixtures/rails_3_2_12_no_init/Guardfile
|
91
89
|
- fixtures/rails_3_2_12_no_init/README.rdoc
|
92
90
|
- fixtures/rails_3_2_12_no_init/Rakefile
|
93
91
|
- fixtures/rails_3_2_12_no_init/app/controllers/application_controller.rb
|
@@ -136,6 +134,7 @@ files:
|
|
136
134
|
- lib/secure_headers/headers/x_content_type_options.rb
|
137
135
|
- lib/secure_headers/headers/x_frame_options.rb
|
138
136
|
- lib/secure_headers/headers/x_xss_protection.rb
|
137
|
+
- lib/secure_headers/padrino.rb
|
139
138
|
- lib/secure_headers/railtie.rb
|
140
139
|
- lib/secure_headers/version.rb
|
141
140
|
- secure_headers.gemspec
|
@@ -168,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
168
167
|
version: '0'
|
169
168
|
requirements: []
|
170
169
|
rubyforge_project:
|
171
|
-
rubygems_version: 2.
|
170
|
+
rubygems_version: 2.1.1
|
172
171
|
signing_key:
|
173
172
|
specification_version: 4
|
174
173
|
summary: Add easily configured browser headers to responses including content security
|
@@ -1,14 +0,0 @@
|
|
1
|
-
guard 'spork', :rspec_port => 1234, :aggressive_kill => false do
|
2
|
-
watch('spec/spec_helper.rb') { :rspec }
|
3
|
-
end
|
4
|
-
|
5
|
-
guard 'rspec', :cli => "--color --drb --drb-port 1234", :keep_failed => true, :all_after_pass => true, :focus_on_failed => true do
|
6
|
-
watch(%r{^spec/.+_spec\.rb$})
|
7
|
-
watch('../../../lib')
|
8
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
9
|
-
watch('spec/spec_helper.rb') { "spec" }
|
10
|
-
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
11
|
-
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
12
|
-
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
13
|
-
end
|
14
|
-
|
@@ -1,14 +0,0 @@
|
|
1
|
-
guard 'spork', :rspec_port => 1235, :aggressive_kill => false do
|
2
|
-
watch('spec/spec_helper.rb') { :rspec }
|
3
|
-
end
|
4
|
-
|
5
|
-
guard 'rspec', :cli => "--color --drb --drb-port 1235", :keep_failed => true, :all_after_pass => true, :focus_on_failed => true do
|
6
|
-
watch(%r{^spec/.+_spec\.rb$})
|
7
|
-
watch('../../../lib')
|
8
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
9
|
-
watch('spec/spec_helper.rb') { "spec" }
|
10
|
-
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
11
|
-
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
12
|
-
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
13
|
-
end
|
14
|
-
|