secure_headers 1.1.1 → 2.0.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.
- data/.gitignore +4 -8
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -3
- data/Gemfile +6 -11
- data/README.md +185 -102
- data/Rakefile +11 -116
- data/fixtures/rails_3_2_12/Gemfile +0 -8
- data/fixtures/rails_3_2_12/app/controllers/things_controller.rb +0 -1
- data/fixtures/rails_3_2_12/app/views/layouts/application.html.erb +1 -4
- data/fixtures/rails_3_2_12/app/views/other_things/index.html.erb +2 -1
- data/fixtures/rails_3_2_12/app/views/things/index.html.erb +1 -21
- data/fixtures/rails_3_2_12/config/application.rb +0 -54
- data/fixtures/rails_3_2_12/config/environments/test.rb +2 -2
- data/fixtures/rails_3_2_12/config/initializers/secure_headers.rb +3 -1
- data/fixtures/rails_3_2_12/config/routes.rb +0 -57
- data/fixtures/rails_3_2_12/config/script_hashes.yml +5 -0
- data/fixtures/rails_3_2_12/config.ru +3 -0
- data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +57 -19
- data/fixtures/rails_3_2_12/spec/controllers/things_controller_spec.rb +18 -13
- data/fixtures/rails_3_2_12/spec/spec_helper.rb +1 -5
- data/fixtures/rails_3_2_12_no_init/Gemfile +0 -9
- data/fixtures/rails_3_2_12_no_init/app/controllers/other_things_controller.rb +1 -2
- data/fixtures/rails_3_2_12_no_init/app/controllers/things_controller.rb +1 -1
- data/fixtures/rails_3_2_12_no_init/app/views/layouts/application.html.erb +0 -2
- data/fixtures/rails_3_2_12_no_init/app/views/things/index.html.erb +0 -21
- data/fixtures/rails_3_2_12_no_init/config/application.rb +0 -51
- data/fixtures/rails_3_2_12_no_init/config/environments/test.rb +2 -2
- data/fixtures/rails_3_2_12_no_init/config/routes.rb +0 -57
- data/fixtures/rails_3_2_12_no_init/spec/controllers/other_things_controller_spec.rb +17 -12
- data/fixtures/rails_3_2_12_no_init/spec/controllers/things_controller_spec.rb +17 -12
- data/fixtures/rails_3_2_12_no_init/spec/spec_helper.rb +3 -18
- data/fixtures/rails_4_1_8/Gemfile +5 -0
- data/fixtures/rails_4_1_8/README.rdoc +28 -0
- data/fixtures/rails_4_1_8/Rakefile +6 -0
- data/fixtures/rails_4_1_8/app/controllers/application_controller.rb +4 -0
- data/fixtures/rails_4_1_8/app/controllers/concerns/.keep +0 -0
- data/fixtures/rails_4_1_8/app/controllers/other_things_controller.rb +5 -0
- data/fixtures/rails_4_1_8/app/controllers/things_controller.rb +5 -0
- data/fixtures/rails_4_1_8/app/models/.keep +0 -0
- data/fixtures/rails_4_1_8/app/models/concerns/.keep +0 -0
- data/fixtures/rails_4_1_8/app/views/layouts/application.html.erb +11 -0
- data/fixtures/rails_4_1_8/app/views/other_things/index.html.erb +2 -0
- data/fixtures/rails_4_1_8/app/views/things/index.html.erb +1 -0
- data/fixtures/rails_4_1_8/config/application.rb +15 -0
- data/fixtures/rails_4_1_8/config/boot.rb +4 -0
- data/fixtures/rails_4_1_8/config/environment.rb +5 -0
- data/fixtures/rails_4_1_8/config/environments/test.rb +10 -0
- data/fixtures/rails_4_1_8/config/initializers/secure_headers.rb +17 -0
- data/fixtures/rails_4_1_8/config/routes.rb +4 -0
- data/fixtures/rails_4_1_8/config/script_hashes.yml +5 -0
- data/fixtures/rails_4_1_8/config/secrets.yml +22 -0
- data/fixtures/rails_4_1_8/config.ru +4 -0
- data/fixtures/rails_4_1_8/lib/assets/.keep +0 -0
- data/fixtures/rails_4_1_8/lib/tasks/.keep +0 -0
- data/fixtures/rails_4_1_8/log/.keep +0 -0
- data/fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb +83 -0
- data/fixtures/rails_4_1_8/spec/controllers/things_controller_spec.rb +59 -0
- data/fixtures/rails_4_1_8/spec/spec_helper.rb +15 -0
- data/fixtures/rails_4_1_8/vendor/assets/javascripts/.keep +0 -0
- data/fixtures/rails_4_1_8/vendor/assets/stylesheets/.keep +0 -0
- data/lib/secure_headers/hash_helper.rb +7 -0
- data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +22 -0
- data/lib/secure_headers/headers/content_security_policy.rb +160 -129
- data/lib/secure_headers/headers/strict_transport_security.rb +2 -1
- data/lib/secure_headers/headers/x_download_options.rb +39 -0
- data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +40 -0
- data/lib/secure_headers/headers/x_xss_protection.rb +2 -1
- data/lib/secure_headers/padrino.rb +14 -0
- data/lib/secure_headers/railtie.rb +19 -24
- data/lib/secure_headers/version.rb +1 -1
- data/lib/secure_headers/view_helper.rb +68 -0
- data/lib/secure_headers.rb +65 -16
- data/lib/tasks/tasks.rake +48 -0
- data/secure_headers.gemspec +4 -4
- data/spec/lib/secure_headers/headers/content_security_policy/script_hash_middleware_spec.rb +47 -0
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +127 -220
- data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +21 -20
- data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +12 -12
- data/spec/lib/secure_headers/headers/x_download_options_spec.rb +32 -0
- data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +12 -12
- data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +64 -0
- data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +20 -19
- data/spec/lib/secure_headers_spec.rb +68 -73
- data/spec/spec_helper.rb +29 -18
- metadata +51 -46
- data/.rvmrc +0 -1
- data/Guardfile +0 -10
- data/HISTORY.md +0 -115
- data/app/controllers/content_security_policy_controller.rb +0 -75
- data/config/curl-ca-bundle.crt +0 -5420
- data/config/routes.rb +0 -3
- data/fixtures/rails_3_2_12/Guardfile +0 -14
- data/fixtures/rails_3_2_12/app/models/thing.rb +0 -3
- data/fixtures/rails_3_2_12/config/database.yml +0 -25
- data/fixtures/rails_3_2_12/config/environments/development.rb +0 -37
- data/fixtures/rails_3_2_12/config/environments/production.rb +0 -67
- data/fixtures/rails_3_2_12/config/initializers/backtrace_silencers.rb +0 -7
- data/fixtures/rails_3_2_12/config/initializers/inflections.rb +0 -15
- data/fixtures/rails_3_2_12/config/initializers/mime_types.rb +0 -5
- data/fixtures/rails_3_2_12/config/initializers/secret_token.rb +0 -7
- data/fixtures/rails_3_2_12/config/initializers/session_store.rb +0 -8
- data/fixtures/rails_3_2_12/config/initializers/wrap_parameters.rb +0 -14
- data/fixtures/rails_3_2_12/config/locales/en.yml +0 -5
- data/fixtures/rails_3_2_12/db/schema.rb +0 -16
- data/fixtures/rails_3_2_12/db/seeds.rb +0 -7
- data/fixtures/rails_3_2_12_no_init/Guardfile +0 -14
- data/fixtures/rails_3_2_12_no_init/app/models/thing.rb +0 -3
- data/fixtures/rails_3_2_12_no_init/app/views/things/_form.html.erb +0 -17
- data/fixtures/rails_3_2_12_no_init/app/views/things/edit.html.erb +0 -6
- data/fixtures/rails_3_2_12_no_init/app/views/things/new.html.erb +0 -5
- data/fixtures/rails_3_2_12_no_init/app/views/things/show.html.erb +0 -5
- data/fixtures/rails_3_2_12_no_init/config/database.yml +0 -25
- data/fixtures/rails_3_2_12_no_init/config/environments/development.rb +0 -37
- data/fixtures/rails_3_2_12_no_init/config/environments/production.rb +0 -67
- data/fixtures/rails_3_2_12_no_init/config/initializers/backtrace_silencers.rb +0 -7
- data/fixtures/rails_3_2_12_no_init/config/initializers/inflections.rb +0 -15
- data/fixtures/rails_3_2_12_no_init/config/initializers/mime_types.rb +0 -5
- data/fixtures/rails_3_2_12_no_init/config/initializers/secret_token.rb +0 -7
- data/fixtures/rails_3_2_12_no_init/config/initializers/session_store.rb +0 -8
- data/fixtures/rails_3_2_12_no_init/config/initializers/wrap_parameters.rb +0 -14
- data/fixtures/rails_3_2_12_no_init/config/locales/en.yml +0 -5
- data/fixtures/rails_3_2_12_no_init/db/schema.rb +0 -16
- data/fixtures/rails_3_2_12_no_init/db/seeds.rb +0 -7
- data/spec/controllers/content_security_policy_controller_spec.rb +0 -90
|
@@ -2,34 +2,34 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
module SecureHeaders
|
|
4
4
|
describe XFrameOptions do
|
|
5
|
-
specify{ XFrameOptions.new.name.
|
|
5
|
+
specify{ expect(XFrameOptions.new.name).to eq("X-Frame-Options") }
|
|
6
6
|
|
|
7
7
|
describe "#value" do
|
|
8
|
-
specify { XFrameOptions.new.value.
|
|
9
|
-
specify { XFrameOptions.new("SAMEORIGIN").value.
|
|
10
|
-
specify { XFrameOptions.new(:value => 'DENY').value.
|
|
8
|
+
specify { expect(XFrameOptions.new.value).to eq(XFrameOptions::Constants::DEFAULT_VALUE)}
|
|
9
|
+
specify { expect(XFrameOptions.new("SAMEORIGIN").value).to eq("SAMEORIGIN")}
|
|
10
|
+
specify { expect(XFrameOptions.new(:value => 'DENY').value).to eq("DENY")}
|
|
11
11
|
|
|
12
12
|
context "with invalid configuration" do
|
|
13
13
|
it "allows SAMEORIGIN" do
|
|
14
|
-
|
|
14
|
+
expect {
|
|
15
15
|
XFrameOptions.new("SAMEORIGIN").value
|
|
16
|
-
}.
|
|
16
|
+
}.not_to raise_error
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
it "allows DENY" do
|
|
20
|
-
|
|
20
|
+
expect {
|
|
21
21
|
XFrameOptions.new("DENY").value
|
|
22
|
-
}.
|
|
22
|
+
}.not_to raise_error end
|
|
23
23
|
|
|
24
24
|
it "allows ALLOW-FROM*" do
|
|
25
|
-
|
|
25
|
+
expect {
|
|
26
26
|
XFrameOptions.new("ALLOW-FROM: example.com").value
|
|
27
|
-
}.
|
|
27
|
+
}.not_to raise_error
|
|
28
28
|
end
|
|
29
29
|
it "does not allow garbage" do
|
|
30
|
-
|
|
30
|
+
expect {
|
|
31
31
|
XFrameOptions.new("I like turtles").value
|
|
32
|
-
}.
|
|
32
|
+
}.to raise_error(XFOBuildError)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module SecureHeaders
|
|
2
|
+
describe XPermittedCrossDomainPolicies do
|
|
3
|
+
specify { expect(XPermittedCrossDomainPolicies.new.name).to eq(XPermittedCrossDomainPolicies::Constants::XPCDP_HEADER_NAME)}
|
|
4
|
+
specify { expect(XPermittedCrossDomainPolicies.new.value).to eq("none")}
|
|
5
|
+
specify { expect(XPermittedCrossDomainPolicies.new('master-only').value).to eq('master-only')}
|
|
6
|
+
specify { expect(XPermittedCrossDomainPolicies.new(:value => 'master-only').value).to eq('master-only') }
|
|
7
|
+
|
|
8
|
+
context "valid configuration values" do
|
|
9
|
+
it "accepts 'all'" do
|
|
10
|
+
expect {
|
|
11
|
+
XPermittedCrossDomainPolicies.new("all")
|
|
12
|
+
}.not_to raise_error
|
|
13
|
+
|
|
14
|
+
expect {
|
|
15
|
+
XPermittedCrossDomainPolicies.new(:value => "all")
|
|
16
|
+
}.not_to raise_error
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "accepts 'by-ftp-filename'" do
|
|
20
|
+
expect {
|
|
21
|
+
XPermittedCrossDomainPolicies.new("by-ftp-filename")
|
|
22
|
+
}.not_to raise_error
|
|
23
|
+
|
|
24
|
+
expect {
|
|
25
|
+
XPermittedCrossDomainPolicies.new(:value => "by-ftp-filename")
|
|
26
|
+
}.not_to raise_error
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "accepts 'by-content-type'" do
|
|
30
|
+
expect {
|
|
31
|
+
XPermittedCrossDomainPolicies.new("by-content-type")
|
|
32
|
+
}.not_to raise_error
|
|
33
|
+
|
|
34
|
+
expect {
|
|
35
|
+
XPermittedCrossDomainPolicies.new(:value => "by-content-type")
|
|
36
|
+
}.not_to raise_error
|
|
37
|
+
end
|
|
38
|
+
it "accepts 'master-only'" do
|
|
39
|
+
expect {
|
|
40
|
+
XPermittedCrossDomainPolicies.new("master-only")
|
|
41
|
+
}.not_to raise_error
|
|
42
|
+
|
|
43
|
+
expect {
|
|
44
|
+
XPermittedCrossDomainPolicies.new(:value => "master-only")
|
|
45
|
+
}.not_to raise_error
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "accepts nil" do
|
|
49
|
+
expect {
|
|
50
|
+
XPermittedCrossDomainPolicies.new
|
|
51
|
+
}.not_to raise_error
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context 'invlaid configuration values' do
|
|
56
|
+
|
|
57
|
+
it "doesn't accept invalid values" do
|
|
58
|
+
expect {
|
|
59
|
+
XPermittedCrossDomainPolicies.new("open")
|
|
60
|
+
}.to raise_error
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -1,53 +1,54 @@
|
|
|
1
1
|
module SecureHeaders
|
|
2
2
|
describe XXssProtection do
|
|
3
|
-
specify { XXssProtection.new.name.
|
|
4
|
-
specify { XXssProtection.new.value.
|
|
5
|
-
specify { XXssProtection.new("0").value.
|
|
6
|
-
specify { XXssProtection.new(:value => 1, :mode => 'block').value.
|
|
3
|
+
specify { expect(XXssProtection.new.name).to eq(X_XSS_PROTECTION_HEADER_NAME)}
|
|
4
|
+
specify { expect(XXssProtection.new.value).to eq("1")}
|
|
5
|
+
specify { expect(XXssProtection.new("0").value).to eq("0")}
|
|
6
|
+
specify { expect(XXssProtection.new(:value => 1, :mode => 'block').value).to eq('1; mode=block') }
|
|
7
|
+
specify { expect(XXssProtection.new(:value => 1, :mode => 'block', :report_uri => 'https://www.secure.com/reports').value).to eq('1; mode=block; report=https://www.secure.com/reports') }
|
|
7
8
|
|
|
8
9
|
context "with invalid configuration" do
|
|
9
10
|
it "should raise an error when providing a string that is not valid" do
|
|
10
|
-
|
|
11
|
+
expect {
|
|
11
12
|
XXssProtection.new("asdf")
|
|
12
|
-
}.
|
|
13
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
expect {
|
|
15
16
|
XXssProtection.new("asdf; mode=donkey")
|
|
16
|
-
}.
|
|
17
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
context "when using a hash value" do
|
|
20
21
|
it "should allow string values ('1' or '0' are the only valid strings)" do
|
|
21
|
-
|
|
22
|
+
expect {
|
|
22
23
|
XXssProtection.new(:value => '1')
|
|
23
|
-
}.
|
|
24
|
+
}.not_to raise_error
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
it "should allow integer values (1 or 0 are the only valid integers)" do
|
|
27
|
-
|
|
28
|
+
expect {
|
|
28
29
|
XXssProtection.new(:value => 1)
|
|
29
|
-
}.
|
|
30
|
+
}.not_to raise_error
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
it "should raise an error if no value key is supplied" do
|
|
33
|
-
|
|
34
|
+
expect {
|
|
34
35
|
XXssProtection.new(:mode => 'block')
|
|
35
|
-
}.
|
|
36
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
it "should raise an error if an invalid key is supplied" do
|
|
39
|
-
|
|
40
|
+
expect {
|
|
40
41
|
XXssProtection.new(:value => 123)
|
|
41
|
-
}.
|
|
42
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
it "should raise an error if mode != block" do
|
|
45
|
-
|
|
46
|
+
expect {
|
|
46
47
|
XXssProtection.new(:value => 1, :mode => "donkey")
|
|
47
|
-
}.
|
|
48
|
+
}.to raise_error(XXssProtectionBuildError)
|
|
48
49
|
end
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
|
-
end
|
|
54
|
+
end
|
|
@@ -13,38 +13,13 @@ describe SecureHeaders do
|
|
|
13
13
|
|
|
14
14
|
before(:each) do
|
|
15
15
|
stub_user_agent(nil)
|
|
16
|
-
headers.
|
|
17
|
-
subject.
|
|
18
|
-
subject.
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
ALL_HEADERS = Hash[[:hsts, :csp, :x_frame_options, :x_content_type_options, :x_xss_protection].map{|header| [header, false]}]
|
|
22
|
-
USER_AGENTS = {
|
|
23
|
-
:firefox => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
|
|
24
|
-
:chrome => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5',
|
|
25
|
-
:ie => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
|
|
26
|
-
:opera => 'Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00',
|
|
27
|
-
:ios5 => "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
|
|
28
|
-
:ios6 => "Mozilla/5.0 (iPhone; CPU iPhone OS 614 like Mac OS X) AppleWebKit/536.26 (KHTML like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25",
|
|
29
|
-
:safari5 => "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",
|
|
30
|
-
:safari5_1 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10",
|
|
31
|
-
:safari6 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
def should_assign_header name, value
|
|
35
|
-
response.headers.should_receive(:[]=).with(name, value)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def should_not_assign_header name
|
|
39
|
-
response.headers.should_not_receive(:[]=).with(name, anything)
|
|
16
|
+
allow(headers).to receive(:[])
|
|
17
|
+
allow(subject).to receive(:response).and_return(response)
|
|
18
|
+
allow(subject).to receive(:request).and_return(request)
|
|
40
19
|
end
|
|
41
20
|
|
|
42
21
|
def stub_user_agent val
|
|
43
|
-
request.
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def options_for header
|
|
47
|
-
ALL_HEADERS.reject{|k,v| k == header}
|
|
22
|
+
allow(request).to receive_message_chain(:env, :[]).and_return(val)
|
|
48
23
|
end
|
|
49
24
|
|
|
50
25
|
def reset_config
|
|
@@ -54,6 +29,8 @@ describe SecureHeaders do
|
|
|
54
29
|
config.x_content_type_options = nil
|
|
55
30
|
config.x_xss_protection = nil
|
|
56
31
|
config.csp = nil
|
|
32
|
+
config.x_download_options = nil
|
|
33
|
+
config.x_permitted_cross_domain_policies = nil
|
|
57
34
|
end
|
|
58
35
|
end
|
|
59
36
|
|
|
@@ -63,14 +40,8 @@ describe SecureHeaders do
|
|
|
63
40
|
subject.set_x_frame_options_header
|
|
64
41
|
subject.set_x_content_type_options_header
|
|
65
42
|
subject.set_x_xss_protection_header
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
describe "#ensure_security_headers" do
|
|
69
|
-
it "sets a before filter" do
|
|
70
|
-
options = {}
|
|
71
|
-
DummyClass.should_receive(:before_filter).exactly(5).times
|
|
72
|
-
DummyClass.ensure_security_headers(options)
|
|
73
|
-
end
|
|
43
|
+
subject.set_x_download_options_header
|
|
44
|
+
subject.set_x_permitted_cross_domain_policies_header
|
|
74
45
|
end
|
|
75
46
|
|
|
76
47
|
describe "#set_header" do
|
|
@@ -86,19 +57,18 @@ describe SecureHeaders do
|
|
|
86
57
|
end
|
|
87
58
|
|
|
88
59
|
describe "#set_security_headers" do
|
|
89
|
-
before(:each) do
|
|
90
|
-
SecureHeaders::ContentSecurityPolicy.stub(:new).and_return(double.as_null_object)
|
|
91
|
-
end
|
|
92
60
|
USER_AGENTS.each do |name, useragent|
|
|
93
61
|
it "sets all default headers for #{name} (smoke test)" do
|
|
94
62
|
stub_user_agent(useragent)
|
|
95
|
-
number_of_headers =
|
|
96
|
-
subject.
|
|
63
|
+
number_of_headers = 7
|
|
64
|
+
expect(subject).to receive(:set_header).exactly(number_of_headers).times # a request for a given header
|
|
97
65
|
subject.set_csp_header
|
|
98
66
|
subject.set_x_frame_options_header
|
|
99
67
|
subject.set_hsts_header
|
|
100
68
|
subject.set_x_xss_protection_header
|
|
101
69
|
subject.set_x_content_type_options_header
|
|
70
|
+
subject.set_x_download_options_header
|
|
71
|
+
subject.set_x_permitted_cross_domain_policies_header
|
|
102
72
|
end
|
|
103
73
|
end
|
|
104
74
|
|
|
@@ -113,26 +83,47 @@ describe SecureHeaders do
|
|
|
113
83
|
subject.set_x_xss_protection_header(false)
|
|
114
84
|
end
|
|
115
85
|
|
|
86
|
+
it "does not set the X-Download-Options header if disabled" do
|
|
87
|
+
should_not_assign_header(XDO_HEADER_NAME)
|
|
88
|
+
subject.set_x_download_options_header(false)
|
|
89
|
+
end
|
|
90
|
+
|
|
116
91
|
it "does not set the X-Frame-Options header if disabled" do
|
|
117
92
|
should_not_assign_header(XFO_HEADER_NAME)
|
|
118
93
|
subject.set_x_frame_options_header(false)
|
|
119
94
|
end
|
|
120
95
|
|
|
96
|
+
it "does not set the X-Permitted-Cross-Domain-Policies header if disabled" do
|
|
97
|
+
should_not_assign_header(XPCDP_HEADER_NAME)
|
|
98
|
+
subject.set_x_permitted_cross_domain_policies_header(false)
|
|
99
|
+
end
|
|
100
|
+
|
|
121
101
|
it "does not set the HSTS header if disabled" do
|
|
122
102
|
should_not_assign_header(HSTS_HEADER_NAME)
|
|
123
103
|
subject.set_hsts_header(false)
|
|
124
104
|
end
|
|
125
105
|
|
|
126
106
|
it "does not set the HSTS header if request is over HTTP" do
|
|
127
|
-
subject.
|
|
107
|
+
allow(subject).to receive_message_chain(:request, :ssl?).and_return(false)
|
|
128
108
|
should_not_assign_header(HSTS_HEADER_NAME)
|
|
129
109
|
subject.set_hsts_header({:include_subdomains => true})
|
|
130
110
|
end
|
|
131
111
|
|
|
132
112
|
it "does not set the CSP header if disabled" do
|
|
133
113
|
stub_user_agent(USER_AGENTS[:chrome])
|
|
134
|
-
should_not_assign_header(
|
|
135
|
-
subject.set_csp_header(
|
|
114
|
+
should_not_assign_header(HEADER_NAME)
|
|
115
|
+
subject.set_csp_header(false)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "saves the options to the env when using script hashes" do
|
|
119
|
+
opts = {
|
|
120
|
+
:default_src => 'self',
|
|
121
|
+
:script_hash_middleware => true
|
|
122
|
+
}
|
|
123
|
+
stub_user_agent(USER_AGENTS[:chrome])
|
|
124
|
+
|
|
125
|
+
expect(SecureHeaders::ContentSecurityPolicy).to receive(:add_to_env)
|
|
126
|
+
subject.set_csp_header(opts)
|
|
136
127
|
end
|
|
137
128
|
|
|
138
129
|
context "when disabled by configuration settings" do
|
|
@@ -143,8 +134,10 @@ describe SecureHeaders do
|
|
|
143
134
|
config.x_content_type_options = false
|
|
144
135
|
config.x_xss_protection = false
|
|
145
136
|
config.csp = false
|
|
137
|
+
config.x_download_options = false
|
|
138
|
+
config.x_permitted_cross_domain_policies = false
|
|
146
139
|
end
|
|
147
|
-
subject.
|
|
140
|
+
expect(subject).not_to receive(:set_header)
|
|
148
141
|
set_security_headers(subject)
|
|
149
142
|
reset_config
|
|
150
143
|
end
|
|
@@ -163,6 +156,18 @@ describe SecureHeaders do
|
|
|
163
156
|
end
|
|
164
157
|
end
|
|
165
158
|
|
|
159
|
+
describe "#set_x_download_options_header" do
|
|
160
|
+
it "sets the X-Download-Options header" do
|
|
161
|
+
should_assign_header(XDO_HEADER_NAME, SecureHeaders::XDownloadOptions::Constants::DEFAULT_VALUE)
|
|
162
|
+
subject.set_x_download_options_header
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "allows a custom X-Download-Options header" do
|
|
166
|
+
should_assign_header(XDO_HEADER_NAME, "noopen")
|
|
167
|
+
subject.set_x_download_options_header(:value => 'noopen')
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
166
171
|
describe "#set_strict_transport_security" do
|
|
167
172
|
it "sets the Strict-Transport-Security header" do
|
|
168
173
|
should_assign_header(HSTS_HEADER_NAME, SecureHeaders::StrictTransportSecurity::Constants::DEFAULT_VALUE)
|
|
@@ -178,6 +183,11 @@ describe SecureHeaders do
|
|
|
178
183
|
should_assign_header(HSTS_HEADER_NAME, "max-age=#{HSTS_MAX_AGE}; includeSubdomains")
|
|
179
184
|
subject.set_hsts_header(:max_age => HSTS_MAX_AGE, :include_subdomains => true)
|
|
180
185
|
end
|
|
186
|
+
|
|
187
|
+
it "allows you to specify preload" do
|
|
188
|
+
should_assign_header(HSTS_HEADER_NAME, "max-age=#{HSTS_MAX_AGE}; includeSubdomains; preload")
|
|
189
|
+
subject.set_hsts_header(:max_age => HSTS_MAX_AGE, :include_subdomains => true, :preload => true)
|
|
190
|
+
end
|
|
181
191
|
end
|
|
182
192
|
|
|
183
193
|
describe "#set_x_xss_protection" do
|
|
@@ -221,7 +231,7 @@ describe SecureHeaders do
|
|
|
221
231
|
context "when using Firefox" do
|
|
222
232
|
it "sets CSP headers" do
|
|
223
233
|
stub_user_agent(USER_AGENTS[:firefox])
|
|
224
|
-
should_assign_header(
|
|
234
|
+
should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
|
|
225
235
|
subject.set_csp_header
|
|
226
236
|
end
|
|
227
237
|
end
|
|
@@ -229,7 +239,7 @@ describe SecureHeaders do
|
|
|
229
239
|
context "when using Chrome" do
|
|
230
240
|
it "sets default CSP header" do
|
|
231
241
|
stub_user_agent(USER_AGENTS[:chrome])
|
|
232
|
-
should_assign_header(
|
|
242
|
+
should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
|
|
233
243
|
subject.set_csp_header
|
|
234
244
|
end
|
|
235
245
|
end
|
|
@@ -237,36 +247,21 @@ describe SecureHeaders do
|
|
|
237
247
|
context "when using a browser besides chrome/firefox" do
|
|
238
248
|
it "sets the CSP header" do
|
|
239
249
|
stub_user_agent(USER_AGENTS[:opera])
|
|
240
|
-
should_assign_header(
|
|
250
|
+
should_assign_header(HEADER_NAME + "-Report-Only", DEFAULT_CSP_HEADER)
|
|
241
251
|
subject.set_csp_header
|
|
242
252
|
end
|
|
243
253
|
end
|
|
254
|
+
end
|
|
244
255
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
:default_src => 'self',
|
|
251
|
-
:script_src => 'https://mycdn.example.com',
|
|
252
|
-
:experimental => {
|
|
253
|
-
:script_src => 'self',
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
it "does not set the header in enforce mode if experimental is supplied, but enforce is disabled" do
|
|
259
|
-
opts = @opts.merge(:enforce => false)
|
|
260
|
-
should_assign_header(STANDARD_HEADER_NAME + "-Report-Only", anything)
|
|
261
|
-
should_not_assign_header(STANDARD_HEADER_NAME)
|
|
262
|
-
subject.set_csp_header(opts)
|
|
263
|
-
end
|
|
256
|
+
describe "#set_x_permitted_cross_domain_policies_header" do
|
|
257
|
+
it "sets the X-Permitted-Cross-Domain-Policies header" do
|
|
258
|
+
should_assign_header(XPCDP_HEADER_NAME, SecureHeaders::XPermittedCrossDomainPolicies::Constants::DEFAULT_VALUE)
|
|
259
|
+
subject.set_x_permitted_cross_domain_policies_header
|
|
260
|
+
end
|
|
264
261
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
subject.set_csp_header(@opts)
|
|
269
|
-
end
|
|
262
|
+
it "allows a custom X-Permitted-Cross-Domain-Policies header" do
|
|
263
|
+
should_assign_header(XPCDP_HEADER_NAME, "master-only")
|
|
264
|
+
subject.set_x_permitted_cross_domain_policies_header(:value => 'master-only')
|
|
270
265
|
end
|
|
271
266
|
end
|
|
272
267
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,25 +1,36 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
-
require '
|
|
2
|
+
require 'rspec'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
require 'simplecov'
|
|
6
|
-
SimpleCov.start do
|
|
7
|
-
add_filter "spec"
|
|
8
|
-
end
|
|
9
|
-
end
|
|
4
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
require 'rspec'
|
|
6
|
+
if defined?(Coveralls)
|
|
7
|
+
Coveralls.wear!
|
|
14
8
|
end
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
10
|
+
include ::SecureHeaders::StrictTransportSecurity::Constants
|
|
11
|
+
include ::SecureHeaders::ContentSecurityPolicy::Constants
|
|
12
|
+
include ::SecureHeaders::XFrameOptions::Constants
|
|
13
|
+
include ::SecureHeaders::XXssProtection::Constants
|
|
14
|
+
include ::SecureHeaders::XContentTypeOptions::Constants
|
|
15
|
+
include ::SecureHeaders::XDownloadOptions::Constants
|
|
16
|
+
include ::SecureHeaders::XPermittedCrossDomainPolicies::Constants
|
|
17
|
+
|
|
18
|
+
USER_AGENTS = {
|
|
19
|
+
:firefox => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
|
|
20
|
+
:chrome => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5',
|
|
21
|
+
:ie => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
|
|
22
|
+
:opera => 'Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00',
|
|
23
|
+
:ios5 => "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
|
|
24
|
+
:ios6 => "Mozilla/5.0 (iPhone; CPU iPhone OS 614 like Mac OS X) AppleWebKit/536.26 (KHTML like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25",
|
|
25
|
+
:safari5 => "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",
|
|
26
|
+
:safari5_1 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10",
|
|
27
|
+
:safari6 => "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def should_assign_header name, value
|
|
31
|
+
expect(response.headers).to receive(:[]=).with(name, value)
|
|
24
32
|
end
|
|
25
33
|
|
|
34
|
+
def should_not_assign_header name
|
|
35
|
+
expect(response.headers).not_to receive(:[]=).with(name, anything)
|
|
36
|
+
end
|